def main(argv): """Decompose all fonts provided in the command line.""" for font_file_name in argv[1:]: with open(font_file_name, 'rb') as font_file: font = sfnt.SFNTReader(font_file, fontNumber=0) num_fonts = font.numFonts for font_number in range(num_fonts): font = ttLib.TTFont(font_file_name, fontNumber=font_number) font.save('%s-part%d' % (font_file_name, font_number))
def DiffTables(font_filename1, font_filename2): """Prints a table-by-table size comparison of two fonts. Args: font_filename1: The first font to compare. font_filename2: The second font to compare. Returns: String describing size difference. One line per unique table in either font. """ result = [' Table Changes Delta-Bytes(from=>to) % Change'] result.append(' -------------------------------------------------') sfnt1 = sfnt.SFNTReader(open(font_filename1)) sfnt2 = sfnt.SFNTReader(open(font_filename2)) font_sz1 = os.stat(font_filename1).st_size sum_tables1 = 0 sum_tables2 = 0 table_l1_l2s = [] for t in fonts.UniqueSort(sfnt1.tables, sfnt2.tables, _KNOWN_TABLES): table1_sz = sfnt1.tables[t].length if t in sfnt1 else 0 table2_sz = sfnt2.tables[t].length if t in sfnt2 else 0 sum_tables1 += table1_sz sum_tables2 += table2_sz table_l1_l2s.append((t, table1_sz, table2_sz)) for (table, table1_sz, table2_sz) in table_l1_l2s: delta_pct = float(table2_sz - table1_sz) / font_sz1 * 100 result.append( ' %s %+6d %06d => %06d %+10.1f%%' % (table, table2_sz - table1_sz, table1_sz, table2_sz, delta_pct)) delta_pct = float(sum_tables2 - sum_tables1) / font_sz1 * 100 result.append( ' TOTAL %+6d %06d => %06d %+10.1f%%' % (sum_tables2 - sum_tables1, sum_tables1, sum_tables2, delta_pct)) return '\n'.join(result)
def get_fonts_table_sizes(fonts): """ Returns tuple with available tables from all fonts and their length """ from fontTools.ttLib import sfnt _fonts = {} tables = [] for font in fonts: _fonts[op.basename(font)] = {} with open(font) as fp_font: sf = sfnt.SFNTReader(fp_font) for t in sf.tables: if t not in tables: tables.append(t) _fonts[op.basename(font)][t] = sf.tables[t].length return tables, _fonts
def run(options): ttc_path = options.ttc_path with open(ttc_path, 'rb') as fp: num_fonts = sfnt.SFNTReader(fp, fontNumber=0).numFonts if options.report: unique_tables = set() print(f'Input font: {os.path.basename(ttc_path)}\n') for ft_idx in range(num_fonts): font = TTFont(ttc_path, fontNumber=ft_idx, lazy=True) psname = get_psname(font) if psname is None: ttc_name = os.path.splitext(os.path.basename(ttc_path))[0] psname = f'{ttc_name}-font{ft_idx}' ext = '.otf' if font.sfntVersion == 'OTTO' else '.ttf' font_filename = f'{psname}{ext}' if options.report: # this code is based on fontTools.ttx.ttList reader = font.reader tags = sorted(reader.keys()) print(f'Font {ft_idx}: {font_filename}') print(' tag ', ' checksum', ' length', ' offset') print(' ----', ' ----------', ' --------', ' --------') for tag in tags: entry = reader.tables[tag] checksum = int(entry.checkSum) if checksum < 0: checksum += 0x100000000 checksum = f'0x{checksum:08X}' tb_info = (tag, checksum, entry.length, entry.offset) if tb_info not in unique_tables: print(' {:4s} {:10s} {:8d} {:8d}'.format(*tb_info)) unique_tables.add(tb_info) else: print(f' {tag:4s} - shared -') print() else: save_path = os.path.join(os.path.dirname(ttc_path), font_filename) font.save(save_path) logger.info(f'Saved {save_path}') font.close()
def __init__(self, file=None, res_name_or_index=None, sfntVersion="\000\001\000\000", checkChecksums=0, verbose=0, recalcBBoxes=1, allowVID=0, ignoreDecompileErrors=False, fontNumber=-1): from fontTools.ttLib import sfnt self.verbose = verbose self.recalcBBoxes = recalcBBoxes self.tables = {} self.reader = None # Permit the user to reference glyphs that are not int the font. self.last_vid = 0xFFFE # Can't make it be 0xFFFF, as the world is full unsigned short integer counters that get incremented after the last seen GID value. self.reverseVIDDict = {} self.VIDDict = {} self.allowVID = allowVID self.ignoreDecompileErrors = ignoreDecompileErrors self.reader = sfnt.SFNTReader(file, checkChecksums, fontNumber=fontNumber) self.sfntVersion = self.reader.sfntVersion
def __init__(self, file=None, res_name_or_index=None, sfntVersion="\000\001\000\000", flavor=None, checkChecksums=False, verbose=None, recalcBBoxes=True, allowVID=False, ignoreDecompileErrors=False, recalcTimestamp=True, fontNumber=-1, lazy=None, quiet=None): """The constructor can be called with a few different arguments. When reading a font from disk, 'file' should be either a pathname pointing to a file, or a readable file object. It we're running on a Macintosh, 'res_name_or_index' maybe an sfnt resource name or an sfnt resource index number or zero. The latter case will cause TTLib to autodetect whether the file is a flat file or a suitcase. (If it's a suitcase, only the first 'sfnt' resource will be read!) The 'checkChecksums' argument is used to specify how sfnt checksums are treated upon reading a file from disk: 0: don't check (default) 1: check, print warnings if a wrong checksum is found 2: check, raise an exception if a wrong checksum is found. The TTFont constructor can also be called without a 'file' argument: this is the way to create a new empty font. In this case you can optionally supply the 'sfntVersion' argument, and a 'flavor' which can be None, 'woff', or 'woff2'. If the recalcBBoxes argument is false, a number of things will *not* be recalculated upon save/compile: 1) 'glyf' glyph bounding boxes 2) 'CFF ' font bounding box 3) 'head' font bounding box 4) 'hhea' min/max values 5) 'vhea' min/max values (1) is needed for certain kinds of CJK fonts (ask Werner Lemberg ;-). Additionally, upon importing an TTX file, this option cause glyphs to be compiled right away. This should reduce memory consumption greatly, and therefore should have some impact on the time needed to parse/compile large fonts. If the recalcTimestamp argument is false, the modified timestamp in the 'head' table will *not* be recalculated upon save/compile. If the allowVID argument is set to true, then virtual GID's are supported. Asking for a glyph ID with a glyph name or GID that is not in the font will return a virtual GID. This is valid for GSUB and cmap tables. For SING glyphlets, the cmap table is used to specify Unicode values for virtual GI's used in GSUB/GPOS rules. If the gid N is requested and does not exist in the font, or the glyphname has the form glyphN and does not exist in the font, then N is used as the virtual GID. Else, the first virtual GID is assigned as 0x1000 -1; for subsequent new virtual GIDs, the next is one less than the previous. If ignoreDecompileErrors is set to True, exceptions raised in individual tables during decompilation will be ignored, falling back to the DefaultTable implementation, which simply keeps the binary data. If lazy is set to True, many data structures are loaded lazily, upon access only. If it is set to False, many data structures are loaded immediately. The default is lazy=None which is somewhere in between. """ from fontTools.ttLib import sfnt for name in ("verbose", "quiet"): val = locals().get(name) if val is not None: deprecateArgument(name, "configure logging instead") setattr(self, name, val) self.lazy = lazy self.recalcBBoxes = recalcBBoxes self.recalcTimestamp = recalcTimestamp self.tables = {} self.reader = None # Permit the user to reference glyphs that are not int the font. self.last_vid = 0xFFFE # Can't make it be 0xFFFF, as the world is full unsigned short integer counters that get incremented after the last seen GID value. self.reverseVIDDict = {} self.VIDDict = {} self.allowVID = allowVID self.ignoreDecompileErrors = ignoreDecompileErrors if not file: self.sfntVersion = sfntVersion self.flavor = flavor self.flavorData = None return if not hasattr(file, "read"): closeStream = True # assume file is a string if res_name_or_index is not None: # see if it contains 'sfnt' resources in the resource or data fork from . import macUtils if res_name_or_index == 0: if macUtils.getSFNTResIndices(file): # get the first available sfnt font. file = macUtils.SFNTResourceReader(file, 1) else: file = open(file, "rb") else: file = macUtils.SFNTResourceReader(file, res_name_or_index) else: file = open(file, "rb") else: # assume "file" is a readable file object closeStream = False if not self.lazy: # read input file in memory and wrap a stream around it to allow overwriting tmp = BytesIO(file.read()) if hasattr(file, 'name'): # save reference to input file name tmp.name = file.name if closeStream: file.close() file = tmp self.reader = sfnt.SFNTReader(file, checkChecksums, fontNumber=fontNumber) self.sfntVersion = self.reader.sfntVersion self.flavor = self.reader.flavor self.flavorData = self.reader.flavorData