line |
stmt |
bran |
cond |
sub |
pod |
time |
code |
1
|
|
|
|
|
|
|
package PDF::Builder::Resource::Font::SynFont; |
2
|
|
|
|
|
|
|
|
3
|
1
|
|
|
1
|
|
1106
|
use base 'PDF::Builder::Resource::Font'; |
|
1
|
|
|
|
|
2
|
|
|
1
|
|
|
|
|
114
|
|
4
|
|
|
|
|
|
|
|
5
|
1
|
|
|
1
|
|
7
|
use strict; |
|
1
|
|
|
|
|
3
|
|
|
1
|
|
|
|
|
21
|
|
6
|
1
|
|
|
1
|
|
7
|
use warnings; |
|
1
|
|
|
|
|
2
|
|
|
1
|
|
|
|
|
59
|
|
7
|
|
|
|
|
|
|
|
8
|
|
|
|
|
|
|
our $VERSION = '3.025'; # VERSION |
9
|
|
|
|
|
|
|
our $LAST_UPDATE = '3.024'; # manually update whenever code is changed |
10
|
|
|
|
|
|
|
|
11
|
1
|
|
|
1
|
|
6
|
use Math::Trig; # CAUTION: deg2rad(0) = deg2rad(360) = 0! |
|
1
|
|
|
|
|
2
|
|
|
1
|
|
|
|
|
259
|
|
12
|
1
|
|
|
1
|
|
8
|
use Unicode::UCD 'charinfo'; |
|
1
|
|
|
|
|
2
|
|
|
1
|
|
|
|
|
90
|
|
13
|
|
|
|
|
|
|
|
14
|
1
|
|
|
1
|
|
7
|
use PDF::Builder::Util; |
|
1
|
|
|
|
|
3
|
|
|
1
|
|
|
|
|
127
|
|
15
|
1
|
|
|
1
|
|
12
|
use PDF::Builder::Basic::PDF::Utils; |
|
1
|
|
|
|
|
3
|
|
|
1
|
|
|
|
|
2393
|
|
16
|
|
|
|
|
|
|
|
17
|
|
|
|
|
|
|
# for noncompatible options, consider '-entry_point' = 'synfont' or |
18
|
|
|
|
|
|
|
# 'synthetic_font' to be picked up and processed correctly per entry point |
19
|
|
|
|
|
|
|
|
20
|
|
|
|
|
|
|
=head1 NAME |
21
|
|
|
|
|
|
|
|
22
|
|
|
|
|
|
|
PDF::Builder::Resource::Font::SynFont - Module for creating temporary synthetic Fonts. |
23
|
|
|
|
|
|
|
|
24
|
|
|
|
|
|
|
=head1 SYNOPSIS |
25
|
|
|
|
|
|
|
|
26
|
|
|
|
|
|
|
This module permits you to create a "new" font (loaded temporarily, but not |
27
|
|
|
|
|
|
|
permanently stored) based on an existing font, where you can modify certain |
28
|
|
|
|
|
|
|
attributes in the original font, such as: |
29
|
|
|
|
|
|
|
|
30
|
|
|
|
|
|
|
* slant/obliqueness |
31
|
|
|
|
|
|
|
* extra weight/boldness (by drawing glyph outlines at various line |
32
|
|
|
|
|
|
|
thicknesses, rather than just filling enclosed areas) |
33
|
|
|
|
|
|
|
* condense/expand (narrower or wider characters) |
34
|
|
|
|
|
|
|
* extra space between characters |
35
|
|
|
|
|
|
|
* small caps (synthesized, not using any provided with a font) |
36
|
|
|
|
|
|
|
* change the encoding |
37
|
|
|
|
|
|
|
|
38
|
|
|
|
|
|
|
$pdf = PDF::Builder->new(); |
39
|
|
|
|
|
|
|
$cft = $pdf->font('Times-Roman'); # corefont, ttfont, etc. also works |
40
|
|
|
|
|
|
|
$sft = $pdf->synfont($cft, 'condense' => .75); # condense by 25% |
41
|
|
|
|
|
|
|
|
42
|
|
|
|
|
|
|
This works for I, I, and I; but does not |
43
|
|
|
|
|
|
|
work for I or I. |
44
|
|
|
|
|
|
|
See also L. |
45
|
|
|
|
|
|
|
|
46
|
|
|
|
|
|
|
B C |
47
|
|
|
|
|
|
|
|
48
|
|
|
|
|
|
|
This is for compatibility with recent changes to PDF::API2. |
49
|
|
|
|
|
|
|
|
50
|
|
|
|
|
|
|
=head1 METHODS |
51
|
|
|
|
|
|
|
|
52
|
|
|
|
|
|
|
=over |
53
|
|
|
|
|
|
|
|
54
|
|
|
|
|
|
|
=item $font = PDF::Builder::Resource::Font::SynFont->new($pdf, $fontobj, %opts) |
55
|
|
|
|
|
|
|
|
56
|
|
|
|
|
|
|
Returns a synfont object. C<$fontobj> is a normal font object read in from |
57
|
|
|
|
|
|
|
a file, and C<$font> is the modified output. |
58
|
|
|
|
|
|
|
|
59
|
|
|
|
|
|
|
Valid options %opts are: |
60
|
|
|
|
|
|
|
|
61
|
|
|
|
|
|
|
=over |
62
|
|
|
|
|
|
|
|
63
|
|
|
|
|
|
|
=item I |
64
|
|
|
|
|
|
|
|
65
|
|
|
|
|
|
|
Changes the encoding of the font from its default. |
66
|
|
|
|
|
|
|
See I for the supported values. B only single byte |
67
|
|
|
|
|
|
|
encodings are supported. Multibyte encodings such as UTF-8 are invalid. |
68
|
|
|
|
|
|
|
|
69
|
|
|
|
|
|
|
=item I |
70
|
|
|
|
|
|
|
|
71
|
|
|
|
|
|
|
Changes the reference-name of the font from its default. |
72
|
|
|
|
|
|
|
The reference-name is normally generated automatically and can be |
73
|
|
|
|
|
|
|
retrieved via $pdfname=$font->name(). |
74
|
|
|
|
|
|
|
|
75
|
|
|
|
|
|
|
B C (for PDF::API2 compatibility) |
76
|
|
|
|
|
|
|
|
77
|
|
|
|
|
|
|
=item I |
78
|
|
|
|
|
|
|
|
79
|
|
|
|
|
|
|
Condense/expand factor (0.1-0.9 = condense, 1 = normal, 1.1+ = expand). |
80
|
|
|
|
|
|
|
It's the multiplier for character widths vs. normal. |
81
|
|
|
|
|
|
|
|
82
|
|
|
|
|
|
|
B C and C (for PDF::API2 compatibility) |
83
|
|
|
|
|
|
|
|
84
|
|
|
|
|
|
|
The I option is a deprecated name in both PDF::Builder and PDF::API2. |
85
|
|
|
|
|
|
|
Its value is the same as I value (1 = normal, unchanged scale). |
86
|
|
|
|
|
|
|
For the I option, the value is percentage (%), with 100 being normal, |
87
|
|
|
|
|
|
|
and other values 100 times the I value. |
88
|
|
|
|
|
|
|
B |
89
|
|
|
|
|
|
|
|
90
|
|
|
|
|
|
|
=item I |
91
|
|
|
|
|
|
|
|
92
|
|
|
|
|
|
|
Italic angle (+/-) in degrees, where the character box is skewed. While |
93
|
|
|
|
|
|
|
it's unlikely that anyone will want to slant characters at +/-360 degrees, they |
94
|
|
|
|
|
|
|
should be aware that these will be treated as an angle of 0 degrees (deg2rad() |
95
|
|
|
|
|
|
|
wraps around). 0 degrees of italic slant (obliqueness) is the default. |
96
|
|
|
|
|
|
|
|
97
|
|
|
|
|
|
|
B C (for PDF::API2 compatibility) |
98
|
|
|
|
|
|
|
|
99
|
|
|
|
|
|
|
B |
100
|
|
|
|
|
|
|
|
101
|
|
|
|
|
|
|
=item I |
102
|
|
|
|
|
|
|
|
103
|
|
|
|
|
|
|
Embolding factor (0.1+, bold=1, heavy=2, ...). It is additional outline |
104
|
|
|
|
|
|
|
B (B), which expands the character (glyph) outwards (as |
105
|
|
|
|
|
|
|
well as shrinking unfilled enclosed areas such as bowls and counters). |
106
|
|
|
|
|
|
|
Normally, the glyph's outline is not drawn (it is only filled); this adds |
107
|
|
|
|
|
|
|
a thick outline. The units are in 1/100ths of a text unit. |
108
|
|
|
|
|
|
|
|
109
|
|
|
|
|
|
|
If used with the C alternate entry name, the unit is 1/1000th |
110
|
|
|
|
|
|
|
of a text unit, so you will need a value 10 times larger than with the |
111
|
|
|
|
|
|
|
C entry to get the same effect |
112
|
|
|
|
|
|
|
|
113
|
|
|
|
|
|
|
=item I |
114
|
|
|
|
|
|
|
|
115
|
|
|
|
|
|
|
Additional charspacing in thousandths of an em. |
116
|
|
|
|
|
|
|
|
117
|
|
|
|
|
|
|
=item I |
118
|
|
|
|
|
|
|
|
119
|
|
|
|
|
|
|
Create synthetic small-caps. 0 = no, 1 = yes. These are capitals of |
120
|
|
|
|
|
|
|
lowercase letters, at 80% height and 88% width. Note that this is guaranteed |
121
|
|
|
|
|
|
|
to cover ASCII lowercase letters only -- single byte encoded accented |
122
|
|
|
|
|
|
|
characters I work, but we can make no promises on accented characters |
123
|
|
|
|
|
|
|
in general, as well as ligatures! |
124
|
|
|
|
|
|
|
|
125
|
|
|
|
|
|
|
B C (for PDF::API2 compatibility) |
126
|
|
|
|
|
|
|
|
127
|
|
|
|
|
|
|
B |
128
|
|
|
|
|
|
|
|
129
|
|
|
|
|
|
|
=back |
130
|
|
|
|
|
|
|
|
131
|
|
|
|
|
|
|
=back |
132
|
|
|
|
|
|
|
|
133
|
|
|
|
|
|
|
=cut |
134
|
|
|
|
|
|
|
|
135
|
|
|
|
|
|
|
sub new { |
136
|
0
|
|
|
0
|
1
|
|
my ($class, $pdf, $font, %opts) = @_; |
137
|
|
|
|
|
|
|
# copy dashed named options to preferred undashed names |
138
|
0
|
0
|
0
|
|
|
|
if (defined $opts{'-encode'} && !defined $opts{'encode'}) { $opts{'encode'} = delete($opts{'-encode'}); } |
|
0
|
|
|
|
|
|
|
139
|
0
|
0
|
0
|
|
|
|
if (defined $opts{'-pdfname'} && !defined $opts{'pdfname'}) { $opts{'pdfname'} = delete($opts{'-pdfname'}); } |
|
0
|
|
|
|
|
|
|
140
|
0
|
0
|
0
|
|
|
|
if (defined $opts{'-name'} && !defined $opts{'name'}) { $opts{'name'} = delete($opts{'-name'}); } |
|
0
|
|
|
|
|
|
|
141
|
0
|
0
|
0
|
|
|
|
if (defined $opts{'-condense'} && !defined $opts{'condense'}) { $opts{'condense'} = delete($opts{'-condense'}); } |
|
0
|
|
|
|
|
|
|
142
|
0
|
0
|
0
|
|
|
|
if (defined $opts{'-slant'} && !defined $opts{'slant'}) { $opts{'slant'} = delete($opts{'-slant'}); } |
|
0
|
|
|
|
|
|
|
143
|
0
|
0
|
0
|
|
|
|
if (defined $opts{'-hscale'} && !defined $opts{'hscale'}) { $opts{'hscale'} = delete($opts{'-hscale'}); } |
|
0
|
|
|
|
|
|
|
144
|
0
|
0
|
0
|
|
|
|
if (defined $opts{'-oblique'} && !defined $opts{'oblique'}) { $opts{'oblique'} = delete($opts{'-oblique'}); } |
|
0
|
|
|
|
|
|
|
145
|
0
|
0
|
0
|
|
|
|
if (defined $opts{'-angle'} && !defined $opts{'angle'}) { $opts{'angle'} = delete($opts{'-angle'}); } |
|
0
|
|
|
|
|
|
|
146
|
0
|
0
|
0
|
|
|
|
if (defined $opts{'-bold'} && !defined $opts{'bold'}) { $opts{'bold'} = delete($opts{'-bold'}); } |
|
0
|
|
|
|
|
|
|
147
|
0
|
0
|
0
|
|
|
|
if (defined $opts{'-space'} && !defined $opts{'space'}) { $opts{'space'} = delete($opts{'-space'}); } |
|
0
|
|
|
|
|
|
|
148
|
0
|
0
|
0
|
|
|
|
if (defined $opts{'-caps'} && !defined $opts{'caps'}) { $opts{'caps'} = delete($opts{'-caps'}); } |
|
0
|
|
|
|
|
|
|
149
|
0
|
0
|
0
|
|
|
|
if (defined $opts{'-smallcaps'} && !defined $opts{'smallcaps'}) { $opts{'smallcaps'} = delete($opts{'-smallcaps'}); } |
|
0
|
|
|
|
|
|
|
150
|
|
|
|
|
|
|
|
151
|
0
|
|
|
|
|
|
my $entry = "synfont"; # synfont or synthetic_font |
152
|
0
|
0
|
|
|
|
|
if (defined $opts{'-entry_point'}) { $entry = $opts{'-entry_point'}; } |
|
0
|
|
|
|
|
|
|
153
|
|
|
|
|
|
|
|
154
|
|
|
|
|
|
|
# deal with simple aliases |
155
|
0
|
0
|
0
|
|
|
|
if (defined $opts{'slant'} && !defined $opts{'condense'}) { $opts{'condense'} = delete($opts{'slant'}); } |
|
0
|
|
|
|
|
|
|
156
|
0
|
0
|
0
|
|
|
|
if (defined $opts{'angle'} && !defined $opts{'oblique'}) { $opts{'oblique'} = delete($opts{'angle'}); } |
|
0
|
|
|
|
|
|
|
157
|
0
|
0
|
0
|
|
|
|
if (defined $opts{'smallcaps'} && !defined $opts{'caps'}) { $opts{'caps'} = delete($opts{'smallcaps'}); } |
|
0
|
|
|
|
|
|
|
158
|
0
|
0
|
0
|
|
|
|
if (defined $opts{'name'} && !defined $opts{'pdfname'}) { $opts{'pdfname'} = delete($opts{'name'}); } |
|
0
|
|
|
|
|
|
|
159
|
|
|
|
|
|
|
# deal with semi-aliases |
160
|
0
|
0
|
0
|
|
|
|
if (defined $opts{'hscale'} && !defined $opts{'condense'}) { $opts{'condense'} = delete($opts{'hscale'})/100; } |
|
0
|
|
|
|
|
|
|
161
|
|
|
|
|
|
|
# deal with entry point differences |
162
|
0
|
0
|
0
|
|
|
|
if (defined $opts{'bold'} && $entry eq 'synthetic_font') { $opts{'bold'} /= 10; } |
|
0
|
|
|
|
|
|
|
163
|
|
|
|
|
|
|
|
164
|
0
|
|
|
|
|
|
my ($self); |
165
|
0
|
|
|
|
|
|
my $first = 1; |
166
|
0
|
|
|
|
|
|
my $last = 255; |
167
|
0
|
|
0
|
|
|
|
my $cond = $opts{'condense'} || 1; |
168
|
0
|
|
0
|
|
|
|
my $oblique = $opts{'oblique'} || 0; |
169
|
0
|
|
0
|
|
|
|
my $space = $opts{'space'} || '0'; |
170
|
0
|
|
0
|
|
|
|
my $bold = ($opts{'bold'} || 0)*10; # convert to em |
171
|
|
|
|
|
|
|
# caps |
172
|
|
|
|
|
|
|
|
173
|
|
|
|
|
|
|
# 5 elements apparently not used anywhere |
174
|
|
|
|
|
|
|
#$self->{' cond'} = $cond; |
175
|
|
|
|
|
|
|
#$self->{' oblique'} = $oblique; |
176
|
|
|
|
|
|
|
#$self->{' bold'} = $bold; |
177
|
|
|
|
|
|
|
#$self->{' boldmove'} = 0.001; |
178
|
|
|
|
|
|
|
#$self->{' space'} = $space; |
179
|
|
|
|
|
|
|
# only available in TT fonts. besides, multibyte encodings not supported |
180
|
0
|
0
|
|
|
|
|
if (defined $opts{'encode'}) { |
181
|
0
|
0
|
|
|
|
|
if ($opts{'encode'} =~ m/^utf/i) { |
182
|
0
|
|
|
|
|
|
die "Invalid multibyte encoding for synfont: $opts{'encode'}\n"; |
183
|
|
|
|
|
|
|
# TBD probably more multibyte encodings to check |
184
|
|
|
|
|
|
|
} |
185
|
0
|
|
|
|
|
|
$font->encodeByName($opts{'encode'}); |
186
|
|
|
|
|
|
|
} |
187
|
|
|
|
|
|
|
|
188
|
0
|
0
|
|
|
|
|
$class = ref $class if ref $class; |
189
|
|
|
|
|
|
|
$self = $class->SUPER::new($pdf, |
190
|
|
|
|
|
|
|
# pdfkey() |
191
|
|
|
|
|
|
|
# .('+' . $font->name()) |
192
|
|
|
|
|
|
|
# .($opts{'caps'} ? '+Caps' : '') |
193
|
|
|
|
|
|
|
# .($opts{'pdfname'} ? '+'.$opts{'pdfname'} : '') |
194
|
0
|
0
|
|
|
|
|
$opts{'pdfname'}? $opts{'pdfname'}: 'Syn' . $font->name() . pdfkey() |
195
|
|
|
|
|
|
|
); |
196
|
0
|
0
|
|
|
|
|
$pdf->new_obj($self) unless $self->is_obj($pdf); |
197
|
0
|
|
|
|
|
|
$self->{' font'} = $font; |
198
|
0
|
|
0
|
|
|
|
$self->{' data'} = { |
199
|
|
|
|
|
|
|
'type' => 'Type3', |
200
|
|
|
|
|
|
|
'ascender' => $font->ascender(), |
201
|
|
|
|
|
|
|
'capheight' => $font->capheight(), |
202
|
|
|
|
|
|
|
'descender' => $font->descender(), |
203
|
|
|
|
|
|
|
'iscore' => '0', |
204
|
|
|
|
|
|
|
'isfixedpitch' => $font->isfixedpitch(), |
205
|
|
|
|
|
|
|
'italicangle' => $font->italicangle() + $oblique, |
206
|
|
|
|
|
|
|
'missingwidth' => ($font->missingwidth()||300) * $cond, |
207
|
|
|
|
|
|
|
'underlineposition' => $font->underlineposition(), |
208
|
|
|
|
|
|
|
'underlinethickness' => $font->underlinethickness(), |
209
|
|
|
|
|
|
|
'xheight' => $font->xheight(), |
210
|
|
|
|
|
|
|
'firstchar' => $first, |
211
|
|
|
|
|
|
|
'lastchar' => $last, |
212
|
|
|
|
|
|
|
'char' => [ '.notdef' ], |
213
|
|
|
|
|
|
|
'uni' => [ 0 ], |
214
|
|
|
|
|
|
|
'u2e' => { 0 => 0 }, |
215
|
|
|
|
|
|
|
'fontbbox' => '', |
216
|
|
|
|
|
|
|
'wx' => { 'space' => '600' }, |
217
|
|
|
|
|
|
|
}; |
218
|
|
|
|
|
|
|
|
219
|
0
|
|
|
|
|
|
my $data = $self->data(); |
220
|
0
|
0
|
|
|
|
|
if (ref($font->fontbbox())) { |
221
|
0
|
|
|
|
|
|
$data->{'fontbbox'} = [ @{$font->fontbbox()} ]; |
|
0
|
|
|
|
|
|
|
222
|
|
|
|
|
|
|
} else { |
223
|
0
|
|
|
|
|
|
$data->{'fontbbox'} = [ $font->fontbbox() ]; |
224
|
|
|
|
|
|
|
} |
225
|
0
|
|
|
|
|
|
$data->{'fontbbox'}->[0] *= $cond; |
226
|
0
|
|
|
|
|
|
$data->{'fontbbox'}->[2] *= $cond; |
227
|
|
|
|
|
|
|
|
228
|
0
|
|
|
|
|
|
$self->{'Subtype'} = PDFName('Type3'); |
229
|
0
|
|
|
|
|
|
$self->{'FirstChar'} = PDFNum($first); |
230
|
0
|
|
|
|
|
|
$self->{'LastChar'} = PDFNum($last); |
231
|
0
|
|
|
|
|
|
$self->{'FontMatrix'} = PDFArray(map { PDFNum($_) } (0.001, 0, 0, 0.001, 0, 0)); |
|
0
|
|
|
|
|
|
|
232
|
0
|
|
|
|
|
|
$self->{'FontBBox'} = PDFArray(map { PDFNum($_) } $self->fontbbox()); |
|
0
|
|
|
|
|
|
|
233
|
|
|
|
|
|
|
|
234
|
0
|
|
|
|
|
|
my $procs = PDFDict(); |
235
|
0
|
|
|
|
|
|
$pdf->new_obj($procs); |
236
|
0
|
|
|
|
|
|
$self->{'CharProcs'} = $procs; |
237
|
|
|
|
|
|
|
|
238
|
0
|
|
|
|
|
|
$self->{'Resources'} = PDFDict(); |
239
|
0
|
|
|
|
|
|
$self->{'Resources'}->{'ProcSet'} = PDFArray(map { PDFName($_) } |
|
0
|
|
|
|
|
|
|
240
|
|
|
|
|
|
|
qw(PDF Text ImageB ImageC ImageI)); |
241
|
0
|
|
|
|
|
|
my $xo = PDFDict(); |
242
|
0
|
|
|
|
|
|
$self->{'Resources'}->{'Font'} = $xo; |
243
|
0
|
|
|
|
|
|
$self->{'Resources'}->{'Font'}->{'FSN'} = $font; |
244
|
0
|
|
|
|
|
|
foreach my $w ($first .. $last) { |
245
|
0
|
|
|
|
|
|
$data->{'char'}->[$w] = $font->glyphByEnc($w); |
246
|
|
|
|
|
|
|
# possible non-standard name... use $w as Unicode value |
247
|
0
|
|
0
|
|
|
|
$data->{'uni'}->[$w] = (uniByName($data->{'char'}->[$w])) || $w; |
248
|
0
|
0
|
|
|
|
|
if (defined $data->{'uni'}->[$w]) { |
249
|
0
|
|
|
|
|
|
$data->{'u2e'}->{$data->{'uni'}->[$w]} = $w; |
250
|
|
|
|
|
|
|
} |
251
|
|
|
|
|
|
|
} |
252
|
|
|
|
|
|
|
|
253
|
0
|
0
|
|
|
|
|
if ($font->isa('PDF::Builder::Resource::CIDFont')) { |
254
|
0
|
|
|
|
|
|
$self->{'Encoding'} = PDFDict(); |
255
|
0
|
|
|
|
|
|
$self->{'Encoding'}->{'Type'} = PDFName('Encoding'); |
256
|
0
|
|
|
|
|
|
$self->{'Encoding'}->{'Differences'} = PDFArray(); |
257
|
0
|
|
|
|
|
|
foreach my $w ($first .. $last) { |
258
|
0
|
|
|
|
|
|
my $char = $data->{'char'}->[$w]; |
259
|
0
|
0
|
0
|
|
|
|
if (defined $char && $char ne '.notdef') { |
260
|
0
|
|
|
|
|
|
$self->{'Encoding'}->{'Differences'}->add_elements(PDFNum($w), |
261
|
|
|
|
|
|
|
PDFName($char)); |
262
|
|
|
|
|
|
|
} |
263
|
|
|
|
|
|
|
} |
264
|
|
|
|
|
|
|
} else { |
265
|
0
|
|
|
|
|
|
$self->{'Encoding'} = $font->{'Encoding'}; |
266
|
|
|
|
|
|
|
} |
267
|
|
|
|
|
|
|
|
268
|
0
|
|
|
|
|
|
my @widths; |
269
|
0
|
|
|
|
|
|
foreach my $w ($first .. $last) { |
270
|
|
|
|
|
|
|
# $w is the "standard encoding" (similar to Windows-1252) PDF |
271
|
|
|
|
|
|
|
# single byte encoding. first 32 .notdef, 255 = U+00FF ydieresis |
272
|
0
|
0
|
|
|
|
|
if ($data->{'char'}->[$w] eq '.notdef') { |
273
|
0
|
|
|
|
|
|
push @widths, $self->missingwidth(); |
274
|
0
|
|
|
|
|
|
next; |
275
|
|
|
|
|
|
|
} |
276
|
0
|
|
|
|
|
|
my $char = PDFDict(); |
277
|
|
|
|
|
|
|
|
278
|
|
|
|
|
|
|
#my $wth = int($font->width(chr($w)) * 1000 * $cond + 2 * $space); |
279
|
0
|
|
|
|
|
|
my $uni = $data->{'uni'}->[$w]; |
280
|
0
|
|
|
|
|
|
my $wth = int($font->width(chr($uni)) * 1000 * $cond + 2*$space); |
281
|
|
|
|
|
|
|
|
282
|
0
|
|
|
|
|
|
$procs->{$font->glyphByEnc($w)} = $char; |
283
|
|
|
|
|
|
|
#$char->{'Filter'} = PDFArray(PDFName('FlateDecode')); |
284
|
0
|
|
|
|
|
|
$char->{' stream'} = $wth." 0 ".join(' ',map { int($_) } $self->fontbbox())." d1\n"; |
|
0
|
|
|
|
|
|
|
285
|
0
|
|
|
|
|
|
$char->{' stream'} .= "BT\n"; |
286
|
0
|
0
|
|
|
|
|
$char->{' stream'} .= join(' ', (1, 0, tan(deg2rad($oblique)), 1, 0, 0))." Tm\n" if $oblique; |
287
|
0
|
0
|
|
|
|
|
$char->{' stream'} .= "2 Tr $bold w\n" if $bold; |
288
|
|
|
|
|
|
|
#my $ci = charinfo($data->{'uni'}->[$w]); |
289
|
0
|
|
|
|
|
|
my $ci = {}; |
290
|
0
|
0
|
|
|
|
|
if ($data->{'uni'}->[$w] ne '') { |
291
|
0
|
|
|
|
|
|
$ci = charinfo($data->{'uni'}->[$w]); |
292
|
|
|
|
|
|
|
} |
293
|
|
|
|
|
|
|
|
294
|
|
|
|
|
|
|
# Small Caps |
295
|
|
|
|
|
|
|
# |
296
|
|
|
|
|
|
|
# Most Unicode characters simply don't appear in the synthetic |
297
|
|
|
|
|
|
|
# font, which is limited to 255 "standard" encoding points. encode |
298
|
|
|
|
|
|
|
# still will be single byte. |
299
|
|
|
|
|
|
|
# |
300
|
|
|
|
|
|
|
# SynFont seems to have trouble with some accented characters, even |
301
|
|
|
|
|
|
|
# though 'upper' is correct and they are in the standard encoding, |
302
|
|
|
|
|
|
|
# particularly if the string is decoded to UTF-8. Keep in mind that |
303
|
|
|
|
|
|
|
# synfont() only creates a 255 character "standard" encoding font, so |
304
|
|
|
|
|
|
|
# you need to apply it to each "plane" of the original font. |
305
|
|
|
|
|
|
|
# |
306
|
|
|
|
|
|
|
# Some single characters (eszett within the standard encoding, long s |
307
|
|
|
|
|
|
|
# outside it) don't have 'upper' defined and are left as-is (or |
308
|
|
|
|
|
|
|
# skipped entirely, if outside the encoding) unless first replaced by |
309
|
|
|
|
|
|
|
# ASCII lowercase ('ss' and 's' respectively). While we're at it, |
310
|
|
|
|
|
|
|
# replace certain Unicode ligatures with ASCII equivalents so they |
311
|
|
|
|
|
|
|
# will be small-capped correctly instead of ignored. Don't forget to |
312
|
|
|
|
|
|
|
# set proper width for multi-letter replacements. |
313
|
|
|
|
|
|
|
# |
314
|
0
|
|
|
|
|
|
my $hasUpper = 0; # if no small caps, still need to output something |
315
|
0
|
0
|
|
|
|
|
if ($opts{'caps'}) { |
316
|
|
|
|
|
|
|
# not all characters have an 'upper' equivalent code point. Some |
317
|
|
|
|
|
|
|
# have U+0000 (dummy entry). |
318
|
0
|
|
|
|
|
|
my $ch; |
319
|
0
|
|
|
|
|
|
my $multiChar = 0; |
320
|
0
|
0
|
0
|
|
|
|
$hasUpper = 1 if defined $ci->{'upper'} && $ci->{'upper'}; |
321
|
|
|
|
|
|
|
|
322
|
0
|
0
|
|
|
|
|
if ($hasUpper) { |
323
|
|
|
|
|
|
|
# standard upper case character and width spec'd by font |
324
|
0
|
|
|
|
|
|
$ch = $self->encByUni(hex($ci->{'upper'})); |
325
|
0
|
|
|
|
|
|
$wth = int($font->width(chr($ch)) * 800 * $cond * 1.1 + 2* $space); |
326
|
|
|
|
|
|
|
} |
327
|
|
|
|
|
|
|
# let's handle some special cases where !$hasUpper |
328
|
|
|
|
|
|
|
# ($hasUpper set to 1) |
329
|
|
|
|
|
|
|
# only characters to be substituted here, unless there is something |
330
|
|
|
|
|
|
|
# in other encodings to deal with |
331
|
|
|
|
|
|
|
# TBD it does not seem to be possible on non-base planes (plane 1+) |
332
|
|
|
|
|
|
|
# to access ASCII letters to build a substitute for ligatures |
333
|
|
|
|
|
|
|
# (e.g., replace U+FB01 fi ligature with F+I) |
334
|
0
|
0
|
|
|
|
|
if ($uni == 0xDF) { # eszett (German sharp s) |
|
|
0
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
335
|
0
|
|
|
|
|
|
$hasUpper = 1; |
336
|
0
|
|
|
|
|
|
$multiChar = 1; |
337
|
|
|
|
|
|
|
# actually, some fonts have a U+1E9E uppercase Eszett, but |
338
|
|
|
|
|
|
|
# since that won't be in any single byte encoding, we use SS |
339
|
0
|
|
|
|
|
|
$wth = 2*(int($font->width('S') * 800 * $cond*1.1 + 2*$space)); |
340
|
0
|
|
|
|
|
|
$ch = $font->text('S').$font->text('S'); |
341
|
|
|
|
|
|
|
} elsif ($uni == 0x0131) { # dotless i |
342
|
|
|
|
|
|
|
# standard encoding doesn't see Unicode point |
343
|
0
|
|
|
|
|
|
$hasUpper = 1; |
344
|
0
|
|
|
|
|
|
$multiChar = 1; |
345
|
0
|
|
|
|
|
|
$wth = int($font->width('I') * 800 * $cond*1.1 + 2*$space); |
346
|
0
|
|
|
|
|
|
$ch = $font->text('I'); |
347
|
|
|
|
|
|
|
} elsif ($uni == 0x0237) { # dotless j |
348
|
|
|
|
|
|
|
# standard encoding doesn't see Unicode point |
349
|
0
|
|
|
|
|
|
$hasUpper = 1; |
350
|
0
|
|
|
|
|
|
$multiChar = 1; |
351
|
0
|
|
|
|
|
|
$wth = int($font->width('J') * 800 * $cond*1.1 + 2*$space); |
352
|
0
|
|
|
|
|
|
$ch = $font->text('J'); |
353
|
|
|
|
|
|
|
} |
354
|
|
|
|
|
|
|
|
355
|
0
|
0
|
|
|
|
|
if ($hasUpper) { |
356
|
|
|
|
|
|
|
# this is a lowercase letter, etc. that has an uppercase version |
357
|
|
|
|
|
|
|
# 80% height x 88% (110% aspect ratio @ 80% font size) width. |
358
|
|
|
|
|
|
|
# slightly wider to thicken stems and make look better. |
359
|
|
|
|
|
|
|
# $ch and $wth already set, either default or special case |
360
|
0
|
|
|
|
|
|
$char->{' stream'} .= "/FSN 800 Tf\n"; |
361
|
0
|
|
|
|
|
|
$char->{' stream'} .= ($cond * 110)." Tz\n"; |
362
|
0
|
0
|
|
|
|
|
$char->{' stream'} .= " [ -$space ] TJ\n" if $space; |
363
|
0
|
0
|
|
|
|
|
if ($multiChar) { |
364
|
0
|
|
|
|
|
|
$ch =~ s/>/g; |
365
|
0
|
|
|
|
|
|
$ch =~ s/\)\(//g; |
366
|
0
|
|
|
|
|
|
$char->{' stream'} .= "$ch"; |
367
|
|
|
|
|
|
|
} else { |
368
|
0
|
|
|
|
|
|
$char->{' stream'} .= $font->text(chr($ch)); |
369
|
|
|
|
|
|
|
} |
370
|
|
|
|
|
|
|
# uc chr($uni) supposed to be always equivalent to |
371
|
|
|
|
|
|
|
# chr hex($ci->{'upper'}), according to "futuramedium" |
372
|
|
|
|
|
|
|
# HOWEVER, uc doesn't seem to know what to do with non-ASCII chars |
373
|
|
|
|
|
|
|
#$wth = int($font->width(uc chr($uni)) * 800 * $cond * 1.1 + 2* $space); |
374
|
|
|
|
|
|
|
#$char->{' stream'} .= $font->text(uc chr($uni)); |
375
|
|
|
|
|
|
|
#$wth = int($font->width(chr(hex($ci->{'upper'}))) * 800 * $cond * 1.1 + 2* $space); |
376
|
|
|
|
|
|
|
#$char->{' stream'} .= $font->text(chr(hex($ci->{'upper'}))); |
377
|
|
|
|
|
|
|
} # else fall through to standard handling below |
378
|
|
|
|
|
|
|
} # small caps requested |
379
|
|
|
|
|
|
|
|
380
|
0
|
0
|
|
|
|
|
if (!$hasUpper) { |
381
|
|
|
|
|
|
|
# Applies to all not small-caps too! |
382
|
|
|
|
|
|
|
# does not have an uppercase ('upper') equivalent, so |
383
|
|
|
|
|
|
|
# output at standard height and aspect ratio |
384
|
0
|
|
|
|
|
|
$char->{' stream'} .= "/FSN 1000 Tf\n"; |
385
|
0
|
0
|
|
|
|
|
$char->{' stream'} .= ($cond * 100)." Tz\n" if $cond != 1; |
386
|
0
|
0
|
|
|
|
|
$char->{' stream'} .= " [ -$space ] TJ\n" if $space; |
387
|
|
|
|
|
|
|
#$char->{' stream'} .= $font->text(chr($w)); |
388
|
0
|
|
|
|
|
|
$char->{' stream'} .= $font->text(chr($uni)); |
389
|
|
|
|
|
|
|
} |
390
|
|
|
|
|
|
|
|
391
|
|
|
|
|
|
|
# finale... all modifications to font have been done |
392
|
0
|
|
|
|
|
|
$char->{' stream'} .= " Tj\nET "; |
393
|
0
|
|
|
|
|
|
push @widths, $wth; |
394
|
0
|
|
|
|
|
|
$data->{'wx'}->{$font->glyphByEnc($w)} = $wth; |
395
|
0
|
|
|
|
|
|
$pdf->new_obj($char); |
396
|
|
|
|
|
|
|
} # loop through 255 standard encoding points |
397
|
|
|
|
|
|
|
|
398
|
|
|
|
|
|
|
# the array as 0 elements at this point! 'space' (among others) IS defined, |
399
|
|
|
|
|
|
|
# so copy that, but TBD what kind of fallback if no such element exists? |
400
|
|
|
|
|
|
|
# $procs->{'.notdef'} = $procs->{$font->data()->{'char'}->[32]}; |
401
|
0
|
|
|
|
|
|
$procs->{'.notdef'} = $procs->{'space'}; |
402
|
|
|
|
|
|
|
|
403
|
0
|
|
|
|
|
|
$self->{'Widths'} = PDFArray(map { PDFNum($_) } @widths); |
|
0
|
|
|
|
|
|
|
404
|
0
|
|
|
|
|
|
$data->{'e2n'} = $data->{'char'}; |
405
|
0
|
|
|
|
|
|
$data->{'e2u'} = $data->{'uni'}; |
406
|
|
|
|
|
|
|
|
407
|
0
|
|
|
|
|
|
$data->{'u2c'} = {}; |
408
|
0
|
|
|
|
|
|
$data->{'u2e'} = {}; |
409
|
0
|
|
|
|
|
|
$data->{'u2n'} = {}; |
410
|
0
|
|
|
|
|
|
$data->{'n2c'} = {}; |
411
|
0
|
|
|
|
|
|
$data->{'n2e'} = {}; |
412
|
0
|
|
|
|
|
|
$data->{'n2u'} = {}; |
413
|
|
|
|
|
|
|
|
414
|
0
|
|
|
|
|
|
foreach my $n (reverse 0 .. 255) { |
415
|
0
|
|
0
|
|
|
|
$data->{'n2c'}->{$data->{'char'}->[$n] // '.notdef'} //= $n; |
|
|
|
0
|
|
|
|
|
416
|
0
|
|
0
|
|
|
|
$data->{'n2e'}->{$data->{'e2n'}->[$n] // '.notdef'} //= $n; |
|
|
|
0
|
|
|
|
|
417
|
|
|
|
|
|
|
|
418
|
0
|
|
0
|
|
|
|
$data->{'n2u'}->{$data->{'e2n'}->[$n] // '.notdef'} //= $data->{'e2u'}->[$n]; |
|
|
|
0
|
|
|
|
|
419
|
0
|
|
0
|
|
|
|
$data->{'n2u'}->{$data->{'char'}->[$n] // '.notdef'} //= $data->{'uni'}->[$n]; |
|
|
|
0
|
|
|
|
|
420
|
|
|
|
|
|
|
|
421
|
0
|
0
|
|
|
|
|
if (defined $data->{'uni'}->[$n]) { |
422
|
0
|
|
0
|
|
|
|
$data->{'u2c'}->{$data->{'uni'}->[$n]} //= $n |
423
|
|
|
|
|
|
|
} |
424
|
0
|
0
|
|
|
|
|
if (defined $data->{'e2u'}->[$n]) { |
425
|
0
|
|
0
|
|
|
|
$data->{'u2e'}->{$data->{'e2u'}->[$n]} //= $n; |
426
|
0
|
|
0
|
|
|
|
my $value = $data->{'e2n'}->[$n] // '.notdef'; |
427
|
0
|
|
0
|
|
|
|
$data->{'u2n'}->{$data->{'e2u'}->[$n]} //= $value; |
428
|
|
|
|
|
|
|
} |
429
|
0
|
0
|
|
|
|
|
if (defined $data->{'uni'}->[$n]) { |
430
|
0
|
|
0
|
|
|
|
my $value = $data->{'char'}->[$n] // '.notdef'; |
431
|
0
|
|
0
|
|
|
|
$data->{'u2n'}->{$data->{'uni'}->[$n]} //= $value; |
432
|
|
|
|
|
|
|
} |
433
|
|
|
|
|
|
|
} |
434
|
|
|
|
|
|
|
|
435
|
0
|
|
|
|
|
|
return $self; |
436
|
|
|
|
|
|
|
} |
437
|
|
|
|
|
|
|
|
438
|
|
|
|
|
|
|
1; |