File Coverage

blib/lib/PDF/API3/Compat/API2/Basic/TTF/Font.pm
Criterion Covered Total %
statement 16 234 6.8
branch 1 80 1.2
condition 1 33 3.0
subroutine 5 21 23.8
pod 12 13 92.3
total 35 381 9.1


line stmt bran cond sub pod time code
1             #=======================================================================
2             # ____ ____ _____ _ ____ ___ ____
3             # | _ \| _ \| ___| _ _ / \ | _ \_ _| |___ \
4             # | |_) | | | | |_ (_) (_) / _ \ | |_) | | __) |
5             # | __/| |_| | _| _ _ / ___ \| __/| | / __/
6             # |_| |____/|_| (_) (_) /_/ \_\_| |___| |_____|
7             #
8             # A Perl Module Chain to faciliate the Creation and Modification
9             # of High-Quality "Portable Document Format (PDF)" Files.
10             #
11             #=======================================================================
12             #
13             # THIS IS A REUSED PERL MODULE, FOR PROPER LICENCING TERMS SEE BELOW:
14             #
15             #
16             # Copyright Martin Hosken 1998.
17             #
18             # No warranty or expression of effectiveness, least of all regarding
19             # anyone's safety, is implied in this software or documentation.
20             #
21             # This specific module is licensed under the Perl Artistic License.
22             #
23             #
24             # $Id: Font.pm,v 2.0 2005/11/16 02:16:00 areibens Exp $
25             #
26             #=======================================================================
27             package PDF::API3::Compat::API2::Basic::TTF::Font;
28            
29             =head1 NAME
30            
31             PDF::API3::Compat::API2::Basic::TTF::Font - Memory representation of a font
32            
33             =head1 SYNOPSIS
34            
35             Here is the regression test (you provide your own font). Run it once and then
36             again on the output of the first run. There should be no differences between
37             the outputs of the two runs.
38            
39             $f = PDF::API3::Compat::API2::Basic::TTF::Font->open($ARGV[0]);
40            
41             # force a read of all the tables
42             $f->tables_do(sub { $_[0]->read; });
43            
44             # force read of all glyphs (use read_dat to use lots of memory!)
45             # $f->{'loca'}->glyphs_do(sub { $_[0]->read; });
46             $f->{'loca'}->glyphs_do(sub { $_[0]->read_dat; });
47             # NB. no need to $g->update since $f->{'glyf'}->out will do it for us
48            
49             $f->out($ARGV[1]);
50             $f->release; # clear up memory forcefully!
51            
52             =head1 DESCRIPTION
53            
54             A Truetype font consists of a header containing a directory of tables which
55             constitute the rest of the file. This class holds that header and directory and
56             also creates objects of the appropriate type for each table within the font.
57             Note that it does not read each table into memory, but creates a short reference
58             which can be read using the form:
59            
60             $f->{$tablename}->read;
61            
62             Classes are included that support many of the different TrueType tables. For
63             those for which no special code exists, the table type C is used, which
64             defaults to L. The current tables which are supported are:
65            
66             table PDF::API3::Compat::API2::Basic::TTF::Table - for unknown tables
67             GDEF PDF::API3::Compat::API2::Basic::TTF::GDEF
68             GPOS PDF::API3::Compat::API2::Basic::TTF::GPOS
69             GSUB PDF::API3::Compat::API2::Basic::TTF::GSUB
70             LTSH PDF::API3::Compat::API2::Basic::TTF::LTSH
71             OS/2 PDF::API3::Compat::API2::Basic::TTF::OS_2
72             PCLT PDF::API3::Compat::API2::Basic::TTF::PCLT
73             bsln PDF::API3::Compat::API2::Basic::TTF::Bsln
74             cmap PDF::API3::Compat::API2::Basic::TTF::Cmap - see also PDF::API3::Compat::API2::Basic::TTF::OldCmap
75             cvt PDF::API3::Compat::API2::Basic::TTF::Cvt_
76             fdsc PDF::API3::Compat::API2::Basic::TTF::Fdsc
77             feat PDF::API3::Compat::API2::Basic::TTF::Feat
78             fmtx PDF::API3::Compat::API2::Basic::TTF::Fmtx
79             fpgm PDF::API3::Compat::API2::Basic::TTF::Fpgm
80             glyf PDF::API3::Compat::API2::Basic::TTF::Glyf - see also PDF::API3::Compat::API2::Basic::TTF::Glyph
81             hdmx PDF::API3::Compat::API2::Basic::TTF::Hdmx
82             head PDF::API3::Compat::API2::Basic::TTF::Head
83             hhea PDF::API3::Compat::API2::Basic::TTF::Hhea
84             hmtx PDF::API3::Compat::API2::Basic::TTF::Hmtx
85             kern PDF::API3::Compat::API2::Basic::TTF::Kern - see alternative PDF::API3::Compat::API2::Basic::TTF::AATKern
86             loca PDF::API3::Compat::API2::Basic::TTF::Loca
87             maxp PDF::API3::Compat::API2::Basic::TTF::Maxp
88             mort PDF::API3::Compat::API2::Basic::TTF::Mort - see also PDF::API3::Compat::API2::Basic::TTF::OldMort
89             name PDF::API3::Compat::API2::Basic::TTF::Name
90             post PDF::API3::Compat::API2::Basic::TTF::Post
91             prep PDF::API3::Compat::API2::Basic::TTF::Prep
92             prop PDF::API3::Compat::API2::Basic::TTF::Prop
93             vhea PDF::API3::Compat::API2::Basic::TTF::Vhea
94             vmtx PDF::API3::Compat::API2::Basic::TTF::Vmtx
95            
96             Links are:
97            
98             L L L L L
99             L L L L L
100             L L L L L
101             L L L L L
102             L L L L L
103             L L L L L
104             L L L
105            
106            
107             =head1 INSTANCE VARIABLES
108            
109             Instance variables begin with a space (and have lengths greater than the 4
110             characters which make up table names).
111            
112             =over
113            
114             =item nocsum
115            
116             This is used during output to disable the creation of the file checksum in the
117             head table. For example, during DSIG table creation, this flag will be set to
118             ensure that the file checksum is left at zero.
119            
120             =item fname (R)
121            
122             Contains the filename of the font which this object was read from.
123            
124             =item INFILE (P)
125            
126             The file handle which reflects the source file for this font.
127            
128             =item OFFSET (P)
129            
130             Contains the offset from the beginning of the read file of this particular
131             font directory, thus providing support for TrueType Collections.
132            
133             =back
134            
135             =head1 METHODS
136            
137             =cut
138            
139 1     1   7 use IO::File;
  1         2  
  1         184  
140            
141 1     1   5 use strict;
  1         2  
  1         36  
142 1     1   6 use vars qw(%tables $VERSION $dumper);
  1         2  
  1         50  
143 1     1   5 use Symbol();
  1         1  
  1         206  
144            
145             require 5.006;
146            
147             $VERSION = 0.32; # MJPH 2-OCT-2002 Bug fixes to TTFBuilder, new methods and some
148             # extension table support in Ttopen and Coverage
149             # $VERSION = 0.31; # MJPH 1-JUL-2002 fix read format 12 cmap (bart@cs.pdx.edu)
150             # improve surrogate support in ttfremap
151             # fix return warn to return warn,undef
152             # ensure correct indexToLocFormat
153             # $VERSION = 0.30; # MJPH 28-MAY-2002 add updated release
154             # $VERSION = 0.29; # MJPH 9-APR-2002 update ttfbuilder, sort out surrogates
155             # $VERSION = 0.28; # MJPH 13-MAR-2002 update ttfbuilder, add PDF::API3::Compat::API2::Basic::TTF::Cmap::ms_enc()
156             # $VERSION = 0.27; # MJPH 6-FEB-2002 update ttfbuilder, support no fpgm, no more __DATA__
157             # $VERSION = 0.26; # MJPH 19-SEP-2001 Update ttfbuilder
158             # $VERSION = 0.25; # MJPH 18-SEP-2001 problems in update of head
159             # $VERSION = 0.24; # MJPH 1-AUG-2001 Sort out update
160             # $VERSION = 0.23; # GST 30-MAY-2001 Memory leak fixed
161             # $VERSION = 0.22; # MJPH 09-APR-2001 Ensure all of AAT stuff included
162             # $VERSION = 0.21; # MJPH 23-MAR-2001 Improve Opentype support
163             # $VERSION = 0.20; # MJPH 13-JAN-2001 Add XML output and some of XML input, AAT & OT tables
164             # $VERSION = 0.19; # MJPH 29-SEP-2000 Add cmap::is_unicode, debug makefile.pl
165             # $VERSION = 0.18; # MJPH 21-JUL-2000 Debug Utils::TTF_bininfo
166             # $VERSION = 0.17; # MJPH 16-JUN-2000 Add utf8 support to names
167             # $VERSION = 0.16; # MJPH 26-APR-2000 Mark read tables as read, tidy up POD
168             # $VERSION = 0.15; # MJPH 5-FEB-2000 Ensure right versions released
169             # $VERSION = 0.14; # MJPH 11-SEP-1999 Sort out Unixisms, agian!
170             # $VERSION = 0.13; # MJPH 9-SEP-1999 Add empty, debug update_bbox
171             # $VERSION = 0.12; # MJPH 22-JUL-1999 Add update_bbox
172             # $VERSION = 0.11; # MJPH 7-JUL-1999 Don't store empties in cmaps
173             # $VERSION = 0.10; # MJPH 21-JUN-1999 Use IO::File
174             # $VERSION = 0.09; # MJPH 9-JUN-1999 Add 5.004 require, minor tweeks in cmap
175             # $VERSION = 0.08; # MJPH 19-MAY-1999 Sort out line endings for Unix
176             # $VERSION = 0.07; # MJPH 28-APR-1999 Get the regression tests to work
177             # $VERSION = 0.06; # MJPH 26-APR-1999 Start to add to CVS, correct MANIFEST.SKIP
178             # $VERSION = 0.05; # MJPH 13-APR-1999 See changes for 0.05
179             # $VERSION = 0.04; # MJPH 13-MAR-1999 Tidy up Tarball
180             # $VERSION = 0.03; # MJPH 9-MAR-1999 Move to PDF::API3::Compat::API2::Basic::TTF for CPAN
181             # $VERSION = 0.02; # MJPH 12-FEB-1999 Add support for ' nocsum' for DSIGS
182             # $VERSION = 0.0001;
183            
184             %tables = (
185             'table' => 'PDF::API3::Compat::API2::Basic::TTF::Table',
186             'GDEF' => 'PDF::API3::Compat::API2::Basic::TTF::GDEF',
187             'GPOS' => 'PDF::API3::Compat::API2::Basic::TTF::GPOS',
188             'GSUB' => 'PDF::API3::Compat::API2::Basic::TTF::GSUB',
189             'LTSH' => 'PDF::API3::Compat::API2::Basic::TTF::LTSH',
190             'OS/2' => 'PDF::API3::Compat::API2::Basic::TTF::OS_2',
191             'PCLT' => 'PDF::API3::Compat::API2::Basic::TTF::PCLT',
192             'bsln' => 'PDF::API3::Compat::API2::Basic::TTF::Bsln',
193             'cmap' => 'PDF::API3::Compat::API2::Basic::TTF::Cmap',
194             'cvt ' => 'PDF::API3::Compat::API2::Basic::TTF::Cvt_',
195             'fdsc' => 'PDF::API3::Compat::API2::Basic::TTF::Fdsc',
196             'feat' => 'PDF::API3::Compat::API2::Basic::TTF::Feat',
197             'fmtx' => 'PDF::API3::Compat::API2::Basic::TTF::Fmtx',
198             'fpgm' => 'PDF::API3::Compat::API2::Basic::TTF::Fpgm',
199             'glyf' => 'PDF::API3::Compat::API2::Basic::TTF::Glyf',
200             'hdmx' => 'PDF::API3::Compat::API2::Basic::TTF::Hdmx',
201             'head' => 'PDF::API3::Compat::API2::Basic::TTF::Head',
202             'hhea' => 'PDF::API3::Compat::API2::Basic::TTF::Hhea',
203             'hmtx' => 'PDF::API3::Compat::API2::Basic::TTF::Hmtx',
204             'kern' => 'PDF::API3::Compat::API2::Basic::TTF::Kern',
205             'loca' => 'PDF::API3::Compat::API2::Basic::TTF::Loca',
206             'maxp' => 'PDF::API3::Compat::API2::Basic::TTF::Maxp',
207             'mort' => 'PDF::API3::Compat::API2::Basic::TTF::Mort',
208             'name' => 'PDF::API3::Compat::API2::Basic::TTF::Name',
209             'post' => 'PDF::API3::Compat::API2::Basic::TTF::Post',
210             'prep' => 'PDF::API3::Compat::API2::Basic::TTF::Prep',
211             'prop' => 'PDF::API3::Compat::API2::Basic::TTF::Prop',
212             'vhea' => 'PDF::API3::Compat::API2::Basic::TTF::Vhea',
213             'vmtx' => 'PDF::API3::Compat::API2::Basic::TTF::Vmtx',
214             );
215            
216             # This is special code because I am fed up of every time I x a table in the debugger
217             # I get the whole font printed. Thus substitutes my 3 line change to dumpvar into
218             # the debugger. Clunky, but nice. You are welcome to a copy if you want one.
219            
220             BEGIN {
221 1     1   3 my ($p);
222            
223 1         3 foreach $p (@INC)
224             {
225 12 50       261 if (-f "$p/mydumpvar.pl")
226             {
227 0         0 $dumper = 'mydumpvar.pl';
228 0         0 last;
229             }
230             }
231 1   50     2470 $dumper ||= 'dumpvar.pl';
232             }
233            
234             sub main::dumpValue
235 0     0     { do $dumper; &main::dumpValue; }
  0            
236            
237            
238             =head2 PDF::API3::Compat::API2::Basic::TTF::Font->AddTable($tablename, $class)
239            
240             Adds the given class to be used when representing the given table name. It also
241             'requires' the class for you.
242            
243             =cut
244            
245             sub AddTable
246             {
247 0     0 1   my ($class, $table, $useclass) = @_;
248            
249 0           $tables{$table} = $useclass;
250             # $useclass =~ s|::|/|oig;
251             # require "$useclass.pm";
252             }
253            
254            
255             =head2 PDF::API3::Compat::API2::Basic::TTF::Font->Init
256            
257             For those people who like making fonts without reading them. This subroutine
258             will require all the table code for the various table types for you. Not
259             needed if using PDF::API3::Compat::API2::Basic::TTF::Font::read before using a table.
260            
261             =cut
262            
263             sub Init
264             {
265 0     0 1   my ($class) = @_;
266 0           my ($t);
267            
268 0           foreach $t (keys %tables)
269             {
270 0           $t =~ s|::|/|oig;
271 0           require "$t.pm";
272             }
273             }
274            
275             =head2 PDF::API3::Compat::API2::Basic::TTF::Font->new(%props)
276            
277             Creates a new font object and initialises with the given properties. This is
278             primarily for use when a TTF is embedded somewhere. Notice that the properties
279             are automatically preceded by a space when inserted into the object. This is in
280             order that fields do not clash with tables.
281            
282             =cut
283            
284             sub new
285             {
286 0     0 1   my ($class, %props) = @_;
287 0           my ($self) = {};
288            
289 0           bless $self, $class;
290            
291 0           foreach (keys %props)
292 0           { $self->{" $_"} = $props{$_}; }
293 0           $self;
294             }
295            
296            
297             =head2 PDF::API3::Compat::API2::Basic::TTF::Font->open($fname)
298            
299             Reads the header and directory for the given font file and creates appropriate
300             objects for each table in the font.
301            
302             =cut
303            
304             sub open
305             {
306 0     0 1   my ($class, $fname) = @_;
307 0           my ($fh);
308 0           my ($self) = {};
309            
310 0 0         unless (ref($fname))
311             {
312 0 0         $fh = IO::File->new($fname) or return undef;
313 0           binmode($fh,':raw');
314             } else
315 0           { $fh = $fname; }
316            
317 0           $self->{' INFILE'} = $fh;
318 0           $self->{' fname'} = $fname;
319 0           $self->{' OFFSET'} = 0;
320 0           bless $self, $class;
321            
322 0           $self->read;
323             }
324            
325             =head2 $f->read
326            
327             Reads a Truetype font directory starting from the current location in the file.
328             This has been separated from the C function to allow support for embedded
329             TTFs for example in TTCs. Also reads the C and C tables immediately.
330            
331             =cut
332            
333             sub read
334             {
335 0     0 1   my ($self) = @_;
336 0           my ($fh) = $self->{' INFILE'};
337 0           my ($dat, $i, $ver, $dir_num, $type, $name, $check, $off, $len, $t);
338            
339 0           $fh->seek($self->{' OFFSET'}, 0);
340 0           $fh->read($dat, 12);
341 0           ($ver, $dir_num) = unpack("Nn", $dat);
342 0 0 0       $ver == 1 << 16 # TTF version 1
      0        
343             || $ver == 0x74727565 # support Mac sfnts
344             || $ver == 0x4F54544F # OpenType with diverse Outlines
345             or return undef;
346            
347 0           for ($i = 0; $i < $dir_num; $i++)
348             {
349 0 0         $fh->read($dat, 16) || die "Reading table entry";
350 0           ($name, $check, $off, $len) = unpack("a4NNN", $dat);
351 0 0 0       $self->{$name} = $self->{' PARENT'}->find($self, $name, $check, $off, $len) && next
352             if (defined $self->{' PARENT'});
353 0   0       $type = $tables{$name} || 'PDF::API3::Compat::API2::Basic::TTF::Table';
354 0           $t = $type;
355 0 0         if ($^O eq "MacOS")
356 0           { $t =~ s/^|::/:/oig; }
357             else
358 0           { $t =~ s|::|/|oig; }
359 0           require "$t.pm";
360 0           $self->{$name} = $type->new(PARENT => $self,
361             NAME => $name,
362             INFILE => $fh,
363             OFFSET => $off,
364             LENGTH => $len,
365             CSUM => $check);
366             }
367            
368 0           foreach $t ('head', 'maxp')
369 0 0         { $self->{$t}->read if defined $self->{$t}; }
370            
371 0           $self;
372             }
373            
374            
375             =head2 $f->out($fname [, @tablelist])
376            
377             Writes a TTF file consisting of the tables in tablelist. The list is checked to
378             ensure that only tables that exist are output. (This means that you can't have
379             non table information stored in the font object with key length of exactly 4)
380            
381             In many cases the user simply wants to output all the tables in alphabetical order.
382             This can be done by not including a @tablelist, in which case the subroutine will
383             output all the defined tables in the font in alphabetical order.
384            
385             Returns $f on success and undef on failure, including warnings.
386            
387             All output files must include the C table.
388            
389             =cut
390            
391             sub out
392             {
393 0     0 1   my ($self, $fname, @tlist) = @_;
394 0           my ($fh);
395 0           my ($dat, $numTables, $sRange, $eSel);
396 0           my (%dir, $k, $mloc, $count);
397 0           my ($csum, $lsum, $msum, $loc, $oldloc, $len, $shift);
398            
399 0 0         unless (ref($fname))
400             {
401 0   0       $fh = IO::File->new("+>$fname") || return warn("Unable to open $fname for writing"), undef;
402 0           binmode($fh,':raw');
403             } else
404 0           { $fh = $fname; }
405            
406 0           $self->{' oname'} = $fname;
407 0           $self->{' outfile'} = $fh;
408            
409 0 0         if ($self->{' wantsig'})
    0          
410             {
411 0           $self->{' nocsum'} = 1;
412             # $self->{'head'}{'checkSumAdjustment'} = 0;
413 0           $self->{' tempDSIG'} = $self->{'DSIG'};
414 0           $self->{' tempcsum'} = $self->{'head'}{' CSUM'};
415 0           delete $self->{'DSIG'};
416 0 0 0       @tlist = sort {$self->{$a}{' OFFSET'} <=> $self->{$b}{' OFFSET'}}
  0            
417             grep (length($_) == 4 && defined $self->{$_}, keys %$self) if ($#tlist < 0);
418             }
419             elsif ($#tlist < 0)
420 0           { @tlist = sort keys %$self; }
421            
422 0   0       @tlist = grep(length($_) == 4 && defined $self->{$_}, @tlist);
423 0           $numTables = $#tlist + 1;
424 0 0         $numTables++ if ($self->{' wantsig'});
425            
426 0           ($numTables, $sRange, $eSel, $shift) = PDF::API3::Compat::API2::Basic::TTF::Utils::TTF_bininfo($numTables, 16);
427 0           $dat = pack("Nnnnn", 1 << 16, $numTables, $sRange, $eSel, $shift);
428 0           $fh->print($dat);
429 0           $msum = unpack("%32N*", $dat);
430            
431             # reserve place holders for each directory entry
432 0           foreach $k (@tlist)
433             {
434 0           $dir{$k} = pack("A4NNN", $k, 0, 0, 0);
435 0           $fh->print($dir{$k});
436             }
437            
438 0 0         $fh->print(pack('A4NNN', '', 0, 0, 0)) if ($self->{' wantsig'});
439            
440 0           $loc = $fh->tell();
441 0 0         if ($loc & 3)
442             {
443 0           $fh->print(substr("\000" x 4, $loc & 3));
444 0           $loc += 4 - ($loc & 3);
445             }
446            
447 0           foreach $k (@tlist)
448             {
449 0           $oldloc = $loc;
450 0           $self->{$k}->out($fh);
451 0           $loc = $fh->tell();
452 0           $len = $loc - $oldloc;
453 0 0         if ($loc & 3)
454             {
455 0           $fh->print(substr("\000" x 4, $loc & 3));
456 0           $loc += 4 - ($loc & 3);
457             }
458 0           $fh->seek($oldloc, 0);
459 0           $csum = 0; $mloc = $loc;
  0            
460 0           while ($mloc > $oldloc)
461             {
462 0 0         $count = ($mloc - $oldloc > 4096) ? 4096 : $mloc - $oldloc;
463 0           $fh->read($dat, $count);
464 0           $csum += unpack("%32N*", $dat);
465             # this line ensures $csum stays within 32 bit bounds, clipping as necessary
466 0 0         if ($csum > 0xffffffff) { $csum -= 0xffffffff; $csum--; }
  0            
  0            
467 0           $mloc -= $count;
468             }
469 0           $dir{$k} = pack("A4NNN", $k, $csum, $oldloc, $len);
470 0           $msum += $csum + unpack("%32N*", $dir{$k});
471 0 0         if ($msum > 0xffffffff) { $msum -= 0xffffffff; $msum--; }
  0            
  0            
472 0           $fh->seek($loc, 0);
473             }
474            
475 0 0         unless ($self->{' nocsum'}) # assuming we want a file checksum
    0          
476             {
477             # Now we need to sort out the head table's checksum
478 0 0         if (!defined $dir{'head'})
479             { # you have to have a head table
480 0           $fh->close();
481 0           return warn("No 'head' table to output in $fname"), undef;
482             }
483 0           ($csum, $loc, $len) = unpack("x4NNN", $dir{'head'});
484 0           $fh->seek($loc + 8, 0);
485 0           $fh->read($dat, 4);
486 0           $lsum = unpack("N", $dat);
487 0 0         if ($lsum != 0)
488             {
489 0           $csum -= $lsum;
490 0 0         if ($csum < 0) { $csum += 0xffffffff; $csum++; }
  0            
  0            
491 0           $msum -= $lsum * 2; # twice (in head and in csum)
492 0           while ($msum < 0) { $msum += 0xffffffff; $msum++; }
  0            
  0            
493             }
494 0           $lsum = 0xB1B0AFBA - $msum;
495 0           $fh->seek($loc + 8, 0);
496 0           $fh->print(pack("N", $lsum));
497 0           $dir{'head'} = pack("A4NNN", 'head', $csum, $loc, $len);
498             } elsif ($self->{' wantsig'})
499             {
500 0 0         if (!defined $dir{'head'})
501             { # you have to have a head table
502 0           $fh->close();
503 0           return warn("No 'head' table to output in $fname"), undef;
504             }
505 0           ($csum, $loc, $len) = unpack("x4NNN", $dir{'head'});
506 0           $fh->seek($loc + 8, 0);
507 0           $fh->print(pack("N", 0));
508             # $dir{'head'} = pack("A4NNN", 'head', $self->{' tempcsum'}, $loc, $len);
509             }
510            
511             # Now we can output the directory again
512 0 0         if ($self->{' wantsig'})
513 0           { @tlist = sort @tlist; }
514 0           $fh->seek(12, 0);
515 0           foreach $k (@tlist)
516 0           { $fh->print($dir{$k}); }
517 0 0         $fh->print(pack('A4NNN', '', 0, 0, 0)) if ($self->{' wantsig'});
518 0           $fh->close();
519 0           $self;
520             }
521            
522            
523             =head2 $f->out_xml($filename [, @tables])
524            
525             Outputs the font in XML format
526            
527             =cut
528            
529             sub out_xml
530             {
531 0     0 1   my ($self, $fname, @tlist) = @_;
532 0           my ($fh, $context, $numTables, $k);
533            
534 0           $context->{'indent'} = ' ' x 4;
535            
536 0 0         unless (ref($fname))
537             {
538 0   0       $fh = IO::File->new("+>$fname") || return warn("Unable to open $fname"), undef;
539 0           binmode($fh,':raw');
540             } else
541 0           { $fh = $fname; }
542            
543 0 0         unless (scalar @tlist > 0)
544             {
545 0           @tlist = sort keys %$self;
546 0   0       @tlist = grep(length($_) == 4 && defined $self->{$_}, @tlist);
547             }
548 0           $numTables = $#tlist + 1;
549            
550 0           $context->{'fh'} = $fh;
551 0           $fh->print("\n");
552 0           $fh->print("\n\n");
553            
554 0           foreach $k (@tlist)
555             {
556 0           $fh->print("\n");
557 0           $self->{$k}->out_xml($context, $context->{'indent'});
558 0           $fh->print("
\n");
559             }
560            
561 0           $fh->print("\n");
562 0           $fh->close;
563 0           $self;
564             }
565            
566            
567             =head2 $f->XML_start($context, $tag, %attrs)
568            
569             Handles start messages from the XML parser. Of particular interest to us are and
570             .
571            
572             =cut
573            
574             sub XML_start
575             {
576 0     0 1   my ($self, $context, $tag, %attrs) = @_;
577 0           my ($name, $type, $t);
578            
579 0 0         if ($tag eq 'font')
    0          
580 0           { $context->{'tree'}[-1] = $self; }
581             elsif ($tag eq 'table')
582             {
583 0           $name = $attrs{'name'};
584 0 0         unless (defined $self->{$name})
585             {
586 0   0       $type = $tables{$name} || 'PDF::API3::Compat::API2::Basic::TTF::Table';
587 0           $t = $type;
588 0 0         if ($^O eq "MacOS")
589 0           { $t =~ s/^|::/:/oig; }
590             else
591 0           { $t =~ s|::|/|oig; }
592 0           require "$t.pm";
593 0           $self->{$name} = $type->new('PARENT' => $self, 'NAME' => $name, 'read' => 1);
594             }
595 0           $context->{'receiver'} = ($context->{'tree'}[-1] = $self->{$name});
596             }
597 0           $context;
598             }
599            
600            
601             sub XML_end
602             {
603 0     0 0   my ($self) = @_;
604 0           my ($context, $tag, %attrs) = @_;
605 0           my ($i);
606            
607 0 0 0       return undef unless ($tag eq 'table' && $attrs{'name'} eq 'loca');
608 0 0 0       if (defined $context->{'glyphs'} && $context->{'glyphs'} ne $self->{'loca'}{'glyphs'})
609             {
610 0           for ($i = 0; $i <= $#{$context->{'glyphs'}}; $i++)
  0            
611 0 0         { $self->{'loca'}{'glyphs'}[$i] = $context->{'glyphs'}[$i] if defined $context->{'glyphs'}[$i]; }
612 0           $context->{'glyphs'} = $self->{'loca'}{'glyphs'};
613             }
614 0           return undef;
615             }
616            
617             =head2 $f->update
618            
619             Sends update to all the tables in the font and then resets all the isDirty
620             flags on each table. The data structure in now consistent as a font (we hope).
621            
622             =cut
623            
624             sub update
625             {
626 0     0 1   my ($self) = @_;
627            
628 0     0     $self->tables_do(sub { $_[0]->update; });
  0            
629            
630 0           $self;
631             }
632            
633             =head2 $f->dirty
634            
635             Dirties all the tables in the font
636            
637             =cut
638            
639             sub dirty
640 0     0 1   { $_[0]->tables_do(sub { $_[0]->dirty; }); $_[0]; }
  0     0      
  0            
641            
642             =head2 $f->tables_do(&func)
643            
644             Calls &func for each table in the font. Calls the table in alphabetical sort
645             order as per the order in the directory:
646            
647             &func($table, $name);
648            
649             =cut
650            
651             sub tables_do
652             {
653 0     0 1   my ($self, $func) = @_;
654 0           my ($t);
655            
656 0           foreach $t (sort grep {length($_) == 4} keys %$self)
  0            
657 0           { &$func($self->{$t}, $t); }
658 0           $self;
659             }
660            
661            
662             =head2 $f->release
663            
664             Releases ALL of the memory used by the TTF font and all of its component
665             objects. After calling this method, do B expect to have anything left in
666             the C object.
667            
668             B, that it is important that you call this method on any
669             C object when you wish to destruct it and free up its memory.
670             Internally, we track things in a structure that can result in circular
671             references, and without calling 'C' these will not properly get
672             cleaned up by Perl. Once you've called this method, though, don't expect to be
673             able to do anything else with the C object; it'll have B
674             internal state whatsoever.
675            
676             B As part of the brute-force cleanup done here, this method
677             will throw a warning message whenever unexpected key values are found within
678             the C object. This is done to help ensure that any unexpected
679             and unfreed values are brought to your attention so that you can bug us to keep
680             the module updated properly; otherwise the potential for memory leaks due to
681             dangling circular references will exist.
682            
683             =cut
684            
685             sub release
686             {
687 0     0 1   my ($self) = @_;
688            
689 0 0         return($self) unless(ref $self);
690             # delete stuff that we know we can, here
691            
692 0           my @tofree = map { delete $self->{$_} } keys %{$self};
  0            
  0            
693            
694 0           while (my $item = shift @tofree)
695             {
696 0           my $ref = ref($item);
697 0 0         if (UNIVERSAL::can($item, 'release'))
    0          
    0          
698 0           { $item->release(); }
699             elsif ($ref eq 'ARRAY')
700 0           { push( @tofree, @{$item} ); }
  0            
701             elsif (UNIVERSAL::isa($ref, 'HASH'))
702 0           { release($item); }
703             }
704            
705             # check that everything has gone - it better had!
706 0           foreach my $key (keys %{$self})
  0            
707 0           { warn ref($self) . " still has '$key' key left after release.\n"; }
708             }
709            
710             1;
711            
712             =head1 BUGS
713            
714             Bugs abound aplenty I am sure. There is a lot of code here and plenty of scope.
715             The parts of the code which haven't been implemented yet are:
716            
717             =over 4
718            
719             =item Post
720            
721             Version 4 format types are not supported yet.
722            
723             =item Cmap
724            
725             Format type 2 (MBCS) has not been implemented yet and therefore may cause
726             somewhat spurious results for this table type.
727            
728             =item Kern
729            
730             Only type 0 & type 2 tables are supported (type 1 & type 3 yet to come).
731            
732             =item TTC
733            
734             The current PDF::API3::Compat::API2::Basic::TTF::Font::out method does not support the writing of TrueType
735             Collections.
736            
737             =back
738            
739             In addition there are weaknesses or features of this module library
740            
741             =over 4
742            
743             =item *
744            
745             There is very little (or no) error reporting. This means that if you have
746             garbled data or garbled data structures, then you are liable to generate duff
747             fonts.
748            
749             =item *
750            
751             The exposing of the internal data structures everywhere means that doing
752             radical re-structuring is almost impossible. But it stop the code from becoming
753             ridiculously large.
754            
755             =back
756            
757             Apart from these, I try to keep the code in a state of "no known bugs", which
758             given the amount of testing this code has had, is not a guarantee of high
759             quality, yet.
760            
761             For more details see the appropriate class files.
762            
763             =head1 AUTHOR
764            
765             Martin Hosken Martin_Hosken@sil.org
766            
767             Copyright Martin Hosken 1998.
768            
769             No warranty or expression of effectiveness, least of all regarding anyone's
770             safety, is implied in this software or documentation.
771            
772             =head2 Licensing
773            
774             The Perl TTF module is licensed under the Perl Artistic License.
775