[ttLib.name] Add nameTable.findMultilingualName() method (#1921)
* add nameTable.findMultilingualName(), to find an existing multilingual name * Make addMultilingualName() reuse nameIDs if possible when asking for a new nameID, by calling findMultilingualName()
This commit is contained in:
parent
37ff36bdd1
commit
4febf38be2
@ -184,6 +184,57 @@ 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):
|
||||
"""Return the name ID of an existing multilingual name that
|
||||
matches the 'names' dictionary, or None if not found.
|
||||
|
||||
'names' is a dictionary with the name in multiple languages,
|
||||
such as {'en': 'Pale', 'de': 'Blaß', 'de-CH': 'Blass'}.
|
||||
The keys can be arbitrary IETF BCP 47 language codes;
|
||||
the values are Unicode strings.
|
||||
|
||||
If 'windows' is True, the returned name ID is guaranteed
|
||||
exist for all requested languages for platformID=3 and
|
||||
platEncID=1.
|
||||
If 'mac' is True, the returned name ID is guaranteed to exist
|
||||
for all requested languages for platformID=1 and platEncID=0.
|
||||
"""
|
||||
# Gather the set of requested
|
||||
# (string, platformID, platEncID, langID)
|
||||
# tuples
|
||||
reqNameSet = set()
|
||||
for lang, name in sorted(names.items()):
|
||||
if windows:
|
||||
windowsName = _makeWindowsName(name, None, lang)
|
||||
if windowsName is not None:
|
||||
reqNameSet.add((windowsName.string,
|
||||
windowsName.platformID,
|
||||
windowsName.platEncID,
|
||||
windowsName.langID))
|
||||
if mac:
|
||||
macName = _makeMacName(name, None, lang)
|
||||
if macName is not None:
|
||||
reqNameSet.add((macName.string,
|
||||
macName.platformID,
|
||||
macName.platEncID,
|
||||
macName.langID))
|
||||
|
||||
# Collect matching name IDs
|
||||
matchingNames = dict()
|
||||
for name in self.names:
|
||||
key = (name.string, name.platformID,
|
||||
name.platEncID, name.langID)
|
||||
if key in reqNameSet:
|
||||
nameSet = matchingNames.setdefault(name.nameID, set())
|
||||
nameSet.add(key)
|
||||
|
||||
# Return the first name ID that defines all requested strings
|
||||
for nameID, nameSet in sorted(matchingNames.items()):
|
||||
if nameSet == reqNameSet:
|
||||
return nameID
|
||||
|
||||
return None # not found
|
||||
|
||||
def addMultilingualName(self, names, ttFont=None, nameID=None,
|
||||
windows=True, mac=True):
|
||||
"""Add a multilingual name, returning its name ID
|
||||
@ -199,7 +250,8 @@ class table__n_a_m_e(DefaultTable.DefaultTable):
|
||||
names that otherwise cannot get encoded at all.
|
||||
|
||||
'nameID' is the name ID to be used, or None to let the library
|
||||
pick an unused name ID.
|
||||
find an existing set of name records that match, or pick an
|
||||
unused name ID.
|
||||
|
||||
If 'windows' is True, a platformID=3 name record will be added.
|
||||
If 'mac' is True, a platformID=1 name record will be added.
|
||||
@ -207,6 +259,10 @@ class table__n_a_m_e(DefaultTable.DefaultTable):
|
||||
if not hasattr(self, 'names'):
|
||||
self.names = []
|
||||
if nameID is None:
|
||||
# Reuse nameID if possible
|
||||
nameID = self.findMultilingualName(names, windows=windows, mac=mac)
|
||||
if nameID is not None:
|
||||
return nameID
|
||||
nameID = self._findUnusedNameID()
|
||||
# TODO: Should minimize BCP 47 language codes.
|
||||
# https://github.com/fonttools/fonttools/issues/930
|
||||
|
@ -144,6 +144,48 @@ class NameTableTest(unittest.TestCase):
|
||||
rec2 = table.getName(2, 1, 0, 0)
|
||||
self.assertEqual(str(rec2), "Regular")
|
||||
|
||||
@staticmethod
|
||||
def _get_test_names():
|
||||
names = {
|
||||
"en": "Width",
|
||||
"de-CH": "Breite",
|
||||
"gsw-LI": "Bräiti",
|
||||
}
|
||||
namesSubSet = names.copy()
|
||||
del namesSubSet["gsw-LI"]
|
||||
namesSuperSet = names.copy()
|
||||
namesSuperSet["nl"] = "Breedte"
|
||||
return names, namesSubSet, namesSuperSet
|
||||
|
||||
def test_findMultilingualName(self):
|
||||
table = table__n_a_m_e()
|
||||
names, namesSubSet, namesSuperSet = self._get_test_names()
|
||||
nameID = table.addMultilingualName(names)
|
||||
assert nameID is not None
|
||||
self.assertEqual(nameID, table.findMultilingualName(names))
|
||||
self.assertEqual(nameID, table.findMultilingualName(namesSubSet))
|
||||
self.assertEqual(None, table.findMultilingualName(namesSuperSet))
|
||||
|
||||
def test_addMultilingualNameReuse(self):
|
||||
table = table__n_a_m_e()
|
||||
names, namesSubSet, namesSuperSet = self._get_test_names()
|
||||
nameID = table.addMultilingualName(names)
|
||||
assert nameID is not None
|
||||
self.assertEqual(nameID, table.addMultilingualName(names))
|
||||
self.assertEqual(nameID, table.addMultilingualName(namesSubSet))
|
||||
self.assertNotEqual(None, table.addMultilingualName(namesSuperSet))
|
||||
|
||||
def test_findMultilingualNameNoMac(self):
|
||||
table = table__n_a_m_e()
|
||||
names, namesSubSet, namesSuperSet = self._get_test_names()
|
||||
nameID = table.addMultilingualName(names, mac=False)
|
||||
assert nameID is not None
|
||||
self.assertEqual(nameID, table.findMultilingualName(names, mac=False))
|
||||
self.assertEqual(None, table.findMultilingualName(names))
|
||||
self.assertEqual(nameID, table.findMultilingualName(namesSubSet, mac=False))
|
||||
self.assertEqual(None, table.findMultilingualName(namesSubSet))
|
||||
self.assertEqual(None, table.findMultilingualName(namesSuperSet))
|
||||
|
||||
def test_addMultilingualName(self):
|
||||
# Microsoft Windows has language codes for “English” (en)
|
||||
# and for “Standard German as used in Switzerland” (de-CH).
|
||||
|
Loading…
x
Reference in New Issue
Block a user