1# Copyright 2013 Google, Inc. All Rights Reserved. 2# 3# Google Author(s): Behdad Esfahbod, Roozbeh Pournader 4 5from fontTools.misc.timeTools import timestampNow 6from fontTools.ttLib.tables.DefaultTable import DefaultTable 7from functools import reduce 8import operator 9import logging 10 11 12log = logging.getLogger("fontTools.merge") 13 14 15# General utility functions for merging values from different fonts 16 17def equal(lst): 18 lst = list(lst) 19 t = iter(lst) 20 first = next(t) 21 assert all(item == first for item in t), "Expected all items to be equal: %s" % lst 22 return first 23 24def first(lst): 25 return next(iter(lst)) 26 27def recalculate(lst): 28 return NotImplemented 29 30def current_time(lst): 31 return timestampNow() 32 33def bitwise_and(lst): 34 return reduce(operator.and_, lst) 35 36def bitwise_or(lst): 37 return reduce(operator.or_, lst) 38 39def avg_int(lst): 40 lst = list(lst) 41 return sum(lst) // len(lst) 42 43def onlyExisting(func): 44 """Returns a filter func that when called with a list, 45 only calls func on the non-NotImplemented items of the list, 46 and only so if there's at least one item remaining. 47 Otherwise returns NotImplemented.""" 48 49 def wrapper(lst): 50 items = [item for item in lst if item is not NotImplemented] 51 return func(items) if items else NotImplemented 52 53 return wrapper 54 55def sumLists(lst): 56 l = [] 57 for item in lst: 58 l.extend(item) 59 return l 60 61def sumDicts(lst): 62 d = {} 63 for item in lst: 64 d.update(item) 65 return d 66 67def mergeBits(bitmap): 68 69 def wrapper(lst): 70 lst = list(lst) 71 returnValue = 0 72 for bitNumber in range(bitmap['size']): 73 try: 74 mergeLogic = bitmap[bitNumber] 75 except KeyError: 76 try: 77 mergeLogic = bitmap['*'] 78 except KeyError: 79 raise Exception("Don't know how to merge bit %s" % bitNumber) 80 shiftedBit = 1 << bitNumber 81 mergedValue = mergeLogic(bool(item & shiftedBit) for item in lst) 82 returnValue |= mergedValue << bitNumber 83 return returnValue 84 85 return wrapper 86 87 88class AttendanceRecordingIdentityDict(object): 89 """A dictionary-like object that records indices of items actually accessed 90 from a list.""" 91 92 def __init__(self, lst): 93 self.l = lst 94 self.d = {id(v):i for i,v in enumerate(lst)} 95 self.s = set() 96 97 def __getitem__(self, v): 98 self.s.add(self.d[id(v)]) 99 return v 100 101class GregariousIdentityDict(object): 102 """A dictionary-like object that welcomes guests without reservations and 103 adds them to the end of the guest list.""" 104 105 def __init__(self, lst): 106 self.l = lst 107 self.s = set(id(v) for v in lst) 108 109 def __getitem__(self, v): 110 if id(v) not in self.s: 111 self.s.add(id(v)) 112 self.l.append(v) 113 return v 114 115class NonhashableDict(object): 116 """A dictionary-like object mapping objects to values.""" 117 118 def __init__(self, keys, values=None): 119 if values is None: 120 self.d = {id(v):i for i,v in enumerate(keys)} 121 else: 122 self.d = {id(k):v for k,v in zip(keys, values)} 123 124 def __getitem__(self, k): 125 return self.d[id(k)] 126 127 def __setitem__(self, k, v): 128 self.d[id(k)] = v 129 130 def __delitem__(self, k): 131 del self.d[id(k)] 132