| # Copyright 2013 Google, Inc. All Rights Reserved. |
| # |
| # Google Author(s): Behdad Esfahbod, Roozbeh Pournader |
| |
| from fontTools.misc.timeTools import timestampNow |
| from fontTools.ttLib.tables.DefaultTable import DefaultTable |
| from functools import reduce |
| import operator |
| import logging |
| |
| |
| log = logging.getLogger("fontTools.merge") |
| |
| |
| # General utility functions for merging values from different fonts |
| |
| |
| def equal(lst): |
| lst = list(lst) |
| t = iter(lst) |
| first = next(t) |
| assert all(item == first for item in t), "Expected all items to be equal: %s" % lst |
| return first |
| |
| |
| def first(lst): |
| return next(iter(lst)) |
| |
| |
| def recalculate(lst): |
| return NotImplemented |
| |
| |
| def current_time(lst): |
| return timestampNow() |
| |
| |
| def bitwise_and(lst): |
| return reduce(operator.and_, lst) |
| |
| |
| def bitwise_or(lst): |
| return reduce(operator.or_, lst) |
| |
| |
| def avg_int(lst): |
| lst = list(lst) |
| return sum(lst) // len(lst) |
| |
| |
| def onlyExisting(func): |
| """Returns a filter func that when called with a list, |
| only calls func on the non-NotImplemented items of the list, |
| and only so if there's at least one item remaining. |
| Otherwise returns NotImplemented.""" |
| |
| def wrapper(lst): |
| items = [item for item in lst if item is not NotImplemented] |
| return func(items) if items else NotImplemented |
| |
| return wrapper |
| |
| |
| def sumLists(lst): |
| l = [] |
| for item in lst: |
| l.extend(item) |
| return l |
| |
| |
| def sumDicts(lst): |
| d = {} |
| for item in lst: |
| d.update(item) |
| return d |
| |
| |
| def mergeBits(bitmap): |
| def wrapper(lst): |
| lst = list(lst) |
| returnValue = 0 |
| for bitNumber in range(bitmap["size"]): |
| try: |
| mergeLogic = bitmap[bitNumber] |
| except KeyError: |
| try: |
| mergeLogic = bitmap["*"] |
| except KeyError: |
| raise Exception("Don't know how to merge bit %s" % bitNumber) |
| shiftedBit = 1 << bitNumber |
| mergedValue = mergeLogic(bool(item & shiftedBit) for item in lst) |
| returnValue |= mergedValue << bitNumber |
| return returnValue |
| |
| return wrapper |
| |
| |
| class AttendanceRecordingIdentityDict(object): |
| """A dictionary-like object that records indices of items actually accessed |
| from a list.""" |
| |
| def __init__(self, lst): |
| self.l = lst |
| self.d = {id(v): i for i, v in enumerate(lst)} |
| self.s = set() |
| |
| def __getitem__(self, v): |
| self.s.add(self.d[id(v)]) |
| return v |
| |
| |
| class GregariousIdentityDict(object): |
| """A dictionary-like object that welcomes guests without reservations and |
| adds them to the end of the guest list.""" |
| |
| def __init__(self, lst): |
| self.l = lst |
| self.s = set(id(v) for v in lst) |
| |
| def __getitem__(self, v): |
| if id(v) not in self.s: |
| self.s.add(id(v)) |
| self.l.append(v) |
| return v |
| |
| |
| class NonhashableDict(object): |
| """A dictionary-like object mapping objects to values.""" |
| |
| def __init__(self, keys, values=None): |
| if values is None: |
| self.d = {id(v): i for i, v in enumerate(keys)} |
| else: |
| self.d = {id(k): v for k, v in zip(keys, values)} |
| |
| def __getitem__(self, k): |
| return self.d[id(k)] |
| |
| def __setitem__(self, k, v): |
| self.d[id(k)] = v |
| |
| def __delitem__(self, k): |
| del self.d[id(k)] |