Skip to content

Instantly share code, notes, and snippets.

@kabeer11000
Created July 25, 2022 18:46
Show Gist options
  • Save kabeer11000/baaf94765c53e6795eac2a1e88c120b6 to your computer and use it in GitHub Desktop.
Save kabeer11000/baaf94765c53e6795eac2a1e88c120b6 to your computer and use it in GitHub Desktop.
#!/usr/bin/env python3
#
# Copyright 2022, Kabeer's Network (Modified WOFF2TTF utility) (https://kabeersnetwork.tk/)
# Initial 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))
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment