package PDF::Builder::FontManager; use strict; use warnings; our $VERSION = '3.025'; # VERSION our $LAST_UPDATE = '3.025'; # manually update whenever code is changed use Carp; use Scalar::Util qw(weaken); # unless otherwise noted, routines beginning with _ are internal helper # functions and should not be used by others # # TBD (future) # spec use of synfont() against a base to get # fake bold, italic, bold+italic # small caps, perhaps petite caps # condensed and expanded (or via hscale()) # support for UTF-8 subfonts for single byte encoding fonts =head1 NAME PDF::Builder::FontManager - Managing the font library for PDF::Builder =head1 SYNOPSIS These routines are called from the PDF::Builder class (see C methods). # core fonts come pre-loaded # Add new a new font face and variants my $rc = $pdf->add_font( 'face' => $unique_face_name, # font family, e.g., Times 'type' => 'core', # note that core fonts preloaded 'style' => 'serif', # also sans-serif, script (cursive), # and symbol 'width' => 'proportional', # also constant 'settings' => { 'encode' => $encoding }, # note that these are actual core font names rather than file paths 'file' => { 'roman' => 'Times-Roman', 'italic' => 'Times-Italic', 'bold' => 'Times-Bold', 'bold-italic' => 'Times-BoldItalic' }, # for non-core these would be the actual file paths # prefixed with font search paths ); $rc = $pdf->add_font( 'face' => 'DejaVuSans', # Deja Vu sans serif family 'type' => 'ttf', # otf uses 'ttf' 'style' => 'sans-serif', 'width' => 'proportional', 'settings' => { 'encode' => 'utf8' }, # the defined font paths will be prepended to find the actual path 'file' => { 'roman' => 'DejaVuSans.ttf', 'italic' => 'DejaVuSans-Oblique.ttf', 'bold' => 'DejaVuSans-Bold.ttf', 'bold-italic' => 'DejaVuSans-BoldOblique.ttf' } ); Some of the global data, which can be reset via the C method: * default-face: initialized to Times-Roman (core), used if you start formatting text without explicitly setting a face * default-serif: initialized to Times-Roman (core), used if you want a "generic" serif typeface * default-sansserif: initialized to Helvetica (core), used if you want a "generic" sans-serif typeface * default-constant: initialized to Courier (core), used if you want a "generic" constant-width typeface * default-script: NOT initialized (no default), used if you want a "generic" script (cursive) typeface * default-symbol initialized to Symbol (core), used if you want a "generic" symbol typeface * font-paths: C:/Windows/Fonts for Windows systems for TTF, other types are in non-standard paths, and for non-Windows, anything goes Usage of C is to specify the face and variants, and then each time, specify I and I to be on or off. If the desired file is not yet opened, it will be, and the C<$font> returned. If the font was already created earlier, the saved C<$font> will be returned. my $font = $pdf->get_font( 'face' => 'Times', 'italic' => 0, # desire Roman (upright) 'bold' => 0, # desire medium weight ); # if $font is undef, we have a problem... $text->font($font, $font_size); $text->... # use this font (medium weight Times-Roman core font) $font = $pdf->get_font('italic' => 1); $text->... # switched to italic $font = $pdf->get_font('italic' => 0); $text->... # back to Roman (upright) text =head1 METHODS =over =item PDF::Builder::FontManager->new(%opts) This is called from Builder.pm's C. Currently there are no options selectable. It will set up the font manager system and preload it with the core fonts. Various defaults will be set for the face (core Times-Roman), serif face (core Times-Roman), sans-serif face (core Helvetica), constant width (fixed pitch) face (core Courier), and a symbol font (core Symbol). There is no default for a script (cursive) font unless you set one using the C method. =cut sub new { my ($class, $pdf) = @_; my $self = bless { 'pdf' => $pdf }, $class; weaken $self->{'pdf'}; #$pdf = $pdf->{'pdf'} if $pdf->isa('PDF::Builder'); #$class = ref($class) if ref($class); #my $self = $class->SUPER::new($pdf); $self->{' pdf'} = $pdf; # current font is default font until face explicitly changed. # Times face should be element 0 of the font-list array. $self->{' current-font'} = {'face' => 'Times', 'index' => 0, 'italic' => 0, 'bold' => 0}; # just the face to use. index assumes standard core initialization $self->{' default-font'} = {'face' => 'Times', 'index' => 0}; $self->{' default-serif'} = {'face' => 'Times', 'index' => 0}; $self->{' default-sansserif'} = {'face' => 'Helvetica', 'index' => 1}; $self->{' default-constant'} = {'face' => 'Courier', 'index' => 2}; $self->{' default-symbol'} = {'face' => 'Symbol', 'index' => 3}; # no script font loaded by default $self->{' default-script'} = {'face' => undef, 'index' => -1}; $self->{' font-paths'} = []; $self->{' font-list'} = []; # For Windows, can at least initialize to TTF place. Any additional fonts # for Windows, and all non-Windows paths, will have to be added by the # user. Note that an absolute (starts with /) or semi-absolute (starts # with ./ or ../) font path/file will NOT have any search paths # prepended! push @{$self->{' font-paths'}}, $pdf->font_path(); # can add any additional paths, but better to do in Builder.pm $self->_initialize_core(); return $self; } # end of new() =item @list = $pdf->font_settings() # Get =item $pdf->font_settings(%info) # Set Get or set some information about fonts, particularly the fonts to be used for "generic" purposes. "Get" returns a list (array) of the default font face name, the default generic serif face, the default generic sans-serif face, the default generic constant width face, the default generic symbol face, and the default generic script (cursive) face. It is possible for an element to be undefined (e.g., the generic script face is C). "Set" changes one or more default settings: 'font' => face to use for the default font face (initialized to Times) 'serif' => face to use for the generic serif face (initialized to Times) 'sans-serif' => face to use for the generic sans serif face (initialized to Helvetica) 'constant' => face to use for the generic constant width face (initialized to Courier) 'script' => face to use for the generic script face (uninitialized) 'symbol' => face to use for the generic symbol face (initialized to Symbol) =cut sub font_settings { my ($self, %info) = @_; if (!keys %info) { # Get default faces, nothing passed in return ( $self->{' default-font'}->{'face'}, $self->{' default-serif'}->{'face'}, $self->{' default-sansserif'}->{'face'}, $self->{' default-constant'}->{'face'}, $self->{' default-script'}->{'face'}, $self->{' default-symbol'}->{'face'}, ); } # Set default info from %info passed in # also check if face exists, and at same time pick up the index value my $index; if (defined $info{'font'}) { $index = $self->_face2index($info{'font'}); if ($index >= 0) { $self->{' default-font'}->{'face'} = $info{'font'}; $self->{' default-font'}->{'index'} = $index; } else { carp "font_settings can't find face $info{'font'}. ignored."; } } if (defined $info{'serif'}) { $index = $self->_face2index($info{'serif'}); if ($index >= 0) { $self->{' default-serif'}->{'face'} = $info{'serif'}; $self->{' default-serif'}->{'index'} = $index; } else { carp "font_settings can't find face $info{'serif'}. ignored."; } } if (defined $info{'sans-serif'}) { $index = $self->_face2index($info{'sans-serif'}); if ($index >= 0) { $self->{' default-sansserif'}->{'face'} = $info{'sans-serif'}; $self->{' default-sansserif'}->{'index'} = $index; } else { carp "font_settings can't find face $info{'sans-serif'}. ignored."; } } if (defined $info{'constant'}) { $index = $self->_face2index($info{'constant'}); if ($index >= 0) { $self->{' default-constant'}->{'face'} = $info{'constant'}; $self->{' default-constant'}->{'index'} = $index; } else { carp "font_settings can't find face $info{'constant'}. ignored."; } } if (defined $info{'script'}) { $index = $self->_face2index($info{'script'}); if ($index >= 0) { $self->{' default-script'}->{'face'} = $info{'script'}; $self->{' default-script'}->{'index'} = $index; } else { carp "font_settings can't find face $info{'script'}. ignored."; } } if (defined $info{'symbol'}) { $index = $self->_face2index($info{'symbol'}); if ($index >= 0) { $self->{' default-symbol'}->{'face'} = $info{'symbol'}; $self->{' default-symbol'}->{'index'} = $index; } else { carp "font_settings can't find face $info{'symbol'}. ignored."; } } return; } =item $rc = $pdf->add_font_path("a directory path", %opts) This method adds one search path to the list of paths to search. In the C method, each defined search path will be prepended to the C entry (except for core fonts) in turn, until the font file is found. However, if the C entry starts with / or ./ or ../, it will be used alone. A C entry starting with .../ is a special case, which is turned into ../ before the search path is prepended. This permits you to give a search path that you expect to move up one or more directories. The font path search list always includes the current directory (.), and is initialized in C as C<@font_path>. For the Windows operating system, C usually contains a number of TTF fonts that come standard with Windows, so it is added by default. Anything else, including all Linux (and other non-Windows operating systems), will have to be added depending on your particular system. Some common places are: Windows (B use / and not \\ in Windows paths!). Linux systems may or may not handle spaces in directory names gracefully! /Windows/Fonts /WinNT/Fonts /Program Files/MikTex 2.9/fonts/type1/urw/bookman (URW Bookman for MikTex) /Program Files (x86)/MikTex 2.9/fonts/type1/urw/bookman (older versions) /Program Files/Adobe/Acrobat DC/Resource/CIDFont (Adobe Reader fonts) GhostScript may have its own directories Note that directory names with spaces (e.g., C) may not play nice with some Linux systems, so they are not included by default. Linux, etc. /usr/share/fonts (common base) /usr/local/share/fonts (common base) /usr/share/fonts/dejavu-sans-fonts (Deja Vu Sans TTF specifically) /usr/share/fonts/truetype/ttf-dejavu /usr/share/fonts/truetype/dejavu /usr/lib/defoma/gs.d/devs/fonts (GhostScript?) /usr/share/fonts/type1/gsfonts (GhostScript PS) /usr/share/X11/fonts/urw-fonts (URW PS) Third-party application installations, such as Adobe's Acrobat Reader, may be a source of installed fonts, too. A return code of 0 means the path was successfully added, while 1 means there was a problem encountered (and a message was issued). No options are currently defined. =cut sub add_font_path { my ($self, $newpath, %opts) = @_; my $rc = 0; # OK so far! # TBD: consider validating that this $newpath exists? # will not be using until actually attempt to open the file! push @{ $self->{' font-paths'} }, $newpath; return $rc; } # end of add_font_path() =item $rc = add_font(%info) Add a new font entry (by face and variants) to the Font Manager's list of known fonts. C<%info> items to be defined: =over =item face => 'face name' This should be a unique string to identify just one entry in the Font Manager fonts table. I.e., you should not have two "Times" (one a core font and the other a TTF font). Give them different names (face names are case I, so 'Times' is different from 'times'). The C name is used to retrieve the desired font. =item type => 'type string' This tells which Builder font routine to use to load the font. The allowed entries are: =over =item B This is a core font, and is loaded via the C routine. Note that the core fonts are automatically pre-loaded (including additional ones on Windows systems), so you should not need to explicitly load any core fonts (at least, the 14 basic ones). All PDF installation are supposed to include these 14 basic core fonts, but the precise actual file type may vary among installations, and substitutions may be made (so long as the metrics match). Currently, core fonts are limited to single byte encodings. On Windows systems, there are an additional 14 core fonts which are normally loaded. These are Georgia, Verdana, Trebuchet, Wingdings, and Webdings faces. Use caution if making use of these additional core fonts, as non-Windows systems may not include them without explicit manual installation of these fonts. These fonts may be safe to use if you know that all your PDF readers will be running on Windows systems. =item B This is a TrueType (.ttf) or OpenType (.otf) font, loaded with C. Currently this is the only type which can be used with multibyte (e.g., I) encodings, as well as with single byte encodings such as I. It is also the only font type that can be used with HarfBuzz::Shaper. Many systems include a number of TTF fonts, but unlike core fonts, none are automatically loaded by the PDF::Builder Font Manager, and must be explicitly loaded via C. =item B This is a PostScript (Type1) font, loaded with C, which used to be quite commonly used, but is fairly rarely used today, having mostly been supplanted by the more capable TTF format. Some systems may include some Type1 fonts, but Windows, for example, does not normally come with any. No Type1 fonts are automatically loaded by the PDF::Builder Font Manager, and must be explicitly loaded via C. It is assumed that the font metrics file (.afm or .pfm) has the same base file name as the glyph file (.pfa or .pfb), is found in the same directory, I either can work with either. If you have need for a different directory, a different base name, or a specific metrics file to go with a specific glyph file, let us know, so we can add such functionality. Otherwise, you will need to directly use the C method in order to specify such different paths. =item B This is an East Asian (Chinese-Japanese-Korean) type font, loaded with the C method. Note that CJK fonts have never been well supported by PDF::Builder, and depend on some fairly old (obsolete) features and external files (.cmap and .data). We suggest that, rather than going directly to CJK files, you first try directly using the (usually) TTF files, in the TTF format. Few systems come with CJK fonts installed. No CJK fonts are automatically loaded by the PDF::Builder Font Manager, and must be explicitly loaded via C. =item B This is an Adobe Bitmap Distribution Format font, loaded with the C method, a very old bitmapped format dating back to the early days of the X11 system. Unlike the filled smooth outlines used in most modern fonts, BDF's are a coarse grid of on/off pixels. Please be kind to your readers and use this format sparingly, such as only for chapter titles or headings! Few systems come with BDF fonts installed any more. No BDF fonts are automatically loaded by the PDF::Builder Font Manager, and must be explicitly loaded via C. =back =item settings => { 'encode' => string, ... } This is a collection of any other settings, flags, etc. accepted by this particular font type. See the POD for C, C, etc. (per I for the allowable entries. An important one will be the encoding, which you will need to specify, if you use any characters beyond basic ASCII. Currently, all fonts may use any single byte encoding you desire (the default is I). Only TTF type fonts (which includes OTF and CJK fonts) may currently specify a multibyte encoding such as I. Needless to say, the text data that you pass to text routines must conform to the given encoding. You are I forced to use the same encoding for all defined fonts, but if you wish to mix-and-match encodings, it is up to you to define your text that uses the encoding specified for the particular font used! Note in particular when you use I that (if numeric) they are given in the Unicode number. When out of the single byte range (x00-xFF), results are unpredictable if you give an entity that does not fall within the encoding's range! Also check results for Unicode points within the range x80-xFF if you are using I encoding. =item style => 'styling' This specifies the styling of the font: B, B, B (constant width, or fixed pitch), B