File Coverage

blib/lib/Text/Layout/PDFAPI2.pm
Criterion Covered Total %
statement 141 352 40.0
branch 35 134 26.1
condition 28 157 17.8
subroutine 13 22 59.0
pod 1 10 10.0
total 218 675 32.3


line stmt bran cond sub pod time code
1             #! perl
2              
3 4     4   266285 use strict;
  4         14  
  4         127  
4 4     4   38 use warnings;
  4         9  
  4         80  
5 4     4   18 use utf8;
  4         7  
  4         18  
6              
7             package Text::Layout::PDFAPI2;
8              
9 4     4   498 use parent 'Text::Layout';
  4         260  
  4         17  
10 4     4   193 use Carp;
  4         7  
  4         210  
11 4     4   22 use List::Util qw(max);
  4         6  
  4         13937  
12              
13             my $hb;
14             my $fc;
15              
16             #### API
17             sub new {
18 2     2 1 13 my ( $pkg, @data ) = @_;
19 2 50 33     26 unless ( @data == 1 && ref($data[0]) =~ /^PDF::(API2|Builder)\b/ ) {
20 0         0 croak("Usage: Text::Layout::PDFAPI2->new(\$pdf)");
21             }
22 2         17 my $self = $pkg->SUPER::new;
23 2         12 $self->{_context} = $data[0];
24 2 50 33     12 if ( !$fc || $fc->{__PDF__} ne $data[0] ) {
25             # Init cache.
26 2         4 $fc = { __PDF__ => $data[0] };
27 2         10 Text::Layout::FontConfig->reset;
28             }
29 2         7 $self;
30             }
31              
32             # Creates a (singleton) HarfBuzz::Shaper object.
33             sub _hb_init {
34 0 0   0   0 return $hb if defined $hb;
35 0         0 $hb = 0;
36 0         0 eval {
37 0         0 require HarfBuzz::Shaper;
38 0         0 $hb = HarfBuzz::Shaper->new;
39             };
40 0         0 return $hb;
41             }
42              
43             # Verify if a font needs shaping, and we can do that.
44             sub _hb_font_check {
45 4     4   6 my ( $f ) = @_;
46 4 100       25 return $f->{_hb_checked} if defined $f->{_hb_checked};
47              
48 1 50       3 if ( $f->get_shaping ) {
49 0         0 my $fn = $f->to_string;
50 0 0       0 if ( $f->{font}->can("fontfilename") ) {
51 0 0       0 if ( _hb_init() ) {
52             # warn("Font $fn will use shaping.\n");
53 0         0 return $f->{_hb_checked} = 1;
54             }
55 0         0 carp("Font $fn: Requires shaping but HarfBuzz cannot be loaded.");
56             }
57             else {
58 0         0 carp("Font $fn: Shaping not supported");
59             }
60             }
61             else {
62             # warn("Font ", $f->to_string, " does not need shaping.\n");
63             }
64 1         4 return $f->{_hb_checked} = 0;
65             }
66              
67             #### API
68             sub render {
69 1     1 0 3 my ( $self, $x, $y, $text, $fp ) = @_;
70              
71 1         2 $self->{_lastx} = $x;
72 1         1 $self->{_lasty} = $y;
73              
74 1         3 my @bb = $self->get_pixel_bbox;
75 1         3 my $bl = $bb[0];
76 1   50     4 my $align = $self->{_alignment} // 0;
77 1 50       2 if ( $self->{_width} ) {
78 0         0 my $w = $bb[3];
79 0 0       0 if ( $w < $self->{_width} ) {
80 0 0       0 if ( $align eq "right" ) {
    0          
81 0         0 $x += $self->{_width} - $w;
82             }
83             elsif ( $align eq "center" ) {
84 0         0 $x += ( $self->{_width} - $w ) / 2;
85             }
86             else {
87 0         0 $x += $bb[1];
88             }
89             }
90             }
91 1         2 my $upem = 1000;
92              
93 1         1 foreach my $fragment ( @{ $self->{_content} } ) {
  1         2  
94 1 50       3 next unless length($fragment->{text});
95 1         2 my $x0 = $x;
96 1         1 my $y0 = $y;
97 1         2 my $f = $fragment->{font};
98 1         2 my $font = $f->get_font($self);
99 1 50       2 unless ( $font ) {
100 0         0 carp("Can't happen?");
101 0         0 $f = $self->{_currentfont};
102 0         0 $font = $f->getfont($self);
103             }
104 1         10 $text->strokecolor( $fragment->{color} );
105 1         179 $text->fillcolor( $fragment->{color} );
106 1   33     127 $text->font( $font, $fragment->{size} || $self->{_currentsize} );
107              
108 1 50       185 if ( _hb_font_check($f) ) {
109 0         0 $hb->set_font( $font->fontfilename );
110 0   0     0 $hb->set_size( $fragment->{size} || $self->{_currentsize} );
111 0         0 $hb->set_text( $fragment->{text} );
112 0 0       0 $hb->set_direction( $f->{direction} ) if $f->{direction};
113 0 0       0 $hb->set_language( $f->{language} ) if $f->{language};
114 0         0 my $info = $hb->shaper($fp);
115 0         0 my $y = $y - $fragment->{base} - $bl;
116 0   0     0 my $sz = $fragment->{size} || $self->{_currentsize};
117 0         0 my $w = 0;
118 0         0 $w += $_->{ax} for @$info;
119              
120 0 0       0 if ( $fragment->{bgcolor} ) {
121 0         0 my $y = $y0;
122 0         0 my $h = -$sz*($font->ascender-$font->descender)/$upem;
123 0         0 my $x = $x0;
124 0         0 $text->add(PDF::API2::Content::_save());
125              
126 0         0 $text->add($text->_fillcolor($fragment->{bgcolor}));
127 0         0 $text->add($text->_strokecolor($fragment->{bgcolor}));
128 0         0 $text->add(PDF::API2::Content::_linewidth(2));
129 0         0 $text->add(PDF::API2::Content::_move($x, $y));
130 0         0 $text->add(PDF::API2::Content::_line($x+$w, $y));
131 0         0 $text->add(PDF::API2::Content::_line($x+$w, $y+$h));
132 0         0 $text->add(PDF::API2::Content::_line($x, $y+$h));
133 0         0 $text->add('h'); # close
134 0         0 $text->add('B'); # fillstroke
135 0         0 $text->add(PDF::API2::Content::_restore());
136             }
137              
138 0         0 foreach my $g ( @$info ) {
139 0         0 $text->translate( $x + $g->{dx}, $y - $g->{dy} );
140 0         0 $text->glyph_by_CId( $g->{g} );
141 0         0 $x += $g->{ax};
142 0         0 $y += $g->{ay};
143             }
144             }
145             else {
146             printf("%.2f %.2f %.2f \"%s\" %s\n",
147             $x, $y-$fragment->{base}-$bl,
148             $font->width($fragment->{text}) * ($fragment->{size} || $self->{_currentsize}),
149             $fragment->{text},
150             join(" ", $fragment->{font}->{family},
151             $fragment->{font}->{style},
152             $fragment->{font}->{weight},
153             $fragment->{size} || $self->{_currentsize},
154             $fragment->{color},
155             $fragment->{underline}||'""', $fragment->{underline_color}||'""',
156 1         1 $fragment->{strikethrough}||'""', $fragment->{strikethrough_color}||'""',
157             ),
158             ) if 0;
159 1         3 my $t = $fragment->{text};
160 1 50       2 if ( $t ne "" ) {
161              
162             # See ChordPro issue 240.
163 1 50 33     3 if ( $font->issymbol && $font->is_standard ) {
164             # This enables byte access to these symbol fonts.
165 0         0 utf8::downgrade( $t, 1 );
166             }
167              
168 1         7 my $y = $y-$fragment->{base}-$bl;
169 1   33     3 my $sz = $fragment->{size} || $self->{_currentsize};
170 1         3 my $w = $font->width($t) * $sz;
171              
172 1 50       267 if ( $fragment->{bgcolor} ) {
173 0         0 my $y = $y0;
174 0         0 my $h = -$sz*($font->ascender-$font->descender)/$upem;
175 0         0 my $x = $x0;
176 0         0 $text->add(PDF::API2::Content::_save());
177              
178 0         0 $text->add($text->_fillcolor($fragment->{bgcolor}));
179 0         0 $text->add($text->_strokecolor($fragment->{bgcolor}));
180 0         0 $text->add(PDF::API2::Content::_linewidth(2));
181 0         0 $text->add(PDF::API2::Content::_move($x, $y));
182 0         0 $text->add(PDF::API2::Content::_line($x+$w, $y));
183 0         0 $text->add(PDF::API2::Content::_line($x+$w, $y+$h));
184 0         0 $text->add(PDF::API2::Content::_line($x, $y+$h));
185 0         0 $text->add('h'); # close
186 0         0 $text->add('B'); # fillstroke
187 0         0 $text->add(PDF::API2::Content::_restore());
188             }
189              
190 1         6 $text->translate( $x, $y );
191 1         432 $text->text($t);
192 1         439 $x += $w;
193             }
194             }
195              
196 1 50       3 next unless $x > $x0;
197              
198 1         2 my $dw = 1000;
199 1         4 my $xh = $font->xheight;
200              
201 1         5 my @strikes;
202 1 50 33     4 if ( $fragment->{underline} && $fragment->{underline} ne 'none' ) {
203 0   0     0 my $sz = $fragment->{size} || $self->{_currentsize};
204             my $d = -( $f->{underline_position}
205 0   0     0 || $font->underlineposition ) * $sz/$dw;
206             my $h = ( $f->{underline_thickness}
207 0   0     0 || $font->underlinethickness ) * $sz/$dw;
208 0   0     0 my $col = $fragment->{underline_color} // $fragment->{color};
209 0 0       0 if ( $fragment->{underline} eq 'double' ) {
210 0         0 push( @strikes, [ $d-0.125*$h, $h * 0.75, $col ],
211             [ $d+1.125*$h, $h * 0.75, $col ] );
212             }
213             else {
214 0         0 push( @strikes, [ $d+$h/2, $h, $col ] );
215             }
216             }
217              
218 1 50       2 if ( $fragment->{strikethrough} ) {
219 0   0     0 my $sz = $fragment->{size} || $self->{_currentsize};
220             my $d = -( $f->{strikeline_position}
221             ? $f->{strikeline_position}
222 0 0       0 : 0.6*$xh ) * $sz/$dw;
223             my $h = ( $f->{strikeline_thickness}
224             || $f->{underline_thickness}
225 0   0     0 || $font->underlinethickness ) * $sz/$dw;
226 0   0     0 my $col = $fragment->{strikethrough_color} // $fragment->{color};
227 0         0 push( @strikes, [ $d+$h/2, $h, $col ] );
228             }
229              
230 1 50 33     3 if ( $fragment->{overline} && $fragment->{overline} ne 'none' ) {
231 0   0     0 my $sz = $fragment->{size} || $self->{_currentsize};
232             my $h = ( $f->{overline_thickness}
233             || $f->{underline_thickness}
234 0   0     0 || $font->underlinethickness ) * $sz/$dw;
235             my $d = -( $f->{overline_position}
236 0 0       0 ? $f->{overline_position} * $sz/$dw
237             : $xh*$sz/$dw + 2*$h );
238 0   0     0 my $col = $fragment->{overline_color} // $fragment->{color};
239 0 0       0 if ( $fragment->{overline} eq 'double' ) {
240 0         0 push( @strikes, [ $d-0.125*$h, $h * 0.75, $col ],
241             [ $d+1.125*$h, $h * 0.75, $col ] );
242             }
243             else {
244 0         0 push( @strikes, [ $d+$h/2, $h, $col ] );
245             }
246             }
247 1         2 for ( @strikes ) {
248              
249             # Mostly copied from PDF::API2::Content::_text_underline.
250 0         0 $text->add_post(PDF::API2::Content::_save());
251              
252 0         0 $text->add_post($text->_strokecolor($_->[2]));
253 0         0 $text->add_post(PDF::API2::Content::_linewidth($_->[1]));
254 0         0 $text->add_post(PDF::API2::Content::_move($x0, $y0-$fragment->{base}-$bl-$_->[0]));
255 0         0 $text->add_post(PDF::API2::Content::_line($x, $y0-$fragment->{base}-$bl-$_->[0]));
256 0         0 $text->add_post(PDF::API2::Content::_stroke());
257 0         0 $text->add_post(PDF::API2::Content::_restore());
258             }
259              
260 1 50       106 if ( $fragment->{href} ) {
261 0   0     0 my $sz = $fragment->{size} || $self->{_currentsize};
262 0         0 my $ann = $text->{' apipage'}->annotation;
263             $ann->url( $fragment->{href},
264             # -border => [ 0, 0, 1 ],
265 0         0 -rect => [ $x0, $y0, #-$fragment->{base}-$bl,
266             $x, $y0 - $sz ]
267             );
268             }
269             }
270             }
271              
272             #### API
273             sub bbox {
274 3     3 0 8 my ( $self, $all ) = @_;
275              
276 3         7 my ( $bl, $x, $y, $w, $h ) = (0) x 4;
277 3         4 my ( $d, $a ) = (0) x 2;
278 3         7 my ( $xMin, $xMax, $yMin, $yMax );
279 3         0 my $dir;
280              
281 3         3 foreach ( @{ $self->{_content} } ) {
  3         7  
282 3         4 my $f = $_->{font};
283 3         8 my $font = $f->get_font($self);
284 3 50       7 unless ( $font ) {
285 0         0 carp("Can't happen?");
286 0         0 $f = $self->{_currentfont};
287 0         0 $font = $f->getfont($self);
288             }
289 3         3 my $upem = 1000; # as delivered by PDF::API2
290 3         4 my $size = $_->{size};
291 3         3 my $base = $_->{base};
292 3   50     10 my $mydir = $f->{direction} || 'ltr';
293              
294             # Width and inkbox, if requested.
295 3 50 66     4 if ( _hb_font_check( $f ) ) {
    50          
296 0         0 $hb->set_font( $font->fontfilename );
297 0         0 $hb->set_size($size);
298 0 0       0 $hb->set_language( $f->{language} ) if $f->{language};
299 0 0       0 $hb->set_direction( $f->{direction} ) if $f->{direction};
300 0         0 $hb->set_text( $_->{text} );
301 0         0 my $info = $hb->shaper;
302 0         0 $mydir = $hb->get_direction;
303             # warn("mydir $mydir\n");
304              
305 0 0       0 if ( $all ) {
306 0         0 my $ext = $hb->get_extents;
307 0         0 foreach my $g ( @$info ) {
308 0         0 my $e = shift(@$ext);
309             printf STDERR ( "G %3d %6.2f %6.2f %6.2f %6.2f %6.2f\n",
310             $g->{g}, $g->{ax},
311 0         0 @$e{ qw( x_bearing y_bearing width height ) } ) if 0;
312             # It is easier to work with the baseline oriented box.
313 0         0 $e->{xMin} = $e->{x_bearing};
314 0         0 $e->{yMin} = $e->{y_bearing} + $e->{height} - $base;
315 0         0 $e->{xMax} = $e->{x_bearing} + $e->{width};
316 0         0 $e->{yMax} = $e->{y_bearing} - $base;
317              
318 0 0 0     0 $xMin //= $w + $e->{xMin} if $e->{width};
319             $yMin = $e->{yMin}
320 0 0 0     0 if !defined($yMin) || $e->{yMin} < $yMin;
321             $yMax = $e->{yMax}
322 0 0 0     0 if !defined($yMax) || $e->{yMax} > $yMax;
323 0         0 $xMax = $w + $e->{xMax};
324 0         0 $w += $g->{ax};
325             }
326             }
327             else {
328 0         0 foreach my $g ( @$info ) {
329 0         0 $w += $g->{ax};
330             }
331             }
332             }
333             elsif ( $all && $font->can("extents") ) {
334 0         0 my $e = $font->extents( $_->{text}, $size );
335 0 0 0     0 printf STDERR ("(%.2f,%.2f)(%.2f,%.2f) -> ",
      0        
      0        
      0        
      0        
336             $xMin//0, $yMin//0, $xMax//0, $yMax//0 ) if $all && 0;
337 0 0       0 $xMax = $w + $e->{xMax} if $all;
338 0         0 $w += $e->{wx};
339             # warn("W \"", $_->{text}, "\" $w, ", $e->{width}, "\n");
340 0 0       0 if ( $all ) {
341 0         0 $_ -= $base for $e->{yMin}, $e->{yMax};
342             # Baseline oriented box.
343 0   0     0 $xMin //= $e->{xMin};
344             $yMin = $e->{yMin}
345 0 0 0     0 if !defined($yMin) || $e->{yMin} < $yMin;
346             $yMax = $e->{yMax}
347 0 0 0     0 if !defined($yMax) || $e->{yMax} > $yMax;
348 0         0 printf STDERR ("(%.2f,%.2f)(%.2f,%.2f)\n",
349             $xMin//0, $yMin//0, $xMax//0, $yMax//0 ) if 0;
350             }
351             }
352             else {
353 3         10 $w += $font->width( $_->{text} ) * $size;
354             }
355              
356             # We have width. Now the rest of the layoutbox.
357 3         804 my ( $d0, $a0 );
358 3 50       6 if ( !$f->get_interline ) {
359             # Use descender/ascender.
360             # Quite accurate, although there are some fonts that do
361             # not include accents on capitals in the ascender.
362 3         9 $d0 = $font->descender * $size / $upem - $base;
363 3         19 $a0 = $font->ascender * $size / $upem - $base;
364             }
365             else {
366             # Use bounding box.
367             # Some (modern) fonts include spacing in the bb.
368 0         0 my @bb = map { $_ * $size / $upem } $font->fontbbox;
  0         0  
369 0         0 $d0 = $bb[1] - $base;
370 0         0 $a0 = $bb[3] - $base;
371             }
372             # Keep track of biggest decender/ascender.
373 3 50       17 $d = $d0 if $d0 < $d;
374 3 50       6 $a = $a0 if $a0 > $a;
375              
376             # Direction.
377 3   33     10 $dir //= $mydir;
378 3 50       8 $dir = 0 unless $dir eq $mydir; # mix
379             }
380 3         4 $bl = $a;
381 3         5 $h = $a - $d;
382              
383 3         5 my $align = $self->{_alignment};
384             # warn("ALIGN: ", $align//"","\n");
385 3 0 33     8 if ( $self->{_width} && $dir && $w < $self->{_width} ) {
      33        
386 0 0 0     0 if ( $dir eq 'rtl' && (!$align || $align eq "left") ) {
      0        
387 0         0 $align = "right";
388             # warn("ALIGN: set to $align\n");
389             }
390             }
391 3 0 33     5 if ( $self->{_width} && $align && $w < $self->{_width} ) {
      33        
392             # warn("ALIGNING...\n");
393 0 0       0 if ( $align eq "right" ) {
    0          
394             # warn("ALIGNING: to $align\n");
395 0         0 $x += my $d = $self->{_width} - $w;
396 0 0       0 $xMin += $d if defined $xMin;
397 0 0       0 $xMax += $d if defined $xMax;
398             }
399             elsif ( $align eq "center" ) {
400             # warn("ALIGNING: to $align\n");
401 0         0 $x += my $d = ( $self->{_width} - $w ) / 2;
402 0 0       0 $xMin += $d if defined $xMin;
403 0 0       0 $xMax += $d if defined $xMax;
404             }
405             }
406              
407 3 50       13 [ $bl, $x, $y-$h, $w, $h,
408             defined $xMin ? ( $xMin, $yMin-$bl, $xMax-$xMin, $yMax-$yMin ) : ()];
409             }
410              
411             #### API
412             sub load_font {
413 2     2 0 5 my ( $self, $font, $fd ) = @_;
414              
415 2 50       7 if ( $fc->{$font} ) {
416             # warn("Loaded font $font (cached)\n");
417 0         0 return $fc->{$font};
418             }
419 2         3 my $ff;
420 2 50       6 if ( $font =~ /\.[ot]tf$/ ) {
421 0         0 eval {
422             $ff = $self->{_context}->ttfont( $font,
423             -dokern => 1,
424             $fd->{nosubset}
425 0 0       0 ? ( -nosubset => 1 )
426             : (),
427             );
428             };
429             }
430             else {
431 2         4 eval {
432 2         7 $ff = $self->{_context}->corefont( $font, -dokern => 1 );
433             };
434             }
435              
436 2 50       62202 croak( "Cannot load font: ", $font, "\n", $@ ) unless $ff;
437             # warn("Loaded font: $font\n");
438 2         5 $self->{font} = $ff;
439 2         5 $fc->{$font} = $ff;
440 2         9 return $ff;
441             }
442              
443             sub xheight {
444 0     0 0 0 $_[0]->data->{xheight};
445             }
446              
447             ################ Extensions to PDF::API2 ################
448              
449             sub PDF::API2::Content::glyph_by_CId {
450 0     0 0 0 my ( $self, $cid ) = @_;
451 0         0 $self->add( sprintf("<%04x> Tj", $cid ) );
452 0         0 $self->{' font'}->fontfile->subsetByCId($cid);
453             }
454              
455             # HarfBuzz requires a TT/OT font. Define the fontfilename method only
456             # for classes that HarfBuzz can deal with.
457             sub PDF::API2::Resource::CIDFont::TrueType::fontfilename {
458 0     0 0 0 my ( $self ) = @_;
459 0         0 $self->fontfile->{' font'}->{' fname'};
460             }
461              
462             # Add extents calculation for CIDfonts.
463             # Note: Origin is x=0 at the baseline.
464             sub PDF::API2::Resource::CIDFont::extents {
465 1     1 0 328815 my ( $self, $text, $size ) = @_;
466 1   50     4 $size //= 1;
467 1         7 my $e = $self->extents_cid( $self->cidsByStr($text), $size );
468 1         4 return $e;
469             }
470              
471             sub PDF::API2::Resource::CIDFont::extents_cid {
472 1     1 0 174 my ( $self, $text, $size ) = @_;
473 1         2 my $width = 0;
474 1         2 my ( $xMin, $xMax, $yMin, $yMax, $bl );
475              
476 1         2 my $upem = $self->data->{upem};
477 1         5 my $glyphs = $self->fontobj->{loca}->read->{glyphs};
478 1         25 $bl = $self->ascender;
479 1         5 my $lastglyph = 0;
480 1         2 my $lastwidth;
481              
482             # Fun ahead! Widths are in 1000 and xMin and such in upem.
483             # Scale to 1000ths.
484 1         2 my $scale = 1000 / $upem;
485              
486 1         4 foreach my $n (unpack('n*', $text)) {
487 19         44 $width += $lastwidth = $self->wxByCId($n);
488 19 50 33     637 if ($self->{'-dokern'} and $self->haveKernPairs()) {
489 0 0       0 if ($self->kernPairCid($lastglyph, $n)) {
490 0         0 $width -= $self->kernPairCid($lastglyph, $n);
491             }
492             }
493 19         20 $lastglyph = $n;
494 19         23 my $ex = $glyphs->[$n];
495 19 50 33     56 unless ( defined $ex && %$ex ) {
496 0         0 warn("Missing glyph: $n\n");
497 0         0 next;
498             }
499 19         42 $ex->read;
500              
501 19         3073 my $e;
502             # Copy while scaling.
503 19         69 $e->{$_} = $ex->{$_} * $scale for qw( xMin yMin xMax yMax );
504              
505             printf STDERR ( "G %3d %6.2f %6.2f %6.2f %6.2f %6.2f\n",
506             $n, $lastwidth,
507 19         22 @$e{ qw( xMin yMin xMax yMax ) } ) if 0;
508              
509 19   66     30 $xMin //= ($width - $lastwidth) + $e->{xMin};
510 19 100 100     60 $yMin = $e->{yMin} if !defined($yMin) || $e->{yMin} < $yMin;
511 19 100 66     44 $yMax = $e->{yMax} if !defined($yMax) || $e->{yMax} > $yMax;
512 19         41 $xMax = ($width - $lastwidth) + $e->{xMax};
513             }
514              
515 1 50       5 if ( defined $lastwidth ) {
516             # $xMax += ($width - $lastwidth);
517             }
518             else {
519 0         0 $xMin = $yMin = $xMax = $yMax = 0;
520 0         0 $width = $self->missingwidth;
521             }
522 1   50     8 $_ = ($_//0)*$size/1000 for $xMin, $xMax, $yMin, $yMax, $bl;
523 1   50     5 $_ = ($_//0)*$size/1000 for $width;
524              
525 1         17 return { x => $xMin,
526             y => $yMin,
527             width => $xMax - $xMin,
528             height => $yMax - $yMin,
529             # These are for convenience
530             xMin => $xMin,
531             yMin => $yMin,
532             xMax => $xMax,
533             yMax => $yMax,
534             wx => $width,
535             bl => $bl,
536             };
537             }
538              
539             ################ Extensions to PDF::Builder ################
540              
541             sub PDF::Builder::Content::glyph_by_CId {
542 0     0     my ( $self, $cid ) = @_;
543 0           $self->add( sprintf("<%04x> Tj", $cid ) );
544 0           $self->{' font'}->fontfile->subsetByCId($cid);
545             }
546              
547             # HarfBuzz requires a TT/OT font. Define the fontfilename method only
548             # for classes that HarfBuzz can deal with.
549             sub PDF::Builder::Resource::CIDFont::TrueType::fontfilename {
550 0     0     my ( $self ) = @_;
551 0           $self->fontfile->{' font'}->{' fname'};
552             }
553              
554             ################ For debugging/convenience ################
555              
556             # Shows the bounding box of the last piece of text that was rendered.
557             sub showbb {
558 0     0 0   my ( $self, $gfx, $x, $y, $col ) = @_;
559 0   0       $x //= $self->{_lastx};
560 0   0       $y //= $self->{_lasty};
561 0   0       $col ||= "magenta";
562              
563 0           my ( $ink, $bb ) = $self->get_pixel_extents;
564 0           my $bl = $bb->{bl};
565             # Bounding box, top-left coordinates.
566             printf( "Ink: %6.2f %6.2f %6.2f %6.2f\n",
567 0           @$ink{qw( x y width height )} );
568             printf( "Layout: %6.2f %6.2f %6.2f %6.2f BL %.2f\n",
569 0           @$bb{qw( x y width height )}, $bl );
570              
571             # NOTE: Some fonts include natural spacing in the bounding box.
572             # NOTE: Some fonts exclude accents on capitals from the bounding box.
573              
574 0           $gfx->save;
575 0           $gfx->translate( $x, $y );
576              
577             # Show origin.
578 0           _showloc($gfx);
579              
580             # Show baseline.
581 0           _line( $gfx, $bb->{x}, -$bl, $bb->{width}, 0, $col );
582 0           $gfx->restore;
583              
584             # Show layout box.
585 0           $gfx->save;
586 0           $gfx->linewidth( 0.25 );
587 0           $gfx->strokecolor($col);
588 0           $gfx->translate( $x, $y );
589 0           for my $e ( $bb ) {
590 0           $gfx->rect( @$e{ qw( x y width height ) } );
591 0           $gfx->stroke;
592             }
593 0           $gfx->restore;
594              
595             # Show ink box.
596 0           $gfx->save;
597 0           $gfx->linewidth( 0.25 );
598 0           $gfx->strokecolor("cyan");
599 0           $gfx->translate( $x, $y );
600 0           for my $e ( $ink ) {
601 0           $gfx->rect( @$e{ qw( x y width height ) } );
602 0           $gfx->stroke;
603             }
604 0           $gfx->restore;
605             }
606              
607             sub _showloc {
608 0     0     my ( $gfx, $x, $y, $d, $col ) = @_;
609 0   0       $x ||= 0; $y ||= 0; $d ||= 50; $col ||= "blue";
  0   0        
  0   0        
  0   0        
610              
611 0           _line( $gfx, $x-$d, $y, 2*$d, 0, $col );
612 0           _line( $gfx, $x, $y-$d, 0, 2*$d, $col );
613             }
614              
615             sub _line {
616 0     0     my ( $gfx, $x, $y, $w, $h, $col, $lw ) = @_;
617 0   0       $col ||= "black";
618 0   0       $lw ||= 0.5;
619              
620 0           $gfx->save;
621 0           $gfx->move( $x, $y );
622 0           $gfx->line( $x+$w, $y+$h );
623 0           $gfx->linewidth($lw);
624 0           $gfx->strokecolor($col);
625 0           $gfx->stroke;
626 0           $gfx->restore;
627             }
628              
629             1;