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: if validateGlyphFormat and self.font["head"].glyphDataFormat == 0:
for name, g in glyphs.items(): for name, g in glyphs.items():
if g.isVarComposite(): if g.numberOfContours > 0 and any(f & flagCubic for f in g.flags):
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):
raise ValueError( raise ValueError(
f"Glyph {name!r} has cubic Bezier outlines, but glyphDataFormat=0; " f"Glyph {name!r} has cubic Bezier outlines, but glyphDataFormat=0; "
"either convert to quadratics with cu2qu or set glyphDataFormat=1." "either convert to quadratics with cu2qu or set glyphDataFormat=1."

View File

@ -225,7 +225,7 @@ def merge(self, m, tables):
g.removeHinting() g.removeHinting()
# Expand composite glyphs to load their # Expand composite glyphs to load their
# composite glyph names. # composite glyph names.
if g.isComposite() or g.isVarComposite(): if g.isComposite():
g.expand(table) g.expand(table)
return DefaultTable.merge(self, m, tables) return DefaultTable.merge(self, m, tables)

View File

@ -11,7 +11,6 @@ from fontTools.cffLib import VarStoreData
import fontTools.cffLib.specializer as cffSpecializer import fontTools.cffLib.specializer as cffSpecializer
from fontTools.varLib import builder # for VarData.calculateNumShorts from fontTools.varLib import builder # for VarData.calculateNumShorts
from fontTools.misc.fixedTools import otRound from fontTools.misc.fixedTools import otRound
from fontTools.ttLib.tables._g_l_y_f import VarComponentFlags
__all__ = ["scale_upem", "ScalerVisitor"] __all__ = ["scale_upem", "ScalerVisitor"]
@ -123,13 +122,6 @@ def visit(visitor, obj, attr, glyphs):
component.y = visitor.scale(component.y) component.y = visitor.scale(component.y)
continue 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"): if hasattr(g, "coordinates"):
coordinates = g.coordinates coordinates = g.coordinates
for i, (x, y) in enumerate(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") @ScalerVisitor.register_attr(ttLib.getTableClass("gvar"), "variations")
def visit(visitor, obj, attr, variations): def visit(visitor, obj, attr, variations):
# VarComposites are a pain to handle :-(
glyfTable = visitor.font["glyf"] glyfTable = visitor.font["glyf"]
for glyphName, varlist in variations.items(): for glyphName, varlist in variations.items():
glyph = glyfTable[glyphName] glyph = glyfTable[glyphName]
isVarComposite = glyph.isVarComposite()
for var in varlist: for var in varlist:
coordinates = var.coordinates coordinates = var.coordinates
for i, xy in enumerate(coordinates):
if not isVarComposite: if xy is None:
for i, xy in enumerate(coordinates): continue
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]
coordinates[i] = visitor.scale(xy[0]), visitor.scale(xy[1]) 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 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: else:
coords, endPts, flags = glyph.getCoordinates(self) coords, endPts, flags = glyph.getCoordinates(self)
coords = coords.copy() coords = coords.copy()
@ -492,10 +469,6 @@ class table__g_l_y_f(DefaultTable.DefaultTable):
for p, comp in zip(coord, glyph.components): for p, comp in zip(coord, glyph.components):
if hasattr(comp, "x"): if hasattr(comp, "x"):
comp.x, comp.y = p comp.x, comp.y = p
elif glyph.isVarComposite():
for comp in glyph.components:
coord = comp.setCoordinates(coord)
assert not coord
elif glyph.numberOfContours == 0: elif glyph.numberOfContours == 0:
assert len(coord) == 0 assert len(coord) == 0
else: else:
@ -737,8 +710,6 @@ class Glyph(object):
return return
if self.isComposite(): if self.isComposite():
self.decompileComponents(data, glyfTable) self.decompileComponents(data, glyfTable)
elif self.isVarComposite():
self.decompileVarComponents(data, glyfTable)
else: else:
self.decompileCoordinates(data) self.decompileCoordinates(data)
@ -758,8 +729,6 @@ class Glyph(object):
data = sstruct.pack(glyphHeaderFormat, self) data = sstruct.pack(glyphHeaderFormat, self)
if self.isComposite(): if self.isComposite():
data = data + self.compileComponents(glyfTable) data = data + self.compileComponents(glyfTable)
elif self.isVarComposite():
data = data + self.compileVarComponents(glyfTable)
else: else:
data = data + self.compileCoordinates() data = data + self.compileCoordinates()
return data return data
@ -769,10 +738,6 @@ class Glyph(object):
for compo in self.components: for compo in self.components:
compo.toXML(writer, ttFont) compo.toXML(writer, ttFont)
haveInstructions = hasattr(self, "program") haveInstructions = hasattr(self, "program")
elif self.isVarComposite():
for compo in self.components:
compo.toXML(writer, ttFont)
haveInstructions = False
else: else:
last = 0 last = 0
for i in range(self.numberOfContours): for i in range(self.numberOfContours):
@ -842,15 +807,6 @@ class Glyph(object):
component = GlyphComponent() component = GlyphComponent()
self.components.append(component) self.components.append(component)
component.fromXML(name, attrs, content, ttFont) 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": elif name == "instructions":
self.program = ttProgram.Program() self.program = ttProgram.Program()
for element in content: for element in content:
@ -860,7 +816,7 @@ class Glyph(object):
self.program.fromXML(name, attrs, content, ttFont) self.program.fromXML(name, attrs, content, ttFont)
def getCompositeMaxpValues(self, glyfTable, maxComponentDepth=1): def getCompositeMaxpValues(self, glyfTable, maxComponentDepth=1):
assert self.isComposite() or self.isVarComposite() assert self.isComposite()
nContours = 0 nContours = 0
nPoints = 0 nPoints = 0
initialMaxComponentDepth = maxComponentDepth initialMaxComponentDepth = maxComponentDepth
@ -904,13 +860,6 @@ class Glyph(object):
len(data), 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): def decompileCoordinates(self, data):
endPtsOfContours = array.array("H") endPtsOfContours = array.array("H")
endPtsOfContours.frombytes(data[: 2 * self.numberOfContours]) endPtsOfContours.frombytes(data[: 2 * self.numberOfContours])
@ -1027,9 +976,6 @@ 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):
return b"".join(c.compile(glyfTable) for c in self.components)
def compileCoordinates(self): def compileCoordinates(self):
assert len(self.coordinates) == len(self.flags) assert len(self.coordinates) == len(self.flags)
data = [] data = []
@ -1231,13 +1177,6 @@ class Glyph(object):
else: else:
return self.numberOfContours == -1 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): def getCoordinates(self, glyfTable):
"""Return the coordinates, end points and flags """Return the coordinates, end points and flags
@ -1308,8 +1247,6 @@ class Glyph(object):
allCoords.extend(coordinates) allCoords.extend(coordinates)
allFlags.extend(flags) allFlags.extend(flags)
return allCoords, allEndPts, allFlags return allCoords, allEndPts, allFlags
elif self.isVarComposite():
raise NotImplementedError("use TTGlyphSet to draw VarComposite glyphs")
else: else:
return GlyphCoordinates(), [], bytearray() return GlyphCoordinates(), [], bytearray()
@ -1319,12 +1256,8 @@ class Glyph(object):
This method can be used on simple glyphs (in which case it returns an This method can be used on simple glyphs (in which case it returns an
empty list) or composite glyphs. 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 not hasattr(self, "data"):
if self.isComposite() or self.isVarComposite(): if self.isComposite():
return [c.glyphName for c in self.components] return [c.glyphName for c in self.components]
else: else:
return [] return []
@ -1367,8 +1300,6 @@ class Glyph(object):
if self.isComposite(): if self.isComposite():
if hasattr(self, "program"): if hasattr(self, "program"):
del self.program del self.program
elif self.isVarComposite():
pass # Doesn't have hinting
else: else:
self.program = ttProgram.Program() self.program = ttProgram.Program()
self.program.fromBytecode([]) self.program.fromBytecode([])
@ -1450,13 +1381,6 @@ class Glyph(object):
i += 2 + instructionLen i += 2 + instructionLen
# Remove padding # Remove padding
data = data[:i] 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 self.data = data
@ -1942,391 +1866,6 @@ class GlyphComponent(object):
return result if result is NotImplemented else not result 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): class GlyphCoordinates(object):
"""A list of glyph coordinates. """A list of glyph coordinates.

View File

@ -245,11 +245,6 @@ class table__g_v_a_r(DefaultTable.DefaultTable):
if glyph.isComposite(): if glyph.isComposite():
return len(glyph.components) + NUM_PHANTOM_POINTS 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: else:
# Empty glyphs (eg. space, nonmarkingreturn) have no "coordinates" attribute. # Empty glyphs (eg. space, nonmarkingreturn) have no "coordinates" attribute.
return len(getattr(glyph, "coordinates", [])) + NUM_PHANTOM_POINTS return len(getattr(glyph, "coordinates", [])) + NUM_PHANTOM_POINTS

View File

@ -178,10 +178,6 @@ class _TTGlyphGlyf(_TTGlyph):
if depth: if depth:
offset = 0 # Offset should only apply at top-level 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) glyph.draw(pen, self.glyphSet.glyfTable, offset)
def drawPoints(self, pen): def drawPoints(self, pen):
@ -194,35 +190,8 @@ class _TTGlyphGlyf(_TTGlyph):
if depth: if depth:
offset = 0 # Offset should only apply at top-level 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) 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): def _getGlyphAndOffset(self):
if self.glyphSet.location and self.glyphSet.gvarTable is not None: if self.glyphSet.location and self.glyphSet.gvarTable is not None:
glyph = self._getGlyphInstance() glyph = self._getGlyphInstance()
@ -300,11 +269,6 @@ def _setCoordinates(glyph, coord, glyfTable, *, recalcBounds=True):
for p, comp in zip(coord, glyph.components): for p, comp in zip(coord, glyph.components):
if hasattr(comp, "x"): if hasattr(comp, "x"):
comp.x, comp.y = p 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: elif glyph.numberOfContours == 0:
assert len(coord) == 0 assert len(coord) == 0
else: else:

View File

@ -1017,8 +1017,6 @@ class WOFF2GlyfTable(getTableClass("glyf")):
return return
elif glyph.isComposite(): elif glyph.isComposite():
self._encodeComponents(glyph) self._encodeComponents(glyph)
elif glyph.isVarComposite():
raise NotImplementedError
else: else:
self._encodeCoordinates(glyph) self._encodeCoordinates(glyph)
self._encodeOverlapSimpleFlag(glyph, glyphID) self._encodeOverlapSimpleFlag(glyph, glyphID)

View File

@ -843,23 +843,6 @@ def _instantiateGvarGlyph(
if defaultDeltas: if defaultDeltas:
coordinates += _g_l_y_f.GlyphCoordinates(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 # _setCoordinates also sets the hmtx/vmtx advance widths and sidebearings from
# the four phantom points and glyph bounding boxes. # the four phantom points and glyph bounding boxes.
# We call it unconditionally even if a glyph has no variations or no deltas are # 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( glyphnames = sorted(
glyf.glyphOrder, glyf.glyphOrder,
key=lambda name: ( key=lambda name: (
( glyf[name].getCompositeMaxpValues(glyf).maxComponentDepth
glyf[name].getCompositeMaxpValues(glyf).maxComponentDepth if glyf[name].isComposite()
if glyf[name].isComposite() or glyf[name].isVarComposite() else 0,
else 0
),
name, name,
), ),
) )

View File

@ -199,11 +199,9 @@ def instantiateVariableFont(varfont, location, inplace=False, overlap=True):
glyphnames = sorted( glyphnames = sorted(
gvar.variations.keys(), gvar.variations.keys(),
key=lambda name: ( key=lambda name: (
( glyf[name].getCompositeMaxpValues(glyf).maxComponentDepth
glyf[name].getCompositeMaxpValues(glyf).maxComponentDepth if glyf[name].isComposite()
if glyf[name].isComposite() or glyf[name].isVarComposite() else 0,
else 0
),
name, name,
), ),
) )
@ -307,9 +305,9 @@ def instantiateVariableFont(varfont, location, inplace=False, overlap=True):
if applies: if applies:
assert record.FeatureTableSubstitution.Version == 0x00010000 assert record.FeatureTableSubstitution.Version == 0x00010000
for rec in record.FeatureTableSubstitution.SubstitutionRecord: for rec in record.FeatureTableSubstitution.SubstitutionRecord:
table.FeatureList.FeatureRecord[rec.FeatureIndex].Feature = ( table.FeatureList.FeatureRecord[
rec.Feature rec.FeatureIndex
) ].Feature = rec.Feature
break break
del table.FeatureVariations del table.FeatureVariations