PDF Output: Fix font kerning issues with some TrueType fonts. Fixes #1929240 [Kerning problems with generated pdf](https://bugs.launchpad.net/calibre/+bug/1929240)

Preserve glyph ids when merging font copies in the PDF. Also merge the
glyph metrics tables, not just the shape tables.
This commit is contained in:
Kovid Goyal 2021-05-22 09:02:15 +05:30
parent d696927d7e
commit dfb86551f8
No known key found for this signature in database
GPG Key ID: 06BC317B515ACE7C

View File

@ -12,25 +12,51 @@ class GlyphSizeMismatch(ValueError):
def merge_truetype_fonts_for_pdf(fonts, log=None): def merge_truetype_fonts_for_pdf(fonts, log=None):
# only merges the glyf and loca tables, ignoring all other tables
all_glyphs = {} all_glyphs = {}
ans = fonts[0] ans = fonts[0]
hmetrics_map = {}
vmetrics_map = {}
for font in fonts: for font in fonts:
loca = font[b'loca'] loca = font[b'loca']
glyf = font[b'glyf'] glyf = font[b'glyf']
num_glyphs = font[b'maxp'].num_glyphs
loca.load_offsets(font[b'head'], font[b'maxp']) loca.load_offsets(font[b'head'], font[b'maxp'])
try:
hhea = font[b'hhea']
except KeyError:
hhea = None
else:
hhea.read_data(font[b'hmtx'], num_glyphs)
try:
vhea = font[b'vhea']
except KeyError:
vhea = None
else:
vhea.read_data(font[b'vmtx'], num_glyphs)
for glyph_id in range(len(loca.offset_map) - 1): for glyph_id in range(len(loca.offset_map) - 1):
offset, sz = loca.glyph_location(glyph_id) offset, sz = loca.glyph_location(glyph_id)
if sz > 0:
prev_glyph_data = all_glyphs.get(glyph_id) prev_glyph_data = all_glyphs.get(glyph_id)
if prev_glyph_data is None: if not prev_glyph_data:
all_glyphs[glyph_id] = glyf.glyph_data(offset, sz, as_raw=True) all_glyphs[glyph_id] = glyf.glyph_data(offset, sz, as_raw=True)
else: if hhea is not None:
hmetrics_map[glyph_id] = hhea.metrics_for(glyph_id)
if vhea is not None:
vmetrics_map[glyph_id] = vhea.metrics_for(glyph_id)
elif sz > 0:
if abs(sz - len(prev_glyph_data)) > 8: if abs(sz - len(prev_glyph_data)) > 8:
# raise Exception('Size mismatch for glyph id: {} prev_sz: {} sz: {}'.format(glyph_id, len(prev_glyph_data), sz)) # raise Exception('Size mismatch for glyph id: {} prev_sz: {} sz: {}'.format(glyph_id, len(prev_glyph_data), sz))
if log is not None: if log is not None:
log('Size mismatch for glyph id: {} prev_sz: {} sz: {}'.format(glyph_id, len(prev_glyph_data), sz)) log('Size mismatch for glyph id: {} prev_sz: {} sz: {}'.format(glyph_id, len(prev_glyph_data), sz))
if hhea is not None:
m = hhea.metrics_for(glyph_id)
if m != hmetrics_map[glyph_id]:
log(f'Metrics mismatch for glyph id: {glyph_id} prev: {hmetrics_map[glyph_id]} cur: {m}')
if vhea is not None:
m = vhea.metrics_for(glyph_id)
if m != vmetrics_map[glyph_id]:
log(f'Metrics mismatch for glyph id: {glyph_id} prev: {vmetrics_map[glyph_id]} cur: {m}')
glyf = ans[b'glyf'] glyf = ans[b'glyf']
head = ans[b'head'] head = ans[b'head']
@ -46,4 +72,11 @@ 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()
if hmetrics_map:
ans[b'hhea'].update(hmetrics_map, ans[b'hmtx'])
if vmetrics_map:
ans[b'vhea'].update(vmetrics_map, ans[b'vmtx'])
for name in 'hdmx GPOS GSUB'.split():
ans.pop(name.encode('ascii'), None)
return ans return ans