merge.py revision 47bee9cfbd47dc22895003cc94ab91f9075ca27f
145d2f38aa51169f400062f2153a74277b9d4d04eBehdad Esfahbod# Copyright 2013 Google, Inc. All Rights Reserved. 245d2f38aa51169f400062f2153a74277b9d4d04eBehdad Esfahbod# 347bee9cfbd47dc22895003cc94ab91f9075ca27fRoozbeh Pournader# Google Author(s): Behdad Esfahbod, Roozbeh Pournader 445d2f38aa51169f400062f2153a74277b9d4d04eBehdad Esfahbod 545d2f38aa51169f400062f2153a74277b9d4d04eBehdad Esfahbod"""Font merger. 645d2f38aa51169f400062f2153a74277b9d4d04eBehdad Esfahbod""" 745d2f38aa51169f400062f2153a74277b9d4d04eBehdad Esfahbod 8f63e80e3fc4ef5406109f1fbe2746e26927fdebfBehdad Esfahbodfrom __future__ import print_function, division 9f63e80e3fc4ef5406109f1fbe2746e26927fdebfBehdad Esfahbodfrom fontTools.misc.py23 import * 10f63e80e3fc4ef5406109f1fbe2746e26927fdebfBehdad Esfahbodfrom fontTools import ttLib, cffLib 11f63e80e3fc4ef5406109f1fbe2746e26927fdebfBehdad Esfahbodfrom fontTools.ttLib.tables import otTables 12f63e80e3fc4ef5406109f1fbe2746e26927fdebfBehdad Esfahbodfrom functools import reduce 1345d2f38aa51169f400062f2153a74277b9d4d04eBehdad Esfahbodimport sys 14f2d5982826530296fd7c8f9e2d2a4dc3e070934dBehdad Esfahbodimport time 1545d2f38aa51169f400062f2153a74277b9d4d04eBehdad Esfahbod 1645d2f38aa51169f400062f2153a74277b9d4d04eBehdad Esfahbod 1745d2f38aa51169f400062f2153a74277b9d4d04eBehdad Esfahboddef _add_method(*clazzes): 18c855f3ab69f31004b6aaca33ac549d384f1efc54Behdad Esfahbod """Returns a decorator function that adds a new method to one or 19c855f3ab69f31004b6aaca33ac549d384f1efc54Behdad Esfahbod more classes.""" 20c855f3ab69f31004b6aaca33ac549d384f1efc54Behdad Esfahbod def wrapper(method): 21c855f3ab69f31004b6aaca33ac549d384f1efc54Behdad Esfahbod for clazz in clazzes: 22c855f3ab69f31004b6aaca33ac549d384f1efc54Behdad Esfahbod assert clazz.__name__ != 'DefaultTable', 'Oops, table class not found.' 23f63e80e3fc4ef5406109f1fbe2746e26927fdebfBehdad Esfahbod assert not hasattr(clazz, method.__name__), \ 24c855f3ab69f31004b6aaca33ac549d384f1efc54Behdad Esfahbod "Oops, class '%s' has method '%s'." % (clazz.__name__, 25f63e80e3fc4ef5406109f1fbe2746e26927fdebfBehdad Esfahbod method.__name__) 26f63e80e3fc4ef5406109f1fbe2746e26927fdebfBehdad Esfahbod setattr(clazz, method.__name__, method) 27c855f3ab69f31004b6aaca33ac549d384f1efc54Behdad Esfahbod return None 28c855f3ab69f31004b6aaca33ac549d384f1efc54Behdad Esfahbod return wrapper 2945d2f38aa51169f400062f2153a74277b9d4d04eBehdad Esfahbod 3047bee9cfbd47dc22895003cc94ab91f9075ca27fRoozbeh Pournader# General utility functions for merging values from different fonts 3147bee9cfbd47dc22895003cc94ab91f9075ca27fRoozbeh Pournaderdef assert_equal(lst): 3247bee9cfbd47dc22895003cc94ab91f9075ca27fRoozbeh Pournader first = lst[0] 3347bee9cfbd47dc22895003cc94ab91f9075ca27fRoozbeh Pournader assert all([item == first for item in lst]) 3447bee9cfbd47dc22895003cc94ab91f9075ca27fRoozbeh Pournader 3547bee9cfbd47dc22895003cc94ab91f9075ca27fRoozbeh Pournaderdef first(lst): 3647bee9cfbd47dc22895003cc94ab91f9075ca27fRoozbeh Pournader return lst[0] 3747bee9cfbd47dc22895003cc94ab91f9075ca27fRoozbeh Pournader 3845d2f38aa51169f400062f2153a74277b9d4d04eBehdad Esfahbod 39be4ecc7c67917d95b44d16b32d8d72446162f3ddBehdad Esfahbod@_add_method(ttLib.getTableClass('maxp')) 400bf4f561da7972ab90f6bee87fa30923e201adf5Behdad Esfahboddef merge(self, m): 4147bee9cfbd47dc22895003cc94ab91f9075ca27fRoozbeh Pournader logic = { 4247bee9cfbd47dc22895003cc94ab91f9075ca27fRoozbeh Pournader '*': max, 4347bee9cfbd47dc22895003cc94ab91f9075ca27fRoozbeh Pournader 'tableVersion': assert_equal, 4447bee9cfbd47dc22895003cc94ab91f9075ca27fRoozbeh Pournader 'numGlyphs': sum, 4547bee9cfbd47dc22895003cc94ab91f9075ca27fRoozbeh Pournader 'maxStorage': max, # FIXME: may need to be changed to sum 4647bee9cfbd47dc22895003cc94ab91f9075ca27fRoozbeh Pournader 'maxFunctionDefs': sum, 4747bee9cfbd47dc22895003cc94ab91f9075ca27fRoozbeh Pournader 'maxInstructionDefs': sum, 4847bee9cfbd47dc22895003cc94ab91f9075ca27fRoozbeh Pournader } 4945d2f38aa51169f400062f2153a74277b9d4d04eBehdad Esfahbod # TODO When we correctly merge hinting data, update these values: 5045d2f38aa51169f400062f2153a74277b9d4d04eBehdad Esfahbod # maxFunctionDefs, maxInstructionDefs, maxSizeOfInstructions 5147bee9cfbd47dc22895003cc94ab91f9075ca27fRoozbeh Pournader m._mergeKeys(self, logic) 5265f19d8440f5fa5d94d1f04593f16cd9f21f176bBehdad Esfahbod return True 5365f19d8440f5fa5d94d1f04593f16cd9f21f176bBehdad Esfahbod 54be4ecc7c67917d95b44d16b32d8d72446162f3ddBehdad Esfahbod@_add_method(ttLib.getTableClass('head')) 550bf4f561da7972ab90f6bee87fa30923e201adf5Behdad Esfahboddef merge(self, m): 56f2d5982826530296fd7c8f9e2d2a4dc3e070934dBehdad Esfahbod # TODO Check that unitsPerEm are the same. 57f2d5982826530296fd7c8f9e2d2a4dc3e070934dBehdad Esfahbod # TODO Use bitwise ops for flags, macStyle, fontDirectionHint 58f2d5982826530296fd7c8f9e2d2a4dc3e070934dBehdad Esfahbod minMembers = ['xMin', 'yMin'] 59f2d5982826530296fd7c8f9e2d2a4dc3e070934dBehdad Esfahbod # Negate some members 60f2d5982826530296fd7c8f9e2d2a4dc3e070934dBehdad Esfahbod for key in minMembers: 610bf4f561da7972ab90f6bee87fa30923e201adf5Behdad Esfahbod for table in m.tables: 62f2d5982826530296fd7c8f9e2d2a4dc3e070934dBehdad Esfahbod setattr(table, key, -getattr(table, key)) 63f2d5982826530296fd7c8f9e2d2a4dc3e070934dBehdad Esfahbod # Get max over members 64f63e80e3fc4ef5406109f1fbe2746e26927fdebfBehdad Esfahbod allKeys = reduce(set.union, (list(vars(table).keys()) for table in m.tables), set()) 656942b22252c4f42c6470ebd1c94fc9e193814a30Behdad Esfahbod for key in allKeys: 660bf4f561da7972ab90f6bee87fa30923e201adf5Behdad Esfahbod setattr(self, key, max(getattr(table, key) for table in m.tables)) 67f2d5982826530296fd7c8f9e2d2a4dc3e070934dBehdad Esfahbod # Negate them back 68f2d5982826530296fd7c8f9e2d2a4dc3e070934dBehdad Esfahbod for key in minMembers: 690bf4f561da7972ab90f6bee87fa30923e201adf5Behdad Esfahbod for table in m.tables: 70f2d5982826530296fd7c8f9e2d2a4dc3e070934dBehdad Esfahbod setattr(table, key, -getattr(table, key)) 71f2d5982826530296fd7c8f9e2d2a4dc3e070934dBehdad Esfahbod setattr(self, key, -getattr(self, key)) 72f2d5982826530296fd7c8f9e2d2a4dc3e070934dBehdad Esfahbod return True 73f2d5982826530296fd7c8f9e2d2a4dc3e070934dBehdad Esfahbod 74be4ecc7c67917d95b44d16b32d8d72446162f3ddBehdad Esfahbod@_add_method(ttLib.getTableClass('hhea')) 750bf4f561da7972ab90f6bee87fa30923e201adf5Behdad Esfahboddef merge(self, m): 7665f19d8440f5fa5d94d1f04593f16cd9f21f176bBehdad Esfahbod # TODO Check that ascent, descent, slope, etc are the same. 7765f19d8440f5fa5d94d1f04593f16cd9f21f176bBehdad Esfahbod minMembers = ['descent', 'minLeftSideBearing', 'minRightSideBearing'] 7865f19d8440f5fa5d94d1f04593f16cd9f21f176bBehdad Esfahbod # Negate some members 7965f19d8440f5fa5d94d1f04593f16cd9f21f176bBehdad Esfahbod for key in minMembers: 800bf4f561da7972ab90f6bee87fa30923e201adf5Behdad Esfahbod for table in m.tables: 8165f19d8440f5fa5d94d1f04593f16cd9f21f176bBehdad Esfahbod setattr(table, key, -getattr(table, key)) 8265f19d8440f5fa5d94d1f04593f16cd9f21f176bBehdad Esfahbod # Get max over members 83f63e80e3fc4ef5406109f1fbe2746e26927fdebfBehdad Esfahbod allKeys = reduce(set.union, (list(vars(table).keys()) for table in m.tables), set()) 846942b22252c4f42c6470ebd1c94fc9e193814a30Behdad Esfahbod for key in allKeys: 850bf4f561da7972ab90f6bee87fa30923e201adf5Behdad Esfahbod setattr(self, key, max(getattr(table, key) for table in m.tables)) 8665f19d8440f5fa5d94d1f04593f16cd9f21f176bBehdad Esfahbod # Negate them back 8765f19d8440f5fa5d94d1f04593f16cd9f21f176bBehdad Esfahbod for key in minMembers: 880bf4f561da7972ab90f6bee87fa30923e201adf5Behdad Esfahbod for table in m.tables: 8965f19d8440f5fa5d94d1f04593f16cd9f21f176bBehdad Esfahbod setattr(table, key, -getattr(table, key)) 9065f19d8440f5fa5d94d1f04593f16cd9f21f176bBehdad Esfahbod setattr(self, key, -getattr(self, key)) 9165f19d8440f5fa5d94d1f04593f16cd9f21f176bBehdad Esfahbod return True 9265f19d8440f5fa5d94d1f04593f16cd9f21f176bBehdad Esfahbod 93be4ecc7c67917d95b44d16b32d8d72446162f3ddBehdad Esfahbod@_add_method(ttLib.getTableClass('OS/2')) 940bf4f561da7972ab90f6bee87fa30923e201adf5Behdad Esfahboddef merge(self, m): 9571294def6730c37839f03dee519b319f982587eaBehdad Esfahbod # TODO Check that weight/width/subscript/superscript/etc are the same. 9671294def6730c37839f03dee519b319f982587eaBehdad Esfahbod # TODO Bitwise ops for UnicodeRange/CodePageRange. 9771294def6730c37839f03dee519b319f982587eaBehdad Esfahbod # TODO Pretty much all fields generated here have bogus values. 9871294def6730c37839f03dee519b319f982587eaBehdad Esfahbod # Get max over members 99f63e80e3fc4ef5406109f1fbe2746e26927fdebfBehdad Esfahbod allKeys = reduce(set.union, (list(vars(table).keys()) for table in m.tables), set()) 1006942b22252c4f42c6470ebd1c94fc9e193814a30Behdad Esfahbod for key in allKeys: 1010bf4f561da7972ab90f6bee87fa30923e201adf5Behdad Esfahbod setattr(self, key, max(getattr(table, key) for table in m.tables)) 10271294def6730c37839f03dee519b319f982587eaBehdad Esfahbod return True 10371294def6730c37839f03dee519b319f982587eaBehdad Esfahbod 104be4ecc7c67917d95b44d16b32d8d72446162f3ddBehdad Esfahbod@_add_method(ttLib.getTableClass('post')) 1050bf4f561da7972ab90f6bee87fa30923e201adf5Behdad Esfahboddef merge(self, m): 106f2d5982826530296fd7c8f9e2d2a4dc3e070934dBehdad Esfahbod # TODO Check that italicAngle, underlinePosition, underlineThickness are the same. 107f2d5982826530296fd7c8f9e2d2a4dc3e070934dBehdad Esfahbod minMembers = ['underlinePosition', 'minMemType42', 'minMemType1'] 108f2d5982826530296fd7c8f9e2d2a4dc3e070934dBehdad Esfahbod # Negate some members 109f2d5982826530296fd7c8f9e2d2a4dc3e070934dBehdad Esfahbod for key in minMembers: 1100bf4f561da7972ab90f6bee87fa30923e201adf5Behdad Esfahbod for table in m.tables: 111f2d5982826530296fd7c8f9e2d2a4dc3e070934dBehdad Esfahbod setattr(table, key, -getattr(table, key)) 112f2d5982826530296fd7c8f9e2d2a4dc3e070934dBehdad Esfahbod # Get max over members 113f63e80e3fc4ef5406109f1fbe2746e26927fdebfBehdad Esfahbod allKeys = reduce(set.union, (list(vars(table).keys()) for table in m.tables), set()) 1146942b22252c4f42c6470ebd1c94fc9e193814a30Behdad Esfahbod if 'mapping' in allKeys: 1156942b22252c4f42c6470ebd1c94fc9e193814a30Behdad Esfahbod allKeys.remove('mapping') 1166942b22252c4f42c6470ebd1c94fc9e193814a30Behdad Esfahbod allKeys.remove('extraNames') 1176942b22252c4f42c6470ebd1c94fc9e193814a30Behdad Esfahbod for key in allKeys: 1180bf4f561da7972ab90f6bee87fa30923e201adf5Behdad Esfahbod setattr(self, key, max(getattr(table, key) for table in m.tables)) 119f2d5982826530296fd7c8f9e2d2a4dc3e070934dBehdad Esfahbod # Negate them back 120f2d5982826530296fd7c8f9e2d2a4dc3e070934dBehdad Esfahbod for key in minMembers: 1210bf4f561da7972ab90f6bee87fa30923e201adf5Behdad Esfahbod for table in m.tables: 122f2d5982826530296fd7c8f9e2d2a4dc3e070934dBehdad Esfahbod setattr(table, key, -getattr(table, key)) 123f2d5982826530296fd7c8f9e2d2a4dc3e070934dBehdad Esfahbod setattr(self, key, -getattr(self, key)) 124f2d5982826530296fd7c8f9e2d2a4dc3e070934dBehdad Esfahbod self.mapping = {} 1250bf4f561da7972ab90f6bee87fa30923e201adf5Behdad Esfahbod for table in m.tables: 126f2d5982826530296fd7c8f9e2d2a4dc3e070934dBehdad Esfahbod if hasattr(table, 'mapping'): 127f2d5982826530296fd7c8f9e2d2a4dc3e070934dBehdad Esfahbod self.mapping.update(table.mapping) 128f2d5982826530296fd7c8f9e2d2a4dc3e070934dBehdad Esfahbod self.extraNames = [] 129f2d5982826530296fd7c8f9e2d2a4dc3e070934dBehdad Esfahbod return True 130f2d5982826530296fd7c8f9e2d2a4dc3e070934dBehdad Esfahbod 131be4ecc7c67917d95b44d16b32d8d72446162f3ddBehdad Esfahbod@_add_method(ttLib.getTableClass('vmtx'), 132be4ecc7c67917d95b44d16b32d8d72446162f3ddBehdad Esfahbod ttLib.getTableClass('hmtx')) 1330bf4f561da7972ab90f6bee87fa30923e201adf5Behdad Esfahboddef merge(self, m): 13465f19d8440f5fa5d94d1f04593f16cd9f21f176bBehdad Esfahbod self.metrics = {} 1350bf4f561da7972ab90f6bee87fa30923e201adf5Behdad Esfahbod for table in m.tables: 13665f19d8440f5fa5d94d1f04593f16cd9f21f176bBehdad Esfahbod self.metrics.update(table.metrics) 13765f19d8440f5fa5d94d1f04593f16cd9f21f176bBehdad Esfahbod return True 13865f19d8440f5fa5d94d1f04593f16cd9f21f176bBehdad Esfahbod 139be4ecc7c67917d95b44d16b32d8d72446162f3ddBehdad Esfahbod@_add_method(ttLib.getTableClass('loca')) 1400bf4f561da7972ab90f6bee87fa30923e201adf5Behdad Esfahboddef merge(self, m): 141f2d5982826530296fd7c8f9e2d2a4dc3e070934dBehdad Esfahbod return True # Will be computed automatically 142f2d5982826530296fd7c8f9e2d2a4dc3e070934dBehdad Esfahbod 143be4ecc7c67917d95b44d16b32d8d72446162f3ddBehdad Esfahbod@_add_method(ttLib.getTableClass('glyf')) 1440bf4f561da7972ab90f6bee87fa30923e201adf5Behdad Esfahboddef merge(self, m): 145f2d5982826530296fd7c8f9e2d2a4dc3e070934dBehdad Esfahbod self.glyphs = {} 1460bf4f561da7972ab90f6bee87fa30923e201adf5Behdad Esfahbod for table in m.tables: 147436503372a7a4aeb529a360e66114ce1b1b6d9ceBehdad Esfahbod for g in table.glyphs.values(): 148436503372a7a4aeb529a360e66114ce1b1b6d9ceBehdad Esfahbod # Drop hints for now, since we don't remap 149436503372a7a4aeb529a360e66114ce1b1b6d9ceBehdad Esfahbod # functions / CVT values. 150436503372a7a4aeb529a360e66114ce1b1b6d9ceBehdad Esfahbod g.removeHinting() 151436503372a7a4aeb529a360e66114ce1b1b6d9ceBehdad Esfahbod # Expand composite glyphs to load their 152436503372a7a4aeb529a360e66114ce1b1b6d9ceBehdad Esfahbod # composite glyph names. 153436503372a7a4aeb529a360e66114ce1b1b6d9ceBehdad Esfahbod if g.isComposite(): 154436503372a7a4aeb529a360e66114ce1b1b6d9ceBehdad Esfahbod g.expand(table) 155f2d5982826530296fd7c8f9e2d2a4dc3e070934dBehdad Esfahbod self.glyphs.update(table.glyphs) 156f2d5982826530296fd7c8f9e2d2a4dc3e070934dBehdad Esfahbod return True 157f2d5982826530296fd7c8f9e2d2a4dc3e070934dBehdad Esfahbod 158be4ecc7c67917d95b44d16b32d8d72446162f3ddBehdad Esfahbod@_add_method(ttLib.getTableClass('prep'), 159be4ecc7c67917d95b44d16b32d8d72446162f3ddBehdad Esfahbod ttLib.getTableClass('fpgm'), 160be4ecc7c67917d95b44d16b32d8d72446162f3ddBehdad Esfahbod ttLib.getTableClass('cvt ')) 1610bf4f561da7972ab90f6bee87fa30923e201adf5Behdad Esfahboddef merge(self, m): 16260eb80455e6b574c5074a9b4a2e7504a283175dcBehdad Esfahbod return False # TODO We don't merge hinting data currently. 16345d2f38aa51169f400062f2153a74277b9d4d04eBehdad Esfahbod 164be4ecc7c67917d95b44d16b32d8d72446162f3ddBehdad Esfahbod@_add_method(ttLib.getTableClass('cmap')) 1650bf4f561da7972ab90f6bee87fa30923e201adf5Behdad Esfahboddef merge(self, m): 16671294def6730c37839f03dee519b319f982587eaBehdad Esfahbod # TODO Handle format=14. 1670bf4f561da7972ab90f6bee87fa30923e201adf5Behdad Esfahbod cmapTables = [t for table in m.tables for t in table.tables 16871294def6730c37839f03dee519b319f982587eaBehdad Esfahbod if t.platformID == 3 and t.platEncID in [1, 10]] 16971294def6730c37839f03dee519b319f982587eaBehdad Esfahbod # TODO Better handle format-4 and format-12 coexisting in same font. 17071294def6730c37839f03dee519b319f982587eaBehdad Esfahbod # TODO Insert both a format-4 and format-12 if needed. 171be4ecc7c67917d95b44d16b32d8d72446162f3ddBehdad Esfahbod module = ttLib.getTableModule('cmap') 17271294def6730c37839f03dee519b319f982587eaBehdad Esfahbod assert all(t.format in [4, 12] for t in cmapTables) 17371294def6730c37839f03dee519b319f982587eaBehdad Esfahbod format = max(t.format for t in cmapTables) 17471294def6730c37839f03dee519b319f982587eaBehdad Esfahbod cmapTable = module.cmap_classes[format](format) 17571294def6730c37839f03dee519b319f982587eaBehdad Esfahbod cmapTable.cmap = {} 17671294def6730c37839f03dee519b319f982587eaBehdad Esfahbod cmapTable.platformID = 3 17771294def6730c37839f03dee519b319f982587eaBehdad Esfahbod cmapTable.platEncID = max(t.platEncID for t in cmapTables) 17871294def6730c37839f03dee519b319f982587eaBehdad Esfahbod cmapTable.language = 0 17971294def6730c37839f03dee519b319f982587eaBehdad Esfahbod for table in cmapTables: 18071294def6730c37839f03dee519b319f982587eaBehdad Esfahbod # TODO handle duplicates. 18171294def6730c37839f03dee519b319f982587eaBehdad Esfahbod cmapTable.cmap.update(table.cmap) 18271294def6730c37839f03dee519b319f982587eaBehdad Esfahbod self.tableVersion = 0 18371294def6730c37839f03dee519b319f982587eaBehdad Esfahbod self.tables = [cmapTable] 18471294def6730c37839f03dee519b319f982587eaBehdad Esfahbod self.numSubTables = len(self.tables) 18571294def6730c37839f03dee519b319f982587eaBehdad Esfahbod return True 18671294def6730c37839f03dee519b319f982587eaBehdad Esfahbod 187c14ab48ae84ce77bbcc0942baaa18f8099edb447Behdad Esfahbod@_add_method(ttLib.getTableClass('GDEF')) 188c14ab48ae84ce77bbcc0942baaa18f8099edb447Behdad Esfahboddef merge(self, m): 189c14ab48ae84ce77bbcc0942baaa18f8099edb447Behdad Esfahbod self.table = otTables.GDEF() 19060eb80455e6b574c5074a9b4a2e7504a283175dcBehdad Esfahbod self.table.Version = 1.0 # TODO version 1.2... 191c14ab48ae84ce77bbcc0942baaa18f8099edb447Behdad Esfahbod 192c14ab48ae84ce77bbcc0942baaa18f8099edb447Behdad Esfahbod if any(t.table.LigCaretList for t in m.tables): 193c14ab48ae84ce77bbcc0942baaa18f8099edb447Behdad Esfahbod glyphs = [] 194c14ab48ae84ce77bbcc0942baaa18f8099edb447Behdad Esfahbod ligGlyphs = [] 195c14ab48ae84ce77bbcc0942baaa18f8099edb447Behdad Esfahbod for table in m.tables: 196c14ab48ae84ce77bbcc0942baaa18f8099edb447Behdad Esfahbod if table.table.LigCaretList: 197c14ab48ae84ce77bbcc0942baaa18f8099edb447Behdad Esfahbod glyphs.extend(table.table.LigCaretList.Coverage.glyphs) 198c14ab48ae84ce77bbcc0942baaa18f8099edb447Behdad Esfahbod ligGlyphs.extend(table.table.LigCaretList.LigGlyph) 199c14ab48ae84ce77bbcc0942baaa18f8099edb447Behdad Esfahbod coverage = otTables.Coverage() 200c14ab48ae84ce77bbcc0942baaa18f8099edb447Behdad Esfahbod coverage.glyphs = glyphs 201c14ab48ae84ce77bbcc0942baaa18f8099edb447Behdad Esfahbod ligCaretList = otTables.LigCaretList() 202c14ab48ae84ce77bbcc0942baaa18f8099edb447Behdad Esfahbod ligCaretList.Coverage = coverage 203c14ab48ae84ce77bbcc0942baaa18f8099edb447Behdad Esfahbod ligCaretList.LigGlyph = ligGlyphs 204c14ab48ae84ce77bbcc0942baaa18f8099edb447Behdad Esfahbod ligCaretList.GlyphCount = len(ligGlyphs) 205c14ab48ae84ce77bbcc0942baaa18f8099edb447Behdad Esfahbod self.table.LigCaretList = ligCaretList 206c14ab48ae84ce77bbcc0942baaa18f8099edb447Behdad Esfahbod else: 207c14ab48ae84ce77bbcc0942baaa18f8099edb447Behdad Esfahbod self.table.LigCaretList = None 208c14ab48ae84ce77bbcc0942baaa18f8099edb447Behdad Esfahbod 209c14ab48ae84ce77bbcc0942baaa18f8099edb447Behdad Esfahbod if any(t.table.MarkAttachClassDef for t in m.tables): 210c14ab48ae84ce77bbcc0942baaa18f8099edb447Behdad Esfahbod classDefs = {} 211c14ab48ae84ce77bbcc0942baaa18f8099edb447Behdad Esfahbod for table in m.tables: 212c14ab48ae84ce77bbcc0942baaa18f8099edb447Behdad Esfahbod if table.table.MarkAttachClassDef: 213c14ab48ae84ce77bbcc0942baaa18f8099edb447Behdad Esfahbod classDefs.update(table.table.MarkAttachClassDef.classDefs) 214c14ab48ae84ce77bbcc0942baaa18f8099edb447Behdad Esfahbod self.table.MarkAttachClassDef = otTables.MarkAttachClassDef() 215c14ab48ae84ce77bbcc0942baaa18f8099edb447Behdad Esfahbod self.table.MarkAttachClassDef.classDefs = classDefs 216c14ab48ae84ce77bbcc0942baaa18f8099edb447Behdad Esfahbod else: 217c14ab48ae84ce77bbcc0942baaa18f8099edb447Behdad Esfahbod self.table.MarkAttachClassDef = None 218c14ab48ae84ce77bbcc0942baaa18f8099edb447Behdad Esfahbod 219c14ab48ae84ce77bbcc0942baaa18f8099edb447Behdad Esfahbod if any(t.table.GlyphClassDef for t in m.tables): 220c14ab48ae84ce77bbcc0942baaa18f8099edb447Behdad Esfahbod classDefs = {} 221c14ab48ae84ce77bbcc0942baaa18f8099edb447Behdad Esfahbod for table in m.tables: 222c14ab48ae84ce77bbcc0942baaa18f8099edb447Behdad Esfahbod if table.table.GlyphClassDef: 223c14ab48ae84ce77bbcc0942baaa18f8099edb447Behdad Esfahbod classDefs.update(table.table.GlyphClassDef.classDefs) 224c14ab48ae84ce77bbcc0942baaa18f8099edb447Behdad Esfahbod self.table.GlyphClassDef = otTables.GlyphClassDef() 225c14ab48ae84ce77bbcc0942baaa18f8099edb447Behdad Esfahbod self.table.GlyphClassDef.classDefs = classDefs 226c14ab48ae84ce77bbcc0942baaa18f8099edb447Behdad Esfahbod else: 227c14ab48ae84ce77bbcc0942baaa18f8099edb447Behdad Esfahbod self.table.GlyphClassDef = None 228c14ab48ae84ce77bbcc0942baaa18f8099edb447Behdad Esfahbod 229c14ab48ae84ce77bbcc0942baaa18f8099edb447Behdad Esfahbod if any(t.table.AttachList for t in m.tables): 230c14ab48ae84ce77bbcc0942baaa18f8099edb447Behdad Esfahbod glyphs = [] 231c14ab48ae84ce77bbcc0942baaa18f8099edb447Behdad Esfahbod attachPoints = [] 232c14ab48ae84ce77bbcc0942baaa18f8099edb447Behdad Esfahbod for table in m.tables: 233c14ab48ae84ce77bbcc0942baaa18f8099edb447Behdad Esfahbod if table.table.AttachList: 234c14ab48ae84ce77bbcc0942baaa18f8099edb447Behdad Esfahbod glyphs.extend(table.table.AttachList.Coverage.glyphs) 235c14ab48ae84ce77bbcc0942baaa18f8099edb447Behdad Esfahbod attachPoints.extend(table.table.AttachList.AttachPoint) 236c14ab48ae84ce77bbcc0942baaa18f8099edb447Behdad Esfahbod coverage = otTables.Coverage() 237c14ab48ae84ce77bbcc0942baaa18f8099edb447Behdad Esfahbod coverage.glyphs = glyphs 238c14ab48ae84ce77bbcc0942baaa18f8099edb447Behdad Esfahbod attachList = otTables.AttachList() 239c14ab48ae84ce77bbcc0942baaa18f8099edb447Behdad Esfahbod attachList.Coverage = coverage 240c14ab48ae84ce77bbcc0942baaa18f8099edb447Behdad Esfahbod attachList.AttachPoint = attachPoints 241c14ab48ae84ce77bbcc0942baaa18f8099edb447Behdad Esfahbod attachList.GlyphCount = len(attachPoints) 242c14ab48ae84ce77bbcc0942baaa18f8099edb447Behdad Esfahbod self.table.AttachList = attachList 243c14ab48ae84ce77bbcc0942baaa18f8099edb447Behdad Esfahbod else: 244c14ab48ae84ce77bbcc0942baaa18f8099edb447Behdad Esfahbod self.table.AttachList = None 245c14ab48ae84ce77bbcc0942baaa18f8099edb447Behdad Esfahbod 246c14ab48ae84ce77bbcc0942baaa18f8099edb447Behdad Esfahbod return True 247c14ab48ae84ce77bbcc0942baaa18f8099edb447Behdad Esfahbod 248f2d5982826530296fd7c8f9e2d2a4dc3e070934dBehdad Esfahbod 249f2d5982826530296fd7c8f9e2d2a4dc3e070934dBehdad Esfahbodclass Options(object): 250f2d5982826530296fd7c8f9e2d2a4dc3e070934dBehdad Esfahbod 251f2d5982826530296fd7c8f9e2d2a4dc3e070934dBehdad Esfahbod class UnknownOptionError(Exception): 252f2d5982826530296fd7c8f9e2d2a4dc3e070934dBehdad Esfahbod pass 253f2d5982826530296fd7c8f9e2d2a4dc3e070934dBehdad Esfahbod 254f2d5982826530296fd7c8f9e2d2a4dc3e070934dBehdad Esfahbod _drop_tables_default = ['fpgm', 'prep', 'cvt ', 'gasp'] 255f2d5982826530296fd7c8f9e2d2a4dc3e070934dBehdad Esfahbod drop_tables = _drop_tables_default 256f2d5982826530296fd7c8f9e2d2a4dc3e070934dBehdad Esfahbod 257f2d5982826530296fd7c8f9e2d2a4dc3e070934dBehdad Esfahbod def __init__(self, **kwargs): 258f2d5982826530296fd7c8f9e2d2a4dc3e070934dBehdad Esfahbod 259f2d5982826530296fd7c8f9e2d2a4dc3e070934dBehdad Esfahbod self.set(**kwargs) 260f2d5982826530296fd7c8f9e2d2a4dc3e070934dBehdad Esfahbod 261f2d5982826530296fd7c8f9e2d2a4dc3e070934dBehdad Esfahbod def set(self, **kwargs): 262f63e80e3fc4ef5406109f1fbe2746e26927fdebfBehdad Esfahbod for k,v in kwargs.items(): 263f2d5982826530296fd7c8f9e2d2a4dc3e070934dBehdad Esfahbod if not hasattr(self, k): 264f2d5982826530296fd7c8f9e2d2a4dc3e070934dBehdad Esfahbod raise self.UnknownOptionError("Unknown option '%s'" % k) 265f2d5982826530296fd7c8f9e2d2a4dc3e070934dBehdad Esfahbod setattr(self, k, v) 266f2d5982826530296fd7c8f9e2d2a4dc3e070934dBehdad Esfahbod 267f2d5982826530296fd7c8f9e2d2a4dc3e070934dBehdad Esfahbod def parse_opts(self, argv, ignore_unknown=False): 268f2d5982826530296fd7c8f9e2d2a4dc3e070934dBehdad Esfahbod ret = [] 269f2d5982826530296fd7c8f9e2d2a4dc3e070934dBehdad Esfahbod opts = {} 270f2d5982826530296fd7c8f9e2d2a4dc3e070934dBehdad Esfahbod for a in argv: 271f2d5982826530296fd7c8f9e2d2a4dc3e070934dBehdad Esfahbod orig_a = a 272f2d5982826530296fd7c8f9e2d2a4dc3e070934dBehdad Esfahbod if not a.startswith('--'): 273f2d5982826530296fd7c8f9e2d2a4dc3e070934dBehdad Esfahbod ret.append(a) 274f2d5982826530296fd7c8f9e2d2a4dc3e070934dBehdad Esfahbod continue 275f2d5982826530296fd7c8f9e2d2a4dc3e070934dBehdad Esfahbod a = a[2:] 276f2d5982826530296fd7c8f9e2d2a4dc3e070934dBehdad Esfahbod i = a.find('=') 277f2d5982826530296fd7c8f9e2d2a4dc3e070934dBehdad Esfahbod op = '=' 278f2d5982826530296fd7c8f9e2d2a4dc3e070934dBehdad Esfahbod if i == -1: 279f2d5982826530296fd7c8f9e2d2a4dc3e070934dBehdad Esfahbod if a.startswith("no-"): 280f2d5982826530296fd7c8f9e2d2a4dc3e070934dBehdad Esfahbod k = a[3:] 281f2d5982826530296fd7c8f9e2d2a4dc3e070934dBehdad Esfahbod v = False 282f2d5982826530296fd7c8f9e2d2a4dc3e070934dBehdad Esfahbod else: 283f2d5982826530296fd7c8f9e2d2a4dc3e070934dBehdad Esfahbod k = a 284f2d5982826530296fd7c8f9e2d2a4dc3e070934dBehdad Esfahbod v = True 285f2d5982826530296fd7c8f9e2d2a4dc3e070934dBehdad Esfahbod else: 286f2d5982826530296fd7c8f9e2d2a4dc3e070934dBehdad Esfahbod k = a[:i] 287f2d5982826530296fd7c8f9e2d2a4dc3e070934dBehdad Esfahbod if k[-1] in "-+": 288f2d5982826530296fd7c8f9e2d2a4dc3e070934dBehdad Esfahbod op = k[-1]+'=' # Ops is '-=' or '+=' now. 289f2d5982826530296fd7c8f9e2d2a4dc3e070934dBehdad Esfahbod k = k[:-1] 290f2d5982826530296fd7c8f9e2d2a4dc3e070934dBehdad Esfahbod v = a[i+1:] 291f2d5982826530296fd7c8f9e2d2a4dc3e070934dBehdad Esfahbod k = k.replace('-', '_') 292f2d5982826530296fd7c8f9e2d2a4dc3e070934dBehdad Esfahbod if not hasattr(self, k): 293f2d5982826530296fd7c8f9e2d2a4dc3e070934dBehdad Esfahbod if ignore_unknown == True or k in ignore_unknown: 294f2d5982826530296fd7c8f9e2d2a4dc3e070934dBehdad Esfahbod ret.append(orig_a) 295f2d5982826530296fd7c8f9e2d2a4dc3e070934dBehdad Esfahbod continue 296f2d5982826530296fd7c8f9e2d2a4dc3e070934dBehdad Esfahbod else: 297f2d5982826530296fd7c8f9e2d2a4dc3e070934dBehdad Esfahbod raise self.UnknownOptionError("Unknown option '%s'" % a) 298f2d5982826530296fd7c8f9e2d2a4dc3e070934dBehdad Esfahbod 299f2d5982826530296fd7c8f9e2d2a4dc3e070934dBehdad Esfahbod ov = getattr(self, k) 300f2d5982826530296fd7c8f9e2d2a4dc3e070934dBehdad Esfahbod if isinstance(ov, bool): 301f2d5982826530296fd7c8f9e2d2a4dc3e070934dBehdad Esfahbod v = bool(v) 302f2d5982826530296fd7c8f9e2d2a4dc3e070934dBehdad Esfahbod elif isinstance(ov, int): 303f2d5982826530296fd7c8f9e2d2a4dc3e070934dBehdad Esfahbod v = int(v) 304f2d5982826530296fd7c8f9e2d2a4dc3e070934dBehdad Esfahbod elif isinstance(ov, list): 305f2d5982826530296fd7c8f9e2d2a4dc3e070934dBehdad Esfahbod vv = v.split(',') 306f2d5982826530296fd7c8f9e2d2a4dc3e070934dBehdad Esfahbod if vv == ['']: 307f2d5982826530296fd7c8f9e2d2a4dc3e070934dBehdad Esfahbod vv = [] 308f2d5982826530296fd7c8f9e2d2a4dc3e070934dBehdad Esfahbod vv = [int(x, 0) if len(x) and x[0] in "0123456789" else x for x in vv] 309f2d5982826530296fd7c8f9e2d2a4dc3e070934dBehdad Esfahbod if op == '=': 310f2d5982826530296fd7c8f9e2d2a4dc3e070934dBehdad Esfahbod v = vv 311f2d5982826530296fd7c8f9e2d2a4dc3e070934dBehdad Esfahbod elif op == '+=': 312f2d5982826530296fd7c8f9e2d2a4dc3e070934dBehdad Esfahbod v = ov 313f2d5982826530296fd7c8f9e2d2a4dc3e070934dBehdad Esfahbod v.extend(vv) 314f2d5982826530296fd7c8f9e2d2a4dc3e070934dBehdad Esfahbod elif op == '-=': 315f2d5982826530296fd7c8f9e2d2a4dc3e070934dBehdad Esfahbod v = ov 316f2d5982826530296fd7c8f9e2d2a4dc3e070934dBehdad Esfahbod for x in vv: 317f2d5982826530296fd7c8f9e2d2a4dc3e070934dBehdad Esfahbod if x in v: 318f2d5982826530296fd7c8f9e2d2a4dc3e070934dBehdad Esfahbod v.remove(x) 319f2d5982826530296fd7c8f9e2d2a4dc3e070934dBehdad Esfahbod else: 320f2d5982826530296fd7c8f9e2d2a4dc3e070934dBehdad Esfahbod assert 0 321f2d5982826530296fd7c8f9e2d2a4dc3e070934dBehdad Esfahbod 322f2d5982826530296fd7c8f9e2d2a4dc3e070934dBehdad Esfahbod opts[k] = v 323f2d5982826530296fd7c8f9e2d2a4dc3e070934dBehdad Esfahbod self.set(**opts) 324f2d5982826530296fd7c8f9e2d2a4dc3e070934dBehdad Esfahbod 325f2d5982826530296fd7c8f9e2d2a4dc3e070934dBehdad Esfahbod return ret 326f2d5982826530296fd7c8f9e2d2a4dc3e070934dBehdad Esfahbod 327f2d5982826530296fd7c8f9e2d2a4dc3e070934dBehdad Esfahbod 32845d2f38aa51169f400062f2153a74277b9d4d04eBehdad Esfahbodclass Merger: 32945d2f38aa51169f400062f2153a74277b9d4d04eBehdad Esfahbod 330f2d5982826530296fd7c8f9e2d2a4dc3e070934dBehdad Esfahbod def __init__(self, options=None, log=None): 331f2d5982826530296fd7c8f9e2d2a4dc3e070934dBehdad Esfahbod 332f2d5982826530296fd7c8f9e2d2a4dc3e070934dBehdad Esfahbod if not log: 333f2d5982826530296fd7c8f9e2d2a4dc3e070934dBehdad Esfahbod log = Logger() 334f2d5982826530296fd7c8f9e2d2a4dc3e070934dBehdad Esfahbod if not options: 335f2d5982826530296fd7c8f9e2d2a4dc3e070934dBehdad Esfahbod options = Options() 336f2d5982826530296fd7c8f9e2d2a4dc3e070934dBehdad Esfahbod 337f2d5982826530296fd7c8f9e2d2a4dc3e070934dBehdad Esfahbod self.options = options 338f2d5982826530296fd7c8f9e2d2a4dc3e070934dBehdad Esfahbod self.log = log 33945d2f38aa51169f400062f2153a74277b9d4d04eBehdad Esfahbod 340f2d5982826530296fd7c8f9e2d2a4dc3e070934dBehdad Esfahbod def merge(self, fontfiles): 34145d2f38aa51169f400062f2153a74277b9d4d04eBehdad Esfahbod 34245d2f38aa51169f400062f2153a74277b9d4d04eBehdad Esfahbod mega = ttLib.TTFont() 34345d2f38aa51169f400062f2153a74277b9d4d04eBehdad Esfahbod 34445d2f38aa51169f400062f2153a74277b9d4d04eBehdad Esfahbod # 34545d2f38aa51169f400062f2153a74277b9d4d04eBehdad Esfahbod # Settle on a mega glyph order. 34645d2f38aa51169f400062f2153a74277b9d4d04eBehdad Esfahbod # 347f2d5982826530296fd7c8f9e2d2a4dc3e070934dBehdad Esfahbod fonts = [ttLib.TTFont(fontfile) for fontfile in fontfiles] 34845d2f38aa51169f400062f2153a74277b9d4d04eBehdad Esfahbod glyphOrders = [font.getGlyphOrder() for font in fonts] 34945d2f38aa51169f400062f2153a74277b9d4d04eBehdad Esfahbod megaGlyphOrder = self._mergeGlyphOrders(glyphOrders) 35045d2f38aa51169f400062f2153a74277b9d4d04eBehdad Esfahbod # Reload fonts and set new glyph names on them. 35145d2f38aa51169f400062f2153a74277b9d4d04eBehdad Esfahbod # TODO Is it necessary to reload font? I think it is. At least 35245d2f38aa51169f400062f2153a74277b9d4d04eBehdad Esfahbod # it's safer, in case tables were loaded to provide glyph names. 353f2d5982826530296fd7c8f9e2d2a4dc3e070934dBehdad Esfahbod fonts = [ttLib.TTFont(fontfile) for fontfile in fontfiles] 3543235a04ea938845d656a68c242591d37148ea353Behdad Esfahbod for font,glyphOrder in zip(fonts, glyphOrders): 3553235a04ea938845d656a68c242591d37148ea353Behdad Esfahbod font.setGlyphOrder(glyphOrder) 35645d2f38aa51169f400062f2153a74277b9d4d04eBehdad Esfahbod mega.setGlyphOrder(megaGlyphOrder) 35745d2f38aa51169f400062f2153a74277b9d4d04eBehdad Esfahbod 358f63e80e3fc4ef5406109f1fbe2746e26927fdebfBehdad Esfahbod allTags = reduce(set.union, (list(font.keys()) for font in fonts), set()) 35945d2f38aa51169f400062f2153a74277b9d4d04eBehdad Esfahbod allTags.remove('GlyphOrder') 36045d2f38aa51169f400062f2153a74277b9d4d04eBehdad Esfahbod for tag in allTags: 361f2d5982826530296fd7c8f9e2d2a4dc3e070934dBehdad Esfahbod 362f2d5982826530296fd7c8f9e2d2a4dc3e070934dBehdad Esfahbod if tag in self.options.drop_tables: 363b640f7435c3d29c57310b1a5f7d8e5b538a4c1e3Behdad Esfahbod self.log("Dropping '%s'." % tag) 364f2d5982826530296fd7c8f9e2d2a4dc3e070934dBehdad Esfahbod continue 365f2d5982826530296fd7c8f9e2d2a4dc3e070934dBehdad Esfahbod 36645d2f38aa51169f400062f2153a74277b9d4d04eBehdad Esfahbod clazz = ttLib.getTableClass(tag) 36745d2f38aa51169f400062f2153a74277b9d4d04eBehdad Esfahbod 36845d2f38aa51169f400062f2153a74277b9d4d04eBehdad Esfahbod if not hasattr(clazz, 'merge'): 369b640f7435c3d29c57310b1a5f7d8e5b538a4c1e3Behdad Esfahbod self.log("Don't know how to merge '%s', dropped." % tag) 37045d2f38aa51169f400062f2153a74277b9d4d04eBehdad Esfahbod continue 37145d2f38aa51169f400062f2153a74277b9d4d04eBehdad Esfahbod 37245d2f38aa51169f400062f2153a74277b9d4d04eBehdad Esfahbod # TODO For now assume all fonts have the same tables. 3730bf4f561da7972ab90f6bee87fa30923e201adf5Behdad Esfahbod self.tables = [font[tag] for font in fonts] 37445d2f38aa51169f400062f2153a74277b9d4d04eBehdad Esfahbod table = clazz(tag) 3750bf4f561da7972ab90f6bee87fa30923e201adf5Behdad Esfahbod if table.merge (self): 37665f19d8440f5fa5d94d1f04593f16cd9f21f176bBehdad Esfahbod mega[tag] = table 377b640f7435c3d29c57310b1a5f7d8e5b538a4c1e3Behdad Esfahbod self.log("Merged '%s'." % tag) 37865f19d8440f5fa5d94d1f04593f16cd9f21f176bBehdad Esfahbod else: 379b640f7435c3d29c57310b1a5f7d8e5b538a4c1e3Behdad Esfahbod self.log("Dropped '%s'. No need to merge explicitly." % tag) 380b640f7435c3d29c57310b1a5f7d8e5b538a4c1e3Behdad Esfahbod self.log.lapse("merge '%s'" % tag) 3810bf4f561da7972ab90f6bee87fa30923e201adf5Behdad Esfahbod del self.tables 38245d2f38aa51169f400062f2153a74277b9d4d04eBehdad Esfahbod 38345d2f38aa51169f400062f2153a74277b9d4d04eBehdad Esfahbod return mega 38445d2f38aa51169f400062f2153a74277b9d4d04eBehdad Esfahbod 38545d2f38aa51169f400062f2153a74277b9d4d04eBehdad Esfahbod def _mergeGlyphOrders(self, glyphOrders): 386c2e27fd88f54fcc081049dabce59889cdc87450eBehdad Esfahbod """Modifies passed-in glyphOrders to reflect new glyph names. 387c2e27fd88f54fcc081049dabce59889cdc87450eBehdad Esfahbod Returns glyphOrder for the merged font.""" 38845d2f38aa51169f400062f2153a74277b9d4d04eBehdad Esfahbod # Simply append font index to the glyph name for now. 389c2e27fd88f54fcc081049dabce59889cdc87450eBehdad Esfahbod # TODO Even this simplistic numbering can result in conflicts. 390c2e27fd88f54fcc081049dabce59889cdc87450eBehdad Esfahbod # But then again, we have to improve this soon anyway. 39145d2f38aa51169f400062f2153a74277b9d4d04eBehdad Esfahbod mega = [] 39245d2f38aa51169f400062f2153a74277b9d4d04eBehdad Esfahbod for n,glyphOrder in enumerate(glyphOrders): 39345d2f38aa51169f400062f2153a74277b9d4d04eBehdad Esfahbod for i,glyphName in enumerate(glyphOrder): 394f63e80e3fc4ef5406109f1fbe2746e26927fdebfBehdad Esfahbod glyphName += "#" + repr(n) 39545d2f38aa51169f400062f2153a74277b9d4d04eBehdad Esfahbod glyphOrder[i] = glyphName 39645d2f38aa51169f400062f2153a74277b9d4d04eBehdad Esfahbod mega.append(glyphName) 39745d2f38aa51169f400062f2153a74277b9d4d04eBehdad Esfahbod return mega 39845d2f38aa51169f400062f2153a74277b9d4d04eBehdad Esfahbod 39947bee9cfbd47dc22895003cc94ab91f9075ca27fRoozbeh Pournader def _mergeKeys(self, return_table, logic): 40047bee9cfbd47dc22895003cc94ab91f9075ca27fRoozbeh Pournader logic['tableTag'] = assert_equal 40147bee9cfbd47dc22895003cc94ab91f9075ca27fRoozbeh Pournader allKeys = set.union(set(), *(vars(table).keys() for table in self.tables)) 40247bee9cfbd47dc22895003cc94ab91f9075ca27fRoozbeh Pournader for key in allKeys: 40347bee9cfbd47dc22895003cc94ab91f9075ca27fRoozbeh Pournader merge_logic = logic.get(key, logic['*']) 40447bee9cfbd47dc22895003cc94ab91f9075ca27fRoozbeh Pournader key_value = merge_logic([getattr(table, key) for table in self.tables]) 40547bee9cfbd47dc22895003cc94ab91f9075ca27fRoozbeh Pournader setattr(return_table, key, key_value) 40647bee9cfbd47dc22895003cc94ab91f9075ca27fRoozbeh Pournader 407f2d5982826530296fd7c8f9e2d2a4dc3e070934dBehdad Esfahbod 408f2d5982826530296fd7c8f9e2d2a4dc3e070934dBehdad Esfahbodclass Logger(object): 409f2d5982826530296fd7c8f9e2d2a4dc3e070934dBehdad Esfahbod 410f2d5982826530296fd7c8f9e2d2a4dc3e070934dBehdad Esfahbod def __init__(self, verbose=False, xml=False, timing=False): 411f2d5982826530296fd7c8f9e2d2a4dc3e070934dBehdad Esfahbod self.verbose = verbose 412f2d5982826530296fd7c8f9e2d2a4dc3e070934dBehdad Esfahbod self.xml = xml 413f2d5982826530296fd7c8f9e2d2a4dc3e070934dBehdad Esfahbod self.timing = timing 414f2d5982826530296fd7c8f9e2d2a4dc3e070934dBehdad Esfahbod self.last_time = self.start_time = time.time() 415f2d5982826530296fd7c8f9e2d2a4dc3e070934dBehdad Esfahbod 416f2d5982826530296fd7c8f9e2d2a4dc3e070934dBehdad Esfahbod def parse_opts(self, argv): 417f2d5982826530296fd7c8f9e2d2a4dc3e070934dBehdad Esfahbod argv = argv[:] 418f2d5982826530296fd7c8f9e2d2a4dc3e070934dBehdad Esfahbod for v in ['verbose', 'xml', 'timing']: 419f2d5982826530296fd7c8f9e2d2a4dc3e070934dBehdad Esfahbod if "--"+v in argv: 420f2d5982826530296fd7c8f9e2d2a4dc3e070934dBehdad Esfahbod setattr(self, v, True) 421f2d5982826530296fd7c8f9e2d2a4dc3e070934dBehdad Esfahbod argv.remove("--"+v) 422f2d5982826530296fd7c8f9e2d2a4dc3e070934dBehdad Esfahbod return argv 423f2d5982826530296fd7c8f9e2d2a4dc3e070934dBehdad Esfahbod 424f2d5982826530296fd7c8f9e2d2a4dc3e070934dBehdad Esfahbod def __call__(self, *things): 425f2d5982826530296fd7c8f9e2d2a4dc3e070934dBehdad Esfahbod if not self.verbose: 426f2d5982826530296fd7c8f9e2d2a4dc3e070934dBehdad Esfahbod return 427f63e80e3fc4ef5406109f1fbe2746e26927fdebfBehdad Esfahbod print(' '.join(str(x) for x in things)) 428f2d5982826530296fd7c8f9e2d2a4dc3e070934dBehdad Esfahbod 429f2d5982826530296fd7c8f9e2d2a4dc3e070934dBehdad Esfahbod def lapse(self, *things): 430f2d5982826530296fd7c8f9e2d2a4dc3e070934dBehdad Esfahbod if not self.timing: 431f2d5982826530296fd7c8f9e2d2a4dc3e070934dBehdad Esfahbod return 432f2d5982826530296fd7c8f9e2d2a4dc3e070934dBehdad Esfahbod new_time = time.time() 433f63e80e3fc4ef5406109f1fbe2746e26927fdebfBehdad Esfahbod print("Took %0.3fs to %s" %(new_time - self.last_time, 434f63e80e3fc4ef5406109f1fbe2746e26927fdebfBehdad Esfahbod ' '.join(str(x) for x in things))) 435f2d5982826530296fd7c8f9e2d2a4dc3e070934dBehdad Esfahbod self.last_time = new_time 436f2d5982826530296fd7c8f9e2d2a4dc3e070934dBehdad Esfahbod 437f2d5982826530296fd7c8f9e2d2a4dc3e070934dBehdad Esfahbod def font(self, font, file=sys.stdout): 438f2d5982826530296fd7c8f9e2d2a4dc3e070934dBehdad Esfahbod if not self.xml: 439f2d5982826530296fd7c8f9e2d2a4dc3e070934dBehdad Esfahbod return 440f2d5982826530296fd7c8f9e2d2a4dc3e070934dBehdad Esfahbod from fontTools.misc import xmlWriter 441f2d5982826530296fd7c8f9e2d2a4dc3e070934dBehdad Esfahbod writer = xmlWriter.XMLWriter(file) 442f2d5982826530296fd7c8f9e2d2a4dc3e070934dBehdad Esfahbod font.disassembleInstructions = False # Work around ttLib bug 443f2d5982826530296fd7c8f9e2d2a4dc3e070934dBehdad Esfahbod for tag in font.keys(): 444f2d5982826530296fd7c8f9e2d2a4dc3e070934dBehdad Esfahbod writer.begintag(tag) 445f2d5982826530296fd7c8f9e2d2a4dc3e070934dBehdad Esfahbod writer.newline() 446f2d5982826530296fd7c8f9e2d2a4dc3e070934dBehdad Esfahbod font[tag].toXML(writer, font) 447f2d5982826530296fd7c8f9e2d2a4dc3e070934dBehdad Esfahbod writer.endtag(tag) 448f2d5982826530296fd7c8f9e2d2a4dc3e070934dBehdad Esfahbod writer.newline() 449f2d5982826530296fd7c8f9e2d2a4dc3e070934dBehdad Esfahbod 450f2d5982826530296fd7c8f9e2d2a4dc3e070934dBehdad Esfahbod 451f2d5982826530296fd7c8f9e2d2a4dc3e070934dBehdad Esfahbod__all__ = [ 452f2d5982826530296fd7c8f9e2d2a4dc3e070934dBehdad Esfahbod 'Options', 453f2d5982826530296fd7c8f9e2d2a4dc3e070934dBehdad Esfahbod 'Merger', 454f2d5982826530296fd7c8f9e2d2a4dc3e070934dBehdad Esfahbod 'Logger', 455f2d5982826530296fd7c8f9e2d2a4dc3e070934dBehdad Esfahbod 'main' 456f2d5982826530296fd7c8f9e2d2a4dc3e070934dBehdad Esfahbod] 457f2d5982826530296fd7c8f9e2d2a4dc3e070934dBehdad Esfahbod 45845d2f38aa51169f400062f2153a74277b9d4d04eBehdad Esfahboddef main(args): 459f2d5982826530296fd7c8f9e2d2a4dc3e070934dBehdad Esfahbod 460f2d5982826530296fd7c8f9e2d2a4dc3e070934dBehdad Esfahbod log = Logger() 461f2d5982826530296fd7c8f9e2d2a4dc3e070934dBehdad Esfahbod args = log.parse_opts(args) 462f2d5982826530296fd7c8f9e2d2a4dc3e070934dBehdad Esfahbod 463f2d5982826530296fd7c8f9e2d2a4dc3e070934dBehdad Esfahbod options = Options() 464f2d5982826530296fd7c8f9e2d2a4dc3e070934dBehdad Esfahbod args = options.parse_opts(args) 465f2d5982826530296fd7c8f9e2d2a4dc3e070934dBehdad Esfahbod 46645d2f38aa51169f400062f2153a74277b9d4d04eBehdad Esfahbod if len(args) < 1: 467f63e80e3fc4ef5406109f1fbe2746e26927fdebfBehdad Esfahbod print("usage: pyftmerge font...", file=sys.stderr) 46845d2f38aa51169f400062f2153a74277b9d4d04eBehdad Esfahbod sys.exit(1) 469f2d5982826530296fd7c8f9e2d2a4dc3e070934dBehdad Esfahbod 470f2d5982826530296fd7c8f9e2d2a4dc3e070934dBehdad Esfahbod merger = Merger(options=options, log=log) 471f2d5982826530296fd7c8f9e2d2a4dc3e070934dBehdad Esfahbod font = merger.merge(args) 47245d2f38aa51169f400062f2153a74277b9d4d04eBehdad Esfahbod outfile = 'merged.ttf' 47345d2f38aa51169f400062f2153a74277b9d4d04eBehdad Esfahbod font.save(outfile) 474b640f7435c3d29c57310b1a5f7d8e5b538a4c1e3Behdad Esfahbod log.lapse("compile and save font") 475b640f7435c3d29c57310b1a5f7d8e5b538a4c1e3Behdad Esfahbod 476b640f7435c3d29c57310b1a5f7d8e5b538a4c1e3Behdad Esfahbod log.last_time = log.start_time 477b640f7435c3d29c57310b1a5f7d8e5b538a4c1e3Behdad Esfahbod log.lapse("make one with everything(TOTAL TIME)") 47845d2f38aa51169f400062f2153a74277b9d4d04eBehdad Esfahbod 47945d2f38aa51169f400062f2153a74277b9d4d04eBehdad Esfahbodif __name__ == "__main__": 48045d2f38aa51169f400062f2153a74277b9d4d04eBehdad Esfahbod main(sys.argv[1:]) 481