Fix for #1985: ensure that the AxisNameID in the STAT table is > 255 (#1986)

Fix for #1985:
* ensure that the AxisNameID in the STAT table is not less than 256. This needed an additional argument to the addMultiLingualName() name table method.
* fvar axis name IDs must also not be less than 256, just like STAT axis names.
This commit is contained in:
Just van Rossum 2020-06-08 19:39:28 +02:00 committed by GitHub
parent df1b499dbc
commit 90c7c7fae1
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 31 additions and 8 deletions

View File

@ -784,7 +784,7 @@ def _buildAxisRecords(axes, nameTable):
for axisRecordIndex, axisDict in enumerate(axes):
axis = ot.AxisRecord()
axis.AxisTag = axisDict["tag"]
axis.AxisNameID = _addName(nameTable, axisDict["name"])
axis.AxisNameID = _addName(nameTable, axisDict["name"], 256)
axis.AxisOrdering = axisDict.get("ordering", axisRecordIndex)
axisRecords.append(axis)
@ -837,7 +837,7 @@ def _buildAxisValuesFormat4(locations, axes, nameTable):
return axisValues
def _addName(nameTable, value):
def _addName(nameTable, value, minNameID=0):
if isinstance(value, int):
# Already a nameID
return value
@ -847,4 +847,4 @@ def _addName(nameTable, value):
names = value
else:
raise TypeError("value must be int, str or dict")
return nameTable.addMultilingualName(names)
return nameTable.addMultilingualName(names, minNameID=minNameID)

View File

@ -184,7 +184,7 @@ class table__n_a_m_e(DefaultTable.DefaultTable):
raise ValueError("nameID must be less than 32768")
return nameID
def findMultilingualName(self, names, windows=True, mac=True):
def findMultilingualName(self, names, windows=True, mac=True, minNameID=0):
"""Return the name ID of an existing multilingual name that
matches the 'names' dictionary, or None if not found.
@ -198,6 +198,9 @@ class table__n_a_m_e(DefaultTable.DefaultTable):
platEncID=1.
If 'mac' is True, the returned name ID is guaranteed to exist
for all requested languages for platformID=1 and platEncID=0.
The returned name ID will not be less than the 'minNameID'
argument.
"""
# Gather the set of requested
# (string, platformID, platEncID, langID)
@ -227,7 +230,7 @@ class table__n_a_m_e(DefaultTable.DefaultTable):
name.platEncID, name.langID)
except UnicodeDecodeError:
continue
if key in reqNameSet:
if key in reqNameSet and name.nameID >= minNameID:
nameSet = matchingNames.setdefault(name.nameID, set())
nameSet.add(key)
@ -239,7 +242,7 @@ class table__n_a_m_e(DefaultTable.DefaultTable):
return None # not found
def addMultilingualName(self, names, ttFont=None, nameID=None,
windows=True, mac=True):
windows=True, mac=True, minNameID=0):
"""Add a multilingual name, returning its name ID
'names' is a dictionary with the name in multiple languages,
@ -258,12 +261,16 @@ class table__n_a_m_e(DefaultTable.DefaultTable):
If 'windows' is True, a platformID=3 name record will be added.
If 'mac' is True, a platformID=1 name record will be added.
If the 'nameID' argument is None, the created nameID will not
be less than the 'minNameID' argument.
"""
if not hasattr(self, 'names'):
self.names = []
if nameID is None:
# Reuse nameID if possible
nameID = self.findMultilingualName(names, windows=windows, mac=mac)
nameID = self.findMultilingualName(
names, windows=windows, mac=mac, minNameID=minNameID)
if nameID is not None:
return nameID
nameID = self._findUnusedNameID()

View File

@ -71,7 +71,7 @@ def _add_fvar(font, axes, instances):
axis.axisTag = Tag(a.tag)
# TODO Skip axes that have no variation.
axis.minValue, axis.defaultValue, axis.maxValue = a.minimum, a.default, a.maximum
axis.axisNameID = nameTable.addMultilingualName(a.labelNames, font)
axis.axisNameID = nameTable.addMultilingualName(a.labelNames, font, minNameID=256)
axis.flags = int(a.hidden)
fvar.axes.append(axis)

View File

@ -1371,6 +1371,11 @@ def test_buildStatTable(axes, axisValues, elidedFallbackName, expected_ttx):
font = ttLib.TTFont()
font["name"] = ttLib.newTable("name")
font["name"].names = []
# https://github.com/fonttools/fonttools/issues/1985
# Add nameID < 256 that matches a test axis name, to test whether
# the nameID is not reused: AxisNameIDs must be > 255 according
# to the spec.
font["name"].addMultilingualName(dict(en="ABCDTest"), nameID=6)
builder.buildStatTable(font, axes, axisValues, elidedFallbackName)
f = io.StringIO()
font.saveXML(f, tables=["STAT"])

View File

@ -294,6 +294,17 @@ class NameTableTest(unittest.TestCase):
nameTable.addMultilingualName({"en": "A", "la": "ⱾƤℚⱤ"})
captor.assertRegex("cannot store language la into 'ltag' table")
def test_addMultilingualName_minNameID(self):
table = table__n_a_m_e()
names, namesSubSet, namesSuperSet = self._get_test_names()
nameID = table.addMultilingualName(names, nameID=2)
self.assertEqual(nameID, 2)
nameID = table.addMultilingualName(names)
self.assertEqual(nameID, 2)
nameID = table.addMultilingualName(names, minNameID=256)
self.assertGreaterEqual(nameID, 256)
self.assertEqual(nameID, table.findMultilingualName(names, minNameID=256))
def test_decompile_badOffset(self):
# https://github.com/fonttools/fonttools/issues/525
table = table__n_a_m_e()