|
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 |
|