Coverage for /usr/lib/python3/dist-packages/fontTools/varLib/builder.py: 18%

91 statements  

« prev     ^ index     » next       coverage.py v7.9.1, created at 2025-06-14 15:55 +0200

1from fontTools import ttLib 

2from fontTools.ttLib.tables import otTables as ot 

3 

4# VariationStore 

5 

6 

7def buildVarRegionAxis(axisSupport): 

8 self = ot.VarRegionAxis() 

9 self.StartCoord, self.PeakCoord, self.EndCoord = [float(v) for v in axisSupport] 

10 return self 

11 

12 

13def buildVarRegion(support, axisTags): 

14 assert all(tag in axisTags for tag in support.keys()), ( 

15 "Unknown axis tag found.", 

16 support, 

17 axisTags, 

18 ) 

19 self = ot.VarRegion() 

20 self.VarRegionAxis = [] 

21 for tag in axisTags: 

22 self.VarRegionAxis.append(buildVarRegionAxis(support.get(tag, (0, 0, 0)))) 

23 return self 

24 

25 

26def buildVarRegionList(supports, axisTags): 

27 self = ot.VarRegionList() 

28 self.RegionAxisCount = len(axisTags) 

29 self.Region = [] 

30 for support in supports: 

31 self.Region.append(buildVarRegion(support, axisTags)) 

32 self.RegionCount = len(self.Region) 

33 return self 

34 

35 

36def _reorderItem(lst, mapping): 

37 return [lst[i] for i in mapping] 

38 

39 

40def VarData_calculateNumShorts(self, optimize=False): 

41 count = self.VarRegionCount 

42 items = self.Item 

43 bit_lengths = [0] * count 

44 for item in items: 

45 # The "+ (i < -1)" magic is to handle two's-compliment. 

46 # That is, we want to get back 7 for -128, whereas 

47 # bit_length() returns 8. Similarly for -65536. 

48 # The reason "i < -1" is used instead of "i < 0" is that 

49 # the latter would make it return 0 for "-1" instead of 1. 

50 bl = [(i + (i < -1)).bit_length() for i in item] 

51 bit_lengths = [max(*pair) for pair in zip(bl, bit_lengths)] 

52 # The addition of 8, instead of seven, is to account for the sign bit. 

53 # This "((b + 8) >> 3) if b else 0" when combined with the above 

54 # "(i + (i < -1)).bit_length()" is a faster way to compute byte-lengths 

55 # conforming to: 

56 # 

57 # byte_length = (0 if i == 0 else 

58 # 1 if -128 <= i < 128 else 

59 # 2 if -65536 <= i < 65536 else 

60 # ...) 

61 byte_lengths = [((b + 8) >> 3) if b else 0 for b in bit_lengths] 

62 

63 # https://github.com/fonttools/fonttools/issues/2279 

64 longWords = any(b > 2 for b in byte_lengths) 

65 

66 if optimize: 

67 # Reorder columns such that wider columns come before narrower columns 

68 mapping = [] 

69 mapping.extend(i for i, b in enumerate(byte_lengths) if b > 2) 

70 mapping.extend(i for i, b in enumerate(byte_lengths) if b == 2) 

71 mapping.extend(i for i, b in enumerate(byte_lengths) if b == 1) 

72 

73 byte_lengths = _reorderItem(byte_lengths, mapping) 

74 self.VarRegionIndex = _reorderItem(self.VarRegionIndex, mapping) 

75 self.VarRegionCount = len(self.VarRegionIndex) 

76 for i in range(len(items)): 

77 items[i] = _reorderItem(items[i], mapping) 

78 

79 if longWords: 

80 self.NumShorts = ( 

81 max((i for i, b in enumerate(byte_lengths) if b > 2), default=-1) + 1 

82 ) 

83 self.NumShorts |= 0x8000 

84 else: 

85 self.NumShorts = ( 

86 max((i for i, b in enumerate(byte_lengths) if b > 1), default=-1) + 1 

87 ) 

88 

89 self.VarRegionCount = len(self.VarRegionIndex) 

90 return self 

91 

92 

93ot.VarData.calculateNumShorts = VarData_calculateNumShorts 

94 

95 

96def VarData_CalculateNumShorts(self, optimize=True): 

97 """Deprecated name for VarData_calculateNumShorts() which 

98 defaults to optimize=True. Use varData.calculateNumShorts() 

99 or varData.optimize().""" 

100 return VarData_calculateNumShorts(self, optimize=optimize) 

101 

102 

103def VarData_optimize(self): 

104 return VarData_calculateNumShorts(self, optimize=True) 

105 

106 

107ot.VarData.optimize = VarData_optimize 

108 

109 

110def buildVarData(varRegionIndices, items, optimize=True): 

111 self = ot.VarData() 

112 self.VarRegionIndex = list(varRegionIndices) 

113 regionCount = self.VarRegionCount = len(self.VarRegionIndex) 

114 records = self.Item = [] 

115 if items: 

116 for item in items: 

117 assert len(item) == regionCount 

118 records.append(list(item)) 

119 self.ItemCount = len(self.Item) 

120 self.calculateNumShorts(optimize=optimize) 

121 return self 

122 

123 

124def buildVarStore(varRegionList, varDataList): 

125 self = ot.VarStore() 

126 self.Format = 1 

127 self.VarRegionList = varRegionList 

128 self.VarData = list(varDataList) 

129 self.VarDataCount = len(self.VarData) 

130 return self 

131 

132 

133# Variation helpers 

134 

135 

136def buildVarIdxMap(varIdxes, glyphOrder): 

137 self = ot.VarIdxMap() 

138 self.mapping = {g: v for g, v in zip(glyphOrder, varIdxes)} 

139 return self 

140 

141 

142def buildDeltaSetIndexMap(varIdxes): 

143 mapping = list(varIdxes) 

144 if all(i == v for i, v in enumerate(mapping)): 

145 return None 

146 self = ot.DeltaSetIndexMap() 

147 self.mapping = mapping 

148 self.Format = 1 if len(mapping) > 0xFFFF else 0 

149 return self 

150 

151 

152def buildVarDevTable(varIdx): 

153 self = ot.Device() 

154 self.DeltaFormat = 0x8000 

155 self.StartSize = varIdx >> 16 

156 self.EndSize = varIdx & 0xFFFF 

157 return self