File Coverage

blib/lib/PDF/Builder/Resource/Font.pm
Criterion Covered Total %
statement 78 131 59.5
branch 10 22 45.4
condition 32 48 66.6
subroutine 7 9 77.7
pod 3 3 100.0
total 130 213 61.0


line stmt bran cond sub pod time code
1             package PDF::Builder::Resource::Font;
2              
3 8     8   1094 use base 'PDF::Builder::Resource::BaseFont';
  8         17  
  8         4730  
4              
5 8     8   59 use strict;
  8         19  
  8         202  
6 8     8   32 use warnings;
  8         32  
  8         605  
7              
8             our $VERSION = '3.028'; # VERSION
9             our $LAST_UPDATE = '3.027'; # manually update whenever code is changed
10              
11 8     8   48 use Encode qw(:all);
  8         15  
  8         2323  
12              
13 8     8   54 use PDF::Builder::Util;
  8         16  
  8         1216  
14 8     8   61 use PDF::Builder::Basic::PDF::Utils;
  8         14  
  8         14009  
15              
16             =head1 NAME
17              
18             PDF::Builder::Resource::Font - Some common support routines for font files
19              
20             Inherits from L<PDF::Builder::Resource::BaseFont>
21              
22             =head1 METHODS
23              
24             =head2 encodeByData
25              
26             $font->encodeByData($enc)
27              
28             =over
29              
30             (No Information)
31              
32             =back
33              
34             =cut
35              
36             sub encodeByData {
37 37     37 1 103 my ($self, $encoding) = @_;
38              
39 37         151 my $data = $self->data();
40              
41 37 100       212 if ($self->issymbol()) {
42 3         7 $encoding = undef;
43             }
44              
45 37 100 100     425 if (defined $encoding && $encoding =~ m|^uni(\d+)$|o) {
    100          
    50          
46 1         5 my $blk = $1;
47 1         7 $data->{'e2u'} = [ map { $blk*256+$_ } (0..255) ];
  256         304  
48 1 50       5 $data->{'e2n'} = [ map { nameByUni($_) || '.notdef' } @{$data->{'e2u'}} ];
  256         290  
  1         4  
49 1         11 $data->{'firstchar'} = 0;
50             } elsif (defined $encoding) {
51 33         784 $data->{'e2u'} = [ unpack('U*', decode($encoding, pack('C*', (0..255)))) ];
52 33 50       5860 $data->{'e2n'} = [ map { nameByUni($_) || '.notdef' } @{$data->{'e2u'}} ];
  8448         14665  
  33         165  
53             } elsif (defined $data->{'uni'}) {
54 3         6 $data->{'e2u'} = [ @{$data->{'uni'}} ];
  3         82  
55 3 50       7 $data->{'e2n'} = [ map { $_ || '.notdef' } @{$data->{'char'}} ];
  768         1605  
  3         16  
56             } else {
57 0         0 $data->{'e2u'} = [ map { uniByName($_) } @{$data->{'char'}} ];
  0         0  
  0         0  
58 0 0       0 $data->{'e2n'} = [ map { $_ || '.notdef' } @{$data->{'char'}} ];
  0         0  
  0         0  
59             }
60              
61 37         521 $data->{'u2c'} = {};
62 37         120 $data->{'u2e'} = {};
63 37         120 $data->{'u2n'} = {};
64 37         118 $data->{'n2c'} = {};
65 37         121 $data->{'n2e'} = {};
66 37         118 $data->{'n2u'} = {};
67              
68 37         185 foreach my $n (0..255) {
69 9472         13029 my $xchar = undef;
70 9472         12132 my $xuni = undef;
71 9472   50     19211 $xchar = $data->{'char'}->[$n] // '.notdef';
72 9472   66     38735 $data->{'n2c'}->{$xchar} //= $n;
73              
74 9472   50     19360 $xchar = $data->{'e2n'}->[$n] // '.notdef';
75 9472   66     34531 $data->{'n2e'}->{$xchar} //= $n;
76              
77 9472   100     33968 $data->{'n2u'}->{$xchar} //= $data->{'e2u'}->[$n];
78              
79 9472   50     22129 $xchar = $data->{'char'}->[$n] // '.notdef';
80 9472   100     20137 $xuni = $data->{'uni'}->[$n] // 0;
81 9472   66     19188 $data->{'n2u'}->{$xchar} //= $xuni;
82              
83 9472   66     34383 $data->{'u2c'}->{$xuni} //= $n;
84              
85 9472   100     18244 $xuni = $data->{'e2u'}->[$n] // 0;
86 9472   66     34806 $data->{'u2e'}->{$xuni} //= $n;
87              
88 9472   50     18554 $xchar = $data->{'e2n'}->[$n] // '.notdef';
89 9472   66     34511 $data->{'u2n'}->{$xuni} //= $xchar;
90              
91 9472   50     18276 $xchar = $data->{'char'}->[$n] // '.notdef';
92 9472   100     19404 $xuni = $data->{'uni'}->[$n] //= 0;
93 9472   66     22390 $data->{'u2n'}->{$xuni} //= $xchar;
94             }
95              
96 37         272 my $en = PDFDict();
97 37         153 $self->{'Encoding'} = $en;
98              
99 37         142 $en->{'Type'} = PDFName('Encoding');
100 37         121 $en->{'BaseEncoding'} = PDFName('WinAnsiEncoding');
101              
102 37         152 $en->{'Differences'} = PDFArray(PDFNum(0));
103 37         402 foreach my $n (0..255) {
104 9472   50     18660 my $element = $self->glyphByEnc($n) || '.notdef';
105 9472         22955 $en->{'Differences'}->add_elements(PDFName($element));
106             }
107              
108 37         220 $self->{'FirstChar'} = PDFNum($data->{'firstchar'});
109 37         133 $self->{'LastChar'} = PDFNum($data->{'lastchar'});
110              
111 37         138 $self->{'Widths'} = PDFArray();
112 37         360 foreach my $n ($data->{'firstchar'} .. $data->{'lastchar'}) {
113 8320         20033 $self->{'Widths'}->add_elements(PDFNum($self->wxByEnc($n)));
114             }
115              
116 37         254 return $self;
117             }
118              
119             =head2 automap
120              
121             $font->automap()
122              
123             =over
124              
125             This applies to core fonts (C<< $pdf->corefont() >>) and PostScript fonts
126             (C<< $pdf->psfont() >>). These cannot use UTF-8 (or other multibyte character)
127             encoded text; only single byte characters. This limits a font to a maximum of
128             256 glyphs (the "standard" single-byte encoding being used). Any other glyphs
129             supplied with the font are inaccessible.
130              
131             C<automap> splits a font containing more than 256 glyphs into "planes" of single
132             byte fonts of up to 256 glyphs, so that all glyphs may be accessed in separate
133             "fonts". An array of new fonts will be returned, with [0] being the standard
134             code page (of the selected encoding). If there are any glyphs beyond xFF on the
135             standard encoding page, they will be returned in one or more additional fonts
136             of 223 glyphs each. I<Why 223?> The first 32 are reserved as control characters
137             (although they have no glyphs), and number x20 is a space. This, plus 223,
138             gives 256 in total (the last plane may have fewer than 223 glyphs). These
139             "fonts" are temporary (dynamic), though as usable as any other font.
140              
141             Note that a plane may be B<empty> (only I<space> at x20 and possibly an unusable
142             character at x21) if the previous plane was full. You might want to check if
143             any character in the plane has a Unicode value (if not, it's empty).
144              
145             The I<ordering> of these 223 glyphs in each following plane does I<not> appear
146             to follow any particular official scheme, so be sure to reference something like
147             C<examples/020_corefonts> to see what is available, and what code point a glyph
148             is at (e.g., an 'A' in the text stream will print something different if you're
149             not on plane 0). For a given font B<file>, they should be I<consistent>. For
150             instance, in Times-Roman core font, an \x21 or ! in plane[1] should always give
151             an A+macron. Further note that new editions of font files released in the future
152             may have changes to the glyph list and the ordering (affecting which plane a
153             glyph appears on), so use automap() with caution. It appears that glyphs are
154             sorted by Unicode number, but if a new glyph is inserted, it would bump other
155             glyphs to new positions, and even to the next plane.
156              
157             An example:
158              
159             $fnt = $pdf->corefont('Times-Roman', 'encode' => 'latin1');
160             @planes = ($fnt, $fnt->automap()); # two planes
161             $text->font($planes[0], 15); # or just $fnt will work
162             $text->text('!'); # prints !
163             $text->font($planes[1], 15);
164             $text->text('!'); # prints A+macron
165              
166             If you had used 'latin2' encoding, an \x21 on plane 1 will give an inverted !
167             (&iexcl; HTML entity).
168              
169             Note that C<< $planes[$n]->fontname() >> should always be the desired base
170             font (e.g., I<Times-Roman>), while C<< $planes[$n]->name() >> will be the font
171             ID (e.g., I<TiRoCBC>) for plane 0, while for other planes there will be a
172             unique suffix added (e.g., I<TiRoCBCam0>).
173              
174             If you have just an occasional non-plane 0 character (or run of characters),
175             it may be tolerable to switch back and forth between planes like this, just as
176             typing an HTML entity once in a while when you need a Greek letter on a web page
177             is acceptable to most people. However, if you're typing a lot of Greek text, a
178             dedicated keyboard may be better for you. Like that, switching to a TTF font in
179             order to be able to use UTF-8 may be easier.
180              
181             =back
182              
183             =cut
184              
185             sub automap {
186 0     0 1   my ($self) = @_;
187 0           my $data = $self->data();
188              
189 0           my %gl = map { $_ => defineName($_) } keys %{$data->{'wx'}};
  0            
  0            
190              
191 0           foreach my $n (0..255) {
192 0           delete $gl{$data->{'e2n'}->[$n]};
193             }
194              
195 0 0 0       if (defined $data->{'comps'} && !$self->{'-nocomps'}) {
196 0           foreach my $n (keys %{$data->{'comps'}}) {
  0            
197 0           delete $gl{$n};
198             }
199             }
200              
201 0           my @nm = sort { $gl{$a} <=> $gl{$b} } keys %gl;
  0            
202              
203 0           my @fonts = ();
204 0           my $count = 0;
205 0           while (my @glyphs = splice(@nm, 0, 223)) {
206 0           my $obj = $self->SUPER::new($self->{' apipdf'},
207             $self->name() . 'am' . $count);
208 0           $obj->{' data'} = { %$data };
209 0           $obj->data()->{'firstchar'} = 32;
210 0           $obj->data()->{'lastchar'} = 32 + scalar(@glyphs);
211 0           push(@fonts, $obj);
212 0           foreach my $key (qw( Subtype BaseFont FontDescriptor )) {
213 0 0         $obj->{$key} = $self->{$key} if defined $self->{$key};
214             }
215 0           $obj->data()->{'char'} = [];
216 0           $obj->data()->{'uni'} = [];
217 0           foreach my $n (0..31) {
218 0           $obj->data()->{'char'}->[$n] = '.notdef';
219 0           $obj->data()->{'uni'}->[$n] = 0;
220             }
221 0           $obj->data()->{'char'}->[32] = 'space';
222 0           $obj->data()->{'uni'}->[32] = 32;
223 0           foreach my $n (33 .. $obj->data()->{'lastchar'}) {
224 0           $obj->data()->{'char'}->[$n] = $glyphs[$n-33];
225 0           $obj->data()->{'uni'}->[$n] = $gl{$glyphs[$n-33]};
226             }
227 0           foreach my $n (($obj->data()->{'lastchar'}+1) .. 255) {
228 0           $obj->data()->{'char'}->[$n] = '.notdef';
229 0           $obj->data()->{'uni'}->[$n] = 0;
230             }
231 0           $obj->encodeByData(undef);
232              
233 0           $count++;
234             }
235              
236 0           return @fonts;
237             }
238              
239             =head2 remap
240              
241             $font->remap($enc)
242              
243             =over
244              
245             (No Information)
246              
247             =back
248              
249             =cut
250              
251             sub remap {
252 0     0 1   my ($self, $enc) = @_;
253              
254 0           my $obj = $self->SUPER::new($self->{' apipdf'},
255             $self->name() . 'rm' . pdfkey());
256 0           $obj->{' data'}={ %{$self->data()} };
  0            
257 0           foreach my $key (qw( Subtype BaseFont FontDescriptor )) {
258 0 0         $obj->{$key} = $self->{$key} if defined $self->{$key};
259             }
260              
261 0           $obj->encodeByData($enc);
262              
263 0           return $obj;
264             }
265              
266             1;