def create_sfnt(self, text_item): get_table = partial(self.qt_hack.get_sfnt_table, text_item) try: ans = Font(Sfnt(get_table)) except UnsupportedFont as e: raise UnsupportedFont('The font %s is not a valid sfnt. Error: %s'%( text_item.font().family(), e)) glyph_map = self.qt_hack.get_glyph_map(text_item) gm = {} for uc, glyph_id in enumerate(glyph_map): if glyph_id not in gm: gm[glyph_id] = unichr(uc) ans.full_glyph_map = gm return ans
def mergeable(fonts): has_type0 = False for font in fonts: if font['Subtype'] == 'Type0': has_type0 = True if not font['Encoding'] or not font['Encoding'].startswith('Identity-'): return False else: if not font['Data']: return False try: sfnt = Sfnt(font['Data']) except UnsupportedFont: return False font['sfnt'] = sfnt if b'glyf' not in sfnt: return False return has_type0
def create_sfnt(self, text_item): get_table = partial(self.qt_hack.get_sfnt_table, text_item) try: ans = Font(Sfnt(get_table)) except UnsupportedFont as e: raise UnsupportedFont( 'The font %s is not a valid sfnt. Error: %s' % (text_item.font().family(), e)) glyph_map = self.qt_hack.get_glyph_map(text_item) gm = {} ans.ignore_glyphs = set() for uc, glyph_id in enumerate(glyph_map): if glyph_id not in gm: gm[glyph_id] = codepoint_to_chr(uc) if uc in (0xad, 0x200b): ans.ignore_glyphs.add(glyph_id) ans.full_glyph_map = gm return ans
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 subset(raw, individual_chars, ranges=(), warnings=None): warn = partial(do_warn, warnings) chars = set(map(ord, individual_chars)) for r in ranges: chars |= set(xrange(ord(r[0]), ord(r[1]) + 1)) # Always add the space character for ease of use from the command line if ord(' ') not in chars: chars.add(ord(' ')) sfnt = Sfnt(raw) old_sizes = sfnt.sizes() # Remove the Digital Signature table since it is useless in a subset # font anyway sfnt.pop(b'DSIG', None) # Remove non core tables as they aren't likely to be used by renderers # anyway core_tables = { b'cmap', b'hhea', b'head', b'hmtx', b'maxp', b'name', b'OS/2', b'post', b'cvt ', b'fpgm', b'glyf', b'loca', b'prep', b'CFF ', b'VORG', b'EBDT', b'EBLC', b'EBSC', b'BASE', b'GSUB', b'GPOS', b'GDEF', b'JSTF', b'gasp', b'hdmx', b'kern', b'LTSH', b'PCLT', b'VDMX', b'vhea', b'vmtx', b'MATH' } for tag in list(sfnt): if tag not in core_tables: del sfnt[tag] try: cmap = sfnt[b'cmap'] except KeyError: raise UnsupportedFont('This font has no cmap table') # Get mapping of chars to glyph ids for all specified chars character_map = cmap.get_character_map(chars) extra_glyphs = set() if b'GSUB' in sfnt: # Parse all substitution rules to ensure that glyphs that can be # substituted for the specified set of glyphs are not removed gsub = sfnt[b'GSUB'] try: gsub.decompile() extra_glyphs = gsub.all_substitutions(character_map.itervalues()) except UnsupportedFont as e: warn('Usupported GSUB table: %s' % e) except Exception as e: warn('Failed to decompile GSUB table:', traceback.format_exc()) if b'loca' in sfnt and b'glyf' in sfnt: # TrueType Outlines subset_truetype(sfnt, character_map, extra_glyphs) elif b'CFF ' in sfnt: # PostScript Outlines subset_postscript(sfnt, character_map, extra_glyphs) else: raise UnsupportedFont('This font does not contain TrueType ' 'or PostScript outlines') # Restrict the cmap table to only contain entries for the resolved glyphs cmap.set_character_map(character_map) if b'kern' in sfnt: try: sfnt[b'kern'].restrict_to_glyphs( frozenset(character_map.itervalues())) except UnsupportedFont as e: warn('kern table unsupported, ignoring: %s' % e) except Exception as e: warn('Subsetting of kern table failed, ignoring:', traceback.format_exc()) raw, new_sizes = sfnt() return raw, old_sizes, new_sizes
cmap = self.cmap.get_character_map(chars) glyph_ids = (cmap[c] for c in chars) pixel_size_x = stretch * pixel_size xscale = pixel_size_x / self.units_per_em return tuple(i * xscale for i in self.glyph_widths(glyph_ids)) def glyph_widths(self, glyph_ids): last = len(self._advance_widths) return tuple(self._advance_widths[i if i < last else -1] for i in glyph_ids) def width(self, string, pixel_size=12.0, stretch=1.0): 'The width of the string at the specified pixel size and stretch, in pixels' return sum(self.advance_widths(string, pixel_size, stretch)) if __name__ == '__main__': import sys from calibre.utils.fonts.sfnt.container import Sfnt with open(sys.argv[-1], 'rb') as f: raw = f.read() sfnt = Sfnt(raw) m = FontMetrics(sfnt) print('Ascent:', m.pdf_ascent) print('Descent:', m.pdf_descent) print('PDF BBox:', m.pdf_bbox) print('CapHeight:', m.pdf_capheight) print('AvgWidth:', m.pdf_avg_width) print('ItalicAngle', m.post.italic_angle) print('StemV', m.pdf_stemv)
def load_font(stream_or_path): raw = stream_or_path if hasattr(raw, 'read'): raw = raw.read() from calibre.utils.fonts.sfnt.container import Sfnt return Sfnt(raw)
def subset(raw, individual_chars, ranges=(), warnings=None): warn = partial(do_warn, warnings) chars = set(map(ord, individual_chars)) for r in ranges: chars |= set(xrange(ord(r[0]), ord(r[1])+1)) # Always add the space character for ease of use from the command line if ord(' ') not in chars: chars.add(ord(' ')) sfnt = Sfnt(raw) old_sizes = sfnt.sizes() # Remove the Digital Signature table since it is useless in a subset # font anyway sfnt.pop(b'DSIG', None) # Remove non core tables as they aren't likely to be used by renderers # anyway core_tables = {b'cmap', b'hhea', b'head', b'hmtx', b'maxp', b'name', b'OS/2', b'post', b'cvt ', b'fpgm', b'glyf', b'loca', b'prep', b'CFF ', b'VORG', b'EBDT', b'EBLC', b'EBSC', b'BASE', b'GSUB', b'GPOS', b'GDEF', b'JSTF', b'gasp', b'hdmx', b'kern', b'LTSH', b'PCLT', b'VDMX', b'vhea', b'vmtx', b'MATH'} for tag in list(sfnt): if tag not in core_tables: del sfnt[tag] try: cmap = sfnt[b'cmap'] except KeyError: raise UnsupportedFont('This font has no cmap table') # Get mapping of chars to glyph ids for all specified chars character_map = cmap.get_character_map(chars) extra_glyphs = set() if b'GSUB' in sfnt: # Parse all substitution rules to ensure that glyphs that can be # substituted for the specified set of glyphs are not removed gsub = sfnt[b'GSUB'] try: gsub.decompile() extra_glyphs = gsub.all_substitutions(character_map.itervalues()) except UnsupportedFont as e: warn('Usupported GSUB table: %s'%e) except Exception as e: warn('Failed to decompile GSUB table:', traceback.format_exc()) if b'loca' in sfnt and b'glyf' in sfnt: # TrueType Outlines subset_truetype(sfnt, character_map, extra_glyphs) elif b'CFF ' in sfnt: # PostScript Outlines subset_postscript(sfnt, character_map, extra_glyphs) else: raise UnsupportedFont('This font does not contain TrueType ' 'or PostScript outlines') # Restrict the cmap table to only contain entries for the resolved glyphs cmap.set_character_map(character_map) if b'kern' in sfnt: try: sfnt[b'kern'].restrict_to_glyphs(frozenset(character_map.itervalues())) except UnsupportedFont as e: warn('kern table unsupported, ignoring: %s'%e) except Exception as e: warn('Subsetting of kern table failed, ignoring:', traceback.format_exc()) raw, new_sizes = sfnt() return raw, old_sizes, new_sizes