merge.py revision 8fec68796d90d217a7e869215810b5179cc28c16
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 81ae29591efbb29492ce05378909ccf4028d7c1eeBehdad Esfahbodfrom __future__ import print_function, division, absolute_import 9f63e80e3fc4ef5406109f1fbe2746e26927fdebfBehdad Esfahbodfrom fontTools.misc.py23 import * 10f63e80e3fc4ef5406109f1fbe2746e26927fdebfBehdad Esfahbodfrom fontTools import ttLib, cffLib 11e219c6c76f116a38e0187b4388d99dd59eff30bcRoozbeh Pournaderfrom fontTools.ttLib.tables import otTables, _h_e_a_d 129e6adb6bd6b92765f72d0a2acf871405832aa11eBehdad Esfahbodfrom fontTools.ttLib.tables.DefaultTable import DefaultTable 13f63e80e3fc4ef5406109f1fbe2746e26927fdebfBehdad Esfahbodfrom functools import reduce 1445d2f38aa51169f400062f2153a74277b9d4d04eBehdad Esfahbodimport sys 15f2d5982826530296fd7c8f9e2d2a4dc3e070934dBehdad Esfahbodimport time 1649028b3ba7196b0389ac964f3a2db163367d24b8Behdad Esfahbodimport operator 1745d2f38aa51169f400062f2153a74277b9d4d04eBehdad Esfahbod 1845d2f38aa51169f400062f2153a74277b9d4d04eBehdad Esfahbod 199e6adb6bd6b92765f72d0a2acf871405832aa11eBehdad Esfahboddef _add_method(*clazzes, **kwargs): 20c855f3ab69f31004b6aaca33ac549d384f1efc54Behdad Esfahbod """Returns a decorator function that adds a new method to one or 21c855f3ab69f31004b6aaca33ac549d384f1efc54Behdad Esfahbod more classes.""" 22c68c0ff12fcf38d97304540b7dc0253f4142b046Behdad Esfahbod allowDefault = kwargs.get('allowDefaultTable', False) 23c855f3ab69f31004b6aaca33ac549d384f1efc54Behdad Esfahbod def wrapper(method): 24c855f3ab69f31004b6aaca33ac549d384f1efc54Behdad Esfahbod for clazz in clazzes: 2535e3c7270d93778f51b0b16110acb2ed234f5593Behdad Esfahbod assert allowDefault or clazz != DefaultTable, 'Oops, table class not found.' 269e6adb6bd6b92765f72d0a2acf871405832aa11eBehdad Esfahbod assert method.__name__ not in clazz.__dict__, \ 27c855f3ab69f31004b6aaca33ac549d384f1efc54Behdad Esfahbod "Oops, class '%s' has method '%s'." % (clazz.__name__, 28f63e80e3fc4ef5406109f1fbe2746e26927fdebfBehdad Esfahbod method.__name__) 299e6adb6bd6b92765f72d0a2acf871405832aa11eBehdad Esfahbod setattr(clazz, method.__name__, method) 30c855f3ab69f31004b6aaca33ac549d384f1efc54Behdad Esfahbod return None 31c855f3ab69f31004b6aaca33ac549d384f1efc54Behdad Esfahbod return wrapper 3245d2f38aa51169f400062f2153a74277b9d4d04eBehdad Esfahbod 3347bee9cfbd47dc22895003cc94ab91f9075ca27fRoozbeh Pournader# General utility functions for merging values from different fonts 3492fd5665772996966aaa0709462ebae5a72d4e19Behdad Esfahbod 3549028b3ba7196b0389ac964f3a2db163367d24b8Behdad Esfahboddef equal(lst): 36477dad1ee854c3701f8b0b6ff338fb7523ea27b6Behdad Esfahbod lst = list(lst) 3749028b3ba7196b0389ac964f3a2db163367d24b8Behdad Esfahbod t = iter(lst) 3849028b3ba7196b0389ac964f3a2db163367d24b8Behdad Esfahbod first = next(t) 39477dad1ee854c3701f8b0b6ff338fb7523ea27b6Behdad Esfahbod assert all(item == first for item in t), "Expected all items to be equal: %s" % lst 40e219c6c76f116a38e0187b4388d99dd59eff30bcRoozbeh Pournader return first 4147bee9cfbd47dc22895003cc94ab91f9075ca27fRoozbeh Pournader 4247bee9cfbd47dc22895003cc94ab91f9075ca27fRoozbeh Pournaderdef first(lst): 4349028b3ba7196b0389ac964f3a2db163367d24b8Behdad Esfahbod return next(iter(lst)) 4447bee9cfbd47dc22895003cc94ab91f9075ca27fRoozbeh Pournader 45e219c6c76f116a38e0187b4388d99dd59eff30bcRoozbeh Pournaderdef recalculate(lst): 4692fd5665772996966aaa0709462ebae5a72d4e19Behdad Esfahbod return NotImplemented 47e219c6c76f116a38e0187b4388d99dd59eff30bcRoozbeh Pournader 48e219c6c76f116a38e0187b4388d99dd59eff30bcRoozbeh Pournaderdef current_time(lst): 4949028b3ba7196b0389ac964f3a2db163367d24b8Behdad Esfahbod return int(time.time() - _h_e_a_d.mac_epoch_diff) 50e219c6c76f116a38e0187b4388d99dd59eff30bcRoozbeh Pournader 51642eaf135d7803894c7cf56bdfd4649da9031adeRoozbeh Pournaderdef bitwise_and(lst): 52642eaf135d7803894c7cf56bdfd4649da9031adeRoozbeh Pournader return reduce(operator.and_, lst) 53642eaf135d7803894c7cf56bdfd4649da9031adeRoozbeh Pournader 54e219c6c76f116a38e0187b4388d99dd59eff30bcRoozbeh Pournaderdef bitwise_or(lst): 5549028b3ba7196b0389ac964f3a2db163367d24b8Behdad Esfahbod return reduce(operator.or_, lst) 56e219c6c76f116a38e0187b4388d99dd59eff30bcRoozbeh Pournader 5792fd5665772996966aaa0709462ebae5a72d4e19Behdad Esfahboddef avg_int(lst): 5892fd5665772996966aaa0709462ebae5a72d4e19Behdad Esfahbod lst = list(lst) 5992fd5665772996966aaa0709462ebae5a72d4e19Behdad Esfahbod return sum(lst) // len(lst) 6045d2f38aa51169f400062f2153a74277b9d4d04eBehdad Esfahbod 610d5fcf498c27bc77ff6203469cc7d622a41dcebbBehdad Esfahboddef onlyExisting(func): 6292fd5665772996966aaa0709462ebae5a72d4e19Behdad Esfahbod """Returns a filter func that when called with a list, 6392fd5665772996966aaa0709462ebae5a72d4e19Behdad Esfahbod only calls func on the non-NotImplemented items of the list, 6492fd5665772996966aaa0709462ebae5a72d4e19Behdad Esfahbod and only so if there's at least one item remaining. 6592fd5665772996966aaa0709462ebae5a72d4e19Behdad Esfahbod Otherwise returns NotImplemented.""" 6692fd5665772996966aaa0709462ebae5a72d4e19Behdad Esfahbod 6792fd5665772996966aaa0709462ebae5a72d4e19Behdad Esfahbod def wrapper(lst): 6892fd5665772996966aaa0709462ebae5a72d4e19Behdad Esfahbod items = [item for item in lst if item is not NotImplemented] 6992fd5665772996966aaa0709462ebae5a72d4e19Behdad Esfahbod return func(items) if items else NotImplemented 7092fd5665772996966aaa0709462ebae5a72d4e19Behdad Esfahbod 7192fd5665772996966aaa0709462ebae5a72d4e19Behdad Esfahbod return wrapper 7292fd5665772996966aaa0709462ebae5a72d4e19Behdad Esfahbod 739e6adb6bd6b92765f72d0a2acf871405832aa11eBehdad Esfahboddef sumLists(lst): 749e6adb6bd6b92765f72d0a2acf871405832aa11eBehdad Esfahbod l = [] 759e6adb6bd6b92765f72d0a2acf871405832aa11eBehdad Esfahbod for item in lst: 769e6adb6bd6b92765f72d0a2acf871405832aa11eBehdad Esfahbod l.extend(item) 779e6adb6bd6b92765f72d0a2acf871405832aa11eBehdad Esfahbod return l 789e6adb6bd6b92765f72d0a2acf871405832aa11eBehdad Esfahbod 799e6adb6bd6b92765f72d0a2acf871405832aa11eBehdad Esfahboddef sumDicts(lst): 809e6adb6bd6b92765f72d0a2acf871405832aa11eBehdad Esfahbod d = {} 819e6adb6bd6b92765f72d0a2acf871405832aa11eBehdad Esfahbod for item in lst: 829e6adb6bd6b92765f72d0a2acf871405832aa11eBehdad Esfahbod d.update(item) 839e6adb6bd6b92765f72d0a2acf871405832aa11eBehdad Esfahbod return d 84f2d5982826530296fd7c8f9e2d2a4dc3e070934dBehdad Esfahbod 8512dd547c012d4e1295a6c9c2eb48835cd57851caBehdad Esfahboddef mergeObjects(lst): 860884507c0eedb210bbe691393064083dfc1aa291Behdad Esfahbod lst = [item for item in lst if item is not NotImplemented] 8712dd547c012d4e1295a6c9c2eb48835cd57851caBehdad Esfahbod if not lst: 880884507c0eedb210bbe691393064083dfc1aa291Behdad Esfahbod return NotImplemented 890884507c0eedb210bbe691393064083dfc1aa291Behdad Esfahbod lst = [item for item in lst if item is not None] 900884507c0eedb210bbe691393064083dfc1aa291Behdad Esfahbod if not lst: 910884507c0eedb210bbe691393064083dfc1aa291Behdad Esfahbod return None 9212dd547c012d4e1295a6c9c2eb48835cd57851caBehdad Esfahbod 9312dd547c012d4e1295a6c9c2eb48835cd57851caBehdad Esfahbod clazz = lst[0].__class__ 9412dd547c012d4e1295a6c9c2eb48835cd57851caBehdad Esfahbod assert all(type(item) == clazz for item in lst), lst 950884507c0eedb210bbe691393064083dfc1aa291Behdad Esfahbod 9612dd547c012d4e1295a6c9c2eb48835cd57851caBehdad Esfahbod logic = clazz.mergeMap 9712dd547c012d4e1295a6c9c2eb48835cd57851caBehdad Esfahbod returnTable = clazz() 9882c54638e32a5b7c0f7ad3ac3dafacf7fa27dad4Behdad Esfahbod returnDict = {} 9912dd547c012d4e1295a6c9c2eb48835cd57851caBehdad Esfahbod 10012dd547c012d4e1295a6c9c2eb48835cd57851caBehdad Esfahbod allKeys = set.union(set(), *(vars(table).keys() for table in lst)) 10112dd547c012d4e1295a6c9c2eb48835cd57851caBehdad Esfahbod for key in allKeys: 10212dd547c012d4e1295a6c9c2eb48835cd57851caBehdad Esfahbod try: 10312dd547c012d4e1295a6c9c2eb48835cd57851caBehdad Esfahbod mergeLogic = logic[key] 10412dd547c012d4e1295a6c9c2eb48835cd57851caBehdad Esfahbod except KeyError: 10512dd547c012d4e1295a6c9c2eb48835cd57851caBehdad Esfahbod try: 10612dd547c012d4e1295a6c9c2eb48835cd57851caBehdad Esfahbod mergeLogic = logic['*'] 10712dd547c012d4e1295a6c9c2eb48835cd57851caBehdad Esfahbod except KeyError: 10812dd547c012d4e1295a6c9c2eb48835cd57851caBehdad Esfahbod raise Exception("Don't know how to merge key %s of class %s" % 10912dd547c012d4e1295a6c9c2eb48835cd57851caBehdad Esfahbod (key, clazz.__name__)) 11012dd547c012d4e1295a6c9c2eb48835cd57851caBehdad Esfahbod if mergeLogic is NotImplemented: 11112dd547c012d4e1295a6c9c2eb48835cd57851caBehdad Esfahbod continue 11212dd547c012d4e1295a6c9c2eb48835cd57851caBehdad Esfahbod value = mergeLogic(getattr(table, key, NotImplemented) for table in lst) 11312dd547c012d4e1295a6c9c2eb48835cd57851caBehdad Esfahbod if value is not NotImplemented: 11482c54638e32a5b7c0f7ad3ac3dafacf7fa27dad4Behdad Esfahbod returnDict[key] = value 11582c54638e32a5b7c0f7ad3ac3dafacf7fa27dad4Behdad Esfahbod 11682c54638e32a5b7c0f7ad3ac3dafacf7fa27dad4Behdad Esfahbod returnTable.__dict__ = returnDict 11712dd547c012d4e1295a6c9c2eb48835cd57851caBehdad Esfahbod 11812dd547c012d4e1295a6c9c2eb48835cd57851caBehdad Esfahbod return returnTable 11912dd547c012d4e1295a6c9c2eb48835cd57851caBehdad Esfahbod 120201a68182159fdf58cf54472ef5f4ea4260984e4Behdad Esfahboddef mergeBits(bitmap): 121201a68182159fdf58cf54472ef5f4ea4260984e4Behdad Esfahbod 122201a68182159fdf58cf54472ef5f4ea4260984e4Behdad Esfahbod def wrapper(lst): 123201a68182159fdf58cf54472ef5f4ea4260984e4Behdad Esfahbod lst = list(lst) 124201a68182159fdf58cf54472ef5f4ea4260984e4Behdad Esfahbod returnValue = 0 125201a68182159fdf58cf54472ef5f4ea4260984e4Behdad Esfahbod for bitNumber in range(bitmap['size']): 126642eaf135d7803894c7cf56bdfd4649da9031adeRoozbeh Pournader try: 127201a68182159fdf58cf54472ef5f4ea4260984e4Behdad Esfahbod mergeLogic = bitmap[bitNumber] 128642eaf135d7803894c7cf56bdfd4649da9031adeRoozbeh Pournader except KeyError: 129201a68182159fdf58cf54472ef5f4ea4260984e4Behdad Esfahbod try: 130201a68182159fdf58cf54472ef5f4ea4260984e4Behdad Esfahbod mergeLogic = bitmap['*'] 131201a68182159fdf58cf54472ef5f4ea4260984e4Behdad Esfahbod except KeyError: 132201a68182159fdf58cf54472ef5f4ea4260984e4Behdad Esfahbod raise Exception("Don't know how to merge bit %s" % bitNumber) 133201a68182159fdf58cf54472ef5f4ea4260984e4Behdad Esfahbod shiftedBit = 1 << bitNumber 134201a68182159fdf58cf54472ef5f4ea4260984e4Behdad Esfahbod mergedValue = mergeLogic(bool(item & shiftedBit) for item in lst) 135201a68182159fdf58cf54472ef5f4ea4260984e4Behdad Esfahbod returnValue |= mergedValue << bitNumber 136201a68182159fdf58cf54472ef5f4ea4260984e4Behdad Esfahbod return returnValue 137201a68182159fdf58cf54472ef5f4ea4260984e4Behdad Esfahbod 138201a68182159fdf58cf54472ef5f4ea4260984e4Behdad Esfahbod return wrapper 139642eaf135d7803894c7cf56bdfd4649da9031adeRoozbeh Pournader 14065f19d8440f5fa5d94d1f04593f16cd9f21f176bBehdad Esfahbod 1419e6adb6bd6b92765f72d0a2acf871405832aa11eBehdad Esfahbod@_add_method(DefaultTable, allowDefaultTable=True) 1423b36f55adf2dacf187e60191b32a92703ef77abcBehdad Esfahboddef merge(self, m, tables): 1439e6adb6bd6b92765f72d0a2acf871405832aa11eBehdad Esfahbod if not hasattr(self, 'mergeMap'): 1449e6adb6bd6b92765f72d0a2acf871405832aa11eBehdad Esfahbod m.log("Don't know how to merge '%s'." % self.tableTag) 14592fd5665772996966aaa0709462ebae5a72d4e19Behdad Esfahbod return NotImplemented 1469e6adb6bd6b92765f72d0a2acf871405832aa11eBehdad Esfahbod 14727c71f9f6093fed7668c73b13943f46dac19eecfBehdad Esfahbod logic = self.mergeMap 14827c71f9f6093fed7668c73b13943f46dac19eecfBehdad Esfahbod 14927c71f9f6093fed7668c73b13943f46dac19eecfBehdad Esfahbod if isinstance(logic, dict): 15027c71f9f6093fed7668c73b13943f46dac19eecfBehdad Esfahbod return m.mergeObjects(self, self.mergeMap, tables) 15127c71f9f6093fed7668c73b13943f46dac19eecfBehdad Esfahbod else: 15227c71f9f6093fed7668c73b13943f46dac19eecfBehdad Esfahbod return logic(tables) 15327c71f9f6093fed7668c73b13943f46dac19eecfBehdad Esfahbod 15471294def6730c37839f03dee519b319f982587eaBehdad Esfahbod 1559e6adb6bd6b92765f72d0a2acf871405832aa11eBehdad EsfahbodttLib.getTableClass('maxp').mergeMap = { 1569e6adb6bd6b92765f72d0a2acf871405832aa11eBehdad Esfahbod '*': max, 1579e6adb6bd6b92765f72d0a2acf871405832aa11eBehdad Esfahbod 'tableTag': equal, 1589e6adb6bd6b92765f72d0a2acf871405832aa11eBehdad Esfahbod 'tableVersion': equal, 1599e6adb6bd6b92765f72d0a2acf871405832aa11eBehdad Esfahbod 'numGlyphs': sum, 16027c71f9f6093fed7668c73b13943f46dac19eecfBehdad Esfahbod 'maxStorage': first, 16127c71f9f6093fed7668c73b13943f46dac19eecfBehdad Esfahbod 'maxFunctionDefs': first, 16227c71f9f6093fed7668c73b13943f46dac19eecfBehdad Esfahbod 'maxInstructionDefs': first, 1639e6adb6bd6b92765f72d0a2acf871405832aa11eBehdad Esfahbod # TODO When we correctly merge hinting data, update these values: 1649e6adb6bd6b92765f72d0a2acf871405832aa11eBehdad Esfahbod # maxFunctionDefs, maxInstructionDefs, maxSizeOfInstructions 1659e6adb6bd6b92765f72d0a2acf871405832aa11eBehdad Esfahbod} 1669e6adb6bd6b92765f72d0a2acf871405832aa11eBehdad Esfahbod 167b8039e26530e2a76c3c15c3bd79b31137e3719f2Behdad EsfahbodheadFlagsMergeBitMap = { 168642eaf135d7803894c7cf56bdfd4649da9031adeRoozbeh Pournader 'size': 16, 169642eaf135d7803894c7cf56bdfd4649da9031adeRoozbeh Pournader '*': bitwise_or, 170642eaf135d7803894c7cf56bdfd4649da9031adeRoozbeh Pournader 1: bitwise_and, # Baseline at y = 0 171642eaf135d7803894c7cf56bdfd4649da9031adeRoozbeh Pournader 2: bitwise_and, # lsb at x = 0 172642eaf135d7803894c7cf56bdfd4649da9031adeRoozbeh Pournader 3: bitwise_and, # Force ppem to integer values. FIXME? 173642eaf135d7803894c7cf56bdfd4649da9031adeRoozbeh Pournader 5: bitwise_and, # Font is vertical 174642eaf135d7803894c7cf56bdfd4649da9031adeRoozbeh Pournader 6: lambda bit: 0, # Always set to zero 175642eaf135d7803894c7cf56bdfd4649da9031adeRoozbeh Pournader 11: bitwise_and, # Font data is 'lossless' 176642eaf135d7803894c7cf56bdfd4649da9031adeRoozbeh Pournader 13: bitwise_and, # Optimized for ClearType 177642eaf135d7803894c7cf56bdfd4649da9031adeRoozbeh Pournader 14: bitwise_and, # Last resort font. FIXME? equal or first may be better 178642eaf135d7803894c7cf56bdfd4649da9031adeRoozbeh Pournader 15: lambda bit: 0, # Always set to zero 179642eaf135d7803894c7cf56bdfd4649da9031adeRoozbeh Pournader} 180642eaf135d7803894c7cf56bdfd4649da9031adeRoozbeh Pournader 1819e6adb6bd6b92765f72d0a2acf871405832aa11eBehdad EsfahbodttLib.getTableClass('head').mergeMap = { 1829e6adb6bd6b92765f72d0a2acf871405832aa11eBehdad Esfahbod 'tableTag': equal, 1839e6adb6bd6b92765f72d0a2acf871405832aa11eBehdad Esfahbod 'tableVersion': max, 1849e6adb6bd6b92765f72d0a2acf871405832aa11eBehdad Esfahbod 'fontRevision': max, 18592fd5665772996966aaa0709462ebae5a72d4e19Behdad Esfahbod 'checkSumAdjustment': lambda lst: 0, # We need *something* here 1869e6adb6bd6b92765f72d0a2acf871405832aa11eBehdad Esfahbod 'magicNumber': equal, 187201a68182159fdf58cf54472ef5f4ea4260984e4Behdad Esfahbod 'flags': mergeBits(headFlagsMergeBitMap), 1889e6adb6bd6b92765f72d0a2acf871405832aa11eBehdad Esfahbod 'unitsPerEm': equal, 1899e6adb6bd6b92765f72d0a2acf871405832aa11eBehdad Esfahbod 'created': current_time, 1909e6adb6bd6b92765f72d0a2acf871405832aa11eBehdad Esfahbod 'modified': current_time, 1919e6adb6bd6b92765f72d0a2acf871405832aa11eBehdad Esfahbod 'xMin': min, 1929e6adb6bd6b92765f72d0a2acf871405832aa11eBehdad Esfahbod 'yMin': min, 1939e6adb6bd6b92765f72d0a2acf871405832aa11eBehdad Esfahbod 'xMax': max, 1949e6adb6bd6b92765f72d0a2acf871405832aa11eBehdad Esfahbod 'yMax': max, 1959e6adb6bd6b92765f72d0a2acf871405832aa11eBehdad Esfahbod 'macStyle': first, 1969e6adb6bd6b92765f72d0a2acf871405832aa11eBehdad Esfahbod 'lowestRecPPEM': max, 1979e6adb6bd6b92765f72d0a2acf871405832aa11eBehdad Esfahbod 'fontDirectionHint': lambda lst: 2, 1989e6adb6bd6b92765f72d0a2acf871405832aa11eBehdad Esfahbod 'indexToLocFormat': recalculate, 1999e6adb6bd6b92765f72d0a2acf871405832aa11eBehdad Esfahbod 'glyphDataFormat': equal, 2009e6adb6bd6b92765f72d0a2acf871405832aa11eBehdad Esfahbod} 2019e6adb6bd6b92765f72d0a2acf871405832aa11eBehdad Esfahbod 2029e6adb6bd6b92765f72d0a2acf871405832aa11eBehdad EsfahbodttLib.getTableClass('hhea').mergeMap = { 2039e6adb6bd6b92765f72d0a2acf871405832aa11eBehdad Esfahbod '*': equal, 2049e6adb6bd6b92765f72d0a2acf871405832aa11eBehdad Esfahbod 'tableTag': equal, 2059e6adb6bd6b92765f72d0a2acf871405832aa11eBehdad Esfahbod 'tableVersion': max, 2069e6adb6bd6b92765f72d0a2acf871405832aa11eBehdad Esfahbod 'ascent': max, 2079e6adb6bd6b92765f72d0a2acf871405832aa11eBehdad Esfahbod 'descent': min, 2089e6adb6bd6b92765f72d0a2acf871405832aa11eBehdad Esfahbod 'lineGap': max, 2099e6adb6bd6b92765f72d0a2acf871405832aa11eBehdad Esfahbod 'advanceWidthMax': max, 2109e6adb6bd6b92765f72d0a2acf871405832aa11eBehdad Esfahbod 'minLeftSideBearing': min, 2119e6adb6bd6b92765f72d0a2acf871405832aa11eBehdad Esfahbod 'minRightSideBearing': min, 2129e6adb6bd6b92765f72d0a2acf871405832aa11eBehdad Esfahbod 'xMaxExtent': max, 213642eaf135d7803894c7cf56bdfd4649da9031adeRoozbeh Pournader 'caretSlopeRise': first, 214642eaf135d7803894c7cf56bdfd4649da9031adeRoozbeh Pournader 'caretSlopeRun': first, 215642eaf135d7803894c7cf56bdfd4649da9031adeRoozbeh Pournader 'caretOffset': first, 2169e6adb6bd6b92765f72d0a2acf871405832aa11eBehdad Esfahbod 'numberOfHMetrics': recalculate, 2179e6adb6bd6b92765f72d0a2acf871405832aa11eBehdad Esfahbod} 2189e6adb6bd6b92765f72d0a2acf871405832aa11eBehdad Esfahbod 219b8039e26530e2a76c3c15c3bd79b31137e3719f2Behdad Esfahbodos2FsTypeMergeBitMap = { 220642eaf135d7803894c7cf56bdfd4649da9031adeRoozbeh Pournader 'size': 16, 221642eaf135d7803894c7cf56bdfd4649da9031adeRoozbeh Pournader '*': lambda bit: 0, 222642eaf135d7803894c7cf56bdfd4649da9031adeRoozbeh Pournader 1: bitwise_or, # no embedding permitted 223642eaf135d7803894c7cf56bdfd4649da9031adeRoozbeh Pournader 2: bitwise_and, # allow previewing and printing documents 224642eaf135d7803894c7cf56bdfd4649da9031adeRoozbeh Pournader 3: bitwise_and, # allow editing documents 225642eaf135d7803894c7cf56bdfd4649da9031adeRoozbeh Pournader 8: bitwise_or, # no subsetting permitted 226642eaf135d7803894c7cf56bdfd4649da9031adeRoozbeh Pournader 9: bitwise_or, # no embedding of outlines permitted 227642eaf135d7803894c7cf56bdfd4649da9031adeRoozbeh Pournader} 228642eaf135d7803894c7cf56bdfd4649da9031adeRoozbeh Pournader 229642eaf135d7803894c7cf56bdfd4649da9031adeRoozbeh Pournaderdef mergeOs2FsType(lst): 230642eaf135d7803894c7cf56bdfd4649da9031adeRoozbeh Pournader lst = list(lst) 231642eaf135d7803894c7cf56bdfd4649da9031adeRoozbeh Pournader if all(item == 0 for item in lst): 232642eaf135d7803894c7cf56bdfd4649da9031adeRoozbeh Pournader return 0 233642eaf135d7803894c7cf56bdfd4649da9031adeRoozbeh Pournader 234642eaf135d7803894c7cf56bdfd4649da9031adeRoozbeh Pournader # Compute least restrictive logic for each fsType value 235642eaf135d7803894c7cf56bdfd4649da9031adeRoozbeh Pournader for i in range(len(lst)): 236642eaf135d7803894c7cf56bdfd4649da9031adeRoozbeh Pournader # unset bit 1 (no embedding permitted) if either bit 2 or 3 is set 237642eaf135d7803894c7cf56bdfd4649da9031adeRoozbeh Pournader if lst[i] & 0x000C: 238642eaf135d7803894c7cf56bdfd4649da9031adeRoozbeh Pournader lst[i] &= ~0x0002 239642eaf135d7803894c7cf56bdfd4649da9031adeRoozbeh Pournader # set bit 2 (allow previewing) if bit 3 is set (allow editing) 240642eaf135d7803894c7cf56bdfd4649da9031adeRoozbeh Pournader elif lst[i] & 0x0008: 241642eaf135d7803894c7cf56bdfd4649da9031adeRoozbeh Pournader lst[i] |= 0x0004 242642eaf135d7803894c7cf56bdfd4649da9031adeRoozbeh Pournader # set bits 2 and 3 if everything is allowed 243642eaf135d7803894c7cf56bdfd4649da9031adeRoozbeh Pournader elif lst[i] == 0: 244642eaf135d7803894c7cf56bdfd4649da9031adeRoozbeh Pournader lst[i] = 0x000C 245642eaf135d7803894c7cf56bdfd4649da9031adeRoozbeh Pournader 246201a68182159fdf58cf54472ef5f4ea4260984e4Behdad Esfahbod fsType = mergeBits(os2FsTypeMergeBitMap)(lst) 247642eaf135d7803894c7cf56bdfd4649da9031adeRoozbeh Pournader # unset bits 2 and 3 if bit 1 is set (some font is "no embedding") 248642eaf135d7803894c7cf56bdfd4649da9031adeRoozbeh Pournader if fsType & 0x0002: 249642eaf135d7803894c7cf56bdfd4649da9031adeRoozbeh Pournader fsType &= ~0x000C 250642eaf135d7803894c7cf56bdfd4649da9031adeRoozbeh Pournader return fsType 251642eaf135d7803894c7cf56bdfd4649da9031adeRoozbeh Pournader 252642eaf135d7803894c7cf56bdfd4649da9031adeRoozbeh Pournader 2539e6adb6bd6b92765f72d0a2acf871405832aa11eBehdad EsfahbodttLib.getTableClass('OS/2').mergeMap = { 2549e6adb6bd6b92765f72d0a2acf871405832aa11eBehdad Esfahbod '*': first, 2559e6adb6bd6b92765f72d0a2acf871405832aa11eBehdad Esfahbod 'tableTag': equal, 2569e6adb6bd6b92765f72d0a2acf871405832aa11eBehdad Esfahbod 'version': max, 25792fd5665772996966aaa0709462ebae5a72d4e19Behdad Esfahbod 'xAvgCharWidth': avg_int, # Apparently fontTools doesn't recalc this 258642eaf135d7803894c7cf56bdfd4649da9031adeRoozbeh Pournader 'fsType': mergeOs2FsType, # Will be overwritten 259642eaf135d7803894c7cf56bdfd4649da9031adeRoozbeh Pournader 'panose': first, # FIXME: should really be the first Latin font 2609e6adb6bd6b92765f72d0a2acf871405832aa11eBehdad Esfahbod 'ulUnicodeRange1': bitwise_or, 2619e6adb6bd6b92765f72d0a2acf871405832aa11eBehdad Esfahbod 'ulUnicodeRange2': bitwise_or, 2629e6adb6bd6b92765f72d0a2acf871405832aa11eBehdad Esfahbod 'ulUnicodeRange3': bitwise_or, 2639e6adb6bd6b92765f72d0a2acf871405832aa11eBehdad Esfahbod 'ulUnicodeRange4': bitwise_or, 2649e6adb6bd6b92765f72d0a2acf871405832aa11eBehdad Esfahbod 'fsFirstCharIndex': min, 2659e6adb6bd6b92765f72d0a2acf871405832aa11eBehdad Esfahbod 'fsLastCharIndex': max, 2669e6adb6bd6b92765f72d0a2acf871405832aa11eBehdad Esfahbod 'sTypoAscender': max, 2679e6adb6bd6b92765f72d0a2acf871405832aa11eBehdad Esfahbod 'sTypoDescender': min, 2689e6adb6bd6b92765f72d0a2acf871405832aa11eBehdad Esfahbod 'sTypoLineGap': max, 2699e6adb6bd6b92765f72d0a2acf871405832aa11eBehdad Esfahbod 'usWinAscent': max, 2709e6adb6bd6b92765f72d0a2acf871405832aa11eBehdad Esfahbod 'usWinDescent': max, 2710e235becc52d6048dde39f3bb400c617877302d7Behdad Esfahbod # Version 2,3,4 2727765421ab76c79a38a565d7c8b631b2d1a13781cBehdad Esfahbod 'ulCodePageRange1': onlyExisting(bitwise_or), 2737765421ab76c79a38a565d7c8b631b2d1a13781cBehdad Esfahbod 'ulCodePageRange2': onlyExisting(bitwise_or), 2747765421ab76c79a38a565d7c8b631b2d1a13781cBehdad Esfahbod 'usMaxContex': onlyExisting(max), 2759e6adb6bd6b92765f72d0a2acf871405832aa11eBehdad Esfahbod # TODO version 5 2769e6adb6bd6b92765f72d0a2acf871405832aa11eBehdad Esfahbod} 2779e6adb6bd6b92765f72d0a2acf871405832aa11eBehdad Esfahbod 278642eaf135d7803894c7cf56bdfd4649da9031adeRoozbeh Pournader@_add_method(ttLib.getTableClass('OS/2')) 279642eaf135d7803894c7cf56bdfd4649da9031adeRoozbeh Pournaderdef merge(self, m, tables): 280642eaf135d7803894c7cf56bdfd4649da9031adeRoozbeh Pournader DefaultTable.merge(self, m, tables) 281642eaf135d7803894c7cf56bdfd4649da9031adeRoozbeh Pournader if self.version < 2: 282642eaf135d7803894c7cf56bdfd4649da9031adeRoozbeh Pournader # bits 8 and 9 are reserved and should be set to zero 283642eaf135d7803894c7cf56bdfd4649da9031adeRoozbeh Pournader self.fsType &= ~0x0300 284642eaf135d7803894c7cf56bdfd4649da9031adeRoozbeh Pournader if self.version >= 3: 285642eaf135d7803894c7cf56bdfd4649da9031adeRoozbeh Pournader # Only one of bits 1, 2, and 3 may be set. We already take 286642eaf135d7803894c7cf56bdfd4649da9031adeRoozbeh Pournader # care of bit 1 implications in mergeOs2FsType. So unset 287642eaf135d7803894c7cf56bdfd4649da9031adeRoozbeh Pournader # bit 2 if bit 3 is already set. 288642eaf135d7803894c7cf56bdfd4649da9031adeRoozbeh Pournader if self.fsType & 0x0008: 289642eaf135d7803894c7cf56bdfd4649da9031adeRoozbeh Pournader self.fsType &= ~0x0004 290642eaf135d7803894c7cf56bdfd4649da9031adeRoozbeh Pournader return self 291642eaf135d7803894c7cf56bdfd4649da9031adeRoozbeh Pournader 2929e6adb6bd6b92765f72d0a2acf871405832aa11eBehdad EsfahbodttLib.getTableClass('post').mergeMap = { 2939e6adb6bd6b92765f72d0a2acf871405832aa11eBehdad Esfahbod '*': first, 2949e6adb6bd6b92765f72d0a2acf871405832aa11eBehdad Esfahbod 'tableTag': equal, 2959e6adb6bd6b92765f72d0a2acf871405832aa11eBehdad Esfahbod 'formatType': max, 2969e6adb6bd6b92765f72d0a2acf871405832aa11eBehdad Esfahbod 'isFixedPitch': min, 2979e6adb6bd6b92765f72d0a2acf871405832aa11eBehdad Esfahbod 'minMemType42': max, 2989e6adb6bd6b92765f72d0a2acf871405832aa11eBehdad Esfahbod 'maxMemType42': lambda lst: 0, 2999e6adb6bd6b92765f72d0a2acf871405832aa11eBehdad Esfahbod 'minMemType1': max, 3009e6adb6bd6b92765f72d0a2acf871405832aa11eBehdad Esfahbod 'maxMemType1': lambda lst: 0, 3010d5fcf498c27bc77ff6203469cc7d622a41dcebbBehdad Esfahbod 'mapping': onlyExisting(sumDicts), 302c68c0ff12fcf38d97304540b7dc0253f4142b046Behdad Esfahbod 'extraNames': lambda lst: [], 3039e6adb6bd6b92765f72d0a2acf871405832aa11eBehdad Esfahbod} 304f2d5982826530296fd7c8f9e2d2a4dc3e070934dBehdad Esfahbod 3059e6adb6bd6b92765f72d0a2acf871405832aa11eBehdad EsfahbodttLib.getTableClass('vmtx').mergeMap = ttLib.getTableClass('hmtx').mergeMap = { 3069e6adb6bd6b92765f72d0a2acf871405832aa11eBehdad Esfahbod 'tableTag': equal, 3079e6adb6bd6b92765f72d0a2acf871405832aa11eBehdad Esfahbod 'metrics': sumDicts, 3089e6adb6bd6b92765f72d0a2acf871405832aa11eBehdad Esfahbod} 30965f19d8440f5fa5d94d1f04593f16cd9f21f176bBehdad Esfahbod 3107a27214fcb96457a071c8a55b4ff2b59f5a43e58Roozbeh PournaderttLib.getTableClass('gasp').mergeMap = { 3117a27214fcb96457a071c8a55b4ff2b59f5a43e58Roozbeh Pournader 'tableTag': equal, 3127a27214fcb96457a071c8a55b4ff2b59f5a43e58Roozbeh Pournader 'version': max, 3137a27214fcb96457a071c8a55b4ff2b59f5a43e58Roozbeh Pournader 'gaspRange': first, # FIXME? Appears irreconcilable 3147a27214fcb96457a071c8a55b4ff2b59f5a43e58Roozbeh Pournader} 3157a27214fcb96457a071c8a55b4ff2b59f5a43e58Roozbeh Pournader 3167a27214fcb96457a071c8a55b4ff2b59f5a43e58Roozbeh PournaderttLib.getTableClass('name').mergeMap = { 3177a27214fcb96457a071c8a55b4ff2b59f5a43e58Roozbeh Pournader 'tableTag': equal, 3187a27214fcb96457a071c8a55b4ff2b59f5a43e58Roozbeh Pournader 'names': first, # FIXME? Does mixing name records make sense? 3197a27214fcb96457a071c8a55b4ff2b59f5a43e58Roozbeh Pournader} 3207a27214fcb96457a071c8a55b4ff2b59f5a43e58Roozbeh Pournader 3219e6adb6bd6b92765f72d0a2acf871405832aa11eBehdad EsfahbodttLib.getTableClass('loca').mergeMap = { 32292fd5665772996966aaa0709462ebae5a72d4e19Behdad Esfahbod '*': recalculate, 3239e6adb6bd6b92765f72d0a2acf871405832aa11eBehdad Esfahbod 'tableTag': equal, 3249e6adb6bd6b92765f72d0a2acf871405832aa11eBehdad Esfahbod} 3259e6adb6bd6b92765f72d0a2acf871405832aa11eBehdad Esfahbod 3269e6adb6bd6b92765f72d0a2acf871405832aa11eBehdad EsfahbodttLib.getTableClass('glyf').mergeMap = { 3279e6adb6bd6b92765f72d0a2acf871405832aa11eBehdad Esfahbod 'tableTag': equal, 3289e6adb6bd6b92765f72d0a2acf871405832aa11eBehdad Esfahbod 'glyphs': sumDicts, 3299e6adb6bd6b92765f72d0a2acf871405832aa11eBehdad Esfahbod 'glyphOrder': sumLists, 3309e6adb6bd6b92765f72d0a2acf871405832aa11eBehdad Esfahbod} 331f2d5982826530296fd7c8f9e2d2a4dc3e070934dBehdad Esfahbod 332be4ecc7c67917d95b44d16b32d8d72446162f3ddBehdad Esfahbod@_add_method(ttLib.getTableClass('glyf')) 3333b36f55adf2dacf187e60191b32a92703ef77abcBehdad Esfahboddef merge(self, m, tables): 33427c71f9f6093fed7668c73b13943f46dac19eecfBehdad Esfahbod for i,table in enumerate(tables): 335436503372a7a4aeb529a360e66114ce1b1b6d9ceBehdad Esfahbod for g in table.glyphs.values(): 33627c71f9f6093fed7668c73b13943f46dac19eecfBehdad Esfahbod if i: 33727c71f9f6093fed7668c73b13943f46dac19eecfBehdad Esfahbod # Drop hints for all but first font, since 33827c71f9f6093fed7668c73b13943f46dac19eecfBehdad Esfahbod # we don't map functions / CVT values. 33927c71f9f6093fed7668c73b13943f46dac19eecfBehdad Esfahbod g.removeHinting() 340436503372a7a4aeb529a360e66114ce1b1b6d9ceBehdad Esfahbod # Expand composite glyphs to load their 341436503372a7a4aeb529a360e66114ce1b1b6d9ceBehdad Esfahbod # composite glyph names. 342436503372a7a4aeb529a360e66114ce1b1b6d9ceBehdad Esfahbod if g.isComposite(): 343436503372a7a4aeb529a360e66114ce1b1b6d9ceBehdad Esfahbod g.expand(table) 34492fd5665772996966aaa0709462ebae5a72d4e19Behdad Esfahbod return DefaultTable.merge(self, m, tables) 345f2d5982826530296fd7c8f9e2d2a4dc3e070934dBehdad Esfahbod 34627c71f9f6093fed7668c73b13943f46dac19eecfBehdad EsfahbodttLib.getTableClass('prep').mergeMap = lambda self, lst: first(lst) 34727c71f9f6093fed7668c73b13943f46dac19eecfBehdad EsfahbodttLib.getTableClass('fpgm').mergeMap = lambda self, lst: first(lst) 34827c71f9f6093fed7668c73b13943f46dac19eecfBehdad EsfahbodttLib.getTableClass('cvt ').mergeMap = lambda self, lst: first(lst) 34945d2f38aa51169f400062f2153a74277b9d4d04eBehdad Esfahbod 350be4ecc7c67917d95b44d16b32d8d72446162f3ddBehdad Esfahbod@_add_method(ttLib.getTableClass('cmap')) 3513b36f55adf2dacf187e60191b32a92703ef77abcBehdad Esfahboddef merge(self, m, tables): 35271294def6730c37839f03dee519b319f982587eaBehdad Esfahbod # TODO Handle format=14. 3538fec68796d90d217a7e869215810b5179cc28c16Behdad Esfahbod cmapTables = [(t,fontIdx) for fontIdx,table in enumerate(tables) for t in table.tables 354f480c7cf21c51ef67570f1a5fa1c1653fa817bfcBehdad Esfahbod if t.isUnicode()] 35571294def6730c37839f03dee519b319f982587eaBehdad Esfahbod # TODO Better handle format-4 and format-12 coexisting in same font. 35671294def6730c37839f03dee519b319f982587eaBehdad Esfahbod # TODO Insert both a format-4 and format-12 if needed. 357be4ecc7c67917d95b44d16b32d8d72446162f3ddBehdad Esfahbod module = ttLib.getTableModule('cmap') 3588fec68796d90d217a7e869215810b5179cc28c16Behdad Esfahbod assert all(t.format in [4, 12] for t,_ in cmapTables) 3598fec68796d90d217a7e869215810b5179cc28c16Behdad Esfahbod format = max(t.format for t,_ in cmapTables) 36071294def6730c37839f03dee519b319f982587eaBehdad Esfahbod cmapTable = module.cmap_classes[format](format) 36171294def6730c37839f03dee519b319f982587eaBehdad Esfahbod cmapTable.cmap = {} 36271294def6730c37839f03dee519b319f982587eaBehdad Esfahbod cmapTable.platformID = 3 3638fec68796d90d217a7e869215810b5179cc28c16Behdad Esfahbod cmapTable.platEncID = max(t.platEncID for t,_ in cmapTables) 36471294def6730c37839f03dee519b319f982587eaBehdad Esfahbod cmapTable.language = 0 3658fec68796d90d217a7e869215810b5179cc28c16Behdad Esfahbod cmap = cmapTable.cmap 3668fec68796d90d217a7e869215810b5179cc28c16Behdad Esfahbod for table,fontIdx in cmapTables: 36771294def6730c37839f03dee519b319f982587eaBehdad Esfahbod # TODO handle duplicates. 3688fec68796d90d217a7e869215810b5179cc28c16Behdad Esfahbod for uni,gid in table.cmap.items(): 3698fec68796d90d217a7e869215810b5179cc28c16Behdad Esfahbod oldgid = cmap.get(uni, None) 3708fec68796d90d217a7e869215810b5179cc28c16Behdad Esfahbod if oldgid is None: 3718fec68796d90d217a7e869215810b5179cc28c16Behdad Esfahbod cmap[uni] = gid 3728fec68796d90d217a7e869215810b5179cc28c16Behdad Esfahbod elif oldgid != gid: 3738fec68796d90d217a7e869215810b5179cc28c16Behdad Esfahbod # Char previously mapped to oldgid, now to gid. 3748fec68796d90d217a7e869215810b5179cc28c16Behdad Esfahbod # Record, to fix up in GSUB 'locl' later. 3758fec68796d90d217a7e869215810b5179cc28c16Behdad Esfahbod assert m.duplicateGlyphsPerFont[fontIdx].get(oldgid, gid) == gid 3768fec68796d90d217a7e869215810b5179cc28c16Behdad Esfahbod m.duplicateGlyphsPerFont[fontIdx][oldgid] = gid 37771294def6730c37839f03dee519b319f982587eaBehdad Esfahbod self.tableVersion = 0 37871294def6730c37839f03dee519b319f982587eaBehdad Esfahbod self.tables = [cmapTable] 37971294def6730c37839f03dee519b319f982587eaBehdad Esfahbod self.numSubTables = len(self.tables) 38092fd5665772996966aaa0709462ebae5a72d4e19Behdad Esfahbod return self 38171294def6730c37839f03dee519b319f982587eaBehdad Esfahbod 382c14ab48ae84ce77bbcc0942baaa18f8099edb447Behdad Esfahbod 3832642934116cc0e3b873e37051cb57d3b29c0a9b4Behdad EsfahbodotTables.ScriptList.mergeMap = { 3842642934116cc0e3b873e37051cb57d3b29c0a9b4Behdad Esfahbod 'ScriptCount': sum, 385972af5af639cc036cddd4534c051cc42bbf81672Behdad Esfahbod 'ScriptRecord': lambda lst: sorted(sumLists(lst), key=lambda s: s.ScriptTag), 3862642934116cc0e3b873e37051cb57d3b29c0a9b4Behdad Esfahbod} 3872642934116cc0e3b873e37051cb57d3b29c0a9b4Behdad Esfahbod 3882642934116cc0e3b873e37051cb57d3b29c0a9b4Behdad EsfahbodotTables.FeatureList.mergeMap = { 3892642934116cc0e3b873e37051cb57d3b29c0a9b4Behdad Esfahbod 'FeatureCount': sum, 3902642934116cc0e3b873e37051cb57d3b29c0a9b4Behdad Esfahbod 'FeatureRecord': sumLists, 3912642934116cc0e3b873e37051cb57d3b29c0a9b4Behdad Esfahbod} 3922642934116cc0e3b873e37051cb57d3b29c0a9b4Behdad Esfahbod 3932642934116cc0e3b873e37051cb57d3b29c0a9b4Behdad EsfahbodotTables.LookupList.mergeMap = { 3942642934116cc0e3b873e37051cb57d3b29c0a9b4Behdad Esfahbod 'LookupCount': sum, 3952642934116cc0e3b873e37051cb57d3b29c0a9b4Behdad Esfahbod 'Lookup': sumLists, 3962642934116cc0e3b873e37051cb57d3b29c0a9b4Behdad Esfahbod} 3972642934116cc0e3b873e37051cb57d3b29c0a9b4Behdad Esfahbod 39812dd547c012d4e1295a6c9c2eb48835cd57851caBehdad EsfahbodotTables.Coverage.mergeMap = { 39912dd547c012d4e1295a6c9c2eb48835cd57851caBehdad Esfahbod 'glyphs': sumLists, 40012dd547c012d4e1295a6c9c2eb48835cd57851caBehdad Esfahbod} 40112dd547c012d4e1295a6c9c2eb48835cd57851caBehdad Esfahbod 40212dd547c012d4e1295a6c9c2eb48835cd57851caBehdad EsfahbodotTables.ClassDef.mergeMap = { 40312dd547c012d4e1295a6c9c2eb48835cd57851caBehdad Esfahbod 'classDefs': sumDicts, 40412dd547c012d4e1295a6c9c2eb48835cd57851caBehdad Esfahbod} 40512dd547c012d4e1295a6c9c2eb48835cd57851caBehdad Esfahbod 40612dd547c012d4e1295a6c9c2eb48835cd57851caBehdad EsfahbodotTables.LigCaretList.mergeMap = { 40712dd547c012d4e1295a6c9c2eb48835cd57851caBehdad Esfahbod 'Coverage': mergeObjects, 40812dd547c012d4e1295a6c9c2eb48835cd57851caBehdad Esfahbod 'LigGlyphCount': sum, 40912dd547c012d4e1295a6c9c2eb48835cd57851caBehdad Esfahbod 'LigGlyph': sumLists, 41012dd547c012d4e1295a6c9c2eb48835cd57851caBehdad Esfahbod} 411c14ab48ae84ce77bbcc0942baaa18f8099edb447Behdad Esfahbod 41212dd547c012d4e1295a6c9c2eb48835cd57851caBehdad EsfahbodotTables.AttachList.mergeMap = { 41312dd547c012d4e1295a6c9c2eb48835cd57851caBehdad Esfahbod 'Coverage': mergeObjects, 41412dd547c012d4e1295a6c9c2eb48835cd57851caBehdad Esfahbod 'GlyphCount': sum, 41512dd547c012d4e1295a6c9c2eb48835cd57851caBehdad Esfahbod 'AttachPoint': sumLists, 41612dd547c012d4e1295a6c9c2eb48835cd57851caBehdad Esfahbod} 41712dd547c012d4e1295a6c9c2eb48835cd57851caBehdad Esfahbod 41812dd547c012d4e1295a6c9c2eb48835cd57851caBehdad Esfahbod# XXX Renumber MarkFilterSets of lookups 41912dd547c012d4e1295a6c9c2eb48835cd57851caBehdad EsfahbodotTables.MarkGlyphSetsDef.mergeMap = { 42012dd547c012d4e1295a6c9c2eb48835cd57851caBehdad Esfahbod 'MarkSetTableFormat': equal, 42112dd547c012d4e1295a6c9c2eb48835cd57851caBehdad Esfahbod 'MarkSetCount': sum, 42212dd547c012d4e1295a6c9c2eb48835cd57851caBehdad Esfahbod 'Coverage': sumLists, 42312dd547c012d4e1295a6c9c2eb48835cd57851caBehdad Esfahbod} 42412dd547c012d4e1295a6c9c2eb48835cd57851caBehdad Esfahbod 42512dd547c012d4e1295a6c9c2eb48835cd57851caBehdad EsfahbodotTables.GDEF.mergeMap = { 42612dd547c012d4e1295a6c9c2eb48835cd57851caBehdad Esfahbod '*': mergeObjects, 42712dd547c012d4e1295a6c9c2eb48835cd57851caBehdad Esfahbod 'Version': max, 42812dd547c012d4e1295a6c9c2eb48835cd57851caBehdad Esfahbod} 42912dd547c012d4e1295a6c9c2eb48835cd57851caBehdad Esfahbod 4302642934116cc0e3b873e37051cb57d3b29c0a9b4Behdad EsfahbodotTables.GSUB.mergeMap = otTables.GPOS.mergeMap = { 4312642934116cc0e3b873e37051cb57d3b29c0a9b4Behdad Esfahbod '*': mergeObjects, 43212dd547c012d4e1295a6c9c2eb48835cd57851caBehdad Esfahbod 'Version': max, 4332642934116cc0e3b873e37051cb57d3b29c0a9b4Behdad Esfahbod} 4342642934116cc0e3b873e37051cb57d3b29c0a9b4Behdad Esfahbod 4352642934116cc0e3b873e37051cb57d3b29c0a9b4Behdad EsfahbodttLib.getTableClass('GDEF').mergeMap = \ 4362642934116cc0e3b873e37051cb57d3b29c0a9b4Behdad EsfahbodttLib.getTableClass('GSUB').mergeMap = \ 4372642934116cc0e3b873e37051cb57d3b29c0a9b4Behdad EsfahbodttLib.getTableClass('GPOS').mergeMap = \ 4382642934116cc0e3b873e37051cb57d3b29c0a9b4Behdad EsfahbodttLib.getTableClass('BASE').mergeMap = \ 4392642934116cc0e3b873e37051cb57d3b29c0a9b4Behdad EsfahbodttLib.getTableClass('JSTF').mergeMap = \ 4402642934116cc0e3b873e37051cb57d3b29c0a9b4Behdad EsfahbodttLib.getTableClass('MATH').mergeMap = \ 4412642934116cc0e3b873e37051cb57d3b29c0a9b4Behdad Esfahbod{ 4420d5fcf498c27bc77ff6203469cc7d622a41dcebbBehdad Esfahbod 'tableTag': onlyExisting(equal), # XXX clean me up 44312dd547c012d4e1295a6c9c2eb48835cd57851caBehdad Esfahbod 'table': mergeObjects, 44412dd547c012d4e1295a6c9c2eb48835cd57851caBehdad Esfahbod} 445f2d5982826530296fd7c8f9e2d2a4dc3e070934dBehdad Esfahbod 4468fec68796d90d217a7e869215810b5179cc28c16Behdad Esfahbod@_add_method(ttLib.getTableClass('GSUB')) 4478fec68796d90d217a7e869215810b5179cc28c16Behdad Esfahboddef merge(self, m, tables): 4488fec68796d90d217a7e869215810b5179cc28c16Behdad Esfahbod 4498fec68796d90d217a7e869215810b5179cc28c16Behdad Esfahbod assert len(tables) == len(m.duplicateGlyphsPerFont) 4508fec68796d90d217a7e869215810b5179cc28c16Behdad Esfahbod for i,(table,dups) in enumerate(zip(tables, m.duplicateGlyphsPerFont)): 4518fec68796d90d217a7e869215810b5179cc28c16Behdad Esfahbod if not dups: continue 4528fec68796d90d217a7e869215810b5179cc28c16Behdad Esfahbod assert (table is not None and table is not NotImplemented), "Have duplicates to resolve for font %d but no GSUB" % (i + 1) 4538fec68796d90d217a7e869215810b5179cc28c16Behdad Esfahbod lookupMap = {id(v):v for v in table.table.LookupList.Lookup} 4548fec68796d90d217a7e869215810b5179cc28c16Behdad Esfahbod featureMap = {id(v):v for v in table.table.FeatureList.FeatureRecord} 4558fec68796d90d217a7e869215810b5179cc28c16Behdad Esfahbod synthFeature = None 4568fec68796d90d217a7e869215810b5179cc28c16Behdad Esfahbod synthLookup = None 4578fec68796d90d217a7e869215810b5179cc28c16Behdad Esfahbod for script in table.table.ScriptList.ScriptRecord: 4588fec68796d90d217a7e869215810b5179cc28c16Behdad Esfahbod if script.ScriptTag == 'DFLT': continue # XXX 4598fec68796d90d217a7e869215810b5179cc28c16Behdad Esfahbod for langsys in [script.Script.DefaultLangSys] + [l.LangSys for l in script.Script.LangSysRecord]: 4608fec68796d90d217a7e869215810b5179cc28c16Behdad Esfahbod feature = [featureMap[v] for v in langsys.FeatureIndex if featureMap[v].FeatureTag == 'locl'] 4618fec68796d90d217a7e869215810b5179cc28c16Behdad Esfahbod assert len(feature) <= 1 4628fec68796d90d217a7e869215810b5179cc28c16Behdad Esfahbod if feature: 4638fec68796d90d217a7e869215810b5179cc28c16Behdad Esfahbod feature = feature[0] 4648fec68796d90d217a7e869215810b5179cc28c16Behdad Esfahbod else: 4658fec68796d90d217a7e869215810b5179cc28c16Behdad Esfahbod if not synthFeature: 4668fec68796d90d217a7e869215810b5179cc28c16Behdad Esfahbod synthFeature = otTables.FeatureRecord() 4678fec68796d90d217a7e869215810b5179cc28c16Behdad Esfahbod synthFeature.FeatureTag = 'locl' 4688fec68796d90d217a7e869215810b5179cc28c16Behdad Esfahbod f = synthFeature.Feature = otTables.Feature() 4698fec68796d90d217a7e869215810b5179cc28c16Behdad Esfahbod f.FeatureParams = None 4708fec68796d90d217a7e869215810b5179cc28c16Behdad Esfahbod f.LookupCount = 0 4718fec68796d90d217a7e869215810b5179cc28c16Behdad Esfahbod f.LookupListIndex = [] 4728fec68796d90d217a7e869215810b5179cc28c16Behdad Esfahbod langsys.FeatureIndex.append(id(synthFeature)) 4738fec68796d90d217a7e869215810b5179cc28c16Behdad Esfahbod featureMap[id(synthFeature)] = synthFeature 4748fec68796d90d217a7e869215810b5179cc28c16Behdad Esfahbod langsys.FeatureIndex.sort(key=lambda v: featureMap[v].FeatureTag) 4758fec68796d90d217a7e869215810b5179cc28c16Behdad Esfahbod table.table.FeatureList.FeatureRecord.append(synthFeature) 4768fec68796d90d217a7e869215810b5179cc28c16Behdad Esfahbod table.table.FeatureList.FeatureCount += 1 4778fec68796d90d217a7e869215810b5179cc28c16Behdad Esfahbod feature = synthFeature 4788fec68796d90d217a7e869215810b5179cc28c16Behdad Esfahbod 4798fec68796d90d217a7e869215810b5179cc28c16Behdad Esfahbod if not synthLookup: 4808fec68796d90d217a7e869215810b5179cc28c16Behdad Esfahbod subtable = otTables.SingleSubst() 4818fec68796d90d217a7e869215810b5179cc28c16Behdad Esfahbod subtable.mapping = dups 4828fec68796d90d217a7e869215810b5179cc28c16Behdad Esfahbod synthLookup = otTables.Lookup() 4838fec68796d90d217a7e869215810b5179cc28c16Behdad Esfahbod synthLookup.LookupFlag = 0 4848fec68796d90d217a7e869215810b5179cc28c16Behdad Esfahbod synthLookup.LookupType = 1 4858fec68796d90d217a7e869215810b5179cc28c16Behdad Esfahbod synthLookup.SubTableCount = 1 4868fec68796d90d217a7e869215810b5179cc28c16Behdad Esfahbod synthLookup.SubTable = [subtable] 4878fec68796d90d217a7e869215810b5179cc28c16Behdad Esfahbod table.table.LookupList.Lookup.append(synthLookup) 4888fec68796d90d217a7e869215810b5179cc28c16Behdad Esfahbod table.table.LookupList.LookupCount += 1 4898fec68796d90d217a7e869215810b5179cc28c16Behdad Esfahbod 4908fec68796d90d217a7e869215810b5179cc28c16Behdad Esfahbod feature.Feature.LookupListIndex[:0] = [id(synthLookup)] 4918fec68796d90d217a7e869215810b5179cc28c16Behdad Esfahbod feature.Feature.LookupCount += 1 4928fec68796d90d217a7e869215810b5179cc28c16Behdad Esfahbod 4938fec68796d90d217a7e869215810b5179cc28c16Behdad Esfahbod 4948fec68796d90d217a7e869215810b5179cc28c16Behdad Esfahbod DefaultTable.merge(self, m, tables) 4958fec68796d90d217a7e869215810b5179cc28c16Behdad Esfahbod return self 4968fec68796d90d217a7e869215810b5179cc28c16Behdad Esfahbod 4978fec68796d90d217a7e869215810b5179cc28c16Behdad Esfahbod 4982642934116cc0e3b873e37051cb57d3b29c0a9b4Behdad Esfahbod 4995080331251906861fbba4a9986efcd04978207beBehdad Esfahbod@_add_method(otTables.SingleSubst, 5005080331251906861fbba4a9986efcd04978207beBehdad Esfahbod otTables.MultipleSubst, 5015080331251906861fbba4a9986efcd04978207beBehdad Esfahbod otTables.AlternateSubst, 5025080331251906861fbba4a9986efcd04978207beBehdad Esfahbod otTables.LigatureSubst, 5035080331251906861fbba4a9986efcd04978207beBehdad Esfahbod otTables.ReverseChainSingleSubst, 5045080331251906861fbba4a9986efcd04978207beBehdad Esfahbod otTables.SinglePos, 5055080331251906861fbba4a9986efcd04978207beBehdad Esfahbod otTables.PairPos, 5065080331251906861fbba4a9986efcd04978207beBehdad Esfahbod otTables.CursivePos, 5075080331251906861fbba4a9986efcd04978207beBehdad Esfahbod otTables.MarkBasePos, 5085080331251906861fbba4a9986efcd04978207beBehdad Esfahbod otTables.MarkLigPos, 5095080331251906861fbba4a9986efcd04978207beBehdad Esfahbod otTables.MarkMarkPos) 5105080331251906861fbba4a9986efcd04978207beBehdad Esfahboddef mapLookups(self, lookupMap): 5115080331251906861fbba4a9986efcd04978207beBehdad Esfahbod pass 5125080331251906861fbba4a9986efcd04978207beBehdad Esfahbod 5135080331251906861fbba4a9986efcd04978207beBehdad Esfahbod# Copied and trimmed down from subset.py 5145080331251906861fbba4a9986efcd04978207beBehdad Esfahbod@_add_method(otTables.ContextSubst, 5155080331251906861fbba4a9986efcd04978207beBehdad Esfahbod otTables.ChainContextSubst, 5165080331251906861fbba4a9986efcd04978207beBehdad Esfahbod otTables.ContextPos, 5175080331251906861fbba4a9986efcd04978207beBehdad Esfahbod otTables.ChainContextPos) 5185080331251906861fbba4a9986efcd04978207beBehdad Esfahboddef __classify_context(self): 5195080331251906861fbba4a9986efcd04978207beBehdad Esfahbod 5205080331251906861fbba4a9986efcd04978207beBehdad Esfahbod class ContextHelper(object): 5215080331251906861fbba4a9986efcd04978207beBehdad Esfahbod def __init__(self, klass, Format): 5225080331251906861fbba4a9986efcd04978207beBehdad Esfahbod if klass.__name__.endswith('Subst'): 5235080331251906861fbba4a9986efcd04978207beBehdad Esfahbod Typ = 'Sub' 5245080331251906861fbba4a9986efcd04978207beBehdad Esfahbod Type = 'Subst' 5255080331251906861fbba4a9986efcd04978207beBehdad Esfahbod else: 5265080331251906861fbba4a9986efcd04978207beBehdad Esfahbod Typ = 'Pos' 5275080331251906861fbba4a9986efcd04978207beBehdad Esfahbod Type = 'Pos' 5285080331251906861fbba4a9986efcd04978207beBehdad Esfahbod if klass.__name__.startswith('Chain'): 5295080331251906861fbba4a9986efcd04978207beBehdad Esfahbod Chain = 'Chain' 5305080331251906861fbba4a9986efcd04978207beBehdad Esfahbod else: 5315080331251906861fbba4a9986efcd04978207beBehdad Esfahbod Chain = '' 5325080331251906861fbba4a9986efcd04978207beBehdad Esfahbod ChainTyp = Chain+Typ 5335080331251906861fbba4a9986efcd04978207beBehdad Esfahbod 5345080331251906861fbba4a9986efcd04978207beBehdad Esfahbod self.Typ = Typ 5355080331251906861fbba4a9986efcd04978207beBehdad Esfahbod self.Type = Type 5365080331251906861fbba4a9986efcd04978207beBehdad Esfahbod self.Chain = Chain 5375080331251906861fbba4a9986efcd04978207beBehdad Esfahbod self.ChainTyp = ChainTyp 5385080331251906861fbba4a9986efcd04978207beBehdad Esfahbod 5395080331251906861fbba4a9986efcd04978207beBehdad Esfahbod self.LookupRecord = Type+'LookupRecord' 5405080331251906861fbba4a9986efcd04978207beBehdad Esfahbod 5415080331251906861fbba4a9986efcd04978207beBehdad Esfahbod if Format == 1: 5425080331251906861fbba4a9986efcd04978207beBehdad Esfahbod self.Rule = ChainTyp+'Rule' 5435080331251906861fbba4a9986efcd04978207beBehdad Esfahbod self.RuleSet = ChainTyp+'RuleSet' 5445080331251906861fbba4a9986efcd04978207beBehdad Esfahbod elif Format == 2: 5455080331251906861fbba4a9986efcd04978207beBehdad Esfahbod self.Rule = ChainTyp+'ClassRule' 5465080331251906861fbba4a9986efcd04978207beBehdad Esfahbod self.RuleSet = ChainTyp+'ClassSet' 5475080331251906861fbba4a9986efcd04978207beBehdad Esfahbod 5485080331251906861fbba4a9986efcd04978207beBehdad Esfahbod if self.Format not in [1, 2, 3]: 5495080331251906861fbba4a9986efcd04978207beBehdad Esfahbod return None # Don't shoot the messenger; let it go 5505080331251906861fbba4a9986efcd04978207beBehdad Esfahbod if not hasattr(self.__class__, "__ContextHelpers"): 5515080331251906861fbba4a9986efcd04978207beBehdad Esfahbod self.__class__.__ContextHelpers = {} 5525080331251906861fbba4a9986efcd04978207beBehdad Esfahbod if self.Format not in self.__class__.__ContextHelpers: 5535080331251906861fbba4a9986efcd04978207beBehdad Esfahbod helper = ContextHelper(self.__class__, self.Format) 5545080331251906861fbba4a9986efcd04978207beBehdad Esfahbod self.__class__.__ContextHelpers[self.Format] = helper 5555080331251906861fbba4a9986efcd04978207beBehdad Esfahbod return self.__class__.__ContextHelpers[self.Format] 5565080331251906861fbba4a9986efcd04978207beBehdad Esfahbod 5575080331251906861fbba4a9986efcd04978207beBehdad Esfahbod 5585080331251906861fbba4a9986efcd04978207beBehdad Esfahbod@_add_method(otTables.ContextSubst, 5595080331251906861fbba4a9986efcd04978207beBehdad Esfahbod otTables.ChainContextSubst, 5605080331251906861fbba4a9986efcd04978207beBehdad Esfahbod otTables.ContextPos, 5615080331251906861fbba4a9986efcd04978207beBehdad Esfahbod otTables.ChainContextPos) 5625080331251906861fbba4a9986efcd04978207beBehdad Esfahboddef mapLookups(self, lookupMap): 5635080331251906861fbba4a9986efcd04978207beBehdad Esfahbod c = self.__classify_context() 5645080331251906861fbba4a9986efcd04978207beBehdad Esfahbod 5655080331251906861fbba4a9986efcd04978207beBehdad Esfahbod if self.Format in [1, 2]: 5665080331251906861fbba4a9986efcd04978207beBehdad Esfahbod for rs in getattr(self, c.RuleSet): 5675080331251906861fbba4a9986efcd04978207beBehdad Esfahbod if not rs: continue 5685080331251906861fbba4a9986efcd04978207beBehdad Esfahbod for r in getattr(rs, c.Rule): 5695080331251906861fbba4a9986efcd04978207beBehdad Esfahbod if not r: continue 5705080331251906861fbba4a9986efcd04978207beBehdad Esfahbod for ll in getattr(r, c.LookupRecord): 5715080331251906861fbba4a9986efcd04978207beBehdad Esfahbod if not ll: continue 5725080331251906861fbba4a9986efcd04978207beBehdad Esfahbod ll.LookupListIndex = lookupMap[ll.LookupListIndex] 5735080331251906861fbba4a9986efcd04978207beBehdad Esfahbod elif self.Format == 3: 5745080331251906861fbba4a9986efcd04978207beBehdad Esfahbod for ll in getattr(self, c.LookupRecord): 5755080331251906861fbba4a9986efcd04978207beBehdad Esfahbod if not ll: continue 5765080331251906861fbba4a9986efcd04978207beBehdad Esfahbod ll.LookupListIndex = lookupMap[ll.LookupListIndex] 5775080331251906861fbba4a9986efcd04978207beBehdad Esfahbod else: 5785080331251906861fbba4a9986efcd04978207beBehdad Esfahbod assert 0, "unknown format: %s" % self.Format 5795080331251906861fbba4a9986efcd04978207beBehdad Esfahbod 5805080331251906861fbba4a9986efcd04978207beBehdad Esfahbod@_add_method(otTables.Lookup) 5815080331251906861fbba4a9986efcd04978207beBehdad Esfahboddef mapLookups(self, lookupMap): 5825080331251906861fbba4a9986efcd04978207beBehdad Esfahbod for st in self.SubTable: 5835080331251906861fbba4a9986efcd04978207beBehdad Esfahbod if not st: continue 5845080331251906861fbba4a9986efcd04978207beBehdad Esfahbod st.mapLookups(lookupMap) 5855080331251906861fbba4a9986efcd04978207beBehdad Esfahbod 5865080331251906861fbba4a9986efcd04978207beBehdad Esfahbod@_add_method(otTables.LookupList) 5875080331251906861fbba4a9986efcd04978207beBehdad Esfahboddef mapLookups(self, lookupMap): 5885080331251906861fbba4a9986efcd04978207beBehdad Esfahbod for l in self.Lookup: 5895080331251906861fbba4a9986efcd04978207beBehdad Esfahbod if not l: continue 5905080331251906861fbba4a9986efcd04978207beBehdad Esfahbod l.mapLookups(lookupMap) 5915080331251906861fbba4a9986efcd04978207beBehdad Esfahbod 5922642934116cc0e3b873e37051cb57d3b29c0a9b4Behdad Esfahbod@_add_method(otTables.Feature) 5932642934116cc0e3b873e37051cb57d3b29c0a9b4Behdad Esfahboddef mapLookups(self, lookupMap): 5942642934116cc0e3b873e37051cb57d3b29c0a9b4Behdad Esfahbod self.LookupListIndex = [lookupMap[i] for i in self.LookupListIndex] 5952642934116cc0e3b873e37051cb57d3b29c0a9b4Behdad Esfahbod 5962642934116cc0e3b873e37051cb57d3b29c0a9b4Behdad Esfahbod@_add_method(otTables.FeatureList) 5972642934116cc0e3b873e37051cb57d3b29c0a9b4Behdad Esfahboddef mapLookups(self, lookupMap): 5982642934116cc0e3b873e37051cb57d3b29c0a9b4Behdad Esfahbod for f in self.FeatureRecord: 5992642934116cc0e3b873e37051cb57d3b29c0a9b4Behdad Esfahbod if not f or not f.Feature: continue 6002642934116cc0e3b873e37051cb57d3b29c0a9b4Behdad Esfahbod f.Feature.mapLookups(lookupMap) 6012642934116cc0e3b873e37051cb57d3b29c0a9b4Behdad Esfahbod 6022642934116cc0e3b873e37051cb57d3b29c0a9b4Behdad Esfahbod@_add_method(otTables.DefaultLangSys, 6032642934116cc0e3b873e37051cb57d3b29c0a9b4Behdad Esfahbod otTables.LangSys) 6042642934116cc0e3b873e37051cb57d3b29c0a9b4Behdad Esfahboddef mapFeatures(self, featureMap): 6052642934116cc0e3b873e37051cb57d3b29c0a9b4Behdad Esfahbod self.FeatureIndex = [featureMap[i] for i in self.FeatureIndex] 6062642934116cc0e3b873e37051cb57d3b29c0a9b4Behdad Esfahbod if self.ReqFeatureIndex != 65535: 6072642934116cc0e3b873e37051cb57d3b29c0a9b4Behdad Esfahbod self.ReqFeatureIndex = featureMap[self.ReqFeatureIndex] 6082642934116cc0e3b873e37051cb57d3b29c0a9b4Behdad Esfahbod 6092642934116cc0e3b873e37051cb57d3b29c0a9b4Behdad Esfahbod@_add_method(otTables.Script) 6102642934116cc0e3b873e37051cb57d3b29c0a9b4Behdad Esfahboddef mapFeatures(self, featureMap): 6112642934116cc0e3b873e37051cb57d3b29c0a9b4Behdad Esfahbod if self.DefaultLangSys: 6122642934116cc0e3b873e37051cb57d3b29c0a9b4Behdad Esfahbod self.DefaultLangSys.mapFeatures(featureMap) 6132642934116cc0e3b873e37051cb57d3b29c0a9b4Behdad Esfahbod for l in self.LangSysRecord: 6142642934116cc0e3b873e37051cb57d3b29c0a9b4Behdad Esfahbod if not l or not l.LangSys: continue 6152642934116cc0e3b873e37051cb57d3b29c0a9b4Behdad Esfahbod l.LangSys.mapFeatures(featureMap) 6162642934116cc0e3b873e37051cb57d3b29c0a9b4Behdad Esfahbod 6172642934116cc0e3b873e37051cb57d3b29c0a9b4Behdad Esfahbod@_add_method(otTables.ScriptList) 618398770d51c3ae0e7c267ef2b315beae2b6aa2df8Behdad Esfahboddef mapFeatures(self, featureMap): 6192642934116cc0e3b873e37051cb57d3b29c0a9b4Behdad Esfahbod for s in self.ScriptRecord: 6202642934116cc0e3b873e37051cb57d3b29c0a9b4Behdad Esfahbod if not s or not s.Script: continue 6212642934116cc0e3b873e37051cb57d3b29c0a9b4Behdad Esfahbod s.Script.mapFeatures(featureMap) 6222642934116cc0e3b873e37051cb57d3b29c0a9b4Behdad Esfahbod 6232642934116cc0e3b873e37051cb57d3b29c0a9b4Behdad Esfahbod 624f2d5982826530296fd7c8f9e2d2a4dc3e070934dBehdad Esfahbodclass Options(object): 625f2d5982826530296fd7c8f9e2d2a4dc3e070934dBehdad Esfahbod 626f2d5982826530296fd7c8f9e2d2a4dc3e070934dBehdad Esfahbod class UnknownOptionError(Exception): 627f2d5982826530296fd7c8f9e2d2a4dc3e070934dBehdad Esfahbod pass 628f2d5982826530296fd7c8f9e2d2a4dc3e070934dBehdad Esfahbod 629f2d5982826530296fd7c8f9e2d2a4dc3e070934dBehdad Esfahbod def __init__(self, **kwargs): 630f2d5982826530296fd7c8f9e2d2a4dc3e070934dBehdad Esfahbod 631f2d5982826530296fd7c8f9e2d2a4dc3e070934dBehdad Esfahbod self.set(**kwargs) 632f2d5982826530296fd7c8f9e2d2a4dc3e070934dBehdad Esfahbod 633f2d5982826530296fd7c8f9e2d2a4dc3e070934dBehdad Esfahbod def set(self, **kwargs): 634f63e80e3fc4ef5406109f1fbe2746e26927fdebfBehdad Esfahbod for k,v in kwargs.items(): 635f2d5982826530296fd7c8f9e2d2a4dc3e070934dBehdad Esfahbod if not hasattr(self, k): 636f2d5982826530296fd7c8f9e2d2a4dc3e070934dBehdad Esfahbod raise self.UnknownOptionError("Unknown option '%s'" % k) 637f2d5982826530296fd7c8f9e2d2a4dc3e070934dBehdad Esfahbod setattr(self, k, v) 638f2d5982826530296fd7c8f9e2d2a4dc3e070934dBehdad Esfahbod 639f2d5982826530296fd7c8f9e2d2a4dc3e070934dBehdad Esfahbod def parse_opts(self, argv, ignore_unknown=False): 640f2d5982826530296fd7c8f9e2d2a4dc3e070934dBehdad Esfahbod ret = [] 641f2d5982826530296fd7c8f9e2d2a4dc3e070934dBehdad Esfahbod opts = {} 642f2d5982826530296fd7c8f9e2d2a4dc3e070934dBehdad Esfahbod for a in argv: 643f2d5982826530296fd7c8f9e2d2a4dc3e070934dBehdad Esfahbod orig_a = a 644f2d5982826530296fd7c8f9e2d2a4dc3e070934dBehdad Esfahbod if not a.startswith('--'): 645f2d5982826530296fd7c8f9e2d2a4dc3e070934dBehdad Esfahbod ret.append(a) 646f2d5982826530296fd7c8f9e2d2a4dc3e070934dBehdad Esfahbod continue 647f2d5982826530296fd7c8f9e2d2a4dc3e070934dBehdad Esfahbod a = a[2:] 648f2d5982826530296fd7c8f9e2d2a4dc3e070934dBehdad Esfahbod i = a.find('=') 649f2d5982826530296fd7c8f9e2d2a4dc3e070934dBehdad Esfahbod op = '=' 650f2d5982826530296fd7c8f9e2d2a4dc3e070934dBehdad Esfahbod if i == -1: 651f2d5982826530296fd7c8f9e2d2a4dc3e070934dBehdad Esfahbod if a.startswith("no-"): 652f2d5982826530296fd7c8f9e2d2a4dc3e070934dBehdad Esfahbod k = a[3:] 653f2d5982826530296fd7c8f9e2d2a4dc3e070934dBehdad Esfahbod v = False 654f2d5982826530296fd7c8f9e2d2a4dc3e070934dBehdad Esfahbod else: 655f2d5982826530296fd7c8f9e2d2a4dc3e070934dBehdad Esfahbod k = a 656f2d5982826530296fd7c8f9e2d2a4dc3e070934dBehdad Esfahbod v = True 657f2d5982826530296fd7c8f9e2d2a4dc3e070934dBehdad Esfahbod else: 658f2d5982826530296fd7c8f9e2d2a4dc3e070934dBehdad Esfahbod k = a[:i] 659f2d5982826530296fd7c8f9e2d2a4dc3e070934dBehdad Esfahbod if k[-1] in "-+": 660f2d5982826530296fd7c8f9e2d2a4dc3e070934dBehdad Esfahbod op = k[-1]+'=' # Ops is '-=' or '+=' now. 661f2d5982826530296fd7c8f9e2d2a4dc3e070934dBehdad Esfahbod k = k[:-1] 662f2d5982826530296fd7c8f9e2d2a4dc3e070934dBehdad Esfahbod v = a[i+1:] 663f2d5982826530296fd7c8f9e2d2a4dc3e070934dBehdad Esfahbod k = k.replace('-', '_') 664f2d5982826530296fd7c8f9e2d2a4dc3e070934dBehdad Esfahbod if not hasattr(self, k): 665f2d5982826530296fd7c8f9e2d2a4dc3e070934dBehdad Esfahbod if ignore_unknown == True or k in ignore_unknown: 666f2d5982826530296fd7c8f9e2d2a4dc3e070934dBehdad Esfahbod ret.append(orig_a) 667f2d5982826530296fd7c8f9e2d2a4dc3e070934dBehdad Esfahbod continue 668f2d5982826530296fd7c8f9e2d2a4dc3e070934dBehdad Esfahbod else: 669f2d5982826530296fd7c8f9e2d2a4dc3e070934dBehdad Esfahbod raise self.UnknownOptionError("Unknown option '%s'" % a) 670f2d5982826530296fd7c8f9e2d2a4dc3e070934dBehdad Esfahbod 671f2d5982826530296fd7c8f9e2d2a4dc3e070934dBehdad Esfahbod ov = getattr(self, k) 672f2d5982826530296fd7c8f9e2d2a4dc3e070934dBehdad Esfahbod if isinstance(ov, bool): 673f2d5982826530296fd7c8f9e2d2a4dc3e070934dBehdad Esfahbod v = bool(v) 674f2d5982826530296fd7c8f9e2d2a4dc3e070934dBehdad Esfahbod elif isinstance(ov, int): 675f2d5982826530296fd7c8f9e2d2a4dc3e070934dBehdad Esfahbod v = int(v) 676f2d5982826530296fd7c8f9e2d2a4dc3e070934dBehdad Esfahbod elif isinstance(ov, list): 677f2d5982826530296fd7c8f9e2d2a4dc3e070934dBehdad Esfahbod vv = v.split(',') 678f2d5982826530296fd7c8f9e2d2a4dc3e070934dBehdad Esfahbod if vv == ['']: 679f2d5982826530296fd7c8f9e2d2a4dc3e070934dBehdad Esfahbod vv = [] 680f2d5982826530296fd7c8f9e2d2a4dc3e070934dBehdad Esfahbod vv = [int(x, 0) if len(x) and x[0] in "0123456789" else x for x in vv] 681f2d5982826530296fd7c8f9e2d2a4dc3e070934dBehdad Esfahbod if op == '=': 682f2d5982826530296fd7c8f9e2d2a4dc3e070934dBehdad Esfahbod v = vv 683f2d5982826530296fd7c8f9e2d2a4dc3e070934dBehdad Esfahbod elif op == '+=': 684f2d5982826530296fd7c8f9e2d2a4dc3e070934dBehdad Esfahbod v = ov 685f2d5982826530296fd7c8f9e2d2a4dc3e070934dBehdad Esfahbod v.extend(vv) 686f2d5982826530296fd7c8f9e2d2a4dc3e070934dBehdad Esfahbod elif op == '-=': 687f2d5982826530296fd7c8f9e2d2a4dc3e070934dBehdad Esfahbod v = ov 688f2d5982826530296fd7c8f9e2d2a4dc3e070934dBehdad Esfahbod for x in vv: 689f2d5982826530296fd7c8f9e2d2a4dc3e070934dBehdad Esfahbod if x in v: 690f2d5982826530296fd7c8f9e2d2a4dc3e070934dBehdad Esfahbod v.remove(x) 691f2d5982826530296fd7c8f9e2d2a4dc3e070934dBehdad Esfahbod else: 692f2d5982826530296fd7c8f9e2d2a4dc3e070934dBehdad Esfahbod assert 0 693f2d5982826530296fd7c8f9e2d2a4dc3e070934dBehdad Esfahbod 694f2d5982826530296fd7c8f9e2d2a4dc3e070934dBehdad Esfahbod opts[k] = v 695f2d5982826530296fd7c8f9e2d2a4dc3e070934dBehdad Esfahbod self.set(**opts) 696f2d5982826530296fd7c8f9e2d2a4dc3e070934dBehdad Esfahbod 697f2d5982826530296fd7c8f9e2d2a4dc3e070934dBehdad Esfahbod return ret 698f2d5982826530296fd7c8f9e2d2a4dc3e070934dBehdad Esfahbod 699f2d5982826530296fd7c8f9e2d2a4dc3e070934dBehdad Esfahbod 7009e6adb6bd6b92765f72d0a2acf871405832aa11eBehdad Esfahbodclass Merger(object): 70145d2f38aa51169f400062f2153a74277b9d4d04eBehdad Esfahbod 702f2d5982826530296fd7c8f9e2d2a4dc3e070934dBehdad Esfahbod def __init__(self, options=None, log=None): 703f2d5982826530296fd7c8f9e2d2a4dc3e070934dBehdad Esfahbod 704f2d5982826530296fd7c8f9e2d2a4dc3e070934dBehdad Esfahbod if not log: 705f2d5982826530296fd7c8f9e2d2a4dc3e070934dBehdad Esfahbod log = Logger() 706f2d5982826530296fd7c8f9e2d2a4dc3e070934dBehdad Esfahbod if not options: 707f2d5982826530296fd7c8f9e2d2a4dc3e070934dBehdad Esfahbod options = Options() 708f2d5982826530296fd7c8f9e2d2a4dc3e070934dBehdad Esfahbod 709f2d5982826530296fd7c8f9e2d2a4dc3e070934dBehdad Esfahbod self.options = options 710f2d5982826530296fd7c8f9e2d2a4dc3e070934dBehdad Esfahbod self.log = log 71145d2f38aa51169f400062f2153a74277b9d4d04eBehdad Esfahbod 712f2d5982826530296fd7c8f9e2d2a4dc3e070934dBehdad Esfahbod def merge(self, fontfiles): 71345d2f38aa51169f400062f2153a74277b9d4d04eBehdad Esfahbod 71445d2f38aa51169f400062f2153a74277b9d4d04eBehdad Esfahbod mega = ttLib.TTFont() 71545d2f38aa51169f400062f2153a74277b9d4d04eBehdad Esfahbod 71645d2f38aa51169f400062f2153a74277b9d4d04eBehdad Esfahbod # 71745d2f38aa51169f400062f2153a74277b9d4d04eBehdad Esfahbod # Settle on a mega glyph order. 71845d2f38aa51169f400062f2153a74277b9d4d04eBehdad Esfahbod # 719f2d5982826530296fd7c8f9e2d2a4dc3e070934dBehdad Esfahbod fonts = [ttLib.TTFont(fontfile) for fontfile in fontfiles] 72045d2f38aa51169f400062f2153a74277b9d4d04eBehdad Esfahbod glyphOrders = [font.getGlyphOrder() for font in fonts] 72145d2f38aa51169f400062f2153a74277b9d4d04eBehdad Esfahbod megaGlyphOrder = self._mergeGlyphOrders(glyphOrders) 72245d2f38aa51169f400062f2153a74277b9d4d04eBehdad Esfahbod # Reload fonts and set new glyph names on them. 72345d2f38aa51169f400062f2153a74277b9d4d04eBehdad Esfahbod # TODO Is it necessary to reload font? I think it is. At least 72445d2f38aa51169f400062f2153a74277b9d4d04eBehdad Esfahbod # it's safer, in case tables were loaded to provide glyph names. 725f2d5982826530296fd7c8f9e2d2a4dc3e070934dBehdad Esfahbod fonts = [ttLib.TTFont(fontfile) for fontfile in fontfiles] 7263235a04ea938845d656a68c242591d37148ea353Behdad Esfahbod for font,glyphOrder in zip(fonts, glyphOrders): 7273235a04ea938845d656a68c242591d37148ea353Behdad Esfahbod font.setGlyphOrder(glyphOrder) 72845d2f38aa51169f400062f2153a74277b9d4d04eBehdad Esfahbod mega.setGlyphOrder(megaGlyphOrder) 72945d2f38aa51169f400062f2153a74277b9d4d04eBehdad Esfahbod 7302642934116cc0e3b873e37051cb57d3b29c0a9b4Behdad Esfahbod for font in fonts: 7312642934116cc0e3b873e37051cb57d3b29c0a9b4Behdad Esfahbod self._preMerge(font) 7322642934116cc0e3b873e37051cb57d3b29c0a9b4Behdad Esfahbod 7338fec68796d90d217a7e869215810b5179cc28c16Behdad Esfahbod self.duplicateGlyphsPerFont = [{} for f in fonts] 7348fec68796d90d217a7e869215810b5179cc28c16Behdad Esfahbod 735f63e80e3fc4ef5406109f1fbe2746e26927fdebfBehdad Esfahbod allTags = reduce(set.union, (list(font.keys()) for font in fonts), set()) 73645d2f38aa51169f400062f2153a74277b9d4d04eBehdad Esfahbod allTags.remove('GlyphOrder') 737d0903e3bb3f0c313532a46d99423ae6f9c327aa5Behdad Esfahbod allTags.remove('cmap') 738d0903e3bb3f0c313532a46d99423ae6f9c327aa5Behdad Esfahbod allTags.remove('GSUB') 739d0903e3bb3f0c313532a46d99423ae6f9c327aa5Behdad Esfahbod allTags = ['cmap', 'GSUB'] + list(allTags) 74045d2f38aa51169f400062f2153a74277b9d4d04eBehdad Esfahbod for tag in allTags: 741f2d5982826530296fd7c8f9e2d2a4dc3e070934dBehdad Esfahbod 7422642934116cc0e3b873e37051cb57d3b29c0a9b4Behdad Esfahbod tables = [font.get(tag, NotImplemented) for font in fonts] 7432772d8496e6979f6b7992d35233e2b973d7b86b5Behdad Esfahbod 7442772d8496e6979f6b7992d35233e2b973d7b86b5Behdad Esfahbod clazz = ttLib.getTableClass(tag) 74592fd5665772996966aaa0709462ebae5a72d4e19Behdad Esfahbod table = clazz(tag).merge(self, tables) 7462772d8496e6979f6b7992d35233e2b973d7b86b5Behdad Esfahbod # XXX Clean this up and use: table = mergeObjects(tables) 7472772d8496e6979f6b7992d35233e2b973d7b86b5Behdad Esfahbod 74892fd5665772996966aaa0709462ebae5a72d4e19Behdad Esfahbod if table is not NotImplemented and table is not False: 74965f19d8440f5fa5d94d1f04593f16cd9f21f176bBehdad Esfahbod mega[tag] = table 750b640f7435c3d29c57310b1a5f7d8e5b538a4c1e3Behdad Esfahbod self.log("Merged '%s'." % tag) 75165f19d8440f5fa5d94d1f04593f16cd9f21f176bBehdad Esfahbod else: 7529e6adb6bd6b92765f72d0a2acf871405832aa11eBehdad Esfahbod self.log("Dropped '%s'." % tag) 753b640f7435c3d29c57310b1a5f7d8e5b538a4c1e3Behdad Esfahbod self.log.lapse("merge '%s'" % tag) 75445d2f38aa51169f400062f2153a74277b9d4d04eBehdad Esfahbod 7558fec68796d90d217a7e869215810b5179cc28c16Behdad Esfahbod del self.duplicateGlyphsPerFont 7568fec68796d90d217a7e869215810b5179cc28c16Behdad Esfahbod 7572642934116cc0e3b873e37051cb57d3b29c0a9b4Behdad Esfahbod self._postMerge(mega) 7582642934116cc0e3b873e37051cb57d3b29c0a9b4Behdad Esfahbod 75945d2f38aa51169f400062f2153a74277b9d4d04eBehdad Esfahbod return mega 76045d2f38aa51169f400062f2153a74277b9d4d04eBehdad Esfahbod 76145d2f38aa51169f400062f2153a74277b9d4d04eBehdad Esfahbod def _mergeGlyphOrders(self, glyphOrders): 762c2e27fd88f54fcc081049dabce59889cdc87450eBehdad Esfahbod """Modifies passed-in glyphOrders to reflect new glyph names. 763c2e27fd88f54fcc081049dabce59889cdc87450eBehdad Esfahbod Returns glyphOrder for the merged font.""" 76445d2f38aa51169f400062f2153a74277b9d4d04eBehdad Esfahbod # Simply append font index to the glyph name for now. 765c2e27fd88f54fcc081049dabce59889cdc87450eBehdad Esfahbod # TODO Even this simplistic numbering can result in conflicts. 766c2e27fd88f54fcc081049dabce59889cdc87450eBehdad Esfahbod # But then again, we have to improve this soon anyway. 76745d2f38aa51169f400062f2153a74277b9d4d04eBehdad Esfahbod mega = [] 76845d2f38aa51169f400062f2153a74277b9d4d04eBehdad Esfahbod for n,glyphOrder in enumerate(glyphOrders): 76945d2f38aa51169f400062f2153a74277b9d4d04eBehdad Esfahbod for i,glyphName in enumerate(glyphOrder): 770f63e80e3fc4ef5406109f1fbe2746e26927fdebfBehdad Esfahbod glyphName += "#" + repr(n) 77145d2f38aa51169f400062f2153a74277b9d4d04eBehdad Esfahbod glyphOrder[i] = glyphName 77245d2f38aa51169f400062f2153a74277b9d4d04eBehdad Esfahbod mega.append(glyphName) 77345d2f38aa51169f400062f2153a74277b9d4d04eBehdad Esfahbod return mega 77445d2f38aa51169f400062f2153a74277b9d4d04eBehdad Esfahbod 7756baf26ea74247a0bed5f0b52d13bc3ef1c931204Behdad Esfahbod def mergeObjects(self, returnTable, logic, tables): 77692fd5665772996966aaa0709462ebae5a72d4e19Behdad Esfahbod # Right now we don't use self at all. Will use in the future 77792fd5665772996966aaa0709462ebae5a72d4e19Behdad Esfahbod # for options and logging. 77892fd5665772996966aaa0709462ebae5a72d4e19Behdad Esfahbod 77992fd5665772996966aaa0709462ebae5a72d4e19Behdad Esfahbod allKeys = set.union(set(), *(vars(table).keys() for table in tables if table is not NotImplemented)) 78047bee9cfbd47dc22895003cc94ab91f9075ca27fRoozbeh Pournader for key in allKeys: 781e219c6c76f116a38e0187b4388d99dd59eff30bcRoozbeh Pournader try: 7826baf26ea74247a0bed5f0b52d13bc3ef1c931204Behdad Esfahbod mergeLogic = logic[key] 783e219c6c76f116a38e0187b4388d99dd59eff30bcRoozbeh Pournader except KeyError: 7849e6adb6bd6b92765f72d0a2acf871405832aa11eBehdad Esfahbod try: 7856baf26ea74247a0bed5f0b52d13bc3ef1c931204Behdad Esfahbod mergeLogic = logic['*'] 7869e6adb6bd6b92765f72d0a2acf871405832aa11eBehdad Esfahbod except KeyError: 7876baf26ea74247a0bed5f0b52d13bc3ef1c931204Behdad Esfahbod raise Exception("Don't know how to merge key %s of class %s" % 7886baf26ea74247a0bed5f0b52d13bc3ef1c931204Behdad Esfahbod (key, returnTable.__class__.__name__)) 78992fd5665772996966aaa0709462ebae5a72d4e19Behdad Esfahbod if mergeLogic is NotImplemented: 790e219c6c76f116a38e0187b4388d99dd59eff30bcRoozbeh Pournader continue 79192fd5665772996966aaa0709462ebae5a72d4e19Behdad Esfahbod value = mergeLogic(getattr(table, key, NotImplemented) for table in tables) 79292fd5665772996966aaa0709462ebae5a72d4e19Behdad Esfahbod if value is not NotImplemented: 79392fd5665772996966aaa0709462ebae5a72d4e19Behdad Esfahbod setattr(returnTable, key, value) 79492fd5665772996966aaa0709462ebae5a72d4e19Behdad Esfahbod 79592fd5665772996966aaa0709462ebae5a72d4e19Behdad Esfahbod return returnTable 79647bee9cfbd47dc22895003cc94ab91f9075ca27fRoozbeh Pournader 7972642934116cc0e3b873e37051cb57d3b29c0a9b4Behdad Esfahbod def _preMerge(self, font): 7982642934116cc0e3b873e37051cb57d3b29c0a9b4Behdad Esfahbod 7998fec68796d90d217a7e869215810b5179cc28c16Behdad Esfahbod # Map indices to references 8008fec68796d90d217a7e869215810b5179cc28c16Behdad Esfahbod 8012642934116cc0e3b873e37051cb57d3b29c0a9b4Behdad Esfahbod GDEF = font.get('GDEF') 8022642934116cc0e3b873e37051cb57d3b29c0a9b4Behdad Esfahbod GSUB = font.get('GSUB') 8032642934116cc0e3b873e37051cb57d3b29c0a9b4Behdad Esfahbod GPOS = font.get('GPOS') 8042642934116cc0e3b873e37051cb57d3b29c0a9b4Behdad Esfahbod 8052642934116cc0e3b873e37051cb57d3b29c0a9b4Behdad Esfahbod for t in [GSUB, GPOS]: 806398770d51c3ae0e7c267ef2b315beae2b6aa2df8Behdad Esfahbod if not t: continue 807398770d51c3ae0e7c267ef2b315beae2b6aa2df8Behdad Esfahbod 8085080331251906861fbba4a9986efcd04978207beBehdad Esfahbod if t.table.LookupList: 809b76d6ff14a8283f7057847494c3c5c81ff236a6eBehdad Esfahbod lookupMap = {i:id(v) for i,v in enumerate(t.table.LookupList.Lookup)} 8105080331251906861fbba4a9986efcd04978207beBehdad Esfahbod t.table.LookupList.mapLookups(lookupMap) 8115080331251906861fbba4a9986efcd04978207beBehdad Esfahbod if t.table.FeatureList: 8125080331251906861fbba4a9986efcd04978207beBehdad Esfahbod # XXX Handle present FeatureList but absent LookupList 8135080331251906861fbba4a9986efcd04978207beBehdad Esfahbod t.table.FeatureList.mapLookups(lookupMap) 8142642934116cc0e3b873e37051cb57d3b29c0a9b4Behdad Esfahbod 815398770d51c3ae0e7c267ef2b315beae2b6aa2df8Behdad Esfahbod if t.table.FeatureList and t.table.ScriptList: 816b76d6ff14a8283f7057847494c3c5c81ff236a6eBehdad Esfahbod featureMap = {i:id(v) for i,v in enumerate(t.table.FeatureList.FeatureRecord)} 817398770d51c3ae0e7c267ef2b315beae2b6aa2df8Behdad Esfahbod t.table.ScriptList.mapFeatures(featureMap) 818398770d51c3ae0e7c267ef2b315beae2b6aa2df8Behdad Esfahbod 819398770d51c3ae0e7c267ef2b315beae2b6aa2df8Behdad Esfahbod # TODO GDEF/Lookup MarkFilteringSets 8202642934116cc0e3b873e37051cb57d3b29c0a9b4Behdad Esfahbod # TODO FeatureParams nameIDs 8212642934116cc0e3b873e37051cb57d3b29c0a9b4Behdad Esfahbod 8222642934116cc0e3b873e37051cb57d3b29c0a9b4Behdad Esfahbod def _postMerge(self, font): 823398770d51c3ae0e7c267ef2b315beae2b6aa2df8Behdad Esfahbod 8248fec68796d90d217a7e869215810b5179cc28c16Behdad Esfahbod # Map references back to indices 8258fec68796d90d217a7e869215810b5179cc28c16Behdad Esfahbod 826398770d51c3ae0e7c267ef2b315beae2b6aa2df8Behdad Esfahbod GDEF = font.get('GDEF') 827398770d51c3ae0e7c267ef2b315beae2b6aa2df8Behdad Esfahbod GSUB = font.get('GSUB') 828398770d51c3ae0e7c267ef2b315beae2b6aa2df8Behdad Esfahbod GPOS = font.get('GPOS') 829398770d51c3ae0e7c267ef2b315beae2b6aa2df8Behdad Esfahbod 830398770d51c3ae0e7c267ef2b315beae2b6aa2df8Behdad Esfahbod for t in [GSUB, GPOS]: 831398770d51c3ae0e7c267ef2b315beae2b6aa2df8Behdad Esfahbod if not t: continue 832398770d51c3ae0e7c267ef2b315beae2b6aa2df8Behdad Esfahbod 8335080331251906861fbba4a9986efcd04978207beBehdad Esfahbod if t.table.LookupList: 834b76d6ff14a8283f7057847494c3c5c81ff236a6eBehdad Esfahbod lookupMap = {id(v):i for i,v in enumerate(t.table.LookupList.Lookup)} 8355080331251906861fbba4a9986efcd04978207beBehdad Esfahbod t.table.LookupList.mapLookups(lookupMap) 8365080331251906861fbba4a9986efcd04978207beBehdad Esfahbod if t.table.FeatureList: 8375080331251906861fbba4a9986efcd04978207beBehdad Esfahbod # XXX Handle present FeatureList but absent LookupList 8385080331251906861fbba4a9986efcd04978207beBehdad Esfahbod t.table.FeatureList.mapLookups(lookupMap) 839398770d51c3ae0e7c267ef2b315beae2b6aa2df8Behdad Esfahbod 840398770d51c3ae0e7c267ef2b315beae2b6aa2df8Behdad Esfahbod if t.table.FeatureList and t.table.ScriptList: 8415080331251906861fbba4a9986efcd04978207beBehdad Esfahbod # XXX Handle present ScriptList but absent FeatureList 842b76d6ff14a8283f7057847494c3c5c81ff236a6eBehdad Esfahbod featureMap = {id(v):i for i,v in enumerate(t.table.FeatureList.FeatureRecord)} 843398770d51c3ae0e7c267ef2b315beae2b6aa2df8Behdad Esfahbod t.table.ScriptList.mapFeatures(featureMap) 844398770d51c3ae0e7c267ef2b315beae2b6aa2df8Behdad Esfahbod 845398770d51c3ae0e7c267ef2b315beae2b6aa2df8Behdad Esfahbod # TODO GDEF/Lookup MarkFilteringSets 846398770d51c3ae0e7c267ef2b315beae2b6aa2df8Behdad Esfahbod # TODO FeatureParams nameIDs 8472642934116cc0e3b873e37051cb57d3b29c0a9b4Behdad Esfahbod 848f2d5982826530296fd7c8f9e2d2a4dc3e070934dBehdad Esfahbod 849f2d5982826530296fd7c8f9e2d2a4dc3e070934dBehdad Esfahbodclass Logger(object): 850f2d5982826530296fd7c8f9e2d2a4dc3e070934dBehdad Esfahbod 851f2d5982826530296fd7c8f9e2d2a4dc3e070934dBehdad Esfahbod def __init__(self, verbose=False, xml=False, timing=False): 852f2d5982826530296fd7c8f9e2d2a4dc3e070934dBehdad Esfahbod self.verbose = verbose 853f2d5982826530296fd7c8f9e2d2a4dc3e070934dBehdad Esfahbod self.xml = xml 854f2d5982826530296fd7c8f9e2d2a4dc3e070934dBehdad Esfahbod self.timing = timing 855f2d5982826530296fd7c8f9e2d2a4dc3e070934dBehdad Esfahbod self.last_time = self.start_time = time.time() 856f2d5982826530296fd7c8f9e2d2a4dc3e070934dBehdad Esfahbod 857f2d5982826530296fd7c8f9e2d2a4dc3e070934dBehdad Esfahbod def parse_opts(self, argv): 858f2d5982826530296fd7c8f9e2d2a4dc3e070934dBehdad Esfahbod argv = argv[:] 859f2d5982826530296fd7c8f9e2d2a4dc3e070934dBehdad Esfahbod for v in ['verbose', 'xml', 'timing']: 860f2d5982826530296fd7c8f9e2d2a4dc3e070934dBehdad Esfahbod if "--"+v in argv: 861f2d5982826530296fd7c8f9e2d2a4dc3e070934dBehdad Esfahbod setattr(self, v, True) 862f2d5982826530296fd7c8f9e2d2a4dc3e070934dBehdad Esfahbod argv.remove("--"+v) 863f2d5982826530296fd7c8f9e2d2a4dc3e070934dBehdad Esfahbod return argv 864f2d5982826530296fd7c8f9e2d2a4dc3e070934dBehdad Esfahbod 865f2d5982826530296fd7c8f9e2d2a4dc3e070934dBehdad Esfahbod def __call__(self, *things): 866f2d5982826530296fd7c8f9e2d2a4dc3e070934dBehdad Esfahbod if not self.verbose: 867f2d5982826530296fd7c8f9e2d2a4dc3e070934dBehdad Esfahbod return 868f63e80e3fc4ef5406109f1fbe2746e26927fdebfBehdad Esfahbod print(' '.join(str(x) for x in things)) 869f2d5982826530296fd7c8f9e2d2a4dc3e070934dBehdad Esfahbod 870f2d5982826530296fd7c8f9e2d2a4dc3e070934dBehdad Esfahbod def lapse(self, *things): 871f2d5982826530296fd7c8f9e2d2a4dc3e070934dBehdad Esfahbod if not self.timing: 872f2d5982826530296fd7c8f9e2d2a4dc3e070934dBehdad Esfahbod return 873f2d5982826530296fd7c8f9e2d2a4dc3e070934dBehdad Esfahbod new_time = time.time() 874f63e80e3fc4ef5406109f1fbe2746e26927fdebfBehdad Esfahbod print("Took %0.3fs to %s" %(new_time - self.last_time, 875f63e80e3fc4ef5406109f1fbe2746e26927fdebfBehdad Esfahbod ' '.join(str(x) for x in things))) 876f2d5982826530296fd7c8f9e2d2a4dc3e070934dBehdad Esfahbod self.last_time = new_time 877f2d5982826530296fd7c8f9e2d2a4dc3e070934dBehdad Esfahbod 878f2d5982826530296fd7c8f9e2d2a4dc3e070934dBehdad Esfahbod def font(self, font, file=sys.stdout): 879f2d5982826530296fd7c8f9e2d2a4dc3e070934dBehdad Esfahbod if not self.xml: 880f2d5982826530296fd7c8f9e2d2a4dc3e070934dBehdad Esfahbod return 881f2d5982826530296fd7c8f9e2d2a4dc3e070934dBehdad Esfahbod from fontTools.misc import xmlWriter 882f2d5982826530296fd7c8f9e2d2a4dc3e070934dBehdad Esfahbod writer = xmlWriter.XMLWriter(file) 883f2d5982826530296fd7c8f9e2d2a4dc3e070934dBehdad Esfahbod font.disassembleInstructions = False # Work around ttLib bug 884f2d5982826530296fd7c8f9e2d2a4dc3e070934dBehdad Esfahbod for tag in font.keys(): 885f2d5982826530296fd7c8f9e2d2a4dc3e070934dBehdad Esfahbod writer.begintag(tag) 886f2d5982826530296fd7c8f9e2d2a4dc3e070934dBehdad Esfahbod writer.newline() 887f2d5982826530296fd7c8f9e2d2a4dc3e070934dBehdad Esfahbod font[tag].toXML(writer, font) 888f2d5982826530296fd7c8f9e2d2a4dc3e070934dBehdad Esfahbod writer.endtag(tag) 889f2d5982826530296fd7c8f9e2d2a4dc3e070934dBehdad Esfahbod writer.newline() 890f2d5982826530296fd7c8f9e2d2a4dc3e070934dBehdad Esfahbod 891f2d5982826530296fd7c8f9e2d2a4dc3e070934dBehdad Esfahbod 892f2d5982826530296fd7c8f9e2d2a4dc3e070934dBehdad Esfahbod__all__ = [ 893f2d5982826530296fd7c8f9e2d2a4dc3e070934dBehdad Esfahbod 'Options', 894f2d5982826530296fd7c8f9e2d2a4dc3e070934dBehdad Esfahbod 'Merger', 895f2d5982826530296fd7c8f9e2d2a4dc3e070934dBehdad Esfahbod 'Logger', 896f2d5982826530296fd7c8f9e2d2a4dc3e070934dBehdad Esfahbod 'main' 897f2d5982826530296fd7c8f9e2d2a4dc3e070934dBehdad Esfahbod] 898f2d5982826530296fd7c8f9e2d2a4dc3e070934dBehdad Esfahbod 89945d2f38aa51169f400062f2153a74277b9d4d04eBehdad Esfahboddef main(args): 900f2d5982826530296fd7c8f9e2d2a4dc3e070934dBehdad Esfahbod 901f2d5982826530296fd7c8f9e2d2a4dc3e070934dBehdad Esfahbod log = Logger() 902f2d5982826530296fd7c8f9e2d2a4dc3e070934dBehdad Esfahbod args = log.parse_opts(args) 903f2d5982826530296fd7c8f9e2d2a4dc3e070934dBehdad Esfahbod 904f2d5982826530296fd7c8f9e2d2a4dc3e070934dBehdad Esfahbod options = Options() 905f2d5982826530296fd7c8f9e2d2a4dc3e070934dBehdad Esfahbod args = options.parse_opts(args) 906f2d5982826530296fd7c8f9e2d2a4dc3e070934dBehdad Esfahbod 90745d2f38aa51169f400062f2153a74277b9d4d04eBehdad Esfahbod if len(args) < 1: 908f63e80e3fc4ef5406109f1fbe2746e26927fdebfBehdad Esfahbod print("usage: pyftmerge font...", file=sys.stderr) 90945d2f38aa51169f400062f2153a74277b9d4d04eBehdad Esfahbod sys.exit(1) 910f2d5982826530296fd7c8f9e2d2a4dc3e070934dBehdad Esfahbod 911f2d5982826530296fd7c8f9e2d2a4dc3e070934dBehdad Esfahbod merger = Merger(options=options, log=log) 912f2d5982826530296fd7c8f9e2d2a4dc3e070934dBehdad Esfahbod font = merger.merge(args) 91345d2f38aa51169f400062f2153a74277b9d4d04eBehdad Esfahbod outfile = 'merged.ttf' 91445d2f38aa51169f400062f2153a74277b9d4d04eBehdad Esfahbod font.save(outfile) 915b640f7435c3d29c57310b1a5f7d8e5b538a4c1e3Behdad Esfahbod log.lapse("compile and save font") 916b640f7435c3d29c57310b1a5f7d8e5b538a4c1e3Behdad Esfahbod 917b640f7435c3d29c57310b1a5f7d8e5b538a4c1e3Behdad Esfahbod log.last_time = log.start_time 918b640f7435c3d29c57310b1a5f7d8e5b538a4c1e3Behdad Esfahbod log.lapse("make one with everything(TOTAL TIME)") 91945d2f38aa51169f400062f2153a74277b9d4d04eBehdad Esfahbod 92045d2f38aa51169f400062f2153a74277b9d4d04eBehdad Esfahbodif __name__ == "__main__": 92145d2f38aa51169f400062f2153a74277b9d4d04eBehdad Esfahbod main(sys.argv[1:]) 922