File Coverage

blib/lib/PDF/API3/Compat/API2/Resource/Font/Postscript.pm
Criterion Covered Total %
statement 35 279 12.5
branch 0 102 0.0
condition 0 34 0.0
subroutine 12 18 66.6
pod 2 6 33.3
total 49 439 11.1


line stmt bran cond sub pod time code
1             #=======================================================================
2             # ____ ____ _____ _ ____ ___ ____
3             # | _ \| _ \| ___| _ _ / \ | _ \_ _| |___ \
4             # | |_) | | | | |_ (_) (_) / _ \ | |_) | | __) |
5             # | __/| |_| | _| _ _ / ___ \| __/| | / __/
6             # |_| |____/|_| (_) (_) /_/ \_\_| |___| |_____|
7             #
8             # A Perl Module Chain to faciliate the Creation and Modification
9             # of High-Quality "Portable Document Format (PDF)" Files.
10             #
11             # Copyright 1999-2005 Alfred Reibenschuh .
12             #
13             #=======================================================================
14             #
15             # This library is free software; you can redistribute it and/or
16             # modify it under the terms of the GNU Lesser General Public
17             # License as published by the Free Software Foundation; either
18             # version 2 of the License, or (at your option) any later version.
19             #
20             # This library is distributed in the hope that it will be useful,
21             # but WITHOUT ANY WARRANTY; without even the implied warranty of
22             # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
23             # Lesser General Public License for more details.
24             #
25             # You should have received a copy of the GNU Lesser General Public
26             # License along with this library; if not, write to the
27             # Free Software Foundation, Inc., 59 Temple Place - Suite 330,
28             # Boston, MA 02111-1307, USA.
29             #
30             # $Id: Postscript.pm,v 2.2 2007/06/27 20:54:16 areibens Exp $
31             #
32             #=======================================================================
33             package PDF::API3::Compat::API2::Resource::Font::Postscript;
34            
35             BEGIN {
36            
37 1     1   7 use utf8;
  1         4  
  1         9  
38 1     1   43 use Encode qw(:all);
  1         2  
  1         370  
39 1     1   7 use PDF::API3::Compat::API2::Util;
  1         2  
  1         196  
40 1     1   7 use PDF::API3::Compat::API2::Basic::PDF::Utils;
  1         2  
  1         126  
41 1     1   7 use PDF::API3::Compat::API2::Resource::Font;
  1         3  
  1         21  
42            
43 1     1   6 use POSIX;
  1         1  
  1         8  
44 1     1   3112 use Compress::Zlib;
  1         186  
  1         417  
45 1     1   9 use File::Find;
  1         2  
  1         65  
46 1     1   7 use IO::File qw();
  1         3  
  1         22  
47 1     1   5 use vars qw(@ISA $VERSION);
  1         2  
  1         139  
48            
49 1     1   31 @ISA = qw( PDF::API3::Compat::API2::Resource::Font );
50            
51 1         37 ( $VERSION ) = sprintf '%i.%03i', split(/\./,('$Revision: 2.2 $' =~ /Revision: (\S+)\s/)[0]); # $Date: 2007/06/27 20:54:16 $
52            
53             }
54 1     1   6 no warnings qw[ deprecated recursion uninitialized ];
  1         2  
  1         3806  
55            
56             =item $font = PDF::API3::Compat::API2::Resource::Font::Postscript->new @parameters
57            
58             Returns a adobe type1 font object (called from $pdf->psfont).
59            
60             =cut
61            
62             sub new {
63 0     0 1   my ($class, $pdf, $psfile, %opts) = @_;
64 0           my ($self,$encoding);
65 0           my (@w,$data);
66            
67 0 0         if(defined $opts{-afmfile}) {
    0          
    0          
68 0           $data = $class->readAFM($opts{-afmfile});
69             } elsif(defined $opts{-pfmfile}) {
70 0           $data = $class->readPFM($opts{-pfmfile});
71             } elsif(defined $opts{-xfmfile}) {
72 0           $data = $class->readXFM($opts{-xfmfile});
73             } else {
74 0           die "no proper font-metrics file specified for '$psfile'.";
75             }
76            
77 0 0         $class = ref $class if ref $class;
78 0           $self = $class->SUPER::new($pdf, $data->{apiname}.pdfkey().'~'.time());
79 0 0         $pdf->new_obj($self) unless($self->is_obj($pdf));
80 0           $self->{' data'}=$data;
81            
82 0 0         if($opts{-pdfname}) {
83 0           $self->name($opts{-pdfname});
84             }
85            
86 0           $self->{'Subtype'} = PDFName("Type1");
87 0           $self->{'FontDescriptor'}=$self->descrByData();
88 0 0         if(-f $psfile)
89             {
90 0           $self->{'BaseFont'} = PDFName(pdfkey().'+'.($self->fontname).'~'.time());
91            
92 0           my ($l1,$l2,$l3,$stream)=$self->readPFAPFB($psfile);
93            
94 0           my $s = PDFDict();
95 0           $self->{'FontDescriptor'}->{'FontFile'} = $s;
96 0           $s->{'Length1'} = PDFNum($l1);
97 0           $s->{'Length2'} = PDFNum($l2);
98 0           $s->{'Length3'} = PDFNum($l3);
99 0           $s->{'Filter'} = PDFArray(PDFName("FlateDecode"));
100 0           $s->{' stream'} = $stream;
101 0 0         if(defined $pdf) {
102 0           $pdf->new_obj($s);
103             }
104             }
105             else
106             {
107 0           $self->{'BaseFont'} = PDFName($self->fontname);
108             }
109            
110 0           $self->encodeByData($opts{-encode});
111            
112 0 0         $self->{-nocomps}=1 if($opts{-nocomps});
113 0 0         $self->{-dokern}=1 if($opts{-dokern});
114            
115 0           return($self);
116             }
117            
118             sub new_api {
119 0     0 1   my ($class, $api, @para) = @_;
120            
121 0 0         $class = ref $class if ref $class;
122 0           my $self = $class->new($api->{pdf},@para);
123            
124 0           $self->{' apipdf'}=$api->{pdf};
125 0           $self->{' api'}=$api;
126            
127 0           return($self);
128             }
129            
130             sub readPFAPFB {
131 0     0 0   my ($self,$file) = @_;
132 0           my ($l1,$l2,$l3,$stream,$t1stream,@lines,$line,$head,$body,$tail);
133            
134 0 0         die "cannot find font '$file' ..." unless(-f $file);
135            
136 0           $l=-s $file;
137            
138 0           open(INF,$file);
139 0           binmode(INF,':raw');
140 0           read(INF,$line,2);
141 0           @lines=unpack('C*',$line);
142 0 0 0       if(($lines[0]==0x80) && ($lines[1]==1)) {
    0          
143 0           read(INF,$line,4);
144 0           $l1=unpack('V',$line);
145 0           seek(INF,$l1,1);
146 0           read(INF,$line,2);
147 0           @lines=unpack('C*',$line);
148 0 0 0       if(($lines[0]==0x80) && ($lines[1]==2)) {
149 0           read(INF,$line,4);
150 0           $l2=unpack('V',$line);
151             } else {
152 0           die "corrupt pfb in file '$file' at marker='2'.";
153             }
154 0           seek(INF,$l2,1);
155 0           read(INF,$line,2);
156 0           @lines=unpack('C*',$line);
157 0 0 0       if(($lines[0]==0x80) && ($lines[1]==1)) {
158 0           read(INF,$line,4);
159 0           $l3=unpack('V',$line);
160             } else {
161 0           die "corrupt pfb in file '$file' at marker='3'.";
162             }
163 0           seek(INF,0,0);
164 0           @lines=;
165 0           close(INF);
166 0           $stream=join('',@lines);
167 0           $t1stream=substr($stream,6,$l1);
168 0           $t1stream.=substr($stream,12+$l1,$l2);
169 0           $t1stream.=substr($stream,18+$l1+$l2,$l3);
170             } elsif($line eq '%!') {
171 0           seek(INF,0,0);
172 0           while($line=) {
173 0 0         if(!$l1) {
    0          
174 0           $head.=$line;
175 0 0         if($line=~/eexec$/){
176 0           chomp($head);
177 0           $head.="\x0d";
178 0           $l1=length($head);
179             }
180             } elsif(!$l2) {
181 0 0         if($line=~/^0+$/) {
182 0           $l2=length($body);
183 0           $tail=$line;
184             } else {
185 0           chomp($line);
186 0           $body.=pack('H*',$line);
187             }
188             } else {
189 0           $tail.=$line;
190             }
191             }
192 0           $l3=length($tail);
193 0           $t1stream="$head$body$tail";
194             } else {
195 0           die "unsupported font-format in file '$file' at marker='1'.";
196             }
197            
198 0           return($l1,$l2,$l3,$t1stream);
199             }
200            
201            
202             # $datahashref = $self->readAFM( $afmfile );
203            
204             sub readAFM
205             {
206 0     0 0   my ($self,$file)=@_;
207 0           my $data={};
208 0           $data->{wx}={};
209 0           $data->{bbox}={};
210 0           $data->{char}=[];
211 0           $data->{firstchar}=255;
212 0           $data->{lastchar}=0;
213            
214 0 0         if(! -e $file) {die "file='$file' not existant.";}
  0            
215 0 0         open(AFMF, $file) or die "Can't find the AFM file for $file";
216 0           local($/, $_) = ("\n", undef); # ensure correct $INPUT_RECORD_SEPARATOR
217 0           while ($_=)
218             {
219 0 0         if (/^StartCharMetrics/ .. /^EndCharMetrics/)
    0          
    0          
220             {
221             # only lines that start with "C" or "CH" are parsed
222 0 0         next unless $_=~/^CH?\s/;
223 0           my($ch) = $_=~/^CH?\s+(\d+)\s*;/;
224 0   0       $ch=$ch||0;
225 0           my($name) = $_=~/\bN\s+(\.?\w+)\s*;/;
226 0           my($wx) = $_=~/\bWX\s+(\d+)\s*;/;
227 0           my($bbox) = $_=~/\bB\s+([^;]+);/;
228 0           $bbox =~ s/\s+$//;
229             # Should also parse lingature data (format: L successor lignature)
230 0           $data->{avgwidth2} += $wx ;
231 0 0         $data->{maxwidth} = $data->{maxwidth}<$wx ? $wx : $data->{maxwidth};
232 0           $data->{wx}->{$name} = $wx ;
233 0           $data->{bbox}->{$name} = [split(/\s+/,$bbox)];
234 0 0         if($ch>0) {
235 0           $data->{'char'}->[$ch]=$name ;
236             }
237 0 0         $data->{lastchar} = $data->{lastchar}<$ch ? $ch : $data->{lastchar};
238 0 0         $data->{firstchar} = $data->{firstchar}>$ch ? $ch : $data->{firstchar};
239 0           next;
240             }
241             elsif(/^StartKernData/ .. /^EndKernData/)
242             {
243 0   0       $data->{kern}||={};
244 0 0         if($_=~m|^KPX\s+(\S+)\s+(\S+)\s+(\S+)\s*$|i)
245             {
246 0           $data->{kern}->{"$1:$2"}=$3;
247             }
248             }
249             elsif(/^StartComposites/ .. /^EndComposites/)
250             {
251 0   0       $data->{comps}||={};
252 0 0         if($_=~m|^CC\s+(\S+)\s+(\S+)\s+;|i)
253             {
254 0           my ($name,$comp)=($1,$2);
255 0           my @cv=split(/;/,$_);
256 0           shift @cv;
257 0           my $rng=[];
258 0           foreach (1..$comp)
259             {
260 0           my @c1=split(/\s+/,shift @cv);
261 0           push @{$rng},$c1[1],$c1[2],$c1[3];
  0            
262             }
263 0           $data->{comps}->{$name}=$rng;
264             }
265             }
266 0 0         last if $_=~/^EndFontMetrics/;
267 0 0         if (/(^\w+)\s+(.*)/)
268             {
269 0           my($key,$val) = ($1, $2);
270 0           $key = lc $key;
271 0 0         if (defined $data->{$key})
272             {
273             # $data->{$key} = [ $data->{$key} ] unless ref $data->{$key};
274             # push(@{$data->{$key}}, $val);
275             }
276             else
277             {
278 0           $val=~s/[\x00\x1f]+//g;
279 0           $data->{$key} = $val;
280             }
281             }
282             else
283             {
284             ## print STDERR "Can't parse: $_";
285             }
286             }
287 0           close(AFMF);
288 0 0         unless (exists $data->{wx}->{'.notdef'})
289             {
290 0           $data->{wx}->{'.notdef'} = 0;
291 0           $data->{bbox}{'.notdef'} = [0, 0, 0, 0];
292             }
293            
294 0           $data->{avgwidth2} /= scalar keys %{$data->{bbox}} ;
  0            
295 0           $data->{avgwidth2} = int($data->{avgwidth2});
296            
297 0           $data->{fontname}=~s/[\x00-\x20]+//og;
298             ## $data->{fontname}=~s/[^A-Za-z0-9]+//og;
299            
300 0 0         if(defined $data->{fullname})
301             {
302 0           $data->{altname}=$data->{fullname};
303             }
304             else
305             {
306 0           $data->{altname}=$data->{familyname};
307 0 0         $data->{altname}.=' Italic' if($data->{italicangle}<0);
308 0 0         $data->{altname}.=' Oblique' if($data->{italicangle}>0);
309 0           $data->{altname}.=' '.$data->{weight};
310             }
311 0           $data->{apiname}=$data->{altname};
312 0           $data->{altname}=~s/[^A-Za-z0-9]+//og;
313            
314 0           $data->{subname}=$data->{weight};
315 0 0         $data->{subname}.=' Italic' if($data->{italicangle}<0);
316 0 0         $data->{subname}.=' Oblique' if($data->{italicangle}>0);
317 0           $data->{subname}=~s/[^A-Za-z0-9]+//og;
318            
319 0   0       $data->{missingwidth}||=$data->{avgwidth2};
320            
321 0           $data->{issymbol} = 0;
322 0           $data->{fontbbox} = [ split(/\s+/,$data->{fontbbox}) ];
323            
324 0           $data->{apiname}=join('',map { $_=~s/[^A-Za-z0-9]+//og; $_=ucfirst(lc(substr($_,0,2))); $_; } split(/\s+/,$data->{apiname}));
  0            
  0            
  0            
325            
326 0           $data->{flags} = 34;
327            
328 0   0       $data->{uni}||=[];
329 0           foreach my $n (0..255)
330             {
331 0   0       $data->{uni}->[$n]=uniByName($data->{char}->[$n] || '.notdef') || 0;
332             }
333 0           delete $data->{bbox};
334            
335 0           return($data);
336             }
337            
338             sub readPFM {
339 0     0 0   my ($self,$file)=@_;
340 0 0         if(! -e $file) {die "pfmfile='$file' not existant.";}
  0            
341 0           my $fh=IO::File->new();
342 0           my $data={};
343            
344 0           $data->{issymbol} = 0;
345            
346 0           $data->{wx}={};
347 0           $data->{bbox}={};
348 0           $data->{kern}={};
349 0           $data->{char}=[];
350            
351 0           my $buf;
352 0 0         open($fh,$file) || return undef;
353 0           binmode($fh,':raw');
354 0           read($fh,$buf,117 + 30);
355            
356 0           my %df;
357             # Packing structure for PFM Header
358 0           ( $df{Version},
359             $df{Size},
360             $df{Copyright},
361             $df{Type},
362             $df{Point},
363             $df{VertRes},
364             $df{HorizRes},
365             $df{Ascent},
366             $df{InternalLeading},
367             $df{ExternalLeading},
368             $df{Italic},
369             $df{Underline},
370             $df{StrikeOut},
371             $df{Weight},
372             #define FW_DONTCARE 0
373             #define FW_THIN 100
374             #define FW_EXTRALIGHT 200
375             #define FW_ULTRALIGHT FW_EXTRALIGHT
376             #define FW_LIGHT 300
377             #define FW_NORMAL 400
378             #define FW_REGULAR 400
379             #define FW_MEDIUM 500
380             #define FW_SEMIBOLD 600
381             #define FW_DEMIBOLD FW_SEMIBOLD
382             #define FW_BOLD 700
383             #define FW_EXTRABOLD 800
384             #define FW_ULTRABOLD FW_EXTRABOLD
385             #define FW_HEAVY 900
386             #define FW_BLACK FW_HEAVY
387             $df{CharSet},
388             #define ANSI_CHARSET 0
389             #define DEFAULT_CHARSET 1
390             #define SYMBOL_CHARSET 2
391             #define SHIFTJIS_CHARSET 128
392             #define HANGEUL_CHARSET 129
393             #define HANGUL_CHARSET 129
394             #define GB2312_CHARSET 134
395             #define CHINESEBIG5_CHARSET 136
396             #define GREEK_CHARSET 161
397             #define TURKISH_CHARSET 162
398             #define HEBREW_CHARSET 177
399             #define ARABIC_CHARSET 178
400             #define BALTIC_CHARSET 186
401             #define RUSSIAN_CHARSET 204
402             #define THAI_CHARSET 222
403             #define EASTEUROPE_CHARSET 238
404             #define OEM_CHARSET 255
405             #define JOHAB_CHARSET 130
406             #define VIETNAMESE_CHARSET 163
407             #define MAC_CHARSET 77
408             #define BALTIC_CHARSET 186
409             #define JOHAB_CHARSET 130
410             #define VIETNAMESE_CHARSET 163
411             $df{PixWidth},
412             $df{PixHeight},
413             $df{PitchAndFamily},
414             #define DEFAULT_PITCH 0
415             #define FIXED_PITCH 1
416             #define VARIABLE_PITCH 2
417             #define MONO_FONT 8
418             #define FF_DECORATIVE 80
419             #define FF_DONTCARE 0
420             #define FF_MODERN 48
421             #define FF_ROMAN 16
422             #define FF_SCRIPT 64
423             #define FF_SWISS 32
424             $df{AvgWidth},
425             $df{MaxWidth},
426             $df{FirstChar},
427             $df{LastChar},
428             $df{DefaultChar},
429             $df{BreakChar},
430             $df{WidthBytes},
431             $df{Device},
432             $df{Face},
433             $df{BitsPointer},
434             $df{BitsOffset},
435             $df{SizeFields}, # Two bytes, the size of extension section
436             $df{ExtMetricsOffset}, # Four bytes, offset value to the 'Extended Text Metrics' section
437             $df{ExtentTable}, # Four bytes Offset value to the Extent Table
438             $df{OriginTable}, # Four bytes 0
439             $df{PairKernTable}, # Four bytes 0
440             $df{TrackKernTable}, # Four bytes 0
441             $df{DriverInfo}, # Four bytes Offset value to the PostScript font name string
442             $df{Reserved}, # Four bytes 0
443             ) = unpack("vVa60vvvvvvvCCCvCvvCvvCCCCvVVVV vVVVVVVV",$buf); # PFM Header + Ext
444            
445 0           seek($fh,$df{Device},0);
446 0           read($fh,$buf,250);
447            
448 0           ($df{postScript}) = unpack("Z*",$buf);
449 0           $buf=substr($buf,length($df{postScript})+1,250);
450 0           ($df{windowsName}) = unpack("Z*",$buf);
451 0           $buf=substr($buf,length($df{windowsName})+1,250);
452 0           ($df{psName}) = unpack("Z*",$buf);
453            
454 0           seek($fh,$df{ExtMetricsOffset},0);
455 0           read($fh,$buf,52);
456            
457 0           ( $df{etmSize},
458             $df{PointSize},
459             $df{Orientation},
460             $df{MasterHeight},
461             $df{MinScale},
462             $df{MaxScale},
463             $df{MasterUnits},
464             $df{CapHeight},
465             $df{xHeight},
466             $df{LowerCaseAscent},
467             $df{LowerCaseDescent},
468             $df{Slant},
469             $df{SuperScript},
470             $df{SubScript},
471             $df{SuperScriptSize},
472             $df{SubScriptSize},
473             $df{UnderlineOffset},
474             $df{UnderlineWidth},
475             $df{DoubleUpperUnderlineOffset},
476             $df{DoubleLowerUnderlineOffset},
477             $df{DoubleUpperUnderlineWidth},
478             $df{DoubleLowerUnderlineWidth},
479             $df{StrikeOutOffset},
480             $df{StrikeOutWidth},
481             $df{KernPairs},
482             $df{KernTracks} ) = unpack('v*',$buf);
483            
484             #use Data::Dumper;
485             # print STDERR Dumper(\%df);
486 0           $data->{fontname}=$df{psName};
487 0           $data->{fontname}=~s/[^A-Za-z0-9]+//og;
488 0           $data->{apiname}=$df{windowsName};
489 0           $data->{apiname}=join('',map { $_=~s/[^A-Za-z0-9]+//og; $_=ucfirst(lc(substr($_,0,2))); $_; } split(/\s+/,$data->{apiname}));
  0            
  0            
  0            
490            
491 0           $data->{upem}=1000;
492            
493 0           $data->{fontbbox}=[-100,-100,$df{MaxWidth},$df{Ascent}];
494            
495 0           $data->{stemv}=0;
496 0           $data->{stemh}=0;
497            
498 0           $data->{lastchar}=$df{LastChar};
499 0           $data->{firstchar}=$df{FirstChar};
500            
501 0           $data->{missingwidth}=$df{AvgWidth};
502 0           $data->{maxwidth}=$df{MaxWidth};
503 0           $data->{ascender}=$df{Ascent};
504 0           $data->{descender}=-$df{LowerCaseDescent};
505            
506 0           $data->{flags} = 0;
507             # FixedPitch 1
508 0 0 0       $data->{flags} |= 1 if((($df{PitchAndFamily} & 1) || ($df{PitchAndFamily} & 8)) && !($df{PitchAndFamily} & 2));
      0        
509             # Serif 2
510 0 0 0       $data->{flags} |= 2 if(($df{PitchAndFamily} & 16) && !($df{PitchAndFamily} & 32));
511             # Symbolic 4
512 0 0         $data->{flags} |= 4 if($df{PitchAndFamily} & 80);
513             # Script 8
514 0 0         $data->{flags} |= 8 if($df{PitchAndFamily} & 64);
515             # Nonsymbolic 32
516 0 0         $data->{flags} |= 32 unless($df{PitchAndFamily} & 80);
517             # Italic 64
518 0 0         $data->{flags} |= 64 if($df{Italic});
519            
520             #bit 17 AllCap
521             #bit 18 SmallCap
522             #bit 19 ForceBold
523            
524 0           $data->{capheight}=$df{CapHeight};
525 0           $data->{xheight}=$df{xHeight};
526            
527 0           $data->{uni}=[ unpack('U*',decode('cp1252',pack('C*',(0..255)))) ];
528 0 0         $data->{char}=[ map { nameByUni($_) || '.notdef' } @{$data->{uni}} ];
  0            
  0            
529            
530 0           $data->{italicangle}=-12*$df{Italic};
531 0   0       $data->{isfixedpitch}=(($df{PitchAndFamily} & 8) || ($df{PitchAndFamily} & 1));
532 0           $data->{underlineposition}=-$df{UnderlineOffset};
533 0           $data->{underlinethickness}=$df{UnderlineWidth};
534            
535 0           seek($fh,$df{ExtentTable},0);
536            
537 0           foreach my $k ($df{FirstChar} .. $df{LastChar}) {
538 0           read($fh,$buf,2);
539 0           my ($wx)=unpack('v',$buf);
540 0           $data->{wx}->{$data->{char}->[$k]}=$wx;
541             # print STDERR "e: c=$k n='".$data->{char}->[$k]."' wx='$wx'\n";
542             }
543 0           $data->{pfm}=\%df;
544 0           close($fh);
545            
546 0           return($data);
547             }
548            
549             sub readXFM {
550 0     0 0   my ($class,$xfmfile) = @_;
551 0 0         die "cannot find font '$xfmfile' ..." unless(-f $xfmfile);
552            
553 0           my $data={};
554            
555 0           return($data);
556             }
557            
558            
559             1;
560            
561             __END__