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   915 use base 'PDF::Builder::Resource::BaseFont';
  8         17  
  8         3335  
4              
5 8     8   61 use strict;
  8         18  
  8         153  
6 8     8   35 use warnings;
  8         16  
  8         410  
7              
8             our $VERSION = '3.024'; # VERSION
9             our $LAST_UPDATE = '3.024'; # manually update whenever code is changed
10              
11 8     8   47 use Encode qw(:all);
  8         21  
  8         1929  
12              
13 8     8   54 use PDF::Builder::Util;
  8         19  
  8         908  
14 8     8   56 use PDF::Builder::Basic::PDF::Utils;
  8         17  
  8         10709  
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 109 my ($self, $encoding) = @_;
24              
25 52         112 my $data = $self->data();
26              
27 52 100       162 if ($self->issymbol()) {
28 5         11 $encoding = undef;
29             }
30              
31 52 100 100     284 if (defined $encoding && $encoding =~ m|^uni(\d+)$|o) {
    100          
    50          
32 1         7 my $blk = $1;
33 1         5 $data->{'e2u'} = [ map { $blk*256+$_ } (0..255) ];
  256         300  
34 1 50       7 $data->{'e2n'} = [ map { nameByUni($_) || '.notdef' } @{$data->{'e2u'}} ];
  256         329  
  1         4  
35 1         9 $data->{'firstchar'} = 0;
36             } elsif (defined $encoding) {
37 46         509 $data->{'e2u'} = [ unpack('U*', decode($encoding, pack('C*', (0..255)))) ];
38 46 50       6240 $data->{'e2n'} = [ map { nameByUni($_) || '.notdef' } @{$data->{'e2u'}} ];
  11776         15568  
  46         142  
39             } elsif (defined $data->{'uni'}) {
40 5         10 $data->{'e2u'} = [ @{$data->{'uni'}} ];
  5         84  
41 5 50       9 $data->{'e2n'} = [ map { $_ || '.notdef' } @{$data->{'char'}} ];
  1280         1856  
  5         15  
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         425 $data->{'u2c'} = {};
48 52         112 $data->{'u2e'} = {};
49 52         92 $data->{'u2n'} = {};
50 52         99 $data->{'n2c'} = {};
51 52         97 $data->{'n2e'} = {};
52 52         113 $data->{'n2u'} = {};
53              
54 52         158 foreach my $n (0..255) {
55 13312         14361 my $xchar = undef;
56 13312         13013 my $xuni = undef;
57 13312   50     19174 $xchar = $data->{'char'}->[$n] // '.notdef';
58 13312   100     34749 $data->{'n2c'}->{$xchar} //= $n;
59              
60 13312   50     19587 $xchar = $data->{'e2n'}->[$n] // '.notdef';
61 13312   100     34594 $data->{'n2e'}->{$xchar} //= $n;
62              
63 13312   100     34985 $data->{'n2u'}->{$xchar} //= $data->{'e2u'}->[$n];
64              
65 13312   50     18944 $xchar = $data->{'char'}->[$n] // '.notdef';
66 13312   100     20424 $xuni = $data->{'uni'}->[$n] // 0;
67 13312   100     19761 $data->{'n2u'}->{$xchar} //= $xuni;
68              
69 13312   100     35420 $data->{'u2c'}->{$xuni} //= $n;
70              
71 13312   100     18641 $xuni = $data->{'e2u'}->[$n] // 0;
72 13312   100     36787 $data->{'u2e'}->{$xuni} //= $n;
73              
74 13312   50     19124 $xchar = $data->{'e2n'}->[$n] // '.notdef';
75 13312   66     37068 $data->{'u2n'}->{$xuni} //= $xchar;
76              
77 13312   50     19587 $xchar = $data->{'char'}->[$n] // '.notdef';
78 13312   100     20000 $xuni = $data->{'uni'}->[$n] //= 0;
79 13312   66     22161 $data->{'u2n'}->{$xuni} //= $xchar;
80             }
81              
82 52         254 my $en = PDFDict();
83 52         114 $self->{'Encoding'} = $en;
84              
85 52         128 $en->{'Type'} = PDFName('Encoding');
86 52         135 $en->{'BaseEncoding'} = PDFName('WinAnsiEncoding');
87              
88 52         206 $en->{'Differences'} = PDFArray(PDFNum(0));
89 52         159 foreach my $n (0..255) {
90 13312   50     20102 my $element = $self->glyphByEnc($n) || '.notdef';
91 13312         21218 $en->{'Differences'}->add_elements(PDFName($element));
92             }
93              
94 52         172 $self->{'FirstChar'} = PDFNum($data->{'firstchar'});
95 52         141 $self->{'LastChar'} = PDFNum($data->{'lastchar'});
96              
97 52         135 $self->{'Widths'} = PDFArray();
98 52         313 foreach my $n ($data->{'firstchar'} .. $data->{'lastchar'}) {
99 11680         19253 $self->{'Widths'}->add_elements(PDFNum($self->wxByEnc($n)));
100             }
101              
102 52         147 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;