Beta Shell
v2.0 ยท web2.us.cloudlogin.co
[FM]
[CMD]
[PHP]
[DB]
[INFO]
[SEC]
File Manager
~
/
usr
/
local
/
share
/
perl5
/
PDF
/
API2
/
Resource
/
CIDFont
/
TrueType
Upload
3 items
Name
Size
Perms
Modified
Actions
[ .. / .. ]
FontFile.pm
19.63 KB
-r--r--r--
2021-12-08 04:53:45
Edit
Del
Editing: FontFile.pm
(19.63 KB)
Path: /usr/local/share/perl5/PDF/API2/Resource/CIDFont/TrueType/FontFile.pm
Back
package PDF::API2::Resource::CIDFont::TrueType::FontFile; use base 'PDF::API2::Basic::PDF::Dict'; use strict; use warnings; our $VERSION = '2.043'; # VERSION use Carp; use Encode qw(:all); use Font::TTF::Font; use POSIX qw(ceil floor); use PDF::API2::Util; use PDF::API2::Basic::PDF::Utils; our $cmap = {}; sub _look_for_cmap { my $map = shift(); my $filename = lc($map); $filename =~ s/[^a-z0-9]+//gi; return { %{$cmap->{$filename}} } if defined $cmap->{$filename}; eval "require 'PDF/API2/Resource/CIDFont/CMap/$filename.cmap'"; unless ($@) { return { %{$cmap->{$filename}} }; } else { die "requested cmap '$map' not installed"; } } sub readcffindex { my ($fh, $off) = @_; my @idx; my $index = []; my $buf; seek($fh, $off, 0); read($fh, $buf, 3); my ($count, $offsize) = unpack('nC', $buf); foreach (0 .. $count) { read($fh, $buf, $offsize); $buf = substr("\x00\x00\x00$buf", -4, 4); my $id = unpack('N', $buf); push @idx, $id; } my $dataoff = tell($fh) - 1; foreach my $i (0 .. $count - 1) { push @{$index}, { 'OFF' => $dataoff + $idx[$i], 'LEN' => $idx[$i + 1] - $idx[$i], }; } return $index; } sub readcffdict { my ($fh, $off, $len, $foff, $buf) = @_; my @idx; my $dict = {}; seek($fh, $off, 0); my @st; while (tell($fh) < ($off + $len)) { read($fh, $buf, 1); my $b0 = unpack('C', $buf); my $v = ''; if ($b0 == 12) { # two byte commands read($fh, $buf, 1); my $b1 = unpack('C', $buf); if ($b1 == 0) { $dict->{'Copyright'} = { 'SID' => splice(@st, -1) }; } elsif ($b1 == 1) { $dict->{'isFixedPitch'} = splice(@st, -1); } elsif ($b1 == 2) { $dict->{'ItalicAngle'} = splice(@st, -1); } elsif ($b1 == 3) { $dict->{'UnderlinePosition'} = splice(@st, -1); } elsif ($b1 == 4) { $dict->{'UnderlineThickness'} = splice(@st, -1); } elsif ($b1 == 5) { $dict->{'PaintType'} = splice(@st, -1); } elsif ($b1 == 6) { $dict->{'CharstringType'} = splice(@st,-1); } elsif ($b1 == 7) { $dict->{'FontMatrix'} = [ splice(@st, -4) ]; } elsif ($b1 == 8) { $dict->{'StrokeWidth'} = splice(@st, -1); } elsif ($b1 == 20) { $dict->{'SyntheticBase'} = splice(@st, -1); } elsif ($b1 == 21) { $dict->{'PostScript'} = { 'SID' => splice(@st, -1) }; } elsif ($b1 == 22) { $dict->{'BaseFontName'} = { 'SID' => splice(@st, -1) }; } elsif ($b1 == 23) { $dict->{'BaseFontBlend'} = [ splice(@st, 0) ]; } elsif ($b1 == 24) { $dict->{'MultipleMaster'} = [ splice(@st, 0) ]; } elsif ($b1 == 25) { $dict->{'BlendAxisTypes'} = [ splice(@st, 0) ]; } elsif ($b1 == 30) { $dict->{'ROS'} = [ splice(@st, -3) ]; } elsif ($b1 == 31) { $dict->{'CIDFontVersion'} = splice(@st, -1); } elsif ($b1 == 32) { $dict->{'CIDFontRevision'} = splice(@st, -1); } elsif ($b1 == 33) { $dict->{'CIDFontType'} = splice(@st, -1); } elsif ($b1 == 34) { $dict->{'CIDCount'} = splice(@st, -1); } elsif ($b1 == 35) { $dict->{'UIDBase'} = splice(@st, -1); } elsif ($b1 == 36) { $dict->{'FDArray'} = { 'OFF' => $foff + splice(@st, -1) }; } elsif ($b1 == 37) { $dict->{'FDSelect'} = { 'OFF' => $foff + splice(@st, -1) }; } elsif ($b1 == 38) { $dict->{'FontName'} = { 'SID' => splice(@st, -1) }; } elsif ($b1 == 39) { $dict->{'Chameleon'} = splice(@st, -1); } next; } elsif ($b0 < 28) { # commands if ($b0 == 0) { $dict->{'Version'} = { 'SID' => splice(@st, -1) }; } elsif ($b0 == 1) { $dict->{'Notice'} = { 'SID' => splice(@st, -1) }; } elsif ($b0 == 2) { $dict->{'FullName'} = { 'SID' => splice(@st, -1) }; } elsif ($b0 == 3) { $dict->{'FamilyName'} = { 'SID' => splice(@st, -1) }; } elsif ($b0 == 4) { $dict->{'Weight'} = { 'SID' => splice(@st, -1) }; } elsif ($b0 == 5) { $dict->{'FontBBX'} = [ splice(@st, -4) ]; } elsif ($b0 == 13) { $dict->{'UniqueID'} = splice(@st, -1); } elsif ($b0 == 14) { $dict->{'XUID'} = [splice(@st, 0)]; } elsif ($b0 == 15) { $dict->{'CharSet'} = { 'OFF' => $foff + splice(@st, -1) }; } elsif ($b0 == 16) { $dict->{'Encoding'} = { 'OFF' => $foff + splice(@st, -1) }; } elsif ($b0 == 17) { $dict->{'CharStrings'} = { 'OFF' => $foff + splice(@st, -1) }; } elsif ($b0 == 18) { $dict->{'Private'} = { 'LEN' => splice(@st, -1), 'OFF' => $foff + splice(@st, -1), }; } next; } elsif ($b0 == 28) { # int16 read($fh, $buf, 2); $v = unpack('n!', $buf); } elsif ($b0 == 29) { # int32 read($fh, $buf, 4); $v = unpack('N!', $buf); } elsif ($b0 == 30) { # float my $e = 1; while ($e) { read($fh, $buf, 1); my $v0 = unpack('C', $buf); foreach my $m ($v0 >> 8, $v0 & 0xf) { if ($m < 10) { $v .= $m; } elsif ($m == 10) { $v .= '.'; } elsif ($m == 11) { $v .= 'E+'; } elsif ($m == 12) { $v .= 'E-'; } elsif ($m == 14) { $v .= '-'; } elsif ($m == 15) { $e = 0; last; } } } } elsif ($b0 == 31) { # command $v = "c=$b0"; next; } elsif ($b0 < 247) { # 1 byte signed $v = $b0 - 139; } elsif ($b0 < 251) { # 2 byte plus read($fh, $buf, 1); $v = unpack('C', $buf); $v = ($b0 - 247) * 256 + ($v + 108); } elsif ($b0 < 255) { # 2 byte minus read($fh, $buf, 1); $v = unpack('C', $buf); $v = -($b0 - 251) * 256 - $v - 108; } push @st, $v; } return $dict; } sub read_kern_table { my ($font, $upem, $self) = @_; my $fh = $font->{' INFILE'}; my $data; my $buf; return unless $font->{'kern'}; seek($fh, $font->{'kern'}->{' OFFSET'} + 2, 0); read($fh, $buf, 2); my $num = unpack('n', $buf); foreach my $n (1 .. $num) { read($fh, $buf, 6); my ($ver, $len, $cov) = unpack('n3', $buf); $len -= 6; my $fmt = $cov >> 8; if ($fmt == 0) { $data ||= {}; read($fh, $buf, 8); my $nc = unpack('n', $buf); foreach (1 .. $nc) { read($fh, $buf, 6); my ($idx1, $idx2, $val) = unpack('n2n!', $buf); if ($val < 0) { $val = -floor($val * 1000 / $upem); } else { $val = -ceil($val * 1000 / $upem); } if ($val != 0) { $data->{"$idx1:$idx2"} = $val; $data->{join(':', ($self->data->{'g2n'}->[$idx1] // ''), ($self->data->{'g2n'}->[$idx2] // ''))} = $val; } } } elsif ($fmt == 2) { read($fh, $buf, $len); } else { read($fh, $buf, $len); } } return $data; } sub readcffstructs { my $font = shift(); my $fh = $font->{' INFILE'}; my $data = {}; # read CFF table seek($fh, $font->{'CFF '}->{' OFFSET'}, 0); my $buf; read($fh, $buf, 4); my ($cffmajor, $cffminor, $cffheadsize, $cffglobaloffsize) = unpack('C4', $buf); $data->{'name'} = readcffindex($fh, $font->{'CFF '}->{' OFFSET'} + $cffheadsize); foreach my $dict (@{$data->{'name'}}) { seek($fh, $dict->{'OFF'}, 0); read($fh, $dict->{'VAL'}, $dict->{'LEN'}); } $data->{'topdict'} = readcffindex($fh, $data->{'name'}->[-1]->{'OFF'} + $data->{'name'}->[-1]->{'LEN'}); foreach my $dict (@{$data->{'topdict'}}) { $dict->{'VAL'} = readcffdict($fh, $dict->{'OFF'}, $dict->{'LEN'}, $font->{'CFF '}->{' OFFSET'}); } $data->{'string'} = readcffindex($fh, $data->{'topdict'}->[-1]->{'OFF'} + $data->{'topdict'}->[-1]->{'LEN'}); foreach my $dict (@{$data->{'string'}}) { seek($fh, $dict->{'OFF'}, 0); read($fh, $dict->{'VAL'}, $dict->{'LEN'}); } push @{$data->{'string'}}, { 'VAL' => '001.000' }; push @{$data->{'string'}}, { 'VAL' => '001.001' }; push @{$data->{'string'}}, { 'VAL' => '001.002' }; push @{$data->{'string'}}, { 'VAL' => '001.003' }; push @{$data->{'string'}}, { 'VAL' => 'Black' }; push @{$data->{'string'}}, { 'VAL' => 'Bold' }; push @{$data->{'string'}}, { 'VAL' => 'Book' }; push @{$data->{'string'}}, { 'VAL' => 'Light' }; push @{$data->{'string'}}, { 'VAL' => 'Medium' }; push @{$data->{'string'}}, { 'VAL' => 'Regular' }; push @{$data->{'string'}}, { 'VAL' => 'Roman' }; push @{$data->{'string'}}, { 'VAL' => 'Semibold' }; foreach my $dict (@{$data->{'topdict'}}) { foreach my $k (keys %{$dict->{'VAL'}}) { my $dt = $dict->{'VAL'}->{$k}; if ($k eq 'ROS') { $dict->{'VAL'}->{$k}->[0] = $data->{'string'}->[$dict->{'VAL'}->{$k}->[0] - 391]->{'VAL'}; $dict->{'VAL'}->{$k}->[1] = $data->{'string'}->[$dict->{'VAL'}->{$k}->[1] - 391]->{'VAL'}; next; } next unless ref($dt) eq 'HASH' and defined($dt->{'SID'}); if ($dt->{'SID'} >= 379) { $dict->{'VAL'}->{$k} = $data->{'string'}->[$dt->{'SID'} - 391]->{'VAL'}; } } } my $dict = {}; foreach my $k (qw(CIDCount CIDFontVersion FamilyName FontBBX FullName ROS Weight XUID)) { $dict->{$k} = $data->{'topdict'}->[0]->{'VAL'}->{$k} if defined $data->{'topdict'}->[0]->{'VAL'}->{$k}; } return $dict; } sub new { my ($class, $pdf, $file, %opts) = @_; my $data = {}; confess "cannot find font '$file'" unless -f $file; my $font = Font::TTF::Font->open($file); $data->{'obj'} = $font; $class = ref($class) if ref($class); my $self = $class->SUPER::new(); $self->{'Filter'} = PDFArray(PDFName('FlateDecode')); $self->{' font'} = $font; $self->{' data'} = $data; $data->{'noembed'} = $opts{'embed'} ? 0 : 1; $data->{'iscff'} = defined($font->{'CFF '}) ? 1 : 0; $self->{'Subtype'} = PDFName('CIDFontType0C') if $data->{'iscff'}; $data->{'fontfamily'} = $font->{'name'}->read->find_name(1); $data->{'fontname'} = $font->{'name'}->read->find_name(4); $font->{'OS/2'}->read(); my @stretch = qw( Normal UltraCondensed ExtraCondensed Condensed SemiCondensed Normal SemiExpanded Expanded ExtraExpanded UltraExpanded ); $data->{'fontstretch'} = $stretch[$font->{'OS/2'}->{'usWidthClass'}] || 'Normal'; $data->{'fontweight'} = $font->{'OS/2'}->{'usWeightClass'}; $data->{'panose'} = pack('n', $font->{'OS/2'}->{'sFamilyClass'}); foreach my $p (qw[bFamilyType bSerifStyle bWeight bProportion bContrast bStrokeVariation bArmStyle bLetterform bMidline bXheight]) { $data->{'panose'} .= pack('C', $font->{'OS/2'}->{$p}); } $data->{'apiname'} = join('', map { ucfirst(lc(substr($_, 0, 2))) } split m/[^A-Za-z0-9\s]+/, $data->{'fontname'}); $data->{'fontname'} =~ s/[\x00-\x1f\s]//g; $data->{'altname'} = $font->{'name'}->find_name(1); $data->{'altname'} =~ s/[\x00-\x1f\s]//g; $data->{'subname'} = $font->{'name'}->find_name(2); $data->{'subname'} =~ s/[\x00-\x1f\s]//g; $font->{'cmap'}->read->find_ms(); if (defined $font->{'cmap'}->find_ms()) { $data->{'issymbol'} = ($font->{'cmap'}->find_ms->{'Platform'} == 3 and $font->{'cmap'}->read->find_ms->{'Encoding'} == 0) || 0; } else { $data->{'issymbol'} = 0; } $data->{'upem'} = $font->{'head'}->read->{'unitsPerEm'}; $data->{'fontbbox'} = [ int($font->{'head'}->{'xMin'} * 1000 / $data->{'upem'}), int($font->{'head'}->{'yMin'} * 1000 / $data->{'upem'}), int($font->{'head'}->{'xMax'} * 1000 / $data->{'upem'}), int($font->{'head'}->{'yMax'} * 1000 / $data->{'upem'}), ]; $data->{'stemv'} = 0; $data->{'stemh'} = 0; $data->{'missingwidth'} = int($font->{'hhea'}->read->{'advanceWidthMax'} * 1000 / $data->{'upem'}) || 1000; $data->{'maxwidth'} = int($font->{'hhea'}->{'advanceWidthMax'} * 1000 / $data->{'upem'}); $data->{'ascender'} = int($font->{'hhea'}->read->{'Ascender'} * 1000 / $data->{'upem'}); $data->{'descender'} = int($font->{'hhea'}{'Descender'} * 1000 / $data->{'upem'}); $data->{'flags'} = 0; $data->{'flags'} |= 1 if $font->{'OS/2'}->read->{'bProportion'} == 9; $data->{'flags'} |= 2 unless $font->{'OS/2'}{'bSerifStyle'} > 10 and $font->{'OS/2'}{'bSerifStyle'} < 14; $data->{'flags'} |= 8 if $font->{'OS/2'}{'bFamilyType'} == 2; $data->{'flags'} |= 32; # if $font->{'OS/2'}{'bFamilyType'} > 3; $data->{'flags'} |= 64 if $font->{'OS/2'}{'bLetterform'} > 8; $data->{'capheight'} = $font->{'OS/2'}->{'CapHeight'} || int($data->{'fontbbox'}->[3] * 0.8); $data->{'xheight'} = $font->{'OS/2'}->{'xHeight'} || int($data->{'fontbbox'}->[3] * 0.4); if ($data->{'issymbol'}) { $data->{'e2u'} = [0xf000 .. 0xf0ff]; } else { $data->{'e2u'} = [unpack('U*', decode('cp1252', pack('C*', 0 .. 255)))]; } if ($font->{'post'}->read->{'FormatType'} == 3 and defined $font->{'cmap'}->read->find_ms()) { $data->{'g2n'} = []; foreach my $u (sort { $a <=> $b } keys %{$font->{'cmap'}->read->find_ms->{'val'}}) { my $n = nameByUni($u); $data->{'g2n'}->[$font->{'cmap'}->read->find_ms->{'val'}->{$u}] = $n; } } else { $data->{'g2n'} = [ map { $_ || '.notdef' } @{$font->{'post'}->read->{'VAL'}} ]; } $data->{'italicangle'} = $font->{'post'}->{'italicAngle'}; $data->{'isfixedpitch'} = $font->{'post'}->{'isFixedPitch'}; $data->{'underlineposition'} = $font->{'post'}->{'underlinePosition'}; $data->{'underlinethickness'} = $font->{'post'}->{'underlineThickness'}; if ($self->iscff()) { $data->{'cff'} = readcffstructs($font); } if (defined $data->{'cff'}->{'ROS'}) { my %cffcmap = ( 'Adobe:Japan1' => 'japanese', 'Adobe:Korea1' => 'korean', 'Adobe:CNS1' => 'traditional', 'Adobe:GB1' => 'simplified', ); my $key = $data->{'cff'}->{'ROS'}->[0] . ':' . $data->{'cff'}->{'ROS'}->[1]; my $ccmap = _look_for_cmap($cffcmap{$key} // $key); $data->{'u2g'} = $ccmap->{'u2g'}; $data->{'g2u'} = $ccmap->{'g2u'}; } else { $data->{'u2g'} = {}; my $gmap = $font->{'cmap'}->read->find_ms->{'val'}; foreach my $u (sort {$a <=> $b} keys %$gmap) { my $uni = $u || 0; $data->{'u2g'}->{$uni} = $gmap->{$uni}; } $data->{'g2u'} = [ map { $_ || 0 } $font->{'cmap'}->read->reverse() ]; } if ($data->{'issymbol'}) { map { $data->{'u2g'}->{$_} ||= $font->{'cmap'}->read->ms_lookup($_) } (0xf000 .. 0xf0ff); map { $data->{'u2g'}->{$_ & 0xff} ||= $font->{'cmap'}->read->ms_lookup($_) } (0xf000 .. 0xf0ff); } $data->{'e2n'} = [ map { $data->{'g2n'}->[$data->{'u2g'}->{$_} || 0] || '.notdef' } @{$data->{'e2u'}} ]; $data->{'e2g'} = [ map { $data->{'u2g'}->{$_ || 0} || 0 } @{$data->{'e2u'}} ]; $data->{'u2e'} = {}; foreach my $n (reverse 0 .. 255) { $data->{'u2e'}->{$data->{'e2u'}->[$n]} //= $n; } $data->{'u2n'} = { map { $data->{'g2u'}->[$_] => $data->{'g2n'}->[$_] } (0 .. (scalar @{$data->{'g2u'}} - 1)) }; $data->{'wx'} = []; foreach my $i (0 .. (scalar @{$data->{'g2u'}} - 1)) { my $hmtx = $font->{'hmtx'}->read->{'advance'}->[$i]; if ($hmtx) { $data->{'wx'}->[$i] = int($hmtx * 1000 / $data->{'upem'}); } else { $data->{'wx'}->[$i] = $data->{'missingwidth'}; } } $data->{'kern'} = read_kern_table($font, $data->{'upem'}, $self); delete $data->{'kern'} unless defined $data->{'kern'}; $data->{'fontname'} =~ s/\s+//g; $data->{'fontfamily'} =~ s/\s+//g; $data->{'apiname'} =~ s/\s+//g; $data->{'altname'} =~ s/\s+//g; $data->{'subname'} =~ s/\s+//g; $self->subsetByCId(0); return ($self, $data); } sub font { return $_[0]->{' font'} } sub data { return $_[0]->{' data'} } sub iscff { return $_[0]->data->{'iscff'} } sub haveKernPairs { return $_[0]->data->{'kern'} ? 1 : 0 } sub kernPairCid { my ($self, $i1, $i2) = @_; return 0 if $i1 == 0 and $i2 == 0; return $self->data->{'kern'}->{"$i1:$i2"} || 0; } sub subsetByCId { my ($self, $g) = @_; $self->data->{'subset'} = 1; vec($self->data->{'subvec'}, $g, 1) = 1; return if $self->iscff(); if (defined $self->font->{'loca'}->read->{'glyphs'}->[$g]) { $self->font->{'loca'}->read->{'glyphs'}->[$g]->read(); foreach my $ref ($self->font->{'loca'}->{'glyphs'}->[$g]->get_refs()) { vec($self->data->{'subvec'}, $ref, 1) = 1; } } } sub subvec { my $self = shift(); return 1 if $self->iscff(); my $g = shift(); return vec($self->data->{'subvec'}, $g, 1); } sub glyphNum { my $self = shift(); return $self->font->{'maxp'}->read->{'numGlyphs'}; } sub outobjdeep { my ($self, $fh, $pdf) = @_; my $f = $self->font(); if ($self->iscff()) { $f->{'CFF '}->read_dat(); $self->{' stream'} = $f->{'CFF '}->{' dat'}; } else { if ($self->data->{'subset'} and not $self->data->{'nosubset'}) { $f->{'glyf'}->read(); for (my $i = 0; $i < $self->glyphNum(); $i++) { next if $self->subvec($i); $f->{'loca'}{'glyphs'}->[$i] = undef; } } unless ($self->data->{'noembed'}) { $self->{' stream'} = ''; my $ffh; CORE::open($ffh, '+>', \$self->{' stream'}); binmode($ffh, ':raw'); $f->out($ffh, 'cmap', 'cvt ', 'fpgm', 'glyf', 'head', 'hhea', 'hmtx', 'loca', 'maxp', 'prep'); $self->{'Length1'} = PDFNum(length($self->{' stream'})); CORE::close($ffh); } } $self->SUPER::outobjdeep($fh, $pdf); } 1;