| line |
stmt |
bran |
cond |
sub |
pod |
time |
code |
|
1
|
|
|
|
|
|
|
#! perl |
|
2
|
|
|
|
|
|
|
|
|
3
|
5
|
|
|
5
|
|
718413
|
use strict; |
|
|
5
|
|
|
|
|
14
|
|
|
|
5
|
|
|
|
|
237
|
|
|
4
|
5
|
|
|
5
|
|
29
|
use warnings; |
|
|
5
|
|
|
|
|
17
|
|
|
|
5
|
|
|
|
|
337
|
|
|
5
|
5
|
|
|
5
|
|
28
|
use utf8; |
|
|
5
|
|
|
|
|
10
|
|
|
|
5
|
|
|
|
|
38
|
|
|
6
|
|
|
|
|
|
|
|
|
7
|
|
|
|
|
|
|
package Text::Layout::PDFAPI2; |
|
8
|
|
|
|
|
|
|
|
|
9
|
5
|
|
|
5
|
|
935
|
use parent 'Text::Layout'; |
|
|
5
|
|
|
|
|
500
|
|
|
|
5
|
|
|
|
|
41
|
|
|
10
|
5
|
|
|
5
|
|
379
|
use Carp; |
|
|
5
|
|
|
|
|
11
|
|
|
|
5
|
|
|
|
|
405
|
|
|
11
|
5
|
|
|
5
|
|
41
|
use List::Util qw(max); |
|
|
5
|
|
|
|
|
9
|
|
|
|
5
|
|
|
|
|
31135
|
|
|
12
|
|
|
|
|
|
|
|
|
13
|
|
|
|
|
|
|
my $hb; |
|
14
|
|
|
|
|
|
|
my $fc; |
|
15
|
|
|
|
|
|
|
|
|
16
|
|
|
|
|
|
|
#### API |
|
17
|
|
|
|
|
|
|
sub new { |
|
18
|
3
|
|
|
3
|
1
|
23
|
my ( $pkg, @data ) = @_; |
|
19
|
3
|
50
|
33
|
|
|
48
|
unless ( @data == 1 && ref($data[0]) =~ /^PDF::(API2|Builder)\b/ ) { |
|
20
|
0
|
|
|
|
|
0
|
croak("Usage: Text::Layout::PDFAPI2->new(\$pdf)"); |
|
21
|
|
|
|
|
|
|
} |
|
22
|
3
|
|
|
|
|
66
|
my $self = $pkg->SUPER::new; |
|
23
|
3
|
|
|
|
|
28
|
$self->{_context} = $data[0]; |
|
24
|
3
|
50
|
33
|
|
|
21
|
if ( !$fc || $fc->{__PDF__} ne $data[0] ) { |
|
25
|
|
|
|
|
|
|
# Init cache. |
|
26
|
3
|
|
|
|
|
10
|
$fc = { __PDF__ => $data[0] }; |
|
27
|
3
|
|
|
|
|
32
|
Text::Layout::FontConfig->reset; |
|
28
|
|
|
|
|
|
|
} |
|
29
|
3
|
|
|
|
|
1907
|
require Text::Layout::PDFAPI2::ImageElement; |
|
30
|
3
|
50
|
|
|
|
53
|
$self->register_element |
|
31
|
|
|
|
|
|
|
( Text::Layout::PDFAPI2::ImageElement->new( pdf => $data[0] ), "img" ) |
|
32
|
|
|
|
|
|
|
unless $self->get_element_handler("img"); |
|
33
|
|
|
|
|
|
|
|
|
34
|
3
|
|
|
|
|
19
|
$self; |
|
35
|
|
|
|
|
|
|
} |
|
36
|
|
|
|
|
|
|
|
|
37
|
|
|
|
|
|
|
sub _pdf { |
|
38
|
0
|
|
|
0
|
|
0
|
my ( $self ) = @_; |
|
39
|
0
|
|
|
|
|
0
|
$self->{_context}; |
|
40
|
|
|
|
|
|
|
} |
|
41
|
|
|
|
|
|
|
|
|
42
|
|
|
|
|
|
|
# Creates a (singleton) HarfBuzz::Shaper object. |
|
43
|
|
|
|
|
|
|
sub _hb_init { |
|
44
|
0
|
0
|
|
0
|
|
0
|
return $hb if defined $hb; |
|
45
|
0
|
|
|
|
|
0
|
$hb = 0; |
|
46
|
0
|
|
|
|
|
0
|
eval { |
|
47
|
0
|
|
|
|
|
0
|
require HarfBuzz::Shaper; |
|
48
|
0
|
|
|
|
|
0
|
$hb = HarfBuzz::Shaper->new; |
|
49
|
|
|
|
|
|
|
}; |
|
50
|
0
|
|
|
|
|
0
|
return $hb; |
|
51
|
|
|
|
|
|
|
} |
|
52
|
|
|
|
|
|
|
|
|
53
|
|
|
|
|
|
|
# Verify if a font needs shaping, and we can do that. |
|
54
|
|
|
|
|
|
|
sub _hb_font_check { |
|
55
|
5
|
|
|
5
|
|
14
|
my ( $f ) = @_; |
|
56
|
5
|
100
|
|
|
|
63
|
return $f->{_hb_checked} if defined $f->{_hb_checked}; |
|
57
|
|
|
|
|
|
|
|
|
58
|
2
|
50
|
|
|
|
15
|
if ( $f->get_shaping ) { |
|
59
|
0
|
|
|
|
|
0
|
my $fn = $f->to_string; |
|
60
|
0
|
0
|
|
|
|
0
|
if ( $f->{font}->can("fontfilename") ) { |
|
61
|
0
|
0
|
|
|
|
0
|
if ( _hb_init() ) { |
|
62
|
|
|
|
|
|
|
# warn("Font $fn will use shaping.\n"); |
|
63
|
0
|
|
|
|
|
0
|
return $f->{_hb_checked} = 1; |
|
64
|
|
|
|
|
|
|
} |
|
65
|
0
|
|
|
|
|
0
|
carp("Font $fn: Requires shaping but HarfBuzz cannot be loaded."); |
|
66
|
|
|
|
|
|
|
} |
|
67
|
|
|
|
|
|
|
else { |
|
68
|
0
|
|
|
|
|
0
|
carp("Font $fn: Shaping not supported"); |
|
69
|
|
|
|
|
|
|
} |
|
70
|
|
|
|
|
|
|
} |
|
71
|
|
|
|
|
|
|
else { |
|
72
|
|
|
|
|
|
|
# warn("Font ", $f->to_string, " does not need shaping.\n"); |
|
73
|
|
|
|
|
|
|
} |
|
74
|
2
|
|
|
|
|
14
|
return $f->{_hb_checked} = 0; |
|
75
|
|
|
|
|
|
|
} |
|
76
|
|
|
|
|
|
|
|
|
77
|
|
|
|
|
|
|
#### API |
|
78
|
|
|
|
|
|
|
sub render { |
|
79
|
1
|
|
|
1
|
0
|
5
|
my ( $self, $x, $y, $text, $fp ) = @_; |
|
80
|
|
|
|
|
|
|
|
|
81
|
1
|
|
|
|
|
6
|
$self->{_lastx} = $x; |
|
82
|
1
|
|
|
|
|
3
|
$self->{_lasty} = $y; |
|
83
|
|
|
|
|
|
|
|
|
84
|
1
|
|
|
|
|
5
|
my @bb = $self->get_pixel_bbox; |
|
85
|
1
|
|
|
|
|
3
|
my $bl = $bb[0]; |
|
86
|
1
|
|
50
|
|
|
26
|
my $align = $self->{_alignment} // 0; |
|
87
|
1
|
50
|
|
|
|
5
|
if ( $self->{_width} ) { |
|
88
|
0
|
|
|
|
|
0
|
my $w = $bb[3]; |
|
89
|
0
|
0
|
|
|
|
0
|
if ( $w < $self->{_width} ) { |
|
90
|
0
|
0
|
|
|
|
0
|
if ( $align eq "right" ) { |
|
|
|
0
|
|
|
|
|
|
|
91
|
0
|
|
|
|
|
0
|
$x += $self->{_width} - $w; |
|
92
|
|
|
|
|
|
|
} |
|
93
|
|
|
|
|
|
|
elsif ( $align eq "center" ) { |
|
94
|
0
|
|
|
|
|
0
|
$x += ( $self->{_width} - $w ) / 2; |
|
95
|
|
|
|
|
|
|
} |
|
96
|
|
|
|
|
|
|
else { |
|
97
|
0
|
|
|
|
|
0
|
$x += $bb[1]; |
|
98
|
|
|
|
|
|
|
} |
|
99
|
|
|
|
|
|
|
} |
|
100
|
|
|
|
|
|
|
} |
|
101
|
1
|
|
|
|
|
3
|
my $upem = 1000; # as per PDF::API2 |
|
102
|
|
|
|
|
|
|
|
|
103
|
|
|
|
|
|
|
my $draw_bg = sub { |
|
104
|
0
|
|
|
0
|
|
0
|
my ( $fx, $nfx, $x, $y, $w ) = @_; |
|
105
|
0
|
|
|
|
|
0
|
my $h = $bb[2]; # "border" |
|
106
|
0
|
|
|
|
|
0
|
my $d = abs($h)/25; |
|
107
|
|
|
|
|
|
|
|
|
108
|
|
|
|
|
|
|
# If first with background, extend a little to the left. |
|
109
|
0
|
0
|
0
|
|
|
0
|
if ( $fx == 0 || !$self->{_content}->[$fx-1]->{bgcolor} ) { |
|
110
|
0
|
|
|
|
|
0
|
$x -= $d; |
|
111
|
0
|
|
|
|
|
0
|
$w += $d; |
|
112
|
|
|
|
|
|
|
} |
|
113
|
|
|
|
|
|
|
# If last with background, extend a little to the right. |
|
114
|
0
|
0
|
0
|
|
|
0
|
if ( $fx == $nfx-1 || !$self->{_content}->[$fx+1]->{bgcolor} ) { |
|
115
|
0
|
|
|
|
|
0
|
$w += 2*$d; |
|
116
|
|
|
|
|
|
|
} |
|
117
|
|
|
|
|
|
|
|
|
118
|
|
|
|
|
|
|
# If next is a strut, followed by same bg color, |
|
119
|
|
|
|
|
|
|
# have the background span the strut. |
|
120
|
|
|
|
|
|
|
#### TODO: Span multiple struts. |
|
121
|
0
|
|
|
|
|
0
|
my $delta; |
|
122
|
0
|
|
|
|
|
0
|
for ( my $i = $fx+1; $i < $nfx; $i++ ) { |
|
123
|
0
|
0
|
0
|
|
|
0
|
if ( $self->{_content}->[$i]->{type} eq "strut" ) { |
|
|
|
0
|
0
|
|
|
|
|
|
124
|
0
|
|
0
|
|
|
0
|
$delta //= 0; |
|
125
|
0
|
|
|
|
|
0
|
$delta += $self->{_content}->[$i]->{width}; |
|
126
|
|
|
|
|
|
|
} |
|
127
|
|
|
|
|
|
|
elsif ( defined($delta) |
|
128
|
|
|
|
|
|
|
&& $self->{_content}->[$i]->{bgcolor} |
|
129
|
|
|
|
|
|
|
&& $self->{_content}->[$i]->{bgcolor} |
|
130
|
|
|
|
|
|
|
eq $self->{_content}->[$fx]->{bgcolor} ) { |
|
131
|
0
|
|
|
|
|
0
|
$w += $delta; |
|
132
|
0
|
|
|
|
|
0
|
last; |
|
133
|
|
|
|
|
|
|
} |
|
134
|
|
|
|
|
|
|
} |
|
135
|
|
|
|
|
|
|
# Draw the background. |
|
136
|
0
|
|
|
|
|
0
|
$text->textend; |
|
137
|
0
|
|
|
|
|
0
|
my $gfx = $text; # sanity |
|
138
|
0
|
|
|
|
|
0
|
$gfx->save; |
|
139
|
0
|
|
|
|
|
0
|
$gfx->fillcolor( $self->{_content}->[$fx]->{bgcolor} ); |
|
140
|
0
|
|
|
|
|
0
|
$gfx->linewidth(0); |
|
141
|
0
|
|
|
|
|
0
|
$gfx->rectangle( $x, $y+$d, $x+$w, $y+$h-$d ); |
|
142
|
0
|
|
|
|
|
0
|
$text->fill; |
|
143
|
0
|
|
|
|
|
0
|
$gfx->restore; |
|
144
|
0
|
|
|
|
|
0
|
$text->textstart; |
|
145
|
1
|
|
|
|
|
10
|
}; |
|
146
|
|
|
|
|
|
|
|
|
147
|
1
|
|
|
|
|
2
|
my $nfx = @{ $self->{_content} }; |
|
|
1
|
|
|
|
|
4
|
|
|
148
|
1
|
|
|
|
|
17
|
for ( my $fx = 0; $fx < $nfx; $fx++ ) { |
|
149
|
1
|
|
|
|
|
4
|
my $fragment = $self->{_content}->[$fx]; |
|
150
|
|
|
|
|
|
|
|
|
151
|
1
|
50
|
|
|
|
11
|
if ( $fragment->{type} eq "strut" ) { |
|
|
|
50
|
|
|
|
|
|
|
152
|
0
|
|
|
|
|
0
|
$x += $fragment->{width}; |
|
153
|
0
|
0
|
|
|
|
0
|
if ( length($fragment->{label}) ) { |
|
154
|
0
|
|
|
|
|
0
|
my $pdf = $text->{' api'}; |
|
155
|
0
|
|
|
|
|
0
|
my $page = $text->{' apipage'}; |
|
156
|
0
|
|
|
|
|
0
|
my $target = $fragment->{label}; |
|
157
|
|
|
|
|
|
|
|
|
158
|
|
|
|
|
|
|
# Augmented API for apps that keep track of bookmarks. |
|
159
|
0
|
|
|
|
|
0
|
my $c = $pdf->can("named_dest_fiddle"); |
|
160
|
0
|
0
|
|
|
|
0
|
$target = $pdf->$c($target) if $c; |
|
161
|
0
|
|
|
|
|
0
|
$c = $pdf->can("named_dest_register"); |
|
162
|
0
|
|
|
|
|
0
|
$pdf->$c( $target, $page ); |
|
163
|
|
|
|
|
|
|
|
|
164
|
0
|
|
|
|
|
0
|
$c = ref($pdf) . '::NamedDestination'; |
|
165
|
0
|
|
|
|
|
0
|
my $dest = $c->new($pdf); |
|
166
|
|
|
|
|
|
|
$dest->goto( $page, |
|
167
|
|
|
|
|
|
|
xyz => ( $x - $fragment->{width}, |
|
168
|
|
|
|
|
|
|
$y + $self->{_currentsize}, |
|
169
|
0
|
|
|
|
|
0
|
undef ) ); |
|
170
|
0
|
|
|
|
|
0
|
$pdf->named_destination( 'Dests', $target, $dest ); |
|
171
|
|
|
|
|
|
|
} |
|
172
|
|
|
|
|
|
|
} |
|
173
|
|
|
|
|
|
|
|
|
174
|
|
|
|
|
|
|
elsif ( my $hd = $self->get_element_handler($fragment->{type}) ) { |
|
175
|
0
|
|
|
|
|
0
|
$text->textend; |
|
176
|
0
|
|
|
|
|
0
|
my $ab = $hd->render($fragment, $text, $x, $y-$bl)->{abox}; |
|
177
|
0
|
|
|
|
|
0
|
$text->textstart; |
|
178
|
0
|
|
|
|
|
0
|
$x += $ab->[2]; |
|
179
|
|
|
|
|
|
|
} |
|
180
|
1
|
50
|
33
|
|
|
9
|
next unless $fragment->{type} eq "text" && length($fragment->{text}); |
|
181
|
|
|
|
|
|
|
|
|
182
|
1
|
|
|
|
|
3
|
my $x0 = $x; |
|
183
|
1
|
|
|
|
|
3
|
my $y0 = $y; |
|
184
|
1
|
|
|
|
|
2
|
my $f = $fragment->{font}; |
|
185
|
1
|
|
|
|
|
7
|
my $font = $f->get_font($self); |
|
186
|
1
|
50
|
|
|
|
5
|
unless ( $font ) { |
|
187
|
0
|
|
|
|
|
0
|
carp("Can't happen?"); |
|
188
|
0
|
|
|
|
|
0
|
$f = $self->{_currentfont}; |
|
189
|
0
|
|
|
|
|
0
|
$font = $f->getfont($self); |
|
190
|
|
|
|
|
|
|
} |
|
191
|
1
|
|
|
|
|
23
|
$text->strokecolor( $fragment->{color} ); |
|
192
|
1
|
|
|
|
|
272
|
$text->fillcolor( $fragment->{color} ); |
|
193
|
1
|
|
33
|
|
|
188
|
$text->font( $font, $fragment->{size} || $self->{_currentsize} ); |
|
194
|
|
|
|
|
|
|
|
|
195
|
1
|
50
|
|
|
|
373
|
if ( _hb_font_check($f) ) { |
|
196
|
0
|
|
|
|
|
0
|
$hb->set_font( $font->fontfilename ); |
|
197
|
0
|
|
0
|
|
|
0
|
$hb->set_size( $fragment->{size} || $self->{_currentsize} ); |
|
198
|
0
|
|
|
|
|
0
|
$hb->set_text( $fragment->{text} ); |
|
199
|
0
|
0
|
|
|
|
0
|
$hb->set_direction( $f->{direction} ) if $f->{direction}; |
|
200
|
0
|
0
|
|
|
|
0
|
$hb->set_language( $f->{language} ) if $f->{language}; |
|
201
|
0
|
|
|
|
|
0
|
my $info = $hb->shaper($fp); |
|
202
|
0
|
|
|
|
|
0
|
my $y = $y - $fragment->{base} - $bl; |
|
203
|
0
|
|
0
|
|
|
0
|
my $sz = $fragment->{size} || $self->{_currentsize}; |
|
204
|
0
|
|
|
|
|
0
|
my $w = 0; |
|
205
|
0
|
|
|
|
|
0
|
$w += $_->{ax} for @$info; |
|
206
|
|
|
|
|
|
|
|
|
207
|
0
|
0
|
|
|
|
0
|
if ( $fragment->{bgcolor} ) { |
|
208
|
0
|
|
|
|
|
0
|
$draw_bg->( $fx, $nfx, $x0, $y0, $w ); |
|
209
|
|
|
|
|
|
|
} |
|
210
|
|
|
|
|
|
|
|
|
211
|
0
|
|
|
|
|
0
|
foreach my $g ( @$info ) { |
|
212
|
0
|
|
|
|
|
0
|
$text->translate( $x + $g->{dx}, $y - $g->{dy} ); |
|
213
|
0
|
|
|
|
|
0
|
$text->glyph_by_CId( $g->{g} ); |
|
214
|
0
|
|
|
|
|
0
|
$x += $g->{ax}; |
|
215
|
0
|
|
|
|
|
0
|
$y += $g->{ay}; |
|
216
|
|
|
|
|
|
|
} |
|
217
|
|
|
|
|
|
|
} |
|
218
|
|
|
|
|
|
|
else { |
|
219
|
|
|
|
|
|
|
printf("%.2f %.2f %.2f \"%s\" %s\n", |
|
220
|
|
|
|
|
|
|
$x, $y-$fragment->{base}-$bl, |
|
221
|
|
|
|
|
|
|
$font->width($fragment->{text}) * ($fragment->{size} || $self->{_currentsize}), |
|
222
|
|
|
|
|
|
|
$fragment->{text}, |
|
223
|
|
|
|
|
|
|
join(" ", $fragment->{font}->{family}, |
|
224
|
|
|
|
|
|
|
$fragment->{font}->{style}, |
|
225
|
|
|
|
|
|
|
$fragment->{font}->{weight}, |
|
226
|
|
|
|
|
|
|
$fragment->{size} || $self->{_currentsize}, |
|
227
|
|
|
|
|
|
|
$fragment->{color}, |
|
228
|
|
|
|
|
|
|
$fragment->{underline}||'""', $fragment->{underline_color}||'""', |
|
229
|
1
|
|
|
|
|
2
|
$fragment->{strikethrough}||'""', $fragment->{strikethrough_color}||'""', |
|
230
|
|
|
|
|
|
|
), |
|
231
|
|
|
|
|
|
|
) if 0; |
|
232
|
1
|
|
|
|
|
22
|
my $t = $fragment->{text}; |
|
233
|
1
|
50
|
|
|
|
7
|
if ( $t ne "" ) { |
|
234
|
|
|
|
|
|
|
|
|
235
|
|
|
|
|
|
|
# See ChordPro issue 240. |
|
236
|
1
|
50
|
33
|
|
|
7
|
if ( $font->issymbol && $font->is_standard ) { |
|
237
|
|
|
|
|
|
|
# This enables byte access to these symbol fonts. |
|
238
|
0
|
|
|
|
|
0
|
utf8::downgrade( $t, 1 ); |
|
239
|
|
|
|
|
|
|
} |
|
240
|
|
|
|
|
|
|
|
|
241
|
1
|
|
|
|
|
16
|
my $y = $y-$fragment->{base}-$bl; |
|
242
|
1
|
|
33
|
|
|
5
|
my $sz = $fragment->{size} || $self->{_currentsize}; |
|
243
|
1
|
|
|
|
|
6
|
my $w = $font->width($t) * $sz; |
|
244
|
|
|
|
|
|
|
|
|
245
|
1
|
50
|
|
|
|
414
|
if ( $fragment->{bgcolor} ) { |
|
246
|
0
|
|
|
|
|
0
|
$draw_bg->( $fx, $nfx, $x0, $y0, $w ); |
|
247
|
|
|
|
|
|
|
} |
|
248
|
|
|
|
|
|
|
|
|
249
|
1
|
|
|
|
|
8
|
$text->font( $f->get_font, $sz ); |
|
250
|
1
|
|
|
|
|
229
|
$text->translate( $x, $y ); |
|
251
|
1
|
|
|
|
|
744
|
$text->text($t); |
|
252
|
1
|
|
|
|
|
734
|
$x += $w; |
|
253
|
|
|
|
|
|
|
} |
|
254
|
|
|
|
|
|
|
} |
|
255
|
|
|
|
|
|
|
|
|
256
|
1
|
50
|
|
|
|
6
|
next unless $x > $x0; |
|
257
|
|
|
|
|
|
|
# While PDF::API2 delivers font metrics in 1/1000s, |
|
258
|
|
|
|
|
|
|
# underlinethickness and position are unscaled UPEM. |
|
259
|
1
|
|
50
|
|
|
5
|
my $dw = $font->data->{upem} // 1000; |
|
260
|
|
|
|
|
|
|
|
|
261
|
1
|
|
|
|
|
12
|
my @strikes; |
|
262
|
1
|
50
|
33
|
|
|
6
|
if ( $fragment->{underline} && $fragment->{underline} ne 'none' ) { |
|
263
|
0
|
|
0
|
|
|
0
|
my $sz = $fragment->{size} || $self->{_currentsize}; |
|
264
|
|
|
|
|
|
|
my $d = -( $f->{underline_position} |
|
265
|
0
|
|
0
|
|
|
0
|
|| $font->underlineposition ) * $sz/$dw; |
|
266
|
|
|
|
|
|
|
my $h = ( $f->{underline_thickness} |
|
267
|
0
|
|
0
|
|
|
0
|
|| $font->underlinethickness ) * $sz/$dw; |
|
268
|
0
|
|
0
|
|
|
0
|
my $col = $fragment->{underline_color} // $fragment->{color}; |
|
269
|
0
|
0
|
|
|
|
0
|
if ( $fragment->{underline} eq 'double' ) { |
|
270
|
0
|
|
|
|
|
0
|
push( @strikes, [ $d-0.125*$h, $h * 0.75, $col ], |
|
271
|
|
|
|
|
|
|
[ $d+1.125*$h, $h * 0.75, $col ] ); |
|
272
|
|
|
|
|
|
|
} |
|
273
|
|
|
|
|
|
|
else { |
|
274
|
0
|
|
|
|
|
0
|
push( @strikes, [ $d+$h/2, $h, $col ] ); |
|
275
|
|
|
|
|
|
|
} |
|
276
|
|
|
|
|
|
|
} |
|
277
|
|
|
|
|
|
|
|
|
278
|
1
|
50
|
|
|
|
4
|
if ( $fragment->{strikethrough} ) { |
|
279
|
0
|
|
0
|
|
|
0
|
my $sz = $fragment->{size} || $self->{_currentsize}; |
|
280
|
0
|
|
|
|
|
0
|
my $xh = $font->xheight / 1000; |
|
281
|
|
|
|
|
|
|
my $d = -( $f->{strikeline_position} |
|
282
|
0
|
0
|
|
|
|
0
|
? $f->{strikeline_position} / $dw |
|
283
|
|
|
|
|
|
|
: 0.6*$xh ) * $sz; |
|
284
|
|
|
|
|
|
|
my $h = ( $f->{strikeline_thickness} |
|
285
|
|
|
|
|
|
|
|| $f->{underline_thickness} |
|
286
|
0
|
|
0
|
|
|
0
|
|| $font->underlinethickness ) * $sz/$dw; |
|
287
|
0
|
|
0
|
|
|
0
|
my $col = $fragment->{strikethrough_color} // $fragment->{color}; |
|
288
|
0
|
|
|
|
|
0
|
push( @strikes, [ $d+$h/2, $h, $col ] ); |
|
289
|
|
|
|
|
|
|
} |
|
290
|
|
|
|
|
|
|
|
|
291
|
1
|
50
|
33
|
|
|
6
|
if ( $fragment->{overline} && $fragment->{overline} ne 'none' ) { |
|
292
|
0
|
|
0
|
|
|
0
|
my $sz = $fragment->{size} || $self->{_currentsize}; |
|
293
|
0
|
|
|
|
|
0
|
my $xh = $font->xheight / 1000; |
|
294
|
|
|
|
|
|
|
my $h = ( $f->{overline_thickness} |
|
295
|
|
|
|
|
|
|
|| $f->{underline_thickness} |
|
296
|
0
|
|
0
|
|
|
0
|
|| $font->underlinethickness ) * $sz/$dw; |
|
297
|
|
|
|
|
|
|
my $d = -( $f->{overline_position} |
|
298
|
0
|
0
|
|
|
|
0
|
? $f->{overline_position} * $sz/$dw |
|
299
|
|
|
|
|
|
|
: $xh*$sz + 2*$h ); |
|
300
|
0
|
|
0
|
|
|
0
|
my $col = $fragment->{overline_color} // $fragment->{color}; |
|
301
|
0
|
0
|
|
|
|
0
|
if ( $fragment->{overline} eq 'double' ) { |
|
302
|
0
|
|
|
|
|
0
|
push( @strikes, [ $d-0.125*$h, $h * 0.75, $col ], |
|
303
|
|
|
|
|
|
|
[ $d+1.125*$h, $h * 0.75, $col ] ); |
|
304
|
|
|
|
|
|
|
} |
|
305
|
|
|
|
|
|
|
else { |
|
306
|
0
|
|
|
|
|
0
|
push( @strikes, [ $d+$h/2, $h, $col ] ); |
|
307
|
|
|
|
|
|
|
} |
|
308
|
|
|
|
|
|
|
} |
|
309
|
1
|
50
|
|
|
|
4
|
$text->textend if @strikes; |
|
310
|
1
|
|
|
|
|
4
|
for ( @strikes ) { |
|
311
|
0
|
|
|
|
|
0
|
my $gfx = $text; # prevent mental insanity |
|
312
|
0
|
|
|
|
|
0
|
$gfx->save; |
|
313
|
0
|
|
|
|
|
0
|
$gfx->strokecolor($_->[2]); |
|
314
|
0
|
|
|
|
|
0
|
$gfx->linewidth($_->[1]); |
|
315
|
0
|
|
|
|
|
0
|
$gfx->move( $x0, $y0-$fragment->{base}-$bl-$_->[0] ); |
|
316
|
0
|
|
|
|
|
0
|
$gfx->line( $x, $y0-$fragment->{base}-$bl-$_->[0] ); |
|
317
|
0
|
|
|
|
|
0
|
$gfx->stroke; |
|
318
|
0
|
|
|
|
|
0
|
$gfx->restore; |
|
319
|
|
|
|
|
|
|
} |
|
320
|
1
|
50
|
|
|
|
4
|
$text->textstart if @strikes; |
|
321
|
|
|
|
|
|
|
|
|
322
|
1
|
50
|
|
|
|
142
|
if ( $fragment->{href} ) { |
|
323
|
0
|
|
0
|
|
|
0
|
my $sz = $fragment->{size} || $self->{_currentsize}; |
|
324
|
0
|
|
|
|
|
0
|
my $ann = $text->{' apipage'}->annotation; |
|
325
|
0
|
|
|
|
|
0
|
my $target = $fragment->{href}; |
|
326
|
|
|
|
|
|
|
|
|
327
|
0
|
0
|
|
|
|
0
|
if ( $target =~ /^#(.+)/ ) { # named destination |
|
|
|
0
|
|
|
|
|
|
|
328
|
|
|
|
|
|
|
# Augmented API for apps that keep track of bookmarks. |
|
329
|
0
|
|
|
|
|
0
|
my $pdf = $text->{' api'}; |
|
330
|
0
|
0
|
|
|
|
0
|
if ( my $c = $pdf->can("named_dest_fiddle") ) { |
|
331
|
0
|
|
|
|
|
0
|
$target = $pdf->$c($1); |
|
332
|
|
|
|
|
|
|
} |
|
333
|
|
|
|
|
|
|
|
|
334
|
0
|
|
|
|
|
0
|
$ann->link($target); |
|
335
|
|
|
|
|
|
|
} |
|
336
|
|
|
|
|
|
|
# Named destination in other PDF. |
|
337
|
|
|
|
|
|
|
elsif ( $target =~ /^(?!\w{3,}:)(.*)\#(.+)$/ ) { |
|
338
|
0
|
|
|
|
|
0
|
$ann->pdf( $1, "/$2" ); |
|
339
|
|
|
|
|
|
|
} |
|
340
|
|
|
|
|
|
|
# Arbitrary document. |
|
341
|
|
|
|
|
|
|
else { |
|
342
|
0
|
|
|
|
|
0
|
$ann->uri($target); |
|
343
|
|
|
|
|
|
|
} |
|
344
|
|
|
|
|
|
|
# $ann->border( 0, 0, 1 ); |
|
345
|
0
|
|
|
|
|
0
|
$ann->rect( $x0, $y0, $x, $y0 + $bb[2] ); |
|
346
|
|
|
|
|
|
|
} |
|
347
|
|
|
|
|
|
|
} |
|
348
|
|
|
|
|
|
|
} |
|
349
|
|
|
|
|
|
|
|
|
350
|
|
|
|
|
|
|
#### API |
|
351
|
|
|
|
|
|
|
sub bbox { |
|
352
|
4
|
|
|
4
|
0
|
15
|
my ( $self, $all ) = @_; |
|
353
|
|
|
|
|
|
|
|
|
354
|
4
|
|
|
|
|
18
|
my ( $bl, $x, $y, $w, $h ) = (0) x 4; |
|
355
|
4
|
|
|
|
|
12
|
my ( $d, $a ) = (0) x 2; |
|
356
|
4
|
|
|
|
|
16
|
my ( $xMin, $xMax, $yMin, $yMax ); |
|
357
|
4
|
|
|
|
|
0
|
my $dir; |
|
358
|
4
|
|
|
|
|
14
|
$self->{_struts} = []; |
|
359
|
|
|
|
|
|
|
|
|
360
|
4
|
|
|
|
|
11
|
foreach ( @{ $self->{_content} } ) { |
|
|
4
|
|
|
|
|
15
|
|
|
361
|
|
|
|
|
|
|
|
|
362
|
|
|
|
|
|
|
0&& |
|
363
|
|
|
|
|
|
|
warn("IB: ", |
|
364
|
|
|
|
|
|
|
join(", ", |
|
365
|
6
|
|
|
|
|
12
|
map { defined($_) ? sprintf("%.2f", $_) : "" } |
|
366
|
|
|
|
|
|
|
$xMin, $yMin, $xMax, $yMax ), "\n"); |
|
367
|
|
|
|
|
|
|
|
|
368
|
6
|
100
|
|
|
|
37
|
if ( $_->{type} eq "strut" ) { |
|
|
|
50
|
|
|
|
|
|
|
369
|
|
|
|
|
|
|
my @ab = ( 0, -($_->{desc}//0), |
|
370
|
2
|
|
50
|
|
|
43
|
$_->{width}//0, $_->{asc}//0 ); |
|
|
|
|
50
|
|
|
|
|
|
|
|
|
50
|
|
|
|
|
|
371
|
2
|
|
|
|
|
14
|
my %s = %$_; |
|
372
|
2
|
|
|
|
|
6
|
delete($s{type}); |
|
373
|
2
|
|
|
|
|
6
|
$s{_x} = $w; |
|
374
|
2
|
|
|
|
|
6
|
$s{_strut} = $_; |
|
375
|
2
|
|
|
|
|
3
|
push( @{ $self->{_struts} }, \%s ); |
|
|
2
|
|
|
|
|
8
|
|
|
376
|
|
|
|
|
|
|
# Add to bbox but not to inkbox. |
|
377
|
2
|
|
|
|
|
4
|
$w += $ab[2]; |
|
378
|
2
|
50
|
|
|
|
7
|
$a = $ab[3] if $ab[3] > $a; |
|
379
|
2
|
50
|
|
|
|
8
|
$d = $ab[1] if $ab[1] < $d; |
|
380
|
|
|
|
|
|
|
} |
|
381
|
|
|
|
|
|
|
|
|
382
|
|
|
|
|
|
|
elsif ( my $hd = $self->get_element_handler($_->{type}) ) { |
|
383
|
0
|
|
|
|
|
0
|
my @ab = @{$hd->bbox($_)->{abox}}; |
|
|
0
|
|
|
|
|
0
|
|
|
384
|
0
|
0
|
0
|
|
|
0
|
$xMin //= $w + $ab[0] if $all; |
|
385
|
0
|
|
|
|
|
0
|
$xMax = $w + $ab[2]; |
|
386
|
0
|
|
|
|
|
0
|
$w += $ab[2]; |
|
387
|
0
|
0
|
|
|
|
0
|
$a = $ab[3] if $ab[3] > $a; |
|
388
|
0
|
0
|
|
|
|
0
|
$d = $ab[1] if $ab[1] < $d; |
|
389
|
0
|
0
|
|
|
|
0
|
if ( $all ) { |
|
390
|
0
|
0
|
0
|
|
|
0
|
$yMin = $ab[1] if !defined($yMin) || $ab[1] < $yMin; |
|
391
|
0
|
0
|
0
|
|
|
0
|
$yMax = $ab[3] if !defined($yMax) || $ab[3] > $yMax; |
|
392
|
|
|
|
|
|
|
} |
|
393
|
|
|
|
|
|
|
} |
|
394
|
|
|
|
|
|
|
|
|
395
|
6
|
100
|
66
|
|
|
39
|
next unless $_->{type} eq "text" && length($_->{text}); |
|
396
|
|
|
|
|
|
|
|
|
397
|
4
|
|
|
|
|
11
|
my $f = $_->{font}; |
|
398
|
4
|
|
|
|
|
26
|
my $font = $f->get_font($self); |
|
399
|
4
|
50
|
|
|
|
14
|
unless ( $font ) { |
|
400
|
0
|
|
|
|
|
0
|
carp("Can't happen?"); |
|
401
|
0
|
|
|
|
|
0
|
$f = $self->{_currentfont}; |
|
402
|
0
|
|
|
|
|
0
|
$font = $f->getfont($self); |
|
403
|
|
|
|
|
|
|
} |
|
404
|
4
|
|
|
|
|
9
|
my $upem = 1000; # as delivered by PDF::API2 |
|
405
|
4
|
|
|
|
|
13
|
my $size = $_->{size}; |
|
406
|
4
|
|
|
|
|
10
|
my $base = $_->{base}; |
|
407
|
4
|
|
50
|
|
|
24
|
my $mydir = $f->{direction} || 'ltr'; |
|
408
|
|
|
|
|
|
|
|
|
409
|
|
|
|
|
|
|
# Width and inkbox, if requested. |
|
410
|
4
|
50
|
66
|
|
|
16
|
if ( _hb_font_check( $f ) ) { |
|
|
|
50
|
|
|
|
|
|
|
411
|
0
|
|
|
|
|
0
|
$hb->set_font( $font->fontfilename ); |
|
412
|
0
|
|
|
|
|
0
|
$hb->set_size($size); |
|
413
|
0
|
0
|
|
|
|
0
|
$hb->set_language( $f->{language} ) if $f->{language}; |
|
414
|
0
|
0
|
|
|
|
0
|
$hb->set_direction( $f->{direction} ) if $f->{direction}; |
|
415
|
0
|
|
|
|
|
0
|
$hb->set_text( $_->{text} ); |
|
416
|
0
|
|
|
|
|
0
|
my $info = $hb->shaper; |
|
417
|
0
|
|
|
|
|
0
|
$mydir = $hb->get_direction; |
|
418
|
|
|
|
|
|
|
# warn("mydir $mydir\n"); |
|
419
|
|
|
|
|
|
|
|
|
420
|
0
|
0
|
|
|
|
0
|
if ( $all ) { |
|
421
|
0
|
|
|
|
|
0
|
my $ext = $hb->get_extents; |
|
422
|
0
|
|
|
|
|
0
|
foreach my $g ( @$info ) { |
|
423
|
0
|
|
|
|
|
0
|
my $e = shift(@$ext); |
|
424
|
|
|
|
|
|
|
printf STDERR ( "G %3d %6.2f %6.2f %6.2f %6.2f %6.2f\n", |
|
425
|
|
|
|
|
|
|
$g->{g}, $g->{ax}, |
|
426
|
0
|
|
|
|
|
0
|
@$e{ qw( x_bearing y_bearing width height ) } ) if 0; |
|
427
|
|
|
|
|
|
|
# It is easier to work with the baseline oriented box. |
|
428
|
0
|
|
|
|
|
0
|
$e->{xMin} = $e->{x_bearing}; |
|
429
|
0
|
|
|
|
|
0
|
$e->{yMin} = $e->{y_bearing} + $e->{height} - $base; |
|
430
|
0
|
|
|
|
|
0
|
$e->{xMax} = $e->{x_bearing} + $e->{width}; |
|
431
|
0
|
|
|
|
|
0
|
$e->{yMax} = $e->{y_bearing} - $base; |
|
432
|
|
|
|
|
|
|
|
|
433
|
0
|
0
|
0
|
|
|
0
|
$xMin //= $w + $e->{xMin} if $e->{width}; |
|
434
|
|
|
|
|
|
|
$yMin = $e->{yMin} |
|
435
|
0
|
0
|
0
|
|
|
0
|
if !defined($yMin) || $e->{yMin} < $yMin; |
|
436
|
|
|
|
|
|
|
$yMax = $e->{yMax} |
|
437
|
0
|
0
|
0
|
|
|
0
|
if !defined($yMax) || $e->{yMax} > $yMax; |
|
438
|
0
|
|
|
|
|
0
|
$xMax = $w + $e->{xMax}; |
|
439
|
0
|
|
|
|
|
0
|
$w += $g->{ax}; |
|
440
|
|
|
|
|
|
|
} |
|
441
|
|
|
|
|
|
|
} |
|
442
|
|
|
|
|
|
|
else { |
|
443
|
0
|
|
|
|
|
0
|
foreach my $g ( @$info ) { |
|
444
|
0
|
|
|
|
|
0
|
$w += $g->{ax}; |
|
445
|
|
|
|
|
|
|
} |
|
446
|
|
|
|
|
|
|
} |
|
447
|
|
|
|
|
|
|
} |
|
448
|
|
|
|
|
|
|
elsif ( $all && $font->can("extents") ) { |
|
449
|
0
|
|
|
|
|
0
|
my $e = $font->extents( $_->{text}, $size ); |
|
450
|
0
|
0
|
0
|
|
|
0
|
printf STDERR ("(%.2f,%.2f)(%.2f,%.2f) -> ", |
|
|
|
|
0
|
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
451
|
|
|
|
|
|
|
$xMin//0, $yMin//0, $xMax//0, $yMax//0 ) if $all && 0; |
|
452
|
0
|
0
|
|
|
|
0
|
$xMax = $w + $e->{xMax} if $all; |
|
453
|
0
|
|
|
|
|
0
|
$w += $e->{wx}; |
|
454
|
|
|
|
|
|
|
# warn("W \"", $_->{text}, "\" $w, ", $e->{width}, "\n"); |
|
455
|
0
|
0
|
|
|
|
0
|
if ( $all ) { |
|
456
|
0
|
|
|
|
|
0
|
$_ -= $base for $e->{yMin}, $e->{yMax}; |
|
457
|
|
|
|
|
|
|
# Baseline oriented box. |
|
458
|
0
|
|
0
|
|
|
0
|
$xMin //= $w - $e->{wx} + $e->{xMin}; |
|
459
|
|
|
|
|
|
|
$yMin = $e->{yMin} |
|
460
|
0
|
0
|
0
|
|
|
0
|
if !defined($yMin) || $e->{yMin} < $yMin; |
|
461
|
|
|
|
|
|
|
$yMax = $e->{yMax} |
|
462
|
0
|
0
|
0
|
|
|
0
|
if !defined($yMax) || $e->{yMax} > $yMax; |
|
463
|
0
|
|
|
|
|
0
|
printf STDERR ("(%.2f,%.2f)(%.2f,%.2f)\n", |
|
464
|
|
|
|
|
|
|
$xMin//0, $yMin//0, $xMax//0, $yMax//0 ) if 0; |
|
465
|
|
|
|
|
|
|
} |
|
466
|
|
|
|
|
|
|
} |
|
467
|
|
|
|
|
|
|
else { |
|
468
|
4
|
|
|
|
|
29
|
$w += $font->width( $_->{text} ) * $size; |
|
469
|
|
|
|
|
|
|
} |
|
470
|
|
|
|
|
|
|
|
|
471
|
|
|
|
|
|
|
# We have width. Now the rest of the layoutbox. |
|
472
|
4
|
|
|
|
|
1561
|
my ( $d0, $a0 ); |
|
473
|
4
|
|
|
|
|
26
|
$d0 = $f->get_descender * $size / $upem - $base; |
|
474
|
4
|
|
|
|
|
16
|
$a0 = $f->get_ascender * $size / $upem - $base; |
|
475
|
|
|
|
|
|
|
# Keep track of biggest decender/ascender. |
|
476
|
4
|
50
|
|
|
|
30
|
$d = $d0 if $d0 < $d; |
|
477
|
4
|
50
|
|
|
|
14
|
$a = $a0 if $a0 > $a; |
|
478
|
|
|
|
|
|
|
|
|
479
|
|
|
|
|
|
|
# Direction. |
|
480
|
4
|
|
33
|
|
|
23
|
$dir //= $mydir; |
|
481
|
4
|
50
|
|
|
|
19
|
$dir = 0 unless $dir eq $mydir; # mix |
|
482
|
|
|
|
|
|
|
} |
|
483
|
4
|
|
|
|
|
11
|
$bl = $a; |
|
484
|
4
|
|
|
|
|
8
|
$h = $a - $d; |
|
485
|
|
|
|
|
|
|
|
|
486
|
4
|
|
|
|
|
12
|
my $align = $self->{_alignment}; |
|
487
|
|
|
|
|
|
|
# warn("ALIGN: ", $align//"","\n"); |
|
488
|
4
|
0
|
33
|
|
|
16
|
if ( $self->{_width} && $dir && $w < $self->{_width} ) { |
|
|
|
|
33
|
|
|
|
|
|
489
|
0
|
0
|
0
|
|
|
0
|
if ( $dir eq 'rtl' && (!$align || $align eq "left") ) { |
|
|
|
|
0
|
|
|
|
|
|
490
|
0
|
|
|
|
|
0
|
$align = "right"; |
|
491
|
|
|
|
|
|
|
# warn("ALIGN: set to $align\n"); |
|
492
|
|
|
|
|
|
|
} |
|
493
|
|
|
|
|
|
|
} |
|
494
|
4
|
0
|
33
|
|
|
27
|
if ( $self->{_width} && $align && $w < $self->{_width} ) { |
|
|
|
|
33
|
|
|
|
|
|
495
|
|
|
|
|
|
|
# warn("ALIGNING...\n"); |
|
496
|
0
|
0
|
|
|
|
0
|
if ( $align eq "right" ) { |
|
|
|
0
|
|
|
|
|
|
|
497
|
|
|
|
|
|
|
# warn("ALIGNING: to $align\n"); |
|
498
|
0
|
|
|
|
|
0
|
$x += my $d = $self->{_width} - $w; |
|
499
|
0
|
0
|
|
|
|
0
|
$xMin += $d if defined $xMin; |
|
500
|
0
|
0
|
|
|
|
0
|
$xMax += $d if defined $xMax; |
|
501
|
|
|
|
|
|
|
} |
|
502
|
|
|
|
|
|
|
elsif ( $align eq "center" ) { |
|
503
|
|
|
|
|
|
|
# warn("ALIGNING: to $align\n"); |
|
504
|
0
|
|
|
|
|
0
|
$x += my $d = ( $self->{_width} - $w ) / 2; |
|
505
|
0
|
0
|
|
|
|
0
|
$xMin += $d if defined $xMin; |
|
506
|
0
|
0
|
|
|
|
0
|
$xMax += $d if defined $xMax; |
|
507
|
|
|
|
|
|
|
} |
|
508
|
|
|
|
|
|
|
} |
|
509
|
|
|
|
|
|
|
|
|
510
|
4
|
50
|
|
|
|
38
|
[ $bl, $x, $y-$h, $w, $h, |
|
511
|
|
|
|
|
|
|
defined $xMin ? ( $xMin, $yMin-$bl, $xMax-$xMin, $yMax-$yMin ) : ()]; |
|
512
|
|
|
|
|
|
|
} |
|
513
|
|
|
|
|
|
|
|
|
514
|
|
|
|
|
|
|
#### API |
|
515
|
|
|
|
|
|
|
sub load_font { |
|
516
|
3
|
|
|
3
|
0
|
10
|
my ( $self, $font, $fd ) = @_; |
|
517
|
|
|
|
|
|
|
|
|
518
|
3
|
50
|
|
|
|
14
|
if ( my $f = $fc->{$font} ) { |
|
519
|
|
|
|
|
|
|
# warn("Loaded font $font (cached)\n"); |
|
520
|
0
|
|
0
|
|
|
0
|
$fd->{ascender} //= $f->ascender; |
|
521
|
0
|
|
0
|
|
|
0
|
$fd->{descender} //= $f->descender; |
|
522
|
0
|
|
|
|
|
0
|
return $f; |
|
523
|
|
|
|
|
|
|
} |
|
524
|
3
|
|
|
|
|
5
|
my $ff; |
|
525
|
3
|
|
33
|
|
|
17
|
my $actual = Text::Layout::FontConfig->remap($font) // $font; |
|
526
|
3
|
50
|
|
|
|
21
|
if ( $actual =~ /\.[ot]tf$/ ) { |
|
|
|
50
|
|
|
|
|
|
|
527
|
0
|
|
|
|
|
0
|
eval { |
|
528
|
|
|
|
|
|
|
$ff = $self->{_context}->ttfont( $actual, |
|
529
|
|
|
|
|
|
|
-dokern => 1, |
|
530
|
|
|
|
|
|
|
$fd->{nosubset} |
|
531
|
0
|
0
|
|
|
|
0
|
? ( -nosubset => 1 ) |
|
532
|
|
|
|
|
|
|
: ( -nosubset => 0 ), |
|
533
|
|
|
|
|
|
|
); |
|
534
|
|
|
|
|
|
|
}; |
|
535
|
|
|
|
|
|
|
} |
|
536
|
|
|
|
|
|
|
elsif ( $actual =~ /(.*\.ttc)(?::(.*))?$/ ) { |
|
537
|
|
|
|
|
|
|
# This requires PDF::API2 augmentations. See below. |
|
538
|
0
|
|
|
|
|
0
|
my $file = $1; |
|
539
|
0
|
|
0
|
|
|
0
|
my $sel = $2 // ""; |
|
540
|
0
|
|
|
|
|
0
|
eval { |
|
541
|
0
|
|
|
|
|
0
|
my $ttc = $self->{_context}->ttc($file); |
|
542
|
|
|
|
|
|
|
$ff = $self->{_context}->ttcfont( $ttc, |
|
543
|
|
|
|
|
|
|
font => $sel, |
|
544
|
|
|
|
|
|
|
-dokern => 1, |
|
545
|
|
|
|
|
|
|
$fd->{nosubset} |
|
546
|
0
|
0
|
|
|
|
0
|
? ( -nosubset => 1 ) |
|
547
|
|
|
|
|
|
|
: ( -nosubset => 0 ), |
|
548
|
|
|
|
|
|
|
); |
|
549
|
|
|
|
|
|
|
}; |
|
550
|
|
|
|
|
|
|
} |
|
551
|
|
|
|
|
|
|
else { |
|
552
|
3
|
|
|
|
|
7
|
eval { |
|
553
|
3
|
|
|
|
|
28
|
$ff = $self->{_context}->corefont( $actual, -dokern => 1 ); |
|
554
|
|
|
|
|
|
|
}; |
|
555
|
|
|
|
|
|
|
} |
|
556
|
|
|
|
|
|
|
|
|
557
|
3
|
0
|
|
|
|
136781
|
croak( "Cannot load font: ", $actual, |
|
|
|
50
|
|
|
|
|
|
|
558
|
|
|
|
|
|
|
$actual ne $font ? " (remapped from $font)" : "", |
|
559
|
|
|
|
|
|
|
"\n", $@ ) unless $ff; |
|
560
|
|
|
|
|
|
|
# warn("Loaded font: $font\n"); |
|
561
|
3
|
|
|
|
|
16
|
$self->{font} = $ff; |
|
562
|
3
|
|
33
|
|
|
85
|
$fd->{ascender} //= $ff->ascender; |
|
563
|
3
|
|
33
|
|
|
57
|
$fd->{descender} //= $ff->descender; |
|
564
|
3
|
|
|
|
|
28
|
$fc->{$font} = $ff; |
|
565
|
3
|
|
|
|
|
31
|
return $ff; |
|
566
|
|
|
|
|
|
|
} |
|
567
|
|
|
|
|
|
|
|
|
568
|
|
|
|
|
|
|
sub xheight { |
|
569
|
0
|
|
|
0
|
0
|
0
|
$_[0]->data->{xheight}; |
|
570
|
|
|
|
|
|
|
} |
|
571
|
|
|
|
|
|
|
|
|
572
|
|
|
|
|
|
|
sub bbextend { |
|
573
|
0
|
|
|
0
|
0
|
0
|
my ( $cur, $bb, $dx, $dy ) = @_; |
|
574
|
0
|
|
0
|
|
|
0
|
$dx //= 0; |
|
575
|
0
|
|
0
|
|
|
0
|
$dy //= 0; |
|
576
|
0
|
0
|
|
|
|
0
|
if ( defined $cur->[0] ) { |
|
577
|
0
|
|
|
|
|
0
|
$dx += $cur->[2]; |
|
578
|
0
|
|
|
|
|
0
|
$dy += $cur->[3]; |
|
579
|
0
|
0
|
|
|
|
0
|
$cur->[0] = $bb->[0] + $dx if $cur->[0] > $bb->[0] + $dx; |
|
580
|
0
|
0
|
|
|
|
0
|
$cur->[1] = $bb->[1] + $dy if $cur->[1] > $bb->[1] + $dy; |
|
581
|
0
|
0
|
|
|
|
0
|
$cur->[2] = $bb->[2] + $dx if $cur->[2] < $bb->[2] + $dx; |
|
582
|
0
|
0
|
|
|
|
0
|
$cur->[3] = $bb->[3] + $dy if $cur->[3] < $bb->[3] + $dy; |
|
583
|
|
|
|
|
|
|
} |
|
584
|
|
|
|
|
|
|
else { |
|
585
|
0
|
|
|
|
|
0
|
$cur->[0] = $bb->[0] + $dx; |
|
586
|
0
|
|
|
|
|
0
|
$cur->[1] = $bb->[1] + $dy; |
|
587
|
0
|
|
|
|
|
0
|
$cur->[2] = $bb->[2] + $dx; |
|
588
|
0
|
|
|
|
|
0
|
$cur->[3] = $bb->[3] + $dy; |
|
589
|
|
|
|
|
|
|
} |
|
590
|
0
|
|
|
|
|
0
|
return $cur; # for convenience |
|
591
|
|
|
|
|
|
|
} |
|
592
|
|
|
|
|
|
|
|
|
593
|
|
|
|
|
|
|
################ Extensions to PDF::API2 ################ |
|
594
|
|
|
|
|
|
|
|
|
595
|
|
|
|
|
|
|
sub PDF::API2::Content::glyph_by_CId { |
|
596
|
0
|
|
|
0
|
0
|
0
|
my ( $self, $cid ) = @_; |
|
597
|
0
|
|
|
|
|
0
|
$self->add( sprintf("<%04x> Tj", $cid ) ); |
|
598
|
0
|
|
|
|
|
0
|
$self->{' font'}->fontfile->subsetByCId($cid); |
|
599
|
|
|
|
|
|
|
} |
|
600
|
|
|
|
|
|
|
|
|
601
|
|
|
|
|
|
|
# HarfBuzz requires a TT/OT font. Define the fontfilename method only |
|
602
|
|
|
|
|
|
|
# for classes that HarfBuzz can deal with. |
|
603
|
|
|
|
|
|
|
sub PDF::API2::Resource::CIDFont::TrueType::fontfilename { |
|
604
|
0
|
|
|
0
|
0
|
0
|
my ( $self ) = @_; |
|
605
|
0
|
|
|
|
|
0
|
$self->fontfile->{' font'}->{' fname'}; |
|
606
|
|
|
|
|
|
|
} |
|
607
|
|
|
|
|
|
|
|
|
608
|
|
|
|
|
|
|
# Add extents calculation for CIDfonts. |
|
609
|
|
|
|
|
|
|
# Note: Origin is x=0 at the baseline. |
|
610
|
|
|
|
|
|
|
sub PDF::API2::Resource::CIDFont::extents { |
|
611
|
1
|
|
|
1
|
0
|
22064
|
my ( $self, $text, $size ) = @_; |
|
612
|
1
|
|
50
|
|
|
5
|
$size //= 1; |
|
613
|
1
|
|
|
|
|
10
|
my $e = $self->extents_cid( $self->cidsByStr($text), $size ); |
|
614
|
1
|
|
|
|
|
8
|
return $e; |
|
615
|
|
|
|
|
|
|
} |
|
616
|
|
|
|
|
|
|
|
|
617
|
|
|
|
|
|
|
sub PDF::API2::Resource::CIDFont::extents_cid { |
|
618
|
1
|
|
|
1
|
0
|
262
|
my ( $self, $text, $size ) = @_; |
|
619
|
1
|
|
|
|
|
3
|
my $width = 0; |
|
620
|
1
|
|
|
|
|
2
|
my ( $xMin, $xMax, $yMin, $yMax, $bl ); |
|
621
|
|
|
|
|
|
|
|
|
622
|
1
|
|
|
|
|
4
|
my $upem = $self->data->{upem}; |
|
623
|
1
|
|
|
|
|
9
|
my $glyphs = $self->fontobj->{loca}->read->{glyphs}; |
|
624
|
1
|
|
|
|
|
50
|
$bl = $self->ascender; |
|
625
|
1
|
|
|
|
|
6
|
my $lastglyph = 0; |
|
626
|
1
|
|
|
|
|
2
|
my $lastwidth; |
|
627
|
|
|
|
|
|
|
|
|
628
|
|
|
|
|
|
|
# Fun ahead! Widths are in 1000 and xMin and such in upem. |
|
629
|
|
|
|
|
|
|
# Scale to 1000ths. |
|
630
|
1
|
|
|
|
|
4
|
my $scale = 1000 / $upem; |
|
631
|
|
|
|
|
|
|
|
|
632
|
1
|
|
|
|
|
5
|
foreach my $n (unpack('n*', $text)) { |
|
633
|
19
|
|
|
|
|
56
|
$width += $lastwidth = $self->wxByCId($n); |
|
634
|
19
|
50
|
33
|
|
|
832
|
if ($self->{'-dokern'} and $self->haveKernPairs()) { |
|
635
|
0
|
0
|
|
|
|
0
|
if ($self->kernPairCid($lastglyph, $n)) { |
|
636
|
0
|
|
|
|
|
0
|
$width -= $self->kernPairCid($lastglyph, $n); |
|
637
|
|
|
|
|
|
|
} |
|
638
|
|
|
|
|
|
|
} |
|
639
|
19
|
|
|
|
|
21
|
$lastglyph = $n; |
|
640
|
19
|
|
|
|
|
36
|
my $ex = $glyphs->[$n]; |
|
641
|
19
|
50
|
33
|
|
|
71
|
unless ( defined $ex && %$ex ) { |
|
642
|
0
|
|
|
|
|
0
|
warn("Missing glyph: $n\n"); |
|
643
|
0
|
|
|
|
|
0
|
next; |
|
644
|
|
|
|
|
|
|
} |
|
645
|
19
|
|
|
|
|
56
|
$ex->read; |
|
646
|
|
|
|
|
|
|
|
|
647
|
19
|
|
|
|
|
3858
|
my $e; |
|
648
|
|
|
|
|
|
|
# Copy while scaling. |
|
649
|
19
|
|
|
|
|
92
|
$e->{$_} = $ex->{$_} * $scale for qw( xMin yMin xMax yMax ); |
|
650
|
|
|
|
|
|
|
|
|
651
|
|
|
|
|
|
|
printf STDERR ( "G %3d %6.2f %6.2f %6.2f %6.2f %6.2f\n", |
|
652
|
|
|
|
|
|
|
$n, $lastwidth, |
|
653
|
19
|
|
|
|
|
22
|
@$e{ qw( xMin yMin xMax yMax ) } ) if 0; |
|
654
|
|
|
|
|
|
|
|
|
655
|
19
|
|
66
|
|
|
43
|
$xMin //= ($width - $lastwidth) + $e->{xMin}; |
|
656
|
19
|
100
|
100
|
|
|
69
|
$yMin = $e->{yMin} if !defined($yMin) || $e->{yMin} < $yMin; |
|
657
|
19
|
100
|
66
|
|
|
55
|
$yMax = $e->{yMax} if !defined($yMax) || $e->{yMax} > $yMax; |
|
658
|
19
|
|
|
|
|
54
|
$xMax = ($width - $lastwidth) + $e->{xMax}; |
|
659
|
|
|
|
|
|
|
} |
|
660
|
|
|
|
|
|
|
|
|
661
|
1
|
50
|
|
|
|
8
|
if ( defined $lastwidth ) { |
|
662
|
|
|
|
|
|
|
# $xMax += ($width - $lastwidth); |
|
663
|
|
|
|
|
|
|
} |
|
664
|
|
|
|
|
|
|
else { |
|
665
|
0
|
|
|
|
|
0
|
$xMin = $yMin = $xMax = $yMax = 0; |
|
666
|
0
|
|
|
|
|
0
|
$width = $self->missingwidth; |
|
667
|
|
|
|
|
|
|
} |
|
668
|
1
|
|
50
|
|
|
12
|
$_ = ($_//0)*$size/1000 for $xMin, $xMax, $yMin, $yMax, $bl; |
|
669
|
1
|
|
50
|
|
|
7
|
$_ = ($_//0)*$size/1000 for $width; |
|
670
|
|
|
|
|
|
|
|
|
671
|
1
|
|
|
|
|
16
|
return { x => $xMin, |
|
672
|
|
|
|
|
|
|
y => $yMin, |
|
673
|
|
|
|
|
|
|
width => $xMax - $xMin, |
|
674
|
|
|
|
|
|
|
height => $yMax - $yMin, |
|
675
|
|
|
|
|
|
|
# These are for convenience |
|
676
|
|
|
|
|
|
|
xMin => $xMin, |
|
677
|
|
|
|
|
|
|
yMin => $yMin, |
|
678
|
|
|
|
|
|
|
xMax => $xMax, |
|
679
|
|
|
|
|
|
|
yMax => $yMax, |
|
680
|
|
|
|
|
|
|
wx => $width, |
|
681
|
|
|
|
|
|
|
bl => $bl, |
|
682
|
|
|
|
|
|
|
}; |
|
683
|
|
|
|
|
|
|
} |
|
684
|
|
|
|
|
|
|
|
|
685
|
|
|
|
|
|
|
# Note: This is an augmented copy of the method from PDF::API2 2.047. |
|
686
|
5
|
|
|
5
|
|
66
|
no warnings 'redefine'; |
|
|
5
|
|
|
|
|
15
|
|
|
|
5
|
|
|
|
|
397
|
|
|
687
|
5
|
|
|
5
|
|
4154
|
use PDF::API2::Resource::CIDFont::TrueType::FontFile; |
|
|
5
|
|
|
|
|
487797
|
|
|
|
5
|
|
|
|
|
488
|
|
|
688
|
|
|
|
|
|
|
sub PDF::API2::Resource::CIDFont::TrueType::FontFile::new { |
|
689
|
|
|
|
|
|
|
package PDF::API2::Resource::CIDFont::TrueType::FontFile; |
|
690
|
1
|
|
|
1
|
1
|
285428
|
my ($class, $pdf, $file, %opts) = @_; |
|
691
|
1
|
|
|
|
|
4
|
my $data = {}; |
|
692
|
|
|
|
|
|
|
|
|
693
|
|
|
|
|
|
|
#### Start of changes #### |
|
694
|
5
|
|
|
5
|
|
51
|
use Carp qw(confess); |
|
|
5
|
|
|
|
|
9
|
|
|
|
5
|
|
|
|
|
14140
|
|
|
695
|
1
|
|
|
|
|
3
|
my $font; |
|
696
|
|
|
|
|
|
|
|
|
697
|
|
|
|
|
|
|
# If the file is already a suitable font object, use it. |
|
698
|
1
|
50
|
|
|
|
13
|
if ( UNIVERSAL::isa( $file, 'Font::TTF::Font' ) ) { |
|
699
|
0
|
|
|
|
|
0
|
$font = $file; |
|
700
|
|
|
|
|
|
|
} |
|
701
|
|
|
|
|
|
|
else { |
|
702
|
1
|
50
|
|
|
|
41
|
confess "cannot find font '$file'" unless -f $file; |
|
703
|
1
|
|
|
|
|
10
|
$font = Font::TTF::Font->open($file); |
|
704
|
|
|
|
|
|
|
} |
|
705
|
|
|
|
|
|
|
#### End of changes #### |
|
706
|
|
|
|
|
|
|
|
|
707
|
1
|
|
|
|
|
113019
|
$data->{'obj'} = $font; |
|
708
|
|
|
|
|
|
|
|
|
709
|
1
|
50
|
|
|
|
7
|
$class = ref($class) if ref($class); |
|
710
|
1
|
|
|
|
|
44
|
my $self = $class->SUPER::new(); |
|
711
|
|
|
|
|
|
|
|
|
712
|
1
|
|
|
|
|
40
|
$self->{'Filter'} = PDFArray(PDFName('FlateDecode')); |
|
713
|
1
|
|
|
|
|
42
|
$self->{' font'} = $font; |
|
714
|
1
|
|
|
|
|
4
|
$self->{' data'} = $data; |
|
715
|
|
|
|
|
|
|
|
|
716
|
1
|
50
|
|
|
|
6
|
$data->{'noembed'} = $opts{'embed'} ? 0 : 1; |
|
717
|
1
|
50
|
|
|
|
5
|
$data->{'iscff'} = defined($font->{'CFF '}) ? 1 : 0; |
|
718
|
|
|
|
|
|
|
|
|
719
|
1
|
50
|
|
|
|
3
|
$self->{'Subtype'} = PDFName('CIDFontType0C') if $data->{'iscff'}; |
|
720
|
|
|
|
|
|
|
|
|
721
|
1
|
|
|
|
|
14
|
$data->{'fontfamily'} = $font->{'name'}->read->find_name(1); |
|
722
|
1
|
|
|
|
|
910
|
$data->{'fontname'} = $font->{'name'}->read->find_name(4); |
|
723
|
|
|
|
|
|
|
|
|
724
|
1
|
|
|
|
|
29
|
$font->{'OS/2'}->read(); |
|
725
|
1
|
|
|
|
|
3643
|
my @stretch = qw( |
|
726
|
|
|
|
|
|
|
Normal |
|
727
|
|
|
|
|
|
|
UltraCondensed |
|
728
|
|
|
|
|
|
|
ExtraCondensed |
|
729
|
|
|
|
|
|
|
Condensed |
|
730
|
|
|
|
|
|
|
SemiCondensed |
|
731
|
|
|
|
|
|
|
Normal |
|
732
|
|
|
|
|
|
|
SemiExpanded |
|
733
|
|
|
|
|
|
|
Expanded |
|
734
|
|
|
|
|
|
|
ExtraExpanded |
|
735
|
|
|
|
|
|
|
UltraExpanded |
|
736
|
|
|
|
|
|
|
); |
|
737
|
1
|
|
50
|
|
|
14
|
$data->{'fontstretch'} = $stretch[$font->{'OS/2'}->{'usWidthClass'}] || 'Normal'; |
|
738
|
|
|
|
|
|
|
|
|
739
|
1
|
|
|
|
|
9
|
$data->{'fontweight'} = $font->{'OS/2'}->{'usWeightClass'}; |
|
740
|
|
|
|
|
|
|
|
|
741
|
1
|
|
|
|
|
9
|
$data->{'panose'} = pack('n', $font->{'OS/2'}->{'sFamilyClass'}); |
|
742
|
|
|
|
|
|
|
|
|
743
|
1
|
|
|
|
|
5
|
foreach my $p (qw[bFamilyType bSerifStyle bWeight bProportion bContrast bStrokeVariation bArmStyle bLetterform bMidline bXheight]) { |
|
744
|
10
|
|
|
|
|
31
|
$data->{'panose'} .= pack('C', $font->{'OS/2'}->{$p}); |
|
745
|
|
|
|
|
|
|
} |
|
746
|
|
|
|
|
|
|
|
|
747
|
1
|
|
|
|
|
12
|
$data->{'apiname'} = join('', map { ucfirst(lc(substr($_, 0, 2))) } split m/[^A-Za-z0-9\s]+/, $data->{'fontname'}); |
|
|
2
|
|
|
|
|
32
|
|
|
748
|
1
|
|
|
|
|
13
|
$data->{'fontname'} =~ s/[\x00-\x1f\s]//g; |
|
749
|
|
|
|
|
|
|
|
|
750
|
1
|
|
|
|
|
13
|
$data->{'altname'} = $font->{'name'}->find_name(1); |
|
751
|
1
|
|
|
|
|
96
|
$data->{'altname'} =~ s/[\x00-\x1f\s]//g; |
|
752
|
|
|
|
|
|
|
|
|
753
|
1
|
|
|
|
|
9
|
$data->{'subname'} = $font->{'name'}->find_name(2); |
|
754
|
1
|
|
|
|
|
31
|
$data->{'subname'} =~ s/[\x00-\x1f\s]//g; |
|
755
|
|
|
|
|
|
|
|
|
756
|
1
|
|
|
|
|
12
|
$font->{'cmap'}->read->find_ms(); |
|
757
|
1
|
50
|
|
|
|
5017
|
if (defined $font->{'cmap'}->find_ms()) { |
|
758
|
1
|
|
50
|
|
|
15
|
$data->{'issymbol'} = ($font->{'cmap'}->find_ms->{'Platform'} == 3 and $font->{'cmap'}->read->find_ms->{'Encoding'} == 0) || 0; |
|
759
|
|
|
|
|
|
|
} |
|
760
|
|
|
|
|
|
|
else { |
|
761
|
0
|
|
|
|
|
0
|
$data->{'issymbol'} = 0; |
|
762
|
|
|
|
|
|
|
} |
|
763
|
|
|
|
|
|
|
|
|
764
|
1
|
|
|
|
|
47
|
$data->{'upem'} = $font->{'head'}->read->{'unitsPerEm'}; |
|
765
|
|
|
|
|
|
|
|
|
766
|
|
|
|
|
|
|
$data->{'fontbbox'} = [ |
|
767
|
|
|
|
|
|
|
int($font->{'head'}->{'xMin'} * 1000 / $data->{'upem'}), |
|
768
|
|
|
|
|
|
|
int($font->{'head'}->{'yMin'} * 1000 / $data->{'upem'}), |
|
769
|
|
|
|
|
|
|
int($font->{'head'}->{'xMax'} * 1000 / $data->{'upem'}), |
|
770
|
1
|
|
|
|
|
38
|
int($font->{'head'}->{'yMax'} * 1000 / $data->{'upem'}), |
|
771
|
|
|
|
|
|
|
]; |
|
772
|
|
|
|
|
|
|
|
|
773
|
1
|
|
|
|
|
5
|
$data->{'stemv'} = 0; |
|
774
|
1
|
|
|
|
|
3
|
$data->{'stemh'} = 0; |
|
775
|
|
|
|
|
|
|
|
|
776
|
1
|
|
50
|
|
|
9
|
$data->{'missingwidth'} = int($font->{'hhea'}->read->{'advanceWidthMax'} * 1000 / $data->{'upem'}) || 1000; |
|
777
|
1
|
|
|
|
|
1158
|
$data->{'maxwidth'} = int($font->{'hhea'}->{'advanceWidthMax'} * 1000 / $data->{'upem'}); |
|
778
|
1
|
|
|
|
|
6
|
$data->{'ascender'} = int($font->{'hhea'}->read->{'Ascender'} * 1000 / $data->{'upem'}); |
|
779
|
1
|
|
|
|
|
22
|
$data->{'descender'} = int($font->{'hhea'}{'Descender'} * 1000 / $data->{'upem'}); |
|
780
|
|
|
|
|
|
|
|
|
781
|
1
|
|
|
|
|
4
|
$data->{'flags'} = 0; |
|
782
|
1
|
50
|
|
|
|
10
|
$data->{'flags'} |= 1 if $font->{'OS/2'}->read->{'bProportion'} == 9; |
|
783
|
1
|
50
|
33
|
|
|
25
|
$data->{'flags'} |= 2 unless $font->{'OS/2'}{'bSerifStyle'} > 10 and $font->{'OS/2'}{'bSerifStyle'} < 14; |
|
784
|
1
|
50
|
|
|
|
5
|
$data->{'flags'} |= 8 if $font->{'OS/2'}{'bFamilyType'} == 2; |
|
785
|
1
|
|
|
|
|
4
|
$data->{'flags'} |= 32; # if $font->{'OS/2'}{'bFamilyType'} > 3; |
|
786
|
1
|
50
|
|
|
|
6
|
$data->{'flags'} |= 64 if $font->{'OS/2'}{'bLetterform'} > 8; |
|
787
|
|
|
|
|
|
|
|
|
788
|
1
|
|
33
|
|
|
16
|
$data->{'capheight'} = $font->{'OS/2'}->{'CapHeight'} || int($data->{'fontbbox'}->[3] * 0.8); |
|
789
|
1
|
|
33
|
|
|
45
|
$data->{'xheight'} = $font->{'OS/2'}->{'xHeight'} || int($data->{'fontbbox'}->[3] * 0.4); |
|
790
|
|
|
|
|
|
|
|
|
791
|
1
|
50
|
|
|
|
6
|
if ($data->{'issymbol'}) { |
|
792
|
0
|
|
|
|
|
0
|
$data->{'e2u'} = [0xf000 .. 0xf0ff]; |
|
793
|
|
|
|
|
|
|
} |
|
794
|
|
|
|
|
|
|
else { |
|
795
|
1
|
|
|
|
|
44
|
$data->{'e2u'} = [unpack('U*', decode('cp1252', pack('C*', 0 .. 255)))]; |
|
796
|
|
|
|
|
|
|
} |
|
797
|
|
|
|
|
|
|
|
|
798
|
1
|
50
|
33
|
|
|
137
|
if ($font->{'post'}->read->{'FormatType'} == 3 and defined $font->{'cmap'}->read->find_ms()) { |
|
799
|
0
|
|
|
|
|
0
|
$data->{'g2n'} = []; |
|
800
|
0
|
|
|
|
|
0
|
foreach my $u (sort { $a <=> $b } keys %{$font->{'cmap'}->read->find_ms->{'val'}}) { |
|
|
0
|
|
|
|
|
0
|
|
|
|
0
|
|
|
|
|
0
|
|
|
801
|
0
|
|
|
|
|
0
|
my $n = nameByUni($u); |
|
802
|
0
|
|
|
|
|
0
|
$data->{'g2n'}->[$font->{'cmap'}->read->find_ms->{'val'}->{$u}] = $n; |
|
803
|
|
|
|
|
|
|
} |
|
804
|
|
|
|
|
|
|
} |
|
805
|
|
|
|
|
|
|
else { |
|
806
|
1
|
50
|
|
|
|
4616
|
$data->{'g2n'} = [ map { $_ || '.notdef' } @{$font->{'post'}->read->{'VAL'}} ]; |
|
|
262
|
|
|
|
|
713
|
|
|
|
1
|
|
|
|
|
10
|
|
|
807
|
|
|
|
|
|
|
} |
|
808
|
|
|
|
|
|
|
|
|
809
|
1
|
|
|
|
|
22
|
$data->{'italicangle'} = $font->{'post'}->{'italicAngle'}; |
|
810
|
1
|
|
|
|
|
7
|
$data->{'isfixedpitch'} = $font->{'post'}->{'isFixedPitch'}; |
|
811
|
1
|
|
|
|
|
4
|
$data->{'underlineposition'} = $font->{'post'}->{'underlinePosition'}; |
|
812
|
1
|
|
|
|
|
7
|
$data->{'underlinethickness'} = $font->{'post'}->{'underlineThickness'}; |
|
813
|
|
|
|
|
|
|
|
|
814
|
1
|
50
|
|
|
|
14
|
if ($self->iscff()) { |
|
815
|
0
|
|
|
|
|
0
|
$data->{'cff'} = readcffstructs($font); |
|
816
|
|
|
|
|
|
|
} |
|
817
|
|
|
|
|
|
|
|
|
818
|
1
|
50
|
|
|
|
23
|
if (defined $data->{'cff'}->{'ROS'}) { |
|
819
|
0
|
|
|
|
|
0
|
my %cffcmap = ( |
|
820
|
|
|
|
|
|
|
'Adobe:Japan1' => 'japanese', |
|
821
|
|
|
|
|
|
|
'Adobe:Korea1' => 'korean', |
|
822
|
|
|
|
|
|
|
'Adobe:CNS1' => 'traditional', |
|
823
|
|
|
|
|
|
|
'Adobe:GB1' => 'simplified', |
|
824
|
|
|
|
|
|
|
); |
|
825
|
0
|
|
|
|
|
0
|
my $key = $data->{'cff'}->{'ROS'}->[0] . ':' . $data->{'cff'}->{'ROS'}->[1]; |
|
826
|
0
|
|
0
|
|
|
0
|
my $ccmap = _look_for_cmap($cffcmap{$key} // $key); |
|
827
|
0
|
|
|
|
|
0
|
$data->{'u2g'} = $ccmap->{'u2g'}; |
|
828
|
0
|
|
|
|
|
0
|
$data->{'g2u'} = $ccmap->{'g2u'}; |
|
829
|
|
|
|
|
|
|
} |
|
830
|
|
|
|
|
|
|
else { |
|
831
|
1
|
|
|
|
|
4
|
$data->{'u2g'} = {}; |
|
832
|
|
|
|
|
|
|
|
|
833
|
1
|
|
|
|
|
13
|
my $gmap = $font->{'cmap'}->read->find_ms->{'val'}; |
|
834
|
1
|
|
|
|
|
175
|
foreach my $u (sort {$a <=> $b} keys %$gmap) { |
|
|
1656
|
|
|
|
|
2459
|
|
|
835
|
245
|
|
50
|
|
|
480
|
my $uni = $u || 0; |
|
836
|
245
|
|
|
|
|
734
|
$data->{'u2g'}->{$uni} = $gmap->{$uni}; |
|
837
|
|
|
|
|
|
|
} |
|
838
|
1
|
100
|
|
|
|
28
|
$data->{'g2u'} = [ map { $_ || 0 } $font->{'cmap'}->read->reverse() ]; |
|
|
262
|
|
|
|
|
1996
|
|
|
839
|
|
|
|
|
|
|
} |
|
840
|
|
|
|
|
|
|
|
|
841
|
1
|
50
|
|
|
|
38
|
if ($data->{'issymbol'}) { |
|
842
|
0
|
|
0
|
|
|
0
|
map { $data->{'u2g'}->{$_} ||= $font->{'cmap'}->read->ms_lookup($_) } (0xf000 .. 0xf0ff); |
|
|
0
|
|
|
|
|
0
|
|
|
843
|
0
|
|
0
|
|
|
0
|
map { $data->{'u2g'}->{$_ & 0xff} ||= $font->{'cmap'}->read->ms_lookup($_) } (0xf000 .. 0xf0ff); |
|
|
0
|
|
|
|
|
0
|
|
|
844
|
|
|
|
|
|
|
} |
|
845
|
|
|
|
|
|
|
|
|
846
|
1
|
50
|
100
|
|
|
3
|
$data->{'e2n'} = [ map { $data->{'g2n'}->[$data->{'u2g'}->{$_} || 0] || '.notdef' } @{$data->{'e2u'}} ]; |
|
|
256
|
|
|
|
|
1033
|
|
|
|
1
|
|
|
|
|
7
|
|
|
847
|
|
|
|
|
|
|
|
|
848
|
1
|
100
|
100
|
|
|
15
|
$data->{'e2g'} = [ map { $data->{'u2g'}->{$_ || 0} || 0 } @{$data->{'e2u'}} ]; |
|
|
256
|
|
|
|
|
799
|
|
|
|
1
|
|
|
|
|
6
|
|
|
849
|
1
|
|
|
|
|
13
|
$data->{'u2e'} = {}; |
|
850
|
1
|
|
|
|
|
9
|
foreach my $n (reverse 0 .. 255) { |
|
851
|
256
|
|
100
|
|
|
1123
|
$data->{'u2e'}->{$data->{'e2u'}->[$n]} //= $n; |
|
852
|
|
|
|
|
|
|
} |
|
853
|
|
|
|
|
|
|
|
|
854
|
1
|
|
|
|
|
5
|
$data->{'u2n'} = { map { $data->{'g2u'}->[$_] => $data->{'g2n'}->[$_] } (0 .. (scalar @{$data->{'g2u'}} - 1)) }; |
|
|
262
|
|
|
|
|
777
|
|
|
|
1
|
|
|
|
|
32
|
|
|
855
|
|
|
|
|
|
|
|
|
856
|
1
|
|
|
|
|
36
|
$data->{'wx'} = []; |
|
857
|
1
|
|
|
|
|
3
|
foreach my $i (0 .. (scalar @{$data->{'g2u'}} - 1)) { |
|
|
1
|
|
|
|
|
7
|
|
|
858
|
262
|
|
|
|
|
527
|
my $hmtx = $font->{'hmtx'}->read->{'advance'}->[$i]; |
|
859
|
262
|
100
|
|
|
|
9020
|
if ($hmtx) { |
|
860
|
261
|
|
|
|
|
593
|
$data->{'wx'}->[$i] = int($hmtx * 1000 / $data->{'upem'}); |
|
861
|
|
|
|
|
|
|
} |
|
862
|
|
|
|
|
|
|
else { |
|
863
|
1
|
|
|
|
|
6
|
$data->{'wx'}->[$i] = $data->{'missingwidth'}; |
|
864
|
|
|
|
|
|
|
} |
|
865
|
|
|
|
|
|
|
} |
|
866
|
|
|
|
|
|
|
|
|
867
|
1
|
|
|
|
|
15
|
$data->{'kern'} = read_kern_table($font, $data->{'upem'}, $self); |
|
868
|
1
|
50
|
|
|
|
18
|
delete $data->{'kern'} unless defined $data->{'kern'}; |
|
869
|
|
|
|
|
|
|
|
|
870
|
1
|
|
|
|
|
9
|
$data->{'fontname'} =~ s/\s+//g; |
|
871
|
1
|
|
|
|
|
5
|
$data->{'fontfamily'} =~ s/\s+//g; |
|
872
|
1
|
|
|
|
|
3
|
$data->{'apiname'} =~ s/\s+//g; |
|
873
|
1
|
|
|
|
|
4
|
$data->{'altname'} =~ s/\s+//g; |
|
874
|
1
|
|
|
|
|
4
|
$data->{'subname'} =~ s/\s+//g; |
|
875
|
|
|
|
|
|
|
|
|
876
|
1
|
|
|
|
|
8
|
$self->subsetByCId(0); |
|
877
|
|
|
|
|
|
|
|
|
878
|
1
|
|
|
|
|
6272
|
return ($self, $data); |
|
879
|
|
|
|
|
|
|
} |
|
880
|
5
|
|
|
5
|
|
51
|
use warnings 'redefine'; |
|
|
5
|
|
|
|
|
10
|
|
|
|
5
|
|
|
|
|
1198
|
|
|
881
|
|
|
|
|
|
|
|
|
882
|
|
|
|
|
|
|
# These are additions. Methods of $pdf for convenience. |
|
883
|
|
|
|
|
|
|
|
|
884
|
|
|
|
|
|
|
# Open a TTC (TrueType font Collection) file. |
|
885
|
|
|
|
|
|
|
# |
|
886
|
|
|
|
|
|
|
# This is not really API related, but it is handy to use the API file |
|
887
|
|
|
|
|
|
|
# lookups. |
|
888
|
|
|
|
|
|
|
# |
|
889
|
|
|
|
|
|
|
# Returns a Font::TTF::Ttc object. |
|
890
|
|
|
|
|
|
|
|
|
891
|
|
|
|
|
|
|
sub PDF::API2::ttc { |
|
892
|
0
|
|
|
0
|
0
|
|
my ( $self, $name, %opts ) = @_; |
|
893
|
0
|
0
|
|
|
|
|
my $file = $self->can("_find_font")->($name) |
|
894
|
|
|
|
|
|
|
or croak "Unable to find ttc \"$name\""; |
|
895
|
0
|
|
|
|
|
|
require Font::TTF::Font; |
|
896
|
0
|
|
|
|
|
|
require Font::TTF::Ttc; |
|
897
|
0
|
|
|
|
|
|
return Font::TTF::Ttc->open($file); |
|
898
|
|
|
|
|
|
|
} |
|
899
|
|
|
|
|
|
|
|
|
900
|
|
|
|
|
|
|
# Create a API font from one of ttc fonts. |
|
901
|
|
|
|
|
|
|
# |
|
902
|
|
|
|
|
|
|
# The font is selected with a C option. |
|
903
|
|
|
|
|
|
|
# Font selectors are |
|
904
|
|
|
|
|
|
|
# |
|
905
|
|
|
|
|
|
|
# * The PostScript name of the font (case insensitive) |
|
906
|
|
|
|
|
|
|
# * The family name : style (case insensitive), where both may be omitted |
|
907
|
|
|
|
|
|
|
# to select the first matching. |
|
908
|
|
|
|
|
|
|
# Note that the style must match what is in the font. |
|
909
|
|
|
|
|
|
|
# E.g. "bold italic" (with a space, in this order). |
|
910
|
|
|
|
|
|
|
# |
|
911
|
|
|
|
|
|
|
# If no font option is given, the first font found is used. |
|
912
|
|
|
|
|
|
|
|
|
913
|
|
|
|
|
|
|
our @CARP_NOT = qw( ChordPro::Logger ); |
|
914
|
|
|
|
|
|
|
|
|
915
|
|
|
|
|
|
|
sub PDF::API2::ttcfont { |
|
916
|
0
|
|
|
0
|
0
|
|
my ( $self, $ttc, %opts ) = @_; |
|
917
|
|
|
|
|
|
|
|
|
918
|
5
|
|
|
5
|
|
35
|
use Carp qw(confess); |
|
|
5
|
|
|
|
|
29
|
|
|
|
5
|
|
|
|
|
4690
|
|
|
919
|
|
|
|
|
|
|
|
|
920
|
0
|
|
0
|
|
|
|
my $sel = delete $opts{font} // ""; |
|
921
|
|
|
|
|
|
|
|
|
922
|
0
|
|
|
|
|
|
my $font; |
|
923
|
0
|
|
|
|
|
|
foreach my $d ( @{ $ttc->{directs} } ) { |
|
|
0
|
|
|
|
|
|
|
|
924
|
0
|
|
|
|
|
|
$d->{name}->read; |
|
925
|
0
|
0
|
|
|
|
|
if ( $sel =~ /^(.*):(.*)/ ) { |
|
|
|
0
|
|
|
|
|
|
|
926
|
0
|
0
|
0
|
|
|
|
next if $1 && lc($d->{name}->find_name(1)) ne lc($1); |
|
927
|
0
|
0
|
0
|
|
|
|
next if $2 && lc($d->{name}->find_name(2)) ne lc($2); |
|
928
|
|
|
|
|
|
|
} |
|
929
|
|
|
|
|
|
|
elsif ( $sel ) { |
|
930
|
0
|
0
|
|
|
|
|
next unless lc($d->{name}->find_name(6)) eq lc($sel); |
|
931
|
|
|
|
|
|
|
} |
|
932
|
|
|
|
|
|
|
# else: use first found. |
|
933
|
|
|
|
|
|
|
|
|
934
|
0
|
|
|
|
|
|
$font = $d; |
|
935
|
0
|
|
|
|
|
|
last; |
|
936
|
|
|
|
|
|
|
} |
|
937
|
0
|
0
|
|
|
|
|
confess "Missing font '$sel' in ttcfont" unless $font; |
|
938
|
0
|
0
|
|
|
|
|
$opts{-unicodemap} = 1 unless exists $opts{-unicodemap}; |
|
939
|
0
|
0
|
|
|
|
|
$opts{embed} = 1 unless exists $opts{embed}; |
|
940
|
|
|
|
|
|
|
|
|
941
|
0
|
|
|
|
|
|
require PDF::API2::Resource::CIDFont::TrueType; |
|
942
|
0
|
|
|
|
|
|
my $obj = PDF::API2::Resource::CIDFont::TrueType->new($self->{'pdf'}, $font, %opts); |
|
943
|
0
|
|
|
|
|
|
$self->{'pdf'}->out_obj($self->{'pages'}); |
|
944
|
0
|
0
|
|
|
|
|
$obj->tounicodemap() if $opts{-unicodemap}; |
|
945
|
|
|
|
|
|
|
|
|
946
|
0
|
|
|
|
|
|
return $obj; |
|
947
|
|
|
|
|
|
|
} |
|
948
|
|
|
|
|
|
|
|
|
949
|
|
|
|
|
|
|
################ Extensions to PDF::Builder ################ |
|
950
|
|
|
|
|
|
|
|
|
951
|
|
|
|
|
|
|
sub PDF::Builder::Content::glyph_by_CId { |
|
952
|
0
|
|
|
0
|
|
|
my ( $self, $cid ) = @_; |
|
953
|
0
|
|
|
|
|
|
$self->add( sprintf("<%04x> Tj", $cid ) ); |
|
954
|
0
|
|
|
|
|
|
$self->{' font'}->fontfile->subsetByCId($cid); |
|
955
|
|
|
|
|
|
|
} |
|
956
|
|
|
|
|
|
|
|
|
957
|
|
|
|
|
|
|
# HarfBuzz requires a TT/OT font. Define the fontfilename method only |
|
958
|
|
|
|
|
|
|
# for classes that HarfBuzz can deal with. |
|
959
|
|
|
|
|
|
|
sub PDF::Builder::Resource::CIDFont::TrueType::fontfilename { |
|
960
|
0
|
|
|
0
|
|
|
my ( $self ) = @_; |
|
961
|
0
|
|
|
|
|
|
$self->fontfile->{' font'}->{' fname'}; |
|
962
|
|
|
|
|
|
|
} |
|
963
|
|
|
|
|
|
|
|
|
964
|
|
|
|
|
|
|
################ For debugging/convenience ################ |
|
965
|
|
|
|
|
|
|
|
|
966
|
|
|
|
|
|
|
# Shows the bounding box of the last piece of text that was rendered. |
|
967
|
|
|
|
|
|
|
sub showbb { |
|
968
|
0
|
|
|
0
|
0
|
|
my ( $self, $gfx, $x, $y, $col ) = @_; |
|
969
|
0
|
|
0
|
|
|
|
$x //= $self->{_lastx}; |
|
970
|
0
|
|
0
|
|
|
|
$y //= $self->{_lasty}; |
|
971
|
0
|
|
0
|
|
|
|
$col ||= "magenta"; |
|
972
|
|
|
|
|
|
|
|
|
973
|
0
|
|
|
|
|
|
my ( $ink, $bb ) = $self->get_pixel_extents; |
|
974
|
0
|
|
|
|
|
|
my $bl = $bb->{bl}; |
|
975
|
|
|
|
|
|
|
# Bounding box, top-left coordinates. |
|
976
|
|
|
|
|
|
|
printf( "Ink: %6.2f %6.2f %6.2f %6.2f\n", |
|
977
|
0
|
|
|
|
|
|
@$ink{qw( x y width height )} ); |
|
978
|
|
|
|
|
|
|
printf( "Layout: %6.2f %6.2f %6.2f %6.2f BL %.2f\n", |
|
979
|
0
|
|
|
|
|
|
@$bb{qw( x y width height )}, $bl ); |
|
980
|
|
|
|
|
|
|
|
|
981
|
|
|
|
|
|
|
# NOTE: Some fonts include natural spacing in the bounding box. |
|
982
|
|
|
|
|
|
|
# NOTE: Some fonts exclude accents on capitals from the bounding box. |
|
983
|
|
|
|
|
|
|
|
|
984
|
0
|
|
|
|
|
|
$gfx->save; |
|
985
|
0
|
|
|
|
|
|
$gfx->translate( $x, $y ); |
|
986
|
|
|
|
|
|
|
|
|
987
|
|
|
|
|
|
|
# Show origin. |
|
988
|
0
|
|
|
|
|
|
_showloc($gfx); |
|
989
|
|
|
|
|
|
|
|
|
990
|
|
|
|
|
|
|
# Show baseline. |
|
991
|
0
|
|
|
|
|
|
_line( $gfx, $bb->{x}, -$bl, $bb->{width}, 0, $col ); |
|
992
|
0
|
|
|
|
|
|
$gfx->restore; |
|
993
|
|
|
|
|
|
|
|
|
994
|
|
|
|
|
|
|
# Show layout box. |
|
995
|
0
|
|
|
|
|
|
$gfx->save; |
|
996
|
0
|
|
|
|
|
|
$gfx->linewidth( 0.25 ); |
|
997
|
0
|
|
|
|
|
|
$gfx->strokecolor($col); |
|
998
|
0
|
|
|
|
|
|
$gfx->translate( $x, $y ); |
|
999
|
0
|
|
|
|
|
|
for my $e ( $bb ) { |
|
1000
|
0
|
|
|
|
|
|
$gfx->rect( @$e{ qw( x y width height ) } ); |
|
1001
|
0
|
|
|
|
|
|
$gfx->stroke; |
|
1002
|
|
|
|
|
|
|
} |
|
1003
|
0
|
|
|
|
|
|
$gfx->restore; |
|
1004
|
|
|
|
|
|
|
|
|
1005
|
|
|
|
|
|
|
# Show ink box. |
|
1006
|
0
|
|
|
|
|
|
$gfx->save; |
|
1007
|
0
|
|
|
|
|
|
$gfx->linewidth( 0.25 ); |
|
1008
|
0
|
|
|
|
|
|
$gfx->strokecolor("cyan"); |
|
1009
|
0
|
|
|
|
|
|
$gfx->translate( $x, $y ); |
|
1010
|
0
|
|
|
|
|
|
for my $e ( $ink ) { |
|
1011
|
0
|
|
|
|
|
|
$gfx->rect( @$e{ qw( x y width height ) } ); |
|
1012
|
0
|
|
|
|
|
|
$gfx->stroke; |
|
1013
|
|
|
|
|
|
|
} |
|
1014
|
0
|
|
|
|
|
|
$gfx->restore; |
|
1015
|
|
|
|
|
|
|
} |
|
1016
|
|
|
|
|
|
|
|
|
1017
|
|
|
|
|
|
|
sub _showloc { |
|
1018
|
0
|
|
|
0
|
|
|
my ( $gfx, $x, $y, $d, $col ) = @_; |
|
1019
|
0
|
|
0
|
|
|
|
$x ||= 0; $y ||= 0; $d ||= 50; $col ||= "blue"; |
|
|
0
|
|
0
|
|
|
|
|
|
|
0
|
|
0
|
|
|
|
|
|
|
0
|
|
0
|
|
|
|
|
|
1020
|
|
|
|
|
|
|
|
|
1021
|
0
|
|
|
|
|
|
_line( $gfx, $x-$d, $y, 2*$d, 0, $col ); |
|
1022
|
0
|
|
|
|
|
|
_line( $gfx, $x, $y-$d, 0, 2*$d, $col ); |
|
1023
|
|
|
|
|
|
|
} |
|
1024
|
|
|
|
|
|
|
|
|
1025
|
|
|
|
|
|
|
sub _line { |
|
1026
|
0
|
|
|
0
|
|
|
my ( $gfx, $x, $y, $w, $h, $col, $lw ) = @_; |
|
1027
|
0
|
|
0
|
|
|
|
$col ||= "black"; |
|
1028
|
0
|
|
0
|
|
|
|
$lw ||= 0.5; |
|
1029
|
|
|
|
|
|
|
|
|
1030
|
0
|
|
|
|
|
|
$gfx->save; |
|
1031
|
0
|
|
|
|
|
|
$gfx->move( $x, $y ); |
|
1032
|
0
|
|
|
|
|
|
$gfx->line( $x+$w, $y+$h ); |
|
1033
|
0
|
|
|
|
|
|
$gfx->linewidth($lw); |
|
1034
|
0
|
|
|
|
|
|
$gfx->strokecolor($col); |
|
1035
|
0
|
|
|
|
|
|
$gfx->stroke; |
|
1036
|
0
|
|
|
|
|
|
$gfx->restore; |
|
1037
|
|
|
|
|
|
|
} |
|
1038
|
|
|
|
|
|
|
|
|
1039
|
|
|
|
|
|
|
1; |