Blame lib/Font/TTF/Silf.pm

Packit 5d935b
package Font::TTF::Silf;
Packit 5d935b
Packit 5d935b
=head1 NAME
Packit 5d935b
Packit 5d935b
Font::TTF::Silf - The main Graphite table
Packit 5d935b
Packit 5d935b
=head1 DESCRIPTION
Packit 5d935b
Packit 5d935b
The Silf table holds the core of the Graphite rules for a font. A Silf table has
Packit 5d935b
potentially multiple silf subtables, although there is usually only one. Within a silf subtable,
Packit 5d935b
there are a number of passes which contain the actual finite state machines to match rules
Packit 5d935b
and the constraint and action code to be executed when a rule matches.
Packit 5d935b
Packit 5d935b
=head1 INSTANCE VARIABLES
Packit 5d935b
Packit 5d935b
=over 4
Packit 5d935b
Packit 5d935b
=item Version
Packit 5d935b
Packit 5d935b
Silf table format version
Packit 5d935b
Packit 5d935b
=item Compiler
Packit 5d935b
Packit 5d935b
Lowest compiler version necessary to fully support the semantics expressed in this
Packit 5d935b
Graphite description
Packit 5d935b
Packit 5d935b
=item SILF
Packit 5d935b
Packit 5d935b
An array of Silf subtables
Packit 5d935b
Packit 5d935b
=over 4
Packit 5d935b
Packit 5d935b
=item maxGlyphID
Packit 5d935b
Packit 5d935b
The maximum glyph id referenced including pseudo and non glyphs
Packit 5d935b
Packit 5d935b
=item Ascent
Packit 5d935b
Packit 5d935b
Extra ascent to be added to the font ascent.
Packit 5d935b
Packit 5d935b
=item Descent
Packit 5d935b
Packit 5d935b
Extra descent to be added to the font descent. Both values are assumed to be
Packit 5d935b
positive for a descender below the base line.
Packit 5d935b
Packit 5d935b
=item substPass
Packit 5d935b
Packit 5d935b
Pass index into PASS of the first substitution pass.
Packit 5d935b
Packit 5d935b
=item posPass
Packit 5d935b
Packit 5d935b
Pass index into PASS of the first positioning pass.
Packit 5d935b
Packit 5d935b
=item justPass
Packit 5d935b
Packit 5d935b
Pass index into PASS of the first justification pass.
Packit 5d935b
Packit 5d935b
=item bidiPass
Packit 5d935b
Packit 5d935b
Pass index of the pass before which the bidirectional processing pass will be executed.
Packit 5d935b
0xFF indicates that there is no bidi pass to be executed.
Packit 5d935b
Packit 5d935b
=item Flags
Packit 5d935b
Packit 5d935b
A bitfield of flags:
Packit 5d935b
Packit 5d935b
    0 - Indicates there are line end contextual rules in one of the passes
Packit 5d935b
Packit 5d935b
=item maxPreContext
Packit 5d935b
Packit 5d935b
Maximum length of a context preceding a cross line boundary contextualisation.
Packit 5d935b
Packit 5d935b
=item maxPostContext
Packit 5d935b
Packit 5d935b
Maximum length of a context following a cross line boundary contextualsation.
Packit 5d935b
Packit 5d935b
=item attrPseudo
Packit 5d935b
Packit 5d935b
Glyph attribute for the actual glyph id associated with a pseudo glyph.
Packit 5d935b
Packit 5d935b
=item attrBreakWeight
Packit 5d935b
Packit 5d935b
Glyph attribute number of the attribute holding the default breakweight associated with a glyph.
Packit 5d935b
Packit 5d935b
=item attrDirectionality
Packit 5d935b
Packit 5d935b
Glyph attribute number of the attribute holding the default directionality value associated with a glyph.
Packit 5d935b
Packit 5d935b
=item JUST
Packit 5d935b
Packit 5d935b
The may be a number of justification levels each with their own property values.
Packit 5d935b
This points to an array of hashes, one for each justification level.
Packit 5d935b
Packit 5d935b
=over 4
Packit 5d935b
Packit 5d935b
=item attrStretch
Packit 5d935b
Packit 5d935b
Glyph attribute number for the amount of stretch allowed before this glyph.
Packit 5d935b
Packit 5d935b
=item attrShrink
Packit 5d935b
Packit 5d935b
Glyph attribute number for the amount of shrink allowed before this glyph.
Packit 5d935b
Packit 5d935b
=item attrStep
Packit 5d935b
Packit 5d935b
Glyph attribute number specifying the minimum granularity of actual spacing associated with this glyph at this level.
Packit 5d935b
Packit 5d935b
=item attrWeight
Packit 5d935b
Packit 5d935b
Glyph attribute number giving the weight associated with spreading space across a run of glyphs.
Packit 5d935b
Packit 5d935b
=item runto
Packit 5d935b
Packit 5d935b
Which level starts the next stage.
Packit 5d935b
Packit 5d935b
=back
Packit 5d935b
Packit 5d935b
=item numLigComp
Packit 5d935b
Packit 5d935b
Number of initial glyph attributes that represent ligature components
Packit 5d935b
Packit 5d935b
=item numUserAttr
Packit 5d935b
Packit 5d935b
Number of user defined slot attributes referenced. Tells the engine how much space to
Packit 5d935b
allocate to a slot for user attributes.
Packit 5d935b
Packit 5d935b
=item maxCompPerLig
Packit 5d935b
Packit 5d935b
Maximum number of components per ligature.
Packit 5d935b
Packit 5d935b
=item direction
Packit 5d935b
Packit 5d935b
Supported directions for this writing system
Packit 5d935b
Packit 5d935b
=item CRIT_FEATURE
Packit 5d935b
Packit 5d935b
Array of critical features.
Packit 5d935b
Packit 5d935b
=item scripts
Packit 5d935b
Packit 5d935b
Array of script tags that indicate which set of GDL rules to execute if there is more than one in a font.
Packit 5d935b
Packit 5d935b
=item lbGID
Packit 5d935b
Packit 5d935b
Glyph ID of the linebreak pseudo glyph.
Packit 5d935b
Packit 5d935b
=item pseudos
Packit 5d935b
Packit 5d935b
Hash of Unicode values to pseduo glyph ids.
Packit 5d935b
Packit 5d935b
=item classes
Packit 5d935b
Packit 5d935b
This is an array of classes, each of which is an array of glyph ids in class order.
Packit 5d935b
Packit 5d935b
=item PASS
Packit 5d935b
Packit 5d935b
The details of rules and actions are stored in passes. This value is an array of pass subobjects one for each pass.
Packit 5d935b
Packit 5d935b
=over 4
Packit 5d935b
Packit 5d935b
=item flags
Packit 5d935b
Packit 5d935b
This is a bitfield:
Packit 5d935b
Packit 5d935b
    0 - If true, this pass makes no change to the slot stream considered as a sequence of glyph ids.
Packit 5d935b
        Only slot attributes are expected to change (for example during positioning).
Packit 5d935b
Packit 5d935b
=item maxRuleLoop
Packit 5d935b
Packit 5d935b
How many times the engine will allow rules to be tested and run without the engine advancing through the
Packit 5d935b
input slot stream.
Packit 5d935b
Packit 5d935b
=item maxRuleContext
Packit 5d935b
Packit 5d935b
Number of slots of input needed to run this pass.
Packit 5d935b
Packit 5d935b
=item maxBackup
Packit 5d935b
Packit 5d935b
Number of slots by which the following pass needs to trail this pass (i.e. the maximum this pass is allowed to back up).
Packit 5d935b
Packit 5d935b
=item numRules
Packit 5d935b
Packit 5d935b
Number of action code blocks, and so uncompressed rules, in this pass.
Packit 5d935b
Packit 5d935b
=item numRows
Packit 5d935b
Packit 5d935b
Number of rows in the finite state machine.
Packit 5d935b
Packit 5d935b
=item numTransitional
Packit 5d935b
Packit 5d935b
Number of rows in the finite state machine that are not final states. This specifies the number of rows in the fsm
Packit 5d935b
element.
Packit 5d935b
Packit 5d935b
=item numSuccess
Packit 5d935b
Packit 5d935b
Number of success states. A success state may also be a transitional state.
Packit 5d935b
Packit 5d935b
=item numColumns
Packit 5d935b
Packit 5d935b
Number of columns in the finite state machine.
Packit 5d935b
Packit 5d935b
=item colmap
Packit 5d935b
Packit 5d935b
A hash, indexed by glyphid, that gives the fsm column number associated with that glyphid. If not present, then
Packit 5d935b
the glyphid is not part of the fsm and will finish fsm processing if it occurs.
Packit 5d935b
Packit 5d935b
=item rulemap
Packit 5d935b
Packit 5d935b
An array of arrays, one for each success state. Each array holds a list of rule numbers associated with that state.
Packit 5d935b
Packit 5d935b
=item minRulePreContext
Packit 5d935b
Packit 5d935b
Minimum number of items in a rule's precontext.
Packit 5d935b
Packit 5d935b
=item maxRulePreContext
Packit 5d935b
Packit 5d935b
The maximum number of items in any rule's precontext.
Packit 5d935b
Packit 5d935b
=item startStates
Packit 5d935b
Packit 5d935b
Array of starting state numbers dependeing on the length of actual precontext.
Packit 5d935b
There are maxRulePreContext - minRulePreContext + 1 of these.
Packit 5d935b
Packit 5d935b
=item ruleSortKeys
Packit 5d935b
Packit 5d935b
An array of sort keys one for each rule giving the length of the rule including its precontext.
Packit 5d935b
Packit 5d935b
=item rulePreContexts
Packit 5d935b
Packit 5d935b
An array of precontext lengths for each rule.
Packit 5d935b
Packit 5d935b
=item fsm
Packit 5d935b
Packit 5d935b
A two dimensional array such that $p->{'fsm'}[$row][$col] gives the row of the next node to try in the fsm.
Packit 5d935b
Packit 5d935b
=item passConstraintLen
Packit 5d935b
Packit 5d935b
Length in bytes of the passConstraint code.
Packit 5d935b
Packit 5d935b
=item passConstraintCode
Packit 5d935b
Packit 5d935b
A byte string holding the pass constraint code.
Packit 5d935b
Packit 5d935b
=item constraintCode
Packit 5d935b
Packit 5d935b
An array of byte strings holding the constraint code for each rule.
Packit 5d935b
Packit 5d935b
=item actionCode
Packit 5d935b
Packit 5d935b
An array of byte strings holding the action code for each rule.
Packit 5d935b
Packit 5d935b
=back
Packit 5d935b
Packit 5d935b
=back
Packit 5d935b
Packit 5d935b
=back
Packit 5d935b
Packit 5d935b
=cut
Packit 5d935b
Packit 5d935b
use Font::TTF::Table;
Packit 5d935b
use Font::TTF::Utils;
Packit 5d935b
use strict;
Packit 5d935b
use vars qw(@ISA);
Packit 5d935b
Packit 5d935b
@ISA = qw(Font::TTF::Table);
Packit 5d935b
Packit 5d935b
=head2 @opcodes
Packit 5d935b
Packit 5d935b
Each array holds the name of the opcode, the number of operand bytes and a string describing the operands.
Packit 5d935b
The characters in the string have the following meaning:
Packit 5d935b
Packit 5d935b
    c - lsb of class id
Packit 5d935b
    C - msb of class id
Packit 5d935b
    f - feature index
Packit 5d935b
    g - lsb of glyph attribute id
Packit 5d935b
    G - msb of glyph attribute id
Packit 5d935b
    l - lsb of a 32-bit extension to a 16-bit number
Packit 5d935b
    L - msb of a 32-bit number
Packit 5d935b
    m - glyph metric id
Packit 5d935b
    n - lsb of a number
Packit 5d935b
    N - msb of a 16-bit number
Packit 5d935b
    o - offset (jump)
Packit 5d935b
    s - slot reference
Packit 5d935b
    S - slot attribute id
Packit 5d935b
    v - variable number of following arguments
Packit 5d935b
Packit 5d935b
=cut
Packit 5d935b
Packit 5d935b
our @opcodes = ( ["nop", 0, ""], ["push_byte", 1, "n"], ["push_byte_u", 1, "n"], ["push_short", 2, "Nn"],
Packit 5d935b
             ["push_short_u", 2, "Nn"], ["push_long", 4, "LlNn"], ["add", 0, ""], ["sub", 0, ""],
Packit 5d935b
             ["mul", 0, ""], ["div", 0, ""], ["min", 0, ""], ["max", 0, ""],
Packit 5d935b
             ["neg", 0, ""], ["trunc8", 0, ""], ["trunc16", 0, ""], ["cond", 0, ""],
Packit 5d935b
             ["and", 0, ""], ["or", 0, ""], ["not", 0, ""], ["equal", 0, ""],                                               # 16
Packit 5d935b
             ["not_eq", 0, ""], ["less", 0, ""], ["gtr", 0, ""], ["less_eq", 0, ""],
Packit 5d935b
             ["gtr_eq", 0, ""], ["next", 0, ""], ["next_n", 1, "n"], ["copy_next", 0, ""],
Packit 5d935b
             ["put_glyph_8bit_obs", 1, "c"], ["put_subs_8bit_obs", 3, "scc"], ["put_copy", 1, "s"], ["insert", 0, ""],
Packit 5d935b
             ["delete", 0, ""], ["assoc", -1, "v"], ["cntxt_item", 2, "so"], ["attr_set", 1, "S"],                          # 32
Packit 5d935b
             ["attr_add", 1, "S"], ["attr_sub", 1, "S"], ["attr_set_slot", 1, "S"], ["iattr_set_slot", 2, "Sn"],
Packit 5d935b
             ["push_slot_attr", 2, "Ss"], ["push_glyph_attr_obs", 2, "gs"], ["push_glyph_metric", 3, "msn"], ["push_feat", 2, "fs"],
Packit 5d935b
             ["push_att_to_gattr_obs", 2, "gs"], ["push_att_to_glyph_metric", 3, "msn"], ["push_islot_attr", 3, "Ssn"], ["push_iglyph_attr", 3, "gsn"],
Packit 5d935b
             ["pop_ret", 0, ""], ["ret_zero", 0, ""], ["ret_true", 0, ""], ["iattr_set", 2, "Sn"],                          # 48
Packit 5d935b
             ["iattr_add", 2, "Sn"], ["iattr_sub", 2, "Sn"], ["push_proc_state", 1, "n"], ["push_version", 0, ""],
Packit 5d935b
             ["put_subs", 5, "sCcCc"], ["put_subs2", 4, "cscc"], ["put_subs3", 7, "scscscc"], ["put_glyph", 2, "Cc"],
Packit 5d935b
             ["push_glyph_attr", 3, "Ggs"], ["push_att_to_glyph_attr", 3, "Ggs"], ["bitand", 0, ""], ["bitor", 0, ""],
Packit 5d935b
             ["bitnot", 0, ""], ["setbits", 4, "NnNn"], ["setfeat", 2, "fs"] );                                             # 64
Packit 5d935b
Packit 5d935b
my ($i) = 0;
Packit 5d935b
our %opnames = map {$_->[0] => $i++} @opcodes;
Packit 5d935b
Packit 5d935b
=head2 read
Packit 5d935b
Packit 5d935b
Reads the Silf table into the internal data structure
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 ($dat, $d);
Packit 5d935b
    my ($fh) = $self->{' INFILE'};
Packit 5d935b
    my ($moff) = $self->{' OFFSET'};
Packit 5d935b
    my ($numsilf, @silfo);
Packit 5d935b
    
Packit 5d935b
    $fh->read($dat, 4);
Packit 5d935b
    ($self->{'Version'}) = TTF_Unpack("v", $dat);
Packit 5d935b
    if ($self->{'Version'} >= 3)
Packit 5d935b
    {
Packit 5d935b
        $fh->read($dat, 4);
Packit 5d935b
        ($self->{'Compiler'}) = TTF_Unpack("v", $dat);
Packit 5d935b
    }
Packit 5d935b
    $fh->read($dat, 4);
Packit 5d935b
    ($numsilf) = TTF_Unpack("S", $dat);
Packit 5d935b
    $fh->read($dat, $numsilf * 4);
Packit 5d935b
    foreach my $i (0 .. $numsilf - 1)
Packit 5d935b
    { push (@silfo, TTF_Unpack("L", substr($dat, $i * 4, 4))); }
Packit 5d935b
Packit 5d935b
    foreach my $sili (0 .. $numsilf - 1)
Packit 5d935b
    {
Packit 5d935b
        my ($silf) = {};
Packit 5d935b
        my (@passo, @classo, $classbase, $numJust, $numCritFeatures, $numScript, $numPasses, $numPseudo, $i);
Packit 5d935b
Packit 5d935b
        push (@{$self->{'SILF'}}, $silf);
Packit 5d935b
        $fh->seek($moff + $silfo[$sili], 0);
Packit 5d935b
        if ($self->{'Version'} >= 3)
Packit 5d935b
        {
Packit 5d935b
            $fh->read($dat, 8);
Packit 5d935b
            ($silf->{'Version'}) = TTF_Unpack("v", $dat);
Packit 5d935b
        }
Packit 5d935b
        $fh->read($dat, 20);
Packit 5d935b
        ($silf->{'maxGlyphID'}, $silf->{'Ascent'}, $silf->{'Descent'},
Packit 5d935b
         $numPasses, $silf->{'substPass'}, $silf->{'posPass'}, $silf->{'justPass'}, $silf->{'bidiPass'},
Packit 5d935b
         $silf->{'Flags'}, $silf->{'maxPreContext'}, $silf->{'maxPostContext'}, $silf->{'attrPseudo'},
Packit 5d935b
         $silf->{'attrBreakWeight'}, $silf->{'attrDirectionality'}, $silf->{'attrMirror'}, $silf->{'passBits'}, $numJust) = 
Packit 5d935b
            TTF_Unpack("SssCCCCCCCCCCCCCC", $dat);
Packit 5d935b
        if ($numJust)
Packit 5d935b
        {
Packit 5d935b
            foreach my $j (0 .. $silf->{'numJust'} - 1)
Packit 5d935b
            {
Packit 5d935b
                my ($just) = {};
Packit 5d935b
                push (@{$silf->{'JUST'}}, $just);
Packit 5d935b
                $fh->read($dat, 8);
Packit 5d935b
                ($just->{'attrStretch'}, $just->{'attrShrink'}, $just->{'attrStep'}, $just->{'attrWeight'},
Packit 5d935b
                 $just->{'runto'}) = TTF_Unpack("CCCCC", $dat);
Packit 5d935b
            }
Packit 5d935b
        }
Packit 5d935b
        $fh->read($dat, 10);
Packit 5d935b
        ($silf->{'numLigComp'}, $silf->{'numUserAttr'}, $silf->{'maxCompPerLig'}, $silf->{'direction'},
Packit 5d935b
         $silf->{'attCollisions'}, $d, $d, $d, $numCritFeatures) = TTF_Unpack("SCCCCCCCC", $dat);
Packit 5d935b
        if ($numCritFeatures)
Packit 5d935b
        {
Packit 5d935b
            $fh->read($dat, $numCritFeatures * 2);
Packit 5d935b
            $silf->{'CRIT_FEATURE'} = [TTF_Unpack("S$numCritFeatures", $dat)];
Packit 5d935b
        }
Packit 5d935b
        $fh->read($dat, 2);
Packit 5d935b
        ($d, $numScript) = TTF_Unpack("CC", $dat);
Packit 5d935b
        if ($numScript)
Packit 5d935b
        {
Packit 5d935b
            $fh->read($dat, $numScript * 4);
Packit 5d935b
            foreach (0 .. $numScript - 1)
Packit 5d935b
            { push (@{$silf->{'scripts'}}, unpack('a4', substr($dat, $_ * 4, 4))); }
Packit 5d935b
        }
Packit 5d935b
        $fh->read($dat, 2);
Packit 5d935b
        ($silf->{'lbGID'}) = TTF_Unpack("S", $dat);
Packit 5d935b
        $fh->read($dat, $numPasses * 4 + 4);
Packit 5d935b
        @passo = unpack("N*", $dat);
Packit 5d935b
        $fh->read($dat, 8);
Packit 5d935b
        ($numPseudo) = TTF_Unpack("S", $dat);
Packit 5d935b
        if ($numPseudo)
Packit 5d935b
        {
Packit 5d935b
            $fh->read($dat, $numPseudo * 6);
Packit 5d935b
            foreach (0 .. $numPseudo - 1)
Packit 5d935b
            {
Packit 5d935b
                my ($uni, $gid) = TTF_Unpack("LS", substr($dat, $_ * 6, 6));
Packit 5d935b
                $silf->{'pseudos'}{$uni} = $gid;
Packit 5d935b
            }
Packit 5d935b
        }
Packit 5d935b
        $classbase = $fh->tell();
Packit 5d935b
        $fh->read($dat, 4);
Packit 5d935b
        my ($numClasses, $numLinearClasses) = TTF_Unpack("SS", $dat);
Packit 5d935b
        $silf->{'numLinearClasses'} = $numLinearClasses;
Packit 5d935b
        $fh->read($dat, ($numClasses + 1) * ($self->{'Version'} >= 4 ? 4 : 2));
Packit 5d935b
        @classo = unpack($self->{'Version'} >= 4 ? "N*" : "n*", $dat);
Packit 5d935b
        $fh->read($dat, $classo[-1] - $classo[0]);
Packit 5d935b
        for ($i = 0; $i < $numLinearClasses; $i++)
Packit 5d935b
        {
Packit 5d935b
            push (@{$silf->{'classes'}}, [unpack("n*", substr($dat, $classo[$i] - $classo[0], 
Packit 5d935b
                                                            $classo[$i+1] - $classo[$i]))]) 
Packit 5d935b
        }
Packit 5d935b
        for ($i = $numLinearClasses; $i < $numClasses; $i++)
Packit 5d935b
        {
Packit 5d935b
            my (@res);
Packit 5d935b
            my (@c) = unpack("n*", substr($dat, $classo[$i] - $classo[0] + 8, $classo[$i+1] - $classo[$i] - 8));
Packit 5d935b
            for (my $j = 0; $j < @c; $j += 2)
Packit 5d935b
            { $res[$c[$j+1]] = $c[$j]; }
Packit 5d935b
            push (@{$silf->{'classes'}}, \@res);
Packit 5d935b
        }
Packit 5d935b
        foreach (0 .. $numPasses - 1)
Packit 5d935b
        { $self->read_pass($fh, $passo[$_], $moff + $silfo[$sili], $silf, $_); }
Packit 5d935b
    }
Packit 5d935b
    return $self;
Packit 5d935b
}
Packit 5d935b
Packit 5d935b
sub chopcode
Packit 5d935b
{
Packit 5d935b
    my ($dest, $dat, $offsets, $isconstraint) = @_;
Packit 5d935b
    my ($last) = $offsets->[-1];
Packit 5d935b
    my ($i);
Packit 5d935b
Packit 5d935b
    for ($i = $#{$offsets} - 1; $i >= 0; $i--)
Packit 5d935b
    {
Packit 5d935b
        if ((!$isconstraint || $offsets->[$i]) && $offsets->[$i] != $last)
Packit 5d935b
        {
Packit 5d935b
            unshift(@{$dest}, substr($dat, $offsets->[$i], $last - $offsets->[$i]));
Packit 5d935b
            $last = $offsets->[$i];
Packit 5d935b
        }
Packit 5d935b
        else
Packit 5d935b
        { unshift(@{$dest}, ""); }
Packit 5d935b
    }
Packit 5d935b
}
Packit 5d935b
Packit 5d935b
Packit 5d935b
sub read_pass
Packit 5d935b
{
Packit 5d935b
    my ($self, $fh, $offset, $base, $silf, $id) = @_;
Packit 5d935b
    my ($pass) = {'id' => $id};
Packit 5d935b
    my ($d, $dat, $i, @orulemap, @oconstraints, @oactions, $numRanges);
Packit 5d935b
Packit 5d935b
    $fh->seek($offset + $base, 0);
Packit 5d935b
    # printf "pass base = %04X\n", $offset;
Packit 5d935b
    push (@{$silf->{'PASS'}}, $pass);
Packit 5d935b
    $fh->read($dat, 40);
Packit 5d935b
    ($pass->{'flags'}, $pass->{'maxRuleLoop'}, $pass->{'maxRuleContext'}, $pass->{'maxBackup'},
Packit 5d935b
     $pass->{'numRules'}, $d, $d, $d, $d, $d, $pass->{'numRows'}, $pass->{'numTransitional'},
Packit 5d935b
     $pass->{'numSuccess'}, $pass->{'numColumns'}, $numRanges) =
Packit 5d935b
        TTF_Unpack("CCCCSSLLLLSSSSS", $dat);
Packit 5d935b
    $fh->read($dat, $numRanges * 6);
Packit 5d935b
    foreach $i (0 .. $numRanges - 1)
Packit 5d935b
    {
Packit 5d935b
        my ($first, $last, $col) = TTF_Unpack('SSS', substr($dat, $i * 6, 6));
Packit 5d935b
        foreach ($first .. $last)
Packit 5d935b
        { $pass->{'colmap'}{$_} = $col; }
Packit 5d935b
    }
Packit 5d935b
    $fh->read($dat, $pass->{'numSuccess'} * 2 + 2);
Packit 5d935b
    @orulemap = unpack("n*", $dat);
Packit 5d935b
    $fh->read($dat, $orulemap[-1] * 2);
Packit 5d935b
    foreach (0 .. $pass->{'numSuccess'} - 1)
Packit 5d935b
    { push (@{$pass->{'rulemap'}}, [unpack("n*", substr($dat, $orulemap[$_] * 2, ($orulemap[$_+1] - $orulemap[$_]) * 2))]); }
Packit 5d935b
    $fh->read($dat, 2);
Packit 5d935b
    ($pass->{'minRulePreContext'}, $pass->{'maxRulePreContext'}) = TTF_Unpack("CC", $dat);
Packit 5d935b
    $fh->read($dat, ($pass->{'maxRulePreContext'} - $pass->{'minRulePreContext'} + 1) * 2);
Packit 5d935b
    $pass->{'startStates'} = [unpack('n*', $dat)];
Packit 5d935b
    $fh->read($dat, $pass->{'numRules'} * 2);
Packit 5d935b
    $pass->{'ruleSortKeys'} = [unpack('n*', $dat)];
Packit 5d935b
    $fh->read($dat, $pass->{'numRules'});
Packit 5d935b
    $pass->{'rulePreContexts'} = [unpack('C*', $dat)];
Packit 5d935b
    $fh->read($dat, 3);
Packit 5d935b
    ($pass->{'collisionThreshold'}, $pass->{'passConstraintLen'}) = TTF_Unpack("CS", $dat);
Packit 5d935b
    $fh->read($dat, ($pass->{'numRules'} + 1) * 2);
Packit 5d935b
    @oconstraints = unpack('n*', $dat);
Packit 5d935b
    $fh->read($dat, ($pass->{'numRules'} + 1) * 2);
Packit 5d935b
    @oactions = unpack('n*', $dat);
Packit 5d935b
    foreach (0 .. $pass->{'numTransitional'} - 1)
Packit 5d935b
    {
Packit 5d935b
        $fh->read($dat, $pass->{'numColumns'} * 2);
Packit 5d935b
        push (@{$pass->{'fsm'}}, [unpack('n*', $dat)]);
Packit 5d935b
    }
Packit 5d935b
    $fh->read($dat, 1);
Packit 5d935b
    if ($pass->{'passConstraintLen'})
Packit 5d935b
    { $fh->read($pass->{'passConstraintCode'}, $pass->{'passConstraintLen'}); }
Packit 5d935b
    $fh->read($dat, $oconstraints[-1]);
Packit 5d935b
    $pass->{'constraintCode'} = [];
Packit 5d935b
    chopcode($pass->{'constraintCode'}, $dat, \@oconstraints, 1);
Packit 5d935b
    $fh->read($dat, $oactions[-1]);
Packit 5d935b
    $pass->{'actionCode'} = [];
Packit 5d935b
    chopcode($pass->{'actionCode'}, $dat, \@oactions, 0);
Packit 5d935b
    return $pass;
Packit 5d935b
}
Packit 5d935b
Packit 5d935b
sub chopranges
Packit 5d935b
{
Packit 5d935b
    my ($map, $numg) = @_;
Packit 5d935b
    my ($dat, $numRanges);
Packit 5d935b
    my (@keys) = sort {$a <=> $b} keys %{$map};
Packit 5d935b
    my ($first, $last, $col, $g);
Packit 5d935b
Packit 5d935b
    $first = -1;
Packit 5d935b
    $last = -1;
Packit 5d935b
    $col = -1;
Packit 5d935b
    foreach $g (@keys)
Packit 5d935b
    {
Packit 5d935b
        next unless ($g > 0 or $g eq '0');
Packit 5d935b
        if ($g != $last + 1 || $map->{$g} != $col)
Packit 5d935b
        {
Packit 5d935b
            if ($col != -1)
Packit 5d935b
            {
Packit 5d935b
                $dat .= pack("nnn", $first, $last, $col);
Packit 5d935b
                $numRanges++;
Packit 5d935b
            }
Packit 5d935b
            $first = $last = $g;
Packit 5d935b
            $col = $map->{$g};
Packit 5d935b
        }
Packit 5d935b
        else
Packit 5d935b
        { $last++; }
Packit 5d935b
    }
Packit 5d935b
    if ($col != -1)
Packit 5d935b
    {
Packit 5d935b
        $dat .= pack("nnn", $first, $last, $col);
Packit 5d935b
        $numRanges++;
Packit 5d935b
    }
Packit 5d935b
    return ($numRanges, $dat);
Packit 5d935b
}
Packit 5d935b
Packit 5d935b
sub unpack_code
Packit 5d935b
{
Packit 5d935b
    my ($self, $str) = @_;
Packit 5d935b
    my (@res, $i, $j);
Packit 5d935b
    my ($l) = length($str);
Packit 5d935b
Packit 5d935b
    for ($i = 0; $i < $l; )
Packit 5d935b
    {
Packit 5d935b
        my ($a) = unpack('C', substr($str, $i, 1));
Packit 5d935b
        my ($o) = $opcodes[$a];
Packit 5d935b
        my (@args);
Packit 5d935b
        my (@types) = split('', $o->[2]);
Packit 5d935b
        ++$i;
Packit 5d935b
        for ($j = 0; $j < @types; ++$j)
Packit 5d935b
        {
Packit 5d935b
            my ($t) = $types[$j];
Packit 5d935b
            if ($t eq 'v')
Packit 5d935b
            {
Packit 5d935b
                my ($n) = unpack('C', substr($str, $i, 1));
Packit 5d935b
                push (@args, unpack('C*', substr($str, $i + 1, $n)));
Packit 5d935b
                $i += $n + 1;
Packit 5d935b
            }
Packit 5d935b
            elsif ($t eq 'L' or $t eq 'N' or $t eq 'G' or $t eq 'C')
Packit 5d935b
            {
Packit 5d935b
                push (@args, unpack('n', substr($str, $i, 2)));
Packit 5d935b
                $i += 2;
Packit 5d935b
                $j++;
Packit 5d935b
            }
Packit 5d935b
            else
Packit 5d935b
            {
Packit 5d935b
                push (@args, unpack($t eq 's' ? 'c' : 'C', substr($str, $i, 1)));
Packit 5d935b
                $i++;
Packit 5d935b
            }
Packit 5d935b
        }
Packit 5d935b
        push (@res, [$o->[0], @args]);
Packit 5d935b
    }
Packit 5d935b
    return @res;
Packit 5d935b
}
Packit 5d935b
Packit 5d935b
sub pack_code
Packit 5d935b
{
Packit 5d935b
    my ($self, $cmds) = @_;
Packit 5d935b
    my ($res);
Packit 5d935b
Packit 5d935b
    foreach my $c (@{$cmds})
Packit 5d935b
    {
Packit 5d935b
        my ($ind) = $opnames{$c->[0]};
Packit 5d935b
        my ($i) = 1;
Packit 5d935b
        $res .= pack('C', $ind);
Packit 5d935b
        # my (@types) = unpack('C*', $opcodes[$ind][2]);
Packit 5d935b
        my (@types) = split('', $opcodes[$ind][2]);
Packit 5d935b
        for (my $j = 0; $j < @types; $j++)
Packit 5d935b
        {
Packit 5d935b
            my ($t) = $types[$j];
Packit 5d935b
            if ($t eq 'v')
Packit 5d935b
            {
Packit 5d935b
                my ($n) = scalar @{$c} - 1;
Packit 5d935b
                $res .= pack('C*', $n, @{$c}[1..$#{$c}]);
Packit 5d935b
                $i += $n;
Packit 5d935b
            }
Packit 5d935b
            elsif ($t eq 'C' or $t eq 'G' or $t eq 'L' or $t eq 'N')
Packit 5d935b
            {
Packit 5d935b
                $res .= pack('n', $c->[$i]);
Packit 5d935b
                $j++;
Packit 5d935b
            }
Packit 5d935b
            else
Packit 5d935b
            { $res .= pack($t eq 's' ? 'c' : 'C', $c->[$i]); }
Packit 5d935b
            $i++;
Packit 5d935b
        }
Packit 5d935b
    }
Packit 5d935b
    return $res;
Packit 5d935b
}
Packit 5d935b
Packit 5d935b
sub packcode
Packit 5d935b
{
Packit 5d935b
    my ($code, $isconstraint) = @_;
Packit 5d935b
    my ($dat, $c, $res);
Packit 5d935b
Packit 5d935b
    $c = 1;
Packit 5d935b
    $dat = "\000";
Packit 5d935b
    foreach (@{$code})
Packit 5d935b
    {
Packit 5d935b
        if ($_)
Packit 5d935b
        {
Packit 5d935b
            push(@{$res}, $c);
Packit 5d935b
            $dat .= $_;
Packit 5d935b
            $c += length($_);
Packit 5d935b
        }
Packit 5d935b
        else
Packit 5d935b
        { push(@{$res}, $isconstraint ? 0 : $c); }
Packit 5d935b
    }
Packit 5d935b
    push(@{$res}, $c);
Packit 5d935b
    return ($res, $dat);
Packit 5d935b
}
Packit 5d935b
Packit 5d935b
sub out_pass
Packit 5d935b
{
Packit 5d935b
    my ($self, $fh, $pass, $silf, $subbase) = @_;
Packit 5d935b
    my (@orulemap, $dat, $actiondat, $numRanges, $c);
Packit 5d935b
    my (@offsets, $res, $pbase);
Packit 5d935b
Packit 5d935b
    $pbase = $fh->tell();
Packit 5d935b
    # printf "pass base = %04X, ", $pbase - $subbase;
Packit 5d935b
    $fh->print(TTF_Pack("CCCCSSLLLLSSSS", $pass->{'flags'}, $pass->{'maxRuleLoop'}, $pass->{'maxRuleContext'},
Packit 5d935b
                $pass->{'maxBackup'}, $pass->{'numRules'}, 24, 0, 0, 0, 0, $pass->{'numRows'},
Packit 5d935b
                $pass->{'numTransitional'}, $pass->{'numSuccess'}, $pass->{'numColumns'}));
Packit 5d935b
    ($numRanges, $dat) = chopranges($pass->{'colmap'});
Packit 5d935b
#    print "numranges = $numRanges\n";
Packit 5d935b
    $fh->print(TTF_Pack("SSSS", TTF_bininfo($numRanges, 6)));
Packit 5d935b
    $fh->print($dat);
Packit 5d935b
    $dat = "";
Packit 5d935b
    $c = 0;
Packit 5d935b
#    print "transitions = $pass->{'numTransitional'}, success = $pass->{'numSuccess'}, rows = $pass->{'numRows'}\n";
Packit 5d935b
    my ($sucbase) = $pass->{'numRows'} - $pass->{'numSuccess'};
Packit 5d935b
    foreach (0 .. ($pass->{'numSuccess'} - 1))
Packit 5d935b
    {
Packit 5d935b
        push(@orulemap, $c);
Packit 5d935b
        if (defined $pass->{'rulemap'}[$_])
Packit 5d935b
        {
Packit 5d935b
            $dat .= pack("n*", @{$pass->{'rulemap'}[$_]});
Packit 5d935b
            $c += @{$pass->{'rulemap'}[$_]};
Packit 5d935b
        }
Packit 5d935b
        else
Packit 5d935b
        {
Packit 5d935b
            print "No rules for " . ($sucbase + $_);
Packit 5d935b
            if ($sucbase + $_ < $pass->{'numTransitional'})
Packit 5d935b
            { print ": (" . join(",", @{$pass->{'fsm'}[$sucbase + $_]}) . ")"; }
Packit 5d935b
            print "\n";
Packit 5d935b
        }
Packit 5d935b
    }
Packit 5d935b
    push (@orulemap, $c);
Packit 5d935b
    $fh->print(pack("n*", @orulemap));
Packit 5d935b
    $fh->print($dat);
Packit 5d935b
    $fh->print(TTF_Pack("CC", $pass->{'minRulePreContext'}, $pass->{'maxRulePreContext'}));
Packit 5d935b
    $fh->print(pack("n*", @{$pass->{'startStates'}}));
Packit 5d935b
    $fh->print(pack("n*", @{$pass->{'ruleSortKeys'}}));
Packit 5d935b
    $fh->print(pack("C*", @{$pass->{'rulePreContexts'}}));
Packit 5d935b
    $fh->print(TTF_Pack("CS", 0, $pass->{'passConstraintLen'}));
Packit 5d935b
    my ($oconstraints, $oactions);
Packit 5d935b
    ($oconstraints, $dat) = packcode($pass->{'constraintCode'}, 1);
Packit 5d935b
    ($oactions, $actiondat) = packcode($pass->{'actionCode'}, 0);
Packit 5d935b
#    printf "constraint offsets @ %X\n", $fh->tell();
Packit 5d935b
    $fh->print(pack("n*", @{$oconstraints}));
Packit 5d935b
#    printf "action offsets @ %X\n", $fh->tell();
Packit 5d935b
    $fh->print(pack("n*", @{$oactions}));
Packit 5d935b
#    printf "fsm @ %X\n", $fh->tell();
Packit 5d935b
    foreach (@{$pass->{'fsm'}})
Packit 5d935b
    { $fh->print(pack("n*", @{$_})); }
Packit 5d935b
#    printf "end of fsm @ %X\n", $fh->tell();
Packit 5d935b
    $fh->print(pack("C", $pass->{'collisionThreshold'}));
Packit 5d935b
    push(@offsets, $fh->tell() - $subbase);
Packit 5d935b
    $fh->print($pass->{'passConstraintCode'});
Packit 5d935b
    push(@offsets, $fh->tell() - $subbase);
Packit 5d935b
    $fh->print($dat);
Packit 5d935b
    push(@offsets, $fh->tell() - $subbase);
Packit 5d935b
    $fh->print($actiondat);
Packit 5d935b
    push(@offsets, 0);
Packit 5d935b
    print join(", ", @offsets) . "\n";
Packit 5d935b
    $res = $fh->tell();
Packit 5d935b
    $fh->seek($pbase + 8, 0);
Packit 5d935b
    $fh->print(pack("N*", @offsets));
Packit 5d935b
    $fh->seek($res, 0);
Packit 5d935b
#    printf "end = %04X\n", $res - $subbase;
Packit 5d935b
    return $res;
Packit 5d935b
}
Packit 5d935b
Packit 5d935b
=head2 out
Packit 5d935b
Packit 5d935b
Outputs a Silf data structure to a font file in binary format
Packit 5d935b
Packit 5d935b
=cut
Packit 5d935b
Packit 5d935b
sub out
Packit 5d935b
{
Packit 5d935b
    my ($self, $fh) = @_;
Packit 5d935b
    my ($silf, $base, $subbase, $silfc, $end);
Packit 5d935b
Packit 5d935b
    return $self->SUPER::out($fh) unless ($self->{' read'});
Packit 5d935b
    $base = $fh->tell();
Packit 5d935b
    if ($self->{'Version'} >= 3)
Packit 5d935b
    { $fh->print(TTF_Pack("vvSS", $self->{'Version'}, $self->{'Compiler'}, $#{$self->{'SILF'}} + 1, 0)); }
Packit 5d935b
    else
Packit 5d935b
    { $fh->print(TTF_Pack("vSS", $self->{'Version'}, $#{$self->{'SILF'}} + 1, 0)); }
Packit 5d935b
    $fh->print(pack('N*', (0) x (@{$self->{'SILF'}})));
Packit 5d935b
    foreach $silf (@{$self->{'SILF'}})
Packit 5d935b
    {
Packit 5d935b
        my ($subbase) = $fh->tell();
Packit 5d935b
        my ($numlin, $i, @opasses, $oPasses, $oPseudo, $ooPasses);
Packit 5d935b
        if ($self->{'Version'} >= 3)
Packit 5d935b
        {
Packit 5d935b
            $fh->seek($base + 12 + $silfc * 4, 0);
Packit 5d935b
            $fh->print(pack('N', $subbase - $base));
Packit 5d935b
            $fh->seek($subbase, 0);
Packit 5d935b
            $fh->print(TTF_Pack("vSS", $silf->{'Version'}, $ooPasses, $oPseudo));
Packit 5d935b
        }
Packit 5d935b
        else
Packit 5d935b
        {
Packit 5d935b
            $fh->seek($base + 8 + $silfc * 4, 0);
Packit 5d935b
            $fh->print(pack('N', $subbase - $base));
Packit 5d935b
            $fh->seek($subbase, 0);
Packit 5d935b
        }
Packit 5d935b
        $fh->print(TTF_Pack("SssCCCCCCCCCCCCCC", 
Packit 5d935b
             $silf->{'maxGlyphID'}, $silf->{'Ascent'}, $silf->{'Descent'},
Packit 5d935b
             scalar @{$silf->{'PASS'}}, $silf->{'substPass'}, $silf->{'posPass'}, $silf->{'justPass'}, $silf->{'bidiPass'},
Packit 5d935b
             $silf->{'Flags'}, $silf->{'maxPreContext'}, $silf->{'maxPostContext'}, $silf->{'attrPseudo'},
Packit 5d935b
             $silf->{'attrBreakWeight'}, $silf->{'attrDirectionality'}, $silf->{'attrMirror'}, $silf->{'passBits'}, $#{$silf->{'JUST'}} + 1));
Packit 5d935b
        foreach (@{$silf->{'JUST'}})
Packit 5d935b
        { $fh->print(TTF_Pack("CCCCCCCC", $_->{'attrStretch'}, $_->{'attrShrink'}, $_->{'attrStep'},
Packit 5d935b
                        $_->{'attrWeight'}, $_->{'runto'}, 0, 0, 0)); }
Packit 5d935b
        
Packit 5d935b
        $fh->print(TTF_Pack("SCCCCCCCC", $silf->{'numLigComp'}, $silf->{'numUserAttr'}, $silf->{'maxCompPerLig'},
Packit 5d935b
                        $silf->{'direction'}, $silf->{'attCollisions'}, 0, 0, 0, $#{$silf->{'CRIT_FEATURE'}} + 1));
Packit 5d935b
        $fh->print(pack("n*", @{$silf->{'CRIT_FEATURE'}}));
Packit 5d935b
        $fh->print(TTF_Pack("CC", 0, $#{$silf->{'scripts'}} + 1));
Packit 5d935b
        foreach (@{$self->{'scripts'}})
Packit 5d935b
        { $fh->print(pack("a4", $_)); }
Packit 5d935b
        $fh->print(TTF_Pack("S", $silf->{'lbGID'}));
Packit 5d935b
        $ooPasses = $fh->tell();
Packit 5d935b
        if ($silf->{'PASS'}) { $fh->print(pack("N*", (0) x (@{$silf->{'PASS'}} + 1)));}
Packit 5d935b
        $oPseudo = $fh->tell() - $subbase;
Packit 5d935b
        my (@pskeys) = keys %{$silf->{'pseudos'}};
Packit 5d935b
        $fh->print(TTF_Pack("SSSS", TTF_bininfo(scalar @pskeys, 6)));
Packit 5d935b
        foreach my $k (sort {$a <=> $b} @pskeys)
Packit 5d935b
        { $fh->print(TTF_Pack("Ls", $k, $silf->{'pseudos'}{$k})); }
Packit 5d935b
        $numlin = $silf->{'numLinearClasses'};
Packit 5d935b
        $fh->print(TTF_Pack("SS", scalar @{$silf->{'classes'}}, $numlin));
Packit 5d935b
        my (@coffsets);
Packit 5d935b
        # printf "%X, ", $fh->tell() - $base;
Packit 5d935b
        my ($cbase) = (scalar @{$silf->{'classes'}} + 1) * ($self->{'Version'} >= 4 ? 4 : 2) + 4;
Packit 5d935b
        for ($i = 0; $i < $numlin; $i++)
Packit 5d935b
        {
Packit 5d935b
            push (@coffsets, $cbase);
Packit 5d935b
            $cbase += 2 * scalar @{$silf->{'classes'}[$i]};
Packit 5d935b
        }
Packit 5d935b
        my (@nonlinclasses);
Packit 5d935b
        for ($i = $numlin; $i < @{$silf->{'classes'}}; $i++)
Packit 5d935b
        {
Packit 5d935b
            my (@c, $d, @d);
Packit 5d935b
            my $c = $silf->{'classes'}[$i];
Packit 5d935b
            push (@coffsets, $cbase);
Packit 5d935b
            @c = sort {$c->[$a] <=> $c->[$b]} (0 .. $#{$c});
Packit 5d935b
            foreach $d (@c)
Packit 5d935b
            { push (@d, $c->[$d], $d); }
Packit 5d935b
            push (@nonlinclasses, [@d]);
Packit 5d935b
            my ($len) = scalar @d;
Packit 5d935b
            $cbase += 8 + 2 * $len;
Packit 5d935b
        }
Packit 5d935b
        push (@coffsets, $cbase);
Packit 5d935b
        $fh->print(pack(($self->{'Version'} >= 4 ? 'N*' : 'n*'), @coffsets));
Packit 5d935b
        for ($i = 0; $i < $numlin; $i++)
Packit 5d935b
        { $fh->print(pack("n*", @{$silf->{'classes'}[$i]})); }
Packit 5d935b
        # printf "%X, ", $fh->tell() - $base;
Packit 5d935b
        for ($i = $numlin; $i < @{$silf->{'classes'}}; $i++)
Packit 5d935b
        {
Packit 5d935b
            
Packit 5d935b
            my ($num) = scalar @{$nonlinclasses[$i-$numlin]};
Packit 5d935b
            my (@bin) = TTF_bininfo($num/2, 1);
Packit 5d935b
            $fh->print(TTF_Pack("SSSS", @bin));
Packit 5d935b
            $fh->print(pack("n*", @{$nonlinclasses[$i-$numlin]}));
Packit 5d935b
        }
Packit 5d935b
        $oPasses = $fh->tell() - $subbase;
Packit 5d935b
#        printf "original pass = %04X\n", $oPasses;
Packit 5d935b
        push (@opasses, $oPasses);
Packit 5d935b
        foreach (@{$silf->{'PASS'}})
Packit 5d935b
        { push(@opasses, $self->out_pass($fh, $_, $silf, $subbase) - $subbase); }
Packit 5d935b
        $end = $fh->tell();
Packit 5d935b
        $fh->seek($ooPasses, 0);
Packit 5d935b
        $fh->print(pack("N*", @opasses));
Packit 5d935b
        if ($self->{'Version'} >= 3)
Packit 5d935b
        {
Packit 5d935b
            $fh->seek($subbase + 4, 0);
Packit 5d935b
            $fh->print(TTF_Pack("SS", $ooPasses - $subbase, $oPseudo));
Packit 5d935b
        }
Packit 5d935b
        $fh->seek($end, 0);
Packit 5d935b
        $silfc++;
Packit 5d935b
    }
Packit 5d935b
}
Packit 5d935b
Packit 5d935b
sub XML_element
Packit 5d935b
{
Packit 5d935b
    my ($self, $context, $depth, $k, $val, $ind) = @_;
Packit 5d935b
    my ($fh) = $context->{'fh'};
Packit 5d935b
    my ($i);
Packit 5d935b
Packit 5d935b
    return $self if ($k eq 'LOC');
Packit 5d935b
Packit 5d935b
    if ($k eq 'classes')
Packit 5d935b
    {
Packit 5d935b
        $fh->print("$depth<classes>\n");
Packit 5d935b
        foreach $i (0 .. $#{$val})
Packit 5d935b
        {
Packit 5d935b
            $fh->printf("$depth    <class num='%d'>\n", $i);
Packit 5d935b
            $fh->printf("$depth        " . join(" ", map{sprintf("%d", $_)} @{$val->[$i]}));
Packit 5d935b
            $fh->print("\n$depth    </class>\n");
Packit 5d935b
        }
Packit 5d935b
        $fh->print("$depth</classes>\n");
Packit 5d935b
    }
Packit 5d935b
    elsif ($k eq 'fsm')
Packit 5d935b
    {
Packit 5d935b
        $fh->print("$depth<fsm>\n");
Packit 5d935b
        my ($i) = 0;
Packit 5d935b
        foreach (@{$val})
Packit 5d935b
        { $fh->print("$depth    <row index='$i'>" . join(" ", @{$_}) . "</row>\n"); $i++; }
Packit 5d935b
        $fh->print("$depth</fsm>\n");
Packit 5d935b
    }
Packit 5d935b
    elsif ($k eq 'colmap')
Packit 5d935b
    {
Packit 5d935b
        my ($i);
Packit 5d935b
        $fh->print("$depth<colmap>");
Packit 5d935b
        foreach my $k (sort {$a <=> $b} keys %{$val})
Packit 5d935b
        {
Packit 5d935b
            if ($i++ % 8 == 0)
Packit 5d935b
            { $fh->print("\n$depth  "); }
Packit 5d935b
            $fh->printf(" %d=%d", $k, $val->{$k});
Packit 5d935b
        }
Packit 5d935b
        $fh->print("\n$depth</colmap>\n");
Packit 5d935b
    }
Packit 5d935b
    elsif ($k eq 'constraintCode' or $k eq 'actionCode')
Packit 5d935b
    {
Packit 5d935b
        $fh->print("$depth<$k>\n");
Packit 5d935b
        foreach my $i (0 .. $#{$val})
Packit 5d935b
        {
Packit 5d935b
            my (@rules) = $self->unpack_code($val->[$i]);
Packit 5d935b
            next unless (@rules);
Packit 5d935b
            $fh->print("$depth  <elem index='$i' code='" . join(" ", unpack('C*', $val->[$i])) . "'>\n");
Packit 5d935b
            foreach my $r (@rules)
Packit 5d935b
            { $fh->print("$depth    $r->[0]: ". join(", ", @{$r}[1..$#{$r}]) . "\n"); }
Packit 5d935b
            $fh->print("$depth  </elem>\n");
Packit 5d935b
        }
Packit 5d935b
        $fh->print("$depth</$k>\n");
Packit 5d935b
    }       
Packit 5d935b
    else
Packit 5d935b
    { return $self->SUPER::XML_element($context, $depth, $k, $val, $ind); }
Packit 5d935b
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 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