File Coverage

blib/lib/PDF/Builder/Resource/Font/CoreFont.pm
Criterion Covered Total %
statement 92 126 73.0
branch 39 68 57.3
condition 12 25 48.0
subroutine 15 16 93.7
pod 3 4 75.0
total 161 239 67.3


line stmt bran cond sub pod time code
1             package PDF::Builder::Resource::Font::CoreFont;
2              
3 8     8   1696 use base 'PDF::Builder::Resource::Font';
  8         17  
  8         4594  
4              
5 8     8   92 use strict;
  8         17  
  8         205  
6 8     8   61 use warnings;
  8         18  
  8         634  
7              
8             our $VERSION = '3.028'; # VERSION
9             our $LAST_UPDATE = '3.028'; # manually update whenever code is changed
10              
11 8     8   49 use File::Basename;
  8         18  
  8         694  
12 8     8   45 use List::Util qw(any);
  8         16  
  8         532  
13 8     8   43 use PDF::Builder::Util;
  8         15  
  8         1138  
14 8     8   47 use PDF::Builder::Basic::PDF::Utils;
  8         21  
  8         21891  
15              
16             our $fonts;
17             our $alias;
18             our $subs;
19              
20             =head1 NAME
21              
22             PDF::Builder::Resource::Font::CoreFont - Module for using the 14 standard PDF built-in Fonts (plus 15 Windows Fonts)
23              
24             Inherits from L<PDF::Builder::Resource::Font>
25              
26             =head1 SYNOPSIS
27              
28             #
29             use PDF::Builder;
30             #
31             my $pdf = PDF::Builder->new();
32             my $cft = $pdf->font('Times-Roman');
33             #my $cft = $pdf->corefont('Times-Roman');
34             #
35             my $page = $pdf->page();
36             my $text = $page->text();
37             $text->font($cft, 20);
38             $text->translate(200, 700);
39             $text->text("Hello, World!");
40              
41             =head1 METHODS
42              
43             =head2 new
44              
45             $font = PDF::Builder::Resource::Font::CoreFont->new($pdf, $fontname, %options)
46              
47             =over
48              
49             Returns a corefont object.
50              
51             =back
52              
53             =head2 Supported typefaces
54              
55             =head3 Standard PDF types
56              
57             See examples/020_corefonts for a list of each font's glyphs.
58              
59             =over
60              
61             =item * Helvetica, Helvetica-Oblique, Helvetica-Bold, Helvetia-BoldOblique
62              
63             Sans-serif, may have Arial substituted on some systems (e.g., Windows).
64              
65             =item * Courier, Courier-Oblique, Courier-Bold, Courier-BoldOblique
66              
67             Fixed pitch, may have Courier New substituted on some systems (e.g., Windows).
68              
69             =item * Times-Roman, Times-Italic, Times-Bold, Times-BoldItalic
70              
71             Serif, may have Times New Roman substituted on some systems (e.g., Windows).
72              
73             =item * Symbol, ZapfDingbats
74              
75             Various symbols, including the Greek alphabet (in Symbol).
76              
77             =back
78              
79             =head3 Primarily Windows typefaces
80              
81             See examples/022_truefonts /Windows/Fonts/<name>.ttf
82             for a list of each font's glyphs. examples/020_corefonts can also be used.
83              
84             =over
85              
86             =item * Georgia, Georgia-Italic, Georgia-Bold, Georgia-BoldItalic
87              
88             Serif proportional.
89              
90             =item * Trebuchet, Trebuchet-Italic, Trebuchet-Bold, Trebuchet-BoldItalic
91              
92             Sans-serif proportional with simple strokes.
93              
94             =item * Verdana, Verdana-Italic, Verdana-Bold, Verdana-BoldItalic
95              
96             Sans-serif proportional with simple strokes.
97              
98             =item * BankGothic, BankGothic-Italic, BankGothic-Bold, BankGothic-BoldItalic
99              
100             Sans-serif proportional with simple strokes.
101             Free versions of Bank Gothic are often only medium weight Roman (BankGothic),
102             and this is all that usually comes with Windows.
103              
104             =item * Webdings, Wingdings
105              
106             Various symbols, in the vein of Zapf Dingbats.
107              
108             =back
109              
110             Keep in mind that only font metrics (widths) are provided with PDF::Builder;
111             the fonts themselves are provided by the reader's machine (often packaged
112             with the operating system, or obtained separately by the user). To use a
113             specific font may require you to obtain one or more files from some source.
114              
115             If a font (typeface and variant) is not available on a given reader's
116             machine, a substitution I<may> be automatically made. For example, Helvetica is
117             usually not shipped with Windows machines, and Arial might be substituted.
118             For most characters, the glyph widths will be the same, but this can not be
119             guaranteed!
120              
121             PDF::Builder currently uses the [typeface].pm files to map glyph names to
122             code points (single byte encodings only) and to look up the glyph widths for
123             character positioning. There is no guarantee that a given font file includes
124             all the desired glyphs, nor that the widths will be absolutely the same, even
125             in different releases of the same font.
126              
127             =head2 Options
128              
129             =over
130              
131             =item encode
132              
133             Changes the encoding of the font from its default. Notice that the encoding
134             (I<not> the entire font's glyph list) is shown in a PDF object (record), listing
135             256 glyphs associated with this encoding (I<and> that are available in this
136             font).
137              
138             See I<Perl's Encode> for the supported values. B<Warning:> only single byte
139             encodings are permitted. Multibyte encodings such as 'utf8' are forbidden.
140              
141             =item dokern
142              
143             Enables kerning if data is available.
144              
145             C<kerning> is an older name for this option, and is still available as
146             an B<alternative> to C<dokern>.
147              
148             =item pdfname
149              
150             Changes the reference-name of the font from its default.
151             The reference-name is normally generated automatically and can be
152             retrieved via $pdfname=$font->name().
153              
154             =item metrics
155              
156             If given, it is expected to be an anonymous hash of font file data. This is
157             to be used instead of looking up the I<$fontname>.pm file for width and other
158             data. You may need to use this option if your installed font happens to be
159             out of sync with the PDF::Builder built-in core font metrics file (e.g.,
160             I<helveticabold.pm>).
161              
162             =back
163              
164             =cut
165              
166             # PDF standard 14 core fonts
167             my @standard_fonts = qw(
168             Courier Courier-Oblique Courier-Bold Courier-BoldOblique
169             Helvetica Helvetica-Oblique Helvetica-Bold Helvetica-BoldOblique
170             Times-Roman Times-Italic Times-Bold Times-BoldItalic
171             Symbol ZapfDingbats
172             );
173              
174             # Windows extension of 15 core fonts
175             my @windows_fonts = qw(
176             BankGothic
177             Georgia Georgia-Italic Georgia-Bold Georgia-BoldItalic
178             Trebuchet Trebuchet-Italic Trebuchet-Bold Trebuchet-BoldItalic
179             Verdana Verdana-Italic Verdana-Bold Verdana-BoldItalic
180             Webdings Wingdings
181             );
182              
183             # Windows Fonts with Type1 equivalence
184             $alias = {
185             'arial' => 'helvetica',
186             'arialitalic' => 'helveticaoblique',
187             'arialbold' => 'helveticabold',
188             'arialbolditalic' => 'helveticaboldoblique',
189              
190             'times' => 'timesroman',
191             'timesnewromanbolditalic' => 'timesbolditalic',
192             'timesnewromanbold' => 'timesbold',
193             'timesnewromanitalic' => 'timesitalic',
194             'timesnewroman' => 'timesroman',
195              
196             'couriernewbolditalic' => 'courierboldoblique',
197             'couriernewbold' => 'courierbold',
198             'couriernewitalic' => 'courieroblique',
199             'couriernew' => 'courier',
200             };
201              
202             sub _look_for_font {
203 37     37   86 my $name = shift;
204              
205             ## return %{$fonts->{$name}} if defined $fonts->{$name};
206 37         4021 eval "require PDF::Builder::Resource::Font::CoreFont::$name; "; ## no critic
207 37 50       219 unless($@) {
208 37         132 my $class = "PDF::Builder::Resource::Font::CoreFont::$name";
209 37         430 my $font = _deep_copy($class->data());
210 37   50     13199 $font->{'uni'} ||= [];
211 37         172 foreach my $n (0..255) {
212 9472 50       19844 unless (defined $fonts->{'uni'}->[$n]) {
213 9472         18071 $font->{'uni'}->[$n] = uniByName($font->{'char'}->[$n]);
214             }
215             }
216 37         892 return %$font;
217             } else {
218 0         0 die "requested core font '$name' not installed ";
219             }
220             }
221              
222             #
223             # Deep copy something, thanks to Randal L. Schwartz
224             # Changed to deal w/ CODE refs, in which case it doesn't try to deep copy
225             #
226             sub _deep_copy {
227 128340     128340   179774 my $this = shift;
228              
229 128340 100       205690 if (not ref $this) {
    100          
    50          
    0          
230 127973         335018 return $this;
231             } elsif (ref($this) eq "ARRAY") {
232 258         423 return [ map { _deep_copy($_) } @$this]; ## no critic
  10724         16867  
233             } elsif (ref($this) eq "HASH") {
234 109         21436 return +{ map { $_ => _deep_copy($this->{$_}) } keys %$this };
  117579         217190  
235             } elsif (ref($this) eq "CODE") {
236             # Can't deep copy code refs
237 0         0 return $this;
238             } else {
239 0         0 die "what type is $_? Unable to copy a ".ref($this);
240             }
241             }
242              
243             sub new {
244 37     37 1 130 my ($class, $pdf, $name, @opts) = @_;
245              
246 37         96 my ($self, $data);
247 37         93 my %opts = ();
248 37         103 my $is_standard = is_standard($name);
249              
250 37 50       1772 if (-f $name) {
251 0         0 eval "require '$name'; "; ## no critic
252 0         0 $name = basename($name,'.pm');
253             }
254 37         149 my $lookname = lc($name);
255 37         228 $lookname =~ s/[^a-z0-9]+//gi; # e.g., Times-Roman to timesroman
256 37 50       177 %opts = @opts if (scalar @opts)%2 == 0;
257             # copy dashed name options to preferred undashed names
258 37 50 33     218 if (defined $opts{'-encode'} && !defined $opts{'encode'}) { $opts{'encode'} = delete($opts{'-encode'}); }
  0         0  
259 37 50 33     164 if (defined $opts{'-metrics'} && !defined $opts{'metrics'}) { $opts{'metrics'} = delete($opts{'-metrics'}); }
  0         0  
260 37 50 33     159 if (defined $opts{'-dokern'} && !defined $opts{'dokern'}) { $opts{'dokern'} = delete($opts{'-dokern'}); }
  0         0  
261 37 50 33     154 if (defined $opts{'-kerning'} && !defined $opts{'kerning'}) { $opts{'kerning'} = delete($opts{'-dokern'}); }
  0         0  
262 37 50 33     169 if (defined $opts{'-pdfname'} && !defined $opts{'pdfname'}) { $opts{'pdfname'} = delete($opts{'-pdfname'}); }
  0         0  
263              
264 37   100     288 $opts{'encode'} //= 'latin1';
265 37 50       143 $lookname = $alias->{$lookname} if $alias->{$lookname};
266              
267 37 50       129 if (defined $subs->{$lookname}) {
268 0         0 $data = {_look_for_font($subs->{$lookname}->{'-alias'})};
269 0         0 foreach my $k (keys %{$subs->{$lookname}}) {
  0         0  
270 0 0       0 next if $k =~ /^\-/;
271 0         0 $data->{$k} = $subs->{$lookname}->{$k};
272             }
273             } else {
274 37 50       108 unless (defined $opts{'metrics'}) {
275 37         161 $data = {_look_for_font($lookname)};
276             } else {
277 0         0 $data = {%{$opts{'metrics'}}};
  0         0  
278             }
279             }
280              
281 37 50       300 die "Undefined Core Font '$name($lookname)'" unless $data->{'fontname'};
282              
283             # we have data now here so we need to check if
284             # there is a -ttfile or -afmfile/-pfmfile/-pfbfile
285             # and proxy the call to the relevant modules
286             #
287             #if (defined $data->{'-ttfile'} && $data->{'-ttfile'} = _look_for_fontfile($data->{'-ttfile'})) {
288             # return PDF::Builder::Resource::CIDFont::TrueType->new($pdf, $data->{'-ttfile'}, @opts);
289             #} elsif (defined $data->{'-pfbfile'} && $data->{'-pfbfile'} = _look_for_fontfile($data->{'-pfbfile'})) {
290             # $data->{'-afmfile'} = _look_for_fontfile($data->{'-afmfile'});
291             # return PDF::Builder::Resource::Font::Postscript->new($pdf, $data->{'-pfbfile'}, $data->{'-afmfile'}, @opts));
292             #} elsif (defined $data->{'-gfx'}) { # to be written and tested in 'Maki' first!
293             # return PDF::Builder::Resource::Font::gFont->new($pdf, $data, @opts);
294             #}
295              
296 37 50       151 $class = ref($class) if ref($class);
297             # $self = $class->SUPER::new($pdf, $data->{'apiname'}.pdfkey().'~'.time());
298 37         293 $self = $class->SUPER::new($pdf, $data->{'apiname'}.pdfkey());
299 37 50       154 $pdf->new_obj($self) unless $self->is_obj($pdf);
300 37         142 $self->{' data'} = $data;
301 37 50       157 $self->{'-dokern'} = 1 if $opts{'dokern'};
302              
303 37         171 $self->{'Subtype'} = PDFName($self->data()->{'type'});
304 37         213 $self->{'BaseFont'} = PDFName($self->fontname());
305 37 50       181 if ($opts{'pdfname'}) {
306 0         0 $self->name($opts{'pdfname'});
307             }
308              
309 37 50       118 unless ($self->data()->{'iscore'}) {
310 0         0 $self->{'FontDescriptor'} = $self->descrByData();
311             }
312              
313 37 50       254 if ($opts{'encode'} =~ m/^utf/i) {
314 0         0 die "Invalid multibyte encoding for corefont: $opts{'encode'}\n";
315             # probably more encodings to check
316             }
317 37         258 $self->encodeByData($opts{'encode'});
318              
319             # The standard non-symbolic fonts use unmodified WinAnsiEncoding.
320 37 50 66     354 if ($is_standard and not $self->issymbol() and not $opts{'encode'}) {
      66        
321 0         0 $self->{'Encoding'} = PDFName('WinAnsiEncoding');
322 0         0 delete $self->{'FirstChar'};
323 0         0 delete $self->{'LastChar'};
324 0         0 delete $self->{'Widths'};
325             }
326              
327 37         322 return $self;
328             }
329              
330             =head2 is_standard
331              
332             $bool = $class->is_standard($name)
333              
334             $bool = $class->is_standard($name, $win_flag)
335              
336             =over
337              
338             Returns true if C<$name> is an exact, case-sensitive match for one of the
339             standard font names shown above.
340             If C<$win_flag> is given, and is true (1), check against not only the basic
341             14 fonts, but also against the additional 15 Windows core font extensions.
342              
343             =back
344              
345             =cut
346              
347             sub is_standard {
348             # depending on whether it's called as a method or as a local
349             # subroutine, it will or will not have a blessed object as
350             # first argument
351 119     119 1 19072 my $self = shift;
352 119         265 my $name;
353 119 100       499 if ($self =~ /^PDF::Builder/) {
354 82         211 $name = shift;
355             } else {
356 37         115 $name = $self;
357             }
358 119         202 my $win_flag = 0;
359 119 100       368 if (@_) { $win_flag = $_[0]; }
  31         63  
360              
361 119 100       316 if ($win_flag) {
362 29     435   233 return any { $_ eq $name } (@standard_fonts, @windows_fonts);
  435         815  
363             } else {
364 90     602   701 return any { $_ eq $name } @standard_fonts;
  602         1437  
365             }
366             }
367              
368             =head2 names
369              
370             my @font_names = PDF::Builder::Resource::Font::CoreFont->names($flag);
371              
372             my $array_ref = PDF::Builder::Resource::Font::CoreFont->names($flag);
373              
374             Returns an array or a reference to an array containing the names of the built-in
375             core (standard) fonts.
376              
377             If called with an optional C<$flag> of "true" value (1),
378             the additional 15 Windows core fonts are included.
379              
380             =cut
381              
382             sub names {
383 6     6 1 3990 my $self = shift;
384             # need to revise if ever called as direct subroutine in CoreFont.pm
385 6         12 my $Win_ext = 0;
386 6 100       22 if (@_) {
387 4         29 $Win_ext = $_[0];
388             }
389            
390 6 100       15 if ($Win_ext) {
391 3 100       42 return wantarray() ? (@standard_fonts,@windows_fonts) :
392             [(@standard_fonts,@windows_fonts)];
393             } else {
394 3 100       26 return wantarray() ? @standard_fonts : [@standard_fonts];
395             }
396             }
397              
398             # removed from PDF::API2
399             =head2 loadallfonts
400              
401             PDF::Builder::Resource::Font::CoreFont->loadallfonts($flag)
402              
403             =over
404              
405             "Requires in" all fonts available as corefonts, including Windows extensions
406             if the optional C<$flag> is given and is "true" (1).
407              
408             B<Warning:> "dies" if any requested font is not found!
409              
410             =back
411              
412             =cut
413              
414             sub loadallfonts {
415 0     0 0   my $self = shift;
416             # need to revise if ever called as local sub in CoreFont.pm
417 0           my $Win_ext = 0;
418 0 0         if (@_) {
419 0           $Win_ext = $_[0];
420             }
421              
422 0           foreach my $f (@standard_fonts) {
423 0           _look_for_font($f);
424             }
425 0 0         if ($Win_ext) {
426 0           foreach my $f (@windows_fonts) {
427 0           _look_for_font($f);
428             }
429             }
430 0           return;
431             }
432              
433             # not yet supported
434             # andalemono
435             # arialrounded
436             # impact
437             # ozhandicraft
438              
439             BEGIN
440             {
441              
442             # substitutes via synfont() for missing BankGothic variants
443 8     8   112 $subs = {
444             'bankgothicbold' => {
445             'apiname' => 'Bg2',
446             '-alias' => 'bankgothic',
447             'fontname' => 'BankGothicMediumBT,Bold',
448             'flags' => 32+262144,
449             },
450             'bankgothicbolditalic' => {
451             'apiname' => 'Bg3',
452             '-alias' => 'bankgothic',
453             'fontname' => 'BankGothicMediumBT,BoldItalic',
454             'italicangle' => -15,
455             'flags' => 96+262144,
456             },
457             'bankgothicitalic' => {
458             'apiname' => 'Bg4',
459             '-alias' => 'bankgothic',
460             'fontname' => 'BankGothicMediumBT,Italic',
461             'italicangle' => -15,
462             'flags' => 96,
463             },
464             # 'impactitalic' => {
465             # 'apiname' => 'Imp2',
466             # '-alias' => 'impact',
467             # 'fontname' => 'Impact,Italic',
468             # 'italicangle' => -12,
469             # },
470             # 'ozhandicraftbold' => {
471             # 'apiname' => 'Oz2',
472             # '-alias' => 'ozhandicraft',
473             # 'fontname' => 'OzHandicraftBT,Bold',
474             # 'italicangle' => 0,
475             # 'flags' => 32+262144,
476             # },
477             # 'ozhandicraftitalic' => {
478             # 'apiname' => 'Oz3',
479             # '-alias' => 'ozhandicraft',
480             # 'fontname' => 'OzHandicraftBT,Italic',
481             # 'italicangle' => -15,
482             # 'flags' => 96,
483             # },
484             # 'ozhandicraftbolditalic' => {
485             # 'apiname' => 'Oz4',
486             # '-alias' => 'ozhandicraft',
487             # 'fontname' => 'OzHandicraftBT,BoldItalic',
488             # 'italicangle' => -15,
489             # 'flags' => 96+262144,
490             # },
491             # 'arialroundeditalic' => {
492             # 'apiname' => 'ArRo2',
493             # '-alias' => 'arialrounded',
494             # 'fontname' => 'ArialRoundedMTBold,Italic',
495             # 'italicangle' => -15,
496             # 'flags' => 96+262144,
497             # },
498             # 'arialitalic' => {
499             # 'apiname' => 'Ar2',
500             # '-alias' => 'arial',
501             # 'fontname' => 'Arial,Italic',
502             # 'italicangle' => -15,
503             # 'flags' => 96,
504             # },
505             # 'arialbolditalic' => {
506             # 'apiname' => 'Ar3',
507             # '-alias' => 'arial',
508             # 'fontname' => 'Arial,BoldItalic',
509             # 'italicangle' => -15,
510             # 'flags' => 96+262144,
511             # },
512             # 'arialbold' => {
513             # 'apiname' => 'Ar4',
514             # '-alias' => 'arial',
515             # 'fontname' => 'Arial,Bold',
516             # 'flags' => 32+262144,
517             # },
518             };
519              
520 8         322 $fonts = { };
521              
522             }
523              
524             1;
525              
526             __END__
527              
528             =head1 AUTHOR
529              
530             Alfred Reibenschuh
531              
532             =cut
533              
534