PDF Output: yet another glyph width fix for change in Chromium output

Apparently, the HarfBuzz subsetter does not produce mergeable hhea tables,
so we directly merge the W arrays instead, picking the widest width for
each glyph id. Lord knows if the glyph ids are stable across fonts, if
they aren't then we will need to do major surgery.

Fixes #1907675 [MacOS 10.15.7 EPUB to PDF conversion](https://bugs.launchpad.net/calibre/+bug/1907675)
This commit is contained in:
Kovid Goyal 2020-12-11 17:07:04 +05:30
parent 44f2d0e073
commit 24ac765178
No known key found for this signature in database
GPG Key ID: 06BC317B515ACE7C
2 changed files with 42 additions and 28 deletions

View File

@ -799,6 +799,43 @@ def merge_w_arrays(arrays):
return ans return ans
def width_map_from_w_array(w):
ans = {}
i = 0
while i + 1 < len(w):
elem = w[i]
next_elem = w[i+1]
if isinstance(next_elem, list):
for gid, width in zip(range(elem, elem + len(next_elem)), next_elem):
ans[gid] = width
i += 2
else:
try:
width = w[i+2]
except IndexError:
width = 0
for gid in range(elem, next_elem + 1):
ans[gid] = width
i += 3
return ans
def merge_w_arrays_directly(arrays):
width_maps = tuple(map(width_map_from_w_array, arrays))
def getter(gid):
return max(m.get(gid, 0) for m in width_maps)
all_gids = set()
for m in width_maps:
all_gids |= set(m)
widths = []
for gid in sorted(all_gids):
widths.extend((gid, gid, getter(gid)))
return merge_w_arrays((widths,))
class CMap(object): class CMap(object):
def __init__(self): def __init__(self):
@ -933,18 +970,13 @@ def merge_font(fonts, log):
cmaps = list(filter(None, (f['ToUnicode'] for f in t0_fonts))) cmaps = list(filter(None, (f['ToUnicode'] for f in t0_fonts)))
if cmaps: if cmaps:
t0_font['ToUnicode'] = as_bytes(merge_cmaps(cmaps)) t0_font['ToUnicode'] = as_bytes(merge_cmaps(cmaps))
base_font['sfnt'], width_for_glyph_id, height_for_glyph_id = merge_truetype_fonts_for_pdf(tuple(f['sfnt'] for f in descendant_fonts), log) base_font['sfnt'] = merge_truetype_fonts_for_pdf(tuple(f['sfnt'] for f in descendant_fonts), log)
widths = []
arrays = tuple(filter(None, (f['W'] for f in descendant_fonts))) arrays = tuple(filter(None, (f['W'] for f in descendant_fonts)))
if arrays: if arrays:
for gid in all_glyph_ids_in_w_arrays(arrays): base_font['W'] = merge_w_arrays_directly(arrays)
widths.append(gid), widths.append(gid), widths.append(1000*width_for_glyph_id(gid))
base_font['W'] = merge_w_arrays((widths,))
arrays = tuple(filter(None, (f['W2'] for f in descendant_fonts))) arrays = tuple(filter(None, (f['W2'] for f in descendant_fonts)))
if arrays: if arrays:
for gid in all_glyph_ids_in_w_arrays(arrays): base_font['W2'] = merge_w_arrays_directly(arrays)
widths.append(gid), widths.append(gid), widths.append(1000*height_for_glyph_id(gid))
base_font['W2'] = merge_w_arrays((widths,))
return t0_font, base_font, references_to_drop return t0_font, base_font, references_to_drop

View File

@ -15,6 +15,7 @@ def merge_truetype_fonts_for_pdf(fonts, log=None):
# only merges the glyf and loca tables, ignoring all other tables # only merges the glyf and loca tables, ignoring all other tables
all_glyphs = {} all_glyphs = {}
ans = fonts[0] ans = fonts[0]
for font in fonts: for font in fonts:
loca = font[b'loca'] loca = font[b'loca']
glyf = font[b'glyf'] glyf = font[b'glyf']
@ -35,25 +36,6 @@ def merge_truetype_fonts_for_pdf(fonts, log=None):
head = ans[b'head'] head = ans[b'head']
loca = ans[b'loca'] loca = ans[b'loca']
maxp = ans[b'maxp'] maxp = ans[b'maxp']
advance_widths = advance_heights = (0,)
hhea = ans.get(b'hhea')
if hhea is not None:
hhea.read_data(ans[b'hmtx'])
advance_widths = tuple(x/head.units_per_em for x in hhea.advance_widths)
vhea = ans.get(b'vhea')
if vhea is not None:
vhea.read_data(ans[b'vmtx'])
advance_heights = tuple(x/head.units_per_em for x in vhea.advance_heights)
def width_for_glyph_id(gid):
if gid >= len(advance_widths):
gid = -1
return advance_widths[gid]
def height_for_glyph_id(gid):
if gid >= len(advance_heights):
gid = -1
return advance_heights[gid]
gmap = OrderedDict() gmap = OrderedDict()
for glyph_id in sorted(all_glyphs): for glyph_id in sorted(all_glyphs):
@ -64,4 +46,4 @@ def merge_truetype_fonts_for_pdf(fonts, log=None):
head.update() head.update()
maxp.num_glyphs = len(loca.offset_map) - 1 maxp.num_glyphs = len(loca.offset_map) - 1
maxp.update() maxp.update()
return ans, width_for_glyph_id, height_for_glyph_id return ans