wip instancer: support format 4 axisvalues

This commit is contained in:
Marc Foley 2020-10-07 22:59:20 +01:00
parent b95607513c
commit b502471a08
2 changed files with 100 additions and 31 deletions

View File

@ -1298,49 +1298,74 @@ def instantiateVariableFont(
return varfont
def updateNameTable(varfont, axisLimits):
nametable = varfont["name"]
if "STAT" not in varfont:
raise ValueError("Cannot update name table since there is no STAT table.")
stat = varfont['STAT']
def axisValuesFromAxisLimits(stat, axisLimits):
axisRecords = stat.table.DesignAxisRecord.Axis
axisValues = stat.table.AxisValueArray.AxisValue
axisOrder = {a.AxisOrdering: a.AxisTag for a in axisRecords}
keptAxisValues = []
for axisValue in axisValues:
# TODO Format 4
if axisValue.Format == 4:
continue
format4 = [a for a in axisValues if a.Format == 4]
nonformat4 = [a for a in axisValues if a not in format4]
axisValues = format4 + nonformat4
axisTag = axisOrder[axisValue.AxisIndex]
if axisTag in axisLimits:
pinnedAxis = isinstance(axisLimits[axisTag], (float, int))
else:
pinnedAxis = False
axisOrder = {a.AxisOrdering: a.AxisTag for a in axisRecords}
pinnedAxes = set(k for k, v in axisLimits.items() if isinstance(v, (float, int)))
results, seen_axes = [], set()
for axisValue in axisValues:
# Ignore axisValue if it has ELIDABLE_AXIS_VALUE_NAME flag enabled.
# Enabling this flag will hide the axisValue in application font menus.
# TODO this is too greedy! we need to retain wght axisValues
if axisValue.Flags == 2:
continue
if axisValue.Format in (1, 3):
if axisValue.Format == 4:
axisIndexes = set(r.AxisIndex for r in axisValue.AxisValueRecord)
if seen_axes - axisIndexes != seen_axes:
continue
# TODO fix dup appends
for rec in axisValue.AxisValueRecord:
axisTag = axisOrder[rec.AxisIndex]
if axisTag not in pinnedAxes:
continue
if rec.Value == axisLimits[axisTag]:
seen_axes.add(rec.AxisIndex)
results.append((rec.AxisIndex, axisValue))
elif axisValue.Format in (1, 3):
axisTag = axisOrder[axisValue.AxisIndex]
# Add axisValue if it's used to link to another variable font
if axisTag not in axisLimits and axisValue.Value == 1.0:
keptAxisValues.append(axisValue)
seen_axes.add(rec.AxisIndex)
results.append((axisValue.AxisIndex, axisValue))
if axisTag not in pinnedAxes:
continue
# Add axisValue if its value is in the axisLimits and the user has
# pinned the axis
elif pinnedAxis and axisValue.Value == axisLimits[axisTag]:
keptAxisValues.append(axisValue)
elif axisValue.Value == axisLimits[axisTag]:
seen_axes.add(rec.AxisIndex)
results.append((axisValue.AxisIndex,axisValue))
if axisValue.Format == 2:
if pinnedAxis and axisLimits[axisTag] >= axisValue.RangeMinValue \
elif axisValue.Format == 2:
axisTag = axisOrder[axisValue.AxisIndex]
if axisTag not in pinnedAxes:
continue
if axisLimits[axisTag] >= axisValue.RangeMinValue \
and axisLimits[axisTag] <= axisValue.RangeMaxValue:
keptAxisValues.append(axisValue)
seen_axes.add(axisValue.AxisIndex)
results.append((axisValue.AxisIndex, axisValue))
return [v for k, v in sorted(results)]
_updateNameRecords(varfont, nametable, keptAxisValues)
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"]
selectedAxisValues = axisValuesFromAxisLimits(stat, axisLimits)
_updateNameRecords(varfont, nametable, selectedAxisValues)
def _updateNameRecords(varfont, nametable, axisValues):
@ -1383,12 +1408,14 @@ def _updateStyleRecords(
# wwsAxes = frozenset(["wght", "wdth", "ital"])
currentFamilyName = nametable.getName(NameID.TYPOGRAPHIC_FAMILY_NAME, *lang) or \
nametable.getName(NameID.FAMILY_NAME, *lang)
if not currentFamilyName:
return
currentFamilyName = currentFamilyName.toUnicode()
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])
@ -1396,7 +1423,7 @@ def _updateStyleRecords(
nameIDs = {
NameID.FAMILY_NAME: currentFamilyName,
NameID.SUBFAMILY_NAME: ribbiName or "Regular"
NameID.SUBFAMILY_NAME: ribbiName or nametable.getName(NameID.SUBFAMILY_NAME, *lang).toUnicode()
}
if nonRibbiAxisValues:
nameIDs[NameID.FAMILY_NAME] = f"{currentFamilyName} {nonRibbiName}"

View File

@ -1924,7 +1924,8 @@ def _get_name_records(varfont):
}
def test_updateNameTable(varfont):
def test_updateNameTable_with_registered_axes(varfont):
# Regular
instancer.updateNameTable(varfont, {"wght": 400, "wdth": 100})
names = _get_name_records(varfont)
assert names[(1, 3, 1, 0x409)] == "Test Variable Font"
@ -1933,6 +1934,7 @@ def test_updateNameTable(varfont):
assert (16, 3, 1, 0x409) not in names
assert (17, 3, 1, 0x409) not in names
# Black
instancer.updateNameTable(varfont, {"wght": 900, "wdth": 100})
names = _get_name_records(varfont)
assert names[(1, 3, 1, 0x409)] == "Test Variable Font Black"
@ -1941,6 +1943,7 @@ def test_updateNameTable(varfont):
assert names[(16, 3, 1, 0x409)] == "Test Variable Font"
assert names[(17, 3, 1, 0x409)] == "Black"
# Thin
instancer.updateNameTable(varfont, {"wght": 100, "wdth": 100})
names = _get_name_records(varfont)
assert names[(1, 3, 1, 0x409)] == "Test Variable Font Thin"
@ -1949,8 +1952,47 @@ def test_updateNameTable(varfont):
assert names[(16, 3, 1, 0x409)] == "Test Variable Font"
assert names[(17, 3, 1, 0x409)] == "Thin"
# TODO (Marc F) this doesn't work because our test font is using Format 4 for wdth axis
instancer.updateNameTable(varfont, {"wdth": 79, "wdth": 400})
# Thin Condensed
instancer.updateNameTable(varfont, {"wdth": 79, "wght": 100})
names = _get_name_records(varfont)
assert names[(1, 3, 1, 0x409)] == "Test Variable Font Thin Condensed"
assert names[(2, 3, 1, 0x409)] == "Regular"
assert names[(6, 3, 1, 0x409)] == "TestVariableFont-ThinCondensed"
assert names[(16, 3, 1, 0x409)] == "Test Variable Font"
assert names[(17, 3, 1, 0x409)] == "Thin Condensed"
def test_updateNameTable_with_multilingual_names(varfont):
name = varfont["name"]
name.setName("Test Variable Font", 1, 3, 1, 0x405)
name.setName("Normal", 2, 3, 1, 0x405)
name.setName("Normal", 261, 3, 1, 0x405) # nameID 261=Regular STAT entry
name.setName("Negreta",266, 3, 1, 0x405) # nameID 266=Black STAT entry
name.setName("Zhuštěné", 279, 3, 1, 0x405) # nameID 279=Condensed STAT entry
# Regular | Normal
instancer.updateNameTable(varfont, {"wdth": 100, "wght": 400})
names = _get_name_records(varfont)
assert names[(1, 3, 1, 0x405)] == "Test Variable Font"
assert names[(2, 3, 1, 0x405)] == "Normal"
assert (16, 3, 1, 0x405) not in names
assert (17, 3, 1, 0x405) not in names
# Black | Negreta
instancer.updateNameTable(varfont, {"wdth": 100, "wght": 900})
names = _get_name_records(varfont)
assert names[(1, 3, 1, 0x405)] == "Test Variable Font Negreta"
assert names[(2, 3, 1, 0x405)] == "Normal"
assert names[(16, 3, 1, 0x405)] == "Test Variable Font"
assert names[(17, 3, 1, 0x405)] == "Negreta"
# Black Condensed | Negreta Zhuštěné
instancer.updateNameTable(varfont, {"wdth": 79, "wght": 900})
names = _get_name_records(varfont)
assert names[(1, 3, 1, 0x405)] == "Test Variable Font Negreta Zhuštěné"
assert names[(2, 3, 1, 0x405)] == "Normal"
assert names[(16, 3, 1, 0x405)] == "Test Variable Font"
assert names[(17, 3, 1, 0x405)] == "Negreta Zhuštěné"
def test_sanityCheckVariableTables(varfont):