From 672f8e6e23eed2f97d6e78d73eeb5dd01f1de97a Mon Sep 17 00:00:00 2001 From: Just van Rossum Date: Sun, 21 Apr 2024 22:17:30 +0200 Subject: [PATCH 01/25] Update to VARC table --- src/fontra_compile/builder.py | 301 +++++++++++++++++++++++----------- 1 file changed, 204 insertions(+), 97 deletions(-) diff --git a/src/fontra_compile/builder.py b/src/fontra_compile/builder.py index caaa9fa..5276e50 100644 --- a/src/fontra_compile/builder.py +++ b/src/fontra_compile/builder.py @@ -1,3 +1,4 @@ +from dataclasses import dataclass, field from types import SimpleNamespace from typing import Any @@ -8,22 +9,43 @@ from fontTools.misc.fixedTools import floatToFixed as fl2fi from fontTools.misc.timeTools import timestampNow from fontTools.misc.transform import DecomposedTransform +from fontTools.misc.vector import Vector from fontTools.pens.ttGlyphPen import TTGlyphPointPen -from fontTools.ttLib import TTFont -from fontTools.ttLib.tables._g_l_y_f import ( - VAR_COMPONENT_TRANSFORM_MAPPING, - Glyph, - GlyphCoordinates, - GlyphVarComponent, - VarComponentFlags, -) +from fontTools.ttLib import TTFont, newTable +from fontTools.ttLib.tables import otTables as ot +from fontTools.ttLib.tables._g_l_y_f import Glyph, GlyphCoordinates from fontTools.ttLib.tables._g_v_a_r import TupleVariation +from fontTools.ttLib.tables.otTables import VAR_TRANSFORM_MAPPING, VarComponentFlags from fontTools.varLib.models import ( VariationModel, VariationModelError, normalizeLocation, piecewiseLinearMap, ) +from fontTools.varLib.multiVarStore import OnlineMultiVarStoreBuilder + +# If a component transformation has variations in any of the following fields, the +# component can not be a classic component, and should be compiled as a variable +# component, even if there are no axis variations +VARCO_IF_VARYING = { + "rotation", + "scaleX", + "scaleY", + "skewX", + "skewY", + "tCenterX", + "tCenterY", +} + + +@dataclass +class GlyphInfo: + glyph: Glyph + xAdvance: int = 500 + variations: list = field(default_factory=list) + variableComponents: list = field(default_factory=list) + localAxisTags: set = field(default_factory=set) + model: VariationModel | None = None class Builder: @@ -53,11 +75,8 @@ async def setup(self) -> None: self.cachedSourceGlyphs: dict[str, VariableGlyph] = {} self.cachedComponentBaseInfo: dict = {} - self.glyphs: dict[str, Glyph] = {} + self.glyphInfos: dict[str, Glyph] = {} self.cmap: dict[int, str] = {} - self.xAdvances: dict[str, int] = {} - self.variations: dict[str, list[TupleVariation]] = {} - self.localAxisTags: set[str] = set() async def build(self) -> TTFont: await self.buildGlyphs() @@ -74,13 +93,12 @@ async def getSourceGlyph( return sourceGlyph def ensureGlyphDependency(self, glyphName: str) -> None: - if glyphName not in self.glyphs and glyphName not in self.glyphOrder: + if glyphName not in self.glyphInfos and glyphName not in self.glyphOrder: self.glyphOrder.append(glyphName) async def buildGlyphs(self) -> None: for glyphName in self.glyphOrder: codePoints = self.glyphMap.get(glyphName) - self.xAdvances[glyphName] = 500 glyphInfo = None @@ -92,18 +110,12 @@ async def buildGlyphs(self) -> None: raise except (ValueError, VariationModelError) as e: # InterpolationError print("warning", glyphName, repr(e)) # TODO: use logging - else: - self.xAdvances[glyphName] = max(glyphInfo.xAdvance, 0) - if glyphInfo.variations: - self.variations[glyphName] = glyphInfo.variations - self.glyphs[glyphName] = glyphInfo.glyph - self.localAxisTags.update(glyphInfo.localAxisTags) if glyphInfo is None: # make .notdef based on UPM - glyph = TTGlyphPointPen(None).glyph() - self.xAdvances[glyphName] = 500 - self.glyphs[glyphName] = glyph + glyphInfo = GlyphInfo(glyph=TTGlyphPointPen(None).glyph(), xAdvance=500) + + self.glyphInfos[glyphName] = glyphInfo async def buildOneGlyph(self, glyphName: str) -> SimpleNamespace: glyph = await self.getSourceGlyph(glyphName, False) @@ -119,6 +131,7 @@ async def buildOneGlyph(self, glyphName: str) -> SimpleNamespace: defaultGlyph = None componentInfo = await self.collectComponentInfo(glyph) + firstSourcePath = None glyphSources = filterActiveSources(glyph.sources) @@ -134,37 +147,17 @@ async def buildOneGlyph(self, glyphName: str) -> SimpleNamespace: coordinates = GlyphCoordinates() - if componentInfo: - for compoInfo in componentInfo: - location = sortedDict( - mapDictKeys( - { - axisName: values[sourceIndex] - for axisName, values in compoInfo.location.items() - }, - compoInfo.baseAxisTags, - ) - ) - coordinates.extend(getLocationCoords(location, compoInfo.flags)) - - transform = { - attrName: values[sourceIndex] - for attrName, values in compoInfo.transform.items() - } - transform = DecomposedTransform(**transform) - coordinates.extend(getTransformCoords(transform, compoInfo.flags)) + assert isinstance(sourceGlyph.path, PackedPath) + coordinates.array.extend( + sourceGlyph.path.coordinates + ) # shortcut via ._a array + if firstSourcePath is None: + firstSourcePath = sourceGlyph.path else: - assert isinstance(sourceGlyph.path, PackedPath) - coordinates.array.extend( - sourceGlyph.path.coordinates - ) # shortcut via ._a array - if firstSourcePath is None: - firstSourcePath = sourceGlyph.path - else: - if firstSourcePath.contourInfo != sourceGlyph.path.contourInfo: - raise ValueError( - f"contours for source {source.name} of {glyphName} are not compatible" - ) + if firstSourcePath.contourInfo != sourceGlyph.path.contourInfo: + raise ValueError( + f"contours for source {source.name} of {glyphName} are not compatible" + ) # phantom points coordinates.append((0, 0)) coordinates.append((sourceGlyph.xAdvance, 0)) @@ -172,6 +165,8 @@ async def buildOneGlyph(self, glyphName: str) -> SimpleNamespace: coordinates.append((0, 0)) sourceCoordinates.append(coordinates) + locations = [mapDictKeys(s, axisTags) for s in locations] + model = VariationModel(locations) # XXX axis order! numPoints = len(sourceCoordinates[0]) @@ -186,38 +181,23 @@ async def buildOneGlyph(self, glyphName: str) -> SimpleNamespace: for d in deltas: d.toInt() ensureWordRange(d) - supports = [mapDictKeys(s, axisTags) for s in supports] + # supports = [mapDictKeys(s, axisTags) for s in supports] variations = [TupleVariation(s, d) for s, d in zip(supports, deltas)] assert defaultGlyph is not None - if componentInfo: - ttGlyph = Glyph() - ttGlyph.numberOfContours = -2 - ttGlyph.components = [] - ttGlyph.xMin = ttGlyph.yMin = ttGlyph.xMax = ttGlyph.yMax = 0 - for compo, compoInfo in zip(defaultGlyph.components, componentInfo): - ttCompo = GlyphVarComponent() - ttCompo.flags = compoInfo.flags - ttCompo.glyphName = compo.name - ttCompo.transform = compo.transformation - normLoc = normalizeLocation(compo.location, compoInfo.baseAxisDict) - normLoc = filterDict(normLoc, compoInfo.location) - ttCompo.location = sortedDict( - mapDictKeys(normLoc, compoInfo.baseAxisTags) - ) - ttGlyph.components.append(ttCompo) - else: - ttGlyphPen = TTGlyphPointPen(None) - defaultGlyph.path.drawPoints(ttGlyphPen) - ttGlyph = ttGlyphPen.glyph() + ttGlyphPen = TTGlyphPointPen(None) + defaultGlyph.path.drawPoints(ttGlyphPen) + ttGlyph = ttGlyphPen.glyph() - return SimpleNamespace( + return GlyphInfo( glyph=ttGlyph, - xAdvance=defaultGlyph.xAdvance, + xAdvance=max(defaultGlyph.xAdvance, 0), variations=variations, + variableComponents=componentInfo, localAxisTags=set(localAxisTags.values()), + model=model, ) async def collectComponentInfo(self, glyph: VariableGlyph) -> list[SimpleNamespace]: @@ -238,9 +218,7 @@ async def collectComponentInfo(self, glyph: VariableGlyph) -> list[SimpleNamespa components = [ SimpleNamespace( name=compo.name, - transform={ - attrName: [] for attrName in VAR_COMPONENT_TRANSFORM_MAPPING - }, + transform={attrName: [] for attrName in VAR_TRANSFORM_MAPPING}, location={axisName: [] for axisName in axisNames}, **await self.getComponentBaseInfo(compo.name), ) @@ -262,7 +240,7 @@ async def collectComponentInfo(self, glyph: VariableGlyph) -> list[SimpleNamespa f"components not compatible in {glyph.name}: " f"{compo.name} vs. {compoInfo.name}" ) - for attrName in VAR_COMPONENT_TRANSFORM_MAPPING: + for attrName in VAR_TRANSFORM_MAPPING: compoInfo.transform[attrName].append( getattr(compo.transformation, attrName) ) @@ -274,17 +252,25 @@ async def collectComponentInfo(self, glyph: VariableGlyph) -> list[SimpleNamespa numSources = len(glyphSources) for compoInfo in components: - flags = ( - 0 - if compoInfo.respondsToGlobalAxes - else VarComponentFlags.RESET_UNSPECIFIED_AXES - ) + isVariableComponent = bool(compoInfo.location) + + flags = 0 - for attrName, fieldInfo in VAR_COMPONENT_TRANSFORM_MAPPING.items(): + if not compoInfo.respondsToGlobalAxes: + flags |= VarComponentFlags.RESET_UNSPECIFIED_AXES + + if isVariableComponent: + flags |= VarComponentFlags.HAVE_AXES + + for attrName, fieldInfo in VAR_TRANSFORM_MAPPING.items(): values = compoInfo.transform[attrName] - firstValue = values[0] - if any(v != firstValue or v != fieldInfo.defaultValue for v in values): + if any(v != fieldInfo.defaultValue for v in values): flags |= fieldInfo.flag + firstValue = values[0] + if any(v != firstValue for v in values[1:]): + flags |= VarComponentFlags.TRANSFORM_HAS_VARIATION + if attrName in VARCO_IF_VARYING: + isVariableComponent = True # Filter out unknown/unused axes compoInfo.location = { @@ -292,11 +278,12 @@ async def collectComponentInfo(self, glyph: VariableGlyph) -> list[SimpleNamespa for axisName, values in compoInfo.location.items() if values } + axesAtDefault = [] for axisName, values in compoInfo.location.items(): firstValue = values[0] if any(v != firstValue for v in values[1:]): - flags |= VarComponentFlags.AXES_HAVE_VARIATION + flags |= VarComponentFlags.AXIS_VALUES_HAVE_VARIATION elif firstValue == 0: axesAtDefault.append(axisName) @@ -309,6 +296,7 @@ async def collectComponentInfo(self, glyph: VariableGlyph) -> list[SimpleNamespa compoInfo.location[axisName] = [0] * numSources compoInfo.flags = flags + compoInfo.isVariableComponent = isVariableComponent self.ensureGlyphDependency(compoInfo.name) @@ -356,22 +344,134 @@ async def buildFont(self) -> TTFont: builder.updateHead(created=timestampNow(), modified=timestampNow()) builder.setupGlyphOrder(self.glyphOrder) builder.setupNameTable(dict()) - builder.setupGlyf(self.glyphs) - if self.globalAxes or self.localAxisTags: - dsAxes = makeDSAxes(self.globalAxes, sorted(self.localAxisTags)) + builder.setupGlyf(getGlyphInfoAttributes(self.glyphInfos, "glyph")) + + localAxisTags = set() + for glyphInfo in self.glyphInfos.values(): + localAxisTags.update(glyphInfo.localAxisTags) + + axisTags = [] + + if self.globalAxes or localAxisTags: + dsAxes = makeDSAxes(self.globalAxes, sorted(localAxisTags)) + axisTags = [axis.tag for axis in dsAxes] builder.setupFvar(dsAxes, []) if any(axis.map for axis in dsAxes): builder.setupAvar(dsAxes) - if self.variations: - builder.setupGvar(self.variations) + + variations = getGlyphInfoAttributes(self.glyphInfos, "variations") + if variations: + builder.setupGvar(variations) + + if any(glyphInfo.variableComponents for glyphInfo in self.glyphInfos.values()): + varcTable = self.buildVARC(axisTags) + builder.font["VARC"] = varcTable + builder.setupHorizontalHeader() - builder.setupHorizontalMetrics(addLSB(builder.font["glyf"], self.xAdvances)) + builder.setupHorizontalMetrics( + addLSB( + builder.font["glyf"], + getGlyphInfoAttributes(self.glyphInfos, "xAdvance"), + ) + ) builder.setupCharacterMap(self.cmap) builder.setupOS2() builder.setupPost() return builder.font + def buildVARC(self, axisTags): + axisIndicesMapping = {} + storeBuilder = OnlineMultiVarStoreBuilder(axisTags) + + glyphNames = [ + glyphName + for glyphName in self.glyphOrder + if self.glyphInfos[glyphName].variableComponents + ] + coverage = ot.Coverage() + coverage.glyphs = glyphNames + + varcSubtable = ot.VARC() + varcSubtable.Version = 0x00010000 + varcSubtable.Coverage = coverage + + variableComposites = [] + + for glyphName in varcSubtable.Coverage.glyphs: + components = [] + model = self.glyphInfos[glyphName].model + storeBuilder.setModel(model) + + for compoInfo in self.glyphInfos[glyphName].variableComponents: + compo = ot.VarComponent() + compo.flags = compoInfo.flags + compo.glyphName = compoInfo.name + compo.transform = DecomposedTransform( + **{k: v[0] for k, v in compoInfo.transform.items()} + ) + + if compoInfo.flags & VarComponentFlags.TRANSFORM_HAS_VARIATION: + transformValues = [] + for fieldName, fieldMappingValues in VAR_TRANSFORM_MAPPING.items(): + if fieldMappingValues.flag & compoInfo.flags: + + transformValues.append( + [ + fl2fi( + v / fieldMappingValues.scale, + fieldMappingValues.fractionalBits, + ) + for v in compoInfo.transform[fieldName] + ] + ) + + masterValues = [Vector(vec) for vec in zip(*transformValues)] + assert masterValues + + _, varIdx = storeBuilder.storeMasters(masterValues) + compo.transformVarIndex = varIdx + + if compoInfo.flags & VarComponentFlags.HAVE_AXES: + assert compoInfo.location + location = mapDictKeys(compoInfo.location, compoInfo.baseAxisTags) + axisIndices = tuple(axisTags.index(k) for k in location) + axisIndicesIndex = axisIndicesMapping.get(axisIndices) + if axisIndicesIndex is None: + axisIndicesIndex = len(axisIndicesMapping) + axisIndicesMapping[axisIndices] = axisIndicesIndex + + compo.axisIndicesIndex = axisIndicesIndex + compo.axisValues = [v[0] for v in location.values()] + + if compoInfo.flags & VarComponentFlags.AXIS_VALUES_HAVE_VARIATION: + locationValues = [ + [fl2fi(v, 14) for v in values] + for values in location.values() + ] + masterValues = [Vector(vec) for vec in zip(*locationValues)] + _, varIdx = storeBuilder.storeMasters(masterValues) + compo.axisValuesVarIndex = varIdx + + components.append(compo) + + compositeGlyph = ot.VarCompositeGlyph(components) + variableComposites.append(compositeGlyph) + + compoGlyphs = ot.VarCompositeGlyphs() + compoGlyphs.VarCompositeGlyph = variableComposites + varcSubtable.VarCompositeGlyphs = compoGlyphs + + axisIndicesList = ot.AxisIndicesList() + axisIndicesList.Item = [list(k) for k in axisIndicesMapping.keys()] + varcSubtable.AxisIndicesList = axisIndicesList + + varcSubtable.MultiVarStore = storeBuilder.finish() + + varcTable = newTable("VARC") + varcTable.table = varcSubtable + return varcTable + def addLSB(glyfTable, metrics: dict[str, int]) -> dict[str, tuple[int, int]]: return { @@ -439,7 +539,7 @@ def makeLocalAxisTags(axisDict, globalAxes): axisTags = {} for name in axisDict: # Sort axis names, to match current Fontra and RoboCJK behavior. - # TOD: This should be changed to something more controllable. + # TODO: This should be changed to something more controllable. if name in globalAxes: continue numNames = len(axisTags) @@ -461,7 +561,7 @@ def filterDict(d, keys): def getLocationCoords(location, flags): coords = [] - if flags & VarComponentFlags.AXES_HAVE_VARIATION: + if flags & VarComponentFlags.AXIS_VALUES_HAVE_VARIATION: for tag, value in location.items(): coords.append((fl2fi(value, 14), 0)) return coords @@ -512,3 +612,10 @@ async def asyncAny(aiterable): if item: return True return False + + +def getGlyphInfoAttributes(glyphInfos, attrName): + return { + glyphName: getattr(glyphInfo, attrName) + for glyphName, glyphInfo in glyphInfos.items() + } From 21fe1b2337bb7c2bec6509423bd003196c5b8804 Mon Sep 17 00:00:00 2001 From: Just van Rossum Date: Sun, 21 Apr 2024 22:19:38 +0200 Subject: [PATCH 02/25] Use list comp --- src/fontra_compile/builder.py | 22 ++++++++++------------ 1 file changed, 10 insertions(+), 12 deletions(-) diff --git a/src/fontra_compile/builder.py b/src/fontra_compile/builder.py index 5276e50..f558593 100644 --- a/src/fontra_compile/builder.py +++ b/src/fontra_compile/builder.py @@ -412,19 +412,17 @@ def buildVARC(self, axisTags): ) if compoInfo.flags & VarComponentFlags.TRANSFORM_HAS_VARIATION: - transformValues = [] - for fieldName, fieldMappingValues in VAR_TRANSFORM_MAPPING.items(): - if fieldMappingValues.flag & compoInfo.flags: - - transformValues.append( - [ - fl2fi( - v / fieldMappingValues.scale, - fieldMappingValues.fractionalBits, - ) - for v in compoInfo.transform[fieldName] - ] + transformValues = [ + [ + fl2fi( + v / fieldMappingValues.scale, + fieldMappingValues.fractionalBits, ) + for v in compoInfo.transform[fieldName] + ] + for fieldName, fieldMappingValues in VAR_TRANSFORM_MAPPING.items() + if fieldMappingValues.flag & compoInfo.flags + ] masterValues = [Vector(vec) for vec in zip(*transformValues)] assert masterValues From d6543a038a4b1e2d41b36d068e9ad2fe39860736 Mon Sep 17 00:00:00 2001 From: Just van Rossum Date: Sun, 21 Apr 2024 22:23:19 +0200 Subject: [PATCH 03/25] Cleanup --- src/fontra_compile/builder.py | 41 ----------------------------------- 1 file changed, 41 deletions(-) diff --git a/src/fontra_compile/builder.py b/src/fontra_compile/builder.py index f558593..d105e04 100644 --- a/src/fontra_compile/builder.py +++ b/src/fontra_compile/builder.py @@ -181,7 +181,6 @@ async def buildOneGlyph(self, glyphName: str) -> SimpleNamespace: for d in deltas: d.toInt() ensureWordRange(d) - # supports = [mapDictKeys(s, axisTags) for s in supports] variations = [TupleVariation(s, d) for s, d in zip(supports, deltas)] @@ -549,52 +548,12 @@ def mapDictKeys(d, mapping): return {mapping[k]: v for k, v in d.items()} -def sortedDict(d): - return dict(sorted(d.items())) - - -def filterDict(d, keys): - return {k: v for k, v in d.items() if k in keys} - - -def getLocationCoords(location, flags): - coords = [] - if flags & VarComponentFlags.AXIS_VALUES_HAVE_VARIATION: - for tag, value in location.items(): - coords.append((fl2fi(value, 14), 0)) - return coords - - def ensureWordRange(d): for v in d.array: if not (-0x8000 <= v < 0x8000): raise ValueError("delta value out of range") -def getTransformCoords(transform, flags): - # This is mostly taken from _g_l_y_f.py, would be nice if we could - # reuse that code somehow. - coords = [] - if flags & ( - VarComponentFlags.HAVE_TRANSLATE_X | VarComponentFlags.HAVE_TRANSLATE_Y - ): - coords.append((transform.translateX, transform.translateY)) - if flags & VarComponentFlags.HAVE_ROTATION: - coords.append((fl2fi(transform.rotation / 180, 12), 0)) - if flags & (VarComponentFlags.HAVE_SCALE_X | VarComponentFlags.HAVE_SCALE_Y): - coords.append((fl2fi(transform.scaleX, 10), fl2fi(transform.scaleY, 10))) - if flags & (VarComponentFlags.HAVE_SKEW_X | VarComponentFlags.HAVE_SKEW_Y): - coords.append( - ( - fl2fi(transform.skewX / -180, 12), - fl2fi(transform.skewY / 180, 12), - ) - ) - if flags & (VarComponentFlags.HAVE_TCENTER_X | VarComponentFlags.HAVE_TCENTER_Y): - coords.append((transform.tCenterX, transform.tCenterY)) - return coords - - def getComponentBaseNames(glyph): glyphSources = filterActiveSources(glyph.sources) firstSourceGlyph = glyph.layers[glyphSources[0].layerName].glyph From ebd932bb497598a961f0f95b12c7e79fb1c13012 Mon Sep 17 00:00:00 2001 From: Just van Rossum Date: Sun, 21 Apr 2024 22:47:03 +0200 Subject: [PATCH 04/25] Make axis indices order deterministic --- src/fontra_compile/builder.py | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/src/fontra_compile/builder.py b/src/fontra_compile/builder.py index d105e04..21c5c04 100644 --- a/src/fontra_compile/builder.py +++ b/src/fontra_compile/builder.py @@ -431,20 +431,21 @@ def buildVARC(self, axisTags): if compoInfo.flags & VarComponentFlags.HAVE_AXES: assert compoInfo.location - location = mapDictKeys(compoInfo.location, compoInfo.baseAxisTags) - axisIndices = tuple(axisTags.index(k) for k in location) + location = sorted( + mapDictKeys(compoInfo.location, compoInfo.baseAxisTags).items() + ) + axisIndices = tuple(axisTags.index(k) for k, v in location) axisIndicesIndex = axisIndicesMapping.get(axisIndices) if axisIndicesIndex is None: axisIndicesIndex = len(axisIndicesMapping) axisIndicesMapping[axisIndices] = axisIndicesIndex compo.axisIndicesIndex = axisIndicesIndex - compo.axisValues = [v[0] for v in location.values()] + compo.axisValues = [v[0] for k, v in location] if compoInfo.flags & VarComponentFlags.AXIS_VALUES_HAVE_VARIATION: locationValues = [ - [fl2fi(v, 14) for v in values] - for values in location.values() + [fl2fi(v, 14) for v in values] for k, values in location ] masterValues = [Vector(vec) for vec in zip(*locationValues)] _, varIdx = storeBuilder.storeMasters(masterValues) From 512fef81c6462af0289cedd9d205e6993bd4ecc7 Mon Sep 17 00:00:00 2001 From: Just van Rossum Date: Sun, 21 Apr 2024 22:49:58 +0200 Subject: [PATCH 05/25] Adjust test data --- tests/data/MutatorSans.ttx | 2620 +++++++++++++++++++----------------- tests/data/figArnaud.ttx | 2619 ++++++++++++++++++----------------- 2 files changed, 2773 insertions(+), 2466 deletions(-) diff --git a/tests/data/MutatorSans.ttx b/tests/data/MutatorSans.ttx index afb24b3..d17c0eb 100644 --- a/tests/data/MutatorSans.ttx +++ b/tests/data/MutatorSans.ttx @@ -1,5 +1,5 @@ - + @@ -63,14 +63,14 @@ - + - - + + - + @@ -195,7 +195,7 @@ - + @@ -424,27 +424,9 @@ - - - - - - - - - - + - - - - - - - - - - + @@ -938,11 +920,14 @@ - - - - - + + + + + + + + @@ -1343,16 +1328,7 @@ - - - - - - - - - - + @@ -1368,16 +1344,7 @@ - - - - - - - - - - + @@ -1411,78 +1378,19 @@ - - - - - - - - - - + - - - - - - - - - - + - - - - - - - - - - + - - - - - - + - - - - - - - - - - + - - - - - - - - - - - - - - - - - - - + @@ -1565,6 +1473,247 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + @@ -1612,29 +1761,6 @@ - - - - - - - - - - - - - - - - - - - - - - - @@ -1658,6 +1784,29 @@ + + + + + + + + + + + + + + + + + + + + + + + @@ -1685,62 +1834,98 @@ - - - - + + + + - - - - - + + + + - - - - + + + - - - - + + + - - - + - + - + - - - + - - + + - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + @@ -1831,51 +2016,6 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - @@ -1924,43 +2064,6 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - @@ -1999,74 +2102,82 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + @@ -2096,6 +2207,35 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + @@ -2128,6 +2268,29 @@ + + + + + + + + + + + + + + + + + + + + + + + @@ -2174,29 +2337,6 @@ - - - - - - - - - - - - - - - - - - - - - - - @@ -2223,6 +2363,25 @@ + + + + + + + + + + + + + + + + + + + @@ -2261,25 +2420,6 @@ - - - - - - - - - - - - - - - - - - - @@ -2302,6 +2442,50 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + @@ -2390,50 +2574,6 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - @@ -2481,25 +2621,6 @@ - - - - - - - - - - - - - - - - - - - @@ -2520,46 +2641,46 @@ - - - - - - - - - - - - + + + + + + + + + + + - + - - - - - - - - - - - - - - - + + + + + + + + + + + + + + - + + + @@ -2579,6 +2700,25 @@ + + + + + + + + + + + + + + + + + + + @@ -2601,17 +2741,6 @@ - - - - - - - - - - - @@ -2623,6 +2752,17 @@ + + + + + + + + + + + @@ -2637,35 +2777,6 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - @@ -2695,6 +2806,35 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + @@ -2727,31 +2867,6 @@ - - - - - - - - - - - - - - - - - - - - - - - - - @@ -2777,6 +2892,31 @@ + + + + + + + + + + + + + + + + + + + + + + + + + @@ -2805,29 +2945,6 @@ - - - - - - - - - - - - - - - - - - - - - - - @@ -2851,6 +2968,29 @@ + + + + + + + + + + + + + + + + + + + + + + + @@ -2877,28 +3017,6 @@ - - - - - - - - - - - - - - - - - - - - - - @@ -2921,6 +3039,28 @@ + + + + + + + + + + + + + + + + + + + + + + @@ -2946,21 +3086,6 @@ - - - - - - - - - - - - - - - @@ -2976,6 +3101,21 @@ + + + + + + + + + + + + + + + @@ -2994,29 +3134,6 @@ - - - - - - - - - - - - - - - - - - - - - - - @@ -3040,6 +3157,29 @@ + + + + + + + + + + + + + + + + + + + + + + + @@ -3066,25 +3206,6 @@ - - - - - - - - - - - - - - - - - - - @@ -3104,6 +3225,25 @@ + + + + + + + + + + + + + + + + + + + @@ -3126,41 +3266,6 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - @@ -3196,6 +3301,41 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + @@ -3234,35 +3374,6 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - @@ -3292,6 +3403,35 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + @@ -3324,66 +3464,42 @@ - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + - - - - + + + + + + + + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - @@ -3420,6 +3536,42 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + @@ -3459,42 +3611,6 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - @@ -3531,6 +3647,42 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + @@ -3570,57 +3722,6 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - @@ -3668,7 +3769,58 @@ - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + @@ -3778,57 +3930,6 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - @@ -3880,6 +3981,57 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + @@ -4038,21 +4190,6 @@ - - - - - - - - - - - - - - - @@ -4068,6 +4205,21 @@ + + + + + + + + + + + + + + + @@ -4086,31 +4238,6 @@ - - - - - - - - - - - - - - - - - - - - - - - - - @@ -4136,6 +4263,31 @@ + + + + + + + + + + + + + + + + + + + + + + + + + @@ -4164,25 +4316,6 @@ - - - - - - - - - - - - - - - - - - - @@ -4203,50 +4336,46 @@ - - - - - - - - - - - - - + + + + + + + + + + + + - + - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + @@ -4270,6 +4399,29 @@ + + + + + + + + + + + + + + + + + + + + + + + @@ -4296,27 +4448,6 @@ - - - - - - - - - - - - - - - - - - - - - @@ -4338,6 +4469,27 @@ + + + + + + + + + + + + + + + + + + + + + @@ -4362,25 +4514,6 @@ - - - - - - - - - - - - - - - - - - - @@ -4400,6 +4533,25 @@ + + + + + + + + + + + + + + + + + + + @@ -4422,25 +4574,6 @@ - - - - - - - - - - - - - - - - - - - @@ -4460,6 +4593,25 @@ + + + + + + + + + + + + + + + + + + + @@ -4482,17 +4634,6 @@ - - - - - - - - - - - @@ -4504,6 +4645,17 @@ + + + + + + + + + + + @@ -4518,20 +4670,6 @@ - - - - - - - - - - - - - - @@ -4546,6 +4684,20 @@ + + + + + + + + + + + + + + @@ -4563,20 +4715,6 @@ - - - - - - - - - - - - - - @@ -4592,36 +4730,36 @@ - - - - - - - - + + + + + + + - + - - + - - - - - - - + + + + + + + - + + + @@ -4636,6 +4774,20 @@ + + + + + + + + + + + + + + @@ -4653,20 +4805,6 @@ - - - - - - - - - - - - - - @@ -4681,6 +4819,20 @@ + + + + + + + + + + + + + + @@ -4699,50 +4851,29 @@ - - - + + + - - - + - + - + - - - + - + - - - + - - - - - - - - - - - - - - - @@ -4758,6 +4889,21 @@ + + + + + + + + + + + + + + + @@ -4776,36 +4922,41 @@ + + + + + + + - - + + - - - + + - + - - - + + + - - - + + - + + + - - @@ -4817,17 +4968,6 @@ - - - - - - - - - - - @@ -4888,17 +5028,6 @@ - - - - - - - - - - - @@ -4910,6 +5039,17 @@ + + + + + + + + + + + @@ -4925,167 +5065,134 @@ - + - + - - - + - + - + - - - + - - + + - - - + - - - - + + + + - - - - - - - - + + + + - - - - - - - + + + - - - - - - - + + + - - - + - - - + + + - - - + - - + + - - - + - + - - + + - - + - - + + - - - - + + + - - - - + + + - - - + - + - + - - - + - + - - - + - + - + @@ -5103,21 +5210,10 @@ - + - - - - - - - - - - - diff --git a/tests/data/figArnaud.ttx b/tests/data/figArnaud.ttx index 811e5d8..741ee69 100644 --- a/tests/data/figArnaud.ttx +++ b/tests/data/figArnaud.ttx @@ -1,5 +1,5 @@ - + @@ -90,13 +90,13 @@ - + - - - + + + @@ -113,7 +113,7 @@ - + @@ -322,107 +322,25 @@ - - - - - - - - + - - - - - - - - + - - - - - - - - - - - + - - - - - - - - + - - - - - - - - - - - - - + - - - - - - - - + - - - - - - - - - + - - - - - - - - - - + - - - - - - - - + - - - - - - - - - + @@ -751,551 +669,125 @@ - - - - - - - + - - - - - - - - + - - - - - - - - + - - - - - - - - + - - - - - - - - + - - - - - - - + - - - - - - - + - - - - - - - - + - - - - - - - - + - - - - - - - - + - - - - - - - - + - - - - - - - - + - - - - - - - + - - - - - - - - - - + - - - - - - - - - + - - - - - - - - - + - - - - - - - - - - + - - - - - - - + - - - - - - - + - - - - - - - + - - - - - - - + - - - - - - - - + - - - - - - - + - - - - - - - + - - - - - - - - - - - + - - - - - - - - - - - + - - - - - - - - - - - - + - - - - - - - - - - - + - - - - - - - - - - - + - - - - - - - - - - - + - - - - - - - + - - - - - - - - + - - - - - - - + - - - - - - - - + - - - - - - - - + - - - - - - - + - - - - - - - - + - - - - - - - - - + - - - - - - - - + - - - - - - - - + - - - - - - - - - + - - - - - - - - + - - - - - - - + - - - - - - - - + - - - - - - - + - - - - - - - + - - - - - - - - + - - - - - - - + - - - - - - - + - - - - - - - - + - - - - - - - + - - - - - - - - + - - - - - - - - + - - - - - - - + - - - - - - - + - - - - - - - - - + - - - - - - - + - - - - - - - - + - - - - - - - - - + - - - - - - - + @@ -1486,6 +978,1117 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + @@ -1576,83 +2179,55 @@ - + - - - + - - - + - + - - - - + - - - - + - - - - - - - + - - - - - - - + - - - - - - @@ -1660,45 +2235,29 @@ - - - - - - - - - - - - - + - + - - - + - - @@ -1707,30 +2266,14 @@ - - - - - - - - - + - + - - - - - - - - @@ -1738,44 +2281,20 @@ - - - - - - - - - + - - - - - - - - - + - - - - - - - - @@ -1783,14 +2302,6 @@ - - - - - - - - @@ -1798,169 +2309,114 @@ - - - - - - - - - - + + - - - - + - + - - - - + - - - - - + - - - - - - + + - + - - - - - + - + - - - - - + - - - - - - - - - + - + - - - - - + - - - + - - - + - - - - + - - - - + - - - @@ -2920,786 +3376,541 @@ - - - + + + - - - - - - - - - - - + + + + - - - - - - - - + + + + - - - + + + - - - - - - - - - - - - + + + + - - - + + + - - - - - - + + + - - - - - - - - - - - + + + + - - - + + + - - - - - - - + + + - - - - - - - - - - - - + + + + - - - - - - - - + + + + - + - + - - - - - - + + + - - - - - - - - + + - - - - - - + - - - - - - - - - + + + + - - - + + + - - - - - - - + - + - - - - - - + + + - - - - - - + + + - - - - - - + + + - - - - - - + + + - - - - - - - + + + - - - - - - + + + - - - - - - - - - - - - - - + + + + - - - - - - - - - - + + + - - - - - - - - - - - - + + + + - - - - - - - - - - - + + + + - - - - - - - - - - + + + - - - - - - - - - - - + + + + - - - + + + - - - - + - - - - - + - - - + + + - - - - - - - - - - - + + + + - + - - - - - + - - - + + + - - - - - - - - - - - + + + + - - + + - - - - - - + - - - - - - - - + + + + - - - - - - - - + + + + - - + + - - - - - - + - - - - - - - - + + + + - - - + + + - - - - - - - - - - - + + + + - - - + + + - - - - - + + - - - - - - - - - - + + + + - - - + + + - - - - - - + + + - - - - - - + + + - - - - - - - + + + - - - - - - + + + - - - - - - - + + + - - - - - - - + + + - - - - - - + + + - - - - - + + - - - - - - + - - - + + + - - - - - - - - - - - + + + + - - + + - - - - - - + - - - + + + - - - From 13894a05e8f99f451125871980045c4bfb89e7d8 Mon Sep 17 00:00:00 2001 From: Just van Rossum Date: Mon, 22 Apr 2024 09:05:53 +0200 Subject: [PATCH 06/25] If there ae outlines, add a self-referencing component, as per spec --- src/fontra_compile/builder.py | 5 +++++ tests/data/MutatorSans.ttx | 9 ++++++--- 2 files changed, 11 insertions(+), 3 deletions(-) diff --git a/src/fontra_compile/builder.py b/src/fontra_compile/builder.py index 21c5c04..cdd55be 100644 --- a/src/fontra_compile/builder.py +++ b/src/fontra_compile/builder.py @@ -453,6 +453,11 @@ def buildVARC(self, axisTags): components.append(compo) + if self.glyphInfos[glyphName].glyph.numberOfContours: + compo = ot.VarComponent() + compo.glyphName = glyphName + components.append(compo) + compositeGlyph = ot.VarCompositeGlyph(components) variableComposites.append(compositeGlyph) diff --git a/tests/data/MutatorSans.ttx b/tests/data/MutatorSans.ttx index d17c0eb..0be2e04 100644 --- a/tests/data/MutatorSans.ttx +++ b/tests/data/MutatorSans.ttx @@ -63,12 +63,12 @@ - + - - + + @@ -1588,6 +1588,9 @@ + + + From 3110e5a0c20d7522cab5f1ff4e8cf9f4bfc4c1ee Mon Sep 17 00:00:00 2001 From: Just van Rossum Date: Mon, 22 Apr 2024 09:08:09 +0200 Subject: [PATCH 07/25] Use PR VARC branch --- requirements.txt | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/requirements.txt b/requirements.txt index 722211c..f036570 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,4 +1,6 @@ -fonttools==4.51.0 +# for now, use the VARC wip branch from https://github.com/fonttools/fonttools/pull/3395 +https://github.com/fonttools/fonttools.git@varc-table +# fonttools==4.51.0 fontmake==3.9.0 git+https://github.com/googlefonts/fontra.git git+https://github.com/googlefonts/fontra-rcjk.git From a8ca716984fb39401deb031b044576c814c60dfe Mon Sep 17 00:00:00 2001 From: Just van Rossum Date: Mon, 22 Apr 2024 09:12:00 +0200 Subject: [PATCH 08/25] Fix git install line --- requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements.txt b/requirements.txt index f036570..483b268 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,5 +1,5 @@ # for now, use the VARC wip branch from https://github.com/fonttools/fonttools/pull/3395 -https://github.com/fonttools/fonttools.git@varc-table +git+https://github.com/fonttools/fonttools.git@varc-table # fonttools==4.51.0 fontmake==3.9.0 git+https://github.com/googlefonts/fontra.git From c003292016a948943c8016b94892ab8622ce7f08 Mon Sep 17 00:00:00 2001 From: Just van Rossum Date: Mon, 22 Apr 2024 09:17:10 +0200 Subject: [PATCH 09/25] Fix type annotations --- src/fontra_compile/builder.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/fontra_compile/builder.py b/src/fontra_compile/builder.py index cdd55be..334d642 100644 --- a/src/fontra_compile/builder.py +++ b/src/fontra_compile/builder.py @@ -41,7 +41,7 @@ @dataclass class GlyphInfo: glyph: Glyph - xAdvance: int = 500 + xAdvance: float = 500 variations: list = field(default_factory=list) variableComponents: list = field(default_factory=list) localAxisTags: set = field(default_factory=set) @@ -75,7 +75,7 @@ async def setup(self) -> None: self.cachedSourceGlyphs: dict[str, VariableGlyph] = {} self.cachedComponentBaseInfo: dict = {} - self.glyphInfos: dict[str, Glyph] = {} + self.glyphInfos: dict[str, GlyphInfo] = {} self.cmap: dict[int, str] = {} async def build(self) -> TTFont: @@ -117,7 +117,7 @@ async def buildGlyphs(self) -> None: self.glyphInfos[glyphName] = glyphInfo - async def buildOneGlyph(self, glyphName: str) -> SimpleNamespace: + async def buildOneGlyph(self, glyphName: str) -> GlyphInfo: glyph = await self.getSourceGlyph(glyphName, False) localAxisDict = {axis.name: axisTuple(axis) for axis in glyph.axes} localDefaultLocation = {k: v[1] for k, v in localAxisDict.items()} @@ -192,7 +192,7 @@ async def buildOneGlyph(self, glyphName: str) -> SimpleNamespace: return GlyphInfo( glyph=ttGlyph, - xAdvance=max(defaultGlyph.xAdvance, 0), + xAdvance=max(defaultGlyph.xAdvance or 0, 0), variations=variations, variableComponents=componentInfo, localAxisTags=set(localAxisTags.values()), From 8949a9b6cc0eece11e065e2866cade4cbac1b863 Mon Sep 17 00:00:00 2001 From: Just van Rossum Date: Mon, 22 Apr 2024 09:36:19 +0200 Subject: [PATCH 10/25] Add comment --- src/fontra_compile/builder.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/fontra_compile/builder.py b/src/fontra_compile/builder.py index 334d642..217357b 100644 --- a/src/fontra_compile/builder.py +++ b/src/fontra_compile/builder.py @@ -454,6 +454,8 @@ def buildVARC(self, axisTags): components.append(compo) if self.glyphInfos[glyphName].glyph.numberOfContours: + # Add a component for the outline section, so we can effectively + # mix outlines and components. This is a special case in the spec. compo = ot.VarComponent() compo.glyphName = glyphName components.append(compo) From 9d7f8a1b84c34d858030a26450cda9ba5bdc780f Mon Sep 17 00:00:00 2001 From: Just van Rossum Date: Mon, 22 Apr 2024 09:56:25 +0200 Subject: [PATCH 11/25] Use dataclass instead of SimpleNamespace --- src/fontra_compile/builder.py | 18 +++++++++++++++--- 1 file changed, 15 insertions(+), 3 deletions(-) diff --git a/src/fontra_compile/builder.py b/src/fontra_compile/builder.py index 217357b..30ba856 100644 --- a/src/fontra_compile/builder.py +++ b/src/fontra_compile/builder.py @@ -1,5 +1,4 @@ from dataclasses import dataclass, field -from types import SimpleNamespace from typing import Any from fontra.core.classes import VariableGlyph @@ -48,6 +47,19 @@ class GlyphInfo: model: VariationModel | None = None +@dataclass +class ComponentInfo: + name: str + transform: dict[str, list[float]] + location: dict[str, list[float]] + localAxisNames: list + respondsToGlobalAxes: bool + baseAxisDict: dict + baseAxisTags: dict + isVariableComponent: bool = False + flags: int = 0 + + class Builder: def __init__(self, reader, requestedGlyphNames=None): self.reader = reader # a Fontra Backend, such as DesignspaceBackend @@ -199,7 +211,7 @@ async def buildOneGlyph(self, glyphName: str) -> GlyphInfo: model=model, ) - async def collectComponentInfo(self, glyph: VariableGlyph) -> list[SimpleNamespace]: + async def collectComponentInfo(self, glyph: VariableGlyph) -> list[ComponentInfo]: glyphSources = filterActiveSources(glyph.sources) sourceGlyphs = [glyph.layers[source.layerName].glyph for source in glyphSources] @@ -215,7 +227,7 @@ async def collectComponentInfo(self, glyph: VariableGlyph) -> list[SimpleNamespa ] components = [ - SimpleNamespace( + ComponentInfo( name=compo.name, transform={attrName: [] for attrName in VAR_TRANSFORM_MAPPING}, location={axisName: [] for axisName in axisNames}, From 50c815f319de86dab981f4283d2c558727f4b01a Mon Sep 17 00:00:00 2001 From: Just van Rossum Date: Mon, 22 Apr 2024 10:09:23 +0200 Subject: [PATCH 12/25] Rename methods --- src/fontra_compile/builder.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/fontra_compile/builder.py b/src/fontra_compile/builder.py index 30ba856..0902967 100644 --- a/src/fontra_compile/builder.py +++ b/src/fontra_compile/builder.py @@ -91,7 +91,7 @@ async def setup(self) -> None: self.cmap: dict[int, str] = {} async def build(self) -> TTFont: - await self.buildGlyphs() + await self.prepareGlyphs() return await self.buildFont() async def getSourceGlyph( @@ -108,7 +108,7 @@ def ensureGlyphDependency(self, glyphName: str) -> None: if glyphName not in self.glyphInfos and glyphName not in self.glyphOrder: self.glyphOrder.append(glyphName) - async def buildGlyphs(self) -> None: + async def prepareGlyphs(self) -> None: for glyphName in self.glyphOrder: codePoints = self.glyphMap.get(glyphName) @@ -117,7 +117,7 @@ async def buildGlyphs(self) -> None: if codePoints is not None: self.cmap.update((codePoint, glyphName) for codePoint in codePoints) try: - glyphInfo = await self.buildOneGlyph(glyphName) + glyphInfo = await self.prepareOneGlyph(glyphName) except KeyboardInterrupt: raise except (ValueError, VariationModelError) as e: # InterpolationError @@ -129,7 +129,7 @@ async def buildGlyphs(self) -> None: self.glyphInfos[glyphName] = glyphInfo - async def buildOneGlyph(self, glyphName: str) -> GlyphInfo: + async def prepareOneGlyph(self, glyphName: str) -> GlyphInfo: glyph = await self.getSourceGlyph(glyphName, False) localAxisDict = {axis.name: axisTuple(axis) for axis in glyph.axes} localDefaultLocation = {k: v[1] for k, v in localAxisDict.items()} From 97e236282c53079e713d963088700b8e9a5e9f11 Mon Sep 17 00:00:00 2001 From: Just van Rossum Date: Mon, 22 Apr 2024 10:16:43 +0200 Subject: [PATCH 13/25] Factor out preparation of coordinates --- src/fontra_compile/builder.py | 75 +++++++++++++++++++---------------- 1 file changed, 41 insertions(+), 34 deletions(-) diff --git a/src/fontra_compile/builder.py b/src/fontra_compile/builder.py index 0902967..8f650e1 100644 --- a/src/fontra_compile/builder.py +++ b/src/fontra_compile/builder.py @@ -138,44 +138,13 @@ async def prepareOneGlyph(self, glyphName: str) -> GlyphInfo: localAxisTags = makeLocalAxisTags(axisDict, self.globalAxisDict) axisTags = {**self.globalAxisTags, **localAxisTags} - locations = [] - sourceCoordinates = [] - defaultGlyph = None - componentInfo = await self.collectComponentInfo(glyph) - firstSourcePath = None - glyphSources = filterActiveSources(glyph.sources) - for sourceIndex, source in enumerate(glyphSources): - location = {**defaultLocation, **source.location} - locations.append(normalizeLocation(location, axisDict)) - sourceGlyph = glyph.layers[source.layerName].glyph - - if location == defaultLocation: - # This is the fefault glyph - defaultGlyph = sourceGlyph - - coordinates = GlyphCoordinates() - - assert isinstance(sourceGlyph.path, PackedPath) - coordinates.array.extend( - sourceGlyph.path.coordinates - ) # shortcut via ._a array - if firstSourcePath is None: - firstSourcePath = sourceGlyph.path - else: - if firstSourcePath.contourInfo != sourceGlyph.path.contourInfo: - raise ValueError( - f"contours for source {source.name} of {glyphName} are not compatible" - ) - # phantom points - coordinates.append((0, 0)) - coordinates.append((sourceGlyph.xAdvance, 0)) - coordinates.append((0, 0)) - coordinates.append((0, 0)) - sourceCoordinates.append(coordinates) + sourceCoordinates, locations, defaultGlyph = prepareSourceCoordinates( + glyph, glyphSources, defaultLocation, axisDict + ) locations = [mapDictKeys(s, axisTags) for s in locations] @@ -490,6 +459,44 @@ def buildVARC(self, axisTags): return varcTable +def prepareSourceCoordinates( + glyph: VariableGlyph, glyphSources, defaultLocation, axisDict +): + sourceCoordinates = [] + locations = [] + defaultGlyph = None + firstSourcePath = None + + for sourceIndex, source in enumerate(glyphSources): + location = {**defaultLocation, **source.location} + locations.append(normalizeLocation(location, axisDict)) + sourceGlyph = glyph.layers[source.layerName].glyph + + if location == defaultLocation: + # This is the fefault glyph + defaultGlyph = sourceGlyph + + coordinates = GlyphCoordinates() + + assert isinstance(sourceGlyph.path, PackedPath) + coordinates.array.extend(sourceGlyph.path.coordinates) # shortcut via ._a array + if firstSourcePath is None: + firstSourcePath = sourceGlyph.path + else: + if firstSourcePath.contourInfo != sourceGlyph.path.contourInfo: + raise ValueError( + f"contours for source {source.name} of {glyph.name} are not compatible" + ) + # phantom points + coordinates.append((0, 0)) + coordinates.append((sourceGlyph.xAdvance, 0)) + coordinates.append((0, 0)) + coordinates.append((0, 0)) + sourceCoordinates.append(coordinates) + + return sourceCoordinates, locations, defaultGlyph + + def addLSB(glyfTable, metrics: dict[str, int]) -> dict[str, tuple[int, int]]: return { glyphName: (xAdvance, glyfTable[glyphName].xMin) From 227c9cf729e12af72f2f8854180cdee8e8fee7e7 Mon Sep 17 00:00:00 2001 From: Just van Rossum Date: Mon, 22 Apr 2024 10:18:33 +0200 Subject: [PATCH 14/25] Moving a line, whitespace --- src/fontra_compile/builder.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/fontra_compile/builder.py b/src/fontra_compile/builder.py index 8f650e1..bb5d946 100644 --- a/src/fontra_compile/builder.py +++ b/src/fontra_compile/builder.py @@ -131,6 +131,7 @@ async def prepareGlyphs(self) -> None: async def prepareOneGlyph(self, glyphName: str) -> GlyphInfo: glyph = await self.getSourceGlyph(glyphName, False) + localAxisDict = {axis.name: axisTuple(axis) for axis in glyph.axes} localDefaultLocation = {k: v[1] for k, v in localAxisDict.items()} defaultLocation = {**self.defaultLocation, **localDefaultLocation} @@ -138,8 +139,6 @@ async def prepareOneGlyph(self, glyphName: str) -> GlyphInfo: localAxisTags = makeLocalAxisTags(axisDict, self.globalAxisDict) axisTags = {**self.globalAxisTags, **localAxisTags} - componentInfo = await self.collectComponentInfo(glyph) - glyphSources = filterActiveSources(glyph.sources) sourceCoordinates, locations, defaultGlyph = prepareSourceCoordinates( @@ -171,6 +170,8 @@ async def prepareOneGlyph(self, glyphName: str) -> GlyphInfo: defaultGlyph.path.drawPoints(ttGlyphPen) ttGlyph = ttGlyphPen.glyph() + componentInfo = await self.collectComponentInfo(glyph) + return GlyphInfo( glyph=ttGlyph, xAdvance=max(defaultGlyph.xAdvance or 0, 0), From fceb24efe3be21d697a22aba1f99b1103470264a Mon Sep 17 00:00:00 2001 From: Just van Rossum Date: Mon, 22 Apr 2024 10:20:03 +0200 Subject: [PATCH 15/25] Move a line --- src/fontra_compile/builder.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/fontra_compile/builder.py b/src/fontra_compile/builder.py index bb5d946..f062b38 100644 --- a/src/fontra_compile/builder.py +++ b/src/fontra_compile/builder.py @@ -145,6 +145,8 @@ async def prepareOneGlyph(self, glyphName: str) -> GlyphInfo: glyph, glyphSources, defaultLocation, axisDict ) + assert defaultGlyph is not None + locations = [mapDictKeys(s, axisTags) for s in locations] model = VariationModel(locations) # XXX axis order! @@ -164,8 +166,6 @@ async def prepareOneGlyph(self, glyphName: str) -> GlyphInfo: variations = [TupleVariation(s, d) for s, d in zip(supports, deltas)] - assert defaultGlyph is not None - ttGlyphPen = TTGlyphPointPen(None) defaultGlyph.path.drawPoints(ttGlyphPen) ttGlyph = ttGlyphPen.glyph() From 74fa60fcdda0b69a99cbb4d431fd903d241a4e89 Mon Sep 17 00:00:00 2001 From: Just van Rossum Date: Mon, 22 Apr 2024 10:23:06 +0200 Subject: [PATCH 16/25] Factor out gvar variation building --- src/fontra_compile/builder.py | 32 ++++++++++++++++++-------------- 1 file changed, 18 insertions(+), 14 deletions(-) diff --git a/src/fontra_compile/builder.py b/src/fontra_compile/builder.py index f062b38..31a93f9 100644 --- a/src/fontra_compile/builder.py +++ b/src/fontra_compile/builder.py @@ -151,20 +151,7 @@ async def prepareOneGlyph(self, glyphName: str) -> GlyphInfo: model = VariationModel(locations) # XXX axis order! - numPoints = len(sourceCoordinates[0]) - for coords in sourceCoordinates[1:]: - assert len(coords) == numPoints - - deltas, supports = model.getDeltasAndSupports(sourceCoordinates) - assert len(supports) == len(deltas) - - deltas.pop(0) # pop the default - supports.pop(0) # pop the default - for d in deltas: - d.toInt() - ensureWordRange(d) - - variations = [TupleVariation(s, d) for s, d in zip(supports, deltas)] + variations = prepareGvarVariations(sourceCoordinates, model) ttGlyphPen = TTGlyphPointPen(None) defaultGlyph.path.drawPoints(ttGlyphPen) @@ -498,6 +485,23 @@ def prepareSourceCoordinates( return sourceCoordinates, locations, defaultGlyph +def prepareGvarVariations(sourceCoordinates, model): + numPoints = len(sourceCoordinates[0]) + for coords in sourceCoordinates[1:]: + assert len(coords) == numPoints + + deltas, supports = model.getDeltasAndSupports(sourceCoordinates) + assert len(supports) == len(deltas) + + deltas.pop(0) # pop the default + supports.pop(0) # pop the default + for d in deltas: + d.toInt() + ensureWordRange(d) + + return [TupleVariation(s, d) for s, d in zip(supports, deltas)] + + def addLSB(glyfTable, metrics: dict[str, int]) -> dict[str, tuple[int, int]]: return { glyphName: (xAdvance, glyfTable[glyphName].xMin) From 73f30e74ccd7d31db251c3717604759415c06bc3 Mon Sep 17 00:00:00 2001 From: Just van Rossum Date: Mon, 22 Apr 2024 11:00:22 +0200 Subject: [PATCH 17/25] Factor out populating transformation and axis values --- src/fontra_compile/builder.py | 94 +++++++++++++++++++---------------- 1 file changed, 51 insertions(+), 43 deletions(-) diff --git a/src/fontra_compile/builder.py b/src/fontra_compile/builder.py index 31a93f9..d4f69a6 100644 --- a/src/fontra_compile/builder.py +++ b/src/fontra_compile/builder.py @@ -59,6 +59,53 @@ class ComponentInfo: isVariableComponent: bool = False flags: int = 0 + def addTransformationToComponent(self, compo, storeBuilder): + compo.transform = DecomposedTransform( + **{k: v[0] for k, v in self.transform.items()} + ) + + if not self.flags & VarComponentFlags.TRANSFORM_HAS_VARIATION: + return + + transformValues = [ + [ + fl2fi( + v / fieldMappingValues.scale, + fieldMappingValues.fractionalBits, + ) + for v in self.transform[fieldName] + ] + for fieldName, fieldMappingValues in VAR_TRANSFORM_MAPPING.items() + if fieldMappingValues.flag & self.flags + ] + + masterValues = [Vector(vec) for vec in zip(*transformValues)] + assert masterValues + + _, varIdx = storeBuilder.storeMasters(masterValues) + compo.transformVarIndex = varIdx + + def addLocationToComponent(self, compo, axisIndicesMapping, axisTags, storeBuilder): + if not self.flags & VarComponentFlags.HAVE_AXES: + return + + assert self.location + location = sorted(mapDictKeys(self.location, self.baseAxisTags).items()) + axisIndices = tuple(axisTags.index(k) for k, v in location) + axisIndicesIndex = axisIndicesMapping.get(axisIndices) + if axisIndicesIndex is None: + axisIndicesIndex = len(axisIndicesMapping) + axisIndicesMapping[axisIndices] = axisIndicesIndex + + compo.axisIndicesIndex = axisIndicesIndex + compo.axisValues = [v[0] for k, v in location] + + if self.flags & VarComponentFlags.AXIS_VALUES_HAVE_VARIATION: + locationValues = [[fl2fi(v, 14) for v in values] for k, values in location] + masterValues = [Vector(vec) for vec in zip(*locationValues)] + _, varIdx = storeBuilder.storeMasters(masterValues) + compo.axisValuesVarIndex = varIdx + class Builder: def __init__(self, reader, requestedGlyphNames=None): @@ -375,50 +422,11 @@ def buildVARC(self, axisTags): compo = ot.VarComponent() compo.flags = compoInfo.flags compo.glyphName = compoInfo.name - compo.transform = DecomposedTransform( - **{k: v[0] for k, v in compoInfo.transform.items()} - ) - if compoInfo.flags & VarComponentFlags.TRANSFORM_HAS_VARIATION: - transformValues = [ - [ - fl2fi( - v / fieldMappingValues.scale, - fieldMappingValues.fractionalBits, - ) - for v in compoInfo.transform[fieldName] - ] - for fieldName, fieldMappingValues in VAR_TRANSFORM_MAPPING.items() - if fieldMappingValues.flag & compoInfo.flags - ] - - masterValues = [Vector(vec) for vec in zip(*transformValues)] - assert masterValues - - _, varIdx = storeBuilder.storeMasters(masterValues) - compo.transformVarIndex = varIdx - - if compoInfo.flags & VarComponentFlags.HAVE_AXES: - assert compoInfo.location - location = sorted( - mapDictKeys(compoInfo.location, compoInfo.baseAxisTags).items() - ) - axisIndices = tuple(axisTags.index(k) for k, v in location) - axisIndicesIndex = axisIndicesMapping.get(axisIndices) - if axisIndicesIndex is None: - axisIndicesIndex = len(axisIndicesMapping) - axisIndicesMapping[axisIndices] = axisIndicesIndex - - compo.axisIndicesIndex = axisIndicesIndex - compo.axisValues = [v[0] for k, v in location] - - if compoInfo.flags & VarComponentFlags.AXIS_VALUES_HAVE_VARIATION: - locationValues = [ - [fl2fi(v, 14) for v in values] for k, values in location - ] - masterValues = [Vector(vec) for vec in zip(*locationValues)] - _, varIdx = storeBuilder.storeMasters(masterValues) - compo.axisValuesVarIndex = varIdx + compoInfo.addTransformationToComponent(compo, storeBuilder) + compoInfo.addLocationToComponent( + compo, axisIndicesMapping, axisTags, storeBuilder + ) components.append(compo) From 3199d465b477a7afe2ad37a7acfb65aeb27bcf86 Mon Sep 17 00:00:00 2001 From: Just van Rossum Date: Mon, 22 Apr 2024 16:27:11 +0200 Subject: [PATCH 18/25] Don't create variation model if there are no variations --- src/fontra_compile/builder.py | 20 +++++++++++++++++--- 1 file changed, 17 insertions(+), 3 deletions(-) diff --git a/src/fontra_compile/builder.py b/src/fontra_compile/builder.py index d4f69a6..39a2f84 100644 --- a/src/fontra_compile/builder.py +++ b/src/fontra_compile/builder.py @@ -196,9 +196,13 @@ async def prepareOneGlyph(self, glyphName: str) -> GlyphInfo: locations = [mapDictKeys(s, axisTags) for s in locations] - model = VariationModel(locations) # XXX axis order! + model = ( + VariationModel(locations) if len(locations) >= 2 else None + ) # XXX axis order! - variations = prepareGvarVariations(sourceCoordinates, model) + variations = ( + prepareGvarVariations(sourceCoordinates, model) if model is not None else [] + ) ttGlyphPen = TTGlyphPointPen(None) defaultGlyph.path.drawPoints(ttGlyphPen) @@ -416,9 +420,19 @@ def buildVARC(self, axisTags): for glyphName in varcSubtable.Coverage.glyphs: components = [] model = self.glyphInfos[glyphName].model - storeBuilder.setModel(model) + if model is not None: + storeBuilder.setModel(model) for compoInfo in self.glyphInfos[glyphName].variableComponents: + if model is None: + assert ( + not compoInfo.flags & VarComponentFlags.TRANSFORM_HAS_VARIATION + ) + assert ( + not compoInfo.flags + & VarComponentFlags.AXIS_VALUES_HAVE_VARIATION + ) + compo = ot.VarComponent() compo.flags = compoInfo.flags compo.glyphName = compoInfo.name From 2b1d18470c7923fdb84724bf77f86bbfe4aa3afa Mon Sep 17 00:00:00 2001 From: Just van Rossum Date: Mon, 22 Apr 2024 16:29:38 +0200 Subject: [PATCH 19/25] Raise ValueError if a base glyph doesn't exist --- src/fontra_compile/builder.py | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/fontra_compile/builder.py b/src/fontra_compile/builder.py index 39a2f84..0ea474d 100644 --- a/src/fontra_compile/builder.py +++ b/src/fontra_compile/builder.py @@ -330,6 +330,11 @@ async def getComponentBaseInfo(self, baseGlyphName: str) -> dict[str, Any]: async def setupComponentBaseInfo(self, baseGlyphName: str) -> dict[str, Any]: baseGlyph = await self.getSourceGlyph(baseGlyphName, True) + if baseGlyph is None: + raise ValueError( + f"a required base glyph is not available: {baseGlyphName!r}" + ) + localAxisNames = {axis.name for axis in baseGlyph.axes} # To determine the `respondsToGlobalAxes` flag, we take this component and all From 4986383800079dcfad15c30c8151b0b640b6477e Mon Sep 17 00:00:00 2001 From: Just van Rossum Date: Mon, 22 Apr 2024 16:34:00 +0200 Subject: [PATCH 20/25] Use better exceptions --- src/fontra_compile/builder.py | 23 ++++++++++++++++++----- 1 file changed, 18 insertions(+), 5 deletions(-) diff --git a/src/fontra_compile/builder.py b/src/fontra_compile/builder.py index 0ea474d..b1b00e3 100644 --- a/src/fontra_compile/builder.py +++ b/src/fontra_compile/builder.py @@ -23,6 +23,15 @@ ) from fontTools.varLib.multiVarStore import OnlineMultiVarStoreBuilder + +class InterpolationError(Exception): + pass + + +class MissingBaseGlyphError(Exception): + pass + + # If a component transformation has variations in any of the following fields, the # component can not be a classic component, and should be compiled as a variable # component, even if there are no axis variations @@ -167,7 +176,11 @@ async def prepareGlyphs(self) -> None: glyphInfo = await self.prepareOneGlyph(glyphName) except KeyboardInterrupt: raise - except (ValueError, VariationModelError) as e: # InterpolationError + except ( + InterpolationError, + MissingBaseGlyphError, + VariationModelError, + ) as e: print("warning", glyphName, repr(e)) # TODO: use logging if glyphInfo is None: @@ -248,14 +261,14 @@ async def collectComponentInfo(self, glyph: VariableGlyph) -> list[ComponentInfo for sourceGlyph in sourceGlyphs: if len(sourceGlyph.components) != len(components): - raise ValueError( + raise InterpolationError( f"components not compatible {glyph.name}: " f"{len(sourceGlyph.components)} vs. {len(components)}" ) for compoInfo, compo in zip(components, sourceGlyph.components): if compo.name != compoInfo.name: - raise ValueError( + raise InterpolationError( f"components not compatible in {glyph.name}: " f"{compo.name} vs. {compoInfo.name}" ) @@ -331,7 +344,7 @@ async def getComponentBaseInfo(self, baseGlyphName: str) -> dict[str, Any]: async def setupComponentBaseInfo(self, baseGlyphName: str) -> dict[str, Any]: baseGlyph = await self.getSourceGlyph(baseGlyphName, True) if baseGlyph is None: - raise ValueError( + raise MissingBaseGlyphError( f"a required base glyph is not available: {baseGlyphName!r}" ) @@ -499,7 +512,7 @@ def prepareSourceCoordinates( firstSourcePath = sourceGlyph.path else: if firstSourcePath.contourInfo != sourceGlyph.path.contourInfo: - raise ValueError( + raise InterpolationError( f"contours for source {source.name} of {glyph.name} are not compatible" ) # phantom points From b98cdb1f29784a0e7674ff800c17a308ae762ec6 Mon Sep 17 00:00:00 2001 From: Just van Rossum Date: Mon, 22 Apr 2024 16:39:07 +0200 Subject: [PATCH 21/25] Sanitize location first, so we won't get a wrong HAVE_AXES flag etc. --- src/fontra_compile/builder.py | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/src/fontra_compile/builder.py b/src/fontra_compile/builder.py index b1b00e3..5fde693 100644 --- a/src/fontra_compile/builder.py +++ b/src/fontra_compile/builder.py @@ -284,6 +284,13 @@ async def collectComponentInfo(self, glyph: VariableGlyph) -> list[ComponentInfo numSources = len(glyphSources) for compoInfo in components: + # Filter out unknown/unused axes + compoInfo.location = { + axisName: values + for axisName, values in compoInfo.location.items() + if values + } + isVariableComponent = bool(compoInfo.location) flags = 0 @@ -304,13 +311,6 @@ async def collectComponentInfo(self, glyph: VariableGlyph) -> list[ComponentInfo if attrName in VARCO_IF_VARYING: isVariableComponent = True - # Filter out unknown/unused axes - compoInfo.location = { - axisName: values - for axisName, values in compoInfo.location.items() - if values - } - axesAtDefault = [] for axisName, values in compoInfo.location.items(): firstValue = values[0] From b4a7d7ddf202efd3e190af67bb9bb9067cef4dae Mon Sep 17 00:00:00 2001 From: Just van Rossum Date: Mon, 22 Apr 2024 21:24:34 +0200 Subject: [PATCH 22/25] Fix assumption that source 0 is the default source --- src/fontra_compile/builder.py | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/src/fontra_compile/builder.py b/src/fontra_compile/builder.py index 5fde693..1a702a5 100644 --- a/src/fontra_compile/builder.py +++ b/src/fontra_compile/builder.py @@ -67,10 +67,11 @@ class ComponentInfo: baseAxisTags: dict isVariableComponent: bool = False flags: int = 0 + defaultSourceIndex: int = 0 def addTransformationToComponent(self, compo, storeBuilder): compo.transform = DecomposedTransform( - **{k: v[0] for k, v in self.transform.items()} + **{k: v[self.defaultSourceIndex] for k, v in self.transform.items()} ) if not self.flags & VarComponentFlags.TRANSFORM_HAS_VARIATION: @@ -99,6 +100,7 @@ def addLocationToComponent(self, compo, axisIndicesMapping, axisTags, storeBuild return assert self.location + location = sorted(mapDictKeys(self.location, self.baseAxisTags).items()) axisIndices = tuple(axisTags.index(k) for k, v in location) axisIndicesIndex = axisIndicesMapping.get(axisIndices) @@ -107,7 +109,7 @@ def addLocationToComponent(self, compo, axisIndicesMapping, axisTags, storeBuild axisIndicesMapping[axisIndices] = axisIndicesIndex compo.axisIndicesIndex = axisIndicesIndex - compo.axisValues = [v[0] for k, v in location] + compo.axisValues = [v[self.defaultSourceIndex] for k, v in location] if self.flags & VarComponentFlags.AXIS_VALUES_HAVE_VARIATION: locationValues = [[fl2fi(v, 14) for v in values] for k, values in location] @@ -221,7 +223,8 @@ async def prepareOneGlyph(self, glyphName: str) -> GlyphInfo: defaultGlyph.path.drawPoints(ttGlyphPen) ttGlyph = ttGlyphPen.glyph() - componentInfo = await self.collectComponentInfo(glyph) + defaultSourceIndex = model.reverseMapping[0] + componentInfo = await self.collectComponentInfo(glyph, defaultSourceIndex) return GlyphInfo( glyph=ttGlyph, @@ -232,7 +235,9 @@ async def prepareOneGlyph(self, glyphName: str) -> GlyphInfo: model=model, ) - async def collectComponentInfo(self, glyph: VariableGlyph) -> list[ComponentInfo]: + async def collectComponentInfo( + self, glyph: VariableGlyph, defaultSourceIndex: int + ) -> list[ComponentInfo]: glyphSources = filterActiveSources(glyph.sources) sourceGlyphs = [glyph.layers[source.layerName].glyph for source in glyphSources] @@ -253,6 +258,7 @@ async def collectComponentInfo(self, glyph: VariableGlyph) -> list[ComponentInfo transform={attrName: [] for attrName in VAR_TRANSFORM_MAPPING}, location={axisName: [] for axisName in axisNames}, **await self.getComponentBaseInfo(compo.name), + defaultSourceIndex=defaultSourceIndex, ) for compo, axisNames in zip( firstSourceGlyph.components, allComponentAxisNames From c69353acb3ffb768cac9d7076d249f544ec28425 Mon Sep 17 00:00:00 2001 From: Just van Rossum Date: Mon, 22 Apr 2024 22:03:24 +0200 Subject: [PATCH 23/25] Find defaultGlyph via model --- src/fontra_compile/builder.py | 15 +++++---------- 1 file changed, 5 insertions(+), 10 deletions(-) diff --git a/src/fontra_compile/builder.py b/src/fontra_compile/builder.py index 1a702a5..7ca1d85 100644 --- a/src/fontra_compile/builder.py +++ b/src/fontra_compile/builder.py @@ -203,12 +203,10 @@ async def prepareOneGlyph(self, glyphName: str) -> GlyphInfo: glyphSources = filterActiveSources(glyph.sources) - sourceCoordinates, locations, defaultGlyph = prepareSourceCoordinates( + sourceCoordinates, locations = prepareSourceCoordinates( glyph, glyphSources, defaultLocation, axisDict ) - assert defaultGlyph is not None - locations = [mapDictKeys(s, axisTags) for s in locations] model = ( @@ -219,11 +217,13 @@ async def prepareOneGlyph(self, glyphName: str) -> GlyphInfo: prepareGvarVariations(sourceCoordinates, model) if model is not None else [] ) + defaultSourceIndex = model.reverseMapping[0] if model is not None else 0 + defaultGlyph = glyph.layers[glyphSources[defaultSourceIndex].layerName].glyph + ttGlyphPen = TTGlyphPointPen(None) defaultGlyph.path.drawPoints(ttGlyphPen) ttGlyph = ttGlyphPen.glyph() - defaultSourceIndex = model.reverseMapping[0] componentInfo = await self.collectComponentInfo(glyph, defaultSourceIndex) return GlyphInfo( @@ -498,7 +498,6 @@ def prepareSourceCoordinates( ): sourceCoordinates = [] locations = [] - defaultGlyph = None firstSourcePath = None for sourceIndex, source in enumerate(glyphSources): @@ -506,10 +505,6 @@ def prepareSourceCoordinates( locations.append(normalizeLocation(location, axisDict)) sourceGlyph = glyph.layers[source.layerName].glyph - if location == defaultLocation: - # This is the fefault glyph - defaultGlyph = sourceGlyph - coordinates = GlyphCoordinates() assert isinstance(sourceGlyph.path, PackedPath) @@ -528,7 +523,7 @@ def prepareSourceCoordinates( coordinates.append((0, 0)) sourceCoordinates.append(coordinates) - return sourceCoordinates, locations, defaultGlyph + return sourceCoordinates, locations def prepareGvarVariations(sourceCoordinates, model): From 03f63db0591b501cde371b9f0f0af964ba7e56d6 Mon Sep 17 00:00:00 2001 From: Just van Rossum Date: Wed, 24 Apr 2024 17:54:12 +0200 Subject: [PATCH 24/25] Rebuild ttx files --- tests/data/MutatorSans-fontmake.ttx | 8 +- tests/data/MutatorSans.ttx | 5273 ++++++++++++++++++++++++++- 2 files changed, 5276 insertions(+), 5 deletions(-) diff --git a/tests/data/MutatorSans-fontmake.ttx b/tests/data/MutatorSans-fontmake.ttx index 17aae8c..6435fc4 100644 --- a/tests/data/MutatorSans-fontmake.ttx +++ b/tests/data/MutatorSans-fontmake.ttx @@ -1,5 +1,5 @@ - + @@ -63,12 +63,12 @@ - + - - + + diff --git a/tests/data/MutatorSans.ttx b/tests/data/MutatorSans.ttx index ccd697d..adec1b7 100644 --- a/tests/data/MutatorSans.ttx +++ b/tests/data/MutatorSans.ttx @@ -1 +1,5272 @@ -(will rebuild) + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + weight + + + width + + + V000 + + + V001 + + + weight + + + width + + + V000 + + + V001 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + wght + 0x0 + 100.0 + 100.0 + 900.0 + 256 + + + + + wdth + 0x0 + 0.0 + 0.0 + 1000.0 + 257 + + + + + V000 + 0x1 + -1.0 + 0.0 + 1.0 + 258 + + + + + V001 + 0x1 + -1.0 + 0.0 + 1.0 + 259 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + From f2bb19ff944c7af59c480e808512e2d9684168a2 Mon Sep 17 00:00:00 2001 From: Just van Rossum Date: Fri, 24 May 2024 20:59:24 +0200 Subject: [PATCH 25/25] Bump fonttools to release that contains VARC --- requirements.txt | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/requirements.txt b/requirements.txt index 7740297..a24b97a 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,6 +1,4 @@ -# for now, use the VARC wip branch from https://github.com/fonttools/fonttools/pull/3395 -git+https://github.com/fonttools/fonttools.git@varc-table -# fonttools==4.51.0 +fonttools==4.52.1 fontmake==3.9.0 fontc==0.0.1.post5 git+https://github.com/googlefonts/fontra.git