Blame lib/Font/TTF/Mort/Ligature.pm

Packit 5d935b
package Font::TTF::Mort::Ligature;
Packit 5d935b
Packit 5d935b
=head1 NAME
Packit 5d935b
Packit 5d935b
Font::TTF::Mort::Ligature - Ligature Mort subtable for AAT
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::AATutils;
Packit 5d935b
use IO::File;
Packit 5d935b
Packit 5d935b
@ISA = qw(Font::TTF::Mort::Subtable);
Packit 5d935b
Packit 5d935b
sub new
Packit 5d935b
{
Packit 5d935b
    my ($class, $direction, $orientation, $subFeatureFlags) = @_;
Packit 5d935b
    my ($self) = {
Packit 5d935b
                    'direction'            => $direction,
Packit 5d935b
                    'orientation'        => $orientation,
Packit 5d935b
                    'subFeatureFlags'    => $subFeatureFlags
Packit 5d935b
                };
Packit 5d935b
Packit 5d935b
    $class = ref($class) || $class;
Packit 5d935b
    bless $self, $class;
Packit 5d935b
}
Packit 5d935b
Packit 5d935b
=head2 $t->read
Packit 5d935b
Packit 5d935b
Reads the table into memory
Packit 5d935b
Packit 5d935b
=cut
Packit 5d935b
Packit 5d935b
sub read
Packit 5d935b
{
Packit 5d935b
    my ($self, $fh) = @_;
Packit 5d935b
    my ($dat);
Packit 5d935b
Packit 5d935b
    my $stateTableStart = $fh->tell();
Packit 5d935b
    my ($classes, $states, $entries) = AAT_read_state_table($fh, 0);
Packit 5d935b
    
Packit 5d935b
    $fh->seek($stateTableStart, IO::File::SEEK_SET);
Packit 5d935b
    $fh->read($dat, 14);
Packit 5d935b
    my ($stateSize, $classTable, $stateArray, $entryTable,
Packit 5d935b
        $ligActionTable, $componentTable, $ligatureTable) = unpack("nnnnnnn", $dat);
Packit 5d935b
    my $limits = [$classTable, $stateArray, $entryTable, $ligActionTable, $componentTable, $ligatureTable, $self->{'length'} - 8];
Packit 5d935b
    
Packit 5d935b
    my %actions;
Packit 5d935b
    my $actionLists;
Packit 5d935b
    foreach (@$entries) {
Packit 5d935b
        my $offset = $_->{'flags'} & 0x3fff;
Packit 5d935b
        $_->{'flags'} &= ~0x3fff;
Packit 5d935b
        if ($offset != 0) {
Packit 5d935b
            if (not defined $actions{$offset}) {
Packit 5d935b
                $fh->seek($stateTableStart + $offset, IO::File::SEEK_SET);
Packit 5d935b
                my $actionList;
Packit 5d935b
                while (1) {
Packit 5d935b
                    $fh->read($dat, 4);
Packit 5d935b
                    my $action = unpack("N", $dat);
Packit 5d935b
                    my ($last, $store, $component) = (($action & 0x80000000) != 0, ($action & 0xC0000000) != 0, ($action & 0x3fffffff));
Packit 5d935b
                    $component -= 0x40000000 if $component > 0x1fffffff;
Packit 5d935b
                    $component -= $componentTable / 2;
Packit 5d935b
                    push @$actionList, { 'store' => $store, 'component' => $component };
Packit 5d935b
                    last if $last;
Packit 5d935b
                }
Packit 5d935b
                push @$actionLists, $actionList;
Packit 5d935b
                $actions{$offset} = $#$actionLists;
Packit 5d935b
            }
Packit 5d935b
            $_->{'actions'} = $actions{$offset};
Packit 5d935b
        }
Packit 5d935b
    }
Packit 5d935b
    
Packit 5d935b
    $self->{'componentTable'} = $componentTable;
Packit 5d935b
    my $components = [unpack("n*", AAT_read_subtable($fh, $stateTableStart, $componentTable, $limits))];
Packit 5d935b
    foreach (@$components) {
Packit 5d935b
        $_ = ($_ - $ligatureTable) . " +" if $_ >= $ligatureTable;
Packit 5d935b
    }
Packit 5d935b
    $self->{'components'} = $components;
Packit 5d935b
    
Packit 5d935b
    $self->{'ligatureTable'} = $ligatureTable;
Packit 5d935b
    $self->{'ligatures'} = [unpack("n*", AAT_read_subtable($fh, $stateTableStart, $ligatureTable, $limits))];
Packit 5d935b
    
Packit 5d935b
    $self->{'classes'} = $classes;
Packit 5d935b
    $self->{'states'} = $states;
Packit 5d935b
    $self->{'actionLists'} = $actionLists;
Packit 5d935b
        
Packit 5d935b
    $self;
Packit 5d935b
}
Packit 5d935b
Packit 5d935b
=head2 $t->pack_sub($fh)
Packit 5d935b
Packit 5d935b
=cut
Packit 5d935b
Packit 5d935b
sub pack_sub
Packit 5d935b
{
Packit 5d935b
    my ($self) = @_;
Packit 5d935b
    my ($dat);
Packit 5d935b
    
Packit 5d935b
    $dat .= pack("nnnnnnn", (0) x 7);    # placeholders for stateSize, classTable, stateArray, entryTable, actionLists, components, ligatures
Packit 5d935b
Packit 5d935b
    my $classTable = length($dat);
Packit 5d935b
    my $classes = $self->{'classes'};
Packit 5d935b
    $dat .= AAT_pack_classes($classes);
Packit 5d935b
    
Packit 5d935b
    my $stateArray = length($dat);
Packit 5d935b
    my $states = $self->{'states'};
Packit 5d935b
    
Packit 5d935b
    my ($dat1, $stateSize, $entries) = AAT_pack_states($classes, $stateArray, $states,
Packit 5d935b
            sub {
Packit 5d935b
                ( $_->{'flags'} & 0xc000, $_->{'actions'} )
Packit 5d935b
            }
Packit 5d935b
        );
Packit 5d935b
    $dat .= $dat1;
Packit 5d935b
    
Packit 5d935b
    my $actionLists = $self->{'actionLists'};
Packit 5d935b
    my %actionListOffset;
Packit 5d935b
    my $actionListDataLength = 0;
Packit 5d935b
    my @actionListEntries;
Packit 5d935b
    foreach (0 .. $#$entries) {
Packit 5d935b
        my ($nextState, $flags, $offset) = split(/,/, $entries->[$_]);
Packit 5d935b
        if ($offset eq "") {
Packit 5d935b
            $offset = undef;
Packit 5d935b
        }
Packit 5d935b
        else {
Packit 5d935b
            if (defined $actionListOffset{$offset}) {
Packit 5d935b
                $offset = $actionListOffset{$offset};
Packit 5d935b
            }
Packit 5d935b
            else {
Packit 5d935b
                $actionListOffset{$offset} = $actionListDataLength;
Packit 5d935b
                my $list = $actionLists->[$offset];
Packit 5d935b
                $actionListDataLength += 4 * @$list;
Packit 5d935b
                push @actionListEntries, $list;
Packit 5d935b
                $offset = $actionListOffset{$offset};
Packit 5d935b
            }
Packit 5d935b
        }
Packit 5d935b
        $entries->[$_] = [ $nextState, $flags, $offset ];
Packit 5d935b
    }
Packit 5d935b
    my $entryTable = length($dat);
Packit 5d935b
    my $ligActionLists = ($entryTable + @$entries * 4 + 3) & ~3;
Packit 5d935b
    foreach (@$entries) {
Packit 5d935b
        $_->[2] += $ligActionLists if defined $_->[2];
Packit 5d935b
        $dat .= pack("nn", $_->[0], $_->[1] + $_->[2]);
Packit 5d935b
    }
Packit 5d935b
    $dat .= pack("C*", (0) x ($ligActionLists - $entryTable - @$entries * 4));
Packit 5d935b
    
Packit 5d935b
    die "internal error" unless length($dat) == $ligActionLists;
Packit 5d935b
    
Packit 5d935b
    my $componentTable = length($dat) + $actionListDataLength;
Packit 5d935b
    my $actionList;
Packit 5d935b
    foreach $actionList (@actionListEntries) {
Packit 5d935b
        foreach (0 .. $#$actionList) {
Packit 5d935b
            my $action = $actionList->[$_];
Packit 5d935b
            my $val = $action->{'component'} + $componentTable / 2;
Packit 5d935b
            $val += 0x40000000 if $val < 0;
Packit 5d935b
            $val &= 0x3fffffff;
Packit 5d935b
            $val |= 0x40000000 if $action->{'store'};
Packit 5d935b
            $val |= 0x80000000 if $_ == $#$actionList;
Packit 5d935b
            $dat .= pack("N", $val);
Packit 5d935b
        }
Packit 5d935b
    }
Packit 5d935b
Packit 5d935b
    die "internal error" unless length($dat) == $componentTable;
Packit 5d935b
Packit 5d935b
    my $components = $self->{'components'};
Packit 5d935b
    my $ligatureTable = $componentTable + @$components * 2;
Packit 5d935b
    $dat .= pack("n*", map { (index($_, '+') >= 0 ? $ligatureTable : 0) + $_ } @$components);
Packit 5d935b
    
Packit 5d935b
    my $ligatures = $self->{'ligatures'};
Packit 5d935b
    $dat .= pack("n*", @$ligatures);
Packit 5d935b
    
Packit 5d935b
    $dat1 = pack("nnnnnnn", $stateSize, $classTable, $stateArray, $entryTable, $ligActionLists, $componentTable, $ligatureTable);
Packit 5d935b
    substr($dat, 0, length($dat1)) = $dat1;
Packit 5d935b
Packit 5d935b
    return $dat;
Packit 5d935b
}
Packit 5d935b
Packit 5d935b
=head2 $t->print($fh)
Packit 5d935b
Packit 5d935b
Prints a human-readable representation of the table
Packit 5d935b
Packit 5d935b
=cut
Packit 5d935b
Packit 5d935b
sub print
Packit 5d935b
{
Packit 5d935b
    my ($self, $fh) = @_;
Packit 5d935b
    
Packit 5d935b
    my $post = $self->post();
Packit 5d935b
    
Packit 5d935b
    $fh = 'STDOUT' unless defined $fh;
Packit 5d935b
Packit 5d935b
    $self->print_classes($fh);
Packit 5d935b
    
Packit 5d935b
    $fh->print("\n");
Packit 5d935b
    my $states = $self->{'states'};
Packit 5d935b
    foreach (0 .. $#$states) {
Packit 5d935b
        $fh->printf("\t\tState %d:", $_);
Packit 5d935b
        my $state = $states->[$_];
Packit 5d935b
        foreach (@$state) {
Packit 5d935b
            my $flags;
Packit 5d935b
            $flags .= "!" if ($_->{'flags'} & 0x4000);
Packit 5d935b
            $flags .= "*" if ($_->{'flags'} & 0x8000);
Packit 5d935b
            $fh->printf("\t(%s%d,%s)", $flags, $_->{'nextState'}, defined $_->{'actions'} ? $_->{'actions'} : "=");
Packit 5d935b
        }
Packit 5d935b
        $fh->print("\n");
Packit 5d935b
    }
Packit 5d935b
Packit 5d935b
    $fh->print("\n");
Packit 5d935b
    my $actionLists = $self->{'actionLists'};
Packit 5d935b
    foreach (0 .. $#$actionLists) {
Packit 5d935b
        $fh->printf("\t\tList %d:\t", $_);
Packit 5d935b
        my $actionList = $actionLists->[$_];
Packit 5d935b
        $fh->printf("%s\n", join(", ", map { ($_->{'component'} . ($_->{'store'} ? "*" : "") ) } @$actionList));
Packit 5d935b
    }
Packit 5d935b
Packit 5d935b
    my $ligatureTable = $self->{'ligatureTable'};
Packit 5d935b
Packit 5d935b
    $fh->print("\n");
Packit 5d935b
    my $components = $self->{'components'};
Packit 5d935b
    foreach (0 .. $#$components) {
Packit 5d935b
        $fh->printf("\t\tComponent %d: %s\n", $_, $components->[$_]);
Packit 5d935b
    }
Packit 5d935b
    
Packit 5d935b
    $fh->print("\n");
Packit 5d935b
    my $ligatures = $self->{'ligatures'};
Packit 5d935b
    foreach (0 .. $#$ligatures) {
Packit 5d935b
        $fh->printf("\t\tLigature %d: %d [%s]\n", $_, $ligatures->[$_], $post->{'VAL'}[$ligatures->[$_]]);
Packit 5d935b
    }
Packit 5d935b
}
Packit 5d935b
Packit 5d935b
1;
Packit 5d935b
Packit 5d935b
=head1 BUGS
Packit 5d935b
Packit 5d935b
None known
Packit 5d935b
Packit 5d935b
=head1 AUTHOR
Packit 5d935b
Packit 5d935b
Jonathan Kew 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
Packit 5d935b