File Coverage

blib/lib/Text/Layout/PDFAPI2.pm
Criterion Covered Total %
statement 140 350 40.0
branch 34 132 25.7
condition 27 154 17.5
subroutine 13 22 59.0
pod 1 10 10.0
total 215 668 32.1


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