File Coverage

blib/lib/PDF/API2/Resource/CIDFont/TrueType/FontFile.pm
Criterion Covered Total %
statement 27 379 7.1
branch 0 186 0.0
condition 0 53 0.0
subroutine 9 24 37.5
pod 2 14 14.2
total 38 656 5.7


line stmt bran cond sub pod time code
1             package PDF::API2::Resource::CIDFont::TrueType::FontFile;
2              
3 1     1   7 use base 'PDF::API2::Basic::PDF::Dict';
  1         3  
  1         110  
4              
5 1     1   6 use strict;
  1         3  
  1         20  
6 1     1   5 use warnings;
  1         3  
  1         42  
7              
8             our $VERSION = '2.043'; # VERSION
9              
10 1     1   6 use Carp;
  1         3  
  1         83  
11 1     1   7 use Encode qw(:all);
  1         2  
  1         326  
12 1     1   779 use Font::TTF::Font;
  1         4363  
  1         54  
13 1     1   12 use POSIX qw(ceil floor);
  1         2  
  1         10  
14              
15 1     1   137 use PDF::API2::Util;
  1         2  
  1         149  
16 1     1   8 use PDF::API2::Basic::PDF::Utils;
  1         3  
  1         5287  
17              
18             our $cmap = {};
19              
20             sub _look_for_cmap {
21 0     0     my $map = shift();
22 0           my $filename = lc($map);
23 0           $filename =~ s/[^a-z0-9]+//gi;
24 0 0         return { %{$cmap->{$filename}} } if defined $cmap->{$filename};
  0            
25 0           eval "require 'PDF/API2/Resource/CIDFont/CMap/$filename.cmap'";
26 0 0         unless ($@) {
27 0           return { %{$cmap->{$filename}} };
  0            
28             }
29             else {
30 0           die "requested cmap '$map' not installed";
31             }
32             }
33              
34             sub readcffindex {
35 0     0 0   my ($fh, $off) = @_;
36 0           my @idx;
37 0           my $index = [];
38 0           my $buf;
39 0           seek($fh, $off, 0);
40 0           read($fh, $buf, 3);
41 0           my ($count, $offsize) = unpack('nC', $buf);
42 0           foreach (0 .. $count) {
43 0           read($fh, $buf, $offsize);
44 0           $buf = substr("\x00\x00\x00$buf", -4, 4);
45 0           my $id = unpack('N', $buf);
46 0           push @idx, $id;
47             }
48 0           my $dataoff = tell($fh) - 1;
49              
50 0           foreach my $i (0 .. $count - 1) {
51 0           push @{$index}, {
  0            
52             'OFF' => $dataoff + $idx[$i],
53             'LEN' => $idx[$i + 1] - $idx[$i],
54             };
55             }
56 0           return $index;
57             }
58              
59             sub readcffdict {
60 0     0 0   my ($fh, $off, $len, $foff, $buf) = @_;
61 0           my @idx;
62 0           my $dict = {};
63 0           seek($fh, $off, 0);
64 0           my @st;
65 0           while (tell($fh) < ($off + $len)) {
66 0           read($fh, $buf, 1);
67 0           my $b0 = unpack('C', $buf);
68 0           my $v = '';
69              
70 0 0         if ($b0 == 12) { # two byte commands
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
71 0           read($fh, $buf, 1);
72 0           my $b1 = unpack('C', $buf);
73 0 0         if ($b1 == 0) {
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
74 0           $dict->{'Copyright'} = { 'SID' => splice(@st, -1) };
75             }
76             elsif ($b1 == 1) {
77 0           $dict->{'isFixedPitch'} = splice(@st, -1);
78             }
79             elsif ($b1 == 2) {
80 0           $dict->{'ItalicAngle'} = splice(@st, -1);
81             }
82             elsif ($b1 == 3) {
83 0           $dict->{'UnderlinePosition'} = splice(@st, -1);
84             }
85             elsif ($b1 == 4) {
86 0           $dict->{'UnderlineThickness'} = splice(@st, -1);
87             }
88             elsif ($b1 == 5) {
89 0           $dict->{'PaintType'} = splice(@st, -1);
90             }
91             elsif ($b1 == 6) {
92 0           $dict->{'CharstringType'} = splice(@st,-1);
93             }
94             elsif ($b1 == 7) {
95 0           $dict->{'FontMatrix'} = [ splice(@st, -4) ];
96             }
97             elsif ($b1 == 8) {
98 0           $dict->{'StrokeWidth'} = splice(@st, -1);
99             }
100             elsif ($b1 == 20) {
101 0           $dict->{'SyntheticBase'} = splice(@st, -1);
102             }
103             elsif ($b1 == 21) {
104 0           $dict->{'PostScript'} = { 'SID' => splice(@st, -1) };
105             }
106             elsif ($b1 == 22) {
107 0           $dict->{'BaseFontName'} = { 'SID' => splice(@st, -1) };
108             }
109             elsif ($b1 == 23) {
110 0           $dict->{'BaseFontBlend'} = [ splice(@st, 0) ];
111             }
112             elsif ($b1 == 24) {
113 0           $dict->{'MultipleMaster'} = [ splice(@st, 0) ];
114             }
115             elsif ($b1 == 25) {
116 0           $dict->{'BlendAxisTypes'} = [ splice(@st, 0) ];
117             }
118             elsif ($b1 == 30) {
119 0           $dict->{'ROS'} = [ splice(@st, -3) ];
120             }
121             elsif ($b1 == 31) {
122 0           $dict->{'CIDFontVersion'} = splice(@st, -1);
123             }
124             elsif ($b1 == 32) {
125 0           $dict->{'CIDFontRevision'} = splice(@st, -1);
126             }
127             elsif ($b1 == 33) {
128 0           $dict->{'CIDFontType'} = splice(@st, -1);
129             }
130             elsif ($b1 == 34) {
131 0           $dict->{'CIDCount'} = splice(@st, -1);
132             }
133             elsif ($b1 == 35) {
134 0           $dict->{'UIDBase'} = splice(@st, -1);
135             }
136             elsif ($b1 == 36) {
137 0           $dict->{'FDArray'} = { 'OFF' => $foff + splice(@st, -1) };
138             }
139             elsif ($b1 == 37) {
140 0           $dict->{'FDSelect'} = { 'OFF' => $foff + splice(@st, -1) };
141             }
142             elsif ($b1 == 38) {
143 0           $dict->{'FontName'} = { 'SID' => splice(@st, -1) };
144             }
145             elsif ($b1 == 39) {
146 0           $dict->{'Chameleon'} = splice(@st, -1);
147             }
148 0           next;
149             }
150             elsif ($b0 < 28) { # commands
151 0 0         if ($b0 == 0) {
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
152 0           $dict->{'Version'} = { 'SID' => splice(@st, -1) };
153             }
154             elsif ($b0 == 1) {
155 0           $dict->{'Notice'} = { 'SID' => splice(@st, -1) };
156             }
157             elsif ($b0 == 2) {
158 0           $dict->{'FullName'} = { 'SID' => splice(@st, -1) };
159             }
160             elsif ($b0 == 3) {
161 0           $dict->{'FamilyName'} = { 'SID' => splice(@st, -1) };
162             }
163             elsif ($b0 == 4) {
164 0           $dict->{'Weight'} = { 'SID' => splice(@st, -1) };
165             }
166             elsif ($b0 == 5) {
167 0           $dict->{'FontBBX'} = [ splice(@st, -4) ];
168             }
169             elsif ($b0 == 13) {
170 0           $dict->{'UniqueID'} = splice(@st, -1);
171             }
172             elsif ($b0 == 14) {
173 0           $dict->{'XUID'} = [splice(@st, 0)];
174             }
175             elsif ($b0 == 15) {
176 0           $dict->{'CharSet'} = { 'OFF' => $foff + splice(@st, -1) };
177             }
178             elsif ($b0 == 16) {
179 0           $dict->{'Encoding'} = { 'OFF' => $foff + splice(@st, -1) };
180             }
181             elsif ($b0 == 17) {
182 0           $dict->{'CharStrings'} = { 'OFF' => $foff + splice(@st, -1) };
183             }
184             elsif ($b0 == 18) {
185 0           $dict->{'Private'} = {
186             'LEN' => splice(@st, -1),
187             'OFF' => $foff + splice(@st, -1),
188             };
189             }
190 0           next;
191             }
192             elsif ($b0 == 28) { # int16
193 0           read($fh, $buf, 2);
194 0           $v = unpack('n!', $buf);
195             }
196             elsif ($b0 == 29) { # int32
197 0           read($fh, $buf, 4);
198 0           $v = unpack('N!', $buf);
199             }
200             elsif ($b0 == 30) { # float
201 0           my $e = 1;
202 0           while ($e) {
203 0           read($fh, $buf, 1);
204 0           my $v0 = unpack('C', $buf);
205 0           foreach my $m ($v0 >> 8, $v0 & 0xf) {
206 0 0         if ($m < 10) {
    0          
    0          
    0          
    0          
    0          
207 0           $v .= $m;
208             }
209             elsif ($m == 10) {
210 0           $v .= '.';
211             }
212             elsif ($m == 11) {
213 0           $v .= 'E+';
214             }
215             elsif ($m == 12) {
216 0           $v .= 'E-';
217             }
218             elsif ($m == 14) {
219 0           $v .= '-';
220             }
221             elsif ($m == 15) {
222 0           $e = 0;
223 0           last;
224             }
225             }
226             }
227             }
228             elsif ($b0 == 31) { # command
229 0           $v = "c=$b0";
230 0           next;
231             }
232             elsif ($b0 < 247) { # 1 byte signed
233 0           $v = $b0 - 139;
234             }
235             elsif ($b0 < 251) { # 2 byte plus
236 0           read($fh, $buf, 1);
237 0           $v = unpack('C', $buf);
238 0           $v = ($b0 - 247) * 256 + ($v + 108);
239             }
240             elsif ($b0 < 255) { # 2 byte minus
241 0           read($fh, $buf, 1);
242 0           $v = unpack('C', $buf);
243 0           $v = -($b0 - 251) * 256 - $v - 108;
244             }
245 0           push @st, $v;
246             }
247              
248 0           return $dict;
249             }
250              
251             sub read_kern_table {
252 0     0 0   my ($font, $upem, $self) = @_;
253 0           my $fh = $font->{' INFILE'};
254 0           my $data;
255             my $buf;
256              
257 0 0         return unless $font->{'kern'};
258              
259 0           seek($fh, $font->{'kern'}->{' OFFSET'} + 2, 0);
260 0           read($fh, $buf, 2);
261 0           my $num = unpack('n', $buf);
262 0           foreach my $n (1 .. $num) {
263 0           read($fh, $buf, 6);
264 0           my ($ver, $len, $cov) = unpack('n3', $buf);
265 0           $len -= 6;
266 0           my $fmt = $cov >> 8;
267 0 0         if ($fmt == 0) {
    0          
268 0   0       $data ||= {};
269 0           read($fh, $buf, 8);
270 0           my $nc = unpack('n', $buf);
271 0           foreach (1 .. $nc) {
272 0           read($fh, $buf, 6);
273 0           my ($idx1, $idx2, $val) = unpack('n2n!', $buf);
274 0 0         if ($val < 0) {
275 0           $val = -floor($val * 1000 / $upem);
276             }
277             else {
278 0           $val = -ceil($val * 1000 / $upem);
279             }
280 0 0         if ($val != 0) {
281 0           $data->{"$idx1:$idx2"} = $val;
282             $data->{join(':',
283             ($self->data->{'g2n'}->[$idx1] // ''),
284 0   0       ($self->data->{'g2n'}->[$idx2] // ''))} = $val;
      0        
285             }
286             }
287             }
288             elsif ($fmt == 2) {
289 0           read($fh, $buf, $len);
290             }
291             else {
292 0           read($fh, $buf, $len);
293             }
294             }
295 0           return $data;
296             }
297              
298             sub readcffstructs {
299 0     0 0   my $font = shift();
300 0           my $fh = $font->{' INFILE'};
301 0           my $data = {};
302              
303             # read CFF table
304 0           seek($fh, $font->{'CFF '}->{' OFFSET'}, 0);
305 0           my $buf;
306 0           read($fh, $buf, 4);
307 0           my ($cffmajor, $cffminor, $cffheadsize, $cffglobaloffsize) = unpack('C4', $buf);
308              
309 0           $data->{'name'} = readcffindex($fh, $font->{'CFF '}->{' OFFSET'} + $cffheadsize);
310 0           foreach my $dict (@{$data->{'name'}}) {
  0            
311 0           seek($fh, $dict->{'OFF'}, 0);
312 0           read($fh, $dict->{'VAL'}, $dict->{'LEN'});
313             }
314              
315 0           $data->{'topdict'} = readcffindex($fh, $data->{'name'}->[-1]->{'OFF'} + $data->{'name'}->[-1]->{'LEN'});
316 0           foreach my $dict (@{$data->{'topdict'}}) {
  0            
317 0           $dict->{'VAL'} = readcffdict($fh, $dict->{'OFF'}, $dict->{'LEN'}, $font->{'CFF '}->{' OFFSET'});
318             }
319              
320 0           $data->{'string'} = readcffindex($fh, $data->{'topdict'}->[-1]->{'OFF'} + $data->{'topdict'}->[-1]->{'LEN'});
321 0           foreach my $dict (@{$data->{'string'}}) {
  0            
322 0           seek($fh, $dict->{'OFF'}, 0);
323 0           read($fh, $dict->{'VAL'}, $dict->{'LEN'});
324             }
325 0           push @{$data->{'string'}}, { 'VAL' => '001.000' };
  0            
326 0           push @{$data->{'string'}}, { 'VAL' => '001.001' };
  0            
327 0           push @{$data->{'string'}}, { 'VAL' => '001.002' };
  0            
328 0           push @{$data->{'string'}}, { 'VAL' => '001.003' };
  0            
329 0           push @{$data->{'string'}}, { 'VAL' => 'Black' };
  0            
330 0           push @{$data->{'string'}}, { 'VAL' => 'Bold' };
  0            
331 0           push @{$data->{'string'}}, { 'VAL' => 'Book' };
  0            
332 0           push @{$data->{'string'}}, { 'VAL' => 'Light' };
  0            
333 0           push @{$data->{'string'}}, { 'VAL' => 'Medium' };
  0            
334 0           push @{$data->{'string'}}, { 'VAL' => 'Regular' };
  0            
335 0           push @{$data->{'string'}}, { 'VAL' => 'Roman' };
  0            
336 0           push @{$data->{'string'}}, { 'VAL' => 'Semibold' };
  0            
337              
338 0           foreach my $dict (@{$data->{'topdict'}}) {
  0            
339 0           foreach my $k (keys %{$dict->{'VAL'}}) {
  0            
340 0           my $dt = $dict->{'VAL'}->{$k};
341 0 0         if ($k eq 'ROS') {
342 0           $dict->{'VAL'}->{$k}->[0] = $data->{'string'}->[$dict->{'VAL'}->{$k}->[0] - 391]->{'VAL'};
343 0           $dict->{'VAL'}->{$k}->[1] = $data->{'string'}->[$dict->{'VAL'}->{$k}->[1] - 391]->{'VAL'};
344 0           next;
345             }
346 0 0 0       next unless ref($dt) eq 'HASH' and defined($dt->{'SID'});
347 0 0         if ($dt->{'SID'} >= 379) {
348 0           $dict->{'VAL'}->{$k} = $data->{'string'}->[$dt->{'SID'} - 391]->{'VAL'};
349             }
350             }
351             }
352 0           my $dict = {};
353 0           foreach my $k (qw(CIDCount CIDFontVersion FamilyName FontBBX FullName ROS Weight XUID)) {
354 0 0         $dict->{$k} = $data->{'topdict'}->[0]->{'VAL'}->{$k} if defined $data->{'topdict'}->[0]->{'VAL'}->{$k};
355             }
356 0           return $dict;
357             }
358              
359             sub new {
360 0     0 1   my ($class, $pdf, $file, %opts) = @_;
361 0           my $data = {};
362              
363 0 0         confess "cannot find font '$file'" unless -f $file;
364 0           my $font = Font::TTF::Font->open($file);
365 0           $data->{'obj'} = $font;
366              
367 0 0         $class = ref($class) if ref($class);
368 0           my $self = $class->SUPER::new();
369              
370 0           $self->{'Filter'} = PDFArray(PDFName('FlateDecode'));
371 0           $self->{' font'} = $font;
372 0           $self->{' data'} = $data;
373              
374 0 0         $data->{'noembed'} = $opts{'embed'} ? 0 : 1;
375 0 0         $data->{'iscff'} = defined($font->{'CFF '}) ? 1 : 0;
376              
377 0 0         $self->{'Subtype'} = PDFName('CIDFontType0C') if $data->{'iscff'};
378              
379 0           $data->{'fontfamily'} = $font->{'name'}->read->find_name(1);
380 0           $data->{'fontname'} = $font->{'name'}->read->find_name(4);
381              
382 0           $font->{'OS/2'}->read();
383 0           my @stretch = qw(
384             Normal
385             UltraCondensed
386             ExtraCondensed
387             Condensed
388             SemiCondensed
389             Normal
390             SemiExpanded
391             Expanded
392             ExtraExpanded
393             UltraExpanded
394             );
395 0   0       $data->{'fontstretch'} = $stretch[$font->{'OS/2'}->{'usWidthClass'}] || 'Normal';
396              
397 0           $data->{'fontweight'} = $font->{'OS/2'}->{'usWeightClass'};
398              
399 0           $data->{'panose'} = pack('n', $font->{'OS/2'}->{'sFamilyClass'});
400              
401 0           foreach my $p (qw[bFamilyType bSerifStyle bWeight bProportion bContrast bStrokeVariation bArmStyle bLetterform bMidline bXheight]) {
402 0           $data->{'panose'} .= pack('C', $font->{'OS/2'}->{$p});
403             }
404              
405 0           $data->{'apiname'} = join('', map { ucfirst(lc(substr($_, 0, 2))) } split m/[^A-Za-z0-9\s]+/, $data->{'fontname'});
  0            
406 0           $data->{'fontname'} =~ s/[\x00-\x1f\s]//g;
407              
408 0           $data->{'altname'} = $font->{'name'}->find_name(1);
409 0           $data->{'altname'} =~ s/[\x00-\x1f\s]//g;
410              
411 0           $data->{'subname'} = $font->{'name'}->find_name(2);
412 0           $data->{'subname'} =~ s/[\x00-\x1f\s]//g;
413              
414 0           $font->{'cmap'}->read->find_ms();
415 0 0         if (defined $font->{'cmap'}->find_ms()) {
416 0   0       $data->{'issymbol'} = ($font->{'cmap'}->find_ms->{'Platform'} == 3 and $font->{'cmap'}->read->find_ms->{'Encoding'} == 0) || 0;
417             }
418             else {
419 0           $data->{'issymbol'} = 0;
420             }
421              
422 0           $data->{'upem'} = $font->{'head'}->read->{'unitsPerEm'};
423              
424             $data->{'fontbbox'} = [
425             int($font->{'head'}->{'xMin'} * 1000 / $data->{'upem'}),
426             int($font->{'head'}->{'yMin'} * 1000 / $data->{'upem'}),
427             int($font->{'head'}->{'xMax'} * 1000 / $data->{'upem'}),
428 0           int($font->{'head'}->{'yMax'} * 1000 / $data->{'upem'}),
429             ];
430              
431 0           $data->{'stemv'} = 0;
432 0           $data->{'stemh'} = 0;
433              
434 0   0       $data->{'missingwidth'} = int($font->{'hhea'}->read->{'advanceWidthMax'} * 1000 / $data->{'upem'}) || 1000;
435 0           $data->{'maxwidth'} = int($font->{'hhea'}->{'advanceWidthMax'} * 1000 / $data->{'upem'});
436 0           $data->{'ascender'} = int($font->{'hhea'}->read->{'Ascender'} * 1000 / $data->{'upem'});
437 0           $data->{'descender'} = int($font->{'hhea'}{'Descender'} * 1000 / $data->{'upem'});
438              
439 0           $data->{'flags'} = 0;
440 0 0         $data->{'flags'} |= 1 if $font->{'OS/2'}->read->{'bProportion'} == 9;
441 0 0 0       $data->{'flags'} |= 2 unless $font->{'OS/2'}{'bSerifStyle'} > 10 and $font->{'OS/2'}{'bSerifStyle'} < 14;
442 0 0         $data->{'flags'} |= 8 if $font->{'OS/2'}{'bFamilyType'} == 2;
443 0           $data->{'flags'} |= 32; # if $font->{'OS/2'}{'bFamilyType'} > 3;
444 0 0         $data->{'flags'} |= 64 if $font->{'OS/2'}{'bLetterform'} > 8;
445              
446 0   0       $data->{'capheight'} = $font->{'OS/2'}->{'CapHeight'} || int($data->{'fontbbox'}->[3] * 0.8);
447 0   0       $data->{'xheight'} = $font->{'OS/2'}->{'xHeight'} || int($data->{'fontbbox'}->[3] * 0.4);
448              
449 0 0         if ($data->{'issymbol'}) {
450 0           $data->{'e2u'} = [0xf000 .. 0xf0ff];
451             }
452             else {
453 0           $data->{'e2u'} = [unpack('U*', decode('cp1252', pack('C*', 0 .. 255)))];
454             }
455              
456 0 0 0       if ($font->{'post'}->read->{'FormatType'} == 3 and defined $font->{'cmap'}->read->find_ms()) {
457 0           $data->{'g2n'} = [];
458 0           foreach my $u (sort { $a <=> $b } keys %{$font->{'cmap'}->read->find_ms->{'val'}}) {
  0            
  0            
459 0           my $n = nameByUni($u);
460 0           $data->{'g2n'}->[$font->{'cmap'}->read->find_ms->{'val'}->{$u}] = $n;
461             }
462             }
463             else {
464 0 0         $data->{'g2n'} = [ map { $_ || '.notdef' } @{$font->{'post'}->read->{'VAL'}} ];
  0            
  0            
465             }
466              
467 0           $data->{'italicangle'} = $font->{'post'}->{'italicAngle'};
468 0           $data->{'isfixedpitch'} = $font->{'post'}->{'isFixedPitch'};
469 0           $data->{'underlineposition'} = $font->{'post'}->{'underlinePosition'};
470 0           $data->{'underlinethickness'} = $font->{'post'}->{'underlineThickness'};
471              
472 0 0         if ($self->iscff()) {
473 0           $data->{'cff'} = readcffstructs($font);
474             }
475              
476 0 0         if (defined $data->{'cff'}->{'ROS'}) {
477 0           my %cffcmap = (
478             'Adobe:Japan1' => 'japanese',
479             'Adobe:Korea1' => 'korean',
480             'Adobe:CNS1' => 'traditional',
481             'Adobe:GB1' => 'simplified',
482             );
483 0           my $key = $data->{'cff'}->{'ROS'}->[0] . ':' . $data->{'cff'}->{'ROS'}->[1];
484 0   0       my $ccmap = _look_for_cmap($cffcmap{$key} // $key);
485 0           $data->{'u2g'} = $ccmap->{'u2g'};
486 0           $data->{'g2u'} = $ccmap->{'g2u'};
487             }
488             else {
489 0           $data->{'u2g'} = {};
490              
491 0           my $gmap = $font->{'cmap'}->read->find_ms->{'val'};
492 0           foreach my $u (sort {$a <=> $b} keys %$gmap) {
  0            
493 0   0       my $uni = $u || 0;
494 0           $data->{'u2g'}->{$uni} = $gmap->{$uni};
495             }
496 0 0         $data->{'g2u'} = [ map { $_ || 0 } $font->{'cmap'}->read->reverse() ];
  0            
497             }
498              
499 0 0         if ($data->{'issymbol'}) {
500 0   0       map { $data->{'u2g'}->{$_} ||= $font->{'cmap'}->read->ms_lookup($_) } (0xf000 .. 0xf0ff);
  0            
501 0   0       map { $data->{'u2g'}->{$_ & 0xff} ||= $font->{'cmap'}->read->ms_lookup($_) } (0xf000 .. 0xf0ff);
  0            
502             }
503              
504 0 0 0       $data->{'e2n'} = [ map { $data->{'g2n'}->[$data->{'u2g'}->{$_} || 0] || '.notdef' } @{$data->{'e2u'}} ];
  0            
  0            
505              
506 0 0 0       $data->{'e2g'} = [ map { $data->{'u2g'}->{$_ || 0} || 0 } @{$data->{'e2u'}} ];
  0            
  0            
507 0           $data->{'u2e'} = {};
508 0           foreach my $n (reverse 0 .. 255) {
509 0   0       $data->{'u2e'}->{$data->{'e2u'}->[$n]} //= $n;
510             }
511              
512 0           $data->{'u2n'} = { map { $data->{'g2u'}->[$_] => $data->{'g2n'}->[$_] } (0 .. (scalar @{$data->{'g2u'}} - 1)) };
  0            
  0            
513              
514 0           $data->{'wx'} = [];
515 0           foreach my $i (0 .. (scalar @{$data->{'g2u'}} - 1)) {
  0            
516 0           my $hmtx = $font->{'hmtx'}->read->{'advance'}->[$i];
517 0 0         if ($hmtx) {
518 0           $data->{'wx'}->[$i] = int($hmtx * 1000 / $data->{'upem'});
519             }
520             else {
521 0           $data->{'wx'}->[$i] = $data->{'missingwidth'};
522             }
523             }
524              
525 0           $data->{'kern'} = read_kern_table($font, $data->{'upem'}, $self);
526 0 0         delete $data->{'kern'} unless defined $data->{'kern'};
527              
528 0           $data->{'fontname'} =~ s/\s+//g;
529 0           $data->{'fontfamily'} =~ s/\s+//g;
530 0           $data->{'apiname'} =~ s/\s+//g;
531 0           $data->{'altname'} =~ s/\s+//g;
532 0           $data->{'subname'} =~ s/\s+//g;
533              
534 0           $self->subsetByCId(0);
535              
536 0           return ($self, $data);
537             }
538              
539 0     0 0   sub font { return $_[0]->{' font'} }
540 0     0 0   sub data { return $_[0]->{' data'} }
541 0     0 0   sub iscff { return $_[0]->data->{'iscff'} }
542              
543 0 0   0 0   sub haveKernPairs { return $_[0]->data->{'kern'} ? 1 : 0 }
544              
545             sub kernPairCid {
546 0     0 0   my ($self, $i1, $i2) = @_;
547 0 0 0       return 0 if $i1 == 0 and $i2 == 0;
548 0   0       return $self->data->{'kern'}->{"$i1:$i2"} || 0;
549             }
550              
551             sub subsetByCId {
552 0     0 0   my ($self, $g) = @_;
553 0           $self->data->{'subset'} = 1;
554 0           vec($self->data->{'subvec'}, $g, 1) = 1;
555 0 0         return if $self->iscff();
556 0 0         if (defined $self->font->{'loca'}->read->{'glyphs'}->[$g]) {
557 0           $self->font->{'loca'}->read->{'glyphs'}->[$g]->read();
558 0           foreach my $ref ($self->font->{'loca'}->{'glyphs'}->[$g]->get_refs()) {
559 0           vec($self->data->{'subvec'}, $ref, 1) = 1;
560             }
561             }
562             }
563              
564             sub subvec {
565 0     0 0   my $self = shift();
566 0 0         return 1 if $self->iscff();
567 0           my $g = shift();
568 0           return vec($self->data->{'subvec'}, $g, 1);
569             }
570              
571             sub glyphNum {
572 0     0 0   my $self = shift();
573 0           return $self->font->{'maxp'}->read->{'numGlyphs'};
574             }
575              
576             sub outobjdeep {
577 0     0 1   my ($self, $fh, $pdf) = @_;
578              
579 0           my $f = $self->font();
580              
581 0 0         if ($self->iscff()) {
582 0           $f->{'CFF '}->read_dat();
583 0           $self->{' stream'} = $f->{'CFF '}->{' dat'};
584             }
585             else {
586 0 0 0       if ($self->data->{'subset'} and not $self->data->{'nosubset'}) {
587 0           $f->{'glyf'}->read();
588 0           for (my $i = 0; $i < $self->glyphNum(); $i++) {
589 0 0         next if $self->subvec($i);
590 0           $f->{'loca'}{'glyphs'}->[$i] = undef;
591             }
592             }
593 0 0         unless ($self->data->{'noembed'}) {
594 0           $self->{' stream'} = '';
595 0           my $ffh;
596 0           CORE::open($ffh, '+>', \$self->{' stream'});
597 0           binmode($ffh, ':raw');
598 0           $f->out($ffh, 'cmap', 'cvt ', 'fpgm', 'glyf', 'head', 'hhea', 'hmtx', 'loca', 'maxp', 'prep');
599 0           $self->{'Length1'} = PDFNum(length($self->{' stream'}));
600 0           CORE::close($ffh);
601             }
602             }
603              
604 0           $self->SUPER::outobjdeep($fh, $pdf);
605             }
606              
607             1;