Blame lib/Font/TTF/GSUB.pm

Packit 5d935b
package Font::TTF::GSUB;
Packit 5d935b
Packit 5d935b
=head1 NAME
Packit 5d935b
Packit 5d935b
Font::TTF::GSUB - Module support for the GSUB table in conjunction with TTOpen
Packit 5d935b
Packit 5d935b
=head1 DESCRIPTION
Packit 5d935b
Packit 5d935b
Handles the GSUB subtables in relation to Ttopen tables. Due to the variety of
Packit 5d935b
different lookup types, the data structures are not all that straightforward,
Packit 5d935b
although I have tried to make life easy for myself when using this!
Packit 5d935b
Packit 5d935b
=head1 INSTANCE VARIABLES
Packit 5d935b
Packit 5d935b
The structure of a GSUB table is the same as that given in L<Font::TTF::Ttopen>.
Packit 5d935b
Here we give some of the semantics specific to GSUB lookups.
Packit 5d935b
Packit 5d935b
=over 4
Packit 5d935b
Packit 5d935b
=item ACTION_TYPE
Packit 5d935b
Packit 5d935b
This is a string taking one of 4 values indicating the nature of the information
Packit 5d935b
in the ACTION array of the rule:
Packit 5d935b
Packit 5d935b
=over 8
Packit 5d935b
Packit 5d935b
=item g
Packit 5d935b
Packit 5d935b
The action contains a string of glyphs to replace the match string by
Packit 5d935b
Packit 5d935b
=item l
Packit 5d935b
Packit 5d935b
The action array contains a list of offsets and lookups to run, in order, on
Packit 5d935b
the matched string
Packit 5d935b
Packit 5d935b
=item a
Packit 5d935b
Packit 5d935b
The action array is an unordered set of optional replacements for the matched
Packit 5d935b
glyph. The application should make the selection somehow.
Packit 5d935b
Packit 5d935b
=item o
Packit 5d935b
Packit 5d935b
The action array is empty (in fact there is no rule array for this type of
Packit 5d935b
rule) and the ADJUST value should be added to the glyph id to find the replacement
Packit 5d935b
glyph id value
Packit 5d935b
Packit 5d935b
=item r
Packit 5d935b
Packit 5d935b
The action array is a list of replacement glyphs in coverage order. This ACTION_TYPE
Packit 5d935b
is used only for Type 8 Reverse Chaining lookups which, by design, are single glyph
Packit 5d935b
substitution.
Packit 5d935b
Packit 5d935b
=back
Packit 5d935b
Packit 5d935b
=item MATCH_TYPE
Packit 5d935b
Packit 5d935b
This indicates which type of information the various MATCH arrays (MATCH, PRE,
Packit 5d935b
POST) hold in the rule:
Packit 5d935b
Packit 5d935b
=over 8
Packit 5d935b
Packit 5d935b
=item g
Packit 5d935b
Packit 5d935b
The array holds a string of glyph ids which should match exactly
Packit 5d935b
Packit 5d935b
=item c
Packit 5d935b
Packit 5d935b
The array holds a sequence of class definitions which each glyph should
Packit 5d935b
correspondingly match to
Packit 5d935b
Packit 5d935b
=item o
Packit 5d935b
Packit 5d935b
The array holds offsets to coverage tables
Packit 5d935b
Packit 5d935b
=back
Packit 5d935b
Packit 5d935b
=back
Packit 5d935b
Packit 5d935b
=head1 CORRESPONDANCE TO LAYOUT TYPES
Packit 5d935b
Packit 5d935b
The following table gives the values for ACTION_TYPE and MATCH_TYPE for each
Packit 5d935b
of the 12 different lookup types found in the GSUB table definition:
Packit 5d935b
Packit 5d935b
                1.1 1.2  2  3  4  5.1 5.2 5.3  6.1 6.2 6.3  8
Packit 5d935b
  ACTION_TYPE    o   g   g  a  g   l   l   l    l   l   l   r
Packit 5d935b
  MATCH_TYPE                   g   g   c   o    g   c   o   o
Packit 5d935b
Packit 5d935b
Hopefully, the rest of the uses of the variables should make sense from this
Packit 5d935b
table.
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::Ttopen;
Packit 5d935b
Packit 5d935b
@ISA = qw(Font::TTF::Ttopen);
Packit 5d935b
Packit 5d935b
=head2 $t->read_sub($fh, $lookup, $index)
Packit 5d935b
Packit 5d935b
Asked by the superclass to read in from the given file the indexth subtable from
Packit 5d935b
lookup number lookup. The file is positioned ready for the read.
Packit 5d935b
Packit 5d935b
=cut
Packit 5d935b
Packit 5d935b
sub read_sub
Packit 5d935b
{
Packit 5d935b
    my ($self, $fh, $main_lookup, $sindex) = @_;
Packit 5d935b
    my ($type) = $main_lookup->{'TYPE'};
Packit 5d935b
    my ($loc) = $fh->tell();
Packit 5d935b
    my ($lookup) = $main_lookup->{'SUB'}[$sindex];
Packit 5d935b
    my ($dat, $s, @subst, $t, $fmt, $cover, $count, $mcount, $scount, $i, $gid);
Packit 5d935b
    my (@srec);
Packit 5d935b
Packit 5d935b
    if ($type == 6)
Packit 5d935b
    {
Packit 5d935b
        $fh->read($dat, 4);
Packit 5d935b
        ($fmt, $cover) = TTF_Unpack('S2', $dat);
Packit 5d935b
        if ($fmt < 3)
Packit 5d935b
        {
Packit 5d935b
            $fh->read($dat, 2);
Packit 5d935b
            $count = TTF_Unpack('S', $dat);
Packit 5d935b
        }
Packit 5d935b
    } else
Packit 5d935b
    {
Packit 5d935b
        $fh->read($dat, 6);
Packit 5d935b
        ($fmt, $cover, $count) = TTF_Unpack("S3", $dat);
Packit 5d935b
    }
Packit 5d935b
    unless ($fmt == 3 && ($type == 5 || $type == 6))
Packit 5d935b
    { $lookup->{'COVERAGE'} = $self->read_cover($cover, $loc, $lookup, $fh, 1); }
Packit 5d935b
Packit 5d935b
    $lookup->{'FORMAT'} = $fmt;
Packit 5d935b
    if ($type == 1 && $fmt == 1)
Packit 5d935b
    {
Packit 5d935b
        $count -= 65536 if ($count > 32767);
Packit 5d935b
        $lookup->{'ADJUST'} = $count;
Packit 5d935b
        $lookup->{'ACTION_TYPE'} = 'o';
Packit 5d935b
    } elsif ($type == 1 && $fmt == 2)
Packit 5d935b
    {
Packit 5d935b
        $fh->read($dat, $count << 1);
Packit 5d935b
        @subst = TTF_Unpack('S*', $dat);
Packit 5d935b
        foreach $s (@subst)
Packit 5d935b
        { push(@{$lookup->{'RULES'}}, [{'ACTION' => [$s]}]); }
Packit 5d935b
        $lookup->{'ACTION_TYPE'} = 'g';
Packit 5d935b
    } elsif ($type == 2 || $type == 3)
Packit 5d935b
    {
Packit 5d935b
        $fh->read($dat, $count << 1);       # number of offsets
Packit 5d935b
        foreach $s (TTF_Unpack('S*', $dat))
Packit 5d935b
        {
Packit 5d935b
            $fh->seek($loc + $s, 0);
Packit 5d935b
            $fh->read($dat, 2);
Packit 5d935b
            $t = TTF_Unpack('S', $dat);
Packit 5d935b
            $fh->read($dat, $t << 1);
Packit 5d935b
            push(@{$lookup->{'RULES'}}, [{'ACTION' => [TTF_Unpack('S*', $dat)]}]);
Packit 5d935b
        }
Packit 5d935b
        $lookup->{'ACTION_TYPE'} = ($type == 2 ? 'g' : 'a');
Packit 5d935b
    } elsif ($type == 4)
Packit 5d935b
    {
Packit 5d935b
        $fh->read($dat, $count << 1);
Packit 5d935b
        foreach $s (TTF_Unpack('S*', $dat))
Packit 5d935b
        {
Packit 5d935b
            @subst = ();
Packit 5d935b
            $fh->seek($loc + $s, 0);
Packit 5d935b
            $fh->read($dat, 2);
Packit 5d935b
            $t = TTF_Unpack('S', $dat);
Packit 5d935b
            $fh->read($dat, $t << 1);
Packit 5d935b
            foreach $t (TTF_Unpack('S*', $dat))
Packit 5d935b
            {
Packit 5d935b
                $fh->seek($loc + $s + $t, 0);
Packit 5d935b
                $fh->read($dat, 4);
Packit 5d935b
                ($gid, $mcount) = TTF_Unpack('S2', $dat);
Packit 5d935b
                $fh->read($dat, ($mcount - 1) << 1);
Packit 5d935b
                push(@subst, {'ACTION' => [$gid], 'MATCH' => [TTF_Unpack('S*', $dat)]});
Packit 5d935b
            }
Packit 5d935b
            push(@{$lookup->{'RULES'}}, [@subst]);
Packit 5d935b
        }
Packit 5d935b
        $lookup->{'ACTION_TYPE'} = 'g';
Packit 5d935b
        $lookup->{'MATCH_TYPE'} = 'g';
Packit 5d935b
    } elsif ($type == 8)
Packit 5d935b
    {
Packit 5d935b
        $t = {};
Packit 5d935b
        unless ($count == 0)
Packit 5d935b
        {
Packit 5d935b
            @subst = ();
Packit 5d935b
            $fh->read($dat, $count << 1);
Packit 5d935b
            foreach $s (TTF_Unpack('S*', $dat))
Packit 5d935b
            { push(@subst, $self->read_cover($s, $loc, $lookup, $fh, 1)); }
Packit 5d935b
            $t->{'PRE'} = [@subst];
Packit 5d935b
        }
Packit 5d935b
        $fh->read($dat, 2);
Packit 5d935b
        $count = TTF_Unpack('S', $dat);
Packit 5d935b
        unless ($count == 0)
Packit 5d935b
        {
Packit 5d935b
            @subst = ();
Packit 5d935b
            $fh->read($dat, $count << 1);
Packit 5d935b
            foreach $s (TTF_Unpack('S*', $dat))
Packit 5d935b
            { push(@subst, $self->read_cover($s, $loc, $lookup, $fh, 1)); }
Packit 5d935b
            $t->{'POST'} = [@subst];
Packit 5d935b
        }
Packit 5d935b
        $fh->read($dat, 2);
Packit 5d935b
        $count = TTF_Unpack('S', $dat);
Packit 5d935b
        $fh->read($dat, $count << 1);
Packit 5d935b
        $t->{'ACTION'} = [TTF_Unpack('S*', $dat)];
Packit 5d935b
        $lookup->{'RULES'} = [[$t]];
Packit 5d935b
        $lookup->{'ACTION_TYPE'} = 'r';
Packit 5d935b
        $lookup->{'MATCH_TYPE'} = 'o';
Packit 5d935b
    } elsif ($type == 5 || $type == 6)
Packit 5d935b
    { $self->read_context($lookup, $fh, $type, $fmt, $cover, $count, $loc); }
Packit 5d935b
    $lookup;
Packit 5d935b
}
Packit 5d935b
Packit 5d935b
Packit 5d935b
=head2 $t->extension
Packit 5d935b
Packit 5d935b
Returns the table type number for the extension table
Packit 5d935b
Packit 5d935b
=cut
Packit 5d935b
Packit 5d935b
sub extension
Packit 5d935b
{ return 7; }
Packit 5d935b
Packit 5d935b
Packit 5d935b
=head2 $t->out_sub($fh, $lookup, $index)
Packit 5d935b
Packit 5d935b
Passed the filehandle to output to, suitably positioned, the lookup and subtable
Packit 5d935b
index, this function outputs the subtable to $fh at that point.
Packit 5d935b
Packit 5d935b
=cut
Packit 5d935b
Packit 5d935b
sub out_sub
Packit 5d935b
{
Packit 5d935b
    my ($self, $fh, $main_lookup, $index, $ctables, $base) = @_;
Packit 5d935b
    my ($type) = $main_lookup->{'TYPE'};
Packit 5d935b
    my ($lookup) = $main_lookup->{'SUB'}[$index];
Packit 5d935b
    my ($fmt) = $lookup->{'FORMAT'};
Packit 5d935b
    my ($out, $r, $t, $i, $j, $offc, $offd, $numd);
Packit 5d935b
    my ($num) = $#{$lookup->{'RULES'}} + 1;
Packit 5d935b
Packit 5d935b
    if ($type == 1)
Packit 5d935b
    {
Packit 5d935b
        $out = pack("nn", $fmt, Font::TTF::Ttopen::ref_cache($lookup->{'COVERAGE'}, $ctables, 2 + $base));
Packit 5d935b
        if ($fmt == 1)
Packit 5d935b
        { $out .= pack("n", $lookup->{'ADJUST'}); }
Packit 5d935b
        else
Packit 5d935b
        {
Packit 5d935b
            $out .= pack("n", $num);
Packit 5d935b
            foreach $r (@{$lookup->{'RULES'}})
Packit 5d935b
            { $out .= pack("n", $r->[0]{'ACTION'}[0]); }
Packit 5d935b
        }
Packit 5d935b
    } elsif ($type == 2 || $type == 3)
Packit 5d935b
    {
Packit 5d935b
        $out = pack("nnn", $fmt, Font::TTF::Ttopen::ref_cache($lookup->{'COVERAGE'}, $ctables, 2 + $base),
Packit 5d935b
                            $num);
Packit 5d935b
        $out .= pack('n*', (0) x $num);
Packit 5d935b
        $offc = length($out);
Packit 5d935b
        for ($i = 0; $i < $num; $i++)
Packit 5d935b
        {
Packit 5d935b
            $out .= pack("n*", $#{$lookup->{'RULES'}[$i][0]{'ACTION'}} + 1,
Packit 5d935b
                                    @{$lookup->{'RULES'}[$i][0]{'ACTION'}});
Packit 5d935b
            substr($out, ($i << 1) + 6, 2) = pack('n', $offc);
Packit 5d935b
            $offc = length($out);
Packit 5d935b
        }
Packit 5d935b
    } elsif ($type == 8)
Packit 5d935b
    {
Packit 5d935b
        $out = pack("nn", $fmt, Font::TTF::Ttopen::ref_cache($lookup->{'COVERAGE'}, $ctables, 2 + $base));
Packit 5d935b
        $r = $lookup->{'RULES'}[0][0];
Packit 5d935b
        $out .= pack('n', defined $r->{'PRE'} ? scalar @{$r->{'PRE'}} : 0);
Packit 5d935b
        foreach $t (@{$r->{'PRE'}})
Packit 5d935b
        { $out .= pack('n', Font::TTF::Ttopen::ref_cache($t, $ctables, length($out) + $base)); }
Packit 5d935b
        $out .= pack('n', defined $r->{'POST'} ? scalar @{$r->{'POST'}} : 0);
Packit 5d935b
        foreach $t (@{$r->{'POST'}})
Packit 5d935b
        { $out .= pack('n', Font::TTF::Ttopen::ref_cache($t, $ctables, length($out) + $base)); }
Packit 5d935b
        $out .= pack("n*", $#{$r->{'ACTION'}} + 1, @{$r->{'ACTION'}});
Packit 5d935b
    } elsif ($type == 4 || $type == 5 || $type == 6)
Packit 5d935b
    { $out = $self->out_context($lookup, $fh, $type, $fmt, $ctables, $out, $num, $base); }
Packit 5d935b
#    Font::TTF::Ttopen::out_final($fh, $out, [[$ctables, 0]]);
Packit 5d935b
    $out;
Packit 5d935b
}
Packit 5d935b
Packit 5d935b
1;
Packit 5d935b
Packit 5d935b
=head1 AUTHOR
Packit 5d935b
Packit 5d935b
Martin Hosken L<http://scripts.sil.org/FontUtils>.
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