Merge pull request #3360 from fonttools/fea-vars-multiple-tags

[featureVars] allow to assign same subst lookup to several feature tags
This commit is contained in:
Cosimo Lupo 2023-12-01 18:40:35 +00:00 committed by GitHub
commit 0b05cec0a3
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 117 additions and 40 deletions

View File

@ -438,8 +438,8 @@ glyphname pairs: the glyphs that need to be substituted. For a rule to be trigge
See the following issues for more information: See the following issues for more information:
`fontTools#1371 <https://github.com/fonttools/fonttools/issues/1371#issuecomment-590214572>`__ `fontTools#1371 <https://github.com/fonttools/fonttools/issues/1371#issuecomment-590214572>`__
`fontTools#2050 <https://github.com/fonttools/fonttools/issues/2050#issuecomment-678691020>`__ `fontTools#2050 <https://github.com/fonttools/fonttools/issues/2050#issuecomment-678691020>`__
- If you want to use a different feature altogether, e.g. ``calt``, - If you want to use a different feature(s) altogether, e.g. ``calt``,
use the lib key ``com.github.fonttools.varLib.featureVarsFeatureTag`` use the lib key ``com.github.fonttools.varLib.featureVarsFeatureTag``.
.. code:: xml .. code:: xml
@ -450,6 +450,9 @@ glyphname pairs: the glyphs that need to be substituted. For a rule to be trigge
</dict> </dict>
</lib> </lib>
This can also take a comma-separated list of feature tags, e.g. ``salt,ss01``,
if you wish the same rules to be applied with several features.
``<rule>`` element ``<rule>`` element

View File

@ -52,7 +52,8 @@ from .errors import VarLibError, VarLibValidationError
log = logging.getLogger("fontTools.varLib") log = logging.getLogger("fontTools.varLib")
# This is a lib key for the designspace document. The value should be # This is a lib key for the designspace document. The value should be
# an OpenType feature tag, to be used as the FeatureVariations feature. # a comma-separated list of OpenType feature tag(s), to be used as the
# FeatureVariations feature.
# If present, the DesignSpace <rules processing="..."> flag is ignored. # If present, the DesignSpace <rules processing="..."> flag is ignored.
FEAVAR_FEATURETAG_LIB_KEY = "com.github.fonttools.varLib.featureVarsFeatureTag" FEAVAR_FEATURETAG_LIB_KEY = "com.github.fonttools.varLib.featureVarsFeatureTag"
@ -781,7 +782,9 @@ def _merge_OTL(font, model, master_fonts, axisTags):
font["GPOS"].table.remap_device_varidxes(varidx_map) font["GPOS"].table.remap_device_varidxes(varidx_map)
def _add_GSUB_feature_variations(font, axes, internal_axis_supports, rules, featureTag): def _add_GSUB_feature_variations(
font, axes, internal_axis_supports, rules, featureTags
):
def normalize(name, value): def normalize(name, value):
return models.normalizeLocation({name: value}, internal_axis_supports)[name] return models.normalizeLocation({name: value}, internal_axis_supports)[name]
@ -812,7 +815,7 @@ def _add_GSUB_feature_variations(font, axes, internal_axis_supports, rules, feat
conditional_subs.append((region, subs)) conditional_subs.append((region, subs))
addFeatureVariations(font, conditional_subs, featureTag) addFeatureVariations(font, conditional_subs, featureTags)
_DesignSpaceData = namedtuple( _DesignSpaceData = namedtuple(
@ -1204,11 +1207,9 @@ def build(
if "cvar" not in exclude and "glyf" in vf: if "cvar" not in exclude and "glyf" in vf:
_merge_TTHinting(vf, model, master_fonts) _merge_TTHinting(vf, model, master_fonts)
if "GSUB" not in exclude and ds.rules: if "GSUB" not in exclude and ds.rules:
featureTag = ds.lib.get( featureTags = _feature_variations_tags(ds)
FEAVAR_FEATURETAG_LIB_KEY, "rclt" if ds.rulesProcessingLast else "rvrn"
)
_add_GSUB_feature_variations( _add_GSUB_feature_variations(
vf, ds.axes, ds.internal_axis_supports, ds.rules, featureTag vf, ds.axes, ds.internal_axis_supports, ds.rules, featureTags
) )
if "CFF2" not in exclude and ("CFF " in vf or "CFF2" in vf): if "CFF2" not in exclude and ("CFF " in vf or "CFF2" in vf):
_add_CFF2(vf, model, master_fonts) _add_CFF2(vf, model, master_fonts)
@ -1299,6 +1300,14 @@ class MasterFinder(object):
return os.path.normpath(path) return os.path.normpath(path)
def _feature_variations_tags(ds):
raw_tags = ds.lib.get(
FEAVAR_FEATURETAG_LIB_KEY,
"rclt" if ds.rulesProcessingLast else "rvrn",
)
return sorted({t.strip() for t in raw_tags.split(",")})
def main(args=None): def main(args=None):
"""Build variable fonts from a designspace file and masters""" """Build variable fonts from a designspace file and masters"""
from argparse import ArgumentParser from argparse import ArgumentParser

View File

@ -43,9 +43,18 @@ def addFeatureVariations(font, conditionalSubstitutions, featureTag="rvrn"):
# ... ] # ... ]
# >>> addFeatureVariations(f, condSubst) # >>> addFeatureVariations(f, condSubst)
# >>> f.save(dstPath) # >>> f.save(dstPath)
The `featureTag` parameter takes either a str or a iterable of str (the single str
is kept for backwards compatibility), and defines which feature(s) will be
associated with the feature variations.
Note, if this is "rvrn", then the substitution lookup will be inserted at the
beginning of the lookup list so that it is processed before others, otherwise
for any other feature tags it will be appended last.
""" """
processLast = featureTag != "rvrn" # process first when "rvrn" is the only listed tag
featureTags = [featureTag] if isinstance(featureTag, str) else sorted(featureTag)
processLast = "rvrn" not in featureTags or len(featureTags) > 1
_checkSubstitutionGlyphsExist( _checkSubstitutionGlyphsExist(
glyphNames=set(font.getGlyphOrder()), glyphNames=set(font.getGlyphOrder()),
@ -75,7 +84,7 @@ def addFeatureVariations(font, conditionalSubstitutions, featureTag="rvrn"):
(conditionSet, [lookupMap[s] for s in substitutions]) (conditionSet, [lookupMap[s] for s in substitutions])
) )
addFeatureVariationsRaw(font, font["GSUB"].table, conditionsAndLookups, featureTag) addFeatureVariationsRaw(font, font["GSUB"].table, conditionsAndLookups, featureTags)
def _checkSubstitutionGlyphsExist(glyphNames, substitutions): def _checkSubstitutionGlyphsExist(glyphNames, substitutions):
@ -324,13 +333,16 @@ def addFeatureVariationsRaw(font, table, conditionalSubstitutions, featureTag="r
"""Low level implementation of addFeatureVariations that directly """Low level implementation of addFeatureVariations that directly
models the possibilities of the FeatureVariations table.""" models the possibilities of the FeatureVariations table."""
processLast = featureTag != "rvrn" featureTags = [featureTag] if isinstance(featureTag, str) else sorted(featureTag)
processLast = "rvrn" not in featureTags or len(featureTags) > 1
# #
# if there is no <featureTag> feature: # if a <featureTag> feature is not present:
# make empty <featureTag> feature # make empty <featureTag> feature
# sort features, get <featureTag> feature index # sort features, get <featureTag> feature index
# add <featureTag> feature to all scripts # add <featureTag> feature to all scripts
# if a <featureTag> feature is present:
# reuse <featureTag> feature index
# make lookups # make lookups
# add feature variations # add feature variations
# #
@ -339,17 +351,26 @@ def addFeatureVariationsRaw(font, table, conditionalSubstitutions, featureTag="r
table.FeatureVariations = None # delete any existing FeatureVariations table.FeatureVariations = None # delete any existing FeatureVariations
varFeatureIndices = [] varFeatureIndices = set()
for index, feature in enumerate(table.FeatureList.FeatureRecord):
if feature.FeatureTag == featureTag:
varFeatureIndices.append(index)
if not varFeatureIndices: existingTags = {
feature.FeatureTag
for feature in table.FeatureList.FeatureRecord
if feature.FeatureTag in featureTags
}
newTags = set(featureTags) - existingTags
if newTags:
varFeatures = []
for featureTag in sorted(newTags):
varFeature = buildFeatureRecord(featureTag, []) varFeature = buildFeatureRecord(featureTag, [])
table.FeatureList.FeatureRecord.append(varFeature) table.FeatureList.FeatureRecord.append(varFeature)
varFeatures.append(varFeature)
table.FeatureList.FeatureCount = len(table.FeatureList.FeatureRecord) table.FeatureList.FeatureCount = len(table.FeatureList.FeatureRecord)
sortFeatureList(table) sortFeatureList(table)
for varFeature in varFeatures:
varFeatureIndex = table.FeatureList.FeatureRecord.index(varFeature) varFeatureIndex = table.FeatureList.FeatureRecord.index(varFeature)
for scriptRecord in table.ScriptList.ScriptRecord: for scriptRecord in table.ScriptList.ScriptRecord:
@ -362,8 +383,16 @@ def addFeatureVariationsRaw(font, table, conditionalSubstitutions, featureTag="r
for langSys in [scriptRecord.Script.DefaultLangSys] + langSystems: for langSys in [scriptRecord.Script.DefaultLangSys] + langSystems:
langSys.FeatureIndex.append(varFeatureIndex) langSys.FeatureIndex.append(varFeatureIndex)
langSys.FeatureCount = len(langSys.FeatureIndex) langSys.FeatureCount = len(langSys.FeatureIndex)
varFeatureIndices.add(varFeatureIndex)
varFeatureIndices = [varFeatureIndex] if existingTags:
# indices may have changed if we inserted new features and sorted feature list
# so we must do this after the above
varFeatureIndices.update(
index
for index, feature in enumerate(table.FeatureList.FeatureRecord)
if feature.FeatureTag in existingTags
)
axisIndices = { axisIndices = {
axis.axisTag: axisIndex for axisIndex, axis in enumerate(font["fvar"].axes) axis.axisTag: axisIndex for axisIndex, axis in enumerate(font["fvar"].axes)
@ -380,7 +409,7 @@ def addFeatureVariationsRaw(font, table, conditionalSubstitutions, featureTag="r
ct = buildConditionTable(axisIndices[axisTag], minValue, maxValue) ct = buildConditionTable(axisIndices[axisTag], minValue, maxValue)
conditionTable.append(ct) conditionTable.append(ct)
records = [] records = []
for varFeatureIndex in varFeatureIndices: for varFeatureIndex in sorted(varFeatureIndices):
existingLookupIndices = table.FeatureList.FeatureRecord[ existingLookupIndices = table.FeatureList.FeatureRecord[
varFeatureIndex varFeatureIndex
].Feature.LookupListIndex ].Feature.LookupListIndex

View File

@ -71,7 +71,7 @@
<lib> <lib>
<dict> <dict>
<key>com.github.fonttools.varLib.featureVarsFeatureTag</key> <key>com.github.fonttools.varLib.featureVarsFeatureTag</key>
<string>calt</string> <string>rclt,calt</string>
</dict> </dict>
</lib> </lib>
</designspace> </designspace>

View File

@ -33,21 +33,28 @@
<Script> <Script>
<DefaultLangSys> <DefaultLangSys>
<ReqFeatureIndex value="65535"/> <ReqFeatureIndex value="65535"/>
<!-- FeatureCount=1 --> <!-- FeatureCount=2 -->
<FeatureIndex index="0" value="0"/> <FeatureIndex index="0" value="0"/>
<FeatureIndex index="1" value="1"/>
</DefaultLangSys> </DefaultLangSys>
<!-- LangSysCount=0 --> <!-- LangSysCount=0 -->
</Script> </Script>
</ScriptRecord> </ScriptRecord>
</ScriptList> </ScriptList>
<FeatureList> <FeatureList>
<!-- FeatureCount=1 --> <!-- FeatureCount=2 -->
<FeatureRecord index="0"> <FeatureRecord index="0">
<FeatureTag value="calt"/> <FeatureTag value="calt"/>
<Feature> <Feature>
<!-- LookupCount=0 --> <!-- LookupCount=0 -->
</Feature> </Feature>
</FeatureRecord> </FeatureRecord>
<FeatureRecord index="1">
<FeatureTag value="rclt"/>
<Feature>
<!-- LookupCount=0 -->
</Feature>
</FeatureRecord>
</FeatureList> </FeatureList>
<LookupList> <LookupList>
<!-- LookupCount=3 --> <!-- LookupCount=3 -->
@ -95,7 +102,7 @@
</ConditionSet> </ConditionSet>
<FeatureTableSubstitution> <FeatureTableSubstitution>
<Version value="0x00010000"/> <Version value="0x00010000"/>
<!-- SubstitutionCount=1 --> <!-- SubstitutionCount=2 -->
<SubstitutionRecord index="0"> <SubstitutionRecord index="0">
<FeatureIndex value="0"/> <FeatureIndex value="0"/>
<Feature> <Feature>
@ -104,6 +111,14 @@
<LookupListIndex index="1" value="1"/> <LookupListIndex index="1" value="1"/>
</Feature> </Feature>
</SubstitutionRecord> </SubstitutionRecord>
<SubstitutionRecord index="1">
<FeatureIndex value="1"/>
<Feature>
<!-- LookupCount=2 -->
<LookupListIndex index="0" value="0"/>
<LookupListIndex index="1" value="1"/>
</Feature>
</SubstitutionRecord>
</FeatureTableSubstitution> </FeatureTableSubstitution>
</FeatureVariationRecord> </FeatureVariationRecord>
<FeatureVariationRecord index="1"> <FeatureVariationRecord index="1">
@ -122,7 +137,7 @@
</ConditionSet> </ConditionSet>
<FeatureTableSubstitution> <FeatureTableSubstitution>
<Version value="0x00010000"/> <Version value="0x00010000"/>
<!-- SubstitutionCount=1 --> <!-- SubstitutionCount=2 -->
<SubstitutionRecord index="0"> <SubstitutionRecord index="0">
<FeatureIndex value="0"/> <FeatureIndex value="0"/>
<Feature> <Feature>
@ -130,6 +145,13 @@
<LookupListIndex index="0" value="2"/> <LookupListIndex index="0" value="2"/>
</Feature> </Feature>
</SubstitutionRecord> </SubstitutionRecord>
<SubstitutionRecord index="1">
<FeatureIndex value="1"/>
<Feature>
<!-- LookupCount=1 -->
<LookupListIndex index="0" value="2"/>
</Feature>
</SubstitutionRecord>
</FeatureTableSubstitution> </FeatureTableSubstitution>
</FeatureVariationRecord> </FeatureVariationRecord>
<FeatureVariationRecord index="2"> <FeatureVariationRecord index="2">
@ -143,7 +165,7 @@
</ConditionSet> </ConditionSet>
<FeatureTableSubstitution> <FeatureTableSubstitution>
<Version value="0x00010000"/> <Version value="0x00010000"/>
<!-- SubstitutionCount=1 --> <!-- SubstitutionCount=2 -->
<SubstitutionRecord index="0"> <SubstitutionRecord index="0">
<FeatureIndex value="0"/> <FeatureIndex value="0"/>
<Feature> <Feature>
@ -151,6 +173,13 @@
<LookupListIndex index="0" value="1"/> <LookupListIndex index="0" value="1"/>
</Feature> </Feature>
</SubstitutionRecord> </SubstitutionRecord>
<SubstitutionRecord index="1">
<FeatureIndex value="1"/>
<Feature>
<!-- LookupCount=1 -->
<LookupListIndex index="0" value="1"/>
</Feature>
</SubstitutionRecord>
</FeatureTableSubstitution> </FeatureTableSubstitution>
</FeatureVariationRecord> </FeatureVariationRecord>
<FeatureVariationRecord index="3"> <FeatureVariationRecord index="3">
@ -164,7 +193,7 @@
</ConditionSet> </ConditionSet>
<FeatureTableSubstitution> <FeatureTableSubstitution>
<Version value="0x00010000"/> <Version value="0x00010000"/>
<!-- SubstitutionCount=1 --> <!-- SubstitutionCount=2 -->
<SubstitutionRecord index="0"> <SubstitutionRecord index="0">
<FeatureIndex value="0"/> <FeatureIndex value="0"/>
<Feature> <Feature>
@ -172,6 +201,13 @@
<LookupListIndex index="0" value="0"/> <LookupListIndex index="0" value="0"/>
</Feature> </Feature>
</SubstitutionRecord> </SubstitutionRecord>
<SubstitutionRecord index="1">
<FeatureIndex value="1"/>
<Feature>
<!-- LookupCount=1 -->
<LookupListIndex index="0" value="0"/>
</Feature>
</SubstitutionRecord>
</FeatureTableSubstitution> </FeatureTableSubstitution>
</FeatureVariationRecord> </FeatureVariationRecord>
</FeatureVariations> </FeatureVariations>