mirror of
https://github.com/kovidgoyal/calibre.git
synced 2025-07-09 03:04:10 -04:00
Round-tripping for the sfnt container format
This commit is contained in:
parent
023ad0f43b
commit
7b4fe7f8ee
21
src/calibre/utils/fonts/sfnt/__init__.py
Normal file
21
src/calibre/utils/fonts/sfnt/__init__.py
Normal file
@ -0,0 +1,21 @@
|
||||
#!/usr/bin/env python
|
||||
# vim:fileencoding=UTF-8:ts=4:sw=4:sta:et:sts=4:fdm=marker:ai
|
||||
from __future__ import (unicode_literals, division, absolute_import,
|
||||
print_function)
|
||||
|
||||
__license__ = 'GPL v3'
|
||||
__copyright__ = '2012, Kovid Goyal <kovid at kovidgoyal.net>'
|
||||
__docformat__ = 'restructuredtext en'
|
||||
|
||||
|
||||
def align_block(raw, multiple=4, pad=b'\0'):
|
||||
'''
|
||||
Return raw with enough pad bytes append to ensure its length is a multiple
|
||||
of 4.
|
||||
'''
|
||||
extra = len(raw) % multiple
|
||||
if extra == 0: return raw
|
||||
return raw + pad*(multiple - extra)
|
||||
|
||||
|
||||
|
102
src/calibre/utils/fonts/sfnt/container.py
Normal file
102
src/calibre/utils/fonts/sfnt/container.py
Normal file
@ -0,0 +1,102 @@
|
||||
#!/usr/bin/env python
|
||||
# vim:fileencoding=UTF-8:ts=4:sw=4:sta:et:sts=4:fdm=marker:ai
|
||||
from __future__ import (unicode_literals, division, absolute_import,
|
||||
print_function)
|
||||
|
||||
__license__ = 'GPL v3'
|
||||
__copyright__ = '2012, Kovid Goyal <kovid at kovidgoyal.net>'
|
||||
__docformat__ = 'restructuredtext en'
|
||||
|
||||
from math import log
|
||||
from struct import pack, calcsize
|
||||
from io import BytesIO
|
||||
|
||||
from calibre.utils.fonts.utils import (get_tables, checksum_of_block,
|
||||
verify_checksums)
|
||||
from calibre.utils.fonts.sfnt import align_block
|
||||
|
||||
class UnsupportedFont(ValueError):
|
||||
pass
|
||||
|
||||
class UnknownTable(object):
|
||||
|
||||
def __init__(self, raw):
|
||||
self.raw = raw
|
||||
|
||||
def __call__(self):
|
||||
return self.raw
|
||||
|
||||
class Sfnt(object):
|
||||
|
||||
def __init__(self, raw):
|
||||
self.sfnt_version = raw[:4]
|
||||
if self.sfnt_version not in {b'\x00\x01\x00\x00', b'OTTO', b'true',
|
||||
b'type1'}:
|
||||
raise UnsupportedFont('Font has unknown sfnt version: %r'%self.sfnt_version)
|
||||
self.read_tables(raw)
|
||||
|
||||
def read_tables(self, raw):
|
||||
self.tables = {}
|
||||
for table_tag, table, table_index, table_offset, table_checksum in get_tables(raw):
|
||||
self.tables[table_tag] = {
|
||||
}.get(table_tag, UnknownTable)(table)
|
||||
|
||||
def __call__(self):
|
||||
stream = BytesIO()
|
||||
|
||||
def spack(*args):
|
||||
stream.write(pack(*args))
|
||||
|
||||
stream.seek(0)
|
||||
|
||||
# Write header
|
||||
num_tables = len(self.tables)
|
||||
ln2 = int(log(num_tables, 2))
|
||||
srange = (2**ln2) * 16
|
||||
spack(b'>4s4H',
|
||||
self.sfnt_version, num_tables, srange, ln2, num_tables * 16 - srange)
|
||||
|
||||
# Write tables
|
||||
head_offset = None
|
||||
table_data = []
|
||||
offset = stream.tell() + ( calcsize(b'>4s3L') * num_tables )
|
||||
for tag in sorted(self.tables):
|
||||
table = self.tables[tag]
|
||||
raw = table()
|
||||
table_len = len(raw)
|
||||
if tag == b'head':
|
||||
head_offset = offset
|
||||
raw = raw[:8] + b'\0\0\0\0' + raw[12:]
|
||||
raw = align_block(raw)
|
||||
checksum = checksum_of_block(raw)
|
||||
spack(b'>4s3L', tag, checksum, offset, table_len)
|
||||
offset += len(raw)
|
||||
table_data.append(raw)
|
||||
|
||||
for x in table_data:
|
||||
stream.write(x)
|
||||
|
||||
checksum = checksum_of_block(stream.getvalue())
|
||||
q = (0xB1B0AFBA - checksum) & 0xffffffff
|
||||
stream.seek(head_offset + 8)
|
||||
spack(b'>L', q)
|
||||
|
||||
return stream.getvalue()
|
||||
|
||||
def test_roundtrip(ff=None):
|
||||
if ff is None:
|
||||
data = P('fonts/liberation/LiberationSerif-Regular.ttf', data=True)
|
||||
else:
|
||||
with open(ff, 'rb') as f:
|
||||
data = f.read()
|
||||
rd = Sfnt(data)()
|
||||
verify_checksums(rd)
|
||||
if data[:12] != rd[:12]:
|
||||
raise ValueError('Roundtripping failed, font header not the same')
|
||||
if len(data) != len(rd):
|
||||
raise ValueError('Roundtripping failed, size different')
|
||||
|
||||
if __name__ == '__main__':
|
||||
import sys
|
||||
test_roundtrip(sys.argv[-1])
|
||||
|
Loading…
x
Reference in New Issue
Block a user