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:
Behdad Esfahbod 2024-02-06 15:30:27 -07:00
parent ec78b572c9
commit bcd5e4c216
9 changed files with 16 additions and 594 deletions

View File

@ -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."

View File

@ -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)

View File

@ -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])

View File

@ -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.

View File

@ -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

View File

@ -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:

View File

@ -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)

View File

@ -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,
),
)

View File

@ -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