Blame lib/ttfmod.pl

Packit 5d935b
#       Title:      TTFMOD.PL
Packit 5d935b
#       Author:     M. Hosken
Packit 5d935b
#       Description:    Read TTF file calling user functions for each table
Packit 5d935b
#                       and output transformed tables to new TTF file.
Packit 5d935b
#       Useage:     TTFMOD provides the complete control loop for processing
Packit 5d935b
#                   the TTF files.  All that the caller need supply is an
Packit 5d935b
#                   associative array of functions to call keyed by the TTF
Packit 5d935b
#                   table name and the two filenames.
Packit 5d935b
#
Packit 5d935b
#           &ttfmod($infile, $outfile, *fns [, @must]);
Packit 5d935b
#
Packit 5d935b
#                   *fns is an associative array keyed by table name with
Packit 5d935b
#                   values of the name of the subroutine in package main to
Packit 5d935b
#                   be called to transfer the table from INFILE to OUTFILE.
Packit 5d935b
#                   The subroutine is called with the following parameters and
Packit 5d935b
#                   expected return values:
Packit 5d935b
#
Packit 5d935b
#           ($len, $csum) = &sub(*INFILE, *OUTFILE, $len);
Packit 5d935b
#
Packit 5d935b
#                   INFILE and OUTFILE are the input and output streams, $len
Packit 5d935b
#                   is the length of the table according to the directory.
Packit 5d935b
#                   The return values are $len = new length of table to be
Packit 5d935b
#                   given in the table directory.  $csum = new value of table
Packit 5d935b
#                   checksum.  A way to test that this is correct is to
Packit 5d935b
#                   checksum the whole file (e.g. using CSUM.BAT) and to
Packit 5d935b
#                   ensure that the value is 0xB1B0AFBA according to a 32 bit
Packit 5d935b
#                   checksum calculated bigendien.
Packit 5d935b
#
Packit 5d935b
#                   @must consists of a list of tables which must exist in the
Packit 5d935b
#                   final output file, either by being there alread or by being
Packit 5d935b
#                   inserted.
Packit 5d935b
#
Packit 5d935b
# Modifications:
Packit 5d935b
# MJPH  1.00    22-SEP-1994     Original
Packit 5d935b
# MJPH  1.1     18-MAR-1998     Added @must to ttfmod()
Packit 5d935b
# MJPH  1.1.1   25-MAR-1998     Added $csum to copytab (to make reusable)
Packit 5d935b
Packit 5d935b
package ttfmod;
Packit 5d935b
Packit 5d935b
sub main'ttfmod {
Packit 5d935b
    local($infile, $outfile, *fns, @must) = @_;
Packit 5d935b
Packit 5d935b
    # open files as binary.  Notice OUTFILE is opened for update not just write
Packit 5d935b
    open(INFILE, "$infile") || die "Unable top open \"$infile\" for reading";
Packit 5d935b
    binmode INFILE;
Packit 5d935b
    open(OUTFILE, "+>$outfile") || die "Unable to open \"$outfile\" for writing";
Packit 5d935b
    binmode OUTFILE;
Packit 5d935b
Packit 5d935b
    seek(INFILE, 0, 0);
Packit 5d935b
    read(INFILE, $dir_head, 12) || die "Reading table header";
Packit 5d935b
    ($dir_num) = unpack("x4n", $dir_head);
Packit 5d935b
    print OUTFILE $dir_head;
Packit 5d935b
    # read and unpack table directory
Packit 5d935b
    for ($i = 0; $i < $dir_num; $i++)
Packit 5d935b
        {
Packit 5d935b
        read(INFILE, $dir_val, 16) || die "Reading table entry";
Packit 5d935b
        $dir{unpack("a4", $dir_val)} = join(":", $i, unpack("x4NNN", $dir_val));
Packit 5d935b
        print OUTFILE $dir_val;
Packit 5d935b
        printf STDERR "%s %08x\n", unpack("a4", $dir_val), unpack("x8N", $dir_val)
Packit 5d935b
                if (defined $main'opt_z);
Packit 5d935b
        }
Packit 5d935b
    foreach $n (@must)
Packit 5d935b
    {
Packit 5d935b
        next if defined $dir{$n};
Packit 5d935b
        $dir{$n} = "$i:0:-1:0";
Packit 5d935b
        $i++; $dir_num++;
Packit 5d935b
        print OUTFILE pack("a4NNN", $n, 0, -1, 0);
Packit 5d935b
    }
Packit 5d935b
    substr($dir_head, 4, 2) = pack("n", $dir_num);
Packit 5d935b
    $csum = unpack("%32N*", $dir_head);
Packit 5d935b
    $off = tell(OUTFILE);
Packit 5d935b
    seek(OUTFILE, 0, 0);
Packit 5d935b
    print OUTFILE $dir_head;
Packit 5d935b
    seek (OUTFILE, $off, 0);
Packit 5d935b
    # process tables in order they occur in the file
Packit 5d935b
    @dirlist = sort byoffset keys(%dir);
Packit 5d935b
    foreach $tab (@dirlist)
Packit 5d935b
        {
Packit 5d935b
        @tab_split = split(':', $dir{$tab});
Packit 5d935b
        seek(INFILE, $tab_split[2], 0);         # offset
Packit 5d935b
        $tab_split[2] = tell(OUTFILE);
Packit 5d935b
        if (defined $fns{$tab})
Packit 5d935b
            {
Packit 5d935b
            $temp = "main'$fns{$tab}";
Packit 5d935b
            ($dir_len, $sum) = &$temp(*INFILE, *OUTFILE, $tab_split[3]);
Packit 5d935b
            }
Packit 5d935b
        else
Packit 5d935b
            {
Packit 5d935b
            ($dir_len, $sum) = &copytab(*INFILE, *OUTFILE, $tab_split[3]);
Packit 5d935b
            }
Packit 5d935b
        $tab_split[3] = $dir_len;               # len
Packit 5d935b
        $tab_split[1] = $sum;                   # checksum
Packit 5d935b
        $out_dir{$tab} = join(":", @tab_split);
Packit 5d935b
        }
Packit 5d935b
    # now output directory in same order as original directory
Packit 5d935b
    @dirlist = sort byindex keys(%out_dir);
Packit 5d935b
    foreach $tab (@dirlist)
Packit 5d935b
        {
Packit 5d935b
        @tab_split = split(':', $out_dir{$tab});
Packit 5d935b
        seek (OUTFILE, 12 + $tab_split[0] * 16, 0);     # directory index
Packit 5d935b
        print OUTFILE pack("A4N3", $tab, @tab_split[1..3]);
Packit 5d935b
        foreach $i (1..3, 1)        # checksum directory values with csum twice
Packit 5d935b
            {
Packit 5d935b
            $csum += $tab_split[$i];
Packit 5d935b
    # this line ensures $csum stays within 32 bit bounds, clipping as necessary
Packit 5d935b
            if ($csum > 0xffffffff) { $csum -= 0xffffffff; $csum--; }
Packit 5d935b
            }
Packit 5d935b
    # checksum the tag
Packit 5d935b
        $csum += unpack("N", $tab);
Packit 5d935b
        if ($csum > 0xffffffff) { $csum -= 0xffffffff; $csum--; }
Packit 5d935b
        }
Packit 5d935b
    # handle main checksum
Packit 5d935b
    @tab_split = split(':', $out_dir{"head"});
Packit 5d935b
    seek(OUTFILE, $tab_split[2], 0);
Packit 5d935b
    read(OUTFILE, $head_head, 12);          # read first bit of "head" table
Packit 5d935b
    @head_split = unpack("N3", $head_head);
Packit 5d935b
    $tab_split[1] -= $head_split[2];        # subtract old checksum
Packit 5d935b
    $csum -= $head_split[2] * 2;            # twice because had double effect
Packit 5d935b
                                            # already
Packit 5d935b
    if ($csum < 0 ) { $csum += 0xffffffff; $csum++; }
Packit 5d935b
    $head_split[2] = 0xB1B0AFBA - $csum;    # calculate new checksum
Packit 5d935b
    seek (OUTFILE, 12 + $tab_split[0] * 16, 0);
Packit 5d935b
    print OUTFILE pack("A4N3", "head", @tab_split[1..3]);
Packit 5d935b
    seek (OUTFILE, $tab_split[2], 0);       # rewrite first bit of "head" table
Packit 5d935b
    print OUTFILE pack("N3", @head_split);
Packit 5d935b
Packit 5d935b
    # finish up
Packit 5d935b
    close(OUTFILE);
Packit 5d935b
    close(INFILE);
Packit 5d935b
    }
Packit 5d935b
Packit 5d935b
# support function for sorting by table offset
Packit 5d935b
sub byoffset {
Packit 5d935b
    @t1 = split(':', $dir{$a});
Packit 5d935b
    @t2 = split(':', $dir{$b});
Packit 5d935b
    return 1 if ($t1[2] == -1);     # put inserted tables at the end
Packit 5d935b
    return -1 if ($t2[2] == -1);
Packit 5d935b
    return $t1[2] <=> $t2[2];
Packit 5d935b
    }
Packit 5d935b
Packit 5d935b
# support function for sorting by directory entry order
Packit 5d935b
sub byindex {
Packit 5d935b
    $t1 = split(':', $dir{$a}, 1);
Packit 5d935b
    $t2 = split(':', $dir{$b}, 1);
Packit 5d935b
    return $t1 <=> $t2;
Packit 5d935b
    }
Packit 5d935b
Packit 5d935b
# default table action: copies a table from input to output, recalculating
Packit 5d935b
#   the checksum (just to be absolutely sure).
Packit 5d935b
sub copytab {
Packit 5d935b
    local(*INFILE, *OUTFILE, $len, $csum) = @_;
Packit 5d935b
Packit 5d935b
    while ($len > 0)
Packit 5d935b
        {
Packit 5d935b
        $count = ($len > 8192) ? 8192 : $len;       # 8K buffering
Packit 5d935b
        read(INFILE, $buf, $count) == $count || die "Copying";
Packit 5d935b
        $buf .= "\0" x (4 - ($count & 3)) if ($count & 3);      # pad to long
Packit 5d935b
        print OUTFILE $buf;
Packit 5d935b
        $csum += unpack("%32N*", $buf);
Packit 5d935b
        if ($csum > 0xffffffff) { $csum -= 0xffffffff; $csum--; }
Packit 5d935b
        $len -= $count;
Packit 5d935b
        }
Packit 5d935b
    ($_[2], $csum);
Packit 5d935b
    }
Packit 5d935b
Packit 5d935b
# test routine to copy file from input to output, no changes
Packit 5d935b
package main;
Packit 5d935b
Packit 5d935b
if ($test_package)
Packit 5d935b
    {
Packit 5d935b
    &ttfmod($ARGV[0], $ARGV[1], *dummy);
Packit 5d935b
    }
Packit 5d935b
else
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 script 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
=cut