|
Packit |
5d935b |
package Font::TTF::Kern;
|
|
Packit |
5d935b |
|
|
Packit |
5d935b |
=head1 NAME
|
|
Packit |
5d935b |
|
|
Packit |
5d935b |
Font::TTF::Kern - Kerning tables
|
|
Packit |
5d935b |
|
|
Packit |
5d935b |
=head1 DESCRIPTION
|
|
Packit |
5d935b |
|
|
Packit |
5d935b |
Kerning tables are held as an ordered collection of subtables each giving
|
|
Packit |
5d935b |
incremental information regarding the kerning of various pairs of glyphs.
|
|
Packit |
5d935b |
|
|
Packit |
5d935b |
The basic structure of the kerning data structure is:
|
|
Packit |
5d935b |
|
|
Packit |
5d935b |
$kern = $f->{'kern'}{'tables'}[$tnum]{'kerns'}{$leftnum}{$rightnum};
|
|
Packit |
5d935b |
|
|
Packit |
5d935b |
Due to the possible complexity of some kerning tables the above information
|
|
Packit |
5d935b |
is insufficient. Reference also needs to be made to the type of the table and
|
|
Packit |
5d935b |
the coverage field.
|
|
Packit |
5d935b |
|
|
Packit |
5d935b |
=head1 INSTANCE VARIABLES
|
|
Packit |
5d935b |
|
|
Packit |
5d935b |
The instance variables for a kerning table are relatively straightforward.
|
|
Packit |
5d935b |
|
|
Packit |
5d935b |
=over 4
|
|
Packit |
5d935b |
|
|
Packit |
5d935b |
=item Version
|
|
Packit |
5d935b |
|
|
Packit |
5d935b |
Version number of the kerning table
|
|
Packit |
5d935b |
|
|
Packit |
5d935b |
=item Num
|
|
Packit |
5d935b |
|
|
Packit |
5d935b |
Number of subtables in the kerning table
|
|
Packit |
5d935b |
|
|
Packit |
5d935b |
=item tables
|
|
Packit |
5d935b |
|
|
Packit |
5d935b |
Array of subtables in the kerning table
|
|
Packit |
5d935b |
|
|
Packit |
5d935b |
Each subtable has a number of instance variables.
|
|
Packit |
5d935b |
|
|
Packit |
5d935b |
=over 4
|
|
Packit |
5d935b |
|
|
Packit |
5d935b |
=item kern
|
|
Packit |
5d935b |
|
|
Packit |
5d935b |
A two level hash array containing kerning values. The indexing is left
|
|
Packit |
5d935b |
is via left class and right class. It may seem using hashes is strange,
|
|
Packit |
5d935b |
but most tables are not type 2 and this method saves empty array values.
|
|
Packit |
5d935b |
|
|
Packit |
5d935b |
=item type
|
|
Packit |
5d935b |
|
|
Packit |
5d935b |
Stores the table type. Only type 0 and type 2 tables are specified for
|
|
Packit |
5d935b |
TrueType so far.
|
|
Packit |
5d935b |
|
|
Packit |
5d935b |
=item coverage
|
|
Packit |
5d935b |
|
|
Packit |
5d935b |
A bit field of coverage information regarding the kerning value. See the
|
|
Packit |
5d935b |
TrueType specification for details.
|
|
Packit |
5d935b |
|
|
Packit |
5d935b |
=item Version
|
|
Packit |
5d935b |
|
|
Packit |
5d935b |
Contains the version number of the table.
|
|
Packit |
5d935b |
|
|
Packit |
5d935b |
=item Num
|
|
Packit |
5d935b |
|
|
Packit |
5d935b |
Number of kerning pairs in this type 0 table.
|
|
Packit |
5d935b |
|
|
Packit |
5d935b |
=item left
|
|
Packit |
5d935b |
|
|
Packit |
5d935b |
An array indexed by glyph - left_first which returns a class number for
|
|
Packit |
5d935b |
the glyph in type 2 tables.
|
|
Packit |
5d935b |
|
|
Packit |
5d935b |
=item right
|
|
Packit |
5d935b |
|
|
Packit |
5d935b |
An array indexed by glyph - right_first which returns a class number for
|
|
Packit |
5d935b |
the glyph in type 2 tables.
|
|
Packit |
5d935b |
|
|
Packit |
5d935b |
=item left_first
|
|
Packit |
5d935b |
|
|
Packit |
5d935b |
the glyph number of the first element in the left array for type 2 tables.
|
|
Packit |
5d935b |
|
|
Packit |
5d935b |
=item right_first
|
|
Packit |
5d935b |
|
|
Packit |
5d935b |
the glyph number of the first element in the right array for type 2 tables.
|
|
Packit |
5d935b |
|
|
Packit |
5d935b |
=item num_left
|
|
Packit |
5d935b |
|
|
Packit |
5d935b |
Number of left classes
|
|
Packit |
5d935b |
|
|
Packit |
5d935b |
=item num_right
|
|
Packit |
5d935b |
|
|
Packit |
5d935b |
Number of right classes
|
|
Packit |
5d935b |
|
|
Packit |
5d935b |
=back
|
|
Packit |
5d935b |
|
|
Packit |
5d935b |
=back
|
|
Packit |
5d935b |
|
|
Packit |
5d935b |
=head1 METHODS
|
|
Packit |
5d935b |
|
|
Packit |
5d935b |
=cut
|
|
Packit |
5d935b |
|
|
Packit |
5d935b |
use strict;
|
|
Packit |
5d935b |
use vars qw(@ISA);
|
|
Packit |
5d935b |
use Font::TTF::Utils;
|
|
Packit |
5d935b |
use Font::TTF::Table;
|
|
Packit |
5d935b |
use Font::TTF::Kern::Subtable;
|
|
Packit |
5d935b |
|
|
Packit |
5d935b |
@ISA = qw(Font::TTF::Table);
|
|
Packit |
5d935b |
my @subtables = qw(OrderedList StateTable ClassArray CompactClassArray);
|
|
Packit |
5d935b |
|
|
Packit |
5d935b |
=head2 $t->read
|
|
Packit |
5d935b |
|
|
Packit |
5d935b |
Reads the whole kerning table into structures
|
|
Packit |
5d935b |
|
|
Packit |
5d935b |
=cut
|
|
Packit |
5d935b |
|
|
Packit |
5d935b |
sub read
|
|
Packit |
5d935b |
{
|
|
Packit |
5d935b |
my ($self) = @_;
|
|
Packit |
5d935b |
$self->SUPER::read or return $self;
|
|
Packit |
5d935b |
|
|
Packit |
5d935b |
my ($fh) = $self->{' INFILE'};
|
|
Packit |
5d935b |
my ($dat, $i, $numt, $len, $cov, $t);
|
|
Packit |
5d935b |
|
|
Packit |
5d935b |
$fh->read($dat, 4);
|
|
Packit |
5d935b |
($self->{'Version'}, $numt) = unpack("n2", $dat);
|
|
Packit |
5d935b |
if ($self->{'Version'} > 0)
|
|
Packit |
5d935b |
{
|
|
Packit |
5d935b |
$fh->read($dat, 4, 4);
|
|
Packit |
5d935b |
($self->{'Version'}, $numt) = TTF_Unpack("vL", $dat);
|
|
Packit |
5d935b |
}
|
|
Packit |
5d935b |
$self->{'Num'} = $numt;
|
|
Packit |
5d935b |
|
|
Packit |
5d935b |
for ($i = 0; $i < $numt; $i++)
|
|
Packit |
5d935b |
{
|
|
Packit |
5d935b |
if ($self->{'Version'} > 0)
|
|
Packit |
5d935b |
{
|
|
Packit |
5d935b |
$fh->read($dat, 8);
|
|
Packit |
5d935b |
my ($length, $coverage, $index) = unpack("Nnn", $dat);
|
|
Packit |
5d935b |
my ($type) = $coverage & 0xFF;
|
|
Packit |
5d935b |
$t = Font::TTF::Kern::Subtable->create($type, $coverage, $length);
|
|
Packit |
5d935b |
$t->read($fh);
|
|
Packit |
5d935b |
}
|
|
Packit |
5d935b |
else
|
|
Packit |
5d935b |
{
|
|
Packit |
5d935b |
$t = $self->read_subtable($fh);
|
|
Packit |
5d935b |
}
|
|
Packit |
5d935b |
push (@{$self->{'tables'}}, $t);
|
|
Packit |
5d935b |
}
|
|
Packit |
5d935b |
$self;
|
|
Packit |
5d935b |
}
|
|
Packit |
5d935b |
|
|
Packit |
5d935b |
sub read_subtable
|
|
Packit |
5d935b |
{
|
|
Packit |
5d935b |
my ($self, $fh) = @_;
|
|
Packit |
5d935b |
my ($dat, $len, $cov, $t);
|
|
Packit |
5d935b |
|
|
Packit |
5d935b |
$t = {};
|
|
Packit |
5d935b |
$fh->read($dat, 6);
|
|
Packit |
5d935b |
($t->{'Version'}, $len, $cov) = unpack("n3", $dat);
|
|
Packit |
5d935b |
$t->{'coverage'} = $cov & 255;
|
|
Packit |
5d935b |
$t->{'type'} = $cov >> 8;
|
|
Packit |
5d935b |
if ($t->{'Version'} == 0)
|
|
Packit |
5d935b |
{
|
|
Packit |
5d935b |
# NB: Cambria is an example of a font that plays an unsual trick: The
|
|
Packit |
5d935b |
# kern table is much larger than can be represented by the header $len
|
|
Packit |
5d935b |
# would allow. So we use the number of pairs to figure out how much to read.
|
|
Packit |
5d935b |
$fh->read($dat, 8);
|
|
Packit |
5d935b |
$t->{'Num'} = unpack("n", $dat);
|
|
Packit |
5d935b |
$fh->read($dat, $t->{'Num'} * 6);
|
|
Packit |
5d935b |
my (@vals) = unpack("n*", $dat);
|
|
Packit |
5d935b |
for (0 .. ($t->{'Num'} - 1))
|
|
Packit |
5d935b |
{
|
|
Packit |
5d935b |
my ($f, $l, $v);
|
|
Packit |
5d935b |
$f = shift @vals;
|
|
Packit |
5d935b |
$l = shift @vals;
|
|
Packit |
5d935b |
$v = shift @vals;
|
|
Packit |
5d935b |
$v -= 65536 if ($v > 32767);
|
|
Packit |
5d935b |
$t->{'kern'}{$f}{$l} = $v;
|
|
Packit |
5d935b |
}
|
|
Packit |
5d935b |
} elsif ($t->{'Version'} == 2)
|
|
Packit |
5d935b |
{
|
|
Packit |
5d935b |
my ($wid, $off, $numg, $maxl, $maxr, $j);
|
|
Packit |
5d935b |
|
|
Packit |
5d935b |
$fh->read($dat, $len - 6);
|
|
Packit |
5d935b |
$wid = unpack("n", $dat);
|
|
Packit |
5d935b |
$off = unpack("n", substr($dat, 2));
|
|
Packit |
5d935b |
($t->{'left_first'}, $numg) = unpack("n2", substr($dat, $off));
|
|
Packit |
5d935b |
$t->{'left'} = [unpack("n$numg", substr($dat, $off + 4))];
|
|
Packit |
5d935b |
foreach (@{$t->{'left'}})
|
|
Packit |
5d935b |
{
|
|
Packit |
5d935b |
$_ /= $wid;
|
|
Packit |
5d935b |
$maxl = $_ if ($_ > $maxl);
|
|
Packit |
5d935b |
}
|
|
Packit |
5d935b |
$t->{'left_max'} = $maxl;
|
|
Packit |
5d935b |
|
|
Packit |
5d935b |
$off = unpack("n", substr($dat, 4));
|
|
Packit |
5d935b |
($t->{'right_first'}, $numg) = unpack("n2", substr($dat, $off));
|
|
Packit |
5d935b |
$t->{'right'} = [unpack("n$numg", substr($dat, $off + 4))];
|
|
Packit |
5d935b |
foreach (@{$t->{'right'}})
|
|
Packit |
5d935b |
{
|
|
Packit |
5d935b |
$_ >>= 1;
|
|
Packit |
5d935b |
$maxr = $_ if ($_ > $maxr);
|
|
Packit |
5d935b |
}
|
|
Packit |
5d935b |
$t->{'right_max'} = $maxr;
|
|
Packit |
5d935b |
|
|
Packit |
5d935b |
$off = unpack("n", substr($dat, 6));
|
|
Packit |
5d935b |
for ($j = 0; $j <= $maxl; $j++)
|
|
Packit |
5d935b |
{
|
|
Packit |
5d935b |
my ($k) = 0;
|
|
Packit |
5d935b |
|
|
Packit |
5d935b |
map { $t->{'kern'}{$j}{$k} = $_ if $_; $k++; }
|
|
Packit |
5d935b |
unpack("n$maxr", substr($dat, $off + $wid * $j));
|
|
Packit |
5d935b |
}
|
|
Packit |
5d935b |
}
|
|
Packit |
5d935b |
return $t;
|
|
Packit |
5d935b |
}
|
|
Packit |
5d935b |
|
|
Packit |
5d935b |
|
|
Packit |
5d935b |
=head2 $t->out($fh)
|
|
Packit |
5d935b |
|
|
Packit |
5d935b |
Outputs the kerning tables to the given file
|
|
Packit |
5d935b |
|
|
Packit |
5d935b |
=cut
|
|
Packit |
5d935b |
|
|
Packit |
5d935b |
sub out
|
|
Packit |
5d935b |
{
|
|
Packit |
5d935b |
my ($self, $fh) = @_;
|
|
Packit |
5d935b |
my ($i, $l, $r, $t);
|
|
Packit |
5d935b |
|
|
Packit |
5d935b |
return $self->SUPER::out($fh) unless ($self->{' read'});
|
|
Packit |
5d935b |
|
|
Packit |
5d935b |
if ($self->{'Version'} > 0)
|
|
Packit |
5d935b |
{ $fh->print(TTF_Pack("vL", $self->{'Version'}, $self->{'Num'})); }
|
|
Packit |
5d935b |
else
|
|
Packit |
5d935b |
{ $fh->print(pack("n2", $self->{'Version'}, $self->{'Num'})); }
|
|
Packit |
5d935b |
|
|
Packit |
5d935b |
for ($i = 0; $i < $self->{'Num'}; $i++)
|
|
Packit |
5d935b |
{
|
|
Packit |
5d935b |
$t = $self->{'tables'}[$i];
|
|
Packit |
5d935b |
|
|
Packit |
5d935b |
if ($self->{'Version'} > 0)
|
|
Packit |
5d935b |
{ $t->out($fh); }
|
|
Packit |
5d935b |
else
|
|
Packit |
5d935b |
{ $self->out_subtable($fh, $t); }
|
|
Packit |
5d935b |
}
|
|
Packit |
5d935b |
$self;
|
|
Packit |
5d935b |
}
|
|
Packit |
5d935b |
|
|
Packit |
5d935b |
|
|
Packit |
5d935b |
sub out_subtable
|
|
Packit |
5d935b |
{
|
|
Packit |
5d935b |
my ($self, $fh, $t) = @_;
|
|
Packit |
5d935b |
my ($loc) = $fh->tell();
|
|
Packit |
5d935b |
my ($loc1, $l, $r);
|
|
Packit |
5d935b |
|
|
Packit |
5d935b |
$fh->print(pack("nnn", $t->{'Version'}, 0, $t->{'coverage'}));
|
|
Packit |
5d935b |
if ($t->{'Version'} == 0)
|
|
Packit |
5d935b |
{
|
|
Packit |
5d935b |
my ($dat);
|
|
Packit |
5d935b |
foreach $l (sort {$a <=> $b} keys %{$t->{'kern'}})
|
|
Packit |
5d935b |
{
|
|
Packit |
5d935b |
foreach $r (sort {$a <=> $b} keys %{$t->{'kern'}{$l}})
|
|
Packit |
5d935b |
{ $dat .= TTF_Pack("SSs", $l, $r, $t->{'kern'}{$l}{$r}); }
|
|
Packit |
5d935b |
}
|
|
Packit |
5d935b |
$fh->print(TTF_Pack("SSSS", Font::TTF::Utils::TTF_bininfo(length($dat) / 6, 6)));
|
|
Packit |
5d935b |
$fh->print($dat);
|
|
Packit |
5d935b |
} elsif ($t->{'Version'} == 2)
|
|
Packit |
5d935b |
{
|
|
Packit |
5d935b |
my ($arr);
|
|
Packit |
5d935b |
|
|
Packit |
5d935b |
$fh->print(pack("nnnn", $t->{'right_max'} << 1, 8, ($#{$t->{'left'}} + 7) << 1,
|
|
Packit |
5d935b |
($#{$t->{'left'}} + $#{$t->{'right'}} + 10) << 1));
|
|
Packit |
5d935b |
|
|
Packit |
5d935b |
$fh->print(pack("nn", $t->{'left_first'}, $#{$t->{'left'}} + 1));
|
|
Packit |
5d935b |
foreach (@{$t->{'left'}})
|
|
Packit |
5d935b |
{ $fh->print(pack("C", $_ * (($t->{'left_max'} + 1) << 1))); }
|
|
Packit |
5d935b |
|
|
Packit |
5d935b |
$fh->print(pack("nn", $t->{'right_first'}, $#{$t->{'right'}} + 1));
|
|
Packit |
5d935b |
foreach (@{$t->{'right'}})
|
|
Packit |
5d935b |
{ $fh->print(pack("C", $_ << 1)); }
|
|
Packit |
5d935b |
|
|
Packit |
5d935b |
$arr = "\000\000" x (($t->{'left_max'} + 1) * ($t->{'right_max'} + 1));
|
|
Packit |
5d935b |
foreach $l (keys %{$t->{'kern'}})
|
|
Packit |
5d935b |
{
|
|
Packit |
5d935b |
foreach $r (keys %{$t->{'kern'}{$l}})
|
|
Packit |
5d935b |
{ substr($arr, ($l * ($t->{'left_max'} + 1) + $r) << 1, 2)
|
|
Packit |
5d935b |
= pack("n", $t->{'kern'}{$l}{$r}); }
|
|
Packit |
5d935b |
}
|
|
Packit |
5d935b |
$fh->print($arr);
|
|
Packit |
5d935b |
}
|
|
Packit |
5d935b |
$loc1 = $fh->tell();
|
|
Packit |
5d935b |
$fh->seek($loc + 2, 0);
|
|
Packit |
5d935b |
$fh->print(pack("n", $loc1 - $loc));
|
|
Packit |
5d935b |
$fh->seek($loc1, 0);
|
|
Packit |
5d935b |
}
|
|
Packit |
5d935b |
|
|
Packit |
5d935b |
|
|
Packit |
5d935b |
=head2 $t->XML_element($context, $depth, $key, $value)
|
|
Packit |
5d935b |
|
|
Packit |
5d935b |
Handles outputting the kern hash into XML a little more tidily
|
|
Packit |
5d935b |
|
|
Packit |
5d935b |
=cut
|
|
Packit |
5d935b |
|
|
Packit |
5d935b |
sub XML_element
|
|
Packit |
5d935b |
{
|
|
Packit |
5d935b |
my ($self) = shift;
|
|
Packit |
5d935b |
my ($context, $depth, $key, $value) = @_;
|
|
Packit |
5d935b |
my ($fh) = $context->{'fh'};
|
|
Packit |
5d935b |
my ($f, $l);
|
|
Packit |
5d935b |
|
|
Packit |
5d935b |
return $self->SUPER::XML_element(@_) unless ($key eq 'kern');
|
|
Packit |
5d935b |
$fh->print("$depth<kern-table>\n");
|
|
Packit |
5d935b |
foreach $f (sort {$a <=> $b} keys %{$value})
|
|
Packit |
5d935b |
{
|
|
Packit |
5d935b |
foreach $l (sort {$a <=> $b} keys %{$value->{$f}})
|
|
Packit |
5d935b |
{ $fh->print("$depth$context->{'indent'}<adjust first='$f' last='$l' dist='$value->{$f}{$l}'/>\n"); }
|
|
Packit |
5d935b |
}
|
|
Packit |
5d935b |
$fh->print("$depth</kern-table>\n");
|
|
Packit |
5d935b |
$self;
|
|
Packit |
5d935b |
}
|
|
Packit |
5d935b |
|
|
Packit |
5d935b |
=head2 $t->minsize()
|
|
Packit |
5d935b |
|
|
Packit |
5d935b |
Returns the minimum size this table can be. If it is smaller than this, then the table
|
|
Packit |
5d935b |
must be bad and should be deleted or whatever.
|
|
Packit |
5d935b |
|
|
Packit |
5d935b |
=cut
|
|
Packit |
5d935b |
|
|
Packit |
5d935b |
sub minsize
|
|
Packit |
5d935b |
{
|
|
Packit |
5d935b |
return 4;
|
|
Packit |
5d935b |
}
|
|
Packit |
5d935b |
|
|
Packit |
5d935b |
1;
|
|
Packit |
5d935b |
|
|
Packit |
5d935b |
=head1 BUGS
|
|
Packit |
5d935b |
|
|
Packit |
5d935b |
=over 4
|
|
Packit |
5d935b |
|
|
Packit |
5d935b |
=item *
|
|
Packit |
5d935b |
|
|
Packit |
5d935b |
Only supports kerning table types 0 & 2.
|
|
Packit |
5d935b |
|
|
Packit |
5d935b |
=item *
|
|
Packit |
5d935b |
|
|
Packit |
5d935b |
No real support functions to I<do> anything with the kerning tables yet.
|
|
Packit |
5d935b |
|
|
Packit |
5d935b |
=back
|
|
Packit |
5d935b |
|
|
Packit |
5d935b |
=head1 AUTHOR
|
|
Packit |
5d935b |
|
|
Packit |
5d935b |
Martin Hosken L<http://scripts.sil.org/FontUtils>.
|
|
Packit |
5d935b |
|
|
Packit |
5d935b |
|
|
Packit |
5d935b |
=head1 LICENSING
|
|
Packit |
5d935b |
|
|
Packit |
5d935b |
Copyright (c) 1998-2016, SIL International (http://www.sil.org)
|
|
Packit |
5d935b |
|
|
Packit |
5d935b |
This module is released under the terms of the Artistic License 2.0.
|
|
Packit |
5d935b |
For details, see the full text of the license in the file LICENSE.
|
|
Packit |
5d935b |
|
|
Packit |
5d935b |
|
|
Packit |
5d935b |
|
|
Packit |
5d935b |
=cut
|
|
Packit |
5d935b |
|