1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
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()
|