instancer: refactor updateNameTable
This commit is contained in:
parent
b328475072
commit
9a72311d19
@ -127,6 +127,7 @@ class OverlapMode(IntEnum):
|
||||
KEEP_AND_SET_FLAGS = 1
|
||||
REMOVE = 2
|
||||
|
||||
|
||||
class NameID(IntEnum):
|
||||
FAMILY_NAME = 1
|
||||
SUBFAMILY_NAME = 2
|
||||
@ -136,6 +137,7 @@ class NameID(IntEnum):
|
||||
TYPOGRAPHIC_FAMILY_NAME = 16
|
||||
TYPOGRAPHIC_SUBFAMILY_NAME = 17
|
||||
|
||||
|
||||
ELIDABLE_AXIS_VALUE_NAME = 2
|
||||
|
||||
|
||||
@ -1110,6 +1112,280 @@ def pruningUnusedNames(varfont):
|
||||
del varfont["ltag"]
|
||||
|
||||
|
||||
def updateNameTable(varfont, axisLimits):
|
||||
"""Update an instatiated variable font's name table using the STAT
|
||||
table's Axis Value Tables.
|
||||
|
||||
To establish which Axis Value Tables are needed, we first remove all
|
||||
tables whose Value's are not in the axisLimits dictionary. We then
|
||||
remove all tables which have Flag 2 enabled (ELIDABLE_AXIS_VALUE_NAME).
|
||||
Finally, we remove duplicates and ensure Format 4 tables preside over
|
||||
the other formats.
|
||||
|
||||
The updated nametable will conform to the R/I/B/BI naming model.
|
||||
"""
|
||||
if "STAT" not in varfont:
|
||||
raise ValueError("Cannot update name table since there is no STAT table.")
|
||||
stat = varfont["STAT"]
|
||||
fvar = varfont["fvar"]
|
||||
|
||||
# The updated name table must reflect the 'zero origin' of the font.
|
||||
# If a user is instantiating a partial font, we will populate the
|
||||
# unpinned axes with their default values.
|
||||
fvarDefaults = {a.axisTag: a.defaultValue for a in fvar.axes}
|
||||
axisCoords = axisLimits
|
||||
for axisTag, val in fvarDefaults.items():
|
||||
if axisTag not in axisCoords:
|
||||
axisCoords[axisTag] = val
|
||||
elif isinstance(axisCoords[axisTag], tuple):
|
||||
axisCoords[axisTag] = val
|
||||
|
||||
axisValueTables = _axisValueTablesFromAxisCoords(stat, axisCoords)
|
||||
_updateNameRecords(varfont, axisValueTables)
|
||||
|
||||
|
||||
def _axisValueTablesFromAxisCoords(stat, axisCoords):
|
||||
axisValueTables = stat.table.AxisValueArray.AxisValue
|
||||
axisRecords = stat.table.DesignAxisRecord.Axis
|
||||
axisRecordIndex = {a.AxisTag: a.AxisOrdering for a in axisRecords}
|
||||
axisRecordTag = {a.AxisOrdering: a.AxisTag for a in axisRecords}
|
||||
|
||||
axisValuesToFind = {
|
||||
axisRecordIndex[axisTag]: val for axisTag, val in axisCoords.items()
|
||||
}
|
||||
axisValueTables = [
|
||||
v for v in axisValueTables if _axisValueInAxisCoords(v, axisValuesToFind)
|
||||
]
|
||||
axisValueTablesMissing = set(axisValuesToFind) - axisValueRecordsIndexes(
|
||||
axisValueTables
|
||||
)
|
||||
if axisValueTablesMissing:
|
||||
missing = ", ".join(
|
||||
f"{axisRecordTag[i]}={axisValuesToFind[i]}" for i in axisValueTablesMissing
|
||||
)
|
||||
raise ValueError(f"Cannot find Axis Value Tables {missing}")
|
||||
# remove axis Value Tables which have Elidable_AXIS_VALUE_NAME flag set
|
||||
axisValueTables = [
|
||||
v for v in axisValueTables if v.Flags & ELIDABLE_AXIS_VALUE_NAME != 2
|
||||
]
|
||||
return _sortedAxisValues(axisValueTables)
|
||||
|
||||
|
||||
def _axisValueInAxisCoords(axisValueTable, axisCoords):
|
||||
if axisValueTable.Format == 4:
|
||||
res = []
|
||||
for rec in axisValueTable.AxisValueRecord:
|
||||
axisIndex = rec.AxisIndex
|
||||
if axisIndex not in axisCoords:
|
||||
return False
|
||||
if rec.Value == axisCoords[axisIndex]:
|
||||
res.append(True)
|
||||
else:
|
||||
res.append(False)
|
||||
return True if all(res) else False
|
||||
|
||||
axisIndex = axisValueTable.AxisIndex
|
||||
|
||||
if axisValueTable.Format in (1, 3):
|
||||
# A variable font can have additional axes that are not implemented as
|
||||
# dynamic-variation axes in the fvar table, but that are
|
||||
# relevant for the font or the family of which it is a member. This
|
||||
# condition will include them.
|
||||
# A common scenario is a family which consists of two variable fonts,
|
||||
# one for Roman styles, the other for Italic styles. Both fonts have a
|
||||
# weight axis. In order to establish a relationship between the fonts,
|
||||
# an Italic Axis Record is created for both fonts. In the Roman font,
|
||||
# an Axis Value Table is added to the Italic Axis Record which has the
|
||||
# name "Roman" and its Value is set to 0.0, it also includes link
|
||||
# Value of 1. In the Italic font, an Axis Value Table is also added
|
||||
# to the Italic Axis Record which has the name "Italic", its Value set
|
||||
# to 1.0.
|
||||
if axisIndex not in axisCoords and axisValueTable.Value in (0.0, 1.0):
|
||||
return True
|
||||
|
||||
elif axisIndex in axisCoords and axisValueTable.Value == axisCoords[axisIndex]:
|
||||
return True
|
||||
|
||||
if axisValueTable.Format == 2:
|
||||
return (
|
||||
True
|
||||
if all(
|
||||
[
|
||||
axisIndex in axisCoords
|
||||
and axisCoords[axisIndex] >= axisValueTable.RangeMinValue,
|
||||
axisIndex in axisCoords
|
||||
and axisCoords[axisIndex] <= axisValueTable.RangeMaxValue,
|
||||
]
|
||||
)
|
||||
else False
|
||||
)
|
||||
return False
|
||||
|
||||
|
||||
def axisValueRecordsIndexes(axisValueTables):
|
||||
res = set()
|
||||
for val in axisValueTables:
|
||||
res |= axisValueRecordIndexes(val)
|
||||
return res
|
||||
|
||||
|
||||
def axisValueRecordIndexes(axisValueTable):
|
||||
if axisValueTable.Format == 4:
|
||||
return set(r.AxisIndex for r in axisValueTable.AxisValueRecord)
|
||||
return set([axisValueTable.AxisIndex])
|
||||
|
||||
|
||||
def _sortedAxisValues(axisValueTables):
|
||||
# Sort and remove duplicates ensuring that format 4 axis Value Tables
|
||||
# are dominant
|
||||
results = []
|
||||
seenAxes = set()
|
||||
# sort format 4 axes so the tables with the most AxisValueRecords
|
||||
# are first
|
||||
format4 = sorted(
|
||||
[v for v in axisValueTables if v.Format == 4],
|
||||
key=lambda v: len(v.AxisValueRecord),
|
||||
reverse=True,
|
||||
)
|
||||
nonFormat4 = [v for v in axisValueTables if v not in format4]
|
||||
|
||||
for val in format4:
|
||||
axisIndexes = axisValueRecordIndexes(val)
|
||||
if bool(seenAxes & axisIndexes) == False:
|
||||
seenAxes |= axisIndexes
|
||||
results.append((tuple(axisIndexes), val))
|
||||
|
||||
for val in nonFormat4:
|
||||
axisIndex = val.AxisIndex
|
||||
if axisIndex not in seenAxes:
|
||||
seenAxes.add(axisIndex)
|
||||
results.append(((axisIndex,), val))
|
||||
return [axisValueTable for _, axisValueTable in sorted(results)]
|
||||
|
||||
|
||||
def _updateNameRecords(varfont, axisValueTables):
|
||||
# Update nametable based on the axisValues using the R/I/B/BI model.
|
||||
nametable = varfont["name"]
|
||||
|
||||
ribbiAxisValues = _ribbiAxisValueTables(nametable, axisValueTables)
|
||||
nonRibbiAxisValues = [v for v in axisValueTables if v not in ribbiAxisValues]
|
||||
|
||||
nameTablePlatEncLangs = set(
|
||||
(r.platformID, r.platEncID, r.langID) for r in nametable.names
|
||||
)
|
||||
for platEncLang in nameTablePlatEncLangs:
|
||||
_updateStyleRecords(
|
||||
varfont,
|
||||
nametable,
|
||||
ribbiAxisValues,
|
||||
nonRibbiAxisValues,
|
||||
platEncLang,
|
||||
)
|
||||
|
||||
|
||||
def _ribbiAxisValueTables(nametable, axisValueTables):
|
||||
engNameRecords = any([r for r in nametable.names if r.langID == 0x409])
|
||||
if not engNameRecords:
|
||||
raise ValueError(
|
||||
f"Canot determine if there are RIBBI Axis Value Tables "
|
||||
"since there are no name table Records which have "
|
||||
"platformID=3, platEncID=1, langID=0x409"
|
||||
)
|
||||
return [
|
||||
v
|
||||
for v in axisValueTables
|
||||
if nametable.getName(v.ValueNameID, 3, 1, 0x409).toUnicode()
|
||||
in ("Regular", "Italic", "Bold", "Bold Italic")
|
||||
]
|
||||
|
||||
|
||||
def _updateStyleRecords(
|
||||
varfont, nametable, ribbiAxisValues, nonRibbiAxisValues, platEncLang=(3, 1, 0x409)
|
||||
):
|
||||
currentFamilyName = nametable.getName(
|
||||
NameID.TYPOGRAPHIC_FAMILY_NAME, *platEncLang
|
||||
) or nametable.getName(NameID.FAMILY_NAME, *platEncLang)
|
||||
|
||||
currentStyleName = nametable.getName(
|
||||
NameID.TYPOGRAPHIC_SUBFAMILY_NAME, *platEncLang
|
||||
) or nametable.getName(NameID.SUBFAMILY_NAME, *platEncLang)
|
||||
|
||||
if not currentFamilyName or not currentStyleName:
|
||||
# Since no family name or style name entries were found, we cannot
|
||||
# update this set of name Records.
|
||||
return
|
||||
|
||||
currentFamilyName = currentFamilyName.toUnicode()
|
||||
currentStyleName = currentStyleName.toUnicode()
|
||||
|
||||
ribbiName = " ".join(
|
||||
nametable.getName(a.ValueNameID, *platEncLang).toUnicode()
|
||||
for a in ribbiAxisValues
|
||||
)
|
||||
nonRibbiName = " ".join(
|
||||
nametable.getName(a.ValueNameID, *platEncLang).toUnicode()
|
||||
for a in nonRibbiAxisValues
|
||||
)
|
||||
|
||||
nameIDs = {
|
||||
NameID.FAMILY_NAME: currentFamilyName,
|
||||
# TODO (M Foley) what about Elidable fallback name instead?
|
||||
NameID.SUBFAMILY_NAME: ribbiName
|
||||
or nametable.getName(NameID.SUBFAMILY_NAME, *platEncLang).toUnicode(),
|
||||
}
|
||||
if nonRibbiAxisValues:
|
||||
nameIDs[NameID.FAMILY_NAME] = f"{currentFamilyName} {nonRibbiName}".strip()
|
||||
nameIDs[NameID.TYPOGRAPHIC_FAMILY_NAME] = currentFamilyName
|
||||
nameIDs[
|
||||
NameID.TYPOGRAPHIC_SUBFAMILY_NAME
|
||||
] = f"{nonRibbiName} {ribbiName}".strip()
|
||||
|
||||
newFamilyName = nameIDs.get(NameID.TYPOGRAPHIC_FAMILY_NAME) or nameIDs.get(
|
||||
NameID.FAMILY_NAME
|
||||
)
|
||||
newStyleName = nameIDs.get(NameID.TYPOGRAPHIC_SUBFAMILY_NAME) or nameIDs.get(
|
||||
NameID.SUBFAMILY_NAME
|
||||
)
|
||||
|
||||
nameIDs[NameID.FULL_FONT_NAME] = f"{newFamilyName} {newStyleName}"
|
||||
# TODO (M Foley) implement Adobe PS naming for VFs
|
||||
nameIDs[
|
||||
NameID.POSTSCRIPT_NAME
|
||||
] = f"{newFamilyName.replace(' ', '')}-{newStyleName.replace(' ', '')}"
|
||||
nameIDs[NameID.UNIQUE_FONT_IDENTIFIER] = _updateUniqueIdNameRecord(
|
||||
varfont, nameIDs, platEncLang
|
||||
)
|
||||
|
||||
for nameID, string in nameIDs.items():
|
||||
if not string:
|
||||
continue
|
||||
nametable.setName(string, nameID, *platEncLang)
|
||||
|
||||
|
||||
def _updateUniqueIdNameRecord(varfont, nameIDs, platEncLang):
|
||||
name = varfont["name"]
|
||||
record = name.getName(NameID.UNIQUE_FONT_IDENTIFIER, *platEncLang)
|
||||
if not record:
|
||||
return None
|
||||
|
||||
def isSubString(string1, string2):
|
||||
if string2 in string1:
|
||||
return True
|
||||
return False
|
||||
|
||||
# Check if full name and postscript name are a substring
|
||||
for nameID in (4, 6):
|
||||
nameRecord = name.getName(nameID, *platEncLang)
|
||||
if not nameRecord:
|
||||
continue
|
||||
if isSubString(record.toUnicode(), nameRecord.toUnicode()):
|
||||
return record.toUnicode().replace(
|
||||
nameRecord.toUnicode(), nameIDs[nameRecord.nameID]
|
||||
)
|
||||
# TODO (M Foley) Construct new uniqueID if full name or postscript names are not subsets
|
||||
return None
|
||||
|
||||
|
||||
def setMacOverlapFlags(glyfTable):
|
||||
flagOverlapCompound = _g_l_y_f.OVERLAP_COMPOUND
|
||||
flagOverlapSimple = _g_l_y_f.flagOverlapSimple
|
||||
@ -1198,7 +1474,7 @@ def instantiateVariableFont(
|
||||
inplace=False,
|
||||
optimize=True,
|
||||
overlap=OverlapMode.KEEP_AND_SET_FLAGS,
|
||||
update_nametable=False
|
||||
updateFontNames=False,
|
||||
):
|
||||
"""Instantiate variable font, either fully or partially.
|
||||
|
||||
@ -1231,6 +1507,11 @@ def instantiateVariableFont(
|
||||
contours and components, you can pass OverlapMode.REMOVE. Note that this
|
||||
requires the skia-pathops package (available to pip install).
|
||||
The overlap parameter only has effect when generating full static instances.
|
||||
updateFontNames (bool): if True, update the instantiated font's nametable using
|
||||
the Axis Value Tables from the STAT table. The name table will be updated so
|
||||
it conforms to the R/I/B/BI model. If the STAT table is missing or
|
||||
an Axis Value table is missing for a given coordinate, an Error will be
|
||||
raised.
|
||||
"""
|
||||
# 'overlap' used to be bool and is now enum; for backward compat keep accepting bool
|
||||
overlap = OverlapMode(int(overlap))
|
||||
@ -1246,6 +1527,10 @@ def instantiateVariableFont(
|
||||
if not inplace:
|
||||
varfont = deepcopy(varfont)
|
||||
|
||||
if updateFontNames:
|
||||
log.info("Updating nametable")
|
||||
updateNameTable(varfont, axisLimits)
|
||||
|
||||
if "gvar" in varfont:
|
||||
instantiateGvar(varfont, normalizedLimits, optimize=optimize)
|
||||
|
||||
@ -1284,10 +1569,6 @@ def instantiateVariableFont(
|
||||
log.info("Removing overlaps from glyf table")
|
||||
removeOverlaps(varfont)
|
||||
|
||||
if update_nametable:
|
||||
log.info("Updating nametable")
|
||||
updateNameTable(varfont, axisLimits)
|
||||
|
||||
varLib.set_default_weight_width_slant(
|
||||
varfont,
|
||||
location={
|
||||
@ -1300,223 +1581,6 @@ def instantiateVariableFont(
|
||||
return varfont
|
||||
|
||||
|
||||
def axisValueIsSelected(axisValue, seeker):
|
||||
if axisValue.Format == 4:
|
||||
res = []
|
||||
for rec in axisValue.AxisValueRecord:
|
||||
axisIndex = rec.AxisIndex
|
||||
if axisIndex not in seeker:
|
||||
return False
|
||||
if rec.Value == seeker[axisIndex]:
|
||||
res.append(True)
|
||||
else:
|
||||
res.append(False)
|
||||
return True if all(res) else False
|
||||
|
||||
axisIndex = axisValue.AxisIndex
|
||||
|
||||
if axisValue.Format in (1, 3):
|
||||
# Add axisValue if it's an attribute of a font. Font family
|
||||
if axisIndex not in seeker and axisValue.Value in [0.0, 1.0]:
|
||||
return True
|
||||
|
||||
elif axisIndex in seeker and axisValue.Value == seeker[axisIndex]:
|
||||
return True
|
||||
|
||||
if axisValue.Format == 2:
|
||||
return True if all([
|
||||
axisIndex in seeker and seeker[axisIndex] >= axisValue.RangeMinValue,
|
||||
axisIndex in seeker and seeker[axisIndex] <= axisValue.RangeMaxValue
|
||||
]) else False
|
||||
|
||||
return False
|
||||
|
||||
|
||||
def axisValueIndexes(axisValue):
|
||||
if axisValue.Format == 4:
|
||||
return [r.AxisIndex for r in axisValue.AxisValueRecord]
|
||||
return [axisValue.AxisIndex]
|
||||
|
||||
|
||||
def axisValuesIndexes(axisValues):
|
||||
res = []
|
||||
for axisValue in axisValues:
|
||||
res += axisValueIndexes(axisValue)
|
||||
return res
|
||||
|
||||
|
||||
def axisValuesFromAxisLimits(stat, axisLimits):
|
||||
axisValues = stat.table.AxisValueArray.AxisValue
|
||||
axisRecords = stat.table.DesignAxisRecord.Axis
|
||||
axisOrder = {a.AxisTag: a.AxisOrdering for a in axisRecords}
|
||||
axisTag = {a.AxisOrdering: a.AxisTag for a in axisRecords}
|
||||
# Only check pinnedAxes for matching AxisValues
|
||||
axisValuesToFind = {
|
||||
axisOrder[k]: v for k, v in axisLimits.items() \
|
||||
if isinstance(v, (float, int))
|
||||
}
|
||||
|
||||
axisValues = [a for a in axisValues if axisValueIsSelected(a, axisValuesToFind)]
|
||||
axisValuesMissing = set(axisValuesToFind) - set(axisValuesIndexes(axisValues))
|
||||
if axisValuesMissing:
|
||||
missing = [f"{axisTag[i]}={axisValuesToFind[i]}" for i in axisValuesMissing]
|
||||
raise ValueError(f"Cannot find AxisValue for {', '.join(missing)}")
|
||||
# filter out Elidable axisValues
|
||||
axisValues = [a for a in axisValues if a.Flags & ELIDABLE_AXIS_VALUE_NAME != 2]
|
||||
return sortedAxisValues(axisValues)
|
||||
|
||||
|
||||
def sortedAxisValues(axisValues):
|
||||
# Sort and remove duplicates so format 4 axisValues are dominant
|
||||
results, seenAxes = [], set()
|
||||
# ensure format4 axes with the most AxisValueRecords are first
|
||||
format4 = sorted(
|
||||
[a for a in axisValues if a.Format == 4],
|
||||
key=lambda k: len(k.AxisValueRecord), reverse=True
|
||||
)
|
||||
nonFormat4 = [a for a in axisValues if a not in format4]
|
||||
|
||||
for axisValue in format4:
|
||||
axes = set([r.AxisIndex for r in axisValue.AxisValueRecord])
|
||||
if seenAxes - axes == seenAxes:
|
||||
seenAxes |= axes
|
||||
results.append((tuple(axes), axisValue))
|
||||
|
||||
for axisValue in nonFormat4:
|
||||
axisIndex = axisValue.AxisIndex
|
||||
if axisIndex not in seenAxes:
|
||||
results.append(((axisIndex,), axisValue))
|
||||
return [v for k, v in sorted(results)]
|
||||
|
||||
|
||||
def updateNameTable(varfont, axisLimits):
|
||||
if "STAT" not in varfont:
|
||||
raise ValueError("Cannot update name table since there is no STAT table.")
|
||||
stat = varfont['STAT']
|
||||
nametable = varfont["name"]
|
||||
|
||||
# add default axis values if they are missing from axisLimits
|
||||
if 'fvar' in varfont:
|
||||
fvar = varfont['fvar']
|
||||
fvarDefaults = {a.axisTag: a.defaultValue for a in fvar.axes}
|
||||
for k, v in fvarDefaults.items():
|
||||
if k not in axisLimits:
|
||||
axisLimits[k] = v
|
||||
|
||||
selectedAxisValues = axisValuesFromAxisLimits(stat, axisLimits)
|
||||
_updateNameRecords(varfont, nametable, selectedAxisValues)
|
||||
|
||||
|
||||
def _updateNameRecords(varfont, nametable, axisValues):
|
||||
# Update nametable based on the axisValues
|
||||
# using the R/I/B/BI and WWS models.
|
||||
engNameRecords = any([r for r in nametable.names if r.langID == 0x409])
|
||||
if not engNameRecords:
|
||||
# TODO (Marc F) improve error msg
|
||||
raise ValueError("No English namerecords")
|
||||
|
||||
ribbiAxisValues = _ribbiAxisValues(nametable, axisValues)
|
||||
nonRibbiAxisValues = [av for av in axisValues if av not in ribbiAxisValues]
|
||||
|
||||
nametblLangs = set((r.platformID, r.platEncID, r.langID) for r in nametable.names)
|
||||
for lang in nametblLangs:
|
||||
_updateStyleRecords(
|
||||
varfont,
|
||||
nametable,
|
||||
ribbiAxisValues,
|
||||
nonRibbiAxisValues,
|
||||
lang,
|
||||
)
|
||||
|
||||
|
||||
def _ribbiAxisValues(nametable, axisValues):
|
||||
ribbiStyles = frozenset(["Regular", "Italic", "Bold", "Bold Italic"])
|
||||
res = []
|
||||
for axisValue in axisValues:
|
||||
name = nametable.getName(axisValue.ValueNameID, 3, 1, 0x409).toUnicode()
|
||||
if name in ribbiStyles:
|
||||
res.append(axisValue)
|
||||
return res
|
||||
|
||||
|
||||
def _updateStyleRecords(
|
||||
varfont,
|
||||
nametable,
|
||||
ribbiAxisValues,
|
||||
nonRibbiAxisValues,
|
||||
lang=(3, 1, 0x409)
|
||||
):
|
||||
# wwsAxes = frozenset(["wght", "wdth", "ital"])
|
||||
currentFamilyName = nametable.getName(NameID.TYPOGRAPHIC_FAMILY_NAME, *lang) or \
|
||||
nametable.getName(NameID.FAMILY_NAME, *lang)
|
||||
|
||||
currentStyleName = nametable.getName(NameID.TYPOGRAPHIC_SUBFAMILY_NAME, *lang) or \
|
||||
nametable.getName(NameID.SUBFAMILY_NAME, *lang)
|
||||
# TODO cleanup
|
||||
if not currentFamilyName or not currentStyleName:
|
||||
print(f"Cannot update {lang} since it's missing a familyName nameID 1 or subFamilyName nameID 2 entry")
|
||||
return
|
||||
currentFamilyName = currentFamilyName.toUnicode()
|
||||
currentStyleName = currentStyleName.toUnicode()
|
||||
|
||||
ribbiName = " ".join([nametable.getName(a.ValueNameID, *lang).toUnicode() for a in ribbiAxisValues])
|
||||
nonRibbiName = " ".join([nametable.getName(a.ValueNameID, *lang).toUnicode() for a in nonRibbiAxisValues])
|
||||
|
||||
nameIDs = {
|
||||
NameID.FAMILY_NAME: currentFamilyName,
|
||||
NameID.SUBFAMILY_NAME: ribbiName or nametable.getName(NameID.SUBFAMILY_NAME, *lang).toUnicode()
|
||||
}
|
||||
if nonRibbiAxisValues:
|
||||
nameIDs[NameID.FAMILY_NAME] = f"{currentFamilyName} {nonRibbiName}".strip()
|
||||
nameIDs[NameID.TYPOGRAPHIC_FAMILY_NAME] = currentFamilyName
|
||||
nameIDs[NameID.TYPOGRAPHIC_SUBFAMILY_NAME] = f"{nonRibbiName} {ribbiName}".strip()
|
||||
# # Include WWS name records if there are nonWwsParticles
|
||||
# if nonWwsParticles:
|
||||
# nameIDs[21] = f"{currentFamilyName} {' '.join(nonWwsParticles)}"
|
||||
# nameIDs[22] = " ".join(wwsParticles)
|
||||
# # Enable fsSelection bit 8 (WWS)
|
||||
# varfont['OS/2'].fsSelection |= (1 << 8)
|
||||
#
|
||||
newFamilyName = nameIDs.get(NameID.TYPOGRAPHIC_FAMILY_NAME) or \
|
||||
nameIDs.get(NameID.FAMILY_NAME)
|
||||
newStyleName = nameIDs.get(NameID.TYPOGRAPHIC_SUBFAMILY_NAME) or \
|
||||
nameIDs.get(NameID.SUBFAMILY_NAME)
|
||||
|
||||
nameIDs[NameID.FULL_FONT_NAME] = f"{newFamilyName} {newStyleName}"
|
||||
nameIDs[NameID.POSTSCRIPT_NAME] = f"{newFamilyName.replace(' ', '')}-{newStyleName.replace(' ', '')}"
|
||||
nameIDs[NameID.UNIQUE_FONT_IDENTIFIER] = _uniqueIdRecord(varfont, lang, nameIDs)
|
||||
|
||||
for nameID, string in nameIDs.items():
|
||||
if not string:
|
||||
continue
|
||||
nametable.setName(string, nameID, *lang)
|
||||
|
||||
|
||||
def _uniqueIdRecord(varfont, lang, nameIDs):
|
||||
name = varfont['name']
|
||||
record = name.getName(NameID.UNIQUE_FONT_IDENTIFIER, *lang)
|
||||
if not record:
|
||||
return None
|
||||
|
||||
def isSubString(string1, string2):
|
||||
if string2 in string1:
|
||||
return True
|
||||
return False
|
||||
|
||||
# Check if full name and postscript name are a substring
|
||||
for nameID in (4, 6):
|
||||
nameRecord = name.getName(nameID, *lang)
|
||||
if not nameRecord:
|
||||
continue
|
||||
if isSubString(record.toUnicode(), nameRecord.toUnicode()):
|
||||
return record.toUnicode().replace(
|
||||
nameRecord.toUnicode(),
|
||||
nameIDs[nameRecord.nameID]
|
||||
)
|
||||
# TODO (M Foley) Construct new uniqueID if full name or postscript names are not subsets
|
||||
return None
|
||||
|
||||
|
||||
def splitAxisLocationAndRanges(axisLimits, rangeType=AxisRange):
|
||||
location, axisRanges = {}, {}
|
||||
for axisTag, value in axisLimits.items():
|
||||
@ -1613,8 +1677,8 @@ def parseArgs(args):
|
||||
parser.add_argument(
|
||||
"--update-nametable",
|
||||
action="store_true",
|
||||
help="Update the instantiated font's nametable using the STAT "
|
||||
"table Axis Values"
|
||||
help="Update the instantiated font's nametable. Input font must have "
|
||||
"a STAT table with Axis Value Tables",
|
||||
)
|
||||
loggingGroup = parser.add_mutually_exclusive_group(required=False)
|
||||
loggingGroup.add_argument(
|
||||
@ -1667,7 +1731,7 @@ def main(args=None):
|
||||
inplace=True,
|
||||
optimize=options.optimize,
|
||||
overlap=options.overlap,
|
||||
update_nametable=options.update_nametable,
|
||||
updateFontNames=options.update_nametable,
|
||||
)
|
||||
|
||||
outfile = (
|
||||
|
@ -2013,7 +2013,7 @@ def test_updateNametable_partial(varfont):
|
||||
|
||||
|
||||
def test_updateNameTable_missing_axisValues(varfont):
|
||||
with pytest.raises(ValueError, match="Cannot find AxisValue for wght=200"):
|
||||
with pytest.raises(ValueError, match="Cannot find Axis Value Tables wght=200"):
|
||||
instancer.updateNameTable(varfont, {"wght": 200})
|
||||
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user