Merge pull request #2398 from fonttools/vid
Clean up virtual GID handling
This commit is contained in:
commit
a3f988fbf6
@ -57,11 +57,16 @@ class FakeFont:
|
|||||||
def getGlyphID(self, name):
|
def getGlyphID(self, name):
|
||||||
return self.reverseGlyphOrderDict_[name]
|
return self.reverseGlyphOrderDict_[name]
|
||||||
|
|
||||||
|
def getGlyphIDMany(self, lst):
|
||||||
|
return [self.getGlyphID(gid) for gid in lst]
|
||||||
|
|
||||||
def getGlyphName(self, glyphID):
|
def getGlyphName(self, glyphID):
|
||||||
if glyphID < len(self.glyphOrder_):
|
if glyphID < len(self.glyphOrder_):
|
||||||
return self.glyphOrder_[glyphID]
|
return self.glyphOrder_[glyphID]
|
||||||
else:
|
else:
|
||||||
return "glyph%.5d" % glyphID
|
return "glyph%.5d" % glyphID
|
||||||
|
def getGlyphNameMany(self, lst):
|
||||||
|
return [self.getGlyphName(gid) for gid in lst]
|
||||||
|
|
||||||
def getGlyphOrder(self):
|
def getGlyphOrder(self):
|
||||||
return self.glyphOrder_
|
return self.glyphOrder_
|
||||||
@ -136,7 +141,7 @@ class MockFont(object):
|
|||||||
self._reverseGlyphOrder = AllocatingDict({'.notdef': 0})
|
self._reverseGlyphOrder = AllocatingDict({'.notdef': 0})
|
||||||
self.lazy = False
|
self.lazy = False
|
||||||
|
|
||||||
def getGlyphID(self, glyph, requireReal=None):
|
def getGlyphID(self, glyph):
|
||||||
gid = self._reverseGlyphOrder[glyph]
|
gid = self._reverseGlyphOrder[glyph]
|
||||||
return gid
|
return gid
|
||||||
|
|
||||||
|
@ -2864,7 +2864,6 @@ class Subsetter(object):
|
|||||||
glyphOrder = [g for g in glyphOrder if font.getGlyphID(g) <= self.last_retained_order]
|
glyphOrder = [g for g in glyphOrder if font.getGlyphID(g) <= self.last_retained_order]
|
||||||
|
|
||||||
font.setGlyphOrder(glyphOrder)
|
font.setGlyphOrder(glyphOrder)
|
||||||
font._buildReverseGlyphOrderDict()
|
|
||||||
|
|
||||||
|
|
||||||
def _prune_post_subset(self, font):
|
def _prune_post_subset(self, font):
|
||||||
@ -2913,13 +2912,11 @@ class Subsetter(object):
|
|||||||
@timer("load font")
|
@timer("load font")
|
||||||
def load_font(fontFile,
|
def load_font(fontFile,
|
||||||
options,
|
options,
|
||||||
allowVID=False,
|
|
||||||
checkChecksums=0,
|
checkChecksums=0,
|
||||||
dontLoadGlyphNames=False,
|
dontLoadGlyphNames=False,
|
||||||
lazy=True):
|
lazy=True):
|
||||||
|
|
||||||
font = ttLib.TTFont(fontFile,
|
font = ttLib.TTFont(fontFile,
|
||||||
allowVID=allowVID,
|
|
||||||
checkChecksums=checkChecksums,
|
checkChecksums=checkChecksums,
|
||||||
recalcBBoxes=options.recalc_bounds,
|
recalcBBoxes=options.recalc_bounds,
|
||||||
recalcTimestamp=options.recalc_timestamp,
|
recalcTimestamp=options.recalc_timestamp,
|
||||||
|
@ -14,15 +14,11 @@ log = logging.getLogger(__name__)
|
|||||||
|
|
||||||
def _make_map(font, chars, gids):
|
def _make_map(font, chars, gids):
|
||||||
assert len(chars) == len(gids)
|
assert len(chars) == len(gids)
|
||||||
|
glyphNames = font.getGlyphNameMany(gids)
|
||||||
cmap = {}
|
cmap = {}
|
||||||
glyphOrder = font.getGlyphOrder()
|
for char,gid,name in zip(chars,gids,glyphNames):
|
||||||
for char,gid in zip(chars,gids):
|
|
||||||
if gid == 0:
|
if gid == 0:
|
||||||
continue
|
continue
|
||||||
try:
|
|
||||||
name = glyphOrder[gid]
|
|
||||||
except IndexError:
|
|
||||||
name = font.getGlyphName(gid)
|
|
||||||
cmap[char] = name
|
cmap[char] = name
|
||||||
return cmap
|
return cmap
|
||||||
|
|
||||||
|
@ -347,24 +347,11 @@ class GlyphID(SimpleValue):
|
|||||||
staticSize = 2
|
staticSize = 2
|
||||||
typecode = "H"
|
typecode = "H"
|
||||||
def readArray(self, reader, font, tableDict, count):
|
def readArray(self, reader, font, tableDict, count):
|
||||||
glyphOrder = font.getGlyphOrder()
|
return font.getGlyphNameMany(reader.readArray(self.typecode, self.staticSize, count))
|
||||||
gids = reader.readArray(self.typecode, self.staticSize, count)
|
|
||||||
try:
|
|
||||||
l = [glyphOrder[gid] for gid in gids]
|
|
||||||
except IndexError:
|
|
||||||
# Slower, but will not throw an IndexError on an invalid glyph id.
|
|
||||||
l = [font.getGlyphName(gid) for gid in gids]
|
|
||||||
return l
|
|
||||||
def read(self, reader, font, tableDict):
|
def read(self, reader, font, tableDict):
|
||||||
return font.getGlyphName(reader.readValue(self.typecode, self.staticSize))
|
return font.getGlyphName(reader.readValue(self.typecode, self.staticSize))
|
||||||
def writeArray(self, writer, font, tableDict, values):
|
def writeArray(self, writer, font, tableDict, values):
|
||||||
glyphMap = font.getReverseGlyphMap()
|
writer.writeArray(self.typecode, font.getGlyphIDMany(values))
|
||||||
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)
|
|
||||||
def write(self, writer, font, tableDict, value, repeatIndex=None):
|
def write(self, writer, font, tableDict, value, repeatIndex=None):
|
||||||
writer.writeValue(self.typecode, font.getGlyphID(value))
|
writer.writeValue(self.typecode, font.getGlyphID(value))
|
||||||
|
|
||||||
@ -1222,8 +1209,7 @@ class STXHeader(BaseConverter):
|
|||||||
def _readLigatures(self, reader, font):
|
def _readLigatures(self, reader, font):
|
||||||
limit = len(reader.data)
|
limit = len(reader.data)
|
||||||
numLigatureGlyphs = (limit - reader.pos) // 2
|
numLigatureGlyphs = (limit - reader.pos) // 2
|
||||||
return [font.getGlyphName(g)
|
return font.getGlyphNameMany(reader.readUShortArray(numLigatureGlyphs))
|
||||||
for g in reader.readUShortArray(numLigatureGlyphs)]
|
|
||||||
|
|
||||||
def _countPerGlyphLookups(self, table):
|
def _countPerGlyphLookups(self, table):
|
||||||
# Somewhat annoyingly, the morx table does not encode
|
# Somewhat annoyingly, the morx table does not encode
|
||||||
|
@ -425,8 +425,7 @@ class InsertionMorphAction(AATAction):
|
|||||||
return []
|
return []
|
||||||
reader = actionReader.getSubReader(
|
reader = actionReader.getSubReader(
|
||||||
actionReader.pos + index * 2)
|
actionReader.pos + index * 2)
|
||||||
return [font.getGlyphName(glyphID)
|
return font.getGlyphNameMany(reader.readUShortArray(count))
|
||||||
for glyphID in reader.readUShortArray(count)]
|
|
||||||
|
|
||||||
def toXML(self, xmlWriter, font, attrs, name):
|
def toXML(self, xmlWriter, font, attrs, name):
|
||||||
xmlWriter.begintag(name, **attrs)
|
xmlWriter.begintag(name, **attrs)
|
||||||
@ -521,12 +520,10 @@ class Coverage(FormatSwitchingBaseTable):
|
|||||||
|
|
||||||
def postRead(self, rawTable, font):
|
def postRead(self, rawTable, font):
|
||||||
if self.Format == 1:
|
if self.Format == 1:
|
||||||
# TODO only allow glyphs that are valid?
|
|
||||||
self.glyphs = rawTable["GlyphArray"]
|
self.glyphs = rawTable["GlyphArray"]
|
||||||
elif self.Format == 2:
|
elif self.Format == 2:
|
||||||
glyphs = self.glyphs = []
|
glyphs = self.glyphs = []
|
||||||
ranges = rawTable["RangeRecord"]
|
ranges = rawTable["RangeRecord"]
|
||||||
glyphOrder = font.getGlyphOrder()
|
|
||||||
# Some SIL fonts have coverage entries that don't have sorted
|
# Some SIL fonts have coverage entries that don't have sorted
|
||||||
# StartCoverageIndex. If it is so, fixup and warn. We undo
|
# StartCoverageIndex. If it is so, fixup and warn. We undo
|
||||||
# this when writing font out.
|
# this when writing font out.
|
||||||
@ -536,25 +533,11 @@ class Coverage(FormatSwitchingBaseTable):
|
|||||||
ranges = sorted_ranges
|
ranges = sorted_ranges
|
||||||
del sorted_ranges
|
del sorted_ranges
|
||||||
for r in ranges:
|
for r in ranges:
|
||||||
assert r.StartCoverageIndex == len(glyphs), \
|
|
||||||
(r.StartCoverageIndex, len(glyphs))
|
|
||||||
start = r.Start
|
start = r.Start
|
||||||
end = r.End
|
end = r.End
|
||||||
try:
|
startID = font.getGlyphID(start)
|
||||||
startID = font.getGlyphID(start, requireReal=True)
|
endID = font.getGlyphID(end) + 1
|
||||||
except KeyError:
|
glyphs.extend(font.getGlyphNameMany(range(startID, endID)))
|
||||||
log.warning("Coverage table has start glyph ID out of range: %s.", start)
|
|
||||||
continue
|
|
||||||
try:
|
|
||||||
endID = font.getGlyphID(end, requireReal=True) + 1
|
|
||||||
except KeyError:
|
|
||||||
# Apparently some tools use 65535 to "match all" the range
|
|
||||||
if end != 'glyph65535':
|
|
||||||
log.warning("Coverage table has end glyph ID out of range: %s.", end)
|
|
||||||
# NOTE: We clobber out-of-range things here. There are legit uses for those,
|
|
||||||
# but none that we have seen in the wild.
|
|
||||||
endID = len(glyphOrder)
|
|
||||||
glyphs.extend(glyphOrder[glyphID] for glyphID in range(startID, endID))
|
|
||||||
else:
|
else:
|
||||||
self.glyphs = []
|
self.glyphs = []
|
||||||
log.warning("Unknown Coverage format: %s", self.Format)
|
log.warning("Unknown Coverage format: %s", self.Format)
|
||||||
@ -566,10 +549,9 @@ class Coverage(FormatSwitchingBaseTable):
|
|||||||
glyphs = self.glyphs = []
|
glyphs = self.glyphs = []
|
||||||
format = 1
|
format = 1
|
||||||
rawTable = {"GlyphArray": glyphs}
|
rawTable = {"GlyphArray": glyphs}
|
||||||
getGlyphID = font.getGlyphID
|
|
||||||
if glyphs:
|
if glyphs:
|
||||||
# find out whether Format 2 is more compact or not
|
# 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
|
brokenOrder = sorted(glyphIDs) != glyphIDs
|
||||||
|
|
||||||
last = glyphIDs[0]
|
last = glyphIDs[0]
|
||||||
@ -768,9 +750,9 @@ class SingleSubst(FormatSwitchingBaseTable):
|
|||||||
input = _getGlyphsFromCoverageTable(rawTable["Coverage"])
|
input = _getGlyphsFromCoverageTable(rawTable["Coverage"])
|
||||||
if self.Format == 1:
|
if self.Format == 1:
|
||||||
delta = rawTable["DeltaGlyphID"]
|
delta = rawTable["DeltaGlyphID"]
|
||||||
inputGIDS = [ font.getGlyphID(name) for name in input ]
|
inputGIDS = font.getGlyphIDMany(input)
|
||||||
outGIDS = [ (glyphID + delta) % 65536 for glyphID in inputGIDS ]
|
outGIDS = [ (glyphID + delta) % 65536 for glyphID in inputGIDS ]
|
||||||
outNames = [ font.getGlyphName(glyphID) for glyphID in outGIDS ]
|
outNames = font.getGlyphNameMany(outGIDS)
|
||||||
for inp, out in zip(input, outNames):
|
for inp, out in zip(input, outNames):
|
||||||
mapping[inp] = out
|
mapping[inp] = out
|
||||||
elif self.Format == 2:
|
elif self.Format == 2:
|
||||||
@ -924,51 +906,30 @@ class ClassDef(FormatSwitchingBaseTable):
|
|||||||
|
|
||||||
def postRead(self, rawTable, font):
|
def postRead(self, rawTable, font):
|
||||||
classDefs = {}
|
classDefs = {}
|
||||||
glyphOrder = font.getGlyphOrder()
|
|
||||||
|
|
||||||
if self.Format == 1:
|
if self.Format == 1:
|
||||||
start = rawTable["StartGlyph"]
|
start = rawTable["StartGlyph"]
|
||||||
classList = rawTable["ClassValueArray"]
|
classList = rawTable["ClassValueArray"]
|
||||||
try:
|
startID = font.getGlyphID(start)
|
||||||
startID = font.getGlyphID(start, requireReal=True)
|
|
||||||
except KeyError:
|
|
||||||
log.warning("ClassDef table has start glyph ID out of range: %s.", start)
|
|
||||||
startID = len(glyphOrder)
|
|
||||||
endID = startID + len(classList)
|
endID = startID + len(classList)
|
||||||
if endID > len(glyphOrder):
|
glyphNames = font.getGlyphNameMany(range(startID, endID))
|
||||||
log.warning("ClassDef table has entries for out of range glyph IDs: %s,%s.",
|
for glyphName, cls in zip(glyphNames, classList):
|
||||||
start, len(classList))
|
|
||||||
# NOTE: We clobber out-of-range things here. There are legit uses for those,
|
|
||||||
# but none that we have seen in the wild.
|
|
||||||
endID = len(glyphOrder)
|
|
||||||
|
|
||||||
for glyphID, cls in zip(range(startID, endID), classList):
|
|
||||||
if cls:
|
if cls:
|
||||||
classDefs[glyphOrder[glyphID]] = cls
|
classDefs[glyphName] = cls
|
||||||
|
|
||||||
elif self.Format == 2:
|
elif self.Format == 2:
|
||||||
records = rawTable["ClassRangeRecord"]
|
records = rawTable["ClassRangeRecord"]
|
||||||
for rec in records:
|
for rec in records:
|
||||||
|
cls = rec.Class
|
||||||
|
if not cls:
|
||||||
|
continue
|
||||||
start = rec.Start
|
start = rec.Start
|
||||||
end = rec.End
|
end = rec.End
|
||||||
cls = rec.Class
|
startID = font.getGlyphID(start)
|
||||||
try:
|
endID = font.getGlyphID(end) + 1
|
||||||
startID = font.getGlyphID(start, requireReal=True)
|
glyphNames = font.getGlyphNameMany(range(startID, endID))
|
||||||
except KeyError:
|
for glyphName in glyphNames:
|
||||||
log.warning("ClassDef table has start glyph ID out of range: %s.", start)
|
classDefs[glyphName] = cls
|
||||||
continue
|
|
||||||
try:
|
|
||||||
endID = font.getGlyphID(end, requireReal=True) + 1
|
|
||||||
except KeyError:
|
|
||||||
# Apparently some tools use 65535 to "match all" the range
|
|
||||||
if end != 'glyph65535':
|
|
||||||
log.warning("ClassDef table has end glyph ID out of range: %s.", end)
|
|
||||||
# NOTE: We clobber out-of-range things here. There are legit uses for those,
|
|
||||||
# but none that we have seen in the wild.
|
|
||||||
endID = len(glyphOrder)
|
|
||||||
for glyphID in range(startID, endID):
|
|
||||||
if cls:
|
|
||||||
classDefs[glyphOrder[glyphID]] = cls
|
|
||||||
else:
|
else:
|
||||||
log.warning("Unknown ClassDef format: %s", self.Format)
|
log.warning("Unknown ClassDef format: %s", self.Format)
|
||||||
self.classDefs = classDefs
|
self.classDefs = classDefs
|
||||||
|
@ -20,7 +20,7 @@ class TTFont(object):
|
|||||||
|
|
||||||
def __init__(self, file=None, res_name_or_index=None,
|
def __init__(self, file=None, res_name_or_index=None,
|
||||||
sfntVersion="\000\001\000\000", flavor=None, checkChecksums=0,
|
sfntVersion="\000\001\000\000", flavor=None, checkChecksums=0,
|
||||||
verbose=None, recalcBBoxes=True, allowVID=False, ignoreDecompileErrors=False,
|
verbose=None, recalcBBoxes=True, allowVID=NotImplemented, ignoreDecompileErrors=False,
|
||||||
recalcTimestamp=True, fontNumber=-1, lazy=None, quiet=None,
|
recalcTimestamp=True, fontNumber=-1, lazy=None, quiet=None,
|
||||||
_tableCache=None):
|
_tableCache=None):
|
||||||
|
|
||||||
@ -61,16 +61,6 @@ class TTFont(object):
|
|||||||
If the recalcTimestamp argument is false, the modified timestamp in the
|
If the recalcTimestamp argument is false, the modified timestamp in the
|
||||||
'head' table will *not* be recalculated upon save/compile.
|
'head' table will *not* be recalculated upon save/compile.
|
||||||
|
|
||||||
If the allowVID argument is set to true, then virtual GID's are
|
|
||||||
supported. Asking for a glyph ID with a glyph name or GID that is not in
|
|
||||||
the font will return a virtual GID. This is valid for GSUB and cmap
|
|
||||||
tables. For SING glyphlets, the cmap table is used to specify Unicode
|
|
||||||
values for virtual GI's used in GSUB/GPOS rules. If the gid N is requested
|
|
||||||
and does not exist in the font, or the glyphname has the form glyphN
|
|
||||||
and does not exist in the font, then N is used as the virtual GID.
|
|
||||||
Else, the first virtual GID is assigned as 0x1000 -1; for subsequent new
|
|
||||||
virtual GIDs, the next is one less than the previous.
|
|
||||||
|
|
||||||
If ignoreDecompileErrors is set to True, exceptions raised in
|
If ignoreDecompileErrors is set to True, exceptions raised in
|
||||||
individual tables during decompilation will be ignored, falling
|
individual tables during decompilation will be ignored, falling
|
||||||
back to the DefaultTable implementation, which simply keeps the
|
back to the DefaultTable implementation, which simply keeps the
|
||||||
@ -92,12 +82,6 @@ class TTFont(object):
|
|||||||
self.recalcTimestamp = recalcTimestamp
|
self.recalcTimestamp = recalcTimestamp
|
||||||
self.tables = {}
|
self.tables = {}
|
||||||
self.reader = None
|
self.reader = None
|
||||||
|
|
||||||
# Permit the user to reference glyphs that are not int the font.
|
|
||||||
self.last_vid = 0xFFFE # Can't make it be 0xFFFF, as the world is full unsigned short integer counters that get incremented after the last seen GID value.
|
|
||||||
self.reverseVIDDict = {}
|
|
||||||
self.VIDDict = {}
|
|
||||||
self.allowVID = allowVID
|
|
||||||
self.ignoreDecompileErrors = ignoreDecompileErrors
|
self.ignoreDecompileErrors = ignoreDecompileErrors
|
||||||
|
|
||||||
if not file:
|
if not file:
|
||||||
@ -429,6 +413,8 @@ class TTFont(object):
|
|||||||
|
|
||||||
def setGlyphOrder(self, glyphOrder):
|
def setGlyphOrder(self, glyphOrder):
|
||||||
self.glyphOrder = glyphOrder
|
self.glyphOrder = glyphOrder
|
||||||
|
if hasattr(self, '_reverseGlyphOrderDict'):
|
||||||
|
del self._reverseGlyphOrderDict
|
||||||
|
|
||||||
def getGlyphOrder(self):
|
def getGlyphOrder(self):
|
||||||
try:
|
try:
|
||||||
@ -544,67 +530,35 @@ class TTFont(object):
|
|||||||
from fontTools.misc import textTools
|
from fontTools.misc import textTools
|
||||||
return textTools.caselessSort(self.getGlyphOrder())
|
return textTools.caselessSort(self.getGlyphOrder())
|
||||||
|
|
||||||
def getGlyphName(self, glyphID, requireReal=False):
|
def getGlyphName(self, glyphID):
|
||||||
try:
|
try:
|
||||||
return self.getGlyphOrder()[glyphID]
|
return self.getGlyphOrder()[glyphID]
|
||||||
except IndexError:
|
except IndexError:
|
||||||
if requireReal or not self.allowVID:
|
|
||||||
# XXX The ??.W8.otf font that ships with OSX uses higher glyphIDs in
|
|
||||||
# the cmap table than there are glyphs. I don't think it's legal...
|
|
||||||
return "glyph%.5d" % glyphID
|
return "glyph%.5d" % glyphID
|
||||||
else:
|
|
||||||
# user intends virtual GID support
|
|
||||||
try:
|
|
||||||
glyphName = self.VIDDict[glyphID]
|
|
||||||
except KeyError:
|
|
||||||
glyphName ="glyph%.5d" % glyphID
|
|
||||||
self.last_vid = min(glyphID, self.last_vid )
|
|
||||||
self.reverseVIDDict[glyphName] = glyphID
|
|
||||||
self.VIDDict[glyphID] = glyphName
|
|
||||||
return glyphName
|
|
||||||
|
|
||||||
def getGlyphID(self, glyphName, requireReal=False):
|
def getGlyphNameMany(self, lst):
|
||||||
if not hasattr(self, "_reverseGlyphOrderDict"):
|
glyphOrder = self.getGlyphOrder();
|
||||||
self._buildReverseGlyphOrderDict()
|
cnt = len(glyphOrder)
|
||||||
glyphOrder = self.getGlyphOrder()
|
return [glyphOrder[gid] if gid < cnt else "glyph%.5d" % gid
|
||||||
d = self._reverseGlyphOrderDict
|
for gid in lst]
|
||||||
if glyphName not in d:
|
|
||||||
if glyphName in glyphOrder:
|
def getGlyphID(self, glyphName):
|
||||||
self._buildReverseGlyphOrderDict()
|
try:
|
||||||
return self.getGlyphID(glyphName)
|
return self.getReverseGlyphMap()[glyphName]
|
||||||
else:
|
except KeyError:
|
||||||
if requireReal:
|
|
||||||
raise KeyError(glyphName)
|
|
||||||
elif not self.allowVID:
|
|
||||||
# Handle glyphXXX only
|
|
||||||
if glyphName[:5] == "glyph":
|
if glyphName[:5] == "glyph":
|
||||||
try:
|
try:
|
||||||
return int(glyphName[5:])
|
return int(glyphName[5:])
|
||||||
except (NameError, ValueError):
|
except (NameError, ValueError):
|
||||||
raise KeyError(glyphName)
|
raise KeyError(glyphName)
|
||||||
else:
|
|
||||||
# user intends virtual GID support
|
|
||||||
try:
|
|
||||||
glyphID = self.reverseVIDDict[glyphName]
|
|
||||||
except KeyError:
|
|
||||||
# if name is in glyphXXX format, use the specified name.
|
|
||||||
if glyphName[:5] == "glyph":
|
|
||||||
try:
|
|
||||||
glyphID = int(glyphName[5:])
|
|
||||||
except (NameError, ValueError):
|
|
||||||
glyphID = None
|
|
||||||
if glyphID is None:
|
|
||||||
glyphID = self.last_vid -1
|
|
||||||
self.last_vid = glyphID
|
|
||||||
self.reverseVIDDict[glyphName] = glyphID
|
|
||||||
self.VIDDict[glyphID] = glyphName
|
|
||||||
return glyphID
|
|
||||||
|
|
||||||
glyphID = d[glyphName]
|
def getGlyphIDMany(self, lst):
|
||||||
if glyphName != glyphOrder[glyphID]:
|
d = self.getReverseGlyphMap()
|
||||||
self._buildReverseGlyphOrderDict()
|
try:
|
||||||
return self.getGlyphID(glyphName)
|
return [d[glyphName] for glyphName in lst]
|
||||||
return glyphID
|
except KeyError:
|
||||||
|
getGlyphID = self.getGlyphID
|
||||||
|
return [getGlyphID(glyphName) for glyphName in lst]
|
||||||
|
|
||||||
def getReverseGlyphMap(self, rebuild=False):
|
def getReverseGlyphMap(self, rebuild=False):
|
||||||
if rebuild or not hasattr(self, "_reverseGlyphOrderDict"):
|
if rebuild or not hasattr(self, "_reverseGlyphOrderDict"):
|
||||||
@ -613,9 +567,9 @@ class TTFont(object):
|
|||||||
|
|
||||||
def _buildReverseGlyphOrderDict(self):
|
def _buildReverseGlyphOrderDict(self):
|
||||||
self._reverseGlyphOrderDict = d = {}
|
self._reverseGlyphOrderDict = d = {}
|
||||||
glyphOrder = self.getGlyphOrder()
|
for glyphID,glyphName in enumerate(self.getGlyphOrder()):
|
||||||
for glyphID in range(len(glyphOrder)):
|
d[glyphName] = glyphID
|
||||||
d[glyphOrder[glyphID]] = glyphID
|
return d
|
||||||
|
|
||||||
def _writeTable(self, tag, writer, done, tableCache=None):
|
def _writeTable(self, tag, writer, done, tableCache=None):
|
||||||
"""Internal helper function for self.save(). Keeps track of
|
"""Internal helper function for self.save(). Keeps track of
|
||||||
@ -820,9 +774,9 @@ class GlyphOrder(object):
|
|||||||
def fromXML(self, name, attrs, content, ttFont):
|
def fromXML(self, name, attrs, content, ttFont):
|
||||||
if not hasattr(self, "glyphOrder"):
|
if not hasattr(self, "glyphOrder"):
|
||||||
self.glyphOrder = []
|
self.glyphOrder = []
|
||||||
ttFont.setGlyphOrder(self.glyphOrder)
|
|
||||||
if name == "GlyphID":
|
if name == "GlyphID":
|
||||||
self.glyphOrder.append(attrs["name"])
|
self.glyphOrder.append(attrs["name"])
|
||||||
|
ttFont.setGlyphOrder(self.glyphOrder)
|
||||||
|
|
||||||
|
|
||||||
def getTableModule(tag):
|
def getTableModule(tag):
|
||||||
|
@ -118,7 +118,6 @@ class Options(object):
|
|||||||
disassembleInstructions = True
|
disassembleInstructions = True
|
||||||
mergeFile = None
|
mergeFile = None
|
||||||
recalcBBoxes = True
|
recalcBBoxes = True
|
||||||
allowVID = False
|
|
||||||
ignoreDecompileErrors = True
|
ignoreDecompileErrors = True
|
||||||
bitmapGlyphDataFormat = 'raw'
|
bitmapGlyphDataFormat = 'raw'
|
||||||
unicodedata = None
|
unicodedata = None
|
||||||
@ -184,8 +183,6 @@ class Options(object):
|
|||||||
self.mergeFile = value
|
self.mergeFile = value
|
||||||
elif option == "-b":
|
elif option == "-b":
|
||||||
self.recalcBBoxes = False
|
self.recalcBBoxes = False
|
||||||
elif option == "-a":
|
|
||||||
self.allowVID = True
|
|
||||||
elif option == "-e":
|
elif option == "-e":
|
||||||
self.ignoreDecompileErrors = False
|
self.ignoreDecompileErrors = False
|
||||||
elif option == "--unicodedata":
|
elif option == "--unicodedata":
|
||||||
@ -258,7 +255,7 @@ def ttDump(input, output, options):
|
|||||||
log.info('Dumping "%s" to "%s"...', input, output)
|
log.info('Dumping "%s" to "%s"...', input, output)
|
||||||
if options.unicodedata:
|
if options.unicodedata:
|
||||||
setUnicodeData(options.unicodedata)
|
setUnicodeData(options.unicodedata)
|
||||||
ttf = TTFont(input, 0, allowVID=options.allowVID,
|
ttf = TTFont(input, 0,
|
||||||
ignoreDecompileErrors=options.ignoreDecompileErrors,
|
ignoreDecompileErrors=options.ignoreDecompileErrors,
|
||||||
fontNumber=options.fontNumber)
|
fontNumber=options.fontNumber)
|
||||||
ttf.saveXML(output,
|
ttf.saveXML(output,
|
||||||
@ -280,8 +277,7 @@ def ttCompile(input, output, options):
|
|||||||
sfnt.USE_ZOPFLI = True
|
sfnt.USE_ZOPFLI = True
|
||||||
ttf = TTFont(options.mergeFile, flavor=options.flavor,
|
ttf = TTFont(options.mergeFile, flavor=options.flavor,
|
||||||
recalcBBoxes=options.recalcBBoxes,
|
recalcBBoxes=options.recalcBBoxes,
|
||||||
recalcTimestamp=options.recalcTimestamp,
|
recalcTimestamp=options.recalcTimestamp)
|
||||||
allowVID=options.allowVID)
|
|
||||||
ttf.importXML(input)
|
ttf.importXML(input)
|
||||||
|
|
||||||
if options.recalcTimestamp is None and 'head' in ttf:
|
if options.recalcTimestamp is None and 'head' in ttf:
|
||||||
|
BIN
Tests/ttLib/data/TestVGID-Regular.otf
Normal file
BIN
Tests/ttLib/data/TestVGID-Regular.otf
Normal file
Binary file not shown.
18418
Tests/ttLib/data/TestVGID-Regular.ttx
Normal file
18418
Tests/ttLib/data/TestVGID-Regular.ttx
Normal file
File diff suppressed because it is too large
Load Diff
@ -1,7 +1,11 @@
|
|||||||
import io
|
import io
|
||||||
|
import os
|
||||||
|
import re
|
||||||
from fontTools.ttLib import TTFont, newTable, registerCustomTableClass, unregisterCustomTableClass
|
from fontTools.ttLib import TTFont, newTable, registerCustomTableClass, unregisterCustomTableClass
|
||||||
from fontTools.ttLib.tables.DefaultTable import DefaultTable
|
from fontTools.ttLib.tables.DefaultTable import DefaultTable
|
||||||
|
|
||||||
|
DATA_DIR = os.path.join(os.path.abspath(os.path.dirname(__file__)), "data")
|
||||||
|
|
||||||
|
|
||||||
class CustomTableClass(DefaultTable):
|
class CustomTableClass(DefaultTable):
|
||||||
|
|
||||||
@ -20,6 +24,13 @@ table_C_U_S_T_ = CustomTableClass # alias for testing
|
|||||||
TABLETAG = "CUST"
|
TABLETAG = "CUST"
|
||||||
|
|
||||||
|
|
||||||
|
def normalize_TTX(string):
|
||||||
|
string = re.sub(' ttLibVersion=".*"', "", string)
|
||||||
|
string = re.sub('checkSumAdjustment value=".*"', "", string)
|
||||||
|
string = re.sub('modified value=".*"', "", string)
|
||||||
|
return string
|
||||||
|
|
||||||
|
|
||||||
def test_registerCustomTableClass():
|
def test_registerCustomTableClass():
|
||||||
font = TTFont()
|
font = TTFont()
|
||||||
font[TABLETAG] = newTable(TABLETAG)
|
font[TABLETAG] = newTable(TABLETAG)
|
||||||
@ -78,3 +89,32 @@ def test_sfntVersionFromTTX():
|
|||||||
# Font is not "empty", sfntVersion in TTX file will be ignored
|
# Font is not "empty", sfntVersion in TTX file will be ignored
|
||||||
font.importXML(ttx)
|
font.importXML(ttx)
|
||||||
assert font.sfntVersion == "OTTO"
|
assert font.sfntVersion == "OTTO"
|
||||||
|
|
||||||
|
|
||||||
|
def test_virtualGlyphId():
|
||||||
|
otfpath = os.path.join(DATA_DIR, "TestVGID-Regular.otf")
|
||||||
|
ttxpath = os.path.join(DATA_DIR, "TestVGID-Regular.ttx")
|
||||||
|
|
||||||
|
otf = TTFont(otfpath)
|
||||||
|
|
||||||
|
ttx = TTFont()
|
||||||
|
ttx.importXML(ttxpath)
|
||||||
|
|
||||||
|
with open(ttxpath, encoding="utf-8") as fp:
|
||||||
|
xml = normalize_TTX(fp.read()).splitlines()
|
||||||
|
|
||||||
|
for font in (otf, ttx):
|
||||||
|
GSUB = font["GSUB"].table
|
||||||
|
assert GSUB.LookupList.LookupCount == 37
|
||||||
|
lookup = GSUB.LookupList.Lookup[32]
|
||||||
|
assert lookup.LookupType == 8
|
||||||
|
subtable = lookup.SubTable[0]
|
||||||
|
assert subtable.LookAheadGlyphCount == 1
|
||||||
|
lookahead = subtable.LookAheadCoverage[0]
|
||||||
|
assert len(lookahead.glyphs) == 46
|
||||||
|
assert "glyph00453" in lookahead.glyphs
|
||||||
|
|
||||||
|
out = io.StringIO()
|
||||||
|
font.saveXML(out)
|
||||||
|
outxml = normalize_TTX(out.getvalue()).splitlines()
|
||||||
|
assert xml == outxml
|
||||||
|
@ -436,12 +436,6 @@ def test_options_b():
|
|||||||
tto = ttx.Options([("-b", "")], 1)
|
tto = ttx.Options([("-b", "")], 1)
|
||||||
assert tto.recalcBBoxes is False
|
assert tto.recalcBBoxes is False
|
||||||
|
|
||||||
|
|
||||||
def test_options_a():
|
|
||||||
tto = ttx.Options([("-a", "")], 1)
|
|
||||||
assert tto.allowVID is True
|
|
||||||
|
|
||||||
|
|
||||||
def test_options_e():
|
def test_options_e():
|
||||||
tto = ttx.Options([("-e", "")], 1)
|
tto = ttx.Options([("-e", "")], 1)
|
||||||
assert tto.ignoreDecompileErrors is False
|
assert tto.ignoreDecompileErrors is False
|
||||||
|
Loading…
x
Reference in New Issue
Block a user