xref: /aosp_15_r20/external/fonttools/Lib/fontTools/merge/base.py (revision e1fe3e4ad2793916b15cccdc4a7da52a7e1dd0e9)
1# Copyright 2013 Google, Inc. All Rights Reserved.
2#
3# Google Author(s): Behdad Esfahbod, Roozbeh Pournader
4
5from fontTools.ttLib.tables.DefaultTable import DefaultTable
6import logging
7
8
9log = logging.getLogger("fontTools.merge")
10
11
12def add_method(*clazzes, **kwargs):
13    """Returns a decorator function that adds a new method to one or
14    more classes."""
15    allowDefault = kwargs.get("allowDefaultTable", False)
16
17    def wrapper(method):
18        done = []
19        for clazz in clazzes:
20            if clazz in done:
21                continue  # Support multiple names of a clazz
22            done.append(clazz)
23            assert allowDefault or clazz != DefaultTable, "Oops, table class not found."
24            assert (
25                method.__name__ not in clazz.__dict__
26            ), "Oops, class '%s' has method '%s'." % (clazz.__name__, method.__name__)
27            setattr(clazz, method.__name__, method)
28        return None
29
30    return wrapper
31
32
33def mergeObjects(lst):
34    lst = [item for item in lst if item is not NotImplemented]
35    if not lst:
36        return NotImplemented
37    lst = [item for item in lst if item is not None]
38    if not lst:
39        return None
40
41    clazz = lst[0].__class__
42    assert all(type(item) == clazz for item in lst), lst
43
44    logic = clazz.mergeMap
45    returnTable = clazz()
46    returnDict = {}
47
48    allKeys = set.union(set(), *(vars(table).keys() for table in lst))
49    for key in allKeys:
50        try:
51            mergeLogic = logic[key]
52        except KeyError:
53            try:
54                mergeLogic = logic["*"]
55            except KeyError:
56                raise Exception(
57                    "Don't know how to merge key %s of class %s" % (key, clazz.__name__)
58                )
59        if mergeLogic is NotImplemented:
60            continue
61        value = mergeLogic(getattr(table, key, NotImplemented) for table in lst)
62        if value is not NotImplemented:
63            returnDict[key] = value
64
65    returnTable.__dict__ = returnDict
66
67    return returnTable
68
69
70@add_method(DefaultTable, allowDefaultTable=True)
71def merge(self, m, tables):
72    if not hasattr(self, "mergeMap"):
73        log.info("Don't know how to merge '%s'.", self.tableTag)
74        return NotImplemented
75
76    logic = self.mergeMap
77
78    if isinstance(logic, dict):
79        return m.mergeObjects(self, self.mergeMap, tables)
80    else:
81        return logic(tables)
82