-
Notifications
You must be signed in to change notification settings - Fork 1
/
component.py
239 lines (205 loc) · 8.17 KB
/
component.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
from fontTools.misc.roundTools import otRound
from fontTools.misc.fixedTools import floatToFixed as fl2fi
from fontTools.varLib.models import normalizeLocation
from fontTools.ttLib.tables.otTables import (
VarComponent,
VarComponentFlags,
VAR_TRANSFORM_MAPPING,
)
from fontTools.misc.transform import DecomposedTransform
from rcjkTools import *
import struct
class TransformHave:
have_translateX = False
have_translateY = False
have_rotation = False
have_scaleX = False
have_scaleY = False
have_skewX = False
have_skewY = False
have_tcenterX = False
have_tcenterY = False
class ComponentAnalysis:
def __init__(self):
self.coordinates = set()
self.coordinateVaries = False
self.coordinateHaveOverlay = set()
self.coordinateHaveReset = set()
self.coordinateHave = set()
self.coordinatesReset = None
self.transformHave = TransformHave()
def getComponentFlags(self):
flags = 0
if self.coordinatesReset:
flags |= VarComponentFlags.RESET_UNSPECIFIED_AXES
if self.transformHave.have_translateX:
flags |= VarComponentFlags.HAVE_TRANSLATE_X
if self.transformHave.have_translateY:
flags |= VarComponentFlags.HAVE_TRANSLATE_Y
if self.transformHave.have_rotation:
flags |= VarComponentFlags.HAVE_ROTATION
if self.transformHave.have_scaleX:
flags |= VarComponentFlags.HAVE_SCALE_X
if self.transformHave.have_scaleY:
flags |= VarComponentFlags.HAVE_SCALE_Y
if self.transformHave.have_skewX:
flags |= VarComponentFlags.HAVE_SKEW_X
if self.transformHave.have_skewY:
flags |= VarComponentFlags.HAVE_SKEW_Y
if self.transformHave.have_tcenterX:
flags |= VarComponentFlags.HAVE_TCENTER_X
if self.transformHave.have_tcenterY:
flags |= VarComponentFlags.HAVE_TCENTER_Y
return flags
def _usesPublicAxes(componentGlyph, publicAxes, glyphs):
for axis in componentGlyph.axes:
if axis.name in publicAxes:
return True
glyph_masters = glyphMasters(componentGlyph)
layer = next(iter(glyph_masters.values()))
defaultComponents = layer.glyph.components
for component in defaultComponents:
if _usesPublicAxes(glyphs[component.name], publicAxes, glyphs):
return True
return False
def analyzeComponents(glyph_masters, glyphs, glyphAxes, publicAxes):
layer = next(iter(glyph_masters.values()))
defaultComponents = layer.glyph.components
defaultLocations = []
allComponentAxes = []
allUsesPublicAxes = []
for component in defaultComponents:
loc = component.location
componentAxes = {
axis.name: (axis.minValue, axis.defaultValue, axis.maxValue)
for axis in glyphs[component.name].axes
}
allComponentAxes.append(componentAxes)
loc = normalizeLocation(loc, componentAxes)
defaultLocations.append(loc)
usesPublicAxes = any(axis in publicAxes for axis in loc) or _usesPublicAxes(
glyphs[component.name], publicAxes, glyphs
)
allUsesPublicAxes.append(usesPublicAxes)
cas = []
for component in layer.glyph.components:
cas.append(ComponentAnalysis())
for masterLocationTuple, layer in glyph_masters.items():
for i, component in enumerate(layer.glyph.components):
ca = cas[i]
ca.coordinates.update(component.location.keys())
for ca in cas:
ca.coordinates = list(sorted(ca.coordinates))
for masterLocationTuple, layer in glyph_masters.items():
masterLocation = dictifyLocation(masterLocationTuple)
for axis in glyphAxes:
if axis not in masterLocation:
masterLocation[axis] = 0
for i, component in enumerate(layer.glyph.components):
assert component.name == defaultComponents[i].name, (
component.name,
defaultComponents[i].name,
)
ca = cas[i]
t = component.transformation
if otRound(t.translateX):
ca.transformHave.have_translateX = True
if otRound(t.translateY):
ca.transformHave.have_translateY = True
if fl2fi(t.rotation / 180.0, 12):
ca.transformHave.have_rotation = True
if fl2fi(t.scaleX, 10) != 1 << 10:
ca.transformHave.have_scaleX = True
if fl2fi(t.scaleY, 10) != 1 << 10 and fl2fi(t.scaleY, 10) != fl2fi(
t.scaleX, 10
):
ca.transformHave.have_scaleY = True
if fl2fi(t.skewX / 180.0, 12):
ca.transformHave.have_skewX = True
if fl2fi(t.skewY / 180.0, 12):
ca.transformHave.have_skewY = True
if otRound(t.tCenterX):
ca.transformHave.have_tcenterX = True
if otRound(t.tCenterY):
ca.transformHave.have_tcenterY = True
loc = component.location
loc = normalizeLocation(loc, allComponentAxes[i])
for name in ca.coordinates:
c = loc.get(name, 0)
if allUsesPublicAxes[i] or c:
ca.coordinateHaveReset.add(name)
if not (name in masterLocation and c == masterLocation[name]):
ca.coordinateHaveOverlay.add(name)
for ca in cas:
ca.coordinatesReset = len(ca.coordinateHaveReset) < len(
ca.coordinateHaveOverlay
)
ca.coordinateHave = (
ca.coordinateHaveReset if ca.coordinatesReset else ca.coordinateHaveOverlay
)
for layer in list(glyph_masters.values())[1:]:
for i, component in enumerate(layer.glyph.components):
ca = cas[i]
loc = component.location
loc = normalizeLocation(loc, allComponentAxes[i])
for name in ca.coordinates:
# XXX Is this logic correct for coordinatesHaveReset?
if name in ca.coordinateHave and loc.get(name, 0) != defaultLocations[
i
].get(name, 0):
ca.coordinateVaries = True
return cas
def getComponentMasters(
rcjkfont, component, componentGlyph, componentAnalysis, fvarTags, publicAxes
):
ca = componentAnalysis
componentAxes = {
axis.name: (axis.minValue, axis.defaultValue, axis.maxValue)
for axis in componentGlyph.axes
}
axesMap = {}
i = 0
for name in sorted(componentAxes.keys()):
if name in publicAxes:
axesMap[name] = name
elif name in fvarTags:
axesMap[name] = name
else:
axesMap[name] = "%04d" % i
i += 1
coords = component.location
coords = normalizeLocation(coords, componentAxes)
axisIndexMasters, axisValueMasters, transformMasters = [], [], []
for name in ca.coordinateHave:
coord = coords.get(name, 0)
if name not in axesMap:
continue # This happens with bad input data
i = fvarTags.index(axesMap[name])
axisIndexMasters.append(i)
axisValueMasters.append(fl2fi(coord, 14))
if axisIndexMasters:
# Sort them for better sharing
axisIndexMasters, axisValueMasters = zip(
*sorted(zip(axisIndexMasters, axisValueMasters))
)
c = ca.transformHave
t = component.transformation
if c.have_translateX:
transformMasters.append(otRound(t.translateX))
if c.have_translateY:
transformMasters.append(otRound(t.translateY))
if c.have_rotation:
transformMasters.append(fl2fi(t.rotation / 180.0, 12))
if c.have_scaleX:
transformMasters.append(fl2fi(t.scaleX, 10))
if c.have_scaleY:
transformMasters.append(fl2fi(t.scaleY, 10))
if c.have_skewX:
transformMasters.append(fl2fi(t.skewX / 180.0, 12))
if c.have_skewY:
transformMasters.append(fl2fi(t.skewY / 180.0, 12))
if c.have_tcenterX:
transformMasters.append(otRound(t.tCenterX))
if c.have_tcenterY:
transformMasters.append(otRound(t.tCenterY))
return tuple(axisIndexMasters), tuple(axisValueMasters), tuple(transformMasters)