Rip out glyf1 VarComposites
In favor of separate VARC table. ttGlyphSet does NOT yet know how to draw VARC table though. The 9 failing tests are all VarComposite-related and need to be updated with VARC equivalents eventually when we add VARC support to subsetter and instancer.
This commit is contained in:
parent
ec78b572c9
commit
bcd5e4c216
@ -656,11 +656,7 @@ class FontBuilder(object):
|
||||
|
||||
if validateGlyphFormat and self.font["head"].glyphDataFormat == 0:
|
||||
for name, g in glyphs.items():
|
||||
if g.isVarComposite():
|
||||
raise ValueError(
|
||||
f"Glyph {name!r} is a variable composite, but glyphDataFormat=0"
|
||||
)
|
||||
elif g.numberOfContours > 0 and any(f & flagCubic for f in g.flags):
|
||||
if g.numberOfContours > 0 and any(f & flagCubic for f in g.flags):
|
||||
raise ValueError(
|
||||
f"Glyph {name!r} has cubic Bezier outlines, but glyphDataFormat=0; "
|
||||
"either convert to quadratics with cu2qu or set glyphDataFormat=1."
|
||||
|
@ -225,7 +225,7 @@ def merge(self, m, tables):
|
||||
g.removeHinting()
|
||||
# Expand composite glyphs to load their
|
||||
# composite glyph names.
|
||||
if g.isComposite() or g.isVarComposite():
|
||||
if g.isComposite():
|
||||
g.expand(table)
|
||||
return DefaultTable.merge(self, m, tables)
|
||||
|
||||
|
@ -11,7 +11,6 @@ from fontTools.cffLib import VarStoreData
|
||||
import fontTools.cffLib.specializer as cffSpecializer
|
||||
from fontTools.varLib import builder # for VarData.calculateNumShorts
|
||||
from fontTools.misc.fixedTools import otRound
|
||||
from fontTools.ttLib.tables._g_l_y_f import VarComponentFlags
|
||||
|
||||
|
||||
__all__ = ["scale_upem", "ScalerVisitor"]
|
||||
@ -123,13 +122,6 @@ def visit(visitor, obj, attr, glyphs):
|
||||
component.y = visitor.scale(component.y)
|
||||
continue
|
||||
|
||||
if g.isVarComposite():
|
||||
for component in g.components:
|
||||
for attr in ("translateX", "translateY", "tCenterX", "tCenterY"):
|
||||
v = getattr(component.transform, attr)
|
||||
setattr(component.transform, attr, visitor.scale(v))
|
||||
continue
|
||||
|
||||
if hasattr(g, "coordinates"):
|
||||
coordinates = g.coordinates
|
||||
for i, (x, y) in enumerate(coordinates):
|
||||
@ -138,56 +130,15 @@ def visit(visitor, obj, attr, glyphs):
|
||||
|
||||
@ScalerVisitor.register_attr(ttLib.getTableClass("gvar"), "variations")
|
||||
def visit(visitor, obj, attr, variations):
|
||||
# VarComposites are a pain to handle :-(
|
||||
glyfTable = visitor.font["glyf"]
|
||||
|
||||
for glyphName, varlist in variations.items():
|
||||
glyph = glyfTable[glyphName]
|
||||
isVarComposite = glyph.isVarComposite()
|
||||
for var in varlist:
|
||||
coordinates = var.coordinates
|
||||
|
||||
if not isVarComposite:
|
||||
for i, xy in enumerate(coordinates):
|
||||
if xy is None:
|
||||
continue
|
||||
coordinates[i] = visitor.scale(xy[0]), visitor.scale(xy[1])
|
||||
continue
|
||||
|
||||
# VarComposite glyph
|
||||
|
||||
i = 0
|
||||
for component in glyph.components:
|
||||
if component.flags & VarComponentFlags.AXES_HAVE_VARIATION:
|
||||
i += len(component.location)
|
||||
if component.flags & (
|
||||
VarComponentFlags.HAVE_TRANSLATE_X
|
||||
| VarComponentFlags.HAVE_TRANSLATE_Y
|
||||
):
|
||||
xy = coordinates[i]
|
||||
coordinates[i] = visitor.scale(xy[0]), visitor.scale(xy[1])
|
||||
i += 1
|
||||
if component.flags & VarComponentFlags.HAVE_ROTATION:
|
||||
i += 1
|
||||
if component.flags & (
|
||||
VarComponentFlags.HAVE_SCALE_X | VarComponentFlags.HAVE_SCALE_Y
|
||||
):
|
||||
i += 1
|
||||
if component.flags & (
|
||||
VarComponentFlags.HAVE_SKEW_X | VarComponentFlags.HAVE_SKEW_Y
|
||||
):
|
||||
i += 1
|
||||
if component.flags & (
|
||||
VarComponentFlags.HAVE_TCENTER_X | VarComponentFlags.HAVE_TCENTER_Y
|
||||
):
|
||||
xy = coordinates[i]
|
||||
coordinates[i] = visitor.scale(xy[0]), visitor.scale(xy[1])
|
||||
i += 1
|
||||
|
||||
# Phantom points
|
||||
assert i + 4 == len(coordinates)
|
||||
for i in range(i, len(coordinates)):
|
||||
xy = coordinates[i]
|
||||
for i, xy in enumerate(coordinates):
|
||||
if xy is None:
|
||||
continue
|
||||
coordinates[i] = visitor.scale(xy[0]), visitor.scale(xy[1])
|
||||
|
||||
|
||||
|
@ -424,29 +424,6 @@ class table__g_l_y_f(DefaultTable.DefaultTable):
|
||||
for c in glyph.components
|
||||
],
|
||||
)
|
||||
elif glyph.isVarComposite():
|
||||
coords = []
|
||||
controls = []
|
||||
|
||||
for component in glyph.components:
|
||||
(
|
||||
componentCoords,
|
||||
componentControls,
|
||||
) = component.getCoordinatesAndControls()
|
||||
coords.extend(componentCoords)
|
||||
controls.extend(componentControls)
|
||||
|
||||
coords = GlyphCoordinates(coords)
|
||||
|
||||
controls = _GlyphControls(
|
||||
numberOfContours=glyph.numberOfContours,
|
||||
endPts=list(range(len(coords))),
|
||||
flags=None,
|
||||
components=[
|
||||
(c.glyphName, getattr(c, "flags", None)) for c in glyph.components
|
||||
],
|
||||
)
|
||||
|
||||
else:
|
||||
coords, endPts, flags = glyph.getCoordinates(self)
|
||||
coords = coords.copy()
|
||||
@ -492,10 +469,6 @@ class table__g_l_y_f(DefaultTable.DefaultTable):
|
||||
for p, comp in zip(coord, glyph.components):
|
||||
if hasattr(comp, "x"):
|
||||
comp.x, comp.y = p
|
||||
elif glyph.isVarComposite():
|
||||
for comp in glyph.components:
|
||||
coord = comp.setCoordinates(coord)
|
||||
assert not coord
|
||||
elif glyph.numberOfContours == 0:
|
||||
assert len(coord) == 0
|
||||
else:
|
||||
@ -737,8 +710,6 @@ class Glyph(object):
|
||||
return
|
||||
if self.isComposite():
|
||||
self.decompileComponents(data, glyfTable)
|
||||
elif self.isVarComposite():
|
||||
self.decompileVarComponents(data, glyfTable)
|
||||
else:
|
||||
self.decompileCoordinates(data)
|
||||
|
||||
@ -758,8 +729,6 @@ class Glyph(object):
|
||||
data = sstruct.pack(glyphHeaderFormat, self)
|
||||
if self.isComposite():
|
||||
data = data + self.compileComponents(glyfTable)
|
||||
elif self.isVarComposite():
|
||||
data = data + self.compileVarComponents(glyfTable)
|
||||
else:
|
||||
data = data + self.compileCoordinates()
|
||||
return data
|
||||
@ -769,10 +738,6 @@ class Glyph(object):
|
||||
for compo in self.components:
|
||||
compo.toXML(writer, ttFont)
|
||||
haveInstructions = hasattr(self, "program")
|
||||
elif self.isVarComposite():
|
||||
for compo in self.components:
|
||||
compo.toXML(writer, ttFont)
|
||||
haveInstructions = False
|
||||
else:
|
||||
last = 0
|
||||
for i in range(self.numberOfContours):
|
||||
@ -842,15 +807,6 @@ class Glyph(object):
|
||||
component = GlyphComponent()
|
||||
self.components.append(component)
|
||||
component.fromXML(name, attrs, content, ttFont)
|
||||
elif name == "varComponent":
|
||||
if self.numberOfContours > 0:
|
||||
raise ttLib.TTLibError("can't mix composites and contours in glyph")
|
||||
self.numberOfContours = -2
|
||||
if not hasattr(self, "components"):
|
||||
self.components = []
|
||||
component = GlyphVarComponent()
|
||||
self.components.append(component)
|
||||
component.fromXML(name, attrs, content, ttFont)
|
||||
elif name == "instructions":
|
||||
self.program = ttProgram.Program()
|
||||
for element in content:
|
||||
@ -860,7 +816,7 @@ class Glyph(object):
|
||||
self.program.fromXML(name, attrs, content, ttFont)
|
||||
|
||||
def getCompositeMaxpValues(self, glyfTable, maxComponentDepth=1):
|
||||
assert self.isComposite() or self.isVarComposite()
|
||||
assert self.isComposite()
|
||||
nContours = 0
|
||||
nPoints = 0
|
||||
initialMaxComponentDepth = maxComponentDepth
|
||||
@ -904,13 +860,6 @@ class Glyph(object):
|
||||
len(data),
|
||||
)
|
||||
|
||||
def decompileVarComponents(self, data, glyfTable):
|
||||
self.components = []
|
||||
while len(data) >= GlyphVarComponent.MIN_SIZE:
|
||||
component = GlyphVarComponent()
|
||||
data = component.decompile(data, glyfTable)
|
||||
self.components.append(component)
|
||||
|
||||
def decompileCoordinates(self, data):
|
||||
endPtsOfContours = array.array("H")
|
||||
endPtsOfContours.frombytes(data[: 2 * self.numberOfContours])
|
||||
@ -1027,9 +976,6 @@ class Glyph(object):
|
||||
data = data + struct.pack(">h", len(instructions)) + instructions
|
||||
return data
|
||||
|
||||
def compileVarComponents(self, glyfTable):
|
||||
return b"".join(c.compile(glyfTable) for c in self.components)
|
||||
|
||||
def compileCoordinates(self):
|
||||
assert len(self.coordinates) == len(self.flags)
|
||||
data = []
|
||||
@ -1231,13 +1177,6 @@ class Glyph(object):
|
||||
else:
|
||||
return self.numberOfContours == -1
|
||||
|
||||
def isVarComposite(self):
|
||||
"""Test whether a glyph has variable components"""
|
||||
if hasattr(self, "data"):
|
||||
return struct.unpack(">h", self.data[:2])[0] == -2 if self.data else False
|
||||
else:
|
||||
return self.numberOfContours == -2
|
||||
|
||||
def getCoordinates(self, glyfTable):
|
||||
"""Return the coordinates, end points and flags
|
||||
|
||||
@ -1308,8 +1247,6 @@ class Glyph(object):
|
||||
allCoords.extend(coordinates)
|
||||
allFlags.extend(flags)
|
||||
return allCoords, allEndPts, allFlags
|
||||
elif self.isVarComposite():
|
||||
raise NotImplementedError("use TTGlyphSet to draw VarComposite glyphs")
|
||||
else:
|
||||
return GlyphCoordinates(), [], bytearray()
|
||||
|
||||
@ -1319,12 +1256,8 @@ class Glyph(object):
|
||||
This method can be used on simple glyphs (in which case it returns an
|
||||
empty list) or composite glyphs.
|
||||
"""
|
||||
if hasattr(self, "data") and self.isVarComposite():
|
||||
# TODO(VarComposite) Add implementation without expanding glyph
|
||||
self.expand(glyfTable)
|
||||
|
||||
if not hasattr(self, "data"):
|
||||
if self.isComposite() or self.isVarComposite():
|
||||
if self.isComposite():
|
||||
return [c.glyphName for c in self.components]
|
||||
else:
|
||||
return []
|
||||
@ -1367,8 +1300,6 @@ class Glyph(object):
|
||||
if self.isComposite():
|
||||
if hasattr(self, "program"):
|
||||
del self.program
|
||||
elif self.isVarComposite():
|
||||
pass # Doesn't have hinting
|
||||
else:
|
||||
self.program = ttProgram.Program()
|
||||
self.program.fromBytecode([])
|
||||
@ -1450,13 +1381,6 @@ class Glyph(object):
|
||||
i += 2 + instructionLen
|
||||
# Remove padding
|
||||
data = data[:i]
|
||||
elif self.isVarComposite():
|
||||
i = 0
|
||||
MIN_SIZE = GlyphVarComponent.MIN_SIZE
|
||||
while len(data[i : i + MIN_SIZE]) >= MIN_SIZE:
|
||||
size = GlyphVarComponent.getSize(data[i : i + MIN_SIZE])
|
||||
i += size
|
||||
data = data[:i]
|
||||
|
||||
self.data = data
|
||||
|
||||
@ -1942,391 +1866,6 @@ class GlyphComponent(object):
|
||||
return result if result is NotImplemented else not result
|
||||
|
||||
|
||||
#
|
||||
# Variable Composite glyphs
|
||||
# https://github.com/harfbuzz/boring-expansion-spec/blob/main/glyf1.md
|
||||
#
|
||||
|
||||
|
||||
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_24BIT = 0x1000
|
||||
AXES_HAVE_VARIATION = 0x2000
|
||||
RESET_UNSPECIFIED_AXES = 0x4000
|
||||
|
||||
|
||||
VarComponentTransformMappingValues = namedtuple(
|
||||
"VarComponentTransformMappingValues",
|
||||
["flag", "fractionalBits", "scale", "defaultValue"],
|
||||
)
|
||||
|
||||
VAR_COMPONENT_TRANSFORM_MAPPING = {
|
||||
"translateX": VarComponentTransformMappingValues(
|
||||
VarComponentFlags.HAVE_TRANSLATE_X, 0, 1, 0
|
||||
),
|
||||
"translateY": VarComponentTransformMappingValues(
|
||||
VarComponentFlags.HAVE_TRANSLATE_Y, 0, 1, 0
|
||||
),
|
||||
"rotation": VarComponentTransformMappingValues(
|
||||
VarComponentFlags.HAVE_ROTATION, 12, 180, 0
|
||||
),
|
||||
"scaleX": VarComponentTransformMappingValues(
|
||||
VarComponentFlags.HAVE_SCALE_X, 10, 1, 1
|
||||
),
|
||||
"scaleY": VarComponentTransformMappingValues(
|
||||
VarComponentFlags.HAVE_SCALE_Y, 10, 1, 1
|
||||
),
|
||||
"skewX": VarComponentTransformMappingValues(
|
||||
VarComponentFlags.HAVE_SKEW_X, 12, -180, 0
|
||||
),
|
||||
"skewY": VarComponentTransformMappingValues(
|
||||
VarComponentFlags.HAVE_SKEW_Y, 12, 180, 0
|
||||
),
|
||||
"tCenterX": VarComponentTransformMappingValues(
|
||||
VarComponentFlags.HAVE_TCENTER_X, 0, 1, 0
|
||||
),
|
||||
"tCenterY": VarComponentTransformMappingValues(
|
||||
VarComponentFlags.HAVE_TCENTER_Y, 0, 1, 0
|
||||
),
|
||||
}
|
||||
|
||||
|
||||
class GlyphVarComponent(object):
|
||||
MIN_SIZE = 5
|
||||
|
||||
def __init__(self):
|
||||
self.location = {}
|
||||
self.transform = DecomposedTransform()
|
||||
|
||||
@staticmethod
|
||||
def getSize(data):
|
||||
size = 5
|
||||
flags = struct.unpack(">H", data[:2])[0]
|
||||
numAxes = int(data[2])
|
||||
|
||||
if flags & VarComponentFlags.GID_IS_24BIT:
|
||||
size += 1
|
||||
|
||||
size += numAxes
|
||||
if flags & VarComponentFlags.AXIS_INDICES_ARE_SHORT:
|
||||
size += 2 * numAxes
|
||||
else:
|
||||
axisIndices = array.array("B", data[:numAxes])
|
||||
size += numAxes
|
||||
|
||||
for attr_name, mapping_values in VAR_COMPONENT_TRANSFORM_MAPPING.items():
|
||||
if flags & mapping_values.flag:
|
||||
size += 2
|
||||
|
||||
return size
|
||||
|
||||
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_24BIT:
|
||||
glyphID = int(struct.unpack(">L", b"\0" + data[:3])[0])
|
||||
data = data[3:]
|
||||
flags ^= VarComponentFlags.GID_IS_24BIT
|
||||
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":
|
||||
axisIndices.byteswap()
|
||||
data = data[2 * numAxes :]
|
||||
flags ^= VarComponentFlags.AXIS_INDICES_ARE_SHORT
|
||||
else:
|
||||
axisIndices = array.array("B", data[:numAxes])
|
||||
data = data[numAxes:]
|
||||
assert len(axisIndices) == numAxes
|
||||
axisIndices = list(axisIndices)
|
||||
|
||||
axisValues = array.array("h", data[: 2 * numAxes])
|
||||
if sys.byteorder != "big":
|
||||
axisValues.byteswap()
|
||||
data = data[2 * numAxes :]
|
||||
assert len(axisValues) == numAxes
|
||||
axisValues = [fi2fl(v, 14) for v in axisValues]
|
||||
|
||||
self.location = {
|
||||
glyfTable.axisTags[i]: v for i, v in zip(axisIndices, axisValues)
|
||||
}
|
||||
|
||||
def read_transform_component(data, values):
|
||||
if flags & values.flag:
|
||||
return (
|
||||
data[2:],
|
||||
fi2fl(struct.unpack(">h", data[:2])[0], values.fractionalBits)
|
||||
* values.scale,
|
||||
)
|
||||
else:
|
||||
return data, values.defaultValue
|
||||
|
||||
for attr_name, mapping_values in VAR_COMPONENT_TRANSFORM_MAPPING.items():
|
||||
data, value = read_transform_component(data, mapping_values)
|
||||
setattr(self.transform, attr_name, value)
|
||||
|
||||
if flags & VarComponentFlags.UNIFORM_SCALE:
|
||||
if flags & VarComponentFlags.HAVE_SCALE_X and not (
|
||||
flags & VarComponentFlags.HAVE_SCALE_Y
|
||||
):
|
||||
self.transform.scaleY = self.transform.scaleX
|
||||
flags |= VarComponentFlags.HAVE_SCALE_Y
|
||||
flags ^= VarComponentFlags.UNIFORM_SCALE
|
||||
|
||||
return data
|
||||
|
||||
def compile(self, glyfTable):
|
||||
data = b""
|
||||
|
||||
if not hasattr(self, "flags"):
|
||||
flags = 0
|
||||
# Calculate optimal transform component flags
|
||||
for attr_name, mapping in VAR_COMPONENT_TRANSFORM_MAPPING.items():
|
||||
value = getattr(self.transform, attr_name)
|
||||
if fl2fi(value / mapping.scale, mapping.fractionalBits) != fl2fi(
|
||||
mapping.defaultValue / mapping.scale, mapping.fractionalBits
|
||||
):
|
||||
flags |= mapping.flag
|
||||
else:
|
||||
flags = self.flags
|
||||
|
||||
if (
|
||||
flags & VarComponentFlags.HAVE_SCALE_X
|
||||
and flags & VarComponentFlags.HAVE_SCALE_Y
|
||||
and fl2fi(self.transform.scaleX, 10) == fl2fi(self.transform.scaleY, 10)
|
||||
):
|
||||
flags |= VarComponentFlags.UNIFORM_SCALE
|
||||
flags ^= VarComponentFlags.HAVE_SCALE_Y
|
||||
|
||||
numAxes = len(self.location)
|
||||
|
||||
data = data + struct.pack(">B", numAxes)
|
||||
|
||||
glyphID = glyfTable.getGlyphID(self.glyphName)
|
||||
if glyphID > 65535:
|
||||
flags |= VarComponentFlags.GID_IS_24BIT
|
||||
data = data + struct.pack(">L", glyphID)[1:]
|
||||
else:
|
||||
data = data + struct.pack(">H", glyphID)
|
||||
|
||||
axisIndices = [glyfTable.axisTags.index(tag) for tag in self.location.keys()]
|
||||
if all(a <= 255 for a in axisIndices):
|
||||
axisIndices = array.array("B", axisIndices)
|
||||
else:
|
||||
axisIndices = array.array("H", axisIndices)
|
||||
if sys.byteorder != "big":
|
||||
axisIndices.byteswap()
|
||||
flags |= VarComponentFlags.AXIS_INDICES_ARE_SHORT
|
||||
data = data + bytes(axisIndices)
|
||||
|
||||
axisValues = self.location.values()
|
||||
axisValues = array.array("h", (fl2fi(v, 14) for v in axisValues))
|
||||
if sys.byteorder != "big":
|
||||
axisValues.byteswap()
|
||||
data = data + bytes(axisValues)
|
||||
|
||||
def write_transform_component(data, value, values):
|
||||
if flags & values.flag:
|
||||
return data + struct.pack(
|
||||
">h", fl2fi(value / values.scale, values.fractionalBits)
|
||||
)
|
||||
else:
|
||||
return data
|
||||
|
||||
for attr_name, mapping_values in VAR_COMPONENT_TRANSFORM_MAPPING.items():
|
||||
value = getattr(self.transform, attr_name)
|
||||
data = write_transform_component(data, value, mapping_values)
|
||||
|
||||
return struct.pack(">H", flags) + data
|
||||
|
||||
def toXML(self, writer, ttFont):
|
||||
attrs = [("glyphName", self.glyphName)]
|
||||
|
||||
if hasattr(self, "flags"):
|
||||
attrs = attrs + [("flags", hex(self.flags))]
|
||||
|
||||
for attr_name, mapping in VAR_COMPONENT_TRANSFORM_MAPPING.items():
|
||||
v = getattr(self.transform, attr_name)
|
||||
if v != mapping.defaultValue:
|
||||
attrs.append((attr_name, fl2str(v, mapping.fractionalBits)))
|
||||
|
||||
writer.begintag("varComponent", attrs)
|
||||
writer.newline()
|
||||
|
||||
writer.begintag("location")
|
||||
writer.newline()
|
||||
for tag, v in self.location.items():
|
||||
writer.simpletag("axis", [("tag", tag), ("value", fl2str(v, 14))])
|
||||
writer.newline()
|
||||
writer.endtag("location")
|
||||
writer.newline()
|
||||
|
||||
writer.endtag("varComponent")
|
||||
writer.newline()
|
||||
|
||||
def fromXML(self, name, attrs, content, ttFont):
|
||||
self.glyphName = attrs["glyphName"]
|
||||
|
||||
if "flags" in attrs:
|
||||
self.flags = safeEval(attrs["flags"])
|
||||
|
||||
for attr_name, mapping in VAR_COMPONENT_TRANSFORM_MAPPING.items():
|
||||
if attr_name not in attrs:
|
||||
continue
|
||||
v = str2fl(safeEval(attrs[attr_name]), mapping.fractionalBits)
|
||||
setattr(self.transform, attr_name, v)
|
||||
|
||||
for c in content:
|
||||
if not isinstance(c, tuple):
|
||||
continue
|
||||
name, attrs, content = c
|
||||
if name != "location":
|
||||
continue
|
||||
for c in content:
|
||||
if not isinstance(c, tuple):
|
||||
continue
|
||||
name, attrs, content = c
|
||||
assert name == "axis"
|
||||
assert not content
|
||||
self.location[attrs["tag"]] = str2fl(safeEval(attrs["value"]), 14)
|
||||
|
||||
def getPointCount(self):
|
||||
assert hasattr(self, "flags"), "VarComponent with variations must have flags"
|
||||
|
||||
count = 0
|
||||
|
||||
if self.flags & VarComponentFlags.AXES_HAVE_VARIATION:
|
||||
count += len(self.location)
|
||||
|
||||
if self.flags & (
|
||||
VarComponentFlags.HAVE_TRANSLATE_X | VarComponentFlags.HAVE_TRANSLATE_Y
|
||||
):
|
||||
count += 1
|
||||
if self.flags & VarComponentFlags.HAVE_ROTATION:
|
||||
count += 1
|
||||
if self.flags & (
|
||||
VarComponentFlags.HAVE_SCALE_X | VarComponentFlags.HAVE_SCALE_Y
|
||||
):
|
||||
count += 1
|
||||
if self.flags & (VarComponentFlags.HAVE_SKEW_X | VarComponentFlags.HAVE_SKEW_Y):
|
||||
count += 1
|
||||
if self.flags & (
|
||||
VarComponentFlags.HAVE_TCENTER_X | VarComponentFlags.HAVE_TCENTER_Y
|
||||
):
|
||||
count += 1
|
||||
|
||||
return count
|
||||
|
||||
def getCoordinatesAndControls(self):
|
||||
coords = []
|
||||
controls = []
|
||||
|
||||
if self.flags & VarComponentFlags.AXES_HAVE_VARIATION:
|
||||
for tag, v in self.location.items():
|
||||
controls.append(tag)
|
||||
coords.append((fl2fi(v, 14), 0))
|
||||
|
||||
if self.flags & (
|
||||
VarComponentFlags.HAVE_TRANSLATE_X | VarComponentFlags.HAVE_TRANSLATE_Y
|
||||
):
|
||||
controls.append("translate")
|
||||
coords.append((self.transform.translateX, self.transform.translateY))
|
||||
if self.flags & VarComponentFlags.HAVE_ROTATION:
|
||||
controls.append("rotation")
|
||||
coords.append((fl2fi(self.transform.rotation / 180, 12), 0))
|
||||
if self.flags & (
|
||||
VarComponentFlags.HAVE_SCALE_X | VarComponentFlags.HAVE_SCALE_Y
|
||||
):
|
||||
controls.append("scale")
|
||||
coords.append(
|
||||
(fl2fi(self.transform.scaleX, 10), fl2fi(self.transform.scaleY, 10))
|
||||
)
|
||||
if self.flags & (VarComponentFlags.HAVE_SKEW_X | VarComponentFlags.HAVE_SKEW_Y):
|
||||
controls.append("skew")
|
||||
coords.append(
|
||||
(
|
||||
fl2fi(self.transform.skewX / -180, 12),
|
||||
fl2fi(self.transform.skewY / 180, 12),
|
||||
)
|
||||
)
|
||||
if self.flags & (
|
||||
VarComponentFlags.HAVE_TCENTER_X | VarComponentFlags.HAVE_TCENTER_Y
|
||||
):
|
||||
controls.append("tCenter")
|
||||
coords.append((self.transform.tCenterX, self.transform.tCenterY))
|
||||
|
||||
return coords, controls
|
||||
|
||||
def setCoordinates(self, coords):
|
||||
i = 0
|
||||
|
||||
if self.flags & VarComponentFlags.AXES_HAVE_VARIATION:
|
||||
newLocation = {}
|
||||
for tag in self.location:
|
||||
newLocation[tag] = fi2fl(coords[i][0], 14)
|
||||
i += 1
|
||||
self.location = newLocation
|
||||
|
||||
self.transform = DecomposedTransform()
|
||||
if self.flags & (
|
||||
VarComponentFlags.HAVE_TRANSLATE_X | VarComponentFlags.HAVE_TRANSLATE_Y
|
||||
):
|
||||
self.transform.translateX, self.transform.translateY = coords[i]
|
||||
i += 1
|
||||
if self.flags & VarComponentFlags.HAVE_ROTATION:
|
||||
self.transform.rotation = fi2fl(coords[i][0], 12) * 180
|
||||
i += 1
|
||||
if self.flags & (
|
||||
VarComponentFlags.HAVE_SCALE_X | VarComponentFlags.HAVE_SCALE_Y
|
||||
):
|
||||
self.transform.scaleX, self.transform.scaleY = fi2fl(
|
||||
coords[i][0], 10
|
||||
), fi2fl(coords[i][1], 10)
|
||||
i += 1
|
||||
if self.flags & (VarComponentFlags.HAVE_SKEW_X | VarComponentFlags.HAVE_SKEW_Y):
|
||||
self.transform.skewX, self.transform.skewY = (
|
||||
fi2fl(coords[i][0], 12) * -180,
|
||||
fi2fl(coords[i][1], 12) * 180,
|
||||
)
|
||||
i += 1
|
||||
if self.flags & (
|
||||
VarComponentFlags.HAVE_TCENTER_X | VarComponentFlags.HAVE_TCENTER_Y
|
||||
):
|
||||
self.transform.tCenterX, self.transform.tCenterY = coords[i]
|
||||
i += 1
|
||||
|
||||
return coords[i:]
|
||||
|
||||
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):
|
||||
"""A list of glyph coordinates.
|
||||
|
||||
|
@ -245,11 +245,6 @@ class table__g_v_a_r(DefaultTable.DefaultTable):
|
||||
|
||||
if glyph.isComposite():
|
||||
return len(glyph.components) + NUM_PHANTOM_POINTS
|
||||
elif glyph.isVarComposite():
|
||||
count = 0
|
||||
for component in glyph.components:
|
||||
count += component.getPointCount()
|
||||
return count + NUM_PHANTOM_POINTS
|
||||
else:
|
||||
# Empty glyphs (eg. space, nonmarkingreturn) have no "coordinates" attribute.
|
||||
return len(getattr(glyph, "coordinates", [])) + NUM_PHANTOM_POINTS
|
||||
|
@ -178,10 +178,6 @@ class _TTGlyphGlyf(_TTGlyph):
|
||||
if depth:
|
||||
offset = 0 # Offset should only apply at top-level
|
||||
|
||||
if glyph.isVarComposite():
|
||||
self._drawVarComposite(glyph, pen, False)
|
||||
return
|
||||
|
||||
glyph.draw(pen, self.glyphSet.glyfTable, offset)
|
||||
|
||||
def drawPoints(self, pen):
|
||||
@ -194,35 +190,8 @@ class _TTGlyphGlyf(_TTGlyph):
|
||||
if depth:
|
||||
offset = 0 # Offset should only apply at top-level
|
||||
|
||||
if glyph.isVarComposite():
|
||||
self._drawVarComposite(glyph, pen, True)
|
||||
return
|
||||
|
||||
glyph.drawPoints(pen, self.glyphSet.glyfTable, offset)
|
||||
|
||||
def _drawVarComposite(self, glyph, pen, isPointPen):
|
||||
from fontTools.ttLib.tables._g_l_y_f import (
|
||||
VarComponentFlags,
|
||||
VAR_COMPONENT_TRANSFORM_MAPPING,
|
||||
)
|
||||
|
||||
for comp in glyph.components:
|
||||
with self.glyphSet.pushLocation(
|
||||
comp.location, comp.flags & VarComponentFlags.RESET_UNSPECIFIED_AXES
|
||||
):
|
||||
try:
|
||||
pen.addVarComponent(
|
||||
comp.glyphName, comp.transform, self.glyphSet.rawLocation
|
||||
)
|
||||
except AttributeError:
|
||||
t = comp.transform.toTransform()
|
||||
if isPointPen:
|
||||
tPen = TransformPointPen(pen, t)
|
||||
self.glyphSet[comp.glyphName].drawPoints(tPen)
|
||||
else:
|
||||
tPen = TransformPen(pen, t)
|
||||
self.glyphSet[comp.glyphName].draw(tPen)
|
||||
|
||||
def _getGlyphAndOffset(self):
|
||||
if self.glyphSet.location and self.glyphSet.gvarTable is not None:
|
||||
glyph = self._getGlyphInstance()
|
||||
@ -300,11 +269,6 @@ def _setCoordinates(glyph, coord, glyfTable, *, recalcBounds=True):
|
||||
for p, comp in zip(coord, glyph.components):
|
||||
if hasattr(comp, "x"):
|
||||
comp.x, comp.y = p
|
||||
elif glyph.isVarComposite():
|
||||
glyph.components = [copy(comp) for comp in glyph.components] # Shallow copy
|
||||
for comp in glyph.components:
|
||||
coord = comp.setCoordinates(coord)
|
||||
assert not coord
|
||||
elif glyph.numberOfContours == 0:
|
||||
assert len(coord) == 0
|
||||
else:
|
||||
|
@ -1017,8 +1017,6 @@ class WOFF2GlyfTable(getTableClass("glyf")):
|
||||
return
|
||||
elif glyph.isComposite():
|
||||
self._encodeComponents(glyph)
|
||||
elif glyph.isVarComposite():
|
||||
raise NotImplementedError
|
||||
else:
|
||||
self._encodeCoordinates(glyph)
|
||||
self._encodeOverlapSimpleFlag(glyph, glyphID)
|
||||
|
@ -843,23 +843,6 @@ def _instantiateGvarGlyph(
|
||||
if defaultDeltas:
|
||||
coordinates += _g_l_y_f.GlyphCoordinates(defaultDeltas)
|
||||
|
||||
glyph = glyf[glyphname]
|
||||
if glyph.isVarComposite():
|
||||
for component in glyph.components:
|
||||
newLocation = {}
|
||||
for tag, loc in component.location.items():
|
||||
if tag not in axisLimits:
|
||||
newLocation[tag] = loc
|
||||
continue
|
||||
if component.flags & _g_l_y_f.VarComponentFlags.AXES_HAVE_VARIATION:
|
||||
raise NotImplementedError(
|
||||
"Instancing accross VarComposite axes with variation is not supported."
|
||||
)
|
||||
limits = axisLimits[tag]
|
||||
loc = limits.renormalizeValue(loc, extrapolate=False)
|
||||
newLocation[tag] = loc
|
||||
component.location = newLocation
|
||||
|
||||
# _setCoordinates also sets the hmtx/vmtx advance widths and sidebearings from
|
||||
# the four phantom points and glyph bounding boxes.
|
||||
# We call it unconditionally even if a glyph has no variations or no deltas are
|
||||
@ -908,11 +891,9 @@ def instantiateGvar(varfont, axisLimits, optimize=True):
|
||||
glyphnames = sorted(
|
||||
glyf.glyphOrder,
|
||||
key=lambda name: (
|
||||
(
|
||||
glyf[name].getCompositeMaxpValues(glyf).maxComponentDepth
|
||||
if glyf[name].isComposite() or glyf[name].isVarComposite()
|
||||
else 0
|
||||
),
|
||||
glyf[name].getCompositeMaxpValues(glyf).maxComponentDepth
|
||||
if glyf[name].isComposite()
|
||||
else 0,
|
||||
name,
|
||||
),
|
||||
)
|
||||
|
@ -199,11 +199,9 @@ def instantiateVariableFont(varfont, location, inplace=False, overlap=True):
|
||||
glyphnames = sorted(
|
||||
gvar.variations.keys(),
|
||||
key=lambda name: (
|
||||
(
|
||||
glyf[name].getCompositeMaxpValues(glyf).maxComponentDepth
|
||||
if glyf[name].isComposite() or glyf[name].isVarComposite()
|
||||
else 0
|
||||
),
|
||||
glyf[name].getCompositeMaxpValues(glyf).maxComponentDepth
|
||||
if glyf[name].isComposite()
|
||||
else 0,
|
||||
name,
|
||||
),
|
||||
)
|
||||
@ -307,9 +305,9 @@ def instantiateVariableFont(varfont, location, inplace=False, overlap=True):
|
||||
if applies:
|
||||
assert record.FeatureTableSubstitution.Version == 0x00010000
|
||||
for rec in record.FeatureTableSubstitution.SubstitutionRecord:
|
||||
table.FeatureList.FeatureRecord[rec.FeatureIndex].Feature = (
|
||||
rec.Feature
|
||||
)
|
||||
table.FeatureList.FeatureRecord[
|
||||
rec.FeatureIndex
|
||||
].Feature = rec.Feature
|
||||
break
|
||||
del table.FeatureVariations
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user