[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")
|
raise ValueError("nameID must be less than 32768")
|
||||||
return nameID
|
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,
|
def addMultilingualName(self, names, ttFont=None, nameID=None,
|
||||||
windows=True, mac=True):
|
windows=True, mac=True):
|
||||||
"""Add a multilingual name, returning its name ID
|
"""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.
|
names that otherwise cannot get encoded at all.
|
||||||
|
|
||||||
'nameID' is the name ID to be used, or None to let the library
|
'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 'windows' is True, a platformID=3 name record will be added.
|
||||||
If 'mac' is True, a platformID=1 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'):
|
if not hasattr(self, 'names'):
|
||||||
self.names = []
|
self.names = []
|
||||||
if nameID is None:
|
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()
|
nameID = self._findUnusedNameID()
|
||||||
# TODO: Should minimize BCP 47 language codes.
|
# TODO: Should minimize BCP 47 language codes.
|
||||||
# https://github.com/fonttools/fonttools/issues/930
|
# https://github.com/fonttools/fonttools/issues/930
|
||||||
|
@ -144,6 +144,48 @@ class NameTableTest(unittest.TestCase):
|
|||||||
rec2 = table.getName(2, 1, 0, 0)
|
rec2 = table.getName(2, 1, 0, 0)
|
||||||
self.assertEqual(str(rec2), "Regular")
|
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):
|
def test_addMultilingualName(self):
|
||||||
# Microsoft Windows has language codes for “English” (en)
|
# Microsoft Windows has language codes for “English” (en)
|
||||||
# and for “Standard German as used in Switzerland” (de-CH).
|
# and for “Standard German as used in Switzerland” (de-CH).
|
||||||
|
Loading…
x
Reference in New Issue
Block a user