diff options
Diffstat (limited to '')
-rw-r--r-- | src/silfont/scripts/psfcompressgr.py | 100 |
1 files changed, 100 insertions, 0 deletions
diff --git a/src/silfont/scripts/psfcompressgr.py b/src/silfont/scripts/psfcompressgr.py new file mode 100644 index 0000000..67d0f33 --- /dev/null +++ b/src/silfont/scripts/psfcompressgr.py @@ -0,0 +1,100 @@ +#!/usr/bin/env python3 +__doc__ = 'Compress Graphite tables in a font' +__url__ = 'https://github.com/silnrsi/pysilfont' +__copyright__ = 'Copyright (c) 2017 SIL International (https://www.sil.org)' +__license__ = 'Released under the MIT License (https://opensource.org/licenses/MIT)' +__author__ = 'Martin Hosken' + +argspec = [ + ('ifont',{'help': 'Input TTF'}, {'type': 'infont'}), + ('ofont',{'help': 'Output TTF','nargs': '?' }, {'type': 'outfont'}), + ('-l','--log',{'help': 'Optional log file'}, {'type': 'outfile', 'def': '_compressgr', 'optlog': True}) +] + +from silfont.core import execute +from fontTools.ttLib.tables.DefaultTable import DefaultTable +import lz4.block +import sys, struct + +class lz4tuple(object) : + def __init__(self, start) : + self.start = start + self.literal = start + self.literal_len = 0 + self.match_dist = 0 + self.match_len = 0 + self.end = 0 + + def __str__(self) : + return "lz4tuple(@{},{}+{},-{}+{})={}".format(self.start, self.literal, self.literal_len, self.match_dist, self.match_len, self.end) + +def read_literal(t, dat, start, datlen) : + if t == 15 and start < datlen : + v = ord(dat[start:start+1]) + t += v + while v == 0xFF and start < datlen : + start += 1 + v = ord(dat[start:start+1]) + t += v + start += 1 + return (t, start) + +def write_literal(num, shift) : + res = [] + if num > 14 : + res.append(15 << shift) + num -= 15 + while num > 255 : + res.append(255) + num -= 255 + res.append(num) + else : + res.append(num << shift) + return bytearray(res) + +def parseTuple(dat, start, datlen) : + res = lz4tuple(start) + token = ord(dat[start:start+1]) + (res.literal_len, start) = read_literal(token >> 4, dat, start+1, datlen) + res.literal = start + start += res.literal_len + res.end = start + if start > datlen - 2 : + return res + res.match_dist = ord(dat[start:start+1]) + (ord(dat[start+1:start+2]) << 8) + start += 2 + (res.match_len, start) = read_literal(token & 0xF, dat, start, datlen) + res.end = start + return res + +def compressGr(dat, version) : + if ord(dat[1:2]) < version : + vstr = bytes([version]) if sys.version_info.major > 2 else chr(version) + dat = dat[0:1] + vstr + dat[2:] + datc = lz4.block.compress(dat[:-4], mode='high_compression', compression=16, store_size=False) + # now find the final tuple + end = len(datc) + start = 0 + curr = lz4tuple(start) + while curr.end < end : + start = curr.end + curr = parseTuple(datc, start, end) + if curr.end > end : + print("Sync error: {!s}".format(curr)) + newend = write_literal(curr.literal_len + 4, 4) + datc[curr.literal:curr.literal+curr.literal_len+1] + dat[-4:] + lz4hdr = struct.pack(">L", (1 << 27) + (len(dat) & 0x7FFFFFF)) + return dat[0:4] + lz4hdr + datc[0:curr.start] + newend + +def doit(args) : + infont = args.ifont + for tag, version in (('Silf', 5), ('Glat', 3)) : + dat = infont.getTableData(tag) + newdat = bytes(compressGr(dat, version)) + table = DefaultTable(tag) + table.decompile(newdat, infont) + infont[tag] = table + return infont + +def cmd() : execute('FT', doit, argspec) +if __name__ == "__main__" : cmd() + |