Skip to content

Commit

Permalink
Fix for #1985: ensure that the AxisNameID in the STAT table is > 255 (#…
Browse files Browse the repository at this point in the history
…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.
  • Loading branch information
justvanrossum committed Jun 8, 2020
1 parent df1b499 commit 90c7c7f
Show file tree
Hide file tree
Showing 5 changed files with 31 additions and 8 deletions.
6 changes: 3 additions & 3 deletions Lib/fontTools/otlLib/builder.py
Original file line number Diff line number Diff line change
Expand Up @@ -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)

Expand Down Expand Up @@ -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
Expand All @@ -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)
15 changes: 11 additions & 4 deletions Lib/fontTools/ttLib/tables/_n_a_m_e.py
Original file line number Diff line number Diff line change
Expand Up @@ -184,7 +184,7 @@ def _findUnusedNameID(self, minNameID=256):
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.
Expand All @@ -198,6 +198,9 @@ def findMultilingualName(self, names, windows=True, mac=True):
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)
Expand Down Expand Up @@ -227,7 +230,7 @@ def findMultilingualName(self, names, windows=True, mac=True):
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)

Expand All @@ -239,7 +242,7 @@ def findMultilingualName(self, names, windows=True, mac=True):
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,
Expand All @@ -258,12 +261,16 @@ def addMultilingualName(self, names, ttFont=None, nameID=None,
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()
Expand Down
2 changes: 1 addition & 1 deletion Lib/fontTools/varLib/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -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)

Expand Down
5 changes: 5 additions & 0 deletions Tests/otlLib/builder_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -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"])
Expand Down
11 changes: 11 additions & 0 deletions Tests/ttLib/tables/_n_a_m_e_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -294,6 +294,17 @@ def test_addMultilingualName_noTTFont(self):
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()
Expand Down

0 comments on commit 90c7c7f

Please sign in to comment.