[featureVars] expose method to addGSUBFeatureVariations to existing VF

Fixes https://github.com/fonttools/fonttools/issues/3357
This commit is contained in:
Cosimo Lupo 2023-12-01 18:49:29 +00:00
parent 0b05cec0a3
commit 82021732ae
No known key found for this signature in database
GPG Key ID: DF65A8A5A119C9A8
2 changed files with 69 additions and 8 deletions

View File

@ -863,7 +863,7 @@ def _add_COLR(font, model, master_fonts, axisTags, colr_layer_reuse=True):
colr.VarIndexMap = builder.buildDeltaSetIndexMap(varIdxes)
def load_designspace(designspace):
def load_designspace(designspace, log_enabled=True):
# TODO: remove this and always assume 'designspace' is a DesignSpaceDocument,
# never a file path, as that's already handled by caller
if hasattr(designspace, "sources"): # Assume a DesignspaceDocument
@ -911,10 +911,11 @@ def load_designspace(designspace):
axis.labelNames["en"] = tostr(axis_name)
axes[axis_name] = axis
log.info("Axes:\n%s", pformat([axis.asdict() for axis in axes.values()]))
if log_enabled:
log.info("Axes:\n%s", pformat([axis.asdict() for axis in axes.values()]))
axisMappings = ds.axisMappings
if axisMappings:
if axisMappings and log_enabled:
log.info("Mappings:\n%s", pformat(axisMappings))
# Check all master and instance locations are valid and fill in defaults
@ -944,20 +945,23 @@ def load_designspace(designspace):
# Normalize master locations
internal_master_locs = [o.getFullDesignLocation(ds) for o in masters]
log.info("Internal master locations:\n%s", pformat(internal_master_locs))
if log_enabled:
log.info("Internal master locations:\n%s", pformat(internal_master_locs))
# TODO This mapping should ideally be moved closer to logic in _add_fvar/avar
internal_axis_supports = {}
for axis in axes.values():
triple = (axis.minimum, axis.default, axis.maximum)
internal_axis_supports[axis.name] = [axis.map_forward(v) for v in triple]
log.info("Internal axis supports:\n%s", pformat(internal_axis_supports))
if log_enabled:
log.info("Internal axis supports:\n%s", pformat(internal_axis_supports))
normalized_master_locs = [
models.normalizeLocation(m, internal_axis_supports)
for m in internal_master_locs
]
log.info("Normalized master locations:\n%s", pformat(normalized_master_locs))
if log_enabled:
log.info("Normalized master locations:\n%s", pformat(normalized_master_locs))
# Find base master
base_idx = None
@ -972,7 +976,8 @@ def load_designspace(designspace):
raise VarLibValidationError(
"Base master not found; no master at default location?"
)
log.info("Index of base master: %s", base_idx)
if log_enabled:
log.info("Index of base master: %s", base_idx)
return _DesignSpaceData(
axes,
@ -1308,6 +1313,30 @@ def _feature_variations_tags(ds):
return sorted({t.strip() for t in raw_tags.split(",")})
def addGSUBFeatureVariations(vf, designspace, featureTags=(), *, log_enabled=False):
"""Add GSUB FeatureVariations table to variable font, based on DesignSpace rules.
Args:
vf: A TTFont object representing the variable font.
designspace: A DesignSpaceDocument object.
featureTags: Optional feature tag(s) to use for the FeatureVariations records.
If unset, the key 'com.github.fonttools.varLib.featureVarsFeatureTag' is
looked up in the DS <lib> and used; otherwise the default is 'rclt' if
the <rules processing="last"> attribute is set, else 'rvrn'.
See <https://fonttools.readthedocs.io/en/latest/designspaceLib/xml.html#rules-element>
log_enabled: If True, log info about DS axes and sources. Default is False, as
the same info may have already been logged as part of varLib.build.
"""
ds = load_designspace(designspace, log_enabled=log_enabled)
if not ds.rules:
return
if not featureTags:
featureTags = _feature_variations_tags(ds)
_add_GSUB_feature_variations(
vf, ds.axes, ds.internal_axis_supports, ds.rules, featureTags
)
def main(args=None):
"""Build variable fonts from a designspace file and masters"""
from argparse import ArgumentParser

View File

@ -1,7 +1,13 @@
from fontTools.colorLib.builder import buildCOLR
from fontTools.ttLib import TTFont, newTable
from fontTools.ttLib.tables import otTables as ot
from fontTools.varLib import build, build_many, load_designspace, _add_COLR
from fontTools.varLib import (
build,
build_many,
load_designspace,
_add_COLR,
addGSUBFeatureVariations,
)
from fontTools.varLib.errors import VarLibValidationError
import fontTools.varLib.errors as varLibErrors
from fontTools.varLib.models import VariationModel
@ -1009,6 +1015,32 @@ Expected to see .ScriptCount==1, instead saw 0""",
save_before_dump=True,
)
def test_varlib_addGSUBFeatureVariations(self):
ttx_dir = self.get_test_input("master_ttx_interpolatable_ttf")
ds = DesignSpaceDocument.fromfile(
self.get_test_input("FeatureVars.designspace")
)
for source in ds.sources:
ttx_dump = TTFont()
ttx_dump.importXML(
os.path.join(
ttx_dir, os.path.basename(source.filename).replace(".ufo", ".ttx")
)
)
source.font = ttx_dump
varfont, _, _ = build(ds, exclude=["GSUB"])
assert "GSUB" not in varfont
addGSUBFeatureVariations(varfont, ds)
assert "GSUB" in varfont
tables = ["fvar", "GSUB"]
expected_ttx_path = self.get_test_output("FeatureVars.ttx")
self.expect_ttx(varfont, expected_ttx_path, tables)
self.check_ttx_dump(varfont, expected_ttx_path, tables, ".ttf")
def test_load_masters_layerName_without_required_font():
ds = DesignSpaceDocument()