1 package Font::TTF::Glyph;
5 Font::TTF::Glyph - Holds a single glyph's information
9 This is a single glyph description as held in a TT font. On creation only its
10 header is read. Thus you can get the bounding box of each glyph without having
11 to read all the other information.
13 =head1 INSTANCE VARIABLES
15 In addition to the named variables in a glyph header (C<xMin> etc.), there are
16 also all capital instance variables for holding working information, mostly
17 from the location table.
19 The standard attributes each glyph has are:
27 There are also other, derived, instance variables for each glyph which are read
28 when the whole glyph is read (via C<read_dat>):
34 Number of bytes in the hinting instructions (Warning this variable is deprecated,
35 use C<length($g->{'hints'})> instead).
39 The string containing the hinting code for the glyph
43 In addition there are other attribute like instance variables for simple glyphs:
47 For each contour there is:
53 An array of endpoints for each contour in the glyph. There are
54 C<numberOfContours> contours in a glyph. The number of points in a glyph is
55 equal to the highest endpoint of a contour.
59 There are also a number of arrays indexed by point number
65 The flags associated with reading this point. The flags for a point are
66 recalculated for a point when it is C<update>d. Thus the flags are not very
67 useful. The only important bit is bit 0 which indicates whether the point is
68 an 'on' curve point, or an 'off' curve point.
72 The absolute x co-ordinate of the point.
76 The absolute y co-ordinate of the point
82 For composite glyphs there are other variables
88 This holds the component number (not its glyph number) of the component from
89 which the metrics for this glyph should be taken.
93 This is an array of hashes for each component. Each hash has a number of
100 The glyph number of the glyph which comprises this component of the composite.
101 NOTE: In some badly generated fonts, C<glyph> may contain a numerical value
102 but that glyph might not actually exist in the font file. This could
103 occur in any glyph, but is particularly likely for glyphs that have
104 no strokes, such as SPACE, U+00A0 NO-BREAK SPACE, or
105 U+200B ZERO WIDTH SPACE.
109 An array of two arguments which may be an x, y co-ordinate or two attachment
110 points (one on the base glyph the other on the component). See flags for details.
114 The flag for this component
118 A 4 number array for component scaling. This allows stretching, rotating, etc.
119 Note that scaling applies to placement co-ordinates (rather than attachment points)
120 before locating rather than after.
126 This is a generated value which contains the number of components read in for this
131 The private instance variables are:
137 The input file form which to read any information
141 Location relative to the start of the glyf table in the read file
145 The location of the glyf table in the read file
149 This is the number of bytes required by the glyph. It should be kept up to date
150 by calling the C<update> method whenever any of the glyph content changes.
154 Location relative to the start of the glyf table. This variable is only active
155 whilst the output process is going on. It is used to inform the location table
156 where the glyph's location is, since the glyf table is output before the loca
157 table due to alphabetical ordering.
161 This indicates the length of the glyph data when it is output. This more
162 accurately reflects the internal memory form than the C<LEN> variable which
163 only reflects the read file length. The C<OUTLEN> variable is only set after
164 calling C<out> or C<out_dat>.
170 If you want to edit a glyph in some way, then you should read_dat the glyph, then
171 make your changes and then update the glyph or set the $g->{' isdirty'} variable.
172 It is the application's duty to ensure that the following instance variables are
173 correct, from which update will calculate the rest, including the bounding box
179 x, y, flags (only flags bit 0)
183 For components, the numPoints, x, y, endPoints & flags are not required but
184 the following information is required for each component.
186 flag (bits 2, 10, 11, 12)
190 metric (glyph instance variable)
198 use vars qw(%fields @field_info);
199 use Font::TTF::Utils;
200 use Font::TTF::Table;
203 'numberOfContours' => 's',
212 for ($i = 0; $i < $#field_info; $i += 2)
214 ($k, $v, $c) = TTF_Init_Fields($field_info[$i], $c, $field_info[$i + 1]);
215 next unless defined $k && $k ne "";
221 =head1 Font::TTF::Glyph->new(%parms)
223 Creates a new glyph setting various instance variables
229 my ($class, %parms) = @_;
234 foreach $p (keys %parms)
235 { $self->{" $p"} = $parms{$p}; }
236 init unless defined $fields{'xMin'};
243 Reads the header component of the glyph (bounding box, etc.) and also the
244 glyph content, but into a data field rather than breaking it down into
245 its constituent structures. Use read_dat for this.
252 my ($fh) = $self->{' INFILE'};
255 return $self if $self->{' read'};
256 $self->{' read'} = 1;
257 $fh->seek($self->{' LOC'} + $self->{' BASE'}, 0);
258 $fh->read($self->{' DAT'}, $self->{' LEN'});
259 TTF_Read_Fields($self, $self->{' DAT'}, \%fields);
266 Reads the contents of the glyph (components and curves, etc.) from the memory
267 store C<DAT> into structures within the object. Then, to indicate where the
268 master form of the data is, it deletes the C<DAT> instance variable.
275 my ($dat, $num, $max, $i, $flag, $len, $val, $val1, $fp);
277 return $self if (defined $self->{' read'} && $self->{' read'} > 1);
278 $self->read unless $self->{' read'};
279 $dat = $self->{' DAT'};
281 $num = $self->{'numberOfContours'};
284 $self->{'endPoints'} = [unpack("n*", substr($dat, $fp, $num << 1))];
287 foreach (@{$self->{'endPoints'}})
288 { $max = $_ if $_ > $max; }
289 # print STDERR join(",", unpack('C*', $self->{" DAT"}));
290 # printf STDERR ("(%d,%d in %d=%d @ %d)", scalar @{$self->{'endPoints'}}, $max, length($dat), $self->{' LEN'}, $fp);
291 $max++ if (@{$self->{'endPoints'}});
292 $self->{'numPoints'} = $max;
293 $self->{'instLen'} = unpack("n", substr($dat, $fp));
294 $self->{'hints'} = substr($dat, $fp + 2, $self->{'instLen'});
295 $fp += 2 + $self->{'instLen'};
296 # read the flags array
297 for ($i = 0; $i < $max; $i++)
299 $flag = unpack("C", substr($dat, $fp++));
300 $self->{'flags'}[$i] = $flag;
303 $len = unpack("C", substr($dat, $fp++));
307 $self->{'flags'}[$i] = $flag;
312 for ($i = 0; $i < $max; $i++)
314 $flag = $self->{'flags'}[$i];
317 $val = unpack("C", substr($dat, $fp++));
318 $val = -$val unless ($flag & 16);
323 $val = TTF_Unpack("s", substr($dat, $fp));
326 $self->{'x'}[$i] = $i == 0 ? $val : $self->{'x'}[$i - 1] + $val;
329 for ($i = 0; $i < $max; $i++)
331 $flag = $self->{'flags'}[$i];
334 $val = unpack("C", substr($dat, $fp++));
335 $val = -$val unless ($flag & 32);
340 $val = TTF_Unpack("s", substr($dat, $fp));
343 $self->{'y'}[$i] = $i == 0 ? $val : $self->{'y'}[$i - 1] + $val;
350 $flag = 1 << 5; # cheat to get the loop going
351 for ($i = 0; $flag & 32; $i++)
353 ($flag, $self->{'comps'}[$i]{'glyph'}) = unpack("n2", substr($dat, $fp));
355 $self->{'comps'}[$i]{'flag'} = $flag;
356 if ($flag & 1) # ARGS1_AND_2_ARE_WORDS
358 $self->{'comps'}[$i]{'args'} = [TTF_Unpack("s2", substr($dat, $fp))];
362 $self->{'comps'}[$i]{'args'} = [unpack("c2", substr($dat, $fp))];
368 $val = TTF_Unpack("F", substr($dat, $fp));
370 $self->{'comps'}[$i]{'scale'} = [$val, 0, 0, $val];
373 ($val, $val1) = TTF_Unpack("F2", substr($dat, $fp));
375 $self->{'comps'}[$i]{'scale'} = [$val, 0, 0, $val1];
376 } elsif ($flag & 128)
378 $self->{'comps'}[$i]{'scale'} = [TTF_Unpack("F4", substr($dat, $fp))];
381 $self->{'metric'} = $i if ($flag & 512);
383 $self->{'numPoints'} = $i;
384 if ($flag & 256) # HAVE_INSTRUCTIONS
386 $self->{'instLen'} = unpack("n", substr($dat, $fp));
387 $self->{'hints'} = substr($dat, $fp + 2, $self->{'instLen'});
388 $fp += 2 + $self->{'instLen'};
391 return undef if ($fp > length($dat));
392 $self->{' read'} = 2;
399 Writes the glyph data to outfile
405 my ($self, $fh) = @_;
407 $self->read unless $self->{' read'};
408 $self->update if $self->{' isDirty'};
409 $fh->print($self->{' DAT'});
410 $self->{' OUTLEN'} = length($self->{' DAT'});
415 =head2 $g->out_xml($context, $depth)
417 Outputs an XML description of the glyph
423 my ($self, $context, $depth) = @_;
424 my ($addr) = ($self =~ m/\((.+)\)$/o);
427 if ($context->{'addresses'}{$addr})
429 $context->{'fh'}->printf("%s<glyph gid='%s' id_ref='%s'/>\n", $depth, $context->{'gid'}, $addr);
434 $context->{'fh'}->printf("%s<glyph gid='%s' id='%s'>\n", $depth, $context->{'gid'}, $addr);
437 $ndepth = $depth . $context->{'indent'};
439 foreach $k (sort grep {$_ !~ m/^\s/o} keys %{$self})
441 $self->XML_element($context, $ndepth, $k, $self->{$k});
443 $context->{'fh'}->print("$depth</glyph>\n");
444 delete $context->{'done_points'};
451 my ($self, $context, $depth, $key, $val) = @_;
452 my ($fh) = $context->{'fh'};
453 my ($dind) = $depth . $context->{'indent'};
456 if ($self->{'numberOfContours'} >= 0 && ($key eq 'x' || $key eq 'y' || $key eq 'flags'))
458 return $self if ($context->{'done_points'});
459 $context->{'done_points'} = 1;
461 $fh->print("$depth<points>\n");
462 for ($i = 0; $i <= $#{$self->{'flags'}}; $i++)
463 { $fh->printf("%s<point x='%s' y='%s' flags='0x%02X'/>\n", $dind,
464 $self->{'x'}[$i], $self->{'y'}[$i], $self->{'flags'}[$i]); }
465 $fh->print("$depth</points>\n");
467 elsif ($key eq 'hints')
470 $fh->print("$depth<hints>\n");
471 # Font::TTF::Utils::XML_hexdump($context, $depth . $context->{'indent'}, $self->{'hints'});
472 $dat = Font::TTF::Utils::XML_binhint($self->{'hints'}) || "";
473 $dat =~ s/\n(?!$)/\n$depth$context->{'indent'}/mg;
474 $fh->print("$depth$context->{'indent'}$dat");
475 $fh->print("$depth</hints>\n");
478 { return Font::TTF::Table::XML_element(@_); }
486 Generates a C<$self->{'DAT'}> from the internal structures, if the data has
487 been read into structures in the first place. If you are building a glyph
488 from scratch you will need to set the instance variable C<' read'> to 2 (or
489 something > 1) for the update to work.
496 my ($dat, $loc, $len, $flag, $x, $y, $i, $comp, $num);
498 return $self unless (defined $self->{' read'} && $self->{' read'} > 1);
500 $self->{' DAT'} = TTF_Out_Fields($self, \%fields, 10);
501 $num = $self->{'numberOfContours'};
504 $self->{' DAT'} .= pack("n*", @{$self->{'endPoints'}});
505 $len = $self->{'instLen'};
506 $self->{' DAT'} .= pack("n", $len);
507 $self->{' DAT'} .= pack("a" . $len, substr($self->{'hints'}, 0, $len)) if ($len > 0);
508 for ($i = 0; $i < $self->{'numPoints'}; $i++)
510 $flag = $self->{'flags'}[$i] & 1;
513 $x = $self->{'x'}[$i];
514 $y = $self->{'y'}[$i];
517 $x = $self->{'x'}[$i] - $self->{'x'}[$i - 1];
518 $y = $self->{'y'}[$i] - $self->{'y'}[$i - 1];
520 $flag |= 16 if ($x == 0);
521 $flag |= 32 if ($y == 0);
522 if (($flag & 16) == 0 && $x < 256 && $x > -256)
525 $flag |= 16 if ($x >= 0);
527 if (($flag & 32) == 0 && $y < 256 && $y > -256)
530 $flag |= 32 if ($y >= 0);
532 $self->{' DAT'} .= pack("C", $flag); # sorry no repeats
533 $self->{'flags'}[$i] = $flag;
535 for ($i = 0; $i < $self->{'numPoints'}; $i++)
537 $flag = $self->{'flags'}[$i];
538 $x = $self->{'x'}[$i] - (($i == 0) ? 0 : $self->{'x'}[$i - 1]);
539 if (($flag & 18) == 0)
540 { $self->{' DAT'} .= TTF_Pack("s", $x); }
541 elsif (($flag & 18) == 18)
542 { $self->{' DAT'} .= pack("C", $x); }
543 elsif (($flag & 18) == 2)
544 { $self->{' DAT'} .= pack("C", -$x); }
546 for ($i = 0; $i < $self->{'numPoints'}; $i++)
548 $flag = $self->{'flags'}[$i];
549 $y = $self->{'y'}[$i] - (($i == 0) ? 0 : $self->{'y'}[$i - 1]);
550 if (($flag & 36) == 0)
551 { $self->{' DAT'} .= TTF_Pack("s", $y); }
552 elsif (($flag & 36) == 36)
553 { $self->{' DAT'} .= pack("C", $y); }
554 elsif (($flag & 36) == 4)
555 { $self->{' DAT'} .= pack("C", -$y); }
561 for ($i = 0; $i <= $#{$self->{'comps'}}; $i++)
563 $comp = $self->{'comps'}[$i];
564 $flag = $comp->{'flag'} & 7158; # bits 2,10,11,12
565 $flag |= 1 unless ($comp->{'args'}[0] > -129 && $comp->{'args'}[0] < 128
566 && $comp->{'args'}[1] > -129 && $comp->{'args'}[1] < 128);
567 if (defined $comp->{'scale'})
569 if ($comp->{'scale'}[1] == 0 && $comp->{'scale'}[2] == 0)
571 if ($comp->{'scale'}[0] == $comp->{'scale'}[3])
572 { $flag |= 8 unless ($comp->{'scale'}[0] == 0
573 || $comp->{'scale'}[0] == 1); }
580 $flag |= 512 if (defined $self->{'metric'} && $self->{'metric'} == $i);
581 if ($i == $#{$self->{'comps'}})
582 { $flag |= 256 if (defined $self->{'instLen'} && $self->{'instLen'} > 0); }
586 $self->{' DAT'} .= pack("n", $flag);
587 $self->{' DAT'} .= pack("n", $comp->{'glyph'});
588 $comp->{'flag'} = $flag;
591 { $self->{' DAT'} .= TTF_Pack("s2", @{$comp->{'args'}}); }
593 { $self->{' DAT'} .= pack("CC", @{$comp->{'args'}}); }
596 { $self->{' DAT'} .= TTF_Pack("F", $comp->{'scale'}[0]); }
598 { $self->{' DAT'} .= TTF_Pack("F2", $comp->{'scale'}[0], $comp->{'scale'}[3]); }
600 { $self->{' DAT'} .= TTF_Pack("F4", @{$comp->{'scale'}}); }
602 if (defined $self->{'instLen'} && $self->{'instLen'} > 0)
604 $len = $self->{'instLen'};
605 $self->{' DAT'} .= pack("n", $len);
606 $self->{' DAT'} .= pack("a" . $len, substr($self->{'hints'}, 0, $len));
609 my ($olen) = length($self->{' DAT'});
610 $self->{' DAT'} .= ("\000") x (4 - ($olen & 3)) if ($olen & 3);
611 $self->{' OUTLEN'} = length($self->{' DAT'});
612 $self->{' read'} = 2; # changed from 1 to 2 so we don't read_dat() again
613 # we leave numPoints and instLen since maxp stats use this
618 =head2 $g->update_bbox
620 Updates the bounding box for this glyph according to the points in the glyph
627 my ($num, $maxx, $minx, $maxy, $miny, $i, $comp, $x, $y, $compg);
629 return $self unless $self->{' read'} > 1; # only if read_dat done
630 $miny = $minx = 65537; $maxx = $maxy = -65537;
631 $num = $self->{'numberOfContours'};
634 for ($i = 0; $i < $self->{'numPoints'}; $i++)
636 ($x, $y) = ($self->{'x'}[$i], $self->{'y'}[$i]);
638 $maxx = $x if ($x > $maxx);
639 $minx = $x if ($x < $minx);
640 $maxy = $y if ($y > $maxy);
641 $miny = $y if ($y < $miny);
647 foreach $comp (@{$self->{'comps'}})
649 my ($gnx, $gny, $gxx, $gxy);
650 my ($sxx, $sxy, $syx, $syy);
652 my $otherg = $self->{' PARENT'}{'loca'}{'glyphs'}[$comp->{'glyph'}];
653 # work around bad fonts: see documentation for 'comps' above
654 next unless (defined $otherg);
655 $compg = $otherg->read->update_bbox;
656 ($gnx, $gny, $gxx, $gxy) = @{$compg}{'xMin', 'yMin', 'xMax', 'yMax'};
657 if (defined $comp->{'scale'})
659 ($sxx, $sxy, $syx, $syy) = @{$comp->{'scale'}};
660 ($gnx, $gny, $gxx, $gxy) = ($gnx*$sxx+$gny*$syx + $comp->{'args'}[0],
661 $gnx*$sxy+$gny*$syy + $comp->{'args'}[1],
662 $gxx*$sxx+$gxy*$syx + $comp->{'args'}[0],
663 $gxx*$sxy+$gxy*$syy + $comp->{'args'}[1]);
664 } elsif ($comp->{'args'}[0] || $comp->{'args'}[1])
666 $gnx += $comp->{'args'}[0];
667 $gny += $comp->{'args'}[1];
668 $gxx += $comp->{'args'}[0];
669 $gxy += $comp->{'args'}[1];
671 ($gnx, $gxx) = ($gxx, $gnx) if $gnx > $gxx;
672 ($gny, $gxy) = ($gxy, $gny) if $gny > $gxy;
673 $maxx = $gxx if $gxx > $maxx;
674 $minx = $gnx if $gnx < $minx;
675 $maxy = $gxy if $gxy > $maxy;
676 $miny = $gny if $gny < $miny;
679 $self->{'xMax'} = $maxx;
680 $self->{'xMin'} = $minx;
681 $self->{'yMax'} = $maxy;
682 $self->{'yMin'} = $miny;
689 Returns lots of information about a glyph so that the C<maxp> table can update
690 itself. Returns array containing contributions of this glyph to maxPoints, maxContours,
691 maxCompositePoints, maxCompositeContours, maxSizeOfInstructions, maxComponentElements,
692 and maxComponentDepth.
701 $self->read_dat; # make sure we've read some data
702 $res[4] = length($self->{'hints'}) if defined $self->{'hints'};
704 if ($self->{'numberOfContours'} > 0)
706 $res[0] = $self->{'numPoints'};
707 $res[1] = $self->{'numberOfContours'};
708 } elsif ($self->{'numberOfContours'} < 0)
710 for ($i = 0; $i <= $#{$self->{'comps'}}; $i++)
713 $self->{' PARENT'}{'loca'}{'glyphs'}
714 [$self->{'comps'}[$i]{'glyph'}];
716 # work around bad fonts: see documentation for 'comps' above
717 next unless (defined $otherg );
719 @n = $otherg->maxInfo;
721 $res[2] += $n[2] == 0 ? $n[0] : $n[2];
722 $res[3] += $n[3] == 0 ? $n[1] : $n[3];
724 $res[6] = $n[6] + 1 if ($n[6] >= $res[6]);
732 Empties the glyph of all information to the level of not having been read.
733 Useful for saving memory in apps with many glyphs being read
740 my (%keep) = map {(" $_" => 1)} ('LOC', 'OUTLOC', 'PARENT', 'INFILE', 'BASE',
742 map {delete $self->{$_} unless $keep{$_}} keys %$self;
748 =head2 $g->get_points
750 This method creates point information for a compound glyph. The information is
751 stored in the same place as if the glyph was not a compound, but since
752 numberOfContours is negative, the glyph is still marked as being a compound
759 my ($comp, $compg, $nump, $e, $i);
762 return undef unless ($self->{'numberOfContours'} < 0);
764 foreach $comp (@{$self->{'comps'}})
766 $compg = $self->{' PARENT'}{'loca'}{'glyphs'}[$comp->{'glyph'}];
767 # work around bad fonts: see documentation for 'comps' above
768 next unless (defined $compg );
771 for ($i = 0; $i < $compg->{'numPoints'}; $i++)
773 my ($x, $y) = ($compg->{'x'}[$i], $compg->{'y'}[$i]);
774 if (defined $comp->{'scale'})
776 ($x, $y) = ($x * $comp->{'scale'}[0] + $y * $comp->{'scale'}[2],
777 $x * $comp->{'scale'}[1] + $y * $comp->{'scale'}[3]);
779 if (defined $comp->{'args'})
780 { ($x, $y) = ($x + $comp->{'args'}[0], $y + $comp->{'args'}[1]); }
781 push (@{$self->{'x'}}, $x);
782 push (@{$self->{'y'}}, $y);
783 push (@{$self->{'flags'}}, $compg->{'flags'}[$i]);
785 foreach $e (@{$compg->{'endPoints'}})
786 { push (@{$self->{'endPoints'}}, $e + $nump); }
787 $nump += $compg->{'numPoints'};
789 $self->{'numPoints'} = $nump;
796 Returns an array of all the glyph ids that are used to make up this glyph. That
797 is all the compounds and their references and so on. If this glyph is not a
798 compound, then returns an empty array.
800 Please note the warning about bad fonts that reference nonexistant glyphs
801 under INSTANCE VARIABLES above. This function will not attempt to
802 filter out nonexistant glyph numbers.
812 return unless ($self->{'numberOfContours'} < 0);
813 foreach $g (@{$self->{'comps'}})
815 push (@res, $g->{'glyph'});
816 my $otherg = $self->{' PARENT'}{'loca'}{'glyphs'}[$g->{'glyph'}];
817 # work around bad fonts: see documentation for 'comps' above
818 next unless (defined $otherg);
819 my @list = $otherg->get_refs;
833 The instance variables used here are somewhat clunky and inconsistent with
838 C<update> doesn't re-calculate the bounding box or C<numberOfContours>.
844 Martin Hosken Martin_Hosken@sil.org. See L<Font::TTF::Font> for copyright and