File Coverage

blib/lib/Font/TTFMetrics.pm
Criterion Covered Total %
statement 9 529 1.7
branch 0 166 0.0
condition 0 36 0.0
subroutine 3 40 7.5
pod 13 29 44.8
total 25 800 3.1


line stmt bran cond sub pod time code
1             # $Id: TTFMetrics.pm,v 1.4 2003/06/09 13:03:04 malay Exp $
2             # Perl module for Font::TTFMetrics
3             # Author: Malay < curiouser@ccmb.res.in >
4             # Copyright (c) 2003 by Malay. All rights reserved.
5             # You may distribute this module under the same terms as perl itself
6              
7             =head1 NAME
8              
9             Font::TTFMetrics - A parser for the TTF file.
10              
11             =head1 SYNOPSIS
12              
13             use Font::TTFMetrics;
14              
15             my $metrics = Font::TTFMetrics->new("somefont.ttf");
16             my $ascent = $metrics->get_ascent();
17            
18              
19             =head1 DESCRIPTION
20              
21             C encapsulates the font metrics of a true type font
22             file. A true type font file contains several tables which need to be
23             parsed before any useful information could be gathered about the
24             font. There is the excellent module for parsing TTF font in CPAN by
25             Martin Hosken, C. But in my opinion the use of C
26             requires intimate knowledge of TTF font format. This module was
27             written to support the use of TTF in C 2D graphics library in
28             Perl. Three factors prompted me to write this module: first, I
29             required a fast module to access TTF file. Second, all the access
30             required was read-only. Last, I wanted a user friendly, higher level
31             API to access TTF file.
32              
33             Each font file actually contains several informations the most
34             important information is how a particular character will display on
35             screen. The shape of a character (glyph) is determined by a series of
36             points. The points are generally lines or points on curved path. For
37             details see the TTF specification. Remember, the points actually
38             determines the outline of the curve.TTF file stores the glyph shape in
39             the "glyf" table of the font. The first glyph described in this table
40             will be always a particular glyph, called "missing-glyph" which is
41             shown in case the font file doesnot contains the glyph that a software
42             wants.
43              
44             Each character in computer is actually a number. You can find what
45             number corresponds to the character, you can call C on the
46             character. This value is called the ordinal value of the character. If
47             you just use common english typically the number of any character
48             falls between 32-126, commonly called as ASCII. If you use some more
49             extra character not commonly found in key-board like "degree" then
50             your character code will fall between 0-255, commonly called LATIN-1
51             character set. Unicode is a way to use charaters with ordinal values
52             beyond 255. The good thing about it is that the UTF8 encoding in perl
53             works silently in the backdrop and you can intermix characters with
54             any ordinal value. This ofcourse does not mean that you will be able
55             to use character with any ordinal values for display. The font file
56             must contains the corresponding glyph.
57              
58             The way to extract the glyph for a character is done by looking into
59             "cmap" table of the font. This table contains the character ordinal
60             number and a correspoding index. This index is used to look into the
61             "glyf" table to extract the shape of the character. Thar means if you
62             just substitute another index for a particular ordinal number you can
63             actually display a different character, a mechanism known as "glyph
64             substitution". As you can guess there is one more way to display a
65             particular character instead of what if should display in a more font
66             specific manner. If you just add a particular offset to a glyph
67             ordinal value and provide the index for this added value in the "cmap"
68             table, you can generate a completely different glyph. This mechanism
69             works for a particular type of fonts supplied by Microsoft called
70             symbol fonts. Example of these are symbol.ttf and wingding. Both these
71             fonts does not supply any glyphs corresponding to LATIN-1 character
72             sets but with ordinal values in the range of 61472-61695. But notice
73             if you fire up your word-processor and change the font to symbol and
74             type any character on the key board you get a display. For example, if
75             you type A (ordinal value 65) what you get is greek capital
76             alpha. This works this way: as soon as the word-processor find that
77             you are using a symbol font (you can call C method to
78             find that) it just adds 61440 to any character you type and then
79             queries the "cmap" table for the glyph.
80              
81             One more important aspect of using a TTF file is to find the width of
82             a string. The easiest way to find this to query "htmx" table, which
83             contains advanced width of each character, add up all the advance
84             widths of the individual characters in the string and then go look
85             into "kern" table, which contains the kerning value for pair of glyphs
86             add deduct these values from the total width. You need to deduct also
87             the left-side bearing of the first character and the right-side
88             bearing of the last character from the total width.
89              
90             User of this module should keep in mind that all the values
91             returned from this modules are in font-units and should be converted
92             to pixel unit by:
93              
94             fIUnits * pointsize * resolution /(72 * units_per_em)
95              
96             An example from the true type specification at
97             L:
98              
99             A font-feature of 550 units when used with 18 pt on screen (typically
100             72 dpi resolution) will be
101              
102             550 * 18 * 72 / ( 72 * 2048 ) = 4.83 pixels long.
103              
104             Note that the C value is 2048 which is typical for a TTF
105             file. This value can be obtained by calling C call.
106              
107             This module also takes full advantage of the unicode support of
108             Perl. Any strings that you pass to any function call in this module
109             can have unicode built into into it. That means a string like:
110              
111             "Something \x{70ff}" is perfectly valid.
112              
113              
114              
115             =cut
116              
117             package Font::TTFMetrics;
118              
119             $Font::TTFMetrics::VERSION = 0.1;
120              
121 1     1   30785 use IO::File;
  1         23598  
  1         126  
122 1     1   8 use Carp;
  1         1  
  1         55  
123 1     1   5 use strict;
  1         7  
  1         6423  
124              
125             my @glyph_name_index = ();
126             my @post_glyph_name = ();
127             my @mac_glyph_name = ();
128              
129             =head1 CONSTRUCTOR
130              
131             =head2 new()
132              
133             Creates and returns a C object.
134              
135             Usage : my $metrics = Font::TTFMetrics->new($file);
136             Args : $file - TTF filename.
137             Returns : A Font::TTFMetrics object.
138              
139             =cut
140              
141             sub new {
142 0     0 1   my $arg = shift;
143 0   0       my $class = ref($arg) || $arg;
144 0           my $self = {};
145              
146 0           bless $self, $class;
147 0           $self->_init(@_);
148              
149 0           return $self;
150              
151             }
152              
153             sub _init {
154              
155 0     0     my ( $self, @args ) = @_;
156              
157 0 0         unless (@args) {
158 0           croak "Supply filename in Font::TTFMetrics::new()\n";
159             }
160              
161 0           my ($file) = $self->_rearrange( ["FILE"], @args );
162              
163 0           $self->{_fh} = undef;
164 0           $self->{family} = undef;
165 0           $self->{glyphs} = [];
166 0           $self->{tables} = {};
167 0           $self->{platform} = 3;
168 0           $self->{encoding} = 1;
169 0           $self->{subfamily} = undef;
170 0           $self->{glyph_index} = [];
171 0           $self->{advance_width} = [];
172 0           $self->{lsb} = [];
173              
174             # $self->{number_of_glyphs} = undef;
175              
176 0           $self->set_file_handle($file);
177 0           $self->make_directory_entry();
178 0           $self->is_symbol();
179 0           $self->make_ps_name_table();
180 0           $self->make_glyph_index();
181              
182             #print STDERR "After glyph index\n";
183             #$self->make_advance_width();
184 0           $self->process_kern_table();
185             }
186              
187             #sub create_from_file {
188             # my ( $self, @args ) = @_;
189             # my $mod = Pastel::Font::TTF->new();
190             # my ( $path, $file ) = $mod->_rearrange( [ "PATH", "FILE" ], @args );
191             # my $fh;
192              
193             # if ( defined($path) || defined($file) ) {
194              
195             # if ( defined($path) ) {
196             # $mod->set_file_handle($path);
197              
198             # #return $mod;
199             # }
200             # if ( defined($file) ) {
201             # $mod->set_file_handle($file);
202              
203             # #return $mod;
204             # }
205              
206             # }
207             # else {
208             # croak "Supply filename in Pastel::Font::TTF::create_from_file()\n";
209             # }
210             # $mod->make_directory_entry();
211             # $mod->is_symbol();
212              
213             # # print STDERR "before glyph call\n";
214             # #$mod->make_glyph_index();
215             # $mod->make_ps_name_table();
216              
217             # return $mod;
218             #}
219              
220             =head1 METHODS
221              
222             =head2 is_symbol()
223              
224             Returns true if the font is a Symbol font from Microsoft. Remember
225             that Wingding is also a symbol font.
226              
227             Usage : $metrics->is_symbol();
228             Args : Nothing.
229             Returns : True if the font is a Symbol font, false otherwise.
230              
231             =cut
232              
233             sub is_symbol {
234 0     0 1   my $self = shift;
235 0 0         if ( defined( $self->{is_symbol} ) ) {
236 0           return $self->{is_symbol};
237             }
238 0           my $fh = $self->get_file_handle();
239 0           my $buf = "";
240 0           my $add = $self->get_table_address("name");
241 0           seek( $fh, $add, 0 );
242 0           read( $fh, $buf, 6 );
243 0           my ( $num, $offset ) = unpack( "x2nn", $buf );
244              
245             # loop through the name table whether there is an entry of
246             # encoding 0 of platform ID 3. If there is one the font must be a
247             # symbol font. I could not find a better way to do this.
248              
249 0           for ( my $i = 0 ; $i < $num ; $i++ ) {
250 0           read( $fh, $buf, 12 );
251 0           my ( $id, $encoding, $language, $name_id, $length, $string_offset ) =
252             unpack( "n6", $buf );
253 0 0 0       if ( $id == $self->{platform} && $encoding == 0 ) {
254 0           $self->{is_symbol} = 1;
255 0           $self->{encoding} = 0;
256 0           return $self->{is_symbol};
257             }
258             }
259 0           $self->{is_symbol} = 0;
260              
261 0           return $self->{is_symbol};
262             }
263              
264             sub make_directory_entry {
265 0     0 0   my $self = shift;
266 0           my $fh = $self->get_file_handle();
267 0           my $buf = "";
268              
269 0           eval { read( $fh, $buf, 12 ) };
  0            
270 0 0         if ($@) {
271 0           croak "Read error in Pastel::Font::TTF::make_directory_entry\n";
272             }
273              
274 0           my ( $version, $number ) = unpack( "Nn", $buf );
275              
276             #print "Version = $version, Number of tables = $number\n";
277             # print "\nTABLE\tOFFSET\tLENGTH\n";
278              
279 0           for ( my $i = 0 ; $i < $number ; $i++ ) {
280              
281             #print "Inside for\n";
282 0           read( $fh, $buf, 16 );
283 0           my ( $table, $offset, $length ) = unpack( "a4x4NN", $buf );
284 0           $self->{table}->{$table} = $offset;
285              
286             #print "$table\t$offset\t$length\n";
287             }
288              
289             #print $self->{table}->{'OS/2'};
290             }
291              
292             sub get_table_address {
293 0     0 0   my $self = shift;
294 0           my $table_name = shift;
295              
296 0 0         if ( defined( $self->{table}->{$table_name} ) ) {
297 0           return $self->{table}->{$table_name};
298             }
299             else {
300              
301             # croak
302             # "Undefined table address in Font::TTFMetrics::get_table_address()\n";
303 0           return 0;
304             }
305             }
306              
307             =head2 char_width()
308              
309             Returns the advance width of a single character, in font units.
310              
311             Usage : $font->char_width('a');
312             Args : A single perl character. Can be even a unicode.
313             Returns : A scalar value. The width of the character in font units.
314              
315             =cut
316              
317             sub char_width {
318 0     0 1   my ( $self, $char ) = @_;
319 0           my $ord = ord($char);
320 0 0         if ( $self->is_symbol() ) {
321 0           $ord = $ord + 61440;
322             }
323 0           my $index = $self->get_glyph_index($ord);
324 0           return $self->get_advance_width($index);
325              
326             }
327              
328             =head2 string_width()
329              
330             Given a string the function returns the width of the string in font
331             units. The function at present only calculates the advanced width of
332             the each character and deducts the calculated kerning from the whole
333             length. If some one has any better idea then let me know.
334              
335             Usage : $font->string_width("Some string");
336             Args : A perl string. Can be embedded unicode.
337             Returns : A scalar indicating the width of the whole string in font units.
338              
339             =cut
340              
341             sub string_width{
342 0     0 1   my ($self,$string) = @_;
343 0           my @s = split(//, $string);
344            
345 0           my $kern = 0;
346 0           my $width = 0;
347              
348 0           for (my $i = 0; $i <@s; $i++) {
349 0           my $ord = ord($s[$i]);
350 0 0         if ($self->is_symbol()) {
351 0           $ord = $ord + 61440;
352             }
353 0           my $index = $self->get_glyph_index($ord);
354 0           $width = $width + $self->get_advance_width($index);
355 0 0         if ($i < @s -1) {
356 0           my $ord_plus_one = ord($s[$i + 1]);
357 0 0         if ($self->is_symbol()) {
358 0           $ord_plus_one = $ord_plus_one + 61440;
359             }
360 0           my $index_plus_one = $self->get_glyph_index($ord_plus_one);
361 0           $kern = $kern + $self->kern_value($index, $index_plus_one);
362             }
363             }
364 0           my $start_ord = ord ($s[0]);
365 0 0         if ($self->is_symbol()) {
366 0           $start_ord = $start_ord + 61440;
367             }
368 0           my $start_index = $self->get_glyph_index($start_ord);
369             #print STDERR "\n****start index : $start_index\n";
370             #my $lsb = $self->get_lsb($start_index);
371 0           return $width + $kern;
372             }
373              
374             # returns the glyph index for a given chracter ordinal number from the
375             # cmap table. The function first check whether the ordinal number
376             # passed to it lies in the range 0-255. If it is then it simple get
377             # the index number from the $self->{glyph_index} array set by
378             # make_glyph_index(). If the ordinal value is greater than 255 the
379             # function queries the cmap table itself and returns the value.
380              
381             sub get_glyph_index {
382 0     0 0   my $self = shift;
383 0           my $char = shift; # ordinal number of the character
384 0 0         if ( $char < 256 ) {
385 0           return $self->{glyph_index}->[$char];
386             }
387 0           my $buf = "";
388 0           my $fh = $self->get_file_handle();
389 0           my $add = $self->get_table_address('cmap');
390 0           my $offset;
391              
392 0           seek( $fh, $add, 0 );
393 0           read( $fh, $buf, 4 );
394 0           my $num = unpack( "x2n", $buf );
395              
396 0           for ( my $i = 0 ; $i < $num ; $i++ ) {
397 0           read( $fh, $buf, 8 );
398 0           my ( $id, $encoding, $off ) = unpack( "nnN", $buf );
399              
400             #print $id , "\n";
401             #print $encoding , "\n";
402              
403 0 0 0       if ( $id == $self->{platform} && $encoding == $self->{encoding} ) {
404              
405             #print "Match Found ", $id, "\n";
406             # print "Offset: $off\n";
407 0           $offset = $off;
408              
409 0           last;
410             }
411             }
412              
413 0           seek( $fh, $add + $offset, 0 );
414 0           read( $fh, $buf, 6 );
415 0           my ( $format, $length, $version ) = unpack( "nnn", $buf );
416 0           read( $fh, $buf, 8 );
417              
418             #print STDERR "\nlength = $length\n";
419 0           my ( $seg_countX2, $search_range, $entry_selector, $range_shift ) =
420             unpack( "nnnn", $buf );
421 0           my $seg_count = $seg_countX2 / 2;
422              
423             #print STDERR "\n",$seg_count,"\n";
424 0           read( $fh, $buf, 2 * $seg_count );
425 0           my (@end_count) = unpack( "n" x $seg_count, $buf );
426 0           read( $fh, $buf, 2 );
427              
428             #my $reserve_pad = unpack( "n", $buf );
429 0           read( $fh, $buf, 2 * $seg_count );
430 0           my (@start_count) = unpack( "n" x $seg_count, $buf );
431              
432             #print STDERR "\n", "@start_count","\n";
433              
434             #print "Start Count: ", join("\t",@start_count), "\n";
435              
436 0           read( $fh, $buf, 2 * $seg_count );
437 0           my (@id_delta) = unpack( "n" x $seg_count, $buf );
438              
439             #print "idDelta: ", join("\t",@id_delta), "\n";
440              
441 0           read( $fh, $buf, 2 * $seg_count );
442 0           my (@id_range_offset) = unpack( "n" x $seg_count, $buf );
443              
444             #print "idRangeOffset: ", join("\t",@id_range_offset), "\n";
445              
446             #my $num1 = read( $fh, $buf, $length - ( $seg_count * 8 ) - 16 );
447             #my (@glyph_id) = unpack( "n" x ( $num1 / 2 ), $buf );
448             #print STDERR "\n",join("\n",@glyph_id),"\n";
449             #my $i;
450             #my $j;
451 0           my $index;
452 0           my $present =
453             0; # boolean to indicate the char code is actually present or not
454 0           for ( my $i = 0 ; $i < $seg_count ; $i++ ) {
455 0 0 0       if ( $start_count[$i] <= $char && $end_count[$i] >= $char ) {
456 0           $index = $i;
457 0           $present = 1;
458 0           last;
459             }
460              
461             }
462              
463             #print STDERR "\nIndex: ", $index,"\n";
464             #print STDERR "\nId offset: ", $id_range_offset[$index],"\n";
465 0           my $glyph;
466              
467             # If the char code is not there just return the missing glyph
468 0 0         if ( !$present ) {
    0          
469 0           return 0;
470             }
471             elsif ( $id_range_offset[$index] != 0 ) {
472 0           my $glyph_id_index =
473             $id_range_offset[$index] / 2 + ( $char - $start_count[$index] ) -
474             ( $seg_count - $index );
475              
476 0           seek( $fh, $glyph_id_index * 2, 1 );
477 0           read( $fh, $buf, 2 );
478 0           $glyph = unpack( "n", $buf );
479              
480             #print STDERR "is range not 0\n";
481             #print STDERR "\nGlyph : $glyph\n";
482             }
483             else {
484 0           $glyph = ( $id_delta[$index] + $char ) % 65536;
485             }
486              
487 0           return $glyph;
488             }
489              
490             # Look into the cmap table and create and array of 256 glyph
491             # indexes. Should be called only once during the initialization of the
492             # module. This array is used to find quickly the index of a particulr
493             # glyph if the ordinal value of the character lies in the range
494             # 0-255. If the ordinal number in greater than 255 use
495             # get_glyph_index() to get the index of particular glyph.
496              
497             sub make_glyph_index {
498              
499             #print STDERR "**Inside glyph index\n";
500 0     0 0   my $self = shift;
501 0           my $buf;
502             my $offset;
503 0           my $PLATFORM_ID = $self->{platform};
504 0           my $ENCODING_ID = $self->{encoding};
505 0           my $fh = $self->get_file_handle();
506 0           my $cmap = $self->get_table_address("cmap");
507 0           my @glyph_index;
508              
509             #Go there
510 0           seek( $fh, $cmap, 0 );
511              
512             #'cmap' table starts with
513             # USHORT Table version number
514             # USHORT Number of encoding tables
515             # Read 4 bytes
516 0           read( $fh, $buf, 4 );
517              
518             #Get number of tables and skip the version number
519 0           my ($num) = unpack( "x2n", $buf );
520              
521             # Read the tables. There will $num tables
522             # Each one for a specific encoding and platform id
523             # There are three most important id and encoding-
524             # Windows : ID=3 Encoding = 1
525             # Windows symbol : ID=3 Encoding = 0
526             # Mac/Poscript : ID=1 Encoding = 0
527              
528             #Each subtable:
529             # USHORT Platform ID
530             # USHORT Platform specific encoding ID
531             # ULONG Byte ofset from the begining of the 'cmap' table
532              
533 0           for ( my $i = 0 ; $i < $num ; $i++ ) {
534 0           read( $fh, $buf, 8 );
535 0           my ( $id, $encoding, $off ) = unpack( "nnN", $buf );
536              
537             #print $id , "\n";
538             #print $encoding , "\n";
539              
540 0 0 0       if ( $id == $PLATFORM_ID && $encoding == $ENCODING_ID ) {
541              
542             #print "Match Found ", $id, "\n";
543             # print "Offset: $off\n";
544 0           $offset = $off;
545 0           seek( $fh, $cmap + $offset, 0 );
546             }
547             }
548              
549             #Goto the specific table
550              
551             # Mac/Poscript table with encoding 0 use the following format
552             # USHORT format set to 0
553             # USHORT length
554             # USHORT version starts at 0
555             # BYTE glyphIdArray[256] There is no trick here just read the whole
556             # thing as 256 array
557              
558             # If MAC/Postcript table
559 0 0 0       if ( $PLATFORM_ID == "1" && $ENCODING_ID == "0" ) {
560              
561             # Skip the format, length and version information
562 0           read( $fh, $buf, 6 );
563              
564             #print (unpack("nnn", $buf));
565             # Now read the 256 element array directly
566              
567 0           for ( my $i = 0 ; $i < 256 ; $i++ ) {
568 0           read( $fh, $buf, 1 );
569              
570             #print $buf;
571 0           $glyph_index[$i] = unpack( "C", $buf );
572              
573             #print $glyph_index[$i];
574             #print "Char $i\t\t-> Index $glyph_index[$i]\n";
575             }
576              
577             }
578              
579             # Windows table with encoding 1 use the following format FORMAT 4
580             # USHORT format Format number is set to 4.
581             # USHORT length Length in bytes.
582             # USHORT version Version number (starts at 0).
583             # USHORT segCountX2 2 x segCount.
584             # USHORT searchRange 2 x (2**floor(log2(segCount)))
585             # USHORT entrySelector log2(searchRange/2)
586             # USHORT rangeShift 2 x segCount - searchRange
587             # USHORT endCount[segCount] End characterCode for each segment,
588             # last =0xFFFF.
589             # USHORT reservedPad Set to 0.
590             # USHORT startCount[segCount] Start character code for each segment.
591             # USHORT idDelta[segCount] Delta for all character codes in segment.
592             # USHORT idRangeOffset[segCount]Offsets into glyphIdArray or 0
593             # USHORT glyphIdArray[ ] Glyph index array (arbitrary length)
594              
595 0 0         if ( $PLATFORM_ID == 3 ) {
596 0           read( $fh, $buf, 6 );
597 0           my ( $format, $length, $version ) = unpack( "nnn", $buf );
598              
599             #print "Format: $format\tLength: $length\tVersion: $version\n\n";
600 0           read( $fh, $buf, 8 );
601 0           my ( $seg_countX2, $search_range, $entry_selector, $range_shift ) =
602             unpack( "nnnn", $buf );
603 0           my $seg_count = $seg_countX2 / 2;
604              
605             #print "SegcountX2:\t\t$seg_countX2\n";
606             #print "Search Range:\t$search_range\n";
607             #print "Entry:\t$entry_selector\n";
608             #print "Range Shift:\t$range_shift\n";
609              
610 0           read( $fh, $buf, 2 * $seg_count );
611 0           my (@end_count) = unpack( "n" x $seg_count, $buf );
612              
613             #print "EndCount: ", join("\t",@end_count), "\n";
614 0           read( $fh, $buf, 2 );
615 0           my $reserve_pad = unpack( "n", $buf );
616              
617             #print "Reserve Pad: $reserve_pad\n";
618              
619 0           read( $fh, $buf, 2 * $seg_count );
620 0           my (@start_count) = unpack( "n" x $seg_count, $buf );
621              
622             #print "Start Count: ", join("\t",@start_count), "\n";
623              
624 0           read( $fh, $buf, 2 * $seg_count );
625 0           my (@id_delta) = unpack( "n" x $seg_count, $buf );
626              
627             #print "idDelta: ", join("\t",@id_delta), "\n";
628              
629 0           read( $fh, $buf, 2 * $seg_count );
630 0           my (@id_range_offset) = unpack( "n" x $seg_count, $buf );
631              
632             #print "idRangeOffset: ", join("\t",@id_range_offset), "\n";
633              
634 0           my $num = read( $fh, $buf, $length - ( $seg_count * 8 ) - 16 );
635 0           my (@glyph_id) = unpack( "n" x ( $num / 2 ), $buf );
636              
637             #print STDERR "\n",join("\n",@glyph_id),"\n",
638 0           my $i;
639             my $j;
640              
641             #print "Last count:", $end_count[$#end_count], "\n";
642 0           for ( $j = 0 ; $j < $seg_count ; $j++ ) {
643              
644             #for ( $i = $start_count[$j] ; $i <= $end_count[$j] ; $i++ ) {
645 0           for ( $i = $start_count[$j] ; $i < 256 ; $i++ ) {
646              
647             #print $start_count[$j], "****", $end_count[$j], "\n";
648              
649             #if ($end_count[$j] >= $i && $start_count[$j] <= $i){
650             #print "ID RANGE OFFSET $id_range_offset[$j]", "\n";
651 0 0         if ( $id_range_offset[$j] != 0 ) {
652              
653 0           $glyph_index[$i] = $glyph_id[ $id_range_offset[$j] / 2 +
654             ( $i - $start_count[$j] ) - ( $seg_count - $j ) ];
655             }
656             else {
657 0           $glyph_index[$i] = ( $id_delta[$j] + $i ) % 65536;
658              
659             }
660              
661 0 0         if ( !defined( $glyph_index[$i] ) ) {
662              
663             #$glyph_index[$i] = $glyph_id[0];
664 0           $glyph_index[$i] = 0;
665             }
666             }
667             }
668              
669 0           for ( my $i = 0 ; $i < @glyph_index ; $i++ ) {
670 0 0         if ( !defined( $glyph_index[$i] ) ) {
671 0           $glyph_index[$i] = 0;
672             }
673             }
674             }
675 0           $self->{glyph_index} = \@glyph_index;
676              
677             # print STDERR "\n","Number of glyphs:", scalar(@{$self->{glyph_index}}), "\n";
678             # print STDERR "\n","glyphs:", "@{$self->{glyph_index}}", "\n";
679             }
680              
681             sub make_advance_width {
682 0     0 0   my $self = shift;
683 0 0         if ( $self->is_symbol() ) {
684 0           return;
685             }
686 0           my $fh = $self->get_file_handle();
687 0           my $buf;
688              
689             #print STDERR "***", $self->{table}->{"hhea"}, "\n";
690 0           seek( $fh, $self->get_table_address("hhea"), 0 );
691 0           read( $fh, $buf, 36 );
692 0           my ($num) = unpack( "x34n", $buf );
693 0           my $number_of_glyphs = $self->maxp_get_number_of_glyph();
694              
695             #$num = $num > 256 ? 256: $num;
696              
697             #print STDERR "*** ", $num, "\n";
698 0           seek( $fh, $self->get_table_address("hmtx"), 0 );
699 0           read( $fh, $buf, 4 * $num );
700 0           my (@temp) = unpack( "n" x ( 2 * $num ), $buf );
701 0           my @advanced_width;
702             my @lsb;
703 0           my $index = @temp;
704              
705             # if ($num > 256) {
706             # $index = 256 * 2;
707             # }
708 0           for ( my $i = 0 ; $i < $index - 1 ; $i++ ) {
709 0           $advanced_width[@advanced_width] = $temp[$i];
710 0 0         $lsb[@lsb] = $temp[ $i + 1 ] - ( $temp[ $i + 1 ] > 32768 ? 65536 : 0 );
711 0           $i++;
712             }
713              
714 0           my $end_lsb = $number_of_glyphs;
715              
716             # if ($number_of_glyphs > 256) {
717             # $end_lsb = 256;
718             # }else {
719             # $end_lsb = $number_of_glyphs;
720             # }
721 0 0         if ( @lsb < $end_lsb ) {
722 0           my $more_lsb = $end_lsb - scalar(@lsb);
723 0           read( $fh, $buf, 2 * $more_lsb );
724 0           @temp = unpack( "n*", $buf );
725 0           for ( my $i = 0 ; $i < @temp ; $i++ ) {
726 0 0         $lsb[@lsb] = $temp[$i] - ( $temp[$i] > 32768 ? 65536 : 0 );
727             }
728              
729             }
730 0           undef(@temp);
731 0           my @ad;
732             my @l;
733              
734 0           for ( my $i = 0 ; $i < 256 ; $i++ ) {
735 0           my $index = $self->get_glyph_index($i);
736 0 0         if ( $advanced_width[$index] ) {
737 0           $ad[$i] = $advanced_width[$index];
738             }
739             else {
740 0           $ad[$i] = $advanced_width[0];
741             }
742 0 0         if ( defined( $lsb[$index] ) ) {
743              
744 0           $l[$i] = $lsb[$index];
745             }
746             else {
747 0           $l[$i] = $lsb[0];
748             }
749             }
750              
751 0           $self->{advance_width} = \@ad;
752 0           $self->{lsb} = \@l;
753              
754             #print STDERR "\n",$self->get_font_family(),$self->get_subfamily(),"\n";
755             #print STDERR "\nadv:\n@advanced_width", "\n";
756             #print STDERR "\nlsb\n@lsb", "\n";
757             }
758              
759              
760             sub get_lsb {
761 0     0 0   my ($self, $index) = @_;
762              
763 0           my $fh = $self->get_file_handle();
764 0           my $buf;
765              
766 0           seek( $fh, $self->get_table_address("hhea"), 0 );
767 0           read( $fh, $buf, 36 );
768 0           my ($num) = unpack( "x34n", $buf );
769 0           my $number_of_glyphs = $self->maxp_get_number_of_glyph();
770              
771             #$num = $num > 256 ? 256: $num;
772              
773             #print STDERR "*** ", $num, "\n";
774 0           seek( $fh, $self->get_table_address("hmtx"), 0 );
775 0           read( $fh, $buf, 4 * $num );
776 0           my (@temp) = unpack( "n" x ( 2 * $num ), $buf );
777             #my @advanced_width;
778 0           my @lsb;
779 0           my $loop_index = @temp;
780              
781 0           for ( my $i = 0 ; $i < $loop_index - 1 ; $i++ ) {
782             #$advanced_width[@advanced_width] = $temp[$i];
783 0 0         $lsb[@lsb] = $temp[ $i + 1 ] - ( $temp[ $i + 1 ] > 32768 ? 65536 : 0 );
784 0           $i++;
785             }
786              
787 0           my $end_lsb = $number_of_glyphs;
788 0 0         if ( @lsb < $end_lsb ) {
789 0           my $more_lsb = $end_lsb - scalar(@lsb);
790 0           read( $fh, $buf, 2 * $more_lsb );
791 0           @temp = unpack( "n*", $buf );
792 0           for ( my $i = 0 ; $i < @temp ; $i++ ) {
793 0 0         $lsb[@lsb] = $temp[$i] - ( $temp[$i] > 32768 ? 65536 : 0 );
794             }
795              
796             }
797 0 0         return defined ($lsb[$index])? $lsb[$index] : undef;
798              
799              
800             }
801              
802             sub get_advance_width {
803 0     0 0   my $self = shift;
804 0           my $index = shift; # glyph index
805 0           my $fh = $self->get_file_handle();
806 0           my $buf;
807              
808 0           seek( $fh, $self->{table}->{"hhea"}, 0 );
809 0 0         read( $fh, $buf, 36 ) == 36 || die "reading hhea table";
810 0           my ($h_num) = unpack( "x34n", $buf );
811 0           my $num = $h_num;
812              
813 0           seek( $fh, $self->{table}->{"hmtx"}, 0 );
814 0 0         read( $fh, $buf, 4 * $num ) == 4 * $num || die "reading hmtx table";
815 0           my (@h_temp) = unpack( "n" x ( 2 * $num ), $buf );
816              
817             # print "******@h_temp\n";
818 0           my (@advanced_width);
819             #my (@lsb);
820 0           for ( my $i = 0 ; $i < @h_temp - 1 ; $i += 2 ) {
821 0           push ( @advanced_width, $h_temp[$i] );
822             #push ( @lsb, $h_temp[ $i + 1 ] );
823             }
824              
825             #print @advanced_width, "\n";
826             #print @lsb;
827 0 0 0       if ($index > $#advanced_width && $self->is_fixed_pitch()) {
828 0           $index = $#advanced_width;
829             }
830            
831             #if ( $index > @lsb ) { $index = @lsb; }
832 0 0         my $a =
833             $advanced_width[$index] - ( $advanced_width[$index] > 32768 ? 65536 : 0 );
834             #my $l = $lsb[$index] - ( $lsb[$index] > 32768 ? 65536 : 0 );
835              
836             #return $a, $l;
837 0 0         return $a ? $a : undef;
838             }
839              
840             =head2 get_leading()
841              
842             "Leading" is the gap between two lines. The value is present in the
843             C table of the font.
844              
845             Usage : $metrics->get_leading();
846             Args : None.
847             Returns : A scalar Integer.
848              
849             =cut
850              
851             sub get_leading {
852 0     0 1   my $self = shift;
853 0 0         if ( defined( $self->{leading} ) ) {
854 0           return $self->{leading};
855             }
856             else {
857 0           $self->_parse_os2();
858              
859             #$self->{leading} = $self->_get_leading();
860 0           return $self->{leading};
861             }
862             }
863              
864             sub _get_leading {
865 0     0     my $self = shift;
866 0           my $fh = $self->get_file_handle();
867              
868             # Get the adress of the OS/2 table
869 0           my $add = $self->get_table_address('OS/2');
870 0           my $buf;
871              
872             #print $add, "\n";
873              
874             #Leading is sTypoLineGap in OS/2 table
875 0           seek( $fh, $add, 0 );
876 0 0         read( $fh, $buf, 74 ) == 74 || die "reading OS/2 table";
877 0           my ($leading) = unpack( "x72n", $buf );
878              
879             #print join(" ",@panose), "\n";
880             #print $leading, "\n";
881 0 0         return $leading - ( $leading > 32768 ? 65536 : 0 );
882             }
883              
884             =head2 get_units_per_em()
885              
886             Get C of the font. This value is present in the C
887             table of the font and for TTF is generally 2048.
888              
889             Usage : $metrics->get_units_per_em();
890             Args : None.
891             Returns : A scalar integer.
892              
893             =cut
894              
895             sub get_units_per_em {
896 0     0 1   my $self = shift;
897              
898             # Get Headtable address
899 0           my $add = $self->get_table_address("head");
900 0           my $buf;
901 0           my $fh = $self->get_file_handle();
902              
903 0           seek( $fh, $add, 0 );
904              
905 0 0         read( $fh, $buf, 54 ) == 54 || die "reading head table";
906 0           my ( $units_per_em, $index_to_loc ) = unpack( "x18nx30n", $buf );
907              
908             # print "Unit/EM: $units_per_em\tIndex_to_loc: $index_to_loc\n\n";
909              
910 0           return $units_per_em;
911             }
912              
913             =head2 get_ascent()
914              
915             "Ascent" is the distance between the baseline to the top of the glyph.
916              
917             Usage : $metrics->get_ascent();
918             Args : None.
919             Returns : A scalar integer.
920              
921             =cut
922              
923             sub get_ascent {
924 0     0 1   my $self = shift;
925 0 0         if ( defined( $self->{ascent} ) ) {
926 0           return $self->{ascent};
927             }
928             else {
929 0           $self->_parse_os2();
930              
931             #$self->{ascent} = $self->_get_ascent();
932 0           return $self->{ascent};
933             }
934             }
935              
936             sub _get_ascent {
937 0     0     my $self = shift;
938 0           my $fh = $self->get_file_handle();
939              
940             # Get the adress of the OS/2 table
941 0           my $add = $self->get_table_address('OS/2');
942 0           my $buf;
943              
944             #print $add, "\n";
945              
946             # Ascent is is sTypoAscender in OS/2 table
947 0           seek( $fh, $add, 0 );
948 0 0         read( $fh, $buf, 70 ) == 70 || die "reading OS/2 table";
949 0           my ($ascent) = unpack( "x68n", $buf );
950              
951             #print join(" ",@panose), "\n";
952             #print $ascent, "\n";
953 0 0         return $ascent - ( $ascent > 32768 ? 65536 : 0 );
954             }
955              
956             =head2 get_descent()
957              
958             "Descent" is the negative distance from the baseline to the lowest
959             point of the glyph.
960              
961             Usage : $metrics->get_descent();
962             Args : None.
963             Returns : A scalar integer.
964              
965             =cut
966              
967             sub get_descent {
968 0     0 1   my $self = shift;
969 0 0         if ( defined( $self->{descent} ) ) {
970 0           return $self->{descent};
971             }
972             else {
973 0           $self->_parse_os2();
974              
975             #$self->{descent} = $self->_get_descent();
976 0           return $self->{descent};
977             }
978             }
979              
980             sub _parse_os2 {
981 0     0     my $self = shift;
982 0           my $fh = $self->get_file_handle();
983 0           my $add = $self->get_table_address('OS/2');
984 0           my $buf;
985              
986 0           seek( $fh, $add, 0 );
987 0 0         read( $fh, $buf, 74 ) == 74 || die "reading OS/2 table";
988              
989             #my ($ascent, $descent, $leading) =
990             # unpack("x68nnn", $buf);
991 0           my ( $fs, $ascent, $descent, $leading ) = unpack( "x62nx4nnn", $buf );
992              
993             #print STDERR dec2bin($fs) ,"\n";
994 0 0         if ( $fs & 0x20 ) {
995 0           $self->{isbold} = 1;
996             }
997             else {
998 0           $self->{isbold} = 0;
999             }
1000              
1001 0 0         if ( $fs & 0x01 ) {
1002 0           $self->{isitalic} = 1;
1003             }
1004             else {
1005 0           $self->{isitalic} = 0;
1006             }
1007              
1008 0 0         if ( $fs & 0x40 ) {
1009 0           $self->{isregular} = 1;
1010             }
1011             else {
1012 0           $self->{isregular} = 0;
1013             }
1014              
1015 0 0         $self->{ascent} = $ascent - ( $ascent > 32768 ? 65536 : 0 );
1016 0 0         $self->{descent} = $descent - ( $descent > 32768 ? 65536 : 0 );
1017 0 0         $self->{leading} = $leading - ( $leading > 32768 ? 65536 : 0 );
1018             }
1019              
1020             =head2 is_bold()
1021              
1022             Returns true if the font is a bold variation of the font. That means
1023             if you call this function of arial.ttf, it returns false. If you call
1024             this function on arialb.ttf it returns true.
1025              
1026             Usage : $metrics->is_bold()
1027             Args : None.
1028             Returns : True if the font is a bold font, returns false otherwise.
1029              
1030             =cut
1031              
1032             sub is_bold {
1033 0     0 1   my $self = shift;
1034 0 0         if ( defined( $self->{isbold} ) ) {
1035 0           return $self->{isbold};
1036             }
1037             else {
1038 0           $self->_parse_os2();
1039             }
1040 0           return $self->{isbold};
1041             }
1042              
1043             =head2 is_italic()
1044              
1045             Returns true if the font is italic version of the font. Thar means if
1046             you call this function on arialbi.ttf or ariali.ttf it returns true.
1047              
1048             Usage : $metrics->is_italic()
1049             Args : None
1050             Returns : True if the font italic, false otherwise
1051              
1052             =cut
1053              
1054             sub is_italic {
1055 0     0 1   my $self = shift;
1056 0 0         if ( defined( $self->{isitalic} ) ) {
1057 0           return $self->{isitalic};
1058             }
1059             else {
1060 0           $self->_parse_os2();
1061             }
1062 0           return $self->{isitalic};
1063             }
1064              
1065             =head2 get_font_family()
1066              
1067             Returns the family name of the font.
1068              
1069             Usage : $metrics->get_font_family()
1070             Args : None
1071             Returns : A scalar
1072              
1073             =cut
1074              
1075             sub get_font_family {
1076 0     0 1   my $self = shift;
1077 0 0         if ( defined( $self->{family} ) ) {
1078 0           return $self->{family};
1079             }
1080             else {
1081 0           $self->_parse_name_table();
1082             }
1083 0           return $self->{family};
1084             }
1085              
1086             =head2 get_subfamily()
1087              
1088             Reuturns the style variation of the font in text. Note that depending
1089             on this description might actully be pretty confusing. Call
1090             C and/or C to detemine the style. For example
1091             a "demi" version of the font is not "bold" by text. But in display
1092             this in actually bold variation. In this case C will return
1093             true.
1094              
1095             Usage : $metrics->get_subfamily()
1096             Args : None
1097             Returns : A scalar.
1098              
1099             =cut
1100              
1101             sub get_subfamily {
1102 0     0 1   my $self = shift;
1103 0 0         if ( defined( $self->{subfamily} ) ) {
1104 0           return $self->{subfamily};
1105             }
1106             else {
1107 0           $self->_parse_name_table();
1108             }
1109 0           return $self->{subfamily};
1110             }
1111              
1112             sub _parse_name_table {
1113              
1114 0     0     my $self = shift;
1115 0           my $buf;
1116 0           my $fh = $self->get_file_handle();
1117              
1118 0           my $LANGUAGE_ID;
1119 0           my $PLATFORM_ID = $self->{platform};
1120 0           my $ENCODING_ID = $self->{encoding};
1121 0 0 0       if ( $self->{platform} == "1" && $self->{encoding} == "0" ) {
1122 0           $LANGUAGE_ID = 0;
1123             }
1124             else {
1125 0           $LANGUAGE_ID = 1033;
1126             }
1127 0           my $add = $self->get_table_address("name");
1128 0           seek( $fh, $add, 0 );
1129 0           read( $fh, $buf, 6 );
1130 0           my ( $num, $offset ) = unpack( "x2nn", $buf );
1131              
1132             #print "*******NAME : Number of records, $num, Offset: $offset\n";
1133              
1134             my (
1135 0           $copyright_offset, $font_family_name_offset,
1136             $subfamily_offset, $id_offset,
1137             $full_name_offset, $version_string_offset,
1138             $postscript_offset, $trademark_offset
1139             );
1140              
1141             my (
1142 0           $copyright_length, $font_family_length, $subfamily_length,
1143             $id_length, $full_name_length, $version_length,
1144             $postscript_length, $trademark_length
1145             );
1146              
1147 0           for ( my $i = 0 ; $i < $num ; $i++ ) {
1148 0           read( $fh, $buf, 12 );
1149 0           my ( $id, $encoding, $language, $name_id, $length, $string_offset ) =
1150             unpack( "n6", $buf );
1151              
1152             #print "****NAMERECORDS: $id, $encoding, $language, $name_id, $length, $string_offset\n";
1153              
1154 0 0 0       if (
      0        
1155             ( $id == $PLATFORM_ID ) && # Windows??
1156             ( $encoding == $ENCODING_ID ) && #UGL??
1157             ( $language == $LANGUAGE_ID )
1158             )
1159             {
1160 0 0         if ( $name_id == 0 ) { #Copyright
1161 0           $copyright_offset = $string_offset;
1162 0           $copyright_length = $length;
1163             }
1164 0 0         if ( $name_id == 1 ) { # Familyname
1165 0           $font_family_name_offset = $string_offset;
1166 0           $font_family_length = $length;
1167             }
1168 0 0         if ( $name_id == 2 ) { # Subfamily
1169 0           $subfamily_offset = $string_offset;
1170 0           $subfamily_length = $length;
1171             }
1172 0 0         if ( $name_id == 3 ) { # Identifier
1173 0           $id_offset = $string_offset;
1174 0           $id_length = $length;
1175             }
1176 0 0         if ( $name_id == 4 ) { # Full name
1177 0           $full_name_offset = $string_offset;
1178 0           $full_name_length = $length;
1179             }
1180 0 0         if ( $name_id == 5 ) { #version string
1181 0           $version_string_offset = $string_offset;
1182 0           $version_length = $length;
1183             }
1184 0 0         if ( $name_id == 6 ) { # Postscript name
1185 0           $postscript_offset = $string_offset;
1186 0           $postscript_length = $length;
1187             }
1188 0 0         if ( $name_id == 7 ) { # Trademark
1189 0           $trademark_offset = $string_offset;
1190 0           $trademark_length = $length;
1191             }
1192             }
1193              
1194             } # End for loop;
1195              
1196             # Print copyright
1197 0           seek( $fh, $self->get_table_address("name") + $offset + $copyright_offset,
1198             0 );
1199 0           read( $fh, $buf, $copyright_length );
1200              
1201             # print "COPYRIGHT: $buf\n\n";
1202              
1203             # Print familyname
1204 0           seek( $fh,
1205             $self->get_table_address("name") + $offset + $font_family_name_offset,
1206             0 );
1207 0           read( $fh, $buf, $font_family_length );
1208              
1209             #print $s;
1210 0           $self->{family} = $self->_remove_white_space( $buf, $font_family_length );
1211              
1212             #print "\n****", "@char", "*****\n";
1213             #return "@char";
1214             # print "FAMILY: $buf\n\n";
1215              
1216             #Print Subfamily
1217 0           seek( $fh, $self->get_table_address('name') + $offset + $subfamily_offset,
1218             0 );
1219 0           read( $fh, $buf, $subfamily_length );
1220              
1221             #print "SUBFAMILY: $buf\n\n";
1222 0           $self->{subfamily} = $self->_remove_white_space( $buf, $subfamily_length );
1223              
1224             # #Print Identifier
1225             # seek( $fh, $self->get_table_address('name') + $offset + $id_offset, 0 );
1226             # read( $fh, $buf, $id_length );
1227              
1228             # #print "ID: $buf\n\n";
1229              
1230             # #Print Full name
1231             # seek( $fh, $self->get_table_address('name') + $offset + $full_name_offset,
1232             # 0 );
1233             # read( $fh, $buf, $full_name_length );
1234              
1235             # #print "FULL NAME: $buf\n\n";
1236              
1237             # #Print Version string
1238             # seek( $fh,
1239             # $self->get_table_address('name') + $offset + $version_string_offset,
1240             # 0 );
1241             # read( $fh, $buf, $version_length );
1242              
1243             # #print "VERSION: $buf\n\n";
1244              
1245             # #Print Postscript
1246             # seek( $fh, $self->get_table_address('name') + $offset + $postscript_offset,
1247             # 0 );
1248             # read( $fh, $buf, $postscript_length );
1249              
1250             # #print "Postscript: $buf\n\n";
1251              
1252             # #Print Trademark
1253             # seek( $fh, $self->get_table_address('name') + $offset + $trademark_offset,
1254             # 0 );
1255             # read( $fh, $buf, $trademark_length );
1256              
1257             # #print "TRADEMARK: $buf\n\n";
1258              
1259             }
1260              
1261             sub _remove_white_space {
1262 0     0     my $self = shift;
1263 0           my $buf = shift;
1264 0           my $font_family_length = shift;
1265 0           my @char = unpack( "C*", $buf );
1266 0           my $i = $font_family_length;
1267 0           my $s = "";
1268 0           my $j = 0;
1269 0           while ( $j < $i ) {
1270              
1271 0 0         if ( defined $char[ $j + 1 ] ) {
1272 0           $s .= pack( "C", $char[ $j + 1 ] );
1273             }
1274 0           $j += 2;
1275             }
1276 0           return $s;
1277             }
1278              
1279             =head2 is_fixed_pitch()
1280              
1281             Returns true for a fixed-pitched font like courier.
1282              
1283             Usage : $metrics->is_fixed_pitch()
1284             Args : None
1285             Returns : True for a fixed-pitched font, false otherwise
1286              
1287             =cut
1288              
1289             sub is_fixed_pitch {
1290 0     0 1   my $self = shift;
1291 0 0         if ( defined $self->{isfixedpitch} ) {
1292 0           return $self->{isfixedpitch};
1293             }
1294             else {
1295              
1296 0           return 0;
1297             }
1298             }
1299              
1300             sub make_ps_name_table {
1301 0     0 0   my $self = shift;
1302 0           my $fh = $self->get_file_handle();
1303 0           my $address = $self->get_table_address("post");
1304 0           my $buf;
1305 0           seek( $fh, $address, 0 );
1306 0           read( $fh, $buf, 4 );
1307 0           my $format_type = unpack( "N", $buf );
1308              
1309             #print "Format type:$format_type\n";
1310              
1311 0 0         if ( $format_type == 131072 ) { # Test whether 0x00020000
    0          
1312             #print "Microsoft table! \n";
1313 0           read( $fh, $buf, 30 );
1314 0           my ( $italic_angle_m, $italic_angle_f, $fixed_pitched, $num_glyphs ) =
1315             unpack( "nnx4Nx16n", $buf );
1316              
1317             #$italic_angle_m = $italic_angle_m - ($italic_angle_m > 32768 ? 65536 :0);
1318             #print STDERR $fixed_pitched, "\n";
1319 0 0         if ($fixed_pitched) {
1320 0           $self->{isfixedpitch} = 1;
1321             }
1322              
1323             #print $num_glyphs, "\n";
1324 0           my $highest_glyph_index = 0;
1325              
1326 0           for ( my $i = 0 ; $i < $num_glyphs ; $i++ ) {
1327 0           read( $fh, $buf, 2 );
1328 0           $glyph_name_index[$i] = unpack( "n", $buf );
1329 0 0         if ( $highest_glyph_index < $glyph_name_index[$i] ) {
1330 0           $highest_glyph_index = $glyph_name_index[$i];
1331             }
1332             }
1333              
1334 0 0         if ( $highest_glyph_index > 257 ) {
1335 0           $highest_glyph_index -= 257;
1336             }
1337              
1338 0           for ( my $i = 0 ; $i < $highest_glyph_index ; $i++ ) {
1339 0           read( $fh, $buf, 1 );
1340 0           my $length = unpack( "C", $buf );
1341 0           read( $fh, $buf, $length );
1342 0           $post_glyph_name[$i] = pack( "C*", unpack( "C*", $buf ) );
1343              
1344             #print $post_glyph_name[$i], "\n";
1345             }
1346              
1347             }
1348             elsif ( $format_type == 131077 ) {
1349              
1350             #Do Nothing
1351             }
1352             }
1353              
1354             sub make_mac_glyph_name {
1355 0     0 0   @mac_glyph_name = (
1356             ".notdef", "null", "CR", "space",
1357             "exclam", # 4
1358             "quotedbl", # 5
1359             "numbersign", # 6
1360             "dollar", # 7
1361             "percent", # 8
1362             "ampersand", # 9
1363             "quotesingle", # 10
1364             "parenleft", # 11
1365             "parenright", # 12
1366             "asterisk", # 13
1367             "plus", # 14
1368             "comma", # 15
1369             "hyphen", # 16
1370             "period", # 17
1371             "slash", # 18
1372             "zero", # 19
1373             "one", # 20
1374             "two", # 21
1375             "three", # 22
1376             "four", # 23
1377             "five", # 24
1378             "six", # 25
1379             "seven", # 26
1380             "eight", # 27
1381             "nine", # 28
1382             "colon", # 29
1383             "semicolon", # 30
1384             "less", # 31
1385             "equal", # 32
1386             "greater", # 33
1387             "question", # 34
1388             "at", # 35
1389             "A", # 36
1390             "B", # 37
1391             "C", # 38
1392             "D", # 39
1393             "E", # 40
1394             "F", # 41
1395             "G", # 42
1396             "H", # 43
1397             "I", # 44
1398             "J", # 45
1399             "K", # 46
1400             "L", # 47
1401             "M", # 48
1402             "N", # 49
1403             "O", # 50
1404             "P", # 51
1405             "Q", # 52
1406             "R", # 53
1407             "S", # 54
1408             "T", # 55
1409             "U", # 56
1410             "V", # 57
1411             "W", # 58
1412             "X", # 59
1413             "Y", # 60
1414             "Z", # 61
1415             "bracketleft", # 62
1416             "backslash", # 63
1417             "bracketright", # 64
1418             "asciicircum", # 65
1419             "underscore", # 66
1420             "grave", # 67
1421             "a", # 68
1422             "b", # 69
1423             "c", # 70
1424             "d", # 71
1425             "e", # 72
1426             "f", # 73
1427             "g", # 74
1428             "h", # 75
1429             "i", # 76
1430             "j", # 77
1431             "k", # 78
1432             "l", # 79
1433             "m", # 80
1434             "n", # 81
1435             "o", # 82
1436             "p", # 83
1437             "q", # 84
1438             "r", # 85
1439             "s", # 86
1440             "t", # 87
1441             "u", # 88
1442             "v", # 89
1443             "w", # 90
1444             "x", # 91
1445             "y", # 92
1446             "z", # 93
1447             "braceleft", # 94
1448             "bar", # 95
1449             "braceright", # 96
1450             "asciitilde", # 97
1451             "Adieresis", # 98
1452             "Aring", # 99
1453             "Ccedilla", # 100
1454             "Eacute", # 101
1455             "Ntilde", # 102
1456             "Odieresis", # 103
1457             "Udieresis", # 104
1458             "aacute", # 105
1459             "agrave", # 106
1460             "acircumflex", # 107
1461             "adieresis", # 108
1462             "atilde", # 109
1463             "aring", # 110
1464             "ccedilla", # 111
1465             "eacute", # 112
1466             "egrave", # 113
1467             "ecircumflex", # 114
1468             "edieresis", # 115
1469             "iacute", # 116
1470             "igrave", # 117
1471             "icircumflex", # 118
1472             "idieresis", # 119
1473             "ntilde", # 120
1474             "oacute", # 121
1475             "ograve", # 122
1476             "ocircumflex", # 123
1477             "odieresis", # 124
1478             "otilde", # 125
1479             "uacute", # 126
1480             "ugrave", # 127
1481             "ucircumflex", # 128
1482             "udieresis", # 129
1483             "dagger", # 130
1484             "degree", # 131
1485             "cent", # 132
1486             "sterling", # 133
1487             "section", # 134
1488             "bullet", # 135
1489             "paragraph", # 136
1490             "germandbls", # 137
1491             "registered", # 138
1492             "copyright", # 139
1493             "trademark", # 140
1494             "acute", # 141
1495             "dieresis", # 142
1496             "notequal", # 143
1497             "AE", # 144
1498             "Oslash", # 145
1499             "infinity", # 146
1500             "plusminus", # 147
1501             "lessequal", # 148
1502             "greaterequal", # 149
1503             "yen", # 150
1504             "mu", # 151
1505             "partialdiff", # 152
1506             "summation", # 153
1507             "product", # 154
1508             "pi", # 155
1509             "integral'", # 156
1510             "ordfeminine", # 157
1511             "ordmasculine", # 158
1512             "Omega", # 159
1513             "ae", # 160
1514             "oslash", # 161
1515             "questiondown", # 162
1516             "exclamdown", # 163
1517             "logicalnot", # 164
1518             "radical", # 165
1519             "florin", # 166
1520             "approxequal", # 167
1521             "increment", # 168
1522             "guillemotleft", # 169
1523             "guillemotright", #170
1524             "ellipsis", # 171
1525             "nbspace", # 172
1526             "Agrave", # 173
1527             "Atilde", # 174
1528             "Otilde", # 175
1529             "OE", # 176
1530             "oe", # 177
1531             "endash", # 178
1532             "emdash", # 179
1533             "quotedblleft", # 180
1534             "quotedblright", # 181
1535             "quoteleft", # 182
1536             "quoteright", # 183
1537             "divide", # 184
1538             "lozenge", # 185
1539             "ydieresis", # 186
1540             "Ydieresis", # 187
1541             "fraction", # 188
1542             "currency", # 189
1543             "guilsinglleft", # 190
1544             "guilsinglright", #191
1545             "fi", # 192
1546             "fl", # 193
1547             "daggerdbl", # 194
1548             "middot", # 195
1549             "quotesinglbase", #196
1550             "quotedblbase", # 197
1551             "perthousand", # 198
1552             "Acircumflex", # 199
1553             "Ecircumflex", # 200
1554             "Aacute", # 201
1555             "Edieresis", # 202
1556             "Egrave", # 203
1557             "Iacute", # 204
1558             "Icircumflex", # 205
1559             "Idieresis", # 206
1560             "Igrave", # 207
1561             "Oacute", # 208
1562             "Ocircumflex", # 209
1563             "", # 210
1564             "Ograve", # 211
1565             "Uacute", # 212
1566             "Ucircumflex", # 213
1567             "Ugrave", # 214
1568             "dotlessi", # 215
1569             "circumflex", # 216
1570             "tilde", # 217
1571             "overscore", # 218
1572             "breve", # 219
1573             "dotaccent", # 220
1574             "ring", # 221
1575             "cedilla", # 222
1576             "hungarumlaut", # 223
1577             "ogonek", # 224
1578             "caron", # 225
1579             "Lslash", # 226
1580             "lslash", # 227
1581             "Scaron", # 228
1582             "scaron", # 229
1583             "Zcaron", # 230
1584             "zcaron", # 231
1585             "brokenbar", # 232
1586             "Eth", # 233
1587             "eth", # 234
1588             "Yacute", # 235
1589             "yacute", # 236
1590             "Thorn", # 237
1591             "thorn", # 238
1592             "minus", # 239
1593             "multiply", # 240
1594             "onesuperior", # 241
1595             "twosuperior", # 242
1596             "threesuperior", # 243
1597             "onehalf", # 244
1598             "onequarter", # 245
1599             "threequarters", # 246
1600             "franc", # 247
1601             "Gbreve", # 248
1602             "gbreve", # 249
1603             "Idot", # 250
1604             "Scedilla", # 251
1605             "scedilla", # 252
1606             "Cacute", # 253
1607             "cacute", # 254
1608             "Ccaron", # 255
1609             "ccaron", # 256
1610             "" # 257
1611             );
1612             }
1613              
1614             sub get_glyph_name {
1615 0     0 0   my $index = shift;
1616 0 0         if ( $glyph_name_index[$index] > 257 ) {
1617              
1618             #print $post_glyph_name[$glyph_name_index[$index] -258], "******\n";
1619 0           return $post_glyph_name[ $glyph_name_index[$index] - 258 ];
1620             }
1621             else {
1622              
1623             #print $glyph_name_index[$index], "*****\n";
1624             #print $mac_glyph_name[$glyph_name_index[$index]], "******\n";
1625             #print $mac_glyph_name[3], "*****\n";
1626 0           return $mac_glyph_name[ $glyph_name_index[$index] ];
1627             }
1628             }
1629              
1630             sub get_panose {
1631 0     0 0   my $self = shift;
1632 0           my $buf;
1633 0           my $add = $self->get_table_address('OS/2');
1634 0           my $fh = $self->get_file_handle();
1635 0           seek( $fh, $add, 0 );
1636 0           read( $fh, $buf, 42 );
1637              
1638             #Throw away first 32 bytes and take last 10
1639              
1640 0           my (@panose) = unpack( "x32c10", $buf );
1641 0           return @panose;
1642             }
1643              
1644              
1645              
1646             sub kern_value{
1647 0     0 0   my ($self,$left, $right) = @_;
1648 0 0         unless ($self->{kern}) {
1649 0           return 0;
1650             }
1651 0 0         if (exists ($self->{kern}->{$left}->{$right}) ) {
1652 0           return $self->{kern}->{$left}->{$right};
1653             }else {
1654 0           return 0;
1655             }
1656             }
1657              
1658              
1659             sub process_kern_table {
1660 0     0 0   my $self = shift;
1661 0           my $buf;
1662              
1663             #print STDERR $self->get_font_family(), "\n";
1664             #my $s = "";
1665 0 0         unless ( defined( $self->get_table_address("kern") ) ) {
1666 0           return 0;
1667             }
1668 0           my $add = $self->get_table_address("kern");
1669 0           my $fh = $self->get_file_handle();
1670 0           my %kern;
1671              
1672 0           seek( $fh, $add, 0 );
1673 0           read( $fh, $buf, 4 );
1674 0           my $num_of_tables = unpack( "x2n", $buf );
1675              
1676             #print $num_of_tables, "\n";
1677              
1678 0           for ( my $i = 0 ; $i < $num_of_tables ; $i++ ) {
1679 0           read( $fh, $buf, 4 );
1680 0           my $length = unpack( "x2n", $buf );
1681 0           read( $fh, $buf, 2 );
1682 0           my $coverage = unpack( "n", $buf );
1683 0           my $format = $coverage >> 8;
1684              
1685             #print $format, "\n";
1686              
1687 0 0 0       if ( ( $format == 0 ) && ( ( $coverage & 1 ) != 0 ) ) {
1688              
1689             #print "FORMAT 0\n";
1690 0           read( $fh, $buf, 2 );
1691 0           my $npairs = unpack( "n", $buf );
1692              
1693             #print $npairs, "\n";
1694 0           read( $fh, $buf, 6 );
1695              
1696 0           for ( my $j = 0 ; $j < $npairs ; $j++ ) {
1697 0           read( $fh, $buf, 4 );
1698              
1699             # my $right_and_left = unpack("N", $buf);
1700 0           my ( $left, $right ) = unpack( "nn", $buf );
1701 0 0         if ( $left > 255 ) {
1702 0           last;
1703             }
1704 0           read( $fh, $buf, 2 );
1705 0           my $kern_data = unpack( "n", $buf );
1706 0 0         $kern_data = $kern_data - ( $kern_data > 32768 ? 65536 : 0 );
1707              
1708             # $kern_data = $kern_data * ( -1);
1709             # if(exists($kern_to_print{$left})){
1710             # $s .= write_kern_data($left, $right, $kern_data);
1711             # }
1712 0           $kern{$left}->{$right} = $kern_data;
1713              
1714             #print STDERR $left,"\t",$right, "\t", $kern_data,"\n";
1715             #print get_glyph_name($left), ":", get_glyph_name($right);
1716             #print "$right_and_left ";
1717              
1718             # $kern{$right_and_left} = $kern_data;
1719             #print $kern_data, "\n";
1720              
1721             }
1722             }
1723             else {
1724 0           read( $fh, $buf, $length - 6 );
1725             }
1726             }
1727 0           $self->{kern} = \%kern;
1728              
1729             #return $s;
1730             }
1731              
1732             sub DESTROY {
1733 0     0     my $self = shift;
1734 0           close $self->{_fh};
1735             }
1736              
1737              
1738              
1739              
1740              
1741             sub set_file_handle {
1742 0     0 0   my $self = shift;
1743 0           my $path = shift;
1744 0           my $fh = IO::File->new();
1745              
1746 0 0         if ( $fh->open("< $path") ) {
1747 0           binmode($fh);
1748 0           $self->{_fh} = $fh;
1749             }
1750             else {
1751 0           croak "Could not open $path in Pastel::Font::TTF::set_file_handle\n";
1752             }
1753              
1754             }
1755              
1756             sub get_file_handle {
1757 0     0 0   my $self = shift;
1758 0 0         if ( defined( $self->{_fh} ) ) {
1759 0           return $self->{_fh};
1760             }
1761             else {
1762 0           return 0;
1763             }
1764             }
1765              
1766             sub _rearrange {
1767              
1768 0     0     my ( $self, $order, @param ) = @_;
1769              
1770 0 0         return unless @param;
1771 0 0 0       return @param unless ( defined( $param[0] ) && $param[0] =~ /^-/ );
1772              
1773 0           for ( my $i = 0 ; $i < @param ; $i += 2 ) {
1774 0           $param[$i] =~ s/^\-//;
1775 0           $param[$i] =~ tr/a-z/A-Z/;
1776             }
1777              
1778             # Now we'll convert the @params variable into an associative array.
1779 0           local ($^W) = 0; # prevent "odd number of elements" warning with -w.
1780 0           my (%param) = @param;
1781              
1782 0           my (@return_array);
1783              
1784             # What we intend to do is loop through the @{$order} variable,
1785             # and for each value, we use that as a key into our associative
1786             # array, pushing the value at that key onto our return array.
1787             my ($key);
1788              
1789 0           foreach $key ( @{$order} ) {
  0            
1790 0           my ($value) = $param{$key};
1791 0           delete $param{$key};
1792 0           push ( @return_array, $value );
1793             }
1794              
1795             # print "\n_rearrange() after processing:\n";
1796             # my $i; for ($i=0;$i<@return_array;$i++) { printf "%20s => %s\n", ${$order}[$i], $return_array[$i]; } ;
1797              
1798 0           return (@return_array);
1799             }
1800              
1801             sub maxp_get_number_of_glyph {
1802 0     0 0   my $self = shift;
1803 0           my $fh = $self->get_file_handle();
1804 0           my $buf;
1805 0           seek( $fh, $self->get_table_address("maxp"), 0 );
1806 0           read( $fh, $buf, 6 );
1807 0           my ($num_glyph) = unpack( "x4n", $buf );
1808 0           return $num_glyph;
1809              
1810             }
1811              
1812             =head1 SEE ALSO
1813              
1814             L, L.
1815              
1816             =head1 COPYRIGHTS
1817              
1818             Copyright (c) 2003 by Malay . All rights reserved.
1819              
1820             This program is free software; you can redistribute it and/or modify
1821             it under the same terms as Perl itself.
1822              
1823             =cut
1824              
1825             1;