-
Notifications
You must be signed in to change notification settings - Fork 92
/
woff2otf.py
executable file
·110 lines (94 loc) · 4.7 KB
/
woff2otf.py
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
101
102
103
104
105
106
107
108
109
110
#!/usr/bin/env python3
#
# Copyright 2012, Steffen Hanikel (https://github.com/hanikesn)
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#
# A tool to convert a WOFF back to a TTF/OTF font file, in pure Python
import struct
import sys
import zlib
def convert_streams(infile, outfile):
WOFFHeader = {'signature': struct.unpack(">I", infile.read(4))[0],
'flavor': struct.unpack(">I", infile.read(4))[0],
'length': struct.unpack(">I", infile.read(4))[0],
'numTables': struct.unpack(">H", infile.read(2))[0],
'reserved': struct.unpack(">H", infile.read(2))[0],
'totalSfntSize': struct.unpack(">I", infile.read(4))[0],
'majorVersion': struct.unpack(">H", infile.read(2))[0],
'minorVersion': struct.unpack(">H", infile.read(2))[0],
'metaOffset': struct.unpack(">I", infile.read(4))[0],
'metaLength': struct.unpack(">I", infile.read(4))[0],
'metaOrigLength': struct.unpack(">I", infile.read(4))[0],
'privOffset': struct.unpack(">I", infile.read(4))[0],
'privLength': struct.unpack(">I", infile.read(4))[0]}
outfile.write(struct.pack(">I", WOFFHeader['flavor']));
outfile.write(struct.pack(">H", WOFFHeader['numTables']));
maximum = list(filter(lambda x: x[1] <= WOFFHeader['numTables'], [(n, 2**n) for n in range(64)]))[-1];
searchRange = maximum[1] * 16
outfile.write(struct.pack(">H", searchRange));
entrySelector = maximum[0]
outfile.write(struct.pack(">H", entrySelector));
rangeShift = WOFFHeader['numTables'] * 16 - searchRange;
outfile.write(struct.pack(">H", rangeShift));
offset = outfile.tell()
TableDirectoryEntries = []
for i in range(0, WOFFHeader['numTables']):
TableDirectoryEntries.append({'tag': struct.unpack(">I", infile.read(4))[0],
'offset': struct.unpack(">I", infile.read(4))[0],
'compLength': struct.unpack(">I", infile.read(4))[0],
'origLength': struct.unpack(">I", infile.read(4))[0],
'origChecksum': struct.unpack(">I", infile.read(4))[0]})
offset += 4*4
for TableDirectoryEntry in TableDirectoryEntries:
outfile.write(struct.pack(">I", TableDirectoryEntry['tag']))
outfile.write(struct.pack(">I", TableDirectoryEntry['origChecksum']))
outfile.write(struct.pack(">I", offset))
outfile.write(struct.pack(">I", TableDirectoryEntry['origLength']))
TableDirectoryEntry['outOffset'] = offset
offset += TableDirectoryEntry['origLength']
if (offset % 4) != 0:
offset += 4 - (offset % 4)
for TableDirectoryEntry in TableDirectoryEntries:
infile.seek(TableDirectoryEntry['offset'])
compressedData = infile.read(TableDirectoryEntry['compLength'])
if TableDirectoryEntry['compLength'] != TableDirectoryEntry['origLength']:
uncompressedData = zlib.decompress(compressedData)
else:
uncompressedData = compressedData
outfile.seek(TableDirectoryEntry['outOffset'])
outfile.write(uncompressedData)
offset = TableDirectoryEntry['outOffset'] + TableDirectoryEntry['origLength'];
padding = 0
if (offset % 4) != 0:
padding = 4 - (offset % 4)
outfile.write(bytearray(padding));
def convert(infilename, outfilename):
with open(infilename , mode='rb') as infile:
with open(outfilename, mode='wb') as outfile:
convert_streams(infile, outfile)
def main(argv):
if len(argv) == 1 or len(argv) > 3:
print('I convert *.woff files to *.otf files. (one at a time :)\n'
'Usage: woff2otf.py web_font.woff [converted_filename.otf]\n'
'If the target file name is omitted, it will be guessed. Have fun!\n')
return
source_file_name = argv[1]
if len(argv) == 3:
target_file_name = argv[2]
else:
target_file_name = source_file_name.rsplit('.', 1)[0] + '.otf'
convert(source_file_name, target_file_name)
return 0
if __name__ == '__main__':
sys.exit(main(sys.argv))