line |
stmt |
bran |
cond |
sub |
pod |
time |
code |
1
|
|
|
|
|
|
|
package PDF::Builder::Content::Text; |
2
|
|
|
|
|
|
|
|
3
|
38
|
|
|
38
|
|
245
|
use base 'PDF::Builder::Content'; |
|
38
|
|
|
|
|
69
|
|
|
38
|
|
|
|
|
3787
|
|
4
|
|
|
|
|
|
|
|
5
|
38
|
|
|
38
|
|
248
|
use strict; |
|
38
|
|
|
|
|
73
|
|
|
38
|
|
|
|
|
655
|
|
6
|
38
|
|
|
38
|
|
150
|
use warnings; |
|
38
|
|
|
|
|
110
|
|
|
38
|
|
|
|
|
117753
|
|
7
|
|
|
|
|
|
|
|
8
|
|
|
|
|
|
|
our $VERSION = '3.024'; # VERSION |
9
|
|
|
|
|
|
|
our $LAST_UPDATE = '3.024'; # manually update whenever code is changed |
10
|
|
|
|
|
|
|
|
11
|
|
|
|
|
|
|
=head1 NAME |
12
|
|
|
|
|
|
|
|
13
|
|
|
|
|
|
|
PDF::Builder::Content::Text - additional specialized text-related formatting methods. Inherits from L |
14
|
|
|
|
|
|
|
|
15
|
|
|
|
|
|
|
B If you have used some of these methods in PDF::API2 with a I |
16
|
|
|
|
|
|
|
type object (e.g., $page->gfx()->method()), you may have to change to a I |
17
|
|
|
|
|
|
|
type object (e.g., $page->text()->method()). |
18
|
|
|
|
|
|
|
|
19
|
|
|
|
|
|
|
=head1 METHODS |
20
|
|
|
|
|
|
|
|
21
|
|
|
|
|
|
|
=cut |
22
|
|
|
|
|
|
|
|
23
|
|
|
|
|
|
|
sub new { |
24
|
20
|
|
|
20
|
1
|
50
|
my ($class) = @_; |
25
|
20
|
|
|
|
|
101
|
my $self = $class->SUPER::new(@_); |
26
|
20
|
|
|
|
|
94
|
$self->textstart(); |
27
|
20
|
|
|
|
|
49
|
return $self; |
28
|
|
|
|
|
|
|
} |
29
|
|
|
|
|
|
|
|
30
|
|
|
|
|
|
|
=over |
31
|
|
|
|
|
|
|
|
32
|
|
|
|
|
|
|
=item $width = $content->text_left($text, %opts) |
33
|
|
|
|
|
|
|
|
34
|
|
|
|
|
|
|
Alias for C. Implemented for symmetry, for those who use a lot of |
35
|
|
|
|
|
|
|
C and C, and desire a matching C. |
36
|
|
|
|
|
|
|
|
37
|
|
|
|
|
|
|
Adds text to the page (left justified), at the current position. |
38
|
|
|
|
|
|
|
Note that there is no maximum width, and nothing to keep you from overflowing |
39
|
|
|
|
|
|
|
the physical page on the right! |
40
|
|
|
|
|
|
|
The width used (in points) is B. |
41
|
|
|
|
|
|
|
|
42
|
|
|
|
|
|
|
=back |
43
|
|
|
|
|
|
|
|
44
|
|
|
|
|
|
|
=cut |
45
|
|
|
|
|
|
|
|
46
|
|
|
|
|
|
|
sub text_left { |
47
|
0
|
|
|
0
|
1
|
0
|
my ($self, $text, @opts) = @_; |
48
|
|
|
|
|
|
|
|
49
|
0
|
|
|
|
|
0
|
return $self->text($text, @opts); |
50
|
|
|
|
|
|
|
} |
51
|
|
|
|
|
|
|
|
52
|
|
|
|
|
|
|
=over |
53
|
|
|
|
|
|
|
|
54
|
|
|
|
|
|
|
=item $width = $content->text_center($text, %opts) |
55
|
|
|
|
|
|
|
|
56
|
|
|
|
|
|
|
As C, but I on the current point. |
57
|
|
|
|
|
|
|
|
58
|
|
|
|
|
|
|
Adds text to the page (centered). |
59
|
|
|
|
|
|
|
The width used (in points) is B. |
60
|
|
|
|
|
|
|
|
61
|
|
|
|
|
|
|
=back |
62
|
|
|
|
|
|
|
|
63
|
|
|
|
|
|
|
=cut |
64
|
|
|
|
|
|
|
|
65
|
|
|
|
|
|
|
sub text_center { |
66
|
6
|
|
|
6
|
1
|
32
|
my ($self, $text, @opts) = @_; |
67
|
|
|
|
|
|
|
|
68
|
6
|
|
|
|
|
29
|
my $width = $self->advancewidth($text, @opts); |
69
|
6
|
|
|
|
|
37
|
return $self->text($text, 'indent' => -($width/2), @opts); |
70
|
|
|
|
|
|
|
} |
71
|
|
|
|
|
|
|
|
72
|
|
|
|
|
|
|
=over |
73
|
|
|
|
|
|
|
|
74
|
|
|
|
|
|
|
=item $width = $content->text_right($text, %opts) |
75
|
|
|
|
|
|
|
|
76
|
|
|
|
|
|
|
As C, but right-aligned to the current point. |
77
|
|
|
|
|
|
|
|
78
|
|
|
|
|
|
|
Adds text to the page (right justified). |
79
|
|
|
|
|
|
|
Note that there is no maximum width, and nothing to keep you from overflowing |
80
|
|
|
|
|
|
|
the physical page on the left! |
81
|
|
|
|
|
|
|
The width used (in points) is B. |
82
|
|
|
|
|
|
|
|
83
|
|
|
|
|
|
|
=back |
84
|
|
|
|
|
|
|
|
85
|
|
|
|
|
|
|
=cut |
86
|
|
|
|
|
|
|
|
87
|
|
|
|
|
|
|
sub text_right { |
88
|
3
|
|
|
3
|
1
|
15
|
my ($self, $text, @opts) = @_; |
89
|
|
|
|
|
|
|
|
90
|
3
|
|
|
|
|
12
|
my $width = $self->advancewidth($text, @opts); |
91
|
3
|
|
|
|
|
12
|
return $self->text($text, 'indent' => -$width, @opts); |
92
|
|
|
|
|
|
|
} |
93
|
|
|
|
|
|
|
|
94
|
|
|
|
|
|
|
=over |
95
|
|
|
|
|
|
|
|
96
|
|
|
|
|
|
|
=item $width = $content->text_justified($text, $width, %opts) |
97
|
|
|
|
|
|
|
|
98
|
|
|
|
|
|
|
As C, but stretches text using C, C, and (as a |
99
|
|
|
|
|
|
|
last resort) C, to fill the desired |
100
|
|
|
|
|
|
|
(available) C<$width>. Note that if the desired width is I than the |
101
|
|
|
|
|
|
|
natural width taken by the text, it will be I to fit, using the |
102
|
|
|
|
|
|
|
same three routines. |
103
|
|
|
|
|
|
|
|
104
|
|
|
|
|
|
|
The unchanged C<$width> is B, unless there was some reason to |
105
|
|
|
|
|
|
|
change it (e.g., overflow). |
106
|
|
|
|
|
|
|
|
107
|
|
|
|
|
|
|
B |
108
|
|
|
|
|
|
|
|
109
|
|
|
|
|
|
|
=over |
110
|
|
|
|
|
|
|
|
111
|
|
|
|
|
|
|
=item 'nocs' => value |
112
|
|
|
|
|
|
|
|
113
|
|
|
|
|
|
|
If this option value is 1 (default 0), do B use any intercharacter |
114
|
|
|
|
|
|
|
spacing. This is useful for connected characters, such as fonts for Arabic, |
115
|
|
|
|
|
|
|
Devanagari, Latin cursive handwriting, etc. You don't want to add additional |
116
|
|
|
|
|
|
|
space between characters during justification, which would disconnect them. |
117
|
|
|
|
|
|
|
|
118
|
|
|
|
|
|
|
I (interword) spacing values (explicit or default) are doubled if |
119
|
|
|
|
|
|
|
nocs is 1. This is to make up for the lack of added/subtracted intercharacter |
120
|
|
|
|
|
|
|
spacing. |
121
|
|
|
|
|
|
|
|
122
|
|
|
|
|
|
|
=item 'wordsp' => value |
123
|
|
|
|
|
|
|
|
124
|
|
|
|
|
|
|
The percentage of one space character (default 100) that is the maximum amount |
125
|
|
|
|
|
|
|
to add to (each) interword spacing to expand the line. |
126
|
|
|
|
|
|
|
If C is 1, double C. |
127
|
|
|
|
|
|
|
|
128
|
|
|
|
|
|
|
=item 'charsp' => value |
129
|
|
|
|
|
|
|
|
130
|
|
|
|
|
|
|
If adding interword space didn't do enough, the percentage of one em (default |
131
|
|
|
|
|
|
|
100) that is the maximum amount to add to (each) intercharacter spacing to |
132
|
|
|
|
|
|
|
further expand the line. |
133
|
|
|
|
|
|
|
If C is 1, force C to 0. |
134
|
|
|
|
|
|
|
|
135
|
|
|
|
|
|
|
=item 'wordspa' => value |
136
|
|
|
|
|
|
|
|
137
|
|
|
|
|
|
|
If adding intercharacter space didn't do enough, the percentage of one space |
138
|
|
|
|
|
|
|
character (default 100) that is the maximum I amount to add to |
139
|
|
|
|
|
|
|
(each) interword spacing to further expand the line. |
140
|
|
|
|
|
|
|
If C is 1, double C. |
141
|
|
|
|
|
|
|
|
142
|
|
|
|
|
|
|
=item 'charspa' => value |
143
|
|
|
|
|
|
|
|
144
|
|
|
|
|
|
|
If adding more interword space didn't do enough, the percentage of one em |
145
|
|
|
|
|
|
|
(default 100) that is the maximum I amount to add to (each) |
146
|
|
|
|
|
|
|
intercharacter spacing to further expand the line. |
147
|
|
|
|
|
|
|
If C is 1, force C to 0. |
148
|
|
|
|
|
|
|
|
149
|
|
|
|
|
|
|
=item 'condw' => value |
150
|
|
|
|
|
|
|
|
151
|
|
|
|
|
|
|
The percentage of one space character (default 25) that is the maximum amount |
152
|
|
|
|
|
|
|
to subtract from (each) interword spacing to condense the line. |
153
|
|
|
|
|
|
|
If C is 1, double C. |
154
|
|
|
|
|
|
|
|
155
|
|
|
|
|
|
|
=item 'condc' => value |
156
|
|
|
|
|
|
|
|
157
|
|
|
|
|
|
|
If removing interword space didn't do enough, the percentage of one em |
158
|
|
|
|
|
|
|
(default 10) that is the maximum amount to subtract from (each) intercharacter |
159
|
|
|
|
|
|
|
spacing to further condense the line. |
160
|
|
|
|
|
|
|
If C is 1, force C to 0. |
161
|
|
|
|
|
|
|
|
162
|
|
|
|
|
|
|
=back |
163
|
|
|
|
|
|
|
|
164
|
|
|
|
|
|
|
If expansion (or reduction) wordspace and charspace changes didn't do enough |
165
|
|
|
|
|
|
|
to make the line fit the desired width, use C to finish expanding or |
166
|
|
|
|
|
|
|
condensing the line to fit. |
167
|
|
|
|
|
|
|
|
168
|
|
|
|
|
|
|
=back |
169
|
|
|
|
|
|
|
|
170
|
|
|
|
|
|
|
=cut |
171
|
|
|
|
|
|
|
|
172
|
|
|
|
|
|
|
sub text_justified { |
173
|
4
|
|
|
4
|
1
|
18
|
my ($self, $text, $width, %opts) = @_; |
174
|
|
|
|
|
|
|
# copy dashed option names to the preferred undashed names |
175
|
4
|
50
|
33
|
|
|
18
|
if (defined $opts{'-wordsp'} && !defined $opts{'wordsp'}) { $opts{'wordsp'} = delete($opts{'-wordsp'}); } |
|
0
|
|
|
|
|
0
|
|
176
|
4
|
50
|
33
|
|
|
13
|
if (defined $opts{'-charsp'} && !defined $opts{'charsp'}) { $opts{'charsp'} = delete($opts{'-charsp'}); } |
|
0
|
|
|
|
|
0
|
|
177
|
4
|
50
|
33
|
|
|
15
|
if (defined $opts{'-wordspa'} && !defined $opts{'wordspa'}) { $opts{'wordspa'} = delete($opts{'-wordspa'}); } |
|
0
|
|
|
|
|
0
|
|
178
|
4
|
50
|
33
|
|
|
14
|
if (defined $opts{'-charspa'} && !defined $opts{'charspa'}) { $opts{'charspa'} = delete($opts{'-charspa'}); } |
|
0
|
|
|
|
|
0
|
|
179
|
4
|
50
|
33
|
|
|
13
|
if (defined $opts{'-condw'} && !defined $opts{'condw'}) { $opts{'condw'} = delete($opts{'-condw'}); } |
|
0
|
|
|
|
|
0
|
|
180
|
4
|
50
|
33
|
|
|
16
|
if (defined $opts{'-condc'} && !defined $opts{'condc'}) { $opts{'condc'} = delete($opts{'-condc'}); } |
|
0
|
|
|
|
|
0
|
|
181
|
4
|
50
|
33
|
|
|
13
|
if (defined $opts{'-nocs'} && !defined $opts{'nocs'}) { $opts{'nocs'} = delete($opts{'-nocs'}); } |
|
0
|
|
|
|
|
0
|
|
182
|
|
|
|
|
|
|
|
183
|
|
|
|
|
|
|
# optional parameters to control how expansion or condensation are done |
184
|
|
|
|
|
|
|
# 1. expand interword space up to 100% of 1 space |
185
|
4
|
50
|
|
|
|
14
|
my $wordsp = defined($opts{'wordsp'})? $opts{'wordsp'}: 100; |
186
|
|
|
|
|
|
|
# 2. expand intercharacter space up to 100% of 1em |
187
|
4
|
50
|
|
|
|
9
|
my $charsp = defined($opts{'charsp'})? $opts{'charsp'}: 100; |
188
|
|
|
|
|
|
|
# 3. expand interword space up to another 100% of 1 space |
189
|
4
|
50
|
|
|
|
12
|
my $wordspa = defined($opts{'wordspa'})? $opts{'wordspa'}: 100; |
190
|
|
|
|
|
|
|
# 4. expand intercharacter space up to another 100% of 1em |
191
|
4
|
50
|
|
|
|
9
|
my $charspa = defined($opts{'charspa'})? $opts{'charspa'}: 100; |
192
|
|
|
|
|
|
|
# 5. condense interword space up to 25% of 1 space |
193
|
4
|
50
|
|
|
|
9
|
my $condw = defined($opts{'condw'})? $opts{'condw'}: 25; |
194
|
|
|
|
|
|
|
# 6. condense intercharacter space up to 10% of 1em |
195
|
4
|
50
|
|
|
|
9
|
my $condc = defined($opts{'condc'})? $opts{'condc'}: 10; |
196
|
|
|
|
|
|
|
# 7. if still short or long, hscale() |
197
|
|
|
|
|
|
|
|
198
|
4
|
50
|
|
|
|
10
|
my $nocs = defined($opts{'nocs'})? $opts{'nocs'}: 0; |
199
|
4
|
50
|
|
|
|
11
|
if ($nocs) { |
200
|
0
|
|
|
|
|
0
|
$charsp = $charspa = $condc = 0; |
201
|
0
|
|
|
|
|
0
|
$wordsp *= 2; |
202
|
0
|
|
|
|
|
0
|
$wordspa *= 2; |
203
|
0
|
|
|
|
|
0
|
$condw *= 2; |
204
|
|
|
|
|
|
|
} |
205
|
|
|
|
|
|
|
|
206
|
|
|
|
|
|
|
# with original wordspace, charspace, and hscale settings |
207
|
|
|
|
|
|
|
# note that we do NOT change any existing charspace here |
208
|
4
|
|
|
|
|
14
|
my $length = $self->advancewidth($text, %opts); |
209
|
4
|
|
|
|
|
10
|
my $overage = $length - $width; # > 0, raw text is too wide, < 0, narrow |
210
|
|
|
|
|
|
|
|
211
|
4
|
|
|
|
|
9
|
my ($i, @chars, $val, $limit); |
212
|
4
|
|
|
|
|
14
|
my $hs = $self->hscale(); # save old settings and reset to 0 |
213
|
4
|
|
|
|
|
13
|
my $ws = $self->wordspace(); |
214
|
4
|
|
|
|
|
15
|
my $cs = $self->charspace(); |
215
|
4
|
|
|
|
|
14
|
$self->hscale(100); $self->wordspace(0); $self->charspace(0); |
|
4
|
|
|
|
|
13
|
|
|
4
|
|
|
|
|
12
|
|
216
|
|
|
|
|
|
|
|
217
|
|
|
|
|
|
|
# not near perfect fit? not within .1 pt of fitting |
218
|
4
|
50
|
|
|
|
15
|
if (abs($overage) > 0.1) { |
219
|
|
|
|
|
|
|
|
220
|
|
|
|
|
|
|
# how many interword spaces can we change with wordspace? |
221
|
4
|
|
|
|
|
6
|
my $num_spaces = 0; |
222
|
|
|
|
|
|
|
# how many intercharacter spaces can be added to or removed? |
223
|
4
|
|
|
|
|
9
|
my $num_chars = -1; |
224
|
4
|
|
|
|
|
26
|
@chars = split //, $text; |
225
|
4
|
|
|
|
|
16
|
for ($i=0; $i
|
226
|
78
|
100
|
|
|
|
105
|
if ($chars[$i] eq ' ') { $num_spaces++; } # TBD other whitespace? |
|
16
|
|
|
|
|
19
|
|
227
|
78
|
|
|
|
|
111
|
$num_chars++; # count spaces as characters, too |
228
|
|
|
|
|
|
|
} |
229
|
4
|
|
|
|
|
14
|
my $em = $self->advancewidth('M'); |
230
|
4
|
|
|
|
|
12
|
my $sp = $self->advancewidth(' '); |
231
|
|
|
|
|
|
|
|
232
|
4
|
50
|
|
|
|
13
|
if ($overage > 0) { |
233
|
|
|
|
|
|
|
# too wide: need to condense it |
234
|
|
|
|
|
|
|
# 1. subtract from interword space, up to -$condw/100 $sp |
235
|
0
|
0
|
0
|
|
|
0
|
if ($overage > 0 && $num_spaces > 0 && $condw > 0) { |
|
|
|
0
|
|
|
|
|
236
|
0
|
|
|
|
|
0
|
$val = $overage/$num_spaces; |
237
|
0
|
|
|
|
|
0
|
$limit = $condw/100*$sp; |
238
|
0
|
0
|
|
|
|
0
|
if ($val > $limit) { $val = $limit; } |
|
0
|
|
|
|
|
0
|
|
239
|
0
|
|
|
|
|
0
|
$self->wordspace(-$val); |
240
|
0
|
|
|
|
|
0
|
$overage -= $val*$num_spaces; |
241
|
|
|
|
|
|
|
} |
242
|
|
|
|
|
|
|
# 2. subtract from intercharacter space, up to -$condc/100 $em |
243
|
0
|
0
|
0
|
|
|
0
|
if ($overage > 0 && $num_chars > 0 && $condc > 0) { |
|
|
|
0
|
|
|
|
|
244
|
0
|
|
|
|
|
0
|
$val = $overage/$num_chars; |
245
|
0
|
|
|
|
|
0
|
$limit = $condc/100*$em; |
246
|
0
|
0
|
|
|
|
0
|
if ($val > $limit) { $val = $limit; } |
|
0
|
|
|
|
|
0
|
|
247
|
0
|
|
|
|
|
0
|
$self->charspace(-$val); |
248
|
0
|
|
|
|
|
0
|
$overage -= $val*$num_chars; |
249
|
|
|
|
|
|
|
} |
250
|
|
|
|
|
|
|
# 3. nothing more to do than scale down with hscale() |
251
|
|
|
|
|
|
|
} else { |
252
|
|
|
|
|
|
|
# too narrow: need to expand it (usual case) |
253
|
4
|
|
|
|
|
9
|
$overage = -$overage; # working with positive value is easier |
254
|
|
|
|
|
|
|
# 1. add to interword space, up to $wordsp/100 $sp |
255
|
4
|
50
|
33
|
|
|
26
|
if ($overage > 0 && $num_spaces > 0 && $wordsp > 0) { |
|
|
|
33
|
|
|
|
|
256
|
4
|
|
|
|
|
8
|
$val = $overage/$num_spaces; |
257
|
4
|
|
|
|
|
8
|
$limit = $wordsp/100*$sp; |
258
|
4
|
100
|
|
|
|
9
|
if ($val > $limit) { $val = $limit; } |
|
1
|
|
|
|
|
2
|
|
259
|
4
|
|
|
|
|
12
|
$self->wordspace($val); |
260
|
4
|
|
|
|
|
11
|
$overage -= $val*$num_spaces; |
261
|
|
|
|
|
|
|
} |
262
|
|
|
|
|
|
|
# 2. add to intercharacter space, up to $charsp/100 $em |
263
|
4
|
50
|
66
|
|
|
18
|
if ($overage > 0 && $num_chars > 0 && $charsp > 0) { |
|
|
|
66
|
|
|
|
|
264
|
1
|
|
|
|
|
2
|
$val = $overage/$num_chars; |
265
|
1
|
|
|
|
|
4
|
$limit = $charsp/100*$em; |
266
|
1
|
50
|
|
|
|
3
|
if ($val > $limit) { $val = $limit; } |
|
0
|
|
|
|
|
0
|
|
267
|
1
|
|
|
|
|
4
|
$self->charspace($val); |
268
|
1
|
|
|
|
|
3
|
$overage -= $val*$num_chars; |
269
|
|
|
|
|
|
|
} |
270
|
|
|
|
|
|
|
# 3. add to interword space, up to $wordspa/100 $sp additional |
271
|
4
|
0
|
33
|
|
|
13
|
if ($overage > 0 && $num_spaces > 0 && $wordspa > 0) { |
|
|
|
33
|
|
|
|
|
272
|
0
|
|
|
|
|
0
|
$val = $overage/$num_spaces; |
273
|
0
|
|
|
|
|
0
|
$limit = $wordspa/100*$sp; |
274
|
0
|
0
|
|
|
|
0
|
if ($val > $limit) { $val = $limit; } |
|
0
|
|
|
|
|
0
|
|
275
|
0
|
|
|
|
|
0
|
$self->wordspace($val+$self->wordspace()); |
276
|
0
|
|
|
|
|
0
|
$overage -= $val*$num_spaces; |
277
|
|
|
|
|
|
|
} |
278
|
|
|
|
|
|
|
# 4. add to intercharacter space, up to $charspa/100 $em additional |
279
|
4
|
0
|
33
|
|
|
11
|
if ($overage > 0 && $num_chars > 0 && $charspa > 0) { |
|
|
|
33
|
|
|
|
|
280
|
0
|
|
|
|
|
0
|
$val = $overage/$num_chars; |
281
|
0
|
|
|
|
|
0
|
$limit = $charspa/100*$em; |
282
|
0
|
0
|
|
|
|
0
|
if ($val > $limit) { $val = $limit; } |
|
0
|
|
|
|
|
0
|
|
283
|
0
|
|
|
|
|
0
|
$self->charspace($val+$self->charspace()); |
284
|
0
|
|
|
|
|
0
|
$overage -= $val*$num_chars; |
285
|
|
|
|
|
|
|
} |
286
|
|
|
|
|
|
|
# 5. nothing more to do than scale up with hscale() |
287
|
|
|
|
|
|
|
} |
288
|
|
|
|
|
|
|
|
289
|
|
|
|
|
|
|
# last ditch effort to fill the line: use hscale() |
290
|
|
|
|
|
|
|
# temporarily resets hscale to expand width of line to match $width |
291
|
|
|
|
|
|
|
# wordspace and charspace are already (temporarily) at max/min |
292
|
4
|
50
|
|
|
|
11
|
if ($overage > 0.1) { |
293
|
0
|
|
|
|
|
0
|
$self->hscale(100*($width/$self->advancewidth($text, %opts))); |
294
|
|
|
|
|
|
|
} |
295
|
|
|
|
|
|
|
|
296
|
|
|
|
|
|
|
} # original $overage was not near 0 |
297
|
|
|
|
|
|
|
# do the output, with wordspace, charspace, and possiby hscale changed |
298
|
4
|
|
|
|
|
23
|
$self->text($text, %opts); |
299
|
|
|
|
|
|
|
|
300
|
|
|
|
|
|
|
# restore settings |
301
|
4
|
|
|
|
|
16
|
$self->hscale($hs); $self->wordspace($ws); $self->charspace($cs); |
|
4
|
|
|
|
|
13
|
|
|
4
|
|
|
|
|
13
|
|
302
|
|
|
|
|
|
|
|
303
|
4
|
|
|
|
|
19
|
return $width; |
304
|
|
|
|
|
|
|
} |
305
|
|
|
|
|
|
|
|
306
|
|
|
|
|
|
|
=head2 Multiple Lines from a String |
307
|
|
|
|
|
|
|
|
308
|
|
|
|
|
|
|
The string is split at regular blanks (spaces), x20, to find the longest |
309
|
|
|
|
|
|
|
substring that will fit the C<$width>. |
310
|
|
|
|
|
|
|
If a single word is longer than C<$width>, it will overflow. |
311
|
|
|
|
|
|
|
To stay strictly within the desired bounds, set the option |
312
|
|
|
|
|
|
|
C=>0 to disallow spillover. |
313
|
|
|
|
|
|
|
|
314
|
|
|
|
|
|
|
=head3 Hyphenation |
315
|
|
|
|
|
|
|
|
316
|
|
|
|
|
|
|
If hyphenation is enabled, those methods which split up a string into multiple |
317
|
|
|
|
|
|
|
lines (the "text fill", paragraph, and section methods) will attempt to split |
318
|
|
|
|
|
|
|
up the word that overflows the line, in order to pack the text even more |
319
|
|
|
|
|
|
|
tightly ("greedy" line splitting). There are a number of controls over where a |
320
|
|
|
|
|
|
|
word may be split, but note that there is nothing language-specific (i.e., |
321
|
|
|
|
|
|
|
following a given language's rules for where a word may be split). This is left |
322
|
|
|
|
|
|
|
to other packages. |
323
|
|
|
|
|
|
|
|
324
|
|
|
|
|
|
|
There are hard coded minimums of 2 letters before the split, and 2 letters after |
325
|
|
|
|
|
|
|
the split. See C. Note that neither hyphenation nor simple |
326
|
|
|
|
|
|
|
line splitting makes any attempt to prevent widows and orphans, prevent |
327
|
|
|
|
|
|
|
splitting of the last word in a column or page, or otherwise engage in |
328
|
|
|
|
|
|
|
I. |
329
|
|
|
|
|
|
|
|
330
|
|
|
|
|
|
|
=over |
331
|
|
|
|
|
|
|
|
332
|
|
|
|
|
|
|
=item 'hyphenate' => value |
333
|
|
|
|
|
|
|
|
334
|
|
|
|
|
|
|
0: no hyphenation (B), 1: do basic hyphenation. Always allows |
335
|
|
|
|
|
|
|
splitting at a soft hyphen (\xAD). Unicode hyphen (U+2010) and non-splitting |
336
|
|
|
|
|
|
|
hyphen (U+2011) are ignored as split points. |
337
|
|
|
|
|
|
|
|
338
|
|
|
|
|
|
|
=item 'spHH' => value |
339
|
|
|
|
|
|
|
|
340
|
|
|
|
|
|
|
0: do I split at a hard hyphen (x\2D), 1: I (B) |
341
|
|
|
|
|
|
|
|
342
|
|
|
|
|
|
|
=item 'spOP' => value |
343
|
|
|
|
|
|
|
|
344
|
|
|
|
|
|
|
0: do I split after most punctuation, 1: I (B) |
345
|
|
|
|
|
|
|
|
346
|
|
|
|
|
|
|
=item 'spDR' => value |
347
|
|
|
|
|
|
|
|
348
|
|
|
|
|
|
|
0: do I split after a run of one or more digits, 1: I (B) |
349
|
|
|
|
|
|
|
|
350
|
|
|
|
|
|
|
=item 'spLR' => value |
351
|
|
|
|
|
|
|
|
352
|
|
|
|
|
|
|
0: do I split after a run of one or more ASCII letters, 1: I (B) |
353
|
|
|
|
|
|
|
|
354
|
|
|
|
|
|
|
=item 'spCC' => value |
355
|
|
|
|
|
|
|
|
356
|
|
|
|
|
|
|
0: do I split in camelCase between a lowercase letter and an |
357
|
|
|
|
|
|
|
uppercase letter, 1: I (B) |
358
|
|
|
|
|
|
|
|
359
|
|
|
|
|
|
|
=back |
360
|
|
|
|
|
|
|
|
361
|
|
|
|
|
|
|
=head3 Methods |
362
|
|
|
|
|
|
|
|
363
|
|
|
|
|
|
|
=cut |
364
|
|
|
|
|
|
|
|
365
|
|
|
|
|
|
|
# splits input text (on spaces) into words, glues them back together until |
366
|
|
|
|
|
|
|
# have filled desired (available) width. return the new line and remaining |
367
|
|
|
|
|
|
|
# text. runs of spaces should be preserved. if the first word of a line does |
368
|
|
|
|
|
|
|
# not fit within the alloted space, and cannot be split short enough, just |
369
|
|
|
|
|
|
|
# accept the overflow. |
370
|
|
|
|
|
|
|
sub _text_fill_line { |
371
|
20
|
|
|
20
|
|
56
|
my ($self, $text, $width, $over, %opts) = @_; |
372
|
|
|
|
|
|
|
# copy dashed option names to the preferred undashed names |
373
|
20
|
50
|
33
|
|
|
53
|
if (defined $opts{'-hyphenate'} && !defined $opts{'hyphenate'}) { $opts{'hyphenate'} = delete($opts{'-hyphenate'}); } |
|
0
|
|
|
|
|
0
|
|
374
|
20
|
50
|
33
|
|
|
50
|
if (defined $opts{'-lang'} && !defined $opts{'lang'}) { $opts{'lang'} = delete($opts{'-lang'}); } |
|
0
|
|
|
|
|
0
|
|
375
|
20
|
50
|
33
|
|
|
46
|
if (defined $opts{'-nosplit'} && !defined $opts{'nosplit'}) { $opts{'nosplit'} = delete($opts{'-nosplit'}); } |
|
0
|
|
|
|
|
0
|
|
376
|
|
|
|
|
|
|
|
377
|
|
|
|
|
|
|
# options of interest |
378
|
20
|
50
|
|
|
|
241
|
my $hyphenate = defined($opts{'hyphenate'})? $opts{'hyphenate'}: 0; # default off |
379
|
|
|
|
|
|
|
#my $lang = defined($opts{'lang'})? $opts{'lang'}: 'en'; # English rules by default |
380
|
20
|
|
|
|
|
154
|
my $lang = 'basic'; |
381
|
|
|
|
|
|
|
#my $nosplit = defined($opts{'nosplit'})? $opts{'nosplit'}: ''; # indexes NOT to split at, given |
382
|
|
|
|
|
|
|
# as string of integers |
383
|
|
|
|
|
|
|
# my @noSplit = split /[,\s]+/, $nosplit; # normally empty array |
384
|
|
|
|
|
|
|
# 1. indexes start at 0 (split after character N not permitted) |
385
|
|
|
|
|
|
|
# 2. SHYs (soft hyphens) should be skipped |
386
|
|
|
|
|
|
|
# 3. need to map entire string's indexes to each word under |
387
|
|
|
|
|
|
|
# consideration for splitting (hyphenation) |
388
|
|
|
|
|
|
|
|
389
|
|
|
|
|
|
|
# TBD should we consider any non-ASCII spaces? |
390
|
|
|
|
|
|
|
# don't split on non-breaking space (required blank). |
391
|
20
|
|
|
|
|
101
|
my @txt = split(/\x20/, $text); |
392
|
20
|
|
|
|
|
33
|
my @line = (); |
393
|
20
|
|
|
|
|
29
|
local $"; # intent is that reset of separator ($") is local to block |
394
|
20
|
|
|
|
|
39
|
$"=' '; ## no critic |
395
|
20
|
|
|
|
|
25
|
my $lastWord = ''; # the one that didn't quite fit |
396
|
20
|
|
|
|
|
28
|
my $overflowed = 0; |
397
|
|
|
|
|
|
|
|
398
|
20
|
|
|
|
|
47
|
while (@txt) { |
399
|
|
|
|
|
|
|
# build up @line from @txt array until overfills line. |
400
|
|
|
|
|
|
|
# need to remove SHYs (soft hyphens) at this point. |
401
|
119
|
|
|
|
|
174
|
$lastWord = shift @txt; # preserve any SHYs in the word |
402
|
119
|
|
|
|
|
186
|
push @line, (_removeSHY($lastWord)); |
403
|
|
|
|
|
|
|
# one space between each element of line, like join(' ', @line) |
404
|
119
|
|
|
|
|
423
|
$overflowed = $self->advancewidth("@line", %opts) > $width; |
405
|
119
|
100
|
|
|
|
309
|
last if $overflowed; |
406
|
|
|
|
|
|
|
} |
407
|
|
|
|
|
|
|
# if overflowed, and overflow not allowed, remove the last word added, |
408
|
|
|
|
|
|
|
# unless single word in line and we're not going to attempt word splitting. |
409
|
20
|
100
|
66
|
|
|
62
|
if ($overflowed && !$over) { |
410
|
13
|
50
|
33
|
|
|
62
|
if ($hyphenate && @line == 1 || @line > 1) { |
|
|
|
33
|
|
|
|
|
411
|
13
|
|
|
|
|
17
|
pop @line; # discard last (or only) word |
412
|
13
|
|
|
|
|
34
|
unshift @txt,$lastWord; # restore with SHYs intact |
413
|
|
|
|
|
|
|
} |
414
|
|
|
|
|
|
|
# if not hyphenating (splitting words), just leave oversized |
415
|
|
|
|
|
|
|
# single-word line. if hyphenating, could have empty @line. |
416
|
|
|
|
|
|
|
} |
417
|
|
|
|
|
|
|
|
418
|
20
|
|
|
|
|
52
|
my $Txt = "@txt"; # remaining text to put on next line |
419
|
20
|
|
|
|
|
42
|
my $Line = "@line"; # line that fits, but not yet with any split word |
420
|
|
|
|
|
|
|
# may be empty if first word in line overflows |
421
|
|
|
|
|
|
|
|
422
|
|
|
|
|
|
|
# if we try to hyphenate, try splitting up that last word that |
423
|
|
|
|
|
|
|
# broke the camel's back. otherwise, will return $Line and $Txt as is. |
424
|
20
|
50
|
33
|
|
|
48
|
if ($hyphenate && $overflowed) { |
425
|
0
|
|
|
|
|
0
|
my $space; |
426
|
|
|
|
|
|
|
# @line is current whole word list of line, does NOT overflow because |
427
|
|
|
|
|
|
|
# $lastWord was removed. it may be empty if the first word tried was |
428
|
|
|
|
|
|
|
# too long. @txt is whole word list of the remaining words to be output |
429
|
|
|
|
|
|
|
# (includes $lastWord as its first word). |
430
|
|
|
|
|
|
|
# |
431
|
|
|
|
|
|
|
# we want to try splitting $lastWord into short enough left fragment |
432
|
|
|
|
|
|
|
# (with right fragment remainder as first word of next line). if we |
433
|
|
|
|
|
|
|
# fail to do so, just leave whole word as first word of next line, IF |
434
|
|
|
|
|
|
|
# @line was not empty. if @line was empty, accept the overflow and |
435
|
|
|
|
|
|
|
# output $lastWord as @line and remove it from @txt. |
436
|
0
|
0
|
|
|
|
0
|
if (@line) { |
437
|
|
|
|
|
|
|
# line not empty. $space is width for word fragment, not |
438
|
|
|
|
|
|
|
# including blank after previous last word of @line. |
439
|
0
|
|
|
|
|
0
|
$space = $width - $self->advancewidth("@line ", %opts); |
440
|
|
|
|
|
|
|
} else { |
441
|
|
|
|
|
|
|
# line empty (first word too long, and we can try hyphenating). |
442
|
|
|
|
|
|
|
# $space is entire $width available for left fragment. |
443
|
0
|
|
|
|
|
0
|
$space = $width; |
444
|
|
|
|
|
|
|
} |
445
|
|
|
|
|
|
|
|
446
|
0
|
0
|
|
|
|
0
|
if ($space > 0) { |
447
|
0
|
|
|
|
|
0
|
my ($wordLeft, $wordRight); |
448
|
|
|
|
|
|
|
# @line is word(s) (if any) currently fitting within $width. |
449
|
|
|
|
|
|
|
# @txt is remaining words unused in this line. $lastWord is first |
450
|
|
|
|
|
|
|
# word of @txt. $space is width remaining to fill in line. |
451
|
0
|
|
|
|
|
0
|
$wordLeft = ''; $wordRight = $lastWord; # fallbacks |
|
0
|
|
|
|
|
0
|
|
452
|
|
|
|
|
|
|
|
453
|
|
|
|
|
|
|
# if there is an error in Hyphenate_$lang, the message may be |
454
|
|
|
|
|
|
|
# that the splitWord() function can't be found. debug errors by |
455
|
|
|
|
|
|
|
# hard coding the require and splitWord() calls. |
456
|
|
|
|
|
|
|
|
457
|
|
|
|
|
|
|
## test that Hyphenate_$lang exists. if not, use Hyphenate_en |
458
|
|
|
|
|
|
|
## TBD: if Hyphenate_$lang is not found, should we fall back to |
459
|
|
|
|
|
|
|
## English (en) rules, or turn off hyphenation, or do limited |
460
|
|
|
|
|
|
|
## hyphenation (nothing language-specific)? |
461
|
|
|
|
|
|
|
# only Hyphenate_basic. leave language support to other packages |
462
|
0
|
|
|
|
|
0
|
require PDF::Builder::Content::Hyphenate_basic; |
463
|
|
|
|
|
|
|
#eval "require PDF::Builder::Content::Hyphenate_$lang"; |
464
|
|
|
|
|
|
|
#if ($@) { |
465
|
|
|
|
|
|
|
#print "something went wrong with require eval: $@\n"; |
466
|
|
|
|
|
|
|
#$lang = 'en'; # perlmonks 27443 fall back to English |
467
|
|
|
|
|
|
|
#require PDF::Builder::Content::Hyphenate_en; |
468
|
|
|
|
|
|
|
#} |
469
|
0
|
|
|
|
|
0
|
($wordLeft,$wordRight) = PDF::Builder::Content::Hyphenate_basic::splitWord($self, $lastWord, $space, %opts); |
470
|
|
|
|
|
|
|
#eval '($wordLeft,$wordRight) = PDF::Builder::Content::Hyphenate_'.$lang.'::splitWord($self, "$lastWord", $space, %opts)'; |
471
|
0
|
0
|
|
|
|
0
|
if ($@) { print "something went wrong with eval: $@\n"; } |
|
0
|
|
|
|
|
0
|
|
472
|
|
|
|
|
|
|
|
473
|
|
|
|
|
|
|
# $wordLeft is left fragment of $lastWord that fits in $space. |
474
|
|
|
|
|
|
|
# it might be empty '' if couldn't get a small enough piece. it |
475
|
|
|
|
|
|
|
# includes a hyphen, but no leading space, and can be added to |
476
|
|
|
|
|
|
|
# @line. |
477
|
|
|
|
|
|
|
# $wordRight is the remainder of $lastWord (right fragment) that |
478
|
|
|
|
|
|
|
# didn't fit. it might be the entire $lastWord. it shouldn't be |
479
|
|
|
|
|
|
|
# empty, since the whole point of the exercise is that $lastWord |
480
|
|
|
|
|
|
|
# didn't fit in the remaining space. it will replace the first |
481
|
|
|
|
|
|
|
# element of @txt (there should be at least one). |
482
|
|
|
|
|
|
|
|
483
|
|
|
|
|
|
|
# see if have a small enough left fragment of $lastWord to append |
484
|
|
|
|
|
|
|
# to @line. neither left nor right Word should have full $lastWord, |
485
|
|
|
|
|
|
|
# and both cannot be empty. it is highly unlikely that $wordLeft |
486
|
|
|
|
|
|
|
# will be the full $lastWord, but quite possible that it is empty |
487
|
|
|
|
|
|
|
# and $wordRight is $lastWord. |
488
|
|
|
|
|
|
|
|
489
|
0
|
0
|
|
|
|
0
|
if (!@line) { |
490
|
|
|
|
|
|
|
# special case of empty line. if $wordLeft is empty and |
491
|
|
|
|
|
|
|
# $wordRight is presumably the entire $lastWord, use $wordRight |
492
|
|
|
|
|
|
|
# for the line and remove it ($lastWord) from @txt. |
493
|
0
|
0
|
|
|
|
0
|
if ($wordLeft eq '') { |
494
|
0
|
|
|
|
|
0
|
@line = ($wordRight); # probably overflows $width. |
495
|
0
|
|
|
|
|
0
|
shift @txt; # remove $lastWord from @txt. |
496
|
|
|
|
|
|
|
} else { |
497
|
|
|
|
|
|
|
# $wordLeft fragment fits $width. |
498
|
0
|
|
|
|
|
0
|
@line = ($wordLeft); # should fit $width. |
499
|
0
|
|
|
|
|
0
|
shift @txt; # replace first element of @txt ($lastWord) |
500
|
0
|
|
|
|
|
0
|
unshift @txt, $wordRight; |
501
|
|
|
|
|
|
|
} |
502
|
|
|
|
|
|
|
} else { |
503
|
|
|
|
|
|
|
# usual case of some words already in @line. if $wordLeft is |
504
|
|
|
|
|
|
|
# empty and $wordRight is entire $lastWord, we're done here. |
505
|
|
|
|
|
|
|
# if $wordLeft has something, append it to line and replace |
506
|
|
|
|
|
|
|
# first element of @txt with $wordRight (unless empty, which |
507
|
|
|
|
|
|
|
# shouldn't happen). |
508
|
0
|
0
|
|
|
|
0
|
if ($wordLeft eq '') { |
509
|
|
|
|
|
|
|
# was unable to split $lastWord into short enough fragment. |
510
|
|
|
|
|
|
|
# leave @line (already has words) and @txt alone. |
511
|
|
|
|
|
|
|
} else { |
512
|
0
|
|
|
|
|
0
|
push @line, ($wordLeft); # should fit $space. |
513
|
0
|
|
|
|
|
0
|
shift @txt; # replace first element of @txt (was $lastWord) |
514
|
0
|
0
|
|
|
|
0
|
unshift @txt, $wordRight if $wordRight ne ''; |
515
|
|
|
|
|
|
|
} |
516
|
|
|
|
|
|
|
} |
517
|
|
|
|
|
|
|
|
518
|
|
|
|
|
|
|
# rebuild $Line and $Txt, in case they were altered. |
519
|
0
|
|
|
|
|
0
|
$Txt = "@txt"; |
520
|
0
|
|
|
|
|
0
|
$Line = "@line"; |
521
|
|
|
|
|
|
|
} # there was $space available to try to fit a word fragment |
522
|
|
|
|
|
|
|
} # we had an overflow to clean up, and hyphenation (word splitting) OK |
523
|
20
|
|
|
|
|
72
|
return ($Line, $Txt); |
524
|
|
|
|
|
|
|
} |
525
|
|
|
|
|
|
|
|
526
|
|
|
|
|
|
|
# remove soft hyphens (SHYs) from a word. assume is always #173 (good for |
527
|
|
|
|
|
|
|
# Latin-1, CP-1252, UTF-8; might not work for some encodings) TBD |
528
|
|
|
|
|
|
|
sub _removeSHY { |
529
|
119
|
|
|
119
|
|
171
|
my ($word) = @_; |
530
|
|
|
|
|
|
|
|
531
|
119
|
|
|
|
|
229
|
my @chars = split //, $word; |
532
|
119
|
|
|
|
|
151
|
my $out = ''; |
533
|
119
|
|
|
|
|
174
|
foreach (@chars) { |
534
|
357
|
50
|
|
|
|
477
|
next if ord($_) == 173; |
535
|
357
|
|
|
|
|
434
|
$out .= $_; |
536
|
|
|
|
|
|
|
} |
537
|
119
|
|
|
|
|
230
|
return $out; |
538
|
|
|
|
|
|
|
} |
539
|
|
|
|
|
|
|
|
540
|
|
|
|
|
|
|
=over |
541
|
|
|
|
|
|
|
|
542
|
|
|
|
|
|
|
=item ($width, $leftover) = $content->text_fill_left($string, $width, %opts) |
543
|
|
|
|
|
|
|
|
544
|
|
|
|
|
|
|
Fill a line of 'width' with as much text as will fit, |
545
|
|
|
|
|
|
|
and outputs it left justified. |
546
|
|
|
|
|
|
|
The width actually used, and the leftover text (that didn't fit), |
547
|
|
|
|
|
|
|
are B. |
548
|
|
|
|
|
|
|
|
549
|
|
|
|
|
|
|
=item ($width, $leftover) = $content->text_fill($string, $width, %opts) |
550
|
|
|
|
|
|
|
|
551
|
|
|
|
|
|
|
Alias for text_fill_left(). |
552
|
|
|
|
|
|
|
|
553
|
|
|
|
|
|
|
=back |
554
|
|
|
|
|
|
|
|
555
|
|
|
|
|
|
|
=cut |
556
|
|
|
|
|
|
|
|
557
|
|
|
|
|
|
|
sub text_fill_left { |
558
|
10
|
|
|
10
|
1
|
30
|
my ($self, $text, $width, %opts) = @_; |
559
|
|
|
|
|
|
|
# copy dashed option names to preferred undashed names |
560
|
10
|
50
|
33
|
|
|
38
|
if (defined $opts{'-spillover'} && !defined $opts{'spillover'}) { $opts{'spillover'} = delete($opts{'-spillover'}); } |
|
10
|
|
|
|
|
18
|
|
561
|
|
|
|
|
|
|
|
562
|
10
|
|
33
|
|
|
32
|
my $over = (not(defined($opts{'spillover'}) and $opts{'spillover'} == 0)); |
563
|
10
|
|
|
|
|
30
|
my ($line, $ret) = $self->_text_fill_line($text, $width, $over, %opts); |
564
|
10
|
|
|
|
|
34
|
$width = $self->text($line, %opts); |
565
|
10
|
|
|
|
|
33
|
return ($width, $ret); |
566
|
|
|
|
|
|
|
} |
567
|
|
|
|
|
|
|
|
568
|
|
|
|
|
|
|
sub text_fill { |
569
|
0
|
|
|
0
|
1
|
0
|
my $self = shift; |
570
|
0
|
|
|
|
|
0
|
return $self->text_fill_left(@_); |
571
|
|
|
|
|
|
|
} |
572
|
|
|
|
|
|
|
|
573
|
|
|
|
|
|
|
=over |
574
|
|
|
|
|
|
|
|
575
|
|
|
|
|
|
|
=item ($width, $leftover) = $content->text_fill_center($string, $width, %opts) |
576
|
|
|
|
|
|
|
|
577
|
|
|
|
|
|
|
Fill a line of 'width' with as much text as will fit, |
578
|
|
|
|
|
|
|
and outputs it centered. |
579
|
|
|
|
|
|
|
The width actually used, and the leftover text (that didn't fit), |
580
|
|
|
|
|
|
|
are B. |
581
|
|
|
|
|
|
|
|
582
|
|
|
|
|
|
|
=back |
583
|
|
|
|
|
|
|
|
584
|
|
|
|
|
|
|
=cut |
585
|
|
|
|
|
|
|
|
586
|
|
|
|
|
|
|
sub text_fill_center { |
587
|
2
|
|
|
2
|
1
|
6
|
my ($self, $text, $width, %opts) = @_; |
588
|
|
|
|
|
|
|
# copy dashed option names to preferred undashed names |
589
|
2
|
50
|
33
|
|
|
11
|
if (defined $opts{'-spillover'} && !defined $opts{'spillover'}) { $opts{'spillover'} = delete($opts{'-spillover'}); } |
|
2
|
|
|
|
|
5
|
|
590
|
|
|
|
|
|
|
|
591
|
2
|
|
33
|
|
|
8
|
my $over = (not(defined($opts{'spillover'}) and $opts{'spillover'} == 0)); |
592
|
2
|
|
|
|
|
8
|
my ($line, $ret) = $self->_text_fill_line($text, $width, $over, %opts); |
593
|
2
|
|
|
|
|
10
|
$width = $self->text_center($line, %opts); |
594
|
2
|
|
|
|
|
7
|
return ($width, $ret); |
595
|
|
|
|
|
|
|
} |
596
|
|
|
|
|
|
|
|
597
|
|
|
|
|
|
|
=over |
598
|
|
|
|
|
|
|
|
599
|
|
|
|
|
|
|
=item ($width, $leftover) = $content->text_fill_right($string, $width, %opts) |
600
|
|
|
|
|
|
|
|
601
|
|
|
|
|
|
|
Fill a line of 'width' with as much text as will fit, |
602
|
|
|
|
|
|
|
and outputs it right justified. |
603
|
|
|
|
|
|
|
The width actually used, and the leftover text (that didn't fit), |
604
|
|
|
|
|
|
|
are B. |
605
|
|
|
|
|
|
|
|
606
|
|
|
|
|
|
|
=back |
607
|
|
|
|
|
|
|
|
608
|
|
|
|
|
|
|
=cut |
609
|
|
|
|
|
|
|
|
610
|
|
|
|
|
|
|
sub text_fill_right { |
611
|
2
|
|
|
2
|
1
|
8
|
my ($self, $text, $width, %opts) = @_; |
612
|
|
|
|
|
|
|
# copy dashed option names to preferred undashed names |
613
|
2
|
50
|
33
|
|
|
8
|
if (defined $opts{'-spillover'} && !defined $opts{'spillover'}) { $opts{'spillover'} = delete($opts{'-spillover'}); } |
|
2
|
|
|
|
|
5
|
|
614
|
|
|
|
|
|
|
|
615
|
2
|
|
33
|
|
|
12
|
my $over = (not(defined($opts{'spillover'}) and $opts{'spillover'} == 0)); |
616
|
2
|
|
|
|
|
7
|
my ($line, $ret) = $self->_text_fill_line($text, $width, $over, %opts); |
617
|
2
|
|
|
|
|
10
|
$width = $self->text_right($line, %opts); |
618
|
2
|
|
|
|
|
7
|
return ($width, $ret); |
619
|
|
|
|
|
|
|
} |
620
|
|
|
|
|
|
|
|
621
|
|
|
|
|
|
|
=over |
622
|
|
|
|
|
|
|
|
623
|
|
|
|
|
|
|
=item ($width, $leftover) = $content->text_fill_justified($string, $width, %opts) |
624
|
|
|
|
|
|
|
|
625
|
|
|
|
|
|
|
Fill a line of 'width' with as much text as will fit, |
626
|
|
|
|
|
|
|
and outputs it fully justified (stretched or condensed). |
627
|
|
|
|
|
|
|
The width actually used, and the leftover text (that didn't fit), |
628
|
|
|
|
|
|
|
are B. |
629
|
|
|
|
|
|
|
|
630
|
|
|
|
|
|
|
Note that the entire line is fit to the available |
631
|
|
|
|
|
|
|
width via a call to C. |
632
|
|
|
|
|
|
|
See C for options to control stretch and condense. |
633
|
|
|
|
|
|
|
The last line is unjustified (normal size) and left aligned by default, |
634
|
|
|
|
|
|
|
although the option |
635
|
|
|
|
|
|
|
|
636
|
|
|
|
|
|
|
B |
637
|
|
|
|
|
|
|
|
638
|
|
|
|
|
|
|
=over |
639
|
|
|
|
|
|
|
|
640
|
|
|
|
|
|
|
=item 'last_align' => place |
641
|
|
|
|
|
|
|
|
642
|
|
|
|
|
|
|
where place is 'left' (default), 'center', or 'right' (may be shortened to |
643
|
|
|
|
|
|
|
first letter) allows you to specify the alignment of the last line output. |
644
|
|
|
|
|
|
|
|
645
|
|
|
|
|
|
|
=back |
646
|
|
|
|
|
|
|
|
647
|
|
|
|
|
|
|
=back |
648
|
|
|
|
|
|
|
|
649
|
|
|
|
|
|
|
=cut |
650
|
|
|
|
|
|
|
|
651
|
|
|
|
|
|
|
sub text_fill_justified { |
652
|
6
|
|
|
6
|
1
|
21
|
my ($self, $text, $width, %opts) = @_; |
653
|
|
|
|
|
|
|
# copy dashed option names to preferred undashed names |
654
|
6
|
100
|
66
|
|
|
24
|
if (defined $opts{'-last_align'} && !defined $opts{'last_align'}) { $opts{'last_align'} = delete($opts{'-last_align'}); } |
|
4
|
|
|
|
|
13
|
|
655
|
6
|
50
|
33
|
|
|
26
|
if (defined $opts{'-spillover'} && !defined $opts{'spillover'}) { $opts{'spillover'} = delete($opts{'-spillover'}); } |
|
6
|
|
|
|
|
13
|
|
656
|
|
|
|
|
|
|
|
657
|
6
|
|
|
|
|
12
|
my $align = 'l'; # default left align last line |
658
|
6
|
100
|
|
|
|
10
|
if (defined($opts{'last_align'})) { |
659
|
4
|
50
|
|
|
|
23
|
if ($opts{'last_align'} =~ m/^l/i) { $align = 'l'; } |
|
0
|
100
|
|
|
|
0
|
|
|
|
50
|
|
|
|
|
|
660
|
2
|
|
|
|
|
3
|
elsif ($opts{'last_align'} =~ m/^c/i) { $align = 'c'; } |
661
|
2
|
|
|
|
|
5
|
elsif ($opts{'last_align'} =~ m/^r/i) { $align = 'r'; } |
662
|
0
|
|
|
|
|
0
|
else { warn "Unknown last_align for justified fill, 'left' used\n"; } |
663
|
|
|
|
|
|
|
} |
664
|
|
|
|
|
|
|
|
665
|
6
|
|
33
|
|
|
20
|
my $over = (not(defined($opts{'spillover'}) and $opts{'spillover'} == 0)); |
666
|
6
|
|
|
|
|
25
|
my ($line, $ret) = $self->_text_fill_line($text, $width, $over, %opts); |
667
|
|
|
|
|
|
|
# if last line, use $align (don't justify) |
668
|
6
|
100
|
|
|
|
21
|
if ($ret eq '') { |
669
|
3
|
|
|
|
|
10
|
my $lw = $self->advancewidth($line, %opts); |
670
|
3
|
100
|
|
|
|
14
|
if ($align eq 'l') { |
|
|
100
|
|
|
|
|
|
671
|
1
|
|
|
|
|
4
|
$width = $self->text($line, %opts); |
672
|
|
|
|
|
|
|
} elsif ($align eq 'c') { |
673
|
1
|
|
|
|
|
6
|
$width = $self->text($line, 'indent' => ($width-$lw)/2, %opts); |
674
|
|
|
|
|
|
|
} else { # 'r' |
675
|
1
|
|
|
|
|
6
|
$width = $self->text($line, 'indent' => ($width-$lw), %opts); |
676
|
|
|
|
|
|
|
} |
677
|
|
|
|
|
|
|
} else { |
678
|
3
|
|
|
|
|
12
|
$width = $self->text_justified($line, $width, %opts); |
679
|
|
|
|
|
|
|
} |
680
|
6
|
|
|
|
|
22
|
return ($width, $ret); |
681
|
|
|
|
|
|
|
} |
682
|
|
|
|
|
|
|
|
683
|
|
|
|
|
|
|
=over |
684
|
|
|
|
|
|
|
|
685
|
|
|
|
|
|
|
=item ($overflow_text, $unused_height) = $txt->paragraph($text, $width,$height, $continue, %opts) |
686
|
|
|
|
|
|
|
|
687
|
|
|
|
|
|
|
=item $overflow_text = $txt->paragraph($text, $width,$height, $continue, %opts) |
688
|
|
|
|
|
|
|
|
689
|
|
|
|
|
|
|
Print a single string into a rectangular area on the page, of given width and |
690
|
|
|
|
|
|
|
maximum height. The baseline of the first (top) line is at the current text |
691
|
|
|
|
|
|
|
position. |
692
|
|
|
|
|
|
|
|
693
|
|
|
|
|
|
|
Apply the text within the rectangle and B any leftover text (if could |
694
|
|
|
|
|
|
|
not fit all of it within the rectangle). If called in an array context, the |
695
|
|
|
|
|
|
|
unused height is also B (may be 0 or negative if it just filled the |
696
|
|
|
|
|
|
|
rectangle). |
697
|
|
|
|
|
|
|
|
698
|
|
|
|
|
|
|
If C<$continue> is 1, the first line does B get special treatment for |
699
|
|
|
|
|
|
|
indenting or outdenting, because we're printing the continuation of the |
700
|
|
|
|
|
|
|
paragraph that was interrupted earlier. If it's 0, the first line may be |
701
|
|
|
|
|
|
|
indented or outdented. |
702
|
|
|
|
|
|
|
|
703
|
|
|
|
|
|
|
B |
704
|
|
|
|
|
|
|
|
705
|
|
|
|
|
|
|
=over |
706
|
|
|
|
|
|
|
|
707
|
|
|
|
|
|
|
=item 'pndnt' => $indent |
708
|
|
|
|
|
|
|
|
709
|
|
|
|
|
|
|
Give the amount of indent (positive) or outdent (negative, for "hanging") |
710
|
|
|
|
|
|
|
for paragraph first lines). This setting is ignored for centered text. |
711
|
|
|
|
|
|
|
|
712
|
|
|
|
|
|
|
=item 'align' => $choice |
713
|
|
|
|
|
|
|
|
714
|
|
|
|
|
|
|
C<$choice> is 'justified', 'right', 'center', 'left'; the default is 'left'. |
715
|
|
|
|
|
|
|
See C call for options to control how a line is expanded or |
716
|
|
|
|
|
|
|
condensed if C<$choice> is 'justified'. C<$choice> may be shortened to the |
717
|
|
|
|
|
|
|
first letter. |
718
|
|
|
|
|
|
|
|
719
|
|
|
|
|
|
|
=item 'last_align' => place |
720
|
|
|
|
|
|
|
|
721
|
|
|
|
|
|
|
where place is 'left' (default), 'center', or 'right' (may be shortened to |
722
|
|
|
|
|
|
|
first letter) allows you to specify the alignment of the last line output, |
723
|
|
|
|
|
|
|
but applies only when C is 'justified'. |
724
|
|
|
|
|
|
|
|
725
|
|
|
|
|
|
|
=item 'underline' => $distance |
726
|
|
|
|
|
|
|
|
727
|
|
|
|
|
|
|
=item 'underline' => [ $distance, $thickness, ... ] |
728
|
|
|
|
|
|
|
|
729
|
|
|
|
|
|
|
If a scalar, distance below baseline, |
730
|
|
|
|
|
|
|
else array reference with pairs of distance and line thickness. |
731
|
|
|
|
|
|
|
|
732
|
|
|
|
|
|
|
=item 'spillover' => $over |
733
|
|
|
|
|
|
|
|
734
|
|
|
|
|
|
|
Controls if words in a line which exceed the given width should be |
735
|
|
|
|
|
|
|
"spilled over" the bounds, or if a new line should be used for this word. |
736
|
|
|
|
|
|
|
|
737
|
|
|
|
|
|
|
C<$over> is 1 or 0, with the default 1 (spills over the width). |
738
|
|
|
|
|
|
|
|
739
|
|
|
|
|
|
|
=back |
740
|
|
|
|
|
|
|
|
741
|
|
|
|
|
|
|
B |
742
|
|
|
|
|
|
|
|
743
|
|
|
|
|
|
|
$txt->font($font,$fontsize); |
744
|
|
|
|
|
|
|
$txt->leading($leading); |
745
|
|
|
|
|
|
|
$txt->translate($x,$y); |
746
|
|
|
|
|
|
|
$overflow = $txt->paragraph( 'long paragraph here ...', |
747
|
|
|
|
|
|
|
$width, |
748
|
|
|
|
|
|
|
$y+$leading-$bottom_margin ); |
749
|
|
|
|
|
|
|
|
750
|
|
|
|
|
|
|
B if you need to change any text treatment I a paragraph |
751
|
|
|
|
|
|
|
(B or I text, for instance), this can not handle it. Only |
752
|
|
|
|
|
|
|
plain text (all the same font, size, etc.) can be typeset with C. |
753
|
|
|
|
|
|
|
Also, there is currently very limited line splitting (hyphenation) to better |
754
|
|
|
|
|
|
|
fit to a given width, and nothing is done for "widows and orphans". |
755
|
|
|
|
|
|
|
|
756
|
|
|
|
|
|
|
=back |
757
|
|
|
|
|
|
|
|
758
|
|
|
|
|
|
|
=cut |
759
|
|
|
|
|
|
|
|
760
|
|
|
|
|
|
|
# TBD for LTR languages, does indenting on left make sense for right justified? |
761
|
|
|
|
|
|
|
# TBD for bidi/RTL languages, should indenting be on right? |
762
|
|
|
|
|
|
|
|
763
|
|
|
|
|
|
|
sub paragraph { |
764
|
12
|
|
|
12
|
1
|
86
|
my ($self, $text, $width,$height, $continue, %opts) = @_; |
765
|
|
|
|
|
|
|
# copy dashed option names to preferred undashed names |
766
|
12
|
100
|
66
|
|
|
54
|
if (defined $opts{'-align'} && !defined $opts{'align'}) { $opts{'align'} = delete($opts{'-align'}); } |
|
5
|
|
|
|
|
14
|
|
767
|
12
|
50
|
33
|
|
|
39
|
if (defined $opts{'-pndnt'} && !defined $opts{'pndnt'}) { $opts{'pndnt'} = delete($opts{'-pndnt'}); } |
|
0
|
|
|
|
|
0
|
|
768
|
|
|
|
|
|
|
|
769
|
12
|
|
|
|
|
19
|
my @line = (); |
770
|
12
|
|
|
|
|
21
|
my $nwidth = 0; |
771
|
12
|
|
|
|
|
26
|
my $leading = $self->leading(); |
772
|
12
|
|
|
|
|
21
|
my $align = 'l'; # default left |
773
|
12
|
100
|
|
|
|
31
|
if (defined($opts{'align'})) { |
774
|
5
|
50
|
|
|
|
42
|
if ($opts{'align'} =~ /^l/i) { $align = 'l'; } |
|
0
|
100
|
|
|
|
0
|
|
|
|
100
|
|
|
|
|
|
|
|
50
|
|
|
|
|
|
775
|
1
|
|
|
|
|
2
|
elsif ($opts{'align'} =~ /^c/i) { $align = 'c'; } |
776
|
1
|
|
|
|
|
3
|
elsif ($opts{'align'} =~ /^r/i) { $align = 'r'; } |
777
|
3
|
|
|
|
|
7
|
elsif ($opts{'align'} =~ /^j/i) { $align = 'j'; } |
778
|
0
|
|
|
|
|
0
|
else { warn "Unknown align value for paragraph(), 'left' used\n"; } |
779
|
|
|
|
|
|
|
} # default stays at 'l' |
780
|
12
|
50
|
|
|
|
29
|
my $indent = defined($opts{'pndnt'})? $opts{'pndnt'}: 0; |
781
|
12
|
100
|
|
|
|
34
|
if ($align eq 'c') { $indent = 0; } # indent/outdent makes no sense centered |
|
1
|
|
|
|
|
2
|
|
782
|
12
|
|
|
|
|
21
|
my $first_line = !$continue; |
783
|
12
|
|
|
|
|
16
|
my $lw; |
784
|
12
|
|
|
|
|
43
|
my $em = $self->advancewidth('M'); |
785
|
|
|
|
|
|
|
|
786
|
12
|
|
|
|
|
34
|
while (length($text) > 0) { # more text to go... |
787
|
|
|
|
|
|
|
# indent == 0 (flush) all lines normal width |
788
|
|
|
|
|
|
|
# indent (>0) first line moved in on left, subsequent normal width |
789
|
|
|
|
|
|
|
# outdent (<0) first line is normal width, subsequent moved in on left |
790
|
20
|
|
|
|
|
28
|
$lw = $width; |
791
|
20
|
50
|
33
|
|
|
63
|
if ($indent > 0 && $first_line) { $lw -= $indent*$em; } |
|
0
|
|
|
|
|
0
|
|
792
|
20
|
50
|
33
|
|
|
46
|
if ($indent < 0 && !$first_line) { $lw += $indent*$em; } |
|
0
|
|
|
|
|
0
|
|
793
|
|
|
|
|
|
|
# now, need to indent (move line start) right for 'l' and 'j' |
794
|
20
|
0
|
0
|
|
|
48
|
if ($lw < $width && ($align eq 'l' || $align eq 'j')) { |
|
|
|
33
|
|
|
|
|
795
|
0
|
|
|
|
|
0
|
$self->cr($leading); # go UP one line |
796
|
0
|
|
|
|
|
0
|
$self->nl(88*abs($indent)); # come down to right line and move right |
797
|
|
|
|
|
|
|
} |
798
|
|
|
|
|
|
|
|
799
|
20
|
100
|
|
|
|
53
|
if ($align eq 'j') { |
|
|
100
|
|
|
|
|
|
|
|
100
|
|
|
|
|
|
800
|
6
|
|
|
|
|
26
|
($nwidth,$text) = $self->text_fill_justified($text, $lw, %opts); |
801
|
|
|
|
|
|
|
} elsif ($align eq 'r') { |
802
|
2
|
|
|
|
|
9
|
($nwidth,$text) = $self->text_fill_right($text, $lw, %opts); |
803
|
|
|
|
|
|
|
} elsif ($align eq 'c') { |
804
|
2
|
|
|
|
|
10
|
($nwidth,$text) = $self->text_fill_center($text, $lw, %opts); |
805
|
|
|
|
|
|
|
} else { # 'l' |
806
|
10
|
|
|
|
|
28
|
($nwidth,$text) = $self->text_fill_left($text, $lw, %opts); |
807
|
|
|
|
|
|
|
} |
808
|
|
|
|
|
|
|
|
809
|
20
|
|
|
|
|
75
|
$self->nl(); |
810
|
20
|
|
|
|
|
27
|
$first_line = 0; |
811
|
|
|
|
|
|
|
|
812
|
|
|
|
|
|
|
# bail out and just return remaining $text if run out of vertical space |
813
|
20
|
100
|
|
|
|
57
|
last if ($height -= $leading) < 0; |
814
|
|
|
|
|
|
|
} |
815
|
|
|
|
|
|
|
|
816
|
12
|
100
|
|
|
|
28
|
if (wantarray) { |
817
|
|
|
|
|
|
|
# paragraph() called in the context of returning an array |
818
|
6
|
|
|
|
|
18
|
return ($text, $height); |
819
|
|
|
|
|
|
|
} |
820
|
6
|
|
|
|
|
21
|
return $text; |
821
|
|
|
|
|
|
|
} |
822
|
|
|
|
|
|
|
|
823
|
|
|
|
|
|
|
=over |
824
|
|
|
|
|
|
|
|
825
|
|
|
|
|
|
|
=item ($overflow_text, $continue, $unused_height) = $txt->section($text, $width,$height, $continue, %opts) |
826
|
|
|
|
|
|
|
|
827
|
|
|
|
|
|
|
=item $overflow_text = $txt->section($text, $width,$height, $continue, %opts) |
828
|
|
|
|
|
|
|
|
829
|
|
|
|
|
|
|
The C<$text> contains a string with one or more paragraphs C<$width> wide, |
830
|
|
|
|
|
|
|
starting at the current text position, with a newline \n between each |
831
|
|
|
|
|
|
|
paragraph. Each paragraph is output (see C) until the C<$height> |
832
|
|
|
|
|
|
|
limit is met (a partial paragraph may be at the bottom). Whatever wasn't |
833
|
|
|
|
|
|
|
output, will be B. |
834
|
|
|
|
|
|
|
If called in an array context, the |
835
|
|
|
|
|
|
|
unused height and the paragraph "continue" flag are also B. |
836
|
|
|
|
|
|
|
|
837
|
|
|
|
|
|
|
C<$continue> is 0 for the first call of section(), and then use the value |
838
|
|
|
|
|
|
|
returned from the previous call (1 if a paragraph was cut in the middle) to |
839
|
|
|
|
|
|
|
prevent unwanted indenting or outdenting of the first line being printed. |
840
|
|
|
|
|
|
|
|
841
|
|
|
|
|
|
|
For compatibility with recent changes to PDF::API2, B is accepted |
842
|
|
|
|
|
|
|
as an I for C |
843
|
|
|
|
|
|
|
|
844
|
|
|
|
|
|
|
B |
845
|
|
|
|
|
|
|
|
846
|
|
|
|
|
|
|
=over |
847
|
|
|
|
|
|
|
|
848
|
|
|
|
|
|
|
=item 'pvgap' => $vertical |
849
|
|
|
|
|
|
|
|
850
|
|
|
|
|
|
|
Additional vertical space (unit: pt) between paragraphs (default 0). Note that this space |
851
|
|
|
|
|
|
|
will also be added after the last paragraph printed. |
852
|
|
|
|
|
|
|
|
853
|
|
|
|
|
|
|
=back |
854
|
|
|
|
|
|
|
|
855
|
|
|
|
|
|
|
See C for other C<%opts> you can use, such as C and C. |
856
|
|
|
|
|
|
|
|
857
|
|
|
|
|
|
|
=back |
858
|
|
|
|
|
|
|
|
859
|
|
|
|
|
|
|
=cut |
860
|
|
|
|
|
|
|
|
861
|
|
|
|
|
|
|
# alias for compatibility |
862
|
|
|
|
|
|
|
sub paragraphs { |
863
|
1
|
|
|
1
|
0
|
10
|
return section(@_); |
864
|
|
|
|
|
|
|
} |
865
|
|
|
|
|
|
|
|
866
|
|
|
|
|
|
|
sub section { |
867
|
2
|
|
|
2
|
1
|
13
|
my ($self, $text, $width,$height, $continue, %opts) = @_; |
868
|
|
|
|
|
|
|
# copy dashed option names to preferred undashed names |
869
|
2
|
50
|
33
|
|
|
10
|
if (defined $opts{'-pvgap'} && !defined $opts{'pvgap'}) { $opts{'pvgap'} = delete($opts{'-pvgap'}); } |
|
0
|
|
|
|
|
0
|
|
870
|
|
|
|
|
|
|
|
871
|
2
|
|
|
|
|
15
|
my $overflow = ''; # text to return if height fills up |
872
|
2
|
50
|
|
|
|
7
|
my $pvgap = defined($opts{'pvgap'})? $opts{'pvgap'}: 0; |
873
|
|
|
|
|
|
|
# $continue =0 if fresh paragraph, or =1 if continuing one cut in middle |
874
|
|
|
|
|
|
|
|
875
|
2
|
|
|
|
|
10
|
foreach my $para (split(/\n/, $text)) { |
876
|
|
|
|
|
|
|
# regardless of whether we've run out of space vertically, we will |
877
|
|
|
|
|
|
|
# loop through all the paragraphs requested |
878
|
|
|
|
|
|
|
|
879
|
|
|
|
|
|
|
# already seen inability to output more text? |
880
|
|
|
|
|
|
|
# just put unused text back together into the string |
881
|
|
|
|
|
|
|
# $continue should stay 1 |
882
|
6
|
50
|
|
|
|
15
|
if (length($overflow) > 0) { |
883
|
0
|
|
|
|
|
0
|
$overflow .= "\n" . $para; |
884
|
0
|
|
|
|
|
0
|
next; |
885
|
|
|
|
|
|
|
} |
886
|
6
|
|
|
|
|
18
|
($para, $height) = $self->paragraph($para, $width,$height, $continue, %opts); |
887
|
6
|
|
|
|
|
10
|
$continue = 0; |
888
|
6
|
100
|
|
|
|
16
|
if (length($para) > 0) { |
889
|
|
|
|
|
|
|
# we cut a paragraph in half. set flag that continuation doesn't |
890
|
|
|
|
|
|
|
# get indented/outdented |
891
|
2
|
|
|
|
|
5
|
$overflow .= $para; |
892
|
2
|
|
|
|
|
3
|
$continue = 1; |
893
|
|
|
|
|
|
|
} |
894
|
|
|
|
|
|
|
|
895
|
|
|
|
|
|
|
# inter-paragraph vertical space? |
896
|
|
|
|
|
|
|
# note that the last paragraph will also get the extra space after it |
897
|
6
|
50
|
66
|
|
|
18
|
if (length($para) == 0 && $pvgap != 0) { |
898
|
0
|
|
|
|
|
0
|
$self->cr(-$pvgap); |
899
|
0
|
|
|
|
|
0
|
$height -= $pvgap; |
900
|
|
|
|
|
|
|
} |
901
|
|
|
|
|
|
|
} |
902
|
|
|
|
|
|
|
|
903
|
2
|
50
|
|
|
|
8
|
if (wantarray) { |
904
|
|
|
|
|
|
|
# section() called in the context of returning an array |
905
|
0
|
|
|
|
|
0
|
return ($overflow, $continue, $height); |
906
|
|
|
|
|
|
|
} |
907
|
2
|
|
|
|
|
8
|
return $overflow; |
908
|
|
|
|
|
|
|
} |
909
|
|
|
|
|
|
|
|
910
|
|
|
|
|
|
|
=over |
911
|
|
|
|
|
|
|
|
912
|
|
|
|
|
|
|
=item $width = $txt->textlabel($x,$y, $font, $size, $text, %opts) |
913
|
|
|
|
|
|
|
|
914
|
|
|
|
|
|
|
Place a line of text at an arbitrary C<[$x,$y]> on the page, with various text |
915
|
|
|
|
|
|
|
settings (treatments) specified in the call. |
916
|
|
|
|
|
|
|
|
917
|
|
|
|
|
|
|
=over |
918
|
|
|
|
|
|
|
|
919
|
|
|
|
|
|
|
=item $font |
920
|
|
|
|
|
|
|
|
921
|
|
|
|
|
|
|
A previously created font. |
922
|
|
|
|
|
|
|
|
923
|
|
|
|
|
|
|
=item $size |
924
|
|
|
|
|
|
|
|
925
|
|
|
|
|
|
|
The font size (points). |
926
|
|
|
|
|
|
|
|
927
|
|
|
|
|
|
|
=item $text |
928
|
|
|
|
|
|
|
|
929
|
|
|
|
|
|
|
The text to be printed (a single line). |
930
|
|
|
|
|
|
|
|
931
|
|
|
|
|
|
|
=back |
932
|
|
|
|
|
|
|
|
933
|
|
|
|
|
|
|
B |
934
|
|
|
|
|
|
|
|
935
|
|
|
|
|
|
|
=over |
936
|
|
|
|
|
|
|
|
937
|
|
|
|
|
|
|
=item 'rotate' => $deg |
938
|
|
|
|
|
|
|
|
939
|
|
|
|
|
|
|
Rotate C<$deg> degrees counterclockwise from due East. |
940
|
|
|
|
|
|
|
|
941
|
|
|
|
|
|
|
=item 'color' => $cspec |
942
|
|
|
|
|
|
|
|
943
|
|
|
|
|
|
|
A color name or permitted spec, such as C<#CCE840>, for the character I. |
944
|
|
|
|
|
|
|
|
945
|
|
|
|
|
|
|
=item 'strokecolor' => $cspec |
946
|
|
|
|
|
|
|
|
947
|
|
|
|
|
|
|
A color name or permitted spec, such as C<#CCE840>, for the character I. |
948
|
|
|
|
|
|
|
|
949
|
|
|
|
|
|
|
=item 'charspace' => $cdist |
950
|
|
|
|
|
|
|
|
951
|
|
|
|
|
|
|
Additional distance between characters. |
952
|
|
|
|
|
|
|
|
953
|
|
|
|
|
|
|
=item 'wordspace' => $wdist |
954
|
|
|
|
|
|
|
|
955
|
|
|
|
|
|
|
Additional distance between words. |
956
|
|
|
|
|
|
|
|
957
|
|
|
|
|
|
|
=item 'hscale' => $hfactor |
958
|
|
|
|
|
|
|
|
959
|
|
|
|
|
|
|
Horizontal scaling mode (percentage of normal, default is 100). |
960
|
|
|
|
|
|
|
|
961
|
|
|
|
|
|
|
=item 'render' => $mode |
962
|
|
|
|
|
|
|
|
963
|
|
|
|
|
|
|
Character rendering mode (outline only, fill only, etc.). See C call. |
964
|
|
|
|
|
|
|
|
965
|
|
|
|
|
|
|
=item 'left' => 1 |
966
|
|
|
|
|
|
|
|
967
|
|
|
|
|
|
|
Left align on the given point. This is the default. |
968
|
|
|
|
|
|
|
|
969
|
|
|
|
|
|
|
=item 'center' => 1 |
970
|
|
|
|
|
|
|
|
971
|
|
|
|
|
|
|
Center the text on the given point. |
972
|
|
|
|
|
|
|
|
973
|
|
|
|
|
|
|
=item 'right' => 1 |
974
|
|
|
|
|
|
|
|
975
|
|
|
|
|
|
|
Right align on the given point. |
976
|
|
|
|
|
|
|
|
977
|
|
|
|
|
|
|
=item 'align' => $placement |
978
|
|
|
|
|
|
|
|
979
|
|
|
|
|
|
|
Alternate to left, center, and right. C<$placement> is 'left' (default), |
980
|
|
|
|
|
|
|
'center', or 'right'. |
981
|
|
|
|
|
|
|
|
982
|
|
|
|
|
|
|
=back |
983
|
|
|
|
|
|
|
|
984
|
|
|
|
|
|
|
Other options available to C, such as underlining, can be used here. |
985
|
|
|
|
|
|
|
|
986
|
|
|
|
|
|
|
The width used (in points) is B. |
987
|
|
|
|
|
|
|
|
988
|
|
|
|
|
|
|
=back |
989
|
|
|
|
|
|
|
|
990
|
|
|
|
|
|
|
B that C was not designed to interoperate with other |
991
|
|
|
|
|
|
|
text operations. It is a standalone operation, and does I leave a "next |
992
|
|
|
|
|
|
|
write" position (or any other setting) for another C mode operation. A |
993
|
|
|
|
|
|
|
following write will likely be at C<(0,0)>, and not at the expected location. |
994
|
|
|
|
|
|
|
|
995
|
|
|
|
|
|
|
C is intended as an "all in one" convenience function for single |
996
|
|
|
|
|
|
|
lines of text, such as a label on some |
997
|
|
|
|
|
|
|
graphics, and not as part of putting down multiple pieces of text. It I |
998
|
|
|
|
|
|
|
possible to figure out the position of a following write (either C |
999
|
|
|
|
|
|
|
or C) by adding the returned width to the original position's I value |
1000
|
|
|
|
|
|
|
(assuming left-justified positioning). |
1001
|
|
|
|
|
|
|
|
1002
|
|
|
|
|
|
|
=cut |
1003
|
|
|
|
|
|
|
|
1004
|
|
|
|
|
|
|
sub textlabel { |
1005
|
0
|
|
|
0
|
1
|
|
my ($self, $x,$y, $font, $size, $text, %opts) = @_; |
1006
|
|
|
|
|
|
|
# copy dashed option names to preferred undashed names |
1007
|
0
|
0
|
0
|
|
|
|
if (defined $opts{'-rotate'} && !defined $opts{'rotate'}) { $opts{'rotate'} = delete($opts{'-rotate'}); } |
|
0
|
|
|
|
|
|
|
1008
|
0
|
0
|
0
|
|
|
|
if (defined $opts{'-color'} && !defined $opts{'color'}) { $opts{'color'} = delete($opts{'-color'}); } |
|
0
|
|
|
|
|
|
|
1009
|
0
|
0
|
0
|
|
|
|
if (defined $opts{'-strokecolor'} && !defined $opts{'strokecolor'}) { $opts{'strokecolor'} = delete($opts{'-strokecolor'}); } |
|
0
|
|
|
|
|
|
|
1010
|
0
|
0
|
0
|
|
|
|
if (defined $opts{'-charspace'} && !defined $opts{'charspace'}) { $opts{'charspace'} = delete($opts{'-charspace'}); } |
|
0
|
|
|
|
|
|
|
1011
|
0
|
0
|
0
|
|
|
|
if (defined $opts{'-hscale'} && !defined $opts{'hscale'}) { $opts{'hscale'} = delete($opts{'-hscale'}); } |
|
0
|
|
|
|
|
|
|
1012
|
0
|
0
|
0
|
|
|
|
if (defined $opts{'-wordspace'} && !defined $opts{'wordspace'}) { $opts{'wordspace'} = delete($opts{'-wordspace'}); } |
|
0
|
|
|
|
|
|
|
1013
|
0
|
0
|
0
|
|
|
|
if (defined $opts{'-render'} && !defined $opts{'render'}) { $opts{'render'} = delete($opts{'-render'}); } |
|
0
|
|
|
|
|
|
|
1014
|
0
|
0
|
0
|
|
|
|
if (defined $opts{'-right'} && !defined $opts{'right'}) { $opts{'right'} = delete($opts{'-right'}); } |
|
0
|
|
|
|
|
|
|
1015
|
0
|
0
|
0
|
|
|
|
if (defined $opts{'-center'} && !defined $opts{'center'}) { $opts{'center'} = delete($opts{'-center'}); } |
|
0
|
|
|
|
|
|
|
1016
|
0
|
0
|
0
|
|
|
|
if (defined $opts{'-left'} && !defined $opts{'left'}) { $opts{'left'} = delete($opts{'-left'}); } |
|
0
|
|
|
|
|
|
|
1017
|
0
|
0
|
0
|
|
|
|
if (defined $opts{'-align'} && !defined $opts{'align'}) { $opts{'align'} = delete($opts{'-align'}); } |
|
0
|
|
|
|
|
|
|
1018
|
0
|
|
|
|
|
|
my $wht; |
1019
|
|
|
|
|
|
|
|
1020
|
0
|
|
|
|
|
|
my %trans_opts = ( 'translate' => [$x,$y] ); |
1021
|
0
|
|
|
|
|
|
my %text_state = (); |
1022
|
0
|
0
|
|
|
|
|
$trans_opts{'rotate'} = $opts{'rotate'} if defined($opts{'rotate'}); |
1023
|
|
|
|
|
|
|
|
1024
|
0
|
|
|
|
|
|
my $wastext = $self->_in_text_object(); |
1025
|
0
|
0
|
|
|
|
|
if ($wastext) { |
1026
|
0
|
|
|
|
|
|
%text_state = $self->textstate(); |
1027
|
0
|
|
|
|
|
|
$self->textend(); |
1028
|
|
|
|
|
|
|
} |
1029
|
0
|
|
|
|
|
|
$self->save(); |
1030
|
0
|
|
|
|
|
|
$self->textstart(); |
1031
|
|
|
|
|
|
|
|
1032
|
0
|
|
|
|
|
|
$self->transform(%trans_opts); |
1033
|
|
|
|
|
|
|
|
1034
|
0
|
0
|
|
|
|
|
$self->fillcolor(ref($opts{'color'}) ? @{$opts{'color'}} : $opts{'color'}) if defined($opts{'color'}); |
|
0
|
0
|
|
|
|
|
|
1035
|
0
|
0
|
|
|
|
|
$self->strokecolor(ref($opts{'strokecolor'}) ? @{$opts{'strokecolor'}} : $opts{'strokecolor'}) if defined($opts{'strokecolor'}); |
|
0
|
0
|
|
|
|
|
|
1036
|
|
|
|
|
|
|
|
1037
|
0
|
|
|
|
|
|
$self->font($font, $size); |
1038
|
|
|
|
|
|
|
|
1039
|
0
|
0
|
|
|
|
|
$self->charspace($opts{'charspace'}) if defined($opts{'charspace'}); |
1040
|
0
|
0
|
|
|
|
|
$self->hscale($opts{'hscale'}) if defined($opts{'hscale'}); |
1041
|
0
|
0
|
|
|
|
|
$self->wordspace($opts{'wordspace'}) if defined($opts{'wordspace'}); |
1042
|
0
|
0
|
|
|
|
|
$self->render($opts{'render'}) if defined($opts{'render'}); |
1043
|
|
|
|
|
|
|
|
1044
|
0
|
0
|
0
|
|
|
|
if (defined($opts{'right'}) && $opts{'right'} || |
|
|
0
|
0
|
|
|
|
|
|
|
0
|
0
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
0
|
|
|
|
|
1045
|
|
|
|
|
|
|
defined($opts{'align'}) && $opts{'align'} =~ /^r/i) { |
1046
|
0
|
|
|
|
|
|
$wht = $self->text_right($text, %opts); |
1047
|
|
|
|
|
|
|
} elsif (defined($opts{'center'}) && $opts{'center'} || |
1048
|
|
|
|
|
|
|
defined($opts{'align'}) && $opts{'align'} =~ /^c/i) { |
1049
|
0
|
|
|
|
|
|
$wht = $self->text_center($text, %opts); |
1050
|
|
|
|
|
|
|
} elsif (defined($opts{'left'}) && $opts{'left'} || |
1051
|
|
|
|
|
|
|
defined($opts{'align'}) && $opts{'align'} =~ /^l/i) { |
1052
|
0
|
|
|
|
|
|
$wht = $self->text($text, %opts); # explicitly left aligned |
1053
|
|
|
|
|
|
|
} else { |
1054
|
0
|
|
|
|
|
|
$wht = $self->text($text, %opts); # left aligned by default |
1055
|
|
|
|
|
|
|
} |
1056
|
|
|
|
|
|
|
|
1057
|
0
|
|
|
|
|
|
$self->textend(); |
1058
|
0
|
|
|
|
|
|
$self->restore(); |
1059
|
|
|
|
|
|
|
|
1060
|
0
|
0
|
|
|
|
|
if ($wastext) { |
1061
|
0
|
|
|
|
|
|
$self->textstart(); |
1062
|
0
|
|
|
|
|
|
$self->textstate(%text_state); |
1063
|
|
|
|
|
|
|
} |
1064
|
0
|
|
|
|
|
|
return $wht; |
1065
|
|
|
|
|
|
|
} |
1066
|
|
|
|
|
|
|
|
1067
|
|
|
|
|
|
|
1; |