merge.py revision 45d2f38aa51169f400062f2153a74277b9d4d04e
145d2f38aa51169f400062f2153a74277b9d4d04eBehdad Esfahbod# Copyright 2013 Google, Inc. All Rights Reserved. 245d2f38aa51169f400062f2153a74277b9d4d04eBehdad Esfahbod# 345d2f38aa51169f400062f2153a74277b9d4d04eBehdad Esfahbod# Google Author(s): Behdad Esfahbod 445d2f38aa51169f400062f2153a74277b9d4d04eBehdad Esfahbod 545d2f38aa51169f400062f2153a74277b9d4d04eBehdad Esfahbod"""Font merger. 645d2f38aa51169f400062f2153a74277b9d4d04eBehdad Esfahbod""" 745d2f38aa51169f400062f2153a74277b9d4d04eBehdad Esfahbod 845d2f38aa51169f400062f2153a74277b9d4d04eBehdad Esfahbodimport sys 945d2f38aa51169f400062f2153a74277b9d4d04eBehdad Esfahbod 1045d2f38aa51169f400062f2153a74277b9d4d04eBehdad Esfahbodimport fontTools 1145d2f38aa51169f400062f2153a74277b9d4d04eBehdad Esfahbodfrom fontTools import misc, ttLib, cffLib 1245d2f38aa51169f400062f2153a74277b9d4d04eBehdad Esfahbod 1345d2f38aa51169f400062f2153a74277b9d4d04eBehdad Esfahboddef _add_method(*clazzes): 1445d2f38aa51169f400062f2153a74277b9d4d04eBehdad Esfahbod """Returns a decorator function that adds a new method to one or 1545d2f38aa51169f400062f2153a74277b9d4d04eBehdad Esfahbod more classes.""" 1645d2f38aa51169f400062f2153a74277b9d4d04eBehdad Esfahbod def wrapper(method): 1745d2f38aa51169f400062f2153a74277b9d4d04eBehdad Esfahbod for clazz in clazzes: 1845d2f38aa51169f400062f2153a74277b9d4d04eBehdad Esfahbod assert clazz.__name__ != 'DefaultTable', 'Oops, table class not found.' 1945d2f38aa51169f400062f2153a74277b9d4d04eBehdad Esfahbod assert not hasattr(clazz, method.func_name), \ 2045d2f38aa51169f400062f2153a74277b9d4d04eBehdad Esfahbod "Oops, class '%s' has method '%s'." % (clazz.__name__, 2145d2f38aa51169f400062f2153a74277b9d4d04eBehdad Esfahbod method.func_name) 2245d2f38aa51169f400062f2153a74277b9d4d04eBehdad Esfahbod setattr(clazz, method.func_name, method) 2345d2f38aa51169f400062f2153a74277b9d4d04eBehdad Esfahbod return None 2445d2f38aa51169f400062f2153a74277b9d4d04eBehdad Esfahbod return wrapper 2545d2f38aa51169f400062f2153a74277b9d4d04eBehdad Esfahbod 2645d2f38aa51169f400062f2153a74277b9d4d04eBehdad Esfahbod 2745d2f38aa51169f400062f2153a74277b9d4d04eBehdad Esfahbod@_add_method(fontTools.ttLib.getTableClass('maxp')) 2845d2f38aa51169f400062f2153a74277b9d4d04eBehdad Esfahboddef merge(self, tables, fonts): 2945d2f38aa51169f400062f2153a74277b9d4d04eBehdad Esfahbod # TODO When we correctly merge hinting data, update these values: 3045d2f38aa51169f400062f2153a74277b9d4d04eBehdad Esfahbod # maxFunctionDefs, maxInstructionDefs, maxSizeOfInstructions 3145d2f38aa51169f400062f2153a74277b9d4d04eBehdad Esfahbod # TODO Assumes that all tables have format 1.0; safe assumption. 3245d2f38aa51169f400062f2153a74277b9d4d04eBehdad Esfahbod for key in set(sum((vars(table).keys() for table in tables), [])): 3345d2f38aa51169f400062f2153a74277b9d4d04eBehdad Esfahbod setattr(self, key, max(getattr(table, key) for table in tables)) 3445d2f38aa51169f400062f2153a74277b9d4d04eBehdad Esfahbod return self 3545d2f38aa51169f400062f2153a74277b9d4d04eBehdad Esfahbod 3645d2f38aa51169f400062f2153a74277b9d4d04eBehdad Esfahbodclass Merger: 3745d2f38aa51169f400062f2153a74277b9d4d04eBehdad Esfahbod 3845d2f38aa51169f400062f2153a74277b9d4d04eBehdad Esfahbod def __init__(self, fontfiles): 3945d2f38aa51169f400062f2153a74277b9d4d04eBehdad Esfahbod self.fontfiles = fontfiles 4045d2f38aa51169f400062f2153a74277b9d4d04eBehdad Esfahbod 4145d2f38aa51169f400062f2153a74277b9d4d04eBehdad Esfahbod def merge(self): 4245d2f38aa51169f400062f2153a74277b9d4d04eBehdad Esfahbod 4345d2f38aa51169f400062f2153a74277b9d4d04eBehdad Esfahbod mega = ttLib.TTFont() 4445d2f38aa51169f400062f2153a74277b9d4d04eBehdad Esfahbod 4545d2f38aa51169f400062f2153a74277b9d4d04eBehdad Esfahbod # 4645d2f38aa51169f400062f2153a74277b9d4d04eBehdad Esfahbod # Settle on a mega glyph order. 4745d2f38aa51169f400062f2153a74277b9d4d04eBehdad Esfahbod # 4845d2f38aa51169f400062f2153a74277b9d4d04eBehdad Esfahbod fonts = [ttLib.TTFont(fontfile) for fontfile in self.fontfiles] 4945d2f38aa51169f400062f2153a74277b9d4d04eBehdad Esfahbod glyphOrders = [font.getGlyphOrder() for font in fonts] 5045d2f38aa51169f400062f2153a74277b9d4d04eBehdad Esfahbod megaGlyphOrder = self._mergeGlyphOrders(glyphOrders) 5145d2f38aa51169f400062f2153a74277b9d4d04eBehdad Esfahbod # Reload fonts and set new glyph names on them. 5245d2f38aa51169f400062f2153a74277b9d4d04eBehdad Esfahbod # TODO Is it necessary to reload font? I think it is. At least 5345d2f38aa51169f400062f2153a74277b9d4d04eBehdad Esfahbod # it's safer, in case tables were loaded to provide glyph names. 5445d2f38aa51169f400062f2153a74277b9d4d04eBehdad Esfahbod fonts = [ttLib.TTFont(fontfile) for fontfile in self.fontfiles] 5545d2f38aa51169f400062f2153a74277b9d4d04eBehdad Esfahbod map(ttLib.TTFont.setGlyphOrder, fonts, glyphOrders) 5645d2f38aa51169f400062f2153a74277b9d4d04eBehdad Esfahbod mega.setGlyphOrder(megaGlyphOrder) 5745d2f38aa51169f400062f2153a74277b9d4d04eBehdad Esfahbod 5845d2f38aa51169f400062f2153a74277b9d4d04eBehdad Esfahbod cmaps = [self._get_cmap(font) for font in fonts] 5945d2f38aa51169f400062f2153a74277b9d4d04eBehdad Esfahbod 6045d2f38aa51169f400062f2153a74277b9d4d04eBehdad Esfahbod allTags = set(sum([font.keys() for font in fonts], [])) 6145d2f38aa51169f400062f2153a74277b9d4d04eBehdad Esfahbod allTags.remove('GlyphOrder') 6245d2f38aa51169f400062f2153a74277b9d4d04eBehdad Esfahbod for tag in allTags: 6345d2f38aa51169f400062f2153a74277b9d4d04eBehdad Esfahbod clazz = ttLib.getTableClass(tag) 6445d2f38aa51169f400062f2153a74277b9d4d04eBehdad Esfahbod 6545d2f38aa51169f400062f2153a74277b9d4d04eBehdad Esfahbod if not hasattr(clazz, 'merge'): 6645d2f38aa51169f400062f2153a74277b9d4d04eBehdad Esfahbod print "Don't know how to merge '%s', dropped." % tag 6745d2f38aa51169f400062f2153a74277b9d4d04eBehdad Esfahbod continue 6845d2f38aa51169f400062f2153a74277b9d4d04eBehdad Esfahbod 6945d2f38aa51169f400062f2153a74277b9d4d04eBehdad Esfahbod # TODO For now assume all fonts have the same tables. 7045d2f38aa51169f400062f2153a74277b9d4d04eBehdad Esfahbod tables = [font[tag] for font in fonts] 7145d2f38aa51169f400062f2153a74277b9d4d04eBehdad Esfahbod table = clazz(tag) 7245d2f38aa51169f400062f2153a74277b9d4d04eBehdad Esfahbod table.merge (tables, fonts) 7345d2f38aa51169f400062f2153a74277b9d4d04eBehdad Esfahbod mega[tag] = table 7445d2f38aa51169f400062f2153a74277b9d4d04eBehdad Esfahbod print "Merged '%s'." % tag 7545d2f38aa51169f400062f2153a74277b9d4d04eBehdad Esfahbod 7645d2f38aa51169f400062f2153a74277b9d4d04eBehdad Esfahbod return mega 7745d2f38aa51169f400062f2153a74277b9d4d04eBehdad Esfahbod 7845d2f38aa51169f400062f2153a74277b9d4d04eBehdad Esfahbod def _get_cmap(self, font): 7945d2f38aa51169f400062f2153a74277b9d4d04eBehdad Esfahbod cmap = font['cmap'] 8045d2f38aa51169f400062f2153a74277b9d4d04eBehdad Esfahbod tables = [t for t in cmap.tables 8145d2f38aa51169f400062f2153a74277b9d4d04eBehdad Esfahbod if t.platformID == 3 and t.platEncID in [1, 10]] 8245d2f38aa51169f400062f2153a74277b9d4d04eBehdad Esfahbod # XXX Handle format=14 8345d2f38aa51169f400062f2153a74277b9d4d04eBehdad Esfahbod assert len(tables) 8445d2f38aa51169f400062f2153a74277b9d4d04eBehdad Esfahbod # Pick table that has largest coverage 8545d2f38aa51169f400062f2153a74277b9d4d04eBehdad Esfahbod table = max(tables, key=lambda t: len(t.cmap)) 8645d2f38aa51169f400062f2153a74277b9d4d04eBehdad Esfahbod return table 8745d2f38aa51169f400062f2153a74277b9d4d04eBehdad Esfahbod 8845d2f38aa51169f400062f2153a74277b9d4d04eBehdad Esfahbod def _mergeGlyphOrders(self, glyphOrders): 8945d2f38aa51169f400062f2153a74277b9d4d04eBehdad Esfahbod """Modifies passed-in glyphOrders to reflect new glyph names.""" 9045d2f38aa51169f400062f2153a74277b9d4d04eBehdad Esfahbod # Simply append font index to the glyph name for now. 9145d2f38aa51169f400062f2153a74277b9d4d04eBehdad Esfahbod mega = [] 9245d2f38aa51169f400062f2153a74277b9d4d04eBehdad Esfahbod for n,glyphOrder in enumerate(glyphOrders): 9345d2f38aa51169f400062f2153a74277b9d4d04eBehdad Esfahbod for i,glyphName in enumerate(glyphOrder): 9445d2f38aa51169f400062f2153a74277b9d4d04eBehdad Esfahbod glyphName += "#" + `n` 9545d2f38aa51169f400062f2153a74277b9d4d04eBehdad Esfahbod glyphOrder[i] = glyphName 9645d2f38aa51169f400062f2153a74277b9d4d04eBehdad Esfahbod mega.append(glyphName) 9745d2f38aa51169f400062f2153a74277b9d4d04eBehdad Esfahbod return mega 9845d2f38aa51169f400062f2153a74277b9d4d04eBehdad Esfahbod 9945d2f38aa51169f400062f2153a74277b9d4d04eBehdad Esfahboddef main(args): 10045d2f38aa51169f400062f2153a74277b9d4d04eBehdad Esfahbod if len(args) < 1: 10145d2f38aa51169f400062f2153a74277b9d4d04eBehdad Esfahbod print >>sys.stderr, "usage: pyftmerge font..." 10245d2f38aa51169f400062f2153a74277b9d4d04eBehdad Esfahbod sys.exit(1) 10345d2f38aa51169f400062f2153a74277b9d4d04eBehdad Esfahbod merger = Merger(args) 10445d2f38aa51169f400062f2153a74277b9d4d04eBehdad Esfahbod font = merger.merge() 10545d2f38aa51169f400062f2153a74277b9d4d04eBehdad Esfahbod outfile = 'merged.ttf' 10645d2f38aa51169f400062f2153a74277b9d4d04eBehdad Esfahbod font.save(outfile) 10745d2f38aa51169f400062f2153a74277b9d4d04eBehdad Esfahbod 10845d2f38aa51169f400062f2153a74277b9d4d04eBehdad Esfahbodif __name__ == "__main__": 10945d2f38aa51169f400062f2153a74277b9d4d04eBehdad Esfahbod main(sys.argv[1:]) 110