Fix merging of loca tables

This commit is contained in:
Kovid Goyal 2019-07-28 20:46:12 +05:30
parent dd5961af10
commit e77df26174
No known key found for this signature in database
GPG Key ID: 06BC317B515ACE7C
4 changed files with 39 additions and 21 deletions

View File

@ -6,7 +6,7 @@ __license__ = 'GPL v3'
__copyright__ = '2012, Kovid Goyal <kovid at kovidgoyal.net>'
__docformat__ = 'restructuredtext en'
from struct import calcsize, unpack_from, pack
import array, sys
from operator import itemgetter
from itertools import repeat
@ -14,16 +14,22 @@ from calibre.utils.fonts.sfnt import UnknownTable
from polyglot.builtins import iteritems, range
def four_byte_type_code():
for c in 'IL':
a = array.array(c)
if a.itemsize == 4:
return c
class LocaTable(UnknownTable):
def load_offsets(self, head_table, maxp_table):
fmt = 'H' if head_table.index_to_loc_format == 0 else 'L'
num_glyphs = maxp_table.num_glyphs
sz = calcsize(('>%s'%fmt).encode('ascii'))
num = len(self.raw)//sz
self.offset_map = unpack_from(('>%d%s'%(num, fmt)).encode('ascii'),
self.raw)
self.offset_map = self.offset_map[:num_glyphs+1]
fmt = 'H' if head_table.index_to_loc_format == 0 else four_byte_type_code()
locs = array.array(fmt)
locs.fromstring(self.raw)
if sys.byteorder != "big":
locs.byteswap()
self.offset_map = locs.tolist()
if fmt == 'H':
self.offset_map = [2*i for i in self.offset_map]
self.fmt = fmt
@ -37,8 +43,14 @@ class LocaTable(UnknownTable):
'''
Update this table to contain pointers only to the glyphs in
resolved_glyph_map which must be a map of glyph_ids to (offset, sz)
Note that the loca table is generated for all glyphs from 0 to the
largest glyph that is either in resolved_glyph_map or was present
originally. The pointers to glyphs that have no data will be set to
zero. This preserves glyph ids.
'''
current_max_glyph_id = len(self.offset_map) - 2
max_glyph_id = max(resolved_glyph_map or (0,))
max_glyph_id = max(max_glyph_id, current_max_glyph_id)
self.offset_map = list(repeat(0, max_glyph_id + 2))
glyphs = [(glyph_id, x[0], x[1]) for glyph_id, x in
iteritems(resolved_glyph_map)]
@ -54,16 +66,16 @@ class LocaTable(UnknownTable):
vals = self.offset_map
max_offset = max(vals) if vals else 0
max_short_offset = 65535 * 2
if self.fmt == 'L' and max_offset <= max_short_offset:
if max_offset < 0x20000 and all(l % 2 == 0 for l in vals):
self.fmt = 'H'
if self.fmt == 'H':
if max_offset > max_short_offset:
self.fmt = 'L'
else:
vals = [i//2 for i in vals]
vals = array.array(self.fmt, (i // 2 for i in vals))
else:
self.fmt = four_byte_type_code()
vals = array.array(self.fmt, vals)
self.raw = pack(('>%d%s'%(len(vals), self.fmt)).encode('ascii'), *vals)
if sys.byteorder != "big":
vals.byteswap()
self.raw = vals.tostring()
subset = update
def dump_glyphs(self, sfnt):

View File

@ -42,5 +42,5 @@ class MaxpTable(UnknownTable):
setattr(self, f, val)
def update(self):
vals = [getattr(self, f) for f in self._fields]
vals = [getattr(self, f) for f in self.fields]
self.raw = pack(self._fmt, *vals)

View File

@ -11,6 +11,7 @@ from functools import partial
def merge_truetype_fonts_for_pdf(*fonts):
# only merges the glyf and loca tables, ignoring all other tables
all_glyphs = {}
ans = fonts[0]
for font in fonts:
loca = font[b'loca']
glyf = font[b'glyf']
@ -21,12 +22,17 @@ def merge_truetype_fonts_for_pdf(*fonts):
if sz > 0:
all_glyphs[glyph_id] = glyf.glyph_data(offset, sz, as_raw=True)
ans = fonts[0]
loca = ans[b'loca']
glyf = ans[b'glyf']
head = ans[b'head']
loca = ans[b'loca']
maxp = ans[b'maxp']
gmap = OrderedDict()
for glyph_id in sorted(all_glyphs):
gmap[glyph_id] = partial(all_glyphs.__getitem__, glyph_id)
offset_map = glyf.update(gmap)
loca.update(offset_map)
head.index_to_loc_format = 0 if loca.fmt == 'H' else 1
head.update()
maxp.num_glyphs = len(loca.offset_map) - 1
maxp.update()
return ans

View File

@ -64,9 +64,9 @@ def subset_truetype(sfnt, character_map, extra_glyphs):
# Update the loca table
loca.subset(glyph_offset_map)
head.index_to_loc_format = 1 if loca.fmt == 'L' else 0
head.index_to_loc_format = 0 if loca.fmt == 'H' else 1
head.update()
maxp.num_glyphs = len(glyph_offset_map)
maxp.num_glyphs = len(loca.offset_map) - 1
# }}}