Blame lib/Font/TTF/Kern.pm

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