line |
stmt |
bran |
cond |
sub |
pod |
time |
code |
1
|
|
|
|
|
|
|
package PDF::Builder::Resource::Font::Postscript; |
2
|
|
|
|
|
|
|
|
3
|
1
|
|
|
1
|
|
1324
|
use base 'PDF::Builder::Resource::Font'; |
|
1
|
|
|
|
|
2
|
|
|
1
|
|
|
|
|
94
|
|
4
|
|
|
|
|
|
|
|
5
|
1
|
|
|
1
|
|
6
|
use strict; |
|
1
|
|
|
|
|
2
|
|
|
1
|
|
|
|
|
19
|
|
6
|
1
|
|
|
1
|
|
4
|
use warnings; |
|
1
|
|
|
|
|
2
|
|
|
1
|
|
|
|
|
41
|
|
7
|
|
|
|
|
|
|
|
8
|
|
|
|
|
|
|
our $VERSION = '3.024'; # VERSION |
9
|
|
|
|
|
|
|
our $LAST_UPDATE = '3.024'; # manually update whenever code is changed |
10
|
|
|
|
|
|
|
|
11
|
1
|
|
|
1
|
|
5
|
use Encode qw(:all); |
|
1
|
|
|
|
|
2
|
|
|
1
|
|
|
|
|
222
|
|
12
|
1
|
|
|
1
|
|
7
|
use IO::File qw(); |
|
1
|
|
|
|
|
2
|
|
|
1
|
|
|
|
|
16
|
|
13
|
|
|
|
|
|
|
|
14
|
1
|
|
|
1
|
|
4
|
use PDF::Builder::Util; |
|
1
|
|
|
|
|
2
|
|
|
1
|
|
|
|
|
113
|
|
15
|
1
|
|
|
1
|
|
7
|
use PDF::Builder::Basic::PDF::Utils; |
|
1
|
|
|
|
|
2
|
|
|
1
|
|
|
|
|
2961
|
|
16
|
|
|
|
|
|
|
|
17
|
|
|
|
|
|
|
=head1 NAME |
18
|
|
|
|
|
|
|
|
19
|
|
|
|
|
|
|
PDF::Builder::Resource::Font::Postscript - support routines for using PostScript fonts. Inherits from L |
20
|
|
|
|
|
|
|
|
21
|
|
|
|
|
|
|
=cut |
22
|
|
|
|
|
|
|
|
23
|
|
|
|
|
|
|
sub new { |
24
|
0
|
|
|
0
|
1
|
|
my ($class, $pdf, $psfile, %opts) = @_; |
25
|
|
|
|
|
|
|
# copy dashed option names to preferred undashed names |
26
|
0
|
0
|
0
|
|
|
|
if (defined $opts{'-encode'} && !defined $opts{'encode'}) { $opts{'encode'} = delete($opts{'-encode'}); } |
|
0
|
|
|
|
|
|
|
27
|
0
|
0
|
0
|
|
|
|
if (defined $opts{'-afmfile'} && !defined $opts{'afmfile'}) { $opts{'afmfile'} = delete($opts{'-afmfile'}); } |
|
0
|
|
|
|
|
|
|
28
|
0
|
0
|
0
|
|
|
|
if (defined $opts{'-pfmfile'} && !defined $opts{'pfmfile'}) { $opts{'pfmfile'} = delete($opts{'-pfmfile'}); } |
|
0
|
|
|
|
|
|
|
29
|
0
|
0
|
0
|
|
|
|
if (defined $opts{'-xfmfile'} && !defined $opts{'xfmfile'}) { $opts{'xfmfile'} = delete($opts{'-xfmfile'}); } |
|
0
|
|
|
|
|
|
|
30
|
0
|
0
|
0
|
|
|
|
if (defined $opts{'-pdfname'} && !defined $opts{'pdfname'}) { $opts{'pdfname'} = delete($opts{'-pdfname'}); } |
|
0
|
|
|
|
|
|
|
31
|
0
|
0
|
0
|
|
|
|
if (defined $opts{'-nocomps'} && !defined $opts{'nocomps'}) { $opts{'nocomps'} = delete($opts{'-nocomps'}); } |
|
0
|
|
|
|
|
|
|
32
|
0
|
0
|
0
|
|
|
|
if (defined $opts{'-dokern'} && !defined $opts{'dokern'}) { $opts{'dokern'} = delete($opts{'-dokern'}); } |
|
0
|
|
|
|
|
|
|
33
|
|
|
|
|
|
|
|
34
|
0
|
|
|
|
|
|
my ($self); |
35
|
|
|
|
|
|
|
my ($data); |
36
|
|
|
|
|
|
|
|
37
|
0
|
0
|
|
|
|
|
if (defined $opts{'afmfile'}) { |
|
|
0
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
38
|
0
|
|
|
|
|
|
$data = $class->readAFM($opts{'afmfile'}); |
39
|
|
|
|
|
|
|
} elsif (defined $opts{'pfmfile'}) { |
40
|
0
|
|
|
|
|
|
$data = $class->readPFM($opts{'pfmfile'}); |
41
|
|
|
|
|
|
|
} elsif (defined $opts{'xfmfile'}) { |
42
|
0
|
|
|
|
|
|
$data = $class->readXFM($opts{'xfmfile'}); |
43
|
|
|
|
|
|
|
} else { |
44
|
0
|
|
|
|
|
|
die "No proper font-metrics file specified for PostScript file '$psfile'."; |
45
|
|
|
|
|
|
|
} |
46
|
|
|
|
|
|
|
|
47
|
0
|
0
|
|
|
|
|
$class = ref $class if ref $class; |
48
|
|
|
|
|
|
|
# $self = $class->SUPER::new($pdf, $data->{'apiname'}.pdfkey().'~'.time()); |
49
|
0
|
|
|
|
|
|
$self = $class->SUPER::new($pdf, $data->{'apiname'}.'PST1f'.pdfkey()); |
50
|
0
|
0
|
|
|
|
|
$pdf->new_obj($self) unless $self->is_obj($pdf); |
51
|
0
|
|
|
|
|
|
$self->{' data'} = $data; |
52
|
|
|
|
|
|
|
|
53
|
0
|
0
|
|
|
|
|
if ($opts{'pdfname'}) { |
54
|
0
|
|
|
|
|
|
$self->name($opts{'pdfname'}); |
55
|
|
|
|
|
|
|
} |
56
|
|
|
|
|
|
|
|
57
|
0
|
|
|
|
|
|
$self->{'Subtype'} = PDFName("Type1"); |
58
|
0
|
|
|
|
|
|
$self->{'FontDescriptor'} = $self->descrByData(); |
59
|
0
|
0
|
|
|
|
|
if (-f $psfile) { |
60
|
|
|
|
|
|
|
# $self->{'BaseFont'} = PDFName(pdfkey().'+'.$self->fontname().'~'.time()); |
61
|
0
|
|
|
|
|
|
$self->{'BaseFont'} = PDFName(pdfkey().'+'.$self->fontname()); |
62
|
|
|
|
|
|
|
|
63
|
0
|
|
|
|
|
|
my ($l1,$l2,$l3, $stream) = $self->readPFAPFB($psfile); |
64
|
|
|
|
|
|
|
|
65
|
0
|
|
|
|
|
|
my $s = PDFDict(); |
66
|
0
|
|
|
|
|
|
$self->{'FontDescriptor'}->{'FontFile'} = $s; |
67
|
0
|
|
|
|
|
|
$s->{'Length1'} = PDFNum($l1); |
68
|
0
|
|
|
|
|
|
$s->{'Length2'} = PDFNum($l2); |
69
|
0
|
|
|
|
|
|
$s->{'Length3'} = PDFNum($l3); |
70
|
0
|
|
|
|
|
|
$s->{'Filter'} = PDFArray(PDFName("FlateDecode")); |
71
|
0
|
|
|
|
|
|
$s->{' stream'} = $stream; |
72
|
0
|
0
|
|
|
|
|
if (defined $pdf) { |
73
|
0
|
|
|
|
|
|
$pdf->new_obj($s); |
74
|
|
|
|
|
|
|
} |
75
|
|
|
|
|
|
|
} else { |
76
|
0
|
|
|
|
|
|
$self->{'BaseFont'} = PDFName($self->fontname()); |
77
|
|
|
|
|
|
|
} |
78
|
|
|
|
|
|
|
|
79
|
0
|
0
|
0
|
|
|
|
if (defined $opts{'encode'} && $opts{'encode'} =~ m/^utf/i) { |
80
|
0
|
|
|
|
|
|
die "Invalid multibyte encoding for psfont: $opts{'encode'}\n"; |
81
|
|
|
|
|
|
|
# probably more encodings to check |
82
|
|
|
|
|
|
|
} |
83
|
0
|
|
|
|
|
|
$self->encodeByData($opts{'encode'}); # undef arg OK |
84
|
|
|
|
|
|
|
|
85
|
0
|
0
|
|
|
|
|
$self->{'-nocomps'} = 1 if $opts{'nocomps'}; |
86
|
0
|
0
|
|
|
|
|
$self->{'-dokern'} = 1 if $opts{'dokern'}; |
87
|
|
|
|
|
|
|
|
88
|
0
|
|
|
|
|
|
return $self; |
89
|
|
|
|
|
|
|
} # end of new() |
90
|
|
|
|
|
|
|
|
91
|
|
|
|
|
|
|
sub readPFAPFB { |
92
|
0
|
|
|
0
|
0
|
|
my ($self, $file) = @_; |
93
|
0
|
|
|
|
|
|
my ($l1,$l2,$l3, $stream, $t1stream, @lines, $line, $head, $body, $tail); |
94
|
|
|
|
|
|
|
|
95
|
0
|
0
|
|
|
|
|
die "Cannot find PFA/PFB font file '$file' ..." unless -f $file; |
96
|
|
|
|
|
|
|
|
97
|
0
|
|
|
|
|
|
my $l = -s $file; |
98
|
|
|
|
|
|
|
|
99
|
0
|
0
|
|
|
|
|
open(my $inf, "<", $file) or die "$!: $file"; |
100
|
0
|
|
|
|
|
|
binmode($inf,':raw'); |
101
|
0
|
|
|
|
|
|
read($inf, $line, 2); |
102
|
0
|
|
|
|
|
|
@lines = unpack('C*', $line); |
103
|
0
|
0
|
0
|
|
|
|
if ($lines[0] == 0x80 && $lines[1] == 1) { |
|
|
0
|
|
|
|
|
|
104
|
0
|
|
|
|
|
|
read($inf, $line, 4); |
105
|
0
|
|
|
|
|
|
$l1 = unpack('V', $line); |
106
|
0
|
|
|
|
|
|
seek($inf, $l1, 1); |
107
|
0
|
|
|
|
|
|
read($inf, $line, 2); |
108
|
0
|
|
|
|
|
|
@lines = unpack('C*', $line); |
109
|
0
|
0
|
0
|
|
|
|
if ($lines[0] == 0x80 && $lines[1] == 2) { |
110
|
0
|
|
|
|
|
|
read($inf, $line, 4); |
111
|
0
|
|
|
|
|
|
$l2 = unpack('V', $line); |
112
|
|
|
|
|
|
|
} else { |
113
|
0
|
|
|
|
|
|
die "Corrupt PFB in file '$file' at marker='2'."; |
114
|
|
|
|
|
|
|
} |
115
|
0
|
|
|
|
|
|
seek($inf, $l2, 1); |
116
|
0
|
|
|
|
|
|
read($inf, $line, 2); |
117
|
0
|
|
|
|
|
|
@lines = unpack('C*', $line); |
118
|
0
|
0
|
0
|
|
|
|
if ($lines[0] == 0x80 && $lines[1] == 1) { |
119
|
0
|
|
|
|
|
|
read($inf, $line, 4); |
120
|
0
|
|
|
|
|
|
$l3 = unpack('V', $line); |
121
|
|
|
|
|
|
|
} else { |
122
|
0
|
|
|
|
|
|
die "Corrupt PFB in file '$file' at marker='3'."; |
123
|
|
|
|
|
|
|
} |
124
|
0
|
|
|
|
|
|
seek($inf, 0, 0); |
125
|
0
|
|
|
|
|
|
@lines = <$inf>; |
126
|
0
|
|
|
|
|
|
$stream = join('', @lines); |
127
|
0
|
|
|
|
|
|
$t1stream = substr($stream, 6, $l1); |
128
|
0
|
|
|
|
|
|
$t1stream .= substr($stream, 12+$l1, $l2); |
129
|
0
|
|
|
|
|
|
$t1stream .= substr($stream, 18+$l1+$l2, $l3); |
130
|
|
|
|
|
|
|
} elsif ($line eq '%!') { |
131
|
0
|
|
|
|
|
|
seek($inf, 0, 0); |
132
|
0
|
|
|
|
|
|
while ($line = <$inf>) { |
133
|
0
|
0
|
|
|
|
|
if (!$l1) { |
|
|
0
|
|
|
|
|
|
134
|
0
|
|
|
|
|
|
$head .= $line; |
135
|
0
|
0
|
|
|
|
|
if ($line=~/eexec$/) { |
136
|
0
|
|
|
|
|
|
chomp($head); |
137
|
0
|
|
|
|
|
|
$head .= "\x0d"; |
138
|
0
|
|
|
|
|
|
$l1 = length($head); |
139
|
|
|
|
|
|
|
} |
140
|
|
|
|
|
|
|
} elsif (!$l2) { |
141
|
0
|
0
|
|
|
|
|
if ($line =~ /^0+$/) { |
142
|
0
|
|
|
|
|
|
$l2 = length($body); |
143
|
0
|
|
|
|
|
|
$tail = $line; |
144
|
|
|
|
|
|
|
} else { |
145
|
0
|
|
|
|
|
|
chomp($line); |
146
|
0
|
|
|
|
|
|
$body .= pack('H*', $line); |
147
|
|
|
|
|
|
|
} |
148
|
|
|
|
|
|
|
} else { |
149
|
0
|
|
|
|
|
|
$tail .= $line; |
150
|
|
|
|
|
|
|
} |
151
|
|
|
|
|
|
|
} |
152
|
0
|
|
|
|
|
|
$l3 = length($tail); |
153
|
0
|
|
|
|
|
|
$t1stream = "$head$body$tail"; |
154
|
|
|
|
|
|
|
} else { |
155
|
0
|
|
|
|
|
|
die "Unsupported font-format in file '$file' at marker='1'."; |
156
|
|
|
|
|
|
|
} |
157
|
0
|
|
|
|
|
|
close($inf); |
158
|
|
|
|
|
|
|
|
159
|
0
|
|
|
|
|
|
return($l1,$l2,$l3, $t1stream); |
160
|
|
|
|
|
|
|
} # end of readPFAPFB() |
161
|
|
|
|
|
|
|
|
162
|
|
|
|
|
|
|
# $datahashref = $self->readAFM( $afmfile ); |
163
|
|
|
|
|
|
|
|
164
|
|
|
|
|
|
|
sub readAFM { |
165
|
0
|
|
|
0
|
0
|
|
my ($self, $file) = @_; |
166
|
|
|
|
|
|
|
|
167
|
0
|
|
|
|
|
|
my $data = {}; |
168
|
0
|
|
|
|
|
|
$data->{'wx'} = {}; |
169
|
0
|
|
|
|
|
|
$data->{'bbox'} = {}; |
170
|
0
|
|
|
|
|
|
$data->{'char'} = []; |
171
|
0
|
|
|
|
|
|
$data->{'firstchar'} = 255; |
172
|
0
|
|
|
|
|
|
$data->{'lastchar'} = 0; |
173
|
|
|
|
|
|
|
|
174
|
0
|
0
|
|
|
|
|
if (! -e $file) { |
175
|
0
|
|
|
|
|
|
die "File='$file' not found."; |
176
|
|
|
|
|
|
|
} |
177
|
0
|
0
|
|
|
|
|
open(my $afmf, "<", $file) or die "Can't find the AFM file for $file"; |
178
|
0
|
|
|
|
|
|
local($/, $_) = ("\n", undef); # ensure correct $INPUT_RECORD_SEPARATOR |
179
|
0
|
|
|
|
|
|
while ($_ = <$afmf>) { |
180
|
0
|
0
|
|
|
|
|
if (/^StartCharMetrics/ .. /^EndCharMetrics/) { |
|
|
0
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
181
|
|
|
|
|
|
|
# only lines that start with "C" or "CH" are parsed |
182
|
0
|
0
|
|
|
|
|
next unless $_ =~ /^CH?\s/; |
183
|
0
|
|
|
|
|
|
my ($ch) = $_ =~ /^CH?\s+(\d+)\s*;/; |
184
|
0
|
|
0
|
|
|
|
$ch = $ch || 0; |
185
|
0
|
|
|
|
|
|
my ($name) = $_ =~ /\bN\s+(\.?\w+)\s*;/; |
186
|
0
|
|
|
|
|
|
my ($wx) = $_ =~ /\bWX\s+(\d+)\s*;/; |
187
|
0
|
|
|
|
|
|
my ($bbox) = $_ =~ /\bB\s+([^;]+);/; |
188
|
0
|
|
|
|
|
|
$bbox =~ s/\s+$//; |
189
|
|
|
|
|
|
|
# Should also parse ligature data (format: L successor ligature) |
190
|
0
|
|
|
|
|
|
$data->{'avgwidth2'} += $wx ; |
191
|
0
|
0
|
0
|
|
|
|
$data->{'maxwidth'} = ($data->{'maxwidth'}||0) < $wx? $wx: $data->{'maxwidth'}||0; |
|
|
|
0
|
|
|
|
|
192
|
0
|
|
|
|
|
|
$data->{'wx'}->{$name} = $wx; |
193
|
0
|
|
|
|
|
|
$data->{'bbox'}->{$name} = [split(/\s+/,$bbox)]; |
194
|
0
|
0
|
|
|
|
|
if ($ch > 0) { |
195
|
0
|
|
|
|
|
|
$data->{'char'}->[$ch] = $name; |
196
|
|
|
|
|
|
|
} |
197
|
0
|
0
|
|
|
|
|
$data->{'lastchar'} = $data->{'lastchar'} < $ch? $ch: $data->{'lastchar'}; |
198
|
0
|
0
|
|
|
|
|
$data->{'firstchar'} = $data->{'firstchar'} > $ch? $ch: $data->{'firstchar'}; |
199
|
0
|
|
|
|
|
|
next; |
200
|
|
|
|
|
|
|
} elsif (/^StartKernData/ .. /^EndKernData/) { |
201
|
0
|
|
0
|
|
|
|
$data->{'kern'} ||= {}; |
202
|
0
|
0
|
|
|
|
|
if ($_ =~ m|^KPX\s+(\S+)\s+(\S+)\s+(\S+)\s*$|i) { |
203
|
0
|
|
|
|
|
|
$data->{'kern'}->{"$1:$2"} = $3; |
204
|
|
|
|
|
|
|
} |
205
|
|
|
|
|
|
|
} elsif (/^StartComposites/ .. /^EndComposites/) { |
206
|
0
|
|
0
|
|
|
|
$data->{'comps'} ||= {}; |
207
|
0
|
0
|
|
|
|
|
if ($_ =~ m|^CC\s+(\S+)\s+(\S+)\s+;|i) { |
208
|
0
|
|
|
|
|
|
my ($name, $comp) = ($1, $2); |
209
|
0
|
|
|
|
|
|
my @cv = split(/;/, $_); |
210
|
0
|
|
|
|
|
|
shift @cv; |
211
|
0
|
|
|
|
|
|
my $rng = []; |
212
|
0
|
|
|
|
|
|
foreach (1..$comp) { |
213
|
0
|
|
|
|
|
|
my @c1 = split(/\s+/,shift @cv); |
214
|
0
|
|
|
|
|
|
push @{$rng}, $c1[1],$c1[2],$c1[3]; |
|
0
|
|
|
|
|
|
|
215
|
|
|
|
|
|
|
} |
216
|
0
|
|
|
|
|
|
$data->{'comps'}->{$name} = $rng; |
217
|
|
|
|
|
|
|
} |
218
|
|
|
|
|
|
|
} |
219
|
0
|
0
|
|
|
|
|
last if $_ =~ /^EndFontMetrics/; |
220
|
0
|
0
|
|
|
|
|
if (/(^\w+)\s+(.*)/) { |
221
|
0
|
|
|
|
|
|
my($key, $val) = ($1, $2); |
222
|
0
|
|
|
|
|
|
$key = lc($key); |
223
|
0
|
0
|
|
|
|
|
if (defined $data->{$key}) { |
224
|
|
|
|
|
|
|
# $data->{$key} = [ $data->{$key} ] unless ref $data->{$key}; |
225
|
|
|
|
|
|
|
# push(@{$data->{$key}}, $val); |
226
|
|
|
|
|
|
|
} else { |
227
|
0
|
|
|
|
|
|
$val =~ s/[\x00\x1f]+//g; |
228
|
0
|
|
|
|
|
|
$data->{$key} = $val; |
229
|
|
|
|
|
|
|
} |
230
|
|
|
|
|
|
|
} else { |
231
|
|
|
|
|
|
|
## print STDERR "Can't parse: $_"; |
232
|
|
|
|
|
|
|
} |
233
|
|
|
|
|
|
|
} |
234
|
0
|
|
|
|
|
|
close($afmf); |
235
|
0
|
0
|
|
|
|
|
unless (exists $data->{'wx'}->{'.notdef'}) { |
236
|
0
|
|
|
|
|
|
$data->{'wx'}->{'.notdef'} = 0; |
237
|
0
|
|
|
|
|
|
$data->{'bbox'}{'.notdef'} = [0, 0, 0, 0]; |
238
|
|
|
|
|
|
|
} |
239
|
|
|
|
|
|
|
|
240
|
0
|
|
|
|
|
|
$data->{'avgwidth2'} /= scalar keys %{$data->{'bbox'}} ; |
|
0
|
|
|
|
|
|
|
241
|
0
|
|
|
|
|
|
$data->{'avgwidth2'} = int($data->{'avgwidth2'}); |
242
|
|
|
|
|
|
|
|
243
|
0
|
|
|
|
|
|
$data->{'fontname'} =~ s/[\x00-\x20]+//og; |
244
|
|
|
|
|
|
|
## $data->{'fontname'} =~ s/[^A-Za-z0-9]+//og; |
245
|
|
|
|
|
|
|
|
246
|
0
|
0
|
|
|
|
|
if (defined $data->{'fullname'}) { |
247
|
0
|
|
|
|
|
|
$data->{'altname'} = $data->{'fullname'}; |
248
|
|
|
|
|
|
|
} else { |
249
|
0
|
|
|
|
|
|
$data->{'altname'} = $data->{'familyname'}; |
250
|
0
|
0
|
|
|
|
|
$data->{'altname'} .= ' Italic' if $data->{'italicangle'} < 0; |
251
|
0
|
0
|
|
|
|
|
$data->{'altname'} .= ' Oblique' if $data->{'italicangle'} > 0; |
252
|
0
|
|
|
|
|
|
$data->{'altname'} .= ' '.$data->{'weight'}; |
253
|
|
|
|
|
|
|
} |
254
|
0
|
|
|
|
|
|
$data->{'apiname'} = $data->{'altname'}; |
255
|
0
|
|
|
|
|
|
$data->{'altname'} =~ s/[^A-Za-z0-9]+//og; |
256
|
|
|
|
|
|
|
|
257
|
0
|
|
|
|
|
|
$data->{'subname'} = $data->{'weight'}; |
258
|
0
|
0
|
|
|
|
|
$data->{'subname'} .= ' Italic' if $data->{'italicangle'} < 0; |
259
|
0
|
0
|
|
|
|
|
$data->{'subname'} .= ' Oblique' if $data->{'italicangle'} > 0; |
260
|
0
|
|
|
|
|
|
$data->{'subname'} =~ s/[^A-Za-z0-9]+//og; |
261
|
|
|
|
|
|
|
|
262
|
0
|
|
0
|
|
|
|
$data->{'missingwidth'} ||= $data->{'avgwidth2'}; |
263
|
|
|
|
|
|
|
|
264
|
0
|
|
|
|
|
|
$data->{'issymbol'} = 0; |
265
|
0
|
|
|
|
|
|
$data->{'fontbbox'} = [ split(/\s+/,$data->{'fontbbox'}) ]; |
266
|
|
|
|
|
|
|
|
267
|
0
|
|
|
|
|
|
$data->{'apiname'} = join('', map { ucfirst(lc(substr($_, 0, 2))) } split m/[^A-Za-z0-9\s]+/, $data->{'apiname'}); |
|
0
|
|
|
|
|
|
|
268
|
|
|
|
|
|
|
|
269
|
0
|
|
|
|
|
|
$data->{'flags'} = 34; |
270
|
|
|
|
|
|
|
|
271
|
0
|
|
0
|
|
|
|
$data->{'uni'} ||= []; |
272
|
0
|
|
|
|
|
|
foreach my $n (0..255) { |
273
|
0
|
|
0
|
|
|
|
$data->{'uni'}->[$n] = uniByName($data->{'char'}->[$n] || '.notdef') || 0; |
274
|
|
|
|
|
|
|
} |
275
|
0
|
|
|
|
|
|
delete $data->{'bbox'}; |
276
|
|
|
|
|
|
|
|
277
|
0
|
|
|
|
|
|
return $data; |
278
|
|
|
|
|
|
|
} # end of readAFM() |
279
|
|
|
|
|
|
|
|
280
|
|
|
|
|
|
|
sub readPFM { |
281
|
0
|
|
|
0
|
0
|
|
my ($self, $file) = @_; |
282
|
|
|
|
|
|
|
|
283
|
0
|
0
|
|
|
|
|
if (! -e $file) { |
284
|
0
|
|
|
|
|
|
die "pfmfile='$file' not found."; |
285
|
|
|
|
|
|
|
} |
286
|
0
|
|
|
|
|
|
my $fh = IO::File->new(); |
287
|
0
|
|
|
|
|
|
my $data = {}; |
288
|
|
|
|
|
|
|
|
289
|
0
|
|
|
|
|
|
$data->{'issymbol'} = 0; |
290
|
|
|
|
|
|
|
|
291
|
0
|
|
|
|
|
|
$data->{'wx'} = {}; |
292
|
0
|
|
|
|
|
|
$data->{'bbox'} = {}; |
293
|
0
|
|
|
|
|
|
$data->{'kern'} = {}; |
294
|
0
|
|
|
|
|
|
$data->{'char'} = []; |
295
|
|
|
|
|
|
|
|
296
|
0
|
|
|
|
|
|
my $buf; |
297
|
0
|
0
|
|
|
|
|
open($fh, "<", $file) || return; |
298
|
0
|
|
|
|
|
|
binmode($fh, ':raw'); |
299
|
0
|
|
|
|
|
|
read($fh, $buf, 117 + 30); |
300
|
|
|
|
|
|
|
|
301
|
0
|
|
|
|
|
|
my %df; |
302
|
|
|
|
|
|
|
# Packing structure for PFM Header |
303
|
|
|
|
|
|
|
( $df{'Version'}, |
304
|
|
|
|
|
|
|
$df{'Size'}, |
305
|
|
|
|
|
|
|
$df{'Copyright'}, |
306
|
|
|
|
|
|
|
$df{'Type'}, |
307
|
|
|
|
|
|
|
$df{'Point'}, |
308
|
|
|
|
|
|
|
$df{'VertRes'}, |
309
|
|
|
|
|
|
|
$df{'HorizRes'}, |
310
|
|
|
|
|
|
|
$df{'Ascent'}, |
311
|
|
|
|
|
|
|
$df{'InternalLeading'}, |
312
|
|
|
|
|
|
|
$df{'ExternalLeading'}, |
313
|
|
|
|
|
|
|
$df{'Italic'}, |
314
|
|
|
|
|
|
|
$df{'Underline'}, |
315
|
|
|
|
|
|
|
$df{'StrikeOut'}, |
316
|
|
|
|
|
|
|
$df{'Weight'}, |
317
|
|
|
|
|
|
|
#define FW_DONTCARE 0 |
318
|
|
|
|
|
|
|
#define FW_THIN 100 |
319
|
|
|
|
|
|
|
#define FW_EXTRALIGHT 200 |
320
|
|
|
|
|
|
|
#define FW_ULTRALIGHT FW_EXTRALIGHT |
321
|
|
|
|
|
|
|
#define FW_LIGHT 300 |
322
|
|
|
|
|
|
|
#define FW_NORMAL 400 |
323
|
|
|
|
|
|
|
#define FW_REGULAR 400 |
324
|
|
|
|
|
|
|
#define FW_MEDIUM 500 |
325
|
|
|
|
|
|
|
#define FW_SEMIBOLD 600 |
326
|
|
|
|
|
|
|
#define FW_DEMIBOLD FW_SEMIBOLD |
327
|
|
|
|
|
|
|
#define FW_BOLD 700 |
328
|
|
|
|
|
|
|
#define FW_EXTRABOLD 800 |
329
|
|
|
|
|
|
|
#define FW_ULTRABOLD FW_EXTRABOLD |
330
|
|
|
|
|
|
|
#define FW_HEAVY 900 |
331
|
|
|
|
|
|
|
#define FW_BLACK FW_HEAVY |
332
|
|
|
|
|
|
|
$df{'CharSet'}, |
333
|
|
|
|
|
|
|
#define ANSI_CHARSET 0 |
334
|
|
|
|
|
|
|
#define DEFAULT_CHARSET 1 |
335
|
|
|
|
|
|
|
#define SYMBOL_CHARSET 2 |
336
|
|
|
|
|
|
|
#define SHIFTJIS_CHARSET 128 |
337
|
|
|
|
|
|
|
#define HANGEUL_CHARSET 129 |
338
|
|
|
|
|
|
|
#define HANGUL_CHARSET 129 |
339
|
|
|
|
|
|
|
#define GB2312_CHARSET 134 |
340
|
|
|
|
|
|
|
#define CHINESEBIG5_CHARSET 136 |
341
|
|
|
|
|
|
|
#define GREEK_CHARSET 161 |
342
|
|
|
|
|
|
|
#define TURKISH_CHARSET 162 |
343
|
|
|
|
|
|
|
#define HEBREW_CHARSET 177 |
344
|
|
|
|
|
|
|
#define ARABIC_CHARSET 178 |
345
|
|
|
|
|
|
|
#define BALTIC_CHARSET 186 |
346
|
|
|
|
|
|
|
#define RUSSIAN_CHARSET 204 |
347
|
|
|
|
|
|
|
#define THAI_CHARSET 222 |
348
|
|
|
|
|
|
|
#define EASTEUROPE_CHARSET 238 |
349
|
|
|
|
|
|
|
#define OEM_CHARSET 255 |
350
|
|
|
|
|
|
|
#define JOHAB_CHARSET 130 |
351
|
|
|
|
|
|
|
#define VIETNAMESE_CHARSET 163 |
352
|
|
|
|
|
|
|
#define MAC_CHARSET 77 |
353
|
|
|
|
|
|
|
#define BALTIC_CHARSET 186 |
354
|
|
|
|
|
|
|
#define JOHAB_CHARSET 130 |
355
|
|
|
|
|
|
|
#define VIETNAMESE_CHARSET 163 |
356
|
|
|
|
|
|
|
$df{'PixWidth'}, |
357
|
|
|
|
|
|
|
$df{'PixHeight'}, |
358
|
|
|
|
|
|
|
$df{'PitchAndFamily'}, |
359
|
|
|
|
|
|
|
#define DEFAULT_PITCH 0 |
360
|
|
|
|
|
|
|
#define FIXED_PITCH 1 |
361
|
|
|
|
|
|
|
#define VARIABLE_PITCH 2 |
362
|
|
|
|
|
|
|
#define MONO_FONT 8 |
363
|
|
|
|
|
|
|
#define FF_DECORATIVE 80 |
364
|
|
|
|
|
|
|
#define FF_DONTCARE 0 |
365
|
|
|
|
|
|
|
#define FF_MODERN 48 |
366
|
|
|
|
|
|
|
#define FF_ROMAN 16 |
367
|
|
|
|
|
|
|
#define FF_SCRIPT 64 |
368
|
|
|
|
|
|
|
#define FF_SWISS 32 |
369
|
|
|
|
|
|
|
$df{'AvgWidth'}, |
370
|
|
|
|
|
|
|
$df{'MaxWidth'}, |
371
|
|
|
|
|
|
|
$df{'FirstChar'}, |
372
|
|
|
|
|
|
|
$df{'LastChar'}, |
373
|
|
|
|
|
|
|
$df{'DefaultChar'}, |
374
|
|
|
|
|
|
|
$df{'BreakChar'}, |
375
|
|
|
|
|
|
|
$df{'WidthBytes'}, |
376
|
|
|
|
|
|
|
$df{'Device'}, |
377
|
|
|
|
|
|
|
$df{'Face'}, |
378
|
|
|
|
|
|
|
$df{'BitsPointer'}, |
379
|
|
|
|
|
|
|
$df{'BitsOffset'}, |
380
|
|
|
|
|
|
|
$df{'SizeFields'}, # Two bytes, the size of extension section |
381
|
|
|
|
|
|
|
$df{'ExtMetricsOffset'}, # Four bytes, offset value to the 'Extended Text Metrics' section |
382
|
|
|
|
|
|
|
$df{'ExtentTable'}, # Four bytes Offset value to the Extent Table |
383
|
|
|
|
|
|
|
$df{'OriginTable'}, # Four bytes 0 |
384
|
|
|
|
|
|
|
$df{'PairKernTable'}, # Four bytes 0 |
385
|
|
|
|
|
|
|
$df{'TrackKernTable'}, # Four bytes 0 |
386
|
|
|
|
|
|
|
$df{'DriverInfo'}, # Four bytes Offset value to the PostScript font name string |
387
|
0
|
|
|
|
|
|
$df{'Reserved'}, # Four bytes 0 |
388
|
|
|
|
|
|
|
) = unpack("vVa60vvvvvvvCCCvCvvCvvCCCCvVVVV vVVVVVVV",$buf); # PFM Header + Ext |
389
|
|
|
|
|
|
|
|
390
|
0
|
|
|
|
|
|
seek($fh, $df{Device}, 0); |
391
|
0
|
|
|
|
|
|
read($fh, $buf, 250); |
392
|
|
|
|
|
|
|
|
393
|
0
|
|
|
|
|
|
($df{'postScript'}) = unpack("Z*", $buf); |
394
|
0
|
|
|
|
|
|
$buf = substr($buf, length($df{'postScript'})+1, 250); |
395
|
0
|
|
|
|
|
|
($df{'windowsName'}) = unpack("Z*", $buf); |
396
|
0
|
|
|
|
|
|
$buf = substr($buf, length($df{'windowsName'})+1, 250); |
397
|
0
|
|
|
|
|
|
($df{'psName'}) = unpack("Z*", $buf); |
398
|
|
|
|
|
|
|
|
399
|
0
|
|
|
|
|
|
seek($fh, $df{'ExtMetricsOffset'}, 0); |
400
|
0
|
|
|
|
|
|
read($fh, $buf, 52); |
401
|
|
|
|
|
|
|
|
402
|
|
|
|
|
|
|
( $df{'etmSize'}, |
403
|
|
|
|
|
|
|
$df{'PointSize'}, |
404
|
|
|
|
|
|
|
$df{'Orientation'}, |
405
|
|
|
|
|
|
|
$df{'MasterHeight'}, |
406
|
|
|
|
|
|
|
$df{'MinScale'}, |
407
|
|
|
|
|
|
|
$df{'MaxScale'}, |
408
|
|
|
|
|
|
|
$df{'MasterUnits'}, |
409
|
|
|
|
|
|
|
$df{'CapHeight'}, |
410
|
|
|
|
|
|
|
$df{'xHeight'}, |
411
|
|
|
|
|
|
|
$df{'LowerCaseAscent'}, |
412
|
|
|
|
|
|
|
$df{'LowerCaseDescent'}, |
413
|
|
|
|
|
|
|
$df{'Slant'}, |
414
|
|
|
|
|
|
|
$df{'SuperScript'}, |
415
|
|
|
|
|
|
|
$df{'SubScript'}, |
416
|
|
|
|
|
|
|
$df{'SuperScriptSize'}, |
417
|
|
|
|
|
|
|
$df{'SubScriptSize'}, |
418
|
|
|
|
|
|
|
$df{'UnderlineOffset'}, |
419
|
|
|
|
|
|
|
$df{'UnderlineWidth'}, |
420
|
|
|
|
|
|
|
$df{'DoubleUpperUnderlineOffset'}, |
421
|
|
|
|
|
|
|
$df{'DoubleLowerUnderlineOffset'}, |
422
|
|
|
|
|
|
|
$df{'DoubleUpperUnderlineWidth'}, |
423
|
|
|
|
|
|
|
$df{'DoubleLowerUnderlineWidth'}, |
424
|
|
|
|
|
|
|
$df{'StrikeOutOffset'}, |
425
|
|
|
|
|
|
|
$df{'StrikeOutWidth'}, |
426
|
|
|
|
|
|
|
$df{'KernPairs'}, |
427
|
0
|
|
|
|
|
|
$df{'KernTracks'} ) = unpack('v*', $buf); |
428
|
|
|
|
|
|
|
|
429
|
0
|
|
|
|
|
|
$data->{'fontname'} = $df{'psName'}; |
430
|
0
|
|
|
|
|
|
$data->{'fontname'} =~ s/[^A-Za-z0-9]+//og; |
431
|
0
|
|
|
|
|
|
$data->{'apiname'} = join('', map { ucfirst(lc(substr($_, 0, 2))) } split m/[^A-Za-z0-9\s]+/, $df{'windowsName'}); |
|
0
|
|
|
|
|
|
|
432
|
|
|
|
|
|
|
|
433
|
0
|
|
|
|
|
|
$data->{'upem'} = 1000; |
434
|
|
|
|
|
|
|
|
435
|
0
|
|
|
|
|
|
$data->{'fontbbox'} = [-100,-100, $df{'MaxWidth'},$df{'Ascent'}]; |
436
|
|
|
|
|
|
|
|
437
|
0
|
|
|
|
|
|
$data->{'stemv'} = 0; |
438
|
0
|
|
|
|
|
|
$data->{'stemh'} = 0; |
439
|
|
|
|
|
|
|
|
440
|
0
|
|
0
|
|
|
|
$data->{'lastchar'} = $df{'LastChar'}||0; # running max |
441
|
0
|
|
0
|
|
|
|
$data->{'firstchar'} = $df{'FirstChar'}||255; # running min |
442
|
|
|
|
|
|
|
|
443
|
0
|
|
|
|
|
|
$data->{'missingwidth'} = $df{'AvgWidth'}; |
444
|
0
|
|
|
|
|
|
$data->{'maxwidth'} = $df{'MaxWidth'}; |
445
|
0
|
|
|
|
|
|
$data->{'ascender'} = $df{'Ascent'}; |
446
|
0
|
|
|
|
|
|
$data->{'descender'} = -$df{'LowerCaseDescent'}; |
447
|
|
|
|
|
|
|
|
448
|
0
|
|
|
|
|
|
$data->{'flags'} = 0; |
449
|
|
|
|
|
|
|
# FixedPitch 1 |
450
|
0
|
0
|
0
|
|
|
|
$data->{'flags'} |= 1 if (($df{'PitchAndFamily'} & 1) || ($df{'PitchAndFamily'} & 8)) && !($df{'PitchAndFamily'} & 2); |
|
|
|
0
|
|
|
|
|
451
|
|
|
|
|
|
|
# Serif 2 |
452
|
0
|
0
|
0
|
|
|
|
$data->{'flags'} |= 2 if ($df{'PitchAndFamily'} & 16) && !($df{'PitchAndFamily'} & 32); |
453
|
|
|
|
|
|
|
# Symbolic 4 |
454
|
0
|
0
|
|
|
|
|
$data->{'flags'} |= 4 if $df{'PitchAndFamily'} & 80; |
455
|
|
|
|
|
|
|
# Script 8 |
456
|
0
|
0
|
|
|
|
|
$data->{'flags'} |= 8 if $df{'PitchAndFamily'} & 64; |
457
|
|
|
|
|
|
|
# Nonsymbolic 32 |
458
|
0
|
0
|
|
|
|
|
$data->{'flags'} |= 32 unless $df{'PitchAndFamily'} & 80; |
459
|
|
|
|
|
|
|
# Italic 64 |
460
|
0
|
0
|
|
|
|
|
$data->{'flags'} |= 64 if $df{'Italic'}; |
461
|
|
|
|
|
|
|
|
462
|
|
|
|
|
|
|
#bit 17 AllCap |
463
|
|
|
|
|
|
|
#bit 18 SmallCap |
464
|
|
|
|
|
|
|
#bit 19 ForceBold |
465
|
|
|
|
|
|
|
|
466
|
0
|
|
|
|
|
|
$data->{'capheight'} = $df{'CapHeight'}; |
467
|
0
|
|
|
|
|
|
$data->{'xheight'} = $df{'xHeight'}; |
468
|
|
|
|
|
|
|
|
469
|
0
|
|
|
|
|
|
$data->{'uni'} = [ unpack('U*', decode('cp1252', pack('C*',(0..255)))) ]; |
470
|
0
|
0
|
|
|
|
|
$data->{'char'} = [ map { nameByUni($_) || '.notdef' } @{$data->{'uni'}} ]; |
|
0
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
471
|
|
|
|
|
|
|
|
472
|
0
|
|
|
|
|
|
$data->{'italicangle'} = -12*$df{'Italic'}; |
473
|
0
|
|
0
|
|
|
|
$data->{'isfixedpitch'} = ($df{'PitchAndFamily'} & 8) || ($df{'PitchAndFamily'} & 1); |
474
|
0
|
|
|
|
|
|
$data->{'underlineposition'} = -$df{'UnderlineOffset'}; |
475
|
0
|
|
|
|
|
|
$data->{'underlinethickness'} = $df{'UnderlineWidth'}; |
476
|
|
|
|
|
|
|
|
477
|
0
|
|
|
|
|
|
seek($fh, $df{'ExtentTable'}, 0); |
478
|
|
|
|
|
|
|
|
479
|
0
|
|
|
|
|
|
foreach my $k ($df{'FirstChar'} .. $df{'LastChar'}) { |
480
|
0
|
|
|
|
|
|
read($fh, $buf, 2); |
481
|
0
|
|
|
|
|
|
my ($wx) = unpack('v', $buf); |
482
|
0
|
|
|
|
|
|
$data->{'wx'}->{$data->{'char'}->[$k]} = $wx; |
483
|
|
|
|
|
|
|
# print STDERR "e: c=$k n='".$data->{'char'}->[$k]."' wx='$wx'\n"; |
484
|
|
|
|
|
|
|
} |
485
|
0
|
|
|
|
|
|
$data->{'pfm'} = \%df; |
486
|
0
|
|
|
|
|
|
close($fh); |
487
|
|
|
|
|
|
|
|
488
|
0
|
|
|
|
|
|
return $data; |
489
|
|
|
|
|
|
|
} # end of readPFM() |
490
|
|
|
|
|
|
|
|
491
|
|
|
|
|
|
|
sub readXFM { |
492
|
0
|
|
|
0
|
0
|
|
my ($class, $xfmfile) = @_; |
493
|
|
|
|
|
|
|
|
494
|
0
|
0
|
|
|
|
|
die "Cannot find font '$xfmfile' ..." unless -f $xfmfile; |
495
|
|
|
|
|
|
|
|
496
|
0
|
|
|
|
|
|
my $data = {}; |
497
|
|
|
|
|
|
|
|
498
|
0
|
|
|
|
|
|
return $data; |
499
|
|
|
|
|
|
|
} |
500
|
|
|
|
|
|
|
|
501
|
|
|
|
|
|
|
1; |