File Coverage

blib/lib/PDF/Builder/Resource/Font.pm
Criterion Covered Total %
statement 78 131 59.5
branch 10 22 45.4
condition 37 48 77.0
subroutine 7 9 77.7
pod 1 3 33.3
total 133 213 62.4


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