[glyf] Decompile VarComponent

This commit is contained in:
Behdad Esfahbod 2023-01-17 09:34:13 -07:00
parent de6eee7373
commit 483013be69

View File

@ -25,6 +25,7 @@ import os
from fontTools.misc import xmlWriter from fontTools.misc import xmlWriter
from fontTools.misc.filenames import userNameToFileName from fontTools.misc.filenames import userNameToFileName
from fontTools.misc.loggingTools import deprecateFunction from fontTools.misc.loggingTools import deprecateFunction
from enum import IntFlag
log = logging.getLogger(__name__) log = logging.getLogger(__name__)
@ -620,6 +621,8 @@ SCALED_COMPONENT_OFFSET = 0x0800 # composite designed to have the component off
UNSCALED_COMPONENT_OFFSET = 0x1000 # composite designed not to have the component offset scaled (designed for MS) UNSCALED_COMPONENT_OFFSET = 0x1000 # composite designed not to have the component offset scaled (designed for MS)
CompositeMaxpValues = namedtuple( CompositeMaxpValues = namedtuple(
"CompositeMaxpValues", ["nPoints", "nContours", "maxComponentDepth"] "CompositeMaxpValues", ["nPoints", "nContours", "maxComponentDepth"]
) )
@ -826,6 +829,13 @@ class Glyph(object):
len(data), len(data),
) )
def decompileVarComponents(self, data, glyfTable):
self.components = []
while data:
component = GlyphVarComponent()
data = component.decompile(data, glyfTable)
self.components.append(component)
def decompileCoordinates(self, data): def decompileCoordinates(self, data):
endPtsOfContours = array.array("H") endPtsOfContours = array.array("H")
endPtsOfContours.frombytes(data[: 2 * self.numberOfContours]) endPtsOfContours.frombytes(data[: 2 * self.numberOfContours])
@ -942,6 +952,12 @@ class Glyph(object):
data = data + struct.pack(">h", len(instructions)) + instructions data = data + struct.pack(">h", len(instructions)) + instructions
return data return data
def compileVarComponents(self, glyfTable):
data = b""
for component in self.components:
data = data + component.compile(glyfTable)
return data
def compileCoordinates(self): def compileCoordinates(self):
assert len(self.coordinates) == len(self.flags) assert len(self.coordinates) == len(self.flags)
data = [] data = []
@ -1616,6 +1632,145 @@ class GlyphComponent(object):
return result if result is NotImplemented else not result return result if result is NotImplemented else not result
class VarComponentFlags (IntFlag):
USE_MY_METRICS = 0x0001
AXIS_INDICES_ARE_SHORT = 0x0002
UNIFORM_SCALE = 0x0004
HAVE_TRANSLATE_X = 0x0008
HAVE_TRANSLATE_Y = 0x0010
HAVE_ROTATION = 0x0020
HAVE_SCALE_X = 0x0040
HAVE_SCALE_Y = 0x0080
HAVE_SKEW_X = 0x0100
HAVE_SKEW_Y = 0x0200
HAVE_TCENTER_X = 0x0400
HAVE_TCENTER_Y = 0x0800
GID_IS_24 = 0x1000
AXES_HAVE_VARIATION = 0x2000
class GlyphVarComponent(object):
def __init__(self):
pass
def decompile(self, data, glyfTable):
flags = struct.unpack(">H", data[:2])[0]
self.flags = int(flags)
data = data[2:]
numAxes = int(data[0])
data = data[1:]
if flags & VarComponentFlags.GID_IS_24:
glyphID = int(struct.unpack(">L", b'\0'+data[:3])[0])
data = data[3:]
else:
glyphID = int(struct.unpack(">H", data[:2])[0])
data = data[2:]
self.glyphName = glyfTable.getGlyphName(int(glyphID))
if flags & VarComponentFlags.AXIS_INDICES_ARE_SHORT:
axisIndices = array.array("H", data[:2*numAxes])
if sys.byteorder != "big":
arrayIndices.byteswap()
data = data[2*numAxes:]
else:
axisIndices = array.array("B", data[:numAxes])
data = data[numAxes:]
assert len(axisIndices) == numAxes
self.axisIndices = list(axisIndices)
axisValues = array.array("h", data[:2*numAxes])
if sys.byteorder != "big":
axisValues.byteswap()
data = data[2*numAxes:]
assert len(axisValues) == numAxes
self.axisValues = [fl2fi(v, 14) for v in axisValues]
# TODO make axes a dictionary? Needs fvar
def read_transform_component(data, flag, bits, default):
if flags & flag:
return data[2:], fl2fi(struct.unpack(">h", data[:2])[0], bits)
else:
return data, default
data, self.translateX = read_transform_component(data, VarComponentFlags.HAVE_TRANSLATE_X, 0, 0)
data, self.translateY = read_transform_component(data, VarComponentFlags.HAVE_TRANSLATE_Y, 0, 0)
data, self.rotation = read_transform_component(data, VarComponentFlags.HAVE_ROTATION, 12, 0)
data, self.scaleX = read_transform_component(data, VarComponentFlags.HAVE_SCALE_X, 10, 1)
data, self.scaleY = read_transform_component(data, VarComponentFlags.HAVE_SCALE_Y, 10, 1)
data, self.skewX = read_transform_component(data, VarComponentFlags.HAVE_SKEW_X, 12, 0)
data, self.skewY = read_transform_component(data, VarComponentFlags.HAVE_SKEW_Y, 12, 0)
data, self.tCenterX = read_transform_component(data, VarComponentFlags.HAVE_TCENTER_X, 0, 0)
data, self.tCenterY = read_transform_component(data, VarComponentFlags.HAVE_TCENTER_Y, 0, 0)
return data
def compile(self, more, haveInstructions, glyfTable):
data = b""
# reset all flags we will calculate ourselves
flags = self.flags & (
ROUND_XY_TO_GRID
| USE_MY_METRICS
| SCALED_COMPONENT_OFFSET
| UNSCALED_COMPONENT_OFFSET
| NON_OVERLAPPING
| OVERLAP_COMPOUND
)
if more:
flags = flags | MORE_COMPONENTS
if haveInstructions:
flags = flags | WE_HAVE_INSTRUCTIONS
if hasattr(self, "firstPt"):
if (0 <= self.firstPt <= 255) and (0 <= self.secondPt <= 255):
data = data + struct.pack(">BB", self.firstPt, self.secondPt)
else:
data = data + struct.pack(">HH", self.firstPt, self.secondPt)
flags = flags | ARG_1_AND_2_ARE_WORDS
else:
x = otRound(self.x)
y = otRound(self.y)
flags = flags | ARGS_ARE_XY_VALUES
if (-128 <= x <= 127) and (-128 <= y <= 127):
data = data + struct.pack(">bb", x, y)
else:
data = data + struct.pack(">hh", x, y)
flags = flags | ARG_1_AND_2_ARE_WORDS
if hasattr(self, "transform"):
transform = [[fl2fi(x, 14) for x in row] for row in self.transform]
if transform[0][1] or transform[1][0]:
flags = flags | WE_HAVE_A_TWO_BY_TWO
data = data + struct.pack(
">hhhh",
transform[0][0],
transform[0][1],
transform[1][0],
transform[1][1],
)
elif transform[0][0] != transform[1][1]:
flags = flags | WE_HAVE_AN_X_AND_Y_SCALE
data = data + struct.pack(">hh", transform[0][0], transform[1][1])
else:
flags = flags | WE_HAVE_A_SCALE
data = data + struct.pack(">h", transform[0][0])
glyphID = glyfTable.getGlyphID(self.glyphName)
return struct.pack(">HH", flags, glyphID) + data
def __eq__(self, other):
if type(self) != type(other):
return NotImplemented
return self.__dict__ == other.__dict__
def __ne__(self, other):
result = self.__eq__(other)
return result if result is NotImplemented else not result
class GlyphCoordinates(object): class GlyphCoordinates(object):
"""A list of glyph coordinates. """A list of glyph coordinates.