| # Copyright 2013 Google, Inc. All Rights Reserved. |
| # |
| # Google Author(s): Behdad Esfahbod, Roozbeh Pournader |
| |
| from fontTools.ttLib.tables.DefaultTable import DefaultTable |
| import logging |
| |
| |
| log = logging.getLogger("fontTools.merge") |
| |
| |
| def add_method(*clazzes, **kwargs): |
| """Returns a decorator function that adds a new method to one or |
| more classes.""" |
| allowDefault = kwargs.get("allowDefaultTable", False) |
| |
| def wrapper(method): |
| done = [] |
| for clazz in clazzes: |
| if clazz in done: |
| continue # Support multiple names of a clazz |
| done.append(clazz) |
| assert allowDefault or clazz != DefaultTable, "Oops, table class not found." |
| assert ( |
| method.__name__ not in clazz.__dict__ |
| ), "Oops, class '%s' has method '%s'." % (clazz.__name__, method.__name__) |
| setattr(clazz, method.__name__, method) |
| return None |
| |
| return wrapper |
| |
| |
| def mergeObjects(lst): |
| lst = [item for item in lst if item is not NotImplemented] |
| if not lst: |
| return NotImplemented |
| lst = [item for item in lst if item is not None] |
| if not lst: |
| return None |
| |
| clazz = lst[0].__class__ |
| assert all(type(item) == clazz for item in lst), lst |
| |
| logic = clazz.mergeMap |
| returnTable = clazz() |
| returnDict = {} |
| |
| allKeys = set.union(set(), *(vars(table).keys() for table in lst)) |
| for key in allKeys: |
| try: |
| mergeLogic = logic[key] |
| except KeyError: |
| try: |
| mergeLogic = logic["*"] |
| except KeyError: |
| raise Exception( |
| "Don't know how to merge key %s of class %s" % (key, clazz.__name__) |
| ) |
| if mergeLogic is NotImplemented: |
| continue |
| value = mergeLogic(getattr(table, key, NotImplemented) for table in lst) |
| if value is not NotImplemented: |
| returnDict[key] = value |
| |
| returnTable.__dict__ = returnDict |
| |
| return returnTable |
| |
| |
| @add_method(DefaultTable, allowDefaultTable=True) |
| def merge(self, m, tables): |
| if not hasattr(self, "mergeMap"): |
| log.info("Don't know how to merge '%s'.", self.tableTag) |
| return NotImplemented |
| |
| logic = self.mergeMap |
| |
| if isinstance(logic, dict): |
| return m.mergeObjects(self, self.mergeMap, tables) |
| else: |
| return logic(tables) |