From 7a22c0fb07f1178f2ef22c4a5ddaa08a0cad6b93 Mon Sep 17 00:00:00 2001 From: Behdad Esfahbod Date: Sat, 21 Aug 2021 11:16:27 -0600 Subject: [PATCH] [ttFont] Add getGlyphIDMany(); use in otLayout Fixes https://github.com/fonttools/fonttools/issues/1536 Superced https://github.com/fonttools/fonttools/pull/1654 Part of https://github.com/fonttools/fonttools/pull/2398 --- Lib/fontTools/misc/testTools.py | 3 ++ Lib/fontTools/ttLib/tables/otConverters.py | 11 ++----- Lib/fontTools/ttLib/tables/otTables.py | 5 ++-- Lib/fontTools/ttLib/ttFont.py | 35 +++++++++++++++------- 4 files changed, 31 insertions(+), 23 deletions(-) diff --git a/Lib/fontTools/misc/testTools.py b/Lib/fontTools/misc/testTools.py index dcd82cb56..86fa2ea87 100644 --- a/Lib/fontTools/misc/testTools.py +++ b/Lib/fontTools/misc/testTools.py @@ -57,6 +57,9 @@ class FakeFont: def getGlyphID(self, name): return self.reverseGlyphOrderDict_[name] + def getGlyphIDMany(self, lst): + return [self.getGlyphID(gid) for gid in lst] + def getGlyphName(self, glyphID): if glyphID < len(self.glyphOrder_): return self.glyphOrder_[glyphID] diff --git a/Lib/fontTools/ttLib/tables/otConverters.py b/Lib/fontTools/ttLib/tables/otConverters.py index 953df5778..6c8024292 100644 --- a/Lib/fontTools/ttLib/tables/otConverters.py +++ b/Lib/fontTools/ttLib/tables/otConverters.py @@ -347,18 +347,11 @@ class GlyphID(SimpleValue): staticSize = 2 typecode = "H" def readArray(self, reader, font, tableDict, count): - gids = reader.readArray(self.typecode, self.staticSize, count) - return font.getGlyphNameMany(gids) + return font.getGlyphNameMany(reader.readArray(self.typecode, self.staticSize, count)) def read(self, reader, font, tableDict): return font.getGlyphName(reader.readValue(self.typecode, self.staticSize)) def writeArray(self, writer, font, tableDict, values): - glyphMap = font.getReverseGlyphMap() - try: - values = [glyphMap[glyphname] for glyphname in values] - except KeyError: - # Slower, but will not throw a KeyError on an out-of-range glyph name. - values = [font.getGlyphID(glyphname) for glyphname in values] - writer.writeArray(self.typecode, values) + writer.writeArray(self.typecode, font.getGlyphIDMany(values)) def write(self, writer, font, tableDict, value, repeatIndex=None): writer.writeValue(self.typecode, font.getGlyphID(value)) diff --git a/Lib/fontTools/ttLib/tables/otTables.py b/Lib/fontTools/ttLib/tables/otTables.py index 9dbbe8153..5b7ddcce9 100644 --- a/Lib/fontTools/ttLib/tables/otTables.py +++ b/Lib/fontTools/ttLib/tables/otTables.py @@ -549,10 +549,9 @@ class Coverage(FormatSwitchingBaseTable): glyphs = self.glyphs = [] format = 1 rawTable = {"GlyphArray": glyphs} - getGlyphID = font.getGlyphID if glyphs: # find out whether Format 2 is more compact or not - glyphIDs = [getGlyphID(glyphName) for glyphName in glyphs ] + glyphIDs = font.getGlyphIDMany(glyphs) brokenOrder = sorted(glyphIDs) != glyphIDs last = glyphIDs[0] @@ -751,7 +750,7 @@ class SingleSubst(FormatSwitchingBaseTable): input = _getGlyphsFromCoverageTable(rawTable["Coverage"]) if self.Format == 1: delta = rawTable["DeltaGlyphID"] - inputGIDS = [ font.getGlyphID(name) for name in input ] + inputGIDS = font.getGlyphIDMany(input) outGIDS = [ (glyphID + delta) % 65536 for glyphID in inputGIDS ] outNames = font.getGlyphNameMany(outGIDS) for inp, out in zip(input, outNames): diff --git a/Lib/fontTools/ttLib/ttFont.py b/Lib/fontTools/ttLib/ttFont.py index 8ea0c5f81..45bc4966f 100644 --- a/Lib/fontTools/ttLib/ttFont.py +++ b/Lib/fontTools/ttLib/ttFont.py @@ -533,6 +533,7 @@ class TTFont(object): return self.getGlyphOrder()[glyphID] except IndexError: return "glyph%.5d" % glyphID + def getGlyphNameMany(self, lst): glyphOrder = self.getGlyphOrder(); cnt = len(glyphOrder) @@ -540,14 +541,15 @@ class TTFont(object): for gid in lst] def getGlyphID(self, glyphName): - if not hasattr(self, "_reverseGlyphOrderDict"): - self._buildReverseGlyphOrderDict() glyphOrder = self.getGlyphOrder() - d = self._reverseGlyphOrderDict - if glyphName not in d: + d = self.getReverseGlyphMap() + + glyphID = d.get(glyphName) + + if glyphID is None: + # TODO This check is really expensive if glyphName in glyphOrder: - self._buildReverseGlyphOrderDict() - return self.getGlyphID(glyphName) + return self._buildReverseGlyphOrderDict()[glyphName] else: # Handle glyphXXX only if glyphName[:5] == "glyph": @@ -556,12 +558,22 @@ class TTFont(object): except (NameError, ValueError): raise KeyError(glyphName) - glyphID = d[glyphName] if glyphName != glyphOrder[glyphID]: - self._buildReverseGlyphOrderDict() - return self.getGlyphID(glyphName) + return self._buildReverseGlyphOrderDict()[glyphName] + return glyphID + def getGlyphIDMany(self, lst): + d = self.getReverseGlyphMap() + + glyphIDs = [d.get(glyphName) for glyphName in lst] + + if any(glyphID is None for glyphID in glyphIDs): + getGlyphID = self.getGlyphID + return [getGlyphID(glyphName) for glyphName in lst] + + return glyphIDs + def getReverseGlyphMap(self, rebuild=False): if rebuild or not hasattr(self, "_reverseGlyphOrderDict"): self._buildReverseGlyphOrderDict() @@ -570,8 +582,9 @@ class TTFont(object): def _buildReverseGlyphOrderDict(self): self._reverseGlyphOrderDict = d = {} glyphOrder = self.getGlyphOrder() - for glyphID in range(len(glyphOrder)): - d[glyphOrder[glyphID]] = glyphID + for glyphID,glyphName in enumerate(self.getGlyphOrder()): + d[glyphName] = glyphID + return d def _writeTable(self, tag, writer, done, tableCache=None): """Internal helper function for self.save(). Keeps track of