File Coverage

blib/lib/PDF/Builder/Resource/BaseFont.pm
Criterion Covered Total %
statement 162 252 64.2
branch 32 78 41.0
condition 32 77 41.5
subroutine 35 57 61.4
pod 46 50 92.0
total 307 514 59.7


line stmt bran cond sub pod time code
1             package PDF::Builder::Resource::BaseFont;
2              
3 9     9   1755 use base 'PDF::Builder::Resource';
  9         16  
  9         919  
4              
5 9     9   56 use strict;
  9         15  
  9         187  
6 9     9   38 use warnings;
  9         17  
  9         479  
7              
8             our $VERSION = '3.024'; # VERSION
9             our $LAST_UPDATE = '3.024'; # manually update whenever code is changed
10              
11 9     9   50 use Compress::Zlib;
  9         25  
  9         2801  
12             #use Encode qw(:all);
13 9     9   81 use PDF::Builder::Basic::PDF::Utils;
  9         17  
  9         698  
14 9     9   64 use PDF::Builder::Util;
  9         16  
  9         1212  
15 9     9   59 use Scalar::Util qw(weaken);
  9         24  
  9         32289  
16              
17             =head1 NAME
18              
19             PDF::Builder::Resource::BaseFont - Base class for font resources
20              
21             =head1 METHODS
22              
23             =over
24              
25             =item $font = PDF::Builder::Resource::BaseFont->new($pdf, $name)
26              
27             Return a font resource object.
28              
29             =cut
30              
31             sub new {
32 53     53 1 160 my ($class, $pdf, $name) = @_;
33              
34 53         98 my $self;
35              
36 53 50       138 $class = ref($class) if ref($class);
37 53         251 $self = $class->SUPER::new($pdf, $name);
38              
39 53 50       149 $pdf->new_obj($self) unless $self->is_obj($pdf);
40              
41 53         148 $self->{'Type'} = PDFName('Font');
42              
43 53         111 $self->{' apipdf'} = $pdf;
44 53         141 weaken $self->{' apipdf'};
45              
46 53         119 return $self;
47             }
48              
49             sub data {
50 40369     40369 0 73133 return $_[0]->{' data'};
51             }
52              
53             =item $descriptor = $font->descrByData()
54              
55             Return the font's FontDescriptor key structure based on the font's data.
56              
57             =cut
58              
59             sub descrByData {
60 16     16 1 27 my $self = shift();
61              
62 16         33 my $descriptor = PDFDict();
63 16         52 $self->{' apipdf'}->new_obj($descriptor);
64              
65 16         37 $descriptor->{'Type'} = PDFName('FontDescriptor');
66 16         46 $descriptor->{'FontName'} = PDFName($self->fontname());
67              
68 16   100     53 my @box = map { PDFNum($_ || 0) } $self->fontbbox();
  64         139  
69 16         47 $descriptor->{'FontBBox'} = PDFArray(@box);
70              
71 16   50     48 $descriptor->{'Ascent'} = PDFNum($self->ascender() || 0);
72 16   50     51 $descriptor->{'Descent'} = PDFNum($self->descender() || 0);
73 16   100     42 $descriptor->{'ItalicAngle'} = PDFNum($self->italicangle() || 0.0);
74 16   50     47 $descriptor->{'XHeight'} = PDFNum($self->xheight() || (($self->fontbbox())[3]*0.5) || 500);
75 16   50     57 $descriptor->{'CapHeight'} = PDFNum($self->capheight() || ($self->fontbbox())[3] || 800);
76 16   50     45 $descriptor->{'StemV'} = PDFNum($self->stemv() || 0);
77 16   50     43 $descriptor->{'StemH'} = PDFNum($self->stemh() || 0);
78 16   50     47 $descriptor->{'AvgWidth'} = PDFNum($self->avgwidth() || 300);
79 16   100     40 $descriptor->{'MissingWidth'} = PDFNum($self->missingwidth() || 300);
80 16   66     40 $descriptor->{'MaxWidth'} = PDFNum($self->maxwidth() || $self->missingwidth() || ($self->fontbbox())[2]);
81 16 50 50     34 $descriptor->{'Flags'} = PDFNum($self->flags() || 0) unless $self->data()->{'iscore'};
82 16 100       37 if (defined $self->data()->{'panose'}) {
83 1         4 $descriptor->{'Style'} = PDFDict();
84 1         4 $descriptor->{'Style'}->{'Panose'} = PDFStrHex($self->data()->{'panose'});
85             }
86             $descriptor->{'FontFamily'} = PDFString($self->data()->{'fontfamily'}, 'x')
87 16 100       30 if defined $self->data()->{'fontfamily'};
88             $descriptor->{'FontWeight'} = PDFNum($self->data()->{'fontweight'})
89 16 100       31 if defined $self->data()->{'fontweight'};
90             $descriptor->{'FontStretch'} = PDFName($self->data()->{'fontstretch'})
91 16 100       27 if defined $self->data()->{'fontstretch'};
92              
93 16         48 return $descriptor;
94             }
95              
96             sub tounicodemap {
97 1     1 0 28 my $self = shift();
98              
99 1 50       6 return $self if defined $self->{'ToUnicode'};
100              
101 1         3 my $stream = qq|\%\% Custom\n\%\% CMap\n\%\%\n/CIDInit /ProcSet findresource begin\n|;
102 1         4 $stream .= qq|12 dict begin begincmap\n|;
103 1         3 $stream .= qq|/CIDSystemInfo <<\n|;
104 1         8 $stream .= sprintf(qq| /Registry (%s)\n|, $self->name());
105 1         3 $stream .= qq| /Ordering (XYZ)\n|;
106 1         3 $stream .= qq| /Supplement 0\n|;
107 1         3 $stream .= qq|>> def\n|;
108 1         4 $stream .= sprintf(qq|/CMapName /pdfbldr-%s+0 def\n|, $self->name());
109 1 50 33     19 if ($self->can('uniByCId') and $self->can('glyphNum')) {
110             # this is a type0 font
111 0         0 $stream .= sprintf(qq|1 begincodespacerange <0000> <%04X> endcodespacerange\n|, $self->glyphNum() - 1);
112 0         0 for (my $j = 0; $j < $self->glyphNum(); $j++) {
113 0 0       0 my $i = $self->glyphNum() - $j > 100 ? 100 : $self->glyphNum() - $j;
114 0 0       0 if ($j == 0) {
    0          
115 0         0 $stream .= qq|$i beginbfrange\n|;
116             } elsif ($j % 100 == 0) {
117 0         0 $stream .= qq|endbfrange\n|;
118 0         0 $stream .= qq|$i beginbfrange\n|;
119             }
120             # Default to 0000 if uniByCId returns undef in order to match
121             # previous behavior minus an uninitialized value warning. It's
122             # worth looking into what should be happening here, since this may
123             # not be the correct behavior.
124 0   0     0 $stream .= sprintf(qq|<%04x> <%04x> <%04x>\n|, $j, $j, ($self->uniByCId($j) // 0));
125             }
126 0         0 $stream .= "endbfrange\n";
127             } else {
128             # everything else is single byte font
129 1         4 $stream .= qq|1 begincodespacerange\n<00> \nendcodespacerange\n|;
130 1         3 $stream .= qq|256 beginbfchar\n|;
131 1         7 for (my $j=0; $j < 256; $j++) {
132 256         341 $stream .= sprintf(qq|<%02X> <%04X>\n|, $j, $self->uniByEnc($j));
133             }
134 1         4 $stream .= qq|endbfchar\n|;
135             }
136 1         5 $stream .= qq|endcmap CMapName currendict /CMap defineresource pop end end\n|;
137              
138 1         6 my $cmap = PDFDict();
139 1         13 $cmap->{'Type'} = PDFName('CMap');
140 1         5 $cmap->{'CMapName'} = PDFName(sprintf(qq|pdfbldr-%s+0|, $self->name()));
141 1         3 $cmap->{'CIDSystemInfo'} = PDFDict();
142 1         3 $cmap->{'CIDSystemInfo'}->{'Registry'} = PDFString($self->name(), 'x');
143 1         4 $cmap->{'CIDSystemInfo'}->{'Ordering'} = PDFString('XYZ', 'x');
144 1         4 $cmap->{'CIDSystemInfo'}->{'Supplement'} = PDFNum(0);
145              
146 1         8 $self->{' apipdf'}->new_obj($cmap);
147 1         3 $cmap->{' nofilt'} = 1;
148 1         10 $cmap->{' stream'} = Compress::Zlib::compress($stream);
149 1         698 $cmap->{'Filter'} = PDFArray(PDFName('FlateDecode'));
150 1         2 $self->{'ToUnicode'} = $cmap;
151              
152 1         5 return $self;
153             }
154              
155             =back
156              
157             =head1 FONT-MANAGEMENT RELATED METHODS
158              
159             =over
160              
161             =item $name = $font->fontname()
162              
163             Return the font's name (a.k.a. display name).
164              
165             =cut
166              
167             sub fontname {
168 70     70 1 156 return $_[0]->data()->{'fontname'};
169             }
170              
171             =item $name = $font->altname()
172              
173             Return the font's alternative name (a.k.a. Windows name for a PostScript font).
174              
175             =cut
176              
177             sub altname {
178 0     0 1 0 return $_[0]->data()->{'altname'};
179             }
180              
181             =item $name = $font->subname()
182              
183             Return the font's subname (a.k.a. font variant).
184              
185             =cut
186              
187             sub subname {
188 0     0 1 0 return $_[0]->data()->{'subname'};
189             }
190              
191             =item $name = $font->apiname()
192              
193             Return the font's name to be used internally (should be equal to $font->name()).
194              
195             =cut
196              
197             sub apiname {
198 0     0 1 0 return $_[0]->data()->{'apiname'};
199             }
200              
201             =item $issymbol = $font->issymbol()
202              
203             Return the font's symbol flag (i.e., is this a symbol font).
204              
205             =cut
206              
207             sub issymbol {
208 75     75 1 147 return $_[0]->data()->{'issymbol'};
209             }
210              
211             =item $iscff = $font->iscff()
212              
213             Return the font's Compact Font Format flag.
214              
215             =cut
216              
217             sub iscff {
218 0     0 1 0 return $_[0]->data()->{'iscff'};
219             }
220              
221             =back
222              
223             =head1 TYPOGRAPHY-RELATED METHODS
224              
225             =over
226              
227             =item ($llx,$lly, $urx,$ury) = $font->fontbbox()
228              
229             Return the font's bounding box.
230              
231             =cut
232              
233             sub fontbbox {
234 31     31 1 40 my @bbox = @{$_[0]->data()->{'fontbbox'}};
  31         52  
235             # rearrange to LL UR order
236 31 50       69 if ($bbox[0] > $bbox[2]) {
237 0         0 @bbox = ($bbox[2], $bbox[3], $bbox[0], $bbox[1]);
238             }
239 31         84 return @bbox;
240             }
241              
242             =item $capheight = $font->capheight()
243              
244             Return the font's capheight value.
245              
246             =cut
247              
248             sub capheight {
249 16     16 1 32 return $_[0]->data()->{'capheight'};
250             }
251              
252             =item $xheight = $font->xheight()
253              
254             Return the font's xheight value.
255              
256             =cut
257              
258             sub xheight {
259 16     16 1 30 return $_[0]->data()->{'xheight'};
260             }
261              
262             =item $missingwidth = $font->missingwidth()
263              
264             Return the font's missingwidth value.
265              
266             =cut
267              
268             sub missingwidth {
269 276     276 1 415 return $_[0]->data()->{'missingwidth'};
270             }
271              
272             =item $maxwidth = $font->maxwidth()
273              
274             Return the font's maxwidth value.
275              
276             =cut
277              
278             sub maxwidth {
279 16     16 1 31 return $_[0]->data()->{'maxwidth'};
280             }
281              
282             =item $avgwidth = $font->avgwidth()
283              
284             Return the font's avgwidth (average width) value.
285              
286             =cut
287              
288             sub avgwidth {
289 16     16 1 30 my ($self) = @_;
290              
291 16         27 my $aw = $self->data()->{'avgwidth'};
292 16   33     61 $aw ||= ((
293             # numbers are character-frequency weighting counts
294             # presumably for English text... ? it may be a little off for
295             # other languages
296             $self->wxByGlyph('a')*64 +
297             $self->wxByGlyph('b')*14 +
298             $self->wxByGlyph('c')*27 +
299             $self->wxByGlyph('d')*35 +
300             $self->wxByGlyph('e')*100 +
301             $self->wxByGlyph('f')*20 +
302             $self->wxByGlyph('g')*14 +
303             $self->wxByGlyph('h')*42 +
304             $self->wxByGlyph('i')*63 +
305             $self->wxByGlyph('j')* 3 +
306             $self->wxByGlyph('k')* 6 +
307             $self->wxByGlyph('l')*35 +
308             $self->wxByGlyph('m')*20 +
309             $self->wxByGlyph('n')*56 +
310             $self->wxByGlyph('o')*56 +
311             $self->wxByGlyph('p')*17 +
312             $self->wxByGlyph('q')* 4 +
313             $self->wxByGlyph('r')*49 +
314             $self->wxByGlyph('s')*56 +
315             $self->wxByGlyph('t')*71 +
316             $self->wxByGlyph('u')*31 +
317             $self->wxByGlyph('v')*10 +
318             $self->wxByGlyph('w')*18 +
319             $self->wxByGlyph('x')* 3 +
320             $self->wxByGlyph('y')*18 +
321             $self->wxByGlyph('z')* 2 +
322             $self->wxByGlyph('A')*64 +
323             $self->wxByGlyph('B')*14 +
324             $self->wxByGlyph('C')*27 +
325             $self->wxByGlyph('D')*35 +
326             $self->wxByGlyph('E')*100 +
327             $self->wxByGlyph('F')*20 +
328             $self->wxByGlyph('G')*14 +
329             $self->wxByGlyph('H')*42 +
330             $self->wxByGlyph('I')*63 +
331             $self->wxByGlyph('J')* 3 +
332             $self->wxByGlyph('K')* 6 +
333             $self->wxByGlyph('L')*35 +
334             $self->wxByGlyph('M')*20 +
335             $self->wxByGlyph('N')*56 +
336             $self->wxByGlyph('O')*56 +
337             $self->wxByGlyph('P')*17 +
338             $self->wxByGlyph('Q')* 4 +
339             $self->wxByGlyph('R')*49 +
340             $self->wxByGlyph('S')*56 +
341             $self->wxByGlyph('T')*71 +
342             $self->wxByGlyph('U')*31 +
343             $self->wxByGlyph('V')*10 +
344             $self->wxByGlyph('W')*18 +
345             $self->wxByGlyph('X')* 3 +
346             $self->wxByGlyph('Y')*18 +
347             $self->wxByGlyph('Z')* 2 +
348             $self->wxByGlyph('space')*332
349             ) / 2000);
350              
351 16         83 return int($aw);
352             }
353              
354             =item $flags = $font->flags()
355              
356             Return the font's flags value.
357              
358             =cut
359              
360             sub flags {
361 16     16 1 34 return $_[0]->data()->{'flags'};
362             }
363              
364             =item $stemv = $font->stemv()
365              
366             Return the font's stemv value.
367              
368             =cut
369              
370             sub stemv {
371 16     16 1 32 return $_[0]->data()->{'stemv'};
372             }
373              
374             =item $stemh = $font->stemh()
375              
376             Return the font's stemh value.
377              
378             =cut
379              
380             sub stemh {
381 16     16 1 36 return $_[0]->data()->{'stemh'};
382             }
383              
384             =item $italicangle = $font->italicangle()
385              
386             Return the font's italicangle value.
387              
388             =cut
389              
390             sub italicangle {
391 16     16 1 32 return $_[0]->data()->{'italicangle'};
392             }
393              
394             =item $isfixedpitch = $font->isfixedpitch()
395              
396             Return the font's isfixedpitch flag.
397              
398             =cut
399              
400             sub isfixedpitch {
401 0     0 1 0 return $_[0]->data()->{'isfixedpitch'};
402             }
403              
404             =item $underlineposition = $font->underlineposition()
405              
406             Return the font's underlineposition value.
407              
408             =cut
409              
410             sub underlineposition {
411 0     0 1 0 return $_[0]->data()->{'underlineposition'};
412             }
413              
414             =item $underlinethickness = $font->underlinethickness()
415              
416             Return the font's underlinethickness value.
417              
418             =cut
419              
420             sub underlinethickness {
421 0     0 1 0 return $_[0]->data()->{'underlinethickness'};
422             }
423              
424             =item $ascender = $font->ascender()
425              
426             Return the font's ascender value.
427              
428             =cut
429              
430             sub ascender {
431 16     16 1 31 return $_[0]->data()->{'ascender'};
432             }
433              
434             =item $descender = $font->descender()
435              
436             Return the font's descender value.
437              
438             =cut
439              
440             sub descender {
441 16     16 1 31 return $_[0]->data()->{'descender'};
442             }
443              
444             =back
445              
446             =head1 GLYPH-RELATED METHODS
447              
448             =over 4
449              
450             =item @names = $font->glyphNames()
451              
452             Return the defined glyph names of the font.
453              
454             =cut
455              
456             sub glyphNames {
457 0     0 1 0 return keys %{$_[0]->data()->{'wx'}};
  0         0  
458             }
459              
460             =item $glNum = $font->glyphNum()
461              
462             Return the number of defined glyph names of the font.
463              
464             =cut
465              
466             sub glyphNum {
467             #my $self = shift();
468             #return scalar $self->glyphNames();
469 0     0 1 0 return scalar keys %{$_[0]->data()->{'wx'}};
  0         0  
470             }
471              
472             =item $uni = $font->uniByGlyph($char)
473              
474             Return the unicode by glyph name.
475              
476             =cut
477              
478             sub uniByGlyph {
479             #my ($self, $name) = @_;
480             #return $self->data()->{'n2u'}->{$name};
481 0     0 1 0 return $_[0]->data()->{'n2u'}->{$_[1]};
482             }
483              
484             =item $uni = $font->uniByEnc($char)
485              
486             Return the Unicode by the font's encoding map.
487              
488             =cut
489              
490             sub uniByEnc {
491 256     256 1 313 my ($self, $enc) = @_;
492 256         307 my $uni = $self->data()->{'e2u'}->[$enc];
493             # fallback to U+0000 if no match
494 256 50       380 $uni = 0 unless defined $uni;
495 256         544 return $uni;
496             }
497              
498             =item $uni = $font->uniByMap($char)
499              
500             Return the Unicode by the font's default map.
501              
502             =cut
503              
504             sub uniByMap {
505 0     0 1 0 return $_[0]->data()->{'uni'}->[$_[1]];
506             }
507              
508             =item $char = $font->encByGlyph($glyph)
509              
510             Return the character by the given glyph name of the font's encoding map.
511              
512             =cut
513              
514             sub encByGlyph {
515 0   0 0 1 0 return $_[0]->data()->{'n2e'}->{$_[1]} || 0;
516             }
517              
518             =item $char = $font->encByUni($uni)
519              
520             Return the character by the given Unicode of the font's encoding map.
521              
522             =cut
523              
524             sub encByUni {
525             return $_[0]->data()->{'u2e'}->{$_[1]} ||
526 27   50 27 1 39 $_[0]->data()->{'u2c'}->{$_[1]} ||
527             0;
528             }
529              
530             =item $char = $font->mapByGlyph($glyph)
531              
532             Return the character by the given glyph name of the font's default map.
533              
534             =cut
535              
536             sub mapByGlyph {
537 0   0 0 1 0 return $_[0]->data()->{'n2c'}->{$_[1]} || 0;
538             }
539              
540             =item $char = $font->mapByUni($uni)
541              
542             Return the character by the given Unicode of the font's default map.
543              
544             =cut
545              
546             sub mapByUni {
547 0   0 0 1 0 return $_[0]->data()->{'u2c'}->{$_[1]} || 0;
548             }
549              
550             =item $name = $font->glyphByUni($unicode)
551              
552             Return the glyph's name by the font's Unicode map.
553             B non-standard glyph-names are mapped onto
554             the ms-symbol area (0xF000).
555              
556             =cut
557              
558             sub glyphByUni {
559 0   0 0 1 0 return $_[0]->data()->{'u2n'}->{$_[1]} || '.notdef';
560             }
561              
562             =item $name = $font->glyphByEnc($char)
563              
564             Return the glyph's name by the font's encoding map.
565              
566             =cut
567              
568             sub glyphByEnc {
569 25398     25398 1 33302 return $_[0]->data()->{'e2n'}->[$_[1]];
570             }
571              
572             =item $name = $font->glyphByMap($char)
573              
574             Return the glyph's name by the font's default map.
575              
576             =cut
577              
578             sub glyphByMap {
579 0     0 1 0 return $_[0]->data()->{'char'}->[$_[1]];
580             }
581              
582             =item $width = $font->wxByGlyph($glyph)
583              
584             Return the glyph's width.
585             This is a value, that when divided by 1000 and multiplied by
586             the font size (height in points), gives the advance width to the
587             next character's start. Typically, the width will be under 1000.
588              
589             =cut
590              
591             sub wxByGlyph {
592 848     848 1 1112 my ($self, $glyph) = @_;
593              
594 848         897 my $width;
595 848 100       1025 if (ref($self->data()->{'wx'}) eq 'HASH') {
596 795 50       1250 $width = $self->data()->{'wx'}->{$glyph} if defined $glyph;
597             } else {
598 53         90 my $cid = $self->cidByUni(uniByName($glyph));
599 53 50       98 $width = $self->data()->{'wx'}->[$cid] if defined $cid;
600             }
601 848   66     1247 $width //= $self->missingwidth();
602 848   100     1238 $width //= 300;
603              
604 848         1559 return $width;
605             }
606              
607             =item $width = $font->wxByUni($uni)
608              
609             Return the Unicode character's width.
610             This is a value, that when divided by 1000 and multiplied by
611             the font size (height in points), gives the advance width to the
612             next character's start. Typically, the width will be under 1000.
613              
614             =cut
615              
616             sub wxByUni {
617 0     0 1 0 my ($self, $uni) = @_;
618 0         0 my ($gid, $width);
619              
620 0 0       0 $gid = $self->glyphByUni($uni) if defined $uni;
621 0 0       0 $width = $self->data()->{'wx'}->{$gid} if defined $gid;
622 0   0     0 $width //= $self->missingwidth();
623 0   0     0 $width //= 300;
624              
625 0         0 return $width;
626             }
627              
628             =item $width = $font->wxByEnc($char)
629              
630             Return the character's width based on the current encoding.
631             This is a value, that when divided by 1000 and multiplied by
632             the font size (height in points), gives the advance width to the
633             next character's start. Typically, the width will be under 1000.
634              
635             =cut
636              
637             sub wxByEnc {
638 12086     12086 1 15411 my ($self, $char) = @_;
639              
640 12086         12480 my $glyph;
641 12086 50       20956 $glyph = $self->glyphByEnc($char) if defined $char;
642 12086         12941 my $width;
643 12086 50       19205 $width = $self->data()->{'wx'}->{$glyph} if defined $glyph;
644              
645 12086   100     17251 $width //= $self->missingwidth();
646 12086   100     15992 $width //= 300;
647              
648 12086         21222 return $width;
649             }
650              
651             =item $flag = $font->wxMissingByEnc($char)
652              
653             Return true if the character's width (based on the current encoding) is
654             supplied by "missing width" of font.
655              
656             =cut
657              
658             sub wxMissingByEnc {
659 0     0 1 0 my ($self, $char) = @_;
660              
661 0         0 my $glyph = $self->glyphByEnc($char);
662 0         0 my $width = $self->data()->{'wx'}->{$glyph};
663              
664 0         0 return !defined($width);
665             }
666              
667             =item $width = $font->wxByMap($char)
668              
669             Return the character's width based on the font's default encoding.
670             This is a value, that when divided by 1000 and multiplied by
671             the font size (height in points), gives the advance width to the
672             next character's start. Typically, the width will be under 1000.
673              
674             =cut
675              
676             sub wxByMap {
677 0     0 1 0 my ($self, $char) = @_;
678              
679 0         0 my $glyph;
680 0 0       0 $glyph = $self->glyphByMap($char) if defined $char;
681 0         0 my $width;
682 0 0       0 $width = $self->data()->{'wx'}->{$glyph} if defined $glyph;
683 0   0     0 $width //= $self->missingwidth();
684 0   0     0 $width //= 300;
685              
686 0         0 return $width;
687             }
688              
689             =item $wd = $font->width($text)
690              
691             Return the width of $text as if it were at size 1.
692             B works correctly only if a proper Perl string
693             is used, either in native or UTF-8 format (check utf8-flag).
694              
695             =cut
696              
697             sub width {
698 192     192 1 301 my ($self, $text) = @_;
699              
700 192 100       404 $text = $self->strByUtf($text) if utf8::is_utf8($text);
701              
702 192         247 my @cache;
703 192         264 my $width = 0;
704 192   33     373 my $kern = $self->{'-dokern'} && ref($self->data()->{'kern'});
705 192         229 my $last_glyph = '';
706 192         435 foreach my $n (unpack('C*',$text)) {
707 2391   66     3521 $cache[$n] //= $self->wxByEnc($n);
708 2391         2472 $width += $cache[$n];
709 2391 50       3312 if ($kern) {
710 0         0 my $glyph = $self->data()->{'e2n'}->[$n];
711 0   0     0 $width += ($self->data()->{'kern'}->{$last_glyph . ':' . $glyph} // 0);
712 0         0 $last_glyph = $glyph;
713             }
714             }
715 192         308 $width /= 1000;
716 192         465 return $width;
717             }
718              
719             =item @widths = $font->width_array($text)
720              
721             Return (as an array) the widths of the words in $text as if they were at size 1.
722              
723             =cut
724              
725             sub width_array {
726 0     0 1 0 my ($self, $text) = @_;
727              
728 0 0       0 $text = $self->utfByStr($text) unless utf8::is_utf8($text);
729 0         0 my @widths = map { $self->width($_) } split(/\s+/, $text);
  0         0  
730 0         0 return @widths;
731             }
732              
733             =back
734              
735             =head1 STRING METHODS
736              
737             =over
738              
739             =item $utf8string = $font->utfByStr($string)
740              
741             Return the utf8-string from string based on the font's encoding map.
742              
743             =cut
744              
745             sub utfByStr {
746 0     0 1 0 my ($self, $string) = @_;
747              
748 0         0 $string = pack('U*', map { $self->uniByEnc($_) } unpack('C*', $string));
  0         0  
749 0         0 utf8::upgrade($string);
750 0         0 return $string;
751             }
752              
753             =item $string = $font->strByUtf($utf8_string)
754              
755             Return the encoded string from utf8-string based on the font's encoding map.
756              
757             =cut
758              
759             sub strByUtf {
760 9     9 1 16 my ($self, $utf8_string) = @_;
761              
762 9         21 $utf8_string = pack('C*', map { $self->encByUni($_) & 0xFF } unpack('U*', $utf8_string));
  27         45  
763 9         90 utf8::downgrade($utf8_string);
764 9         16 return $utf8_string;
765             }
766              
767             =item $pdf_string = $font->textByStr($string)
768              
769             Return a properly formatted representation of $string for use in the PDF.
770              
771             =cut
772              
773             sub textByStr {
774 31     31 1 53 my ($self, $string) = @_;
775              
776 31 50       65 if (not defined $string) { $string = ''; }
  0         0  
777 31 100       102 $string = $self->strByUtf($string) if utf8::is_utf8($string);
778 31         46 my $text = $string;
779 31         72 $text =~ s/\\/\\\\/go;
780 31         68 $text =~ s/([\x00-\x1f])/sprintf('\%03lo',ord($1))/ge;
  0         0  
781 31         71 $text =~ s/([\{\}\[\]\(\)])/\\$1/g;
782              
783 31         59 return $text;
784             }
785              
786             =item $pdf_string = $font->textByStrKern($string)
787              
788             Return a properly formatted representation of $string, with kerning,
789             for use in the PDF.
790              
791             =cut
792              
793             sub textByStrKern {
794 0     0 1 0 my ($self, $string) = @_;
795              
796 0 0 0     0 return '(' . $self->textByStr($string) . ')' unless $self->{'-dokern'} && ref($self->data()->{'kern'});
797 0 0       0 $string = $self->strByUtf($string) if utf8::is_utf8($string);
798              
799 0         0 my $text = ' ';
800 0         0 my $tBefore = 0;
801 0         0 my $last_glyph = '';
802              
803 0         0 foreach my $n (unpack('C*', $string)) {
804 0         0 my $glyph = $self->data()->{'e2n'}->[$n];
805 0 0       0 if (defined $self->data()->{'kern'}->{$last_glyph . ':' . $glyph}) {
806 0 0       0 $text .= ') ' if $tBefore;
807 0         0 $text .= sprintf('%i ', -($self->data()->{'kern'}->{$last_glyph . ':' . $glyph}));
808 0         0 $tBefore = 0;
809             }
810 0         0 $last_glyph = $glyph;
811 0         0 my $t = pack('C', $n);
812 0         0 $t =~ s/\\/\\\\/go;
813 0         0 $t =~ s/([\x00-\x1f])/sprintf('\%03lo',ord($1))/ge;
  0         0  
814 0         0 $t =~ s/([\{\}\[\]\(\)])/\\$1/g;
815 0 0       0 $text .= '(' unless $tBefore;
816 0         0 $text .= "$t";
817 0         0 $tBefore = 1;
818             }
819 0 0       0 $text .= ') ' if $tBefore;
820 0         0 return $text;
821             }
822              
823             # Maintainer's note: $size here is used solely as a flag to determine whether or
824             # not to append a text-showing operator (TJ or Tj).
825             sub text {
826 31     31 0 83 my ($self, $string, $size, $indent) = @_;
827 31 50       84 if (not defined $string) { $string = ''; }
  0         0  
828 31         88 my $text = $self->textByStr($string);
829              
830 31 50 33     169 if (defined $size && $self->{'-dokern'}) {
    50          
831 0         0 $text = $self->textByStrKern($string);
832 0 0       0 return "[ $indent $text ] TJ" if $indent;
833 0         0 return "[ $text ] TJ";
834             } elsif (defined $size) {
835 31 100       130 return "[ $indent ($text) ] TJ" if $indent;
836 19         83 return "($text) Tj";
837             } else {
838             # will need a later Tj operator to actually see this!
839 0         0 return "($text)";
840             }
841             }
842              
843             sub isvirtual {
844 32     32 0 91 return;
845             }
846              
847             =back
848              
849             =cut
850              
851             1;