diff options
Diffstat (limited to 'Lib/fontTools/varLib/builder.py')
-rw-r--r-- | Lib/fontTools/varLib/builder.py | 71 |
1 files changed, 47 insertions, 24 deletions
diff --git a/Lib/fontTools/varLib/builder.py b/Lib/fontTools/varLib/builder.py index 152336b0..60d7172e 100644 --- a/Lib/fontTools/varLib/builder.py +++ b/Lib/fontTools/varLib/builder.py @@ -26,39 +26,54 @@ def buildVarRegionList(supports, axisTags): return self -def _reorderItem(lst, narrows, zeroes): - out = [] - count = len(lst) - for i in range(count): - if i not in narrows: - out.append(lst[i]) - for i in range(count): - if i in narrows and i not in zeroes: - out.append(lst[i]) - return out +def _reorderItem(lst, mapping): + return [lst[i] for i in mapping] def VarData_calculateNumShorts(self, optimize=False): count = self.VarRegionCount items = self.Item - narrows = set(range(count)) - zeroes = set(range(count)) + bit_lengths = [0] * count for item in items: - wides = [i for i in narrows if not (-128 <= item[i] <= 127)] - narrows.difference_update(wides) - nonzeroes = [i for i in zeroes if item[i]] - zeroes.difference_update(nonzeroes) - if not narrows and not zeroes: - break + # The "+ (i < -1)" magic is to handle two's-compliment. + # That is, we want to get back 7 for -128, whereas + # bit_length() returns 8. Similarly for -65536. + # The reason "i < -1" is used instead of "i < 0" is that + # the latter would make it return 0 for "-1" instead of 1. + bl = [(i + (i < -1)).bit_length() for i in item] + bit_lengths = [max(*pair) for pair in zip(bl, bit_lengths)] + # The addition of 8, instead of seven, is to account for the sign bit. + # This "((b + 8) >> 3) if b else 0" when combined with the above + # "(i + (i < -1)).bit_length()" is a faster way to compute byte-lengths + # conforming to: + # + # byte_length = (0 if i == 0 else + # 1 if -128 <= i < 128 else + # 2 if -65536 <= i < 65536 else + # ...) + byte_lengths = [((b + 8) >> 3) if b else 0 for b in bit_lengths] + + # https://github.com/fonttools/fonttools/issues/2279 + longWords = any(b > 2 for b in byte_lengths) + if optimize: - # Reorder columns such that all SHORT columns come before UINT8 - self.VarRegionIndex = _reorderItem(self.VarRegionIndex, narrows, zeroes) + # Reorder columns such that wider columns come before narrower columns + mapping = [] + mapping.extend(i for i,b in enumerate(byte_lengths) if b > 2) + mapping.extend(i for i,b in enumerate(byte_lengths) if b == 2) + mapping.extend(i for i,b in enumerate(byte_lengths) if b == 1) + + byte_lengths = _reorderItem(byte_lengths, mapping) + self.VarRegionIndex = _reorderItem(self.VarRegionIndex, mapping) self.VarRegionCount = len(self.VarRegionIndex) for i in range(len(items)): - items[i] = _reorderItem(items[i], narrows, zeroes) - self.NumShorts = count - len(narrows) + items[i] = _reorderItem(items[i], mapping) + + if longWords: + self.NumShorts = max((i for i,b in enumerate(byte_lengths) if b > 2), default=-1) + 1 + self.NumShorts |= 0x8000 else: - wides = set(range(count)) - narrows - self.NumShorts = 1+max(wides) if wides else 0 + self.NumShorts = max((i for i,b in enumerate(byte_lengths) if b > 1), default=-1) + 1 + self.VarRegionCount = len(self.VarRegionIndex) return self @@ -106,6 +121,14 @@ def buildVarIdxMap(varIdxes, glyphOrder): self.mapping = {g:v for g,v in zip(glyphOrder, varIdxes)} return self + +def buildDeltaSetIndexMap(varIdxes): + self = ot.DeltaSetIndexMap() + self.mapping = list(varIdxes) + self.Format = 1 if len(varIdxes) > 0xFFFF else 0 + return self + + def buildVarDevTable(varIdx): self = ot.Device() self.DeltaFormat = 0x8000 |