def merge_font(fonts): # choose the largest font as the base font fonts.sort(key=lambda f: len(f['Data'] or b''), reverse=True) base_font = fonts[0] t0_font = next(f for f in fonts if f['DescendantFont'] == base_font['Reference']) descendant_fonts = [f for f in fonts if f['Subtype'] != 'Type0'] t0_fonts = [f for f in fonts if f['Subtype'] == 'Type0'] references_to_drop = tuple(f['Reference'] for f in fonts if f is not base_font and f is not t0_font) if fonts_are_identical(descendant_fonts): return t0_font, base_font, references_to_drop cmaps = list(filter(None, (f['ToUnicode'] for f in t0_fonts))) if 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(*(f['sfnt'] for f in descendant_fonts)) widths = [] arrays = tuple(filter(None, (f['W'] for f in descendant_fonts))) if arrays: for gid in all_glyph_ids_in_w_arrays(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))) if arrays: for gid in all_glyph_ids_in_w_arrays(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
def merge_font_files(fonts, log): # As of Qt 5.15.1 Chromium has switched to harfbuzz and dropped sfntly. It # now produces font descriptors whose W arrays dont match the glyph width # information from the hhea table, in contravention of the PDF spec. So # we can no longer merge font descriptors, all we can do is merge the # actual sfnt data streams into a single stream and subset it to contain # only the glyphs from all W arrays. # choose the largest font as the base font fonts.sort(key=lambda f: len(f['Data'] or b''), reverse=True) descendant_fonts = [f for f in fonts if f['Subtype'] != 'Type0'] total_size = sum(len(f['Data']) for f in descendant_fonts) merged_sfnt = merge_truetype_fonts_for_pdf( tuple(f['sfnt'] for f in descendant_fonts), log) w_arrays = tuple(filter(None, (f['W'] for f in descendant_fonts))) glyph_ids = all_glyph_ids_in_w_arrays(w_arrays, as_set=True) h_arrays = tuple(filter(None, (f['W2'] for f in descendant_fonts))) glyph_ids |= all_glyph_ids_in_w_arrays(h_arrays, as_set=True) try: pdf_subset(merged_sfnt, glyph_ids) except NoGlyphs: log.warn( f'Subsetting of {fonts[0]["BaseFont"]} failed with no glyphs found, ignoring' ) font_data = merged_sfnt()[0] log(f'Merged {len(fonts)} instances of {fonts[0]["BaseFont"]} reducing size from {human_readable(total_size)} to {human_readable(len(font_data))}' ) return font_data, tuple(f['Reference'] for f in descendant_fonts)