File Coverage

blib/lib/Font/AFM.pm
Criterion Covered Total %
statement 21 96 21.8
branch 4 48 8.3
condition n/a
subroutine 5 9 55.5
pod 4 4 100.0
total 34 157 21.6


line stmt bran cond sub pod time code
1             # This -*- perl -*- module is a simple parser for Adobe Font Metrics files.
2              
3             package Font::AFM;
4              
5             =head1 NAME
6              
7             Font::AFM - Interface to Adobe Font Metrics files
8              
9             =head1 SYNOPSIS
10              
11             use Font::AFM;
12             $h = new Font::AFM "Helvetica";
13             $copyright = $h->Notice;
14             $w = $h->Wx->{"aring"};
15             $w = $h->stringwidth("Gisle", 10);
16             $h->dump; # for debugging
17              
18             =head1 DESCRIPTION
19              
20             This module implements the Font::AFM class. Objects of this class are
21             initialised from an AFM (Adobe Font Metrics) file and allow you to obtain information
22             about the font and the metrics of the various glyphs in the font.
23              
24             All measurements in AFM files are given in terms of units equal to
25             1/1000 of the scale factor of the font being used. To compute actual
26             sizes in a document, these amounts should be multiplied by (scale
27             factor of font)/1000.
28              
29             The following methods are available:
30              
31             =over 3
32              
33             =item $afm = Font::AFM->new($fontname)
34              
35             Object constructor. Takes the name of the font as argument.
36             Croaks if the font can not be found.
37              
38             =item $afm->latin1_wx_table()
39              
40             Returns a 256-element array, where each element contains the width
41             of the corresponding character in the iso-8859-1 character set.
42              
43             =item $afm->stringwidth($string, [$fontsize])
44              
45             Returns the width of the argument string. The string is
46             assumed to be encoded in the iso-8859-1 character set. A second
47             argument can be used to scale the width according to the font size.
48              
49             =item $afm->FontName
50              
51             The name of the font as presented to the PostScript language
52             C operator, for instance "Times-Roman".
53              
54             =item $afm->FullName
55              
56             Unique, human-readable name for an individual font, for instance
57             "Times Roman".
58              
59             =item $afm->FamilyName
60              
61             Human-readable name for a group of fonts that are stylistic variants
62             of a single design. All fonts that are members of such a group should
63             have exactly the same C. Example of a family name is
64             "Times".
65              
66             =item $afm->Weight
67              
68             Human-readable name for the weight, or "boldness", attribute of a font.
69             Examples are C, C, C.
70              
71             =item $afm->ItalicAngle
72              
73             Angle in degrees counterclockwise from the vertical of the dominant
74             vertical strokes of the font.
75              
76             =item $afm->IsFixedPitch
77              
78             If C, the font is a fixed-pitch
79             (monospaced) font.
80              
81             =item $afm->FontBBox
82              
83             A string of four numbers giving the lower-left x, lower-left y,
84             upper-right x, and upper-right y of the font bounding box. The font
85             bounding box is the smallest rectangle enclosing the shape that would
86             result if all the characters of the font were placed with their
87             origins coincident, and then painted.
88              
89             =item $afm->UnderlinePosition
90              
91             Recommended distance from the baseline for positioning underline
92             strokes. This number is the y coordinate of the center of the stroke.
93              
94             =item $afm->UnderlineThickness
95              
96             Recommended stroke width for underlining.
97              
98             =item $afm->Version
99              
100             Version number of the font.
101              
102             =item $afm->Notice
103              
104             Trademark or copyright notice, if applicable.
105              
106             =item $afm->Comment
107              
108             Comments found in the AFM file.
109              
110             =item $afm->EncodingScheme
111              
112             The name of the standard encoding scheme for the font. Most Adobe
113             fonts use the C. Special fonts might state
114             C.
115              
116             =item $afm->CapHeight
117              
118             Usually the y-value of the top of the capital H.
119              
120             =item $afm->XHeight
121              
122             Typically the y-value of the top of the lowercase x.
123              
124             =item $afm->Ascender
125              
126             Typically the y-value of the top of the lowercase d.
127              
128             =item $afm->Descender
129              
130             Typically the y-value of the bottom of the lowercase p.
131              
132             =item $afm->Wx
133              
134             Returns a hash table that maps from glyph names to the width of that glyph.
135              
136             =item $afm->BBox
137              
138             Returns a hash table that maps from glyph names to bounding box information.
139             The bounding box consist of four numbers: llx, lly, urx, ury.
140              
141             =item $afm->dump
142              
143             Dumps the content of the Font::AFM object to STDOUT. Might sometimes
144             be useful for debugging.
145              
146             =back
147              
148              
149             The AFM specification can be found at:
150              
151             http://partners.adobe.com/asn/developer/pdfs/tn/5004.AFM_Spec.pdf
152              
153              
154             =head1 ENVIRONMENT
155              
156             =over 10
157              
158             =item METRICS
159              
160             Contains the path to search for AFM-files. Format is as for the PATH
161             environment variable. The default path built into this library is:
162              
163             /usr/lib/afm:/usr/local/lib/afm:/usr/openwin/lib/fonts/afm/:.
164              
165             =back
166              
167              
168             =head1 BUGS
169              
170             Kerning data and composite character data are not yet parsed.
171             Ligature data is not parsed.
172              
173              
174             =head1 COPYRIGHT
175              
176             Copyright 1995-1998 Gisle Aas. All rights reserved.
177              
178             This program is free software; you can redistribute it and/or modify
179             it under the same terms as Perl itself.
180              
181             =cut
182              
183             #-------perl resumes here--------------------------------------------
184              
185 1     1   938 use Carp;
  1         2  
  1         103  
186 1     1   6 use strict;
  1         2  
  1         46  
187 1     1   19 use vars qw($VERSION @ISOLatin1Encoding);
  1         2  
  1         2136  
188              
189             $VERSION = "1.20";
190              
191              
192             # The metrics_path is used to locate metrics files
193             #
194             my $metrics_path = $ENV{METRICS} ||
195             "/usr/lib/afm:/usr/local/lib/afm:/usr/openwin/lib/fonts/afm/:.";
196             my @metrics_path = split(/:/, $metrics_path);
197             foreach (@metrics_path) { s,/$,, } # reove trailing slashes
198              
199             @ISOLatin1Encoding = qw(
200             .notdef .notdef .notdef .notdef .notdef .notdef .notdef .notdef
201             .notdef .notdef .notdef .notdef .notdef .notdef .notdef .notdef
202             .notdef .notdef .notdef .notdef .notdef .notdef .notdef .notdef
203             .notdef .notdef .notdef .notdef .notdef .notdef .notdef .notdef space
204             exclam quotedbl numbersign dollar percent ampersand quoteright
205             parenleft parenright asterisk plus comma minus period slash zero one
206             two three four five six seven eight nine colon semicolon less equal
207             greater question at A B C D E F G H I J K L M N O P Q R S
208             T U V W X Y Z bracketleft backslash bracketright asciicircum
209             underscore quoteleft a b c d e f g h i j k l m n o p q r s
210             t u v w x y z braceleft bar braceright asciitilde .notdef .notdef
211             .notdef .notdef .notdef .notdef .notdef .notdef .notdef .notdef
212             .notdef .notdef .notdef .notdef .notdef .notdef .notdef dotlessi grave
213             acute circumflex tilde macron breve dotaccent dieresis .notdef ring
214             cedilla .notdef hungarumlaut ogonek caron space exclamdown cent
215             sterling currency yen brokenbar section dieresis copyright ordfeminine
216             guillemotleft logicalnot hyphen registered macron degree plusminus
217             twosuperior threesuperior acute mu paragraph periodcentered cedilla
218             onesuperior ordmasculine guillemotright onequarter onehalf threequarters
219             questiondown Agrave Aacute Acircumflex Atilde Adieresis Aring AE
220             Ccedilla Egrave Eacute Ecircumflex Edieresis Igrave Iacute Icircumflex
221             Idieresis Eth Ntilde Ograve Oacute Ocircumflex Otilde Odieresis
222             multiply Oslash Ugrave Uacute Ucircumflex Udieresis Yacute Thorn
223             germandbls agrave aacute acircumflex atilde adieresis aring ae
224             ccedilla egrave eacute ecircumflex edieresis igrave iacute icircumflex
225             idieresis eth ntilde ograve oacute ocircumflex otilde odieresis divide
226             oslash ugrave uacute ucircumflex udieresis yacute thorn ydieresis
227             );
228              
229              
230             # Creates a new Font::AFM object. Pass it the name of the font as parameter.
231             # Synopisis:
232             #
233             # $h = new Font::AFM "Helvetica";
234             #
235              
236             sub new
237             {
238 1     1 1 11 my($class, $fontname) = @_;
239 1         2 my $file;
240 1         3 $fontname =~ s/\.afm$//;
241 1 50       6 if ($^O eq 'VMS') {
242 0         0 $file = "sys\$ps_font_metrics:$fontname.afm";
243             } else {
244 1         2 $file = "$fontname.afm";
245 1 50       4 unless ($file =~ m,^/,) {
246             # not absolute, search the metrics path for the file
247 1         2 foreach (@metrics_path) {
248 4 50       88 if (-f "$_/$file") {
249 0         0 $file = "$_/$file";
250 0         0 last;
251             }
252             }
253             }
254             }
255 1 50       240 open(AFM, $file) or croak "Can't find the AFM file for $fontname";
256 0           my $self = bless { }, $class;
257 0           local($/, $_) = ("\n", undef); # ensure correct $INPUT_RECORD_SEPARATOR
258 0           while () {
259 0 0         next if /^StartKernData/ .. /^EndKernData/; # kern data not parsed yet
260 0 0         next if /^StartComposites/ .. /^EndComposites/; # same for composites
261 0 0         if (/^StartCharMetrics/ .. /^EndCharMetrics/) {
262             # only lines that start with "C" or "CH" are parsed
263 0 0         next unless /^CH?\s/;
264 0           my($name) = /\bN\s+(\.?\w+)\s*;/;
265 0           my($wx) = /\bWX\s+(\d+)\s*;/;
266 0           my($bbox) = /\bB\s+([^;]+);/;
267 0           $bbox =~ s/\s+$//;
268             # Should also parse lingature data (format: L successor lignature)
269 0           $self->{'wx'}{$name} = $wx;
270 0           $self->{'bbox'}{$name} = $bbox;
271 0           next;
272             }
273 0 0         last if /^EndFontMetrics/;
274 0 0         if (/(^\w+)\s+(.*)/) {
275 0           my($key,$val) = ($1, $2);
276 0           $key = lc $key;
277 0 0         if (defined $self->{$key}) {
278 0 0         $self->{$key} = [ $self->{$key} ] unless ref $self->{$key};
279 0           push(@{$self->{$key}}, $val);
  0            
280             } else {
281 0           $self->{$key} = $val;
282             }
283             } else {
284 0           print STDERR "Can't parse: $_";
285             }
286             }
287 0           close(AFM);
288 0 0         unless (exists $self->{wx}->{'.notdef'}) {
289 0           $self->{wx}->{'.notdef'} = 0;
290 0           $self->{bbox}{'.notdef'} = "0 0 0 0";
291             }
292 0           $self;
293             }
294              
295             # Returns an 256 element array that maps from characters to width
296             sub latin1_wx_table
297             {
298 0     0 1   my($self) = @_;
299 0 0         unless ($self->{'_wx_table'}) {
300 0           my @wx;
301 0           for (0..255) {
302 0           my $name = $ISOLatin1Encoding[$_];
303 0 0         if (exists $self->{wx}->{$name}) {
304 0           push(@wx, $self->{wx}->{$name})
305             } else {
306 0           push(@wx, $self->{wx}->{'.notdef'});
307             }
308             }
309 0           $self->{'_wx_table'} = \@wx;
310             }
311 0 0         wantarray ? @{ $self->{'_wx_table'} } : $self->{'_wx_table'};
  0            
312             }
313              
314             sub stringwidth
315             {
316 0     0 1   my($self, $string, $pointsize) = @_;
317 0 0         return 0.0 unless defined $string;
318 0 0         return 0.0 unless length $string;
319              
320 0           my @wx = $self->latin1_wx_table;
321 0           my $width = 0.0;
322 0           for (unpack("C*", $string)) {
323 0           $width += $wx[$_];
324             }
325 0 0         if ($pointsize) {
326 0           $width *= $pointsize / 1000;
327             }
328 0           $width;
329             }
330              
331             sub FontName;
332             sub FullName;
333             sub FamilyName;
334             sub Weight;
335             sub ItalicAngle;
336             sub IsFixedPitch;
337             sub FontBBox;
338             sub UnderlinePosition;
339             sub UnderlineThickness;
340             sub Version;
341             sub Notice;
342             sub Comment;
343             sub EncodingScheme;
344             sub CapHeight;
345             sub XHeight;
346             sub Ascender;
347             sub Descender;
348             sub Wx;
349             sub BBox;
350              
351             # We implement all the access functions within this simple autoload
352             # function.
353              
354             sub AUTOLOAD
355             {
356 1     1   7 no strict 'vars'; # don't want to declare $AUTOLOAD
  1         1  
  1         674  
357              
358             #print "AUTOLOAD: $AUTOLOAD\n";
359 0 0   0     if ($AUTOLOAD =~ /::DESTROY$/) {
360 0           eval "sub $AUTOLOAD {}";
361 0           goto &$AUTOLOAD;
362             } else {
363 0           my $name = $AUTOLOAD;
364 0           $name =~ s/^.*:://;
365 0 0         croak "Attribute $name not defined for AFM object"
366             unless defined $_[0]->{lc $name};
367 0           return $_[0]->{lc $name};
368             }
369             }
370              
371              
372             # Dumping might be useful for debugging
373              
374             sub dump
375             {
376 0     0 1   my($self) = @_;
377 0           my($key, $val);
378 0           foreach $key (sort keys %$self) {
379 0 0         if (ref $self->{$key}) {
380 0 0         if (ref $self->{$key} eq "ARRAY") {
    0          
381 0           print "$key = [\n\t", join("\n\t", @{$self->{$key}}), "\n]\n";
  0            
382             } elsif (ref $self->{$key} eq "HASH") {
383 0           print "$key = {\n";
384 0           my $key2;
385 0           foreach $key2 (sort keys %{$self->{$key}}) {
  0            
386 0           print "\t$key2 => $self->{$key}{$key2},\n";
387             }
388 0           print "}\n";
389             } else {
390 0           print "$key = $self->{$key}\n";
391             }
392             } else {
393 0           print "$key = $self->{$key}\n";
394             }
395             }
396             }
397              
398             1;