xref: /aosp_15_r20/external/fonttools/Lib/fontTools/varLib/builder.py (revision e1fe3e4ad2793916b15cccdc4a7da52a7e1dd0e9)
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