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)
def embed(self, objects): self.font_descriptor['FontFile'+('3' if self.is_otf else '2') ] = objects.add(self.font_stream) self.write_widths(objects) self.write_to_unicode(objects) pdf_subset(self.metrics.sfnt, self.used_glyphs) if self.is_otf: self.font_stream.write(self.metrics.sfnt['CFF '].raw) else: self.metrics.os2.zero_fstype() self.metrics.sfnt(self.font_stream)
def embed(self, objects, debug): self.font_descriptor['FontFile'+('3' if self.is_otf else '2') ] = objects.add(self.font_stream) self.write_widths(objects) self.write_to_unicode(objects) try: pdf_subset(self.metrics.sfnt, self.used_glyphs) except UnsupportedFont as e: debug('Subsetting of %s not supported, embedding full font. Error: %s'%( self.metrics.names.get('full_name', 'Unknown'), as_unicode(e))) if self.is_otf: self.font_stream.write(self.metrics.sfnt['CFF '].raw) else: self.metrics.os2.zero_fstype() self.metrics.sfnt(self.font_stream)
def embed(self, objects, debug): self.font_descriptor['FontFile' + ('3' if self.is_otf else '2')] = objects.add( self.font_stream) self.write_widths(objects) self.write_to_unicode(objects) try: pdf_subset(self.metrics.sfnt, self.used_glyphs) except UnsupportedFont as e: debug( 'Subsetting of %s not supported, embedding full font. Error: %s' % (self.metrics.names.get('full_name', 'Unknown'), as_unicode(e))) if self.is_otf: self.font_stream.write(self.metrics.sfnt['CFF '].raw) else: self.metrics.os2.zero_fstype() self.metrics.sfnt(self.font_stream)
def subset_fonts(pdf_doc, log): all_fonts = pdf_doc.list_fonts(True) for font in all_fonts: if font['Subtype'] != 'Type0' and font['Data']: try: sfnt = Sfnt(font['Data']) except UnsupportedFont: continue if b'glyf' not in sfnt: continue num, gen = font['Reference'] glyphs = all_glyph_ids_in_w_arrays( (font['W'] or (), font['W2'] or ()), as_set=True) pdf_subset(sfnt, glyphs) data = sfnt()[0] log('Subset embedded font from: {} to {}'.format( human_readable(len(font['Data'])), human_readable(len(data)))) pdf_doc.replace_font_data(data, num, gen)
def embed(self, objects, debug): self.font_descriptor['FontFile'+('3' if self.is_otf else '2') ] = objects.add(self.font_stream) self.write_widths(objects) self.write_to_unicode(objects) try: pdf_subset(self.metrics.sfnt, self.used_glyphs) except UnsupportedFont as e: debug('Subsetting of %s not supported, embedding full font. Error: %s'%( self.metrics.names.get('full_name', 'Unknown'), as_unicode(e))) except NoGlyphs: if self.used_glyphs: debug( 'Subsetting of %s failed, font appears to have no glyphs for the %d characters it is used with, some text may not be rendered in the PDF' % (self.metrics.names.get('full_name', 'Unknown'), len(self.used_glyphs))) if self.is_otf: self.font_stream.write(self.metrics.sfnt['CFF '].raw) else: self.metrics.os2.zero_fstype() self.metrics.sfnt(self.font_stream)