1*e1fe3e4aSElliott Hughesfrom fontTools import ttLib 2*e1fe3e4aSElliott Hughesfrom fontTools.ttLib.tables import otTables as ot 3*e1fe3e4aSElliott Hughes 4*e1fe3e4aSElliott Hughes# VariationStore 5*e1fe3e4aSElliott Hughes 6*e1fe3e4aSElliott Hughes 7*e1fe3e4aSElliott Hughesdef buildVarRegionAxis(axisSupport): 8*e1fe3e4aSElliott Hughes self = ot.VarRegionAxis() 9*e1fe3e4aSElliott Hughes self.StartCoord, self.PeakCoord, self.EndCoord = [float(v) for v in axisSupport] 10*e1fe3e4aSElliott Hughes return self 11*e1fe3e4aSElliott Hughes 12*e1fe3e4aSElliott Hughes 13*e1fe3e4aSElliott Hughesdef buildVarRegion(support, axisTags): 14*e1fe3e4aSElliott Hughes assert all(tag in axisTags for tag in support.keys()), ( 15*e1fe3e4aSElliott Hughes "Unknown axis tag found.", 16*e1fe3e4aSElliott Hughes support, 17*e1fe3e4aSElliott Hughes axisTags, 18*e1fe3e4aSElliott Hughes ) 19*e1fe3e4aSElliott Hughes self = ot.VarRegion() 20*e1fe3e4aSElliott Hughes self.VarRegionAxis = [] 21*e1fe3e4aSElliott Hughes for tag in axisTags: 22*e1fe3e4aSElliott Hughes self.VarRegionAxis.append(buildVarRegionAxis(support.get(tag, (0, 0, 0)))) 23*e1fe3e4aSElliott Hughes return self 24*e1fe3e4aSElliott Hughes 25*e1fe3e4aSElliott Hughes 26*e1fe3e4aSElliott Hughesdef buildVarRegionList(supports, axisTags): 27*e1fe3e4aSElliott Hughes self = ot.VarRegionList() 28*e1fe3e4aSElliott Hughes self.RegionAxisCount = len(axisTags) 29*e1fe3e4aSElliott Hughes self.Region = [] 30*e1fe3e4aSElliott Hughes for support in supports: 31*e1fe3e4aSElliott Hughes self.Region.append(buildVarRegion(support, axisTags)) 32*e1fe3e4aSElliott Hughes self.RegionCount = len(self.Region) 33*e1fe3e4aSElliott Hughes return self 34*e1fe3e4aSElliott Hughes 35*e1fe3e4aSElliott Hughes 36*e1fe3e4aSElliott Hughesdef _reorderItem(lst, mapping): 37*e1fe3e4aSElliott Hughes return [lst[i] for i in mapping] 38*e1fe3e4aSElliott Hughes 39*e1fe3e4aSElliott Hughes 40*e1fe3e4aSElliott Hughesdef VarData_calculateNumShorts(self, optimize=False): 41*e1fe3e4aSElliott Hughes count = self.VarRegionCount 42*e1fe3e4aSElliott Hughes items = self.Item 43*e1fe3e4aSElliott Hughes bit_lengths = [0] * count 44*e1fe3e4aSElliott Hughes for item in items: 45*e1fe3e4aSElliott Hughes # The "+ (i < -1)" magic is to handle two's-compliment. 46*e1fe3e4aSElliott Hughes # That is, we want to get back 7 for -128, whereas 47*e1fe3e4aSElliott Hughes # bit_length() returns 8. Similarly for -65536. 48*e1fe3e4aSElliott Hughes # The reason "i < -1" is used instead of "i < 0" is that 49*e1fe3e4aSElliott Hughes # the latter would make it return 0 for "-1" instead of 1. 50*e1fe3e4aSElliott Hughes bl = [(i + (i < -1)).bit_length() for i in item] 51*e1fe3e4aSElliott Hughes bit_lengths = [max(*pair) for pair in zip(bl, bit_lengths)] 52*e1fe3e4aSElliott Hughes # The addition of 8, instead of seven, is to account for the sign bit. 53*e1fe3e4aSElliott Hughes # This "((b + 8) >> 3) if b else 0" when combined with the above 54*e1fe3e4aSElliott Hughes # "(i + (i < -1)).bit_length()" is a faster way to compute byte-lengths 55*e1fe3e4aSElliott Hughes # conforming to: 56*e1fe3e4aSElliott Hughes # 57*e1fe3e4aSElliott Hughes # byte_length = (0 if i == 0 else 58*e1fe3e4aSElliott Hughes # 1 if -128 <= i < 128 else 59*e1fe3e4aSElliott Hughes # 2 if -65536 <= i < 65536 else 60*e1fe3e4aSElliott Hughes # ...) 61*e1fe3e4aSElliott Hughes byte_lengths = [((b + 8) >> 3) if b else 0 for b in bit_lengths] 62*e1fe3e4aSElliott Hughes 63*e1fe3e4aSElliott Hughes # https://github.com/fonttools/fonttools/issues/2279 64*e1fe3e4aSElliott Hughes longWords = any(b > 2 for b in byte_lengths) 65*e1fe3e4aSElliott Hughes 66*e1fe3e4aSElliott Hughes if optimize: 67*e1fe3e4aSElliott Hughes # Reorder columns such that wider columns come before narrower columns 68*e1fe3e4aSElliott Hughes mapping = [] 69*e1fe3e4aSElliott Hughes mapping.extend(i for i, b in enumerate(byte_lengths) if b > 2) 70*e1fe3e4aSElliott Hughes mapping.extend(i for i, b in enumerate(byte_lengths) if b == 2) 71*e1fe3e4aSElliott Hughes mapping.extend(i for i, b in enumerate(byte_lengths) if b == 1) 72*e1fe3e4aSElliott Hughes 73*e1fe3e4aSElliott Hughes byte_lengths = _reorderItem(byte_lengths, mapping) 74*e1fe3e4aSElliott Hughes self.VarRegionIndex = _reorderItem(self.VarRegionIndex, mapping) 75*e1fe3e4aSElliott Hughes self.VarRegionCount = len(self.VarRegionIndex) 76*e1fe3e4aSElliott Hughes for i in range(len(items)): 77*e1fe3e4aSElliott Hughes items[i] = _reorderItem(items[i], mapping) 78*e1fe3e4aSElliott Hughes 79*e1fe3e4aSElliott Hughes if longWords: 80*e1fe3e4aSElliott Hughes self.NumShorts = ( 81*e1fe3e4aSElliott Hughes max((i for i, b in enumerate(byte_lengths) if b > 2), default=-1) + 1 82*e1fe3e4aSElliott Hughes ) 83*e1fe3e4aSElliott Hughes self.NumShorts |= 0x8000 84*e1fe3e4aSElliott Hughes else: 85*e1fe3e4aSElliott Hughes self.NumShorts = ( 86*e1fe3e4aSElliott Hughes max((i for i, b in enumerate(byte_lengths) if b > 1), default=-1) + 1 87*e1fe3e4aSElliott Hughes ) 88*e1fe3e4aSElliott Hughes 89*e1fe3e4aSElliott Hughes self.VarRegionCount = len(self.VarRegionIndex) 90*e1fe3e4aSElliott Hughes return self 91*e1fe3e4aSElliott Hughes 92*e1fe3e4aSElliott Hughes 93*e1fe3e4aSElliott Hughesot.VarData.calculateNumShorts = VarData_calculateNumShorts 94*e1fe3e4aSElliott Hughes 95*e1fe3e4aSElliott Hughes 96*e1fe3e4aSElliott Hughesdef VarData_CalculateNumShorts(self, optimize=True): 97*e1fe3e4aSElliott Hughes """Deprecated name for VarData_calculateNumShorts() which 98*e1fe3e4aSElliott Hughes defaults to optimize=True. Use varData.calculateNumShorts() 99*e1fe3e4aSElliott Hughes or varData.optimize().""" 100*e1fe3e4aSElliott Hughes return VarData_calculateNumShorts(self, optimize=optimize) 101*e1fe3e4aSElliott Hughes 102*e1fe3e4aSElliott Hughes 103*e1fe3e4aSElliott Hughesdef VarData_optimize(self): 104*e1fe3e4aSElliott Hughes return VarData_calculateNumShorts(self, optimize=True) 105*e1fe3e4aSElliott Hughes 106*e1fe3e4aSElliott Hughes 107*e1fe3e4aSElliott Hughesot.VarData.optimize = VarData_optimize 108*e1fe3e4aSElliott Hughes 109*e1fe3e4aSElliott Hughes 110*e1fe3e4aSElliott Hughesdef buildVarData(varRegionIndices, items, optimize=True): 111*e1fe3e4aSElliott Hughes self = ot.VarData() 112*e1fe3e4aSElliott Hughes self.VarRegionIndex = list(varRegionIndices) 113*e1fe3e4aSElliott Hughes regionCount = self.VarRegionCount = len(self.VarRegionIndex) 114*e1fe3e4aSElliott Hughes records = self.Item = [] 115*e1fe3e4aSElliott Hughes if items: 116*e1fe3e4aSElliott Hughes for item in items: 117*e1fe3e4aSElliott Hughes assert len(item) == regionCount 118*e1fe3e4aSElliott Hughes records.append(list(item)) 119*e1fe3e4aSElliott Hughes self.ItemCount = len(self.Item) 120*e1fe3e4aSElliott Hughes self.calculateNumShorts(optimize=optimize) 121*e1fe3e4aSElliott Hughes return self 122*e1fe3e4aSElliott Hughes 123*e1fe3e4aSElliott Hughes 124*e1fe3e4aSElliott Hughesdef buildVarStore(varRegionList, varDataList): 125*e1fe3e4aSElliott Hughes self = ot.VarStore() 126*e1fe3e4aSElliott Hughes self.Format = 1 127*e1fe3e4aSElliott Hughes self.VarRegionList = varRegionList 128*e1fe3e4aSElliott Hughes self.VarData = list(varDataList) 129*e1fe3e4aSElliott Hughes self.VarDataCount = len(self.VarData) 130*e1fe3e4aSElliott Hughes return self 131*e1fe3e4aSElliott Hughes 132*e1fe3e4aSElliott Hughes 133*e1fe3e4aSElliott Hughes# Variation helpers 134*e1fe3e4aSElliott Hughes 135*e1fe3e4aSElliott Hughes 136*e1fe3e4aSElliott Hughesdef buildVarIdxMap(varIdxes, glyphOrder): 137*e1fe3e4aSElliott Hughes self = ot.VarIdxMap() 138*e1fe3e4aSElliott Hughes self.mapping = {g: v for g, v in zip(glyphOrder, varIdxes)} 139*e1fe3e4aSElliott Hughes return self 140*e1fe3e4aSElliott Hughes 141*e1fe3e4aSElliott Hughes 142*e1fe3e4aSElliott Hughesdef buildDeltaSetIndexMap(varIdxes): 143*e1fe3e4aSElliott Hughes mapping = list(varIdxes) 144*e1fe3e4aSElliott Hughes if all(i == v for i, v in enumerate(mapping)): 145*e1fe3e4aSElliott Hughes return None 146*e1fe3e4aSElliott Hughes self = ot.DeltaSetIndexMap() 147*e1fe3e4aSElliott Hughes self.mapping = mapping 148*e1fe3e4aSElliott Hughes self.Format = 1 if len(mapping) > 0xFFFF else 0 149*e1fe3e4aSElliott Hughes return self 150*e1fe3e4aSElliott Hughes 151*e1fe3e4aSElliott Hughes 152*e1fe3e4aSElliott Hughesdef buildVarDevTable(varIdx): 153*e1fe3e4aSElliott Hughes self = ot.Device() 154*e1fe3e4aSElliott Hughes self.DeltaFormat = 0x8000 155*e1fe3e4aSElliott Hughes self.StartSize = varIdx >> 16 156*e1fe3e4aSElliott Hughes self.EndSize = varIdx & 0xFFFF 157*e1fe3e4aSElliott Hughes return self 158