|
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) = ©tab(*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
|