More whitespace

This commit is contained in:
Behdad Esfahbod 2015-04-26 02:01:01 -04:00
parent bd67253118
commit b30e12ae00
63 changed files with 1327 additions and 1327 deletions

View File

@ -712,11 +712,11 @@ UV2AGL = {}
def _builddicts(): def _builddicts():
import re import re
lines = _aglText.splitlines() lines = _aglText.splitlines()
parseAGL_RE = re.compile("([0-9A-F]{4});([A-Za-z_0-9.]+);.*?$") parseAGL_RE = re.compile("([0-9A-F]{4});([A-Za-z_0-9.]+);.*?$")
for line in lines: for line in lines:
if not line or line[:1] == '#': if not line or line[:1] == '#':
continue continue
@ -733,5 +733,5 @@ def _builddicts():
else: else:
AGL2UV[glyphName] = unicode AGL2UV[glyphName] = unicode
UV2AGL[unicode] = glyphName UV2AGL[unicode] = glyphName
_builddicts() _builddicts()

View File

@ -18,15 +18,15 @@ cffHeaderFormat = """
""" """
class CFFFontSet(object): class CFFFontSet(object):
def __init__(self): def __init__(self):
pass pass
def decompile(self, file, otFont): def decompile(self, file, otFont):
sstruct.unpack(cffHeaderFormat, file.read(4), self) sstruct.unpack(cffHeaderFormat, file.read(4), self)
assert self.major == 1 and self.minor == 0, \ assert self.major == 1 and self.minor == 0, \
"unknown CFF format: %d.%d" % (self.major, self.minor) "unknown CFF format: %d.%d" % (self.major, self.minor)
file.seek(self.hdrSize) file.seek(self.hdrSize)
self.fontNames = list(Index(file)) self.fontNames = list(Index(file))
self.topDictIndex = TopDictIndex(file) self.topDictIndex = TopDictIndex(file)
@ -34,23 +34,23 @@ class CFFFontSet(object):
self.GlobalSubrs = GlobalSubrsIndex(file) self.GlobalSubrs = GlobalSubrsIndex(file)
self.topDictIndex.strings = self.strings self.topDictIndex.strings = self.strings
self.topDictIndex.GlobalSubrs = self.GlobalSubrs self.topDictIndex.GlobalSubrs = self.GlobalSubrs
def __len__(self): def __len__(self):
return len(self.fontNames) return len(self.fontNames)
def keys(self): def keys(self):
return list(self.fontNames) return list(self.fontNames)
def values(self): def values(self):
return self.topDictIndex return self.topDictIndex
def __getitem__(self, name): def __getitem__(self, name):
try: try:
index = self.fontNames.index(name) index = self.fontNames.index(name)
except ValueError: except ValueError:
raise KeyError(name) raise KeyError(name)
return self.topDictIndex[index] return self.topDictIndex[index]
def compile(self, file, otFont): def compile(self, file, otFont):
strings = IndexedStrings() strings = IndexedStrings()
writer = CFFWriter() writer = CFFWriter()
@ -63,17 +63,17 @@ class CFFFontSet(object):
writer.add(topCompiler) writer.add(topCompiler)
writer.add(strings.getCompiler()) writer.add(strings.getCompiler())
writer.add(self.GlobalSubrs.getCompiler(strings, None)) writer.add(self.GlobalSubrs.getCompiler(strings, None))
for topDict in self.topDictIndex: for topDict in self.topDictIndex:
if not hasattr(topDict, "charset") or topDict.charset is None: if not hasattr(topDict, "charset") or topDict.charset is None:
charset = otFont.getGlyphOrder() charset = otFont.getGlyphOrder()
topDict.charset = charset topDict.charset = charset
for child in topCompiler.getChildren(strings): for child in topCompiler.getChildren(strings):
writer.add(child) writer.add(child)
writer.toFile(file) writer.toFile(file)
def toXML(self, xmlWriter, progress=None): def toXML(self, xmlWriter, progress=None):
for fontName in self.fontNames: for fontName in self.fontNames:
xmlWriter.begintag("CFFFont", name=tostr(fontName)) xmlWriter.begintag("CFFFont", name=tostr(fontName))
@ -88,7 +88,7 @@ class CFFFontSet(object):
self.GlobalSubrs.toXML(xmlWriter, progress) self.GlobalSubrs.toXML(xmlWriter, progress)
xmlWriter.endtag("GlobalSubrs") xmlWriter.endtag("GlobalSubrs")
xmlWriter.newline() xmlWriter.newline()
def fromXML(self, name, attrs, content): def fromXML(self, name, attrs, content):
if not hasattr(self, "GlobalSubrs"): if not hasattr(self, "GlobalSubrs"):
self.GlobalSubrs = GlobalSubrsIndex() self.GlobalSubrs = GlobalSubrsIndex()
@ -121,13 +121,13 @@ class CFFFontSet(object):
class CFFWriter(object): class CFFWriter(object):
def __init__(self): def __init__(self):
self.data = [] self.data = []
def add(self, table): def add(self, table):
self.data.append(table) self.data.append(table)
def toFile(self, file): def toFile(self, file):
lastPosList = None lastPosList = None
count = 1 count = 1
@ -175,14 +175,14 @@ def calcOffSize(largestOffset):
class IndexCompiler(object): class IndexCompiler(object):
def __init__(self, items, strings, parent): def __init__(self, items, strings, parent):
self.items = self.getItems(items, strings) self.items = self.getItems(items, strings)
self.parent = parent self.parent = parent
def getItems(self, items, strings): def getItems(self, items, strings):
return items return items
def getOffsets(self): def getOffsets(self):
pos = 1 pos = 1
offsets = [pos] offsets = [pos]
@ -193,7 +193,7 @@ class IndexCompiler(object):
pos = pos + len(item) pos = pos + len(item)
offsets.append(pos) offsets.append(pos)
return offsets return offsets
def getDataLength(self): def getDataLength(self):
lastOffset = self.getOffsets()[-1] lastOffset = self.getOffsets()[-1]
offSize = calcOffSize(lastOffset) offSize = calcOffSize(lastOffset)
@ -204,7 +204,7 @@ class IndexCompiler(object):
lastOffset - 1 # size of object data lastOffset - 1 # size of object data
) )
return dataLength return dataLength
def toFile(self, file): def toFile(self, file):
offsets = self.getOffsets() offsets = self.getOffsets()
writeCard16(file, len(self.items)) writeCard16(file, len(self.items))
@ -224,19 +224,19 @@ class IndexCompiler(object):
class IndexedStringsCompiler(IndexCompiler): class IndexedStringsCompiler(IndexCompiler):
def getItems(self, items, strings): def getItems(self, items, strings):
return items.strings return items.strings
class TopDictIndexCompiler(IndexCompiler): class TopDictIndexCompiler(IndexCompiler):
def getItems(self, items, strings): def getItems(self, items, strings):
out = [] out = []
for item in items: for item in items:
out.append(item.getCompiler(strings, self)) out.append(item.getCompiler(strings, self))
return out return out
def getChildren(self, strings): def getChildren(self, strings):
children = [] children = []
for topDict in self.items: for topDict in self.items:
@ -245,13 +245,13 @@ class TopDictIndexCompiler(IndexCompiler):
class FDArrayIndexCompiler(IndexCompiler): class FDArrayIndexCompiler(IndexCompiler):
def getItems(self, items, strings): def getItems(self, items, strings):
out = [] out = []
for item in items: for item in items:
out.append(item.getCompiler(strings, self)) out.append(item.getCompiler(strings, self))
return out return out
def getChildren(self, strings): def getChildren(self, strings):
children = [] children = []
for fontDict in self.items: for fontDict in self.items:
@ -298,11 +298,11 @@ class CharStringsCompiler(GlobalSubrsCompiler):
class Index(object): class Index(object):
"""This class represents what the CFF spec calls an INDEX.""" """This class represents what the CFF spec calls an INDEX."""
compilerClass = IndexCompiler compilerClass = IndexCompiler
def __init__(self, file=None): def __init__(self, file=None):
self.items = [] self.items = []
name = self.__class__.__name__ name = self.__class__.__name__
@ -330,10 +330,10 @@ class Index(object):
file.seek(self.offsetBase + offsets[-1]) # pretend we've read the whole lot file.seek(self.offsetBase + offsets[-1]) # pretend we've read the whole lot
if DEBUG: if DEBUG:
print(" end of %s at %s" % (name, file.tell())) print(" end of %s at %s" % (name, file.tell()))
def __len__(self): def __len__(self):
return len(self.items) return len(self.items)
def __getitem__(self, index): def __getitem__(self, index):
item = self.items[index] item = self.items[index]
if item is not None: if item is not None:
@ -347,21 +347,21 @@ class Index(object):
item = self.produceItem(index, data, file, offset, size) item = self.produceItem(index, data, file, offset, size)
self.items[index] = item self.items[index] = item
return item return item
def produceItem(self, index, data, file, offset, size): def produceItem(self, index, data, file, offset, size):
return data return data
def append(self, item): def append(self, item):
self.items.append(item) self.items.append(item)
def getCompiler(self, strings, parent): def getCompiler(self, strings, parent):
return self.compilerClass(self, strings, parent) return self.compilerClass(self, strings, parent)
class GlobalSubrsIndex(Index): class GlobalSubrsIndex(Index):
compilerClass = GlobalSubrsCompiler compilerClass = GlobalSubrsCompiler
def __init__(self, file=None, globalSubrs=None, private=None, fdSelect=None, fdArray=None): def __init__(self, file=None, globalSubrs=None, private=None, fdSelect=None, fdArray=None):
Index.__init__(self, file) Index.__init__(self, file)
self.globalSubrs = globalSubrs self.globalSubrs = globalSubrs
@ -370,7 +370,7 @@ class GlobalSubrsIndex(Index):
self.fdSelect = fdSelect self.fdSelect = fdSelect
if fdArray: if fdArray:
self.fdArray = fdArray self.fdArray = fdArray
def produceItem(self, index, data, file, offset, size): def produceItem(self, index, data, file, offset, size):
if self.private is not None: if self.private is not None:
private = self.private private = self.private
@ -379,7 +379,7 @@ class GlobalSubrsIndex(Index):
else: else:
private = None private = None
return psCharStrings.T2CharString(data, private=private, globalSubrs=self.globalSubrs) return psCharStrings.T2CharString(data, private=private, globalSubrs=self.globalSubrs)
def toXML(self, xmlWriter, progress): def toXML(self, xmlWriter, progress):
xmlWriter.comment("The 'index' attribute is only for humans; it is ignored when parsed.") xmlWriter.comment("The 'index' attribute is only for humans; it is ignored when parsed.")
xmlWriter.newline() xmlWriter.newline()
@ -393,14 +393,14 @@ class GlobalSubrsIndex(Index):
subr.toXML(xmlWriter) subr.toXML(xmlWriter)
xmlWriter.endtag("CharString") xmlWriter.endtag("CharString")
xmlWriter.newline() xmlWriter.newline()
def fromXML(self, name, attrs, content): def fromXML(self, name, attrs, content):
if name != "CharString": if name != "CharString":
return return
subr = psCharStrings.T2CharString() subr = psCharStrings.T2CharString()
subr.fromXML(name, attrs, content) subr.fromXML(name, attrs, content)
self.append(subr) self.append(subr)
def getItemAndSelector(self, index): def getItemAndSelector(self, index):
sel = None sel = None
if hasattr(self, 'fdSelect'): if hasattr(self, 'fdSelect'):
@ -413,14 +413,14 @@ class SubrsIndex(GlobalSubrsIndex):
class TopDictIndex(Index): class TopDictIndex(Index):
compilerClass = TopDictIndexCompiler compilerClass = TopDictIndexCompiler
def produceItem(self, index, data, file, offset, size): def produceItem(self, index, data, file, offset, size):
top = TopDict(self.strings, file, offset, self.GlobalSubrs) top = TopDict(self.strings, file, offset, self.GlobalSubrs)
top.decompile(data) top.decompile(data)
return top return top
def toXML(self, xmlWriter, progress): def toXML(self, xmlWriter, progress):
for i in range(len(self)): for i in range(len(self)):
xmlWriter.begintag("FontDict", index=i) xmlWriter.begintag("FontDict", index=i)
@ -431,7 +431,7 @@ class TopDictIndex(Index):
class FDArrayIndex(TopDictIndex): class FDArrayIndex(TopDictIndex):
compilerClass = FDArrayIndexCompiler compilerClass = FDArrayIndexCompiler
def fromXML(self, name, attrs, content): def fromXML(self, name, attrs, content):
@ -480,19 +480,19 @@ class FDSelect:
def __len__(self): def __len__(self):
return len(self.gidArray) return len(self.gidArray)
def __getitem__(self, index): def __getitem__(self, index):
return self.gidArray[index] return self.gidArray[index]
def __setitem__(self, index, fdSelectValue): def __setitem__(self, index, fdSelectValue):
self.gidArray[index] = fdSelectValue self.gidArray[index] = fdSelectValue
def append(self, fdSelectValue): def append(self, fdSelectValue):
self.gidArray.append(fdSelectValue) self.gidArray.append(fdSelectValue)
class CharStrings(object): class CharStrings(object):
def __init__(self, file, charset, globalSubrs, private, fdSelect, fdArray): def __init__(self, file, charset, globalSubrs, private, fdSelect, fdArray):
if file is not None: if file is not None:
self.charStringsIndex = SubrsIndex(file, globalSubrs, private, fdSelect, fdArray) self.charStringsIndex = SubrsIndex(file, globalSubrs, private, fdSelect, fdArray)
@ -509,37 +509,37 @@ class CharStrings(object):
self.fdSelect = fdSelect self.fdSelect = fdSelect
if fdArray is not None: if fdArray is not None:
self.fdArray = fdArray self.fdArray = fdArray
def keys(self): def keys(self):
return list(self.charStrings.keys()) return list(self.charStrings.keys())
def values(self): def values(self):
if self.charStringsAreIndexed: if self.charStringsAreIndexed:
return self.charStringsIndex return self.charStringsIndex
else: else:
return list(self.charStrings.values()) return list(self.charStrings.values())
def has_key(self, name): def has_key(self, name):
return name in self.charStrings return name in self.charStrings
__contains__ = has_key __contains__ = has_key
def __len__(self): def __len__(self):
return len(self.charStrings) return len(self.charStrings)
def __getitem__(self, name): def __getitem__(self, name):
charString = self.charStrings[name] charString = self.charStrings[name]
if self.charStringsAreIndexed: if self.charStringsAreIndexed:
charString = self.charStringsIndex[charString] charString = self.charStringsIndex[charString]
return charString return charString
def __setitem__(self, name, charString): def __setitem__(self, name, charString):
if self.charStringsAreIndexed: if self.charStringsAreIndexed:
index = self.charStrings[name] index = self.charStrings[name]
self.charStringsIndex[index] = charString self.charStringsIndex[index] = charString
else: else:
self.charStrings[name] = charString self.charStrings[name] = charString
def getItemAndSelector(self, name): def getItemAndSelector(self, name):
if self.charStringsAreIndexed: if self.charStringsAreIndexed:
index = self.charStrings[name] index = self.charStrings[name]
@ -550,7 +550,7 @@ class CharStrings(object):
else: else:
raise KeyError("fdSelect array not yet defined.") raise KeyError("fdSelect array not yet defined.")
return self.charStrings[name], sel return self.charStrings[name], sel
def toXML(self, xmlWriter, progress): def toXML(self, xmlWriter, progress):
names = sorted(self.keys()) names = sorted(self.keys())
i = 0 i = 0
@ -575,7 +575,7 @@ class CharStrings(object):
progress.setLabel("Dumping 'CFF ' table... (%s)" % name) progress.setLabel("Dumping 'CFF ' table... (%s)" % name)
progress.increment(step / numGlyphs) progress.increment(step / numGlyphs)
i = i + 1 i = i + 1
def fromXML(self, name, attrs, content): def fromXML(self, name, attrs, content):
for element in content: for element in content:
if isinstance(element, basestring): if isinstance(element, basestring):
@ -589,7 +589,7 @@ class CharStrings(object):
private = self.fdArray[fdID].Private private = self.fdArray[fdID].Private
else: else:
private = self.private private = self.private
glyphName = attrs["name"] glyphName = attrs["name"]
charString = psCharStrings.T2CharString( charString = psCharStrings.T2CharString(
private=private, private=private,
@ -767,10 +767,10 @@ class CharStringsConverter(TableConverter):
return 0 # dummy value return 0 # dummy value
def xmlRead(self, name, attrs, content, parent): def xmlRead(self, name, attrs, content, parent):
if hasattr(parent, "ROS"): if hasattr(parent, "ROS"):
# if it is a CID-keyed font, then the private Dict is extracted from the parent.FDArray # if it is a CID-keyed font, then the private Dict is extracted from the parent.FDArray
private, fdSelect, fdArray = None, parent.FDSelect, parent.FDArray private, fdSelect, fdArray = None, parent.FDSelect, parent.FDArray
else: else:
# if it is a name-keyed font, then the private dict is in the top dict, and there is no fdArray. # if it is a name-keyed font, then the private dict is in the top dict, and there is no fdArray.
private, fdSelect, fdArray = parent.Private, None, None private, fdSelect, fdArray = parent.Private, None, None
charStrings = CharStrings(None, None, parent.GlobalSubrs, private, fdSelect, fdArray) charStrings = CharStrings(None, None, parent.GlobalSubrs, private, fdSelect, fdArray)
charStrings.fromXML(name, attrs, content) charStrings.fromXML(name, attrs, content)
@ -796,7 +796,7 @@ class CharsetConverter(object):
if DEBUG: if DEBUG:
print(" charset end at %s" % file.tell()) print(" charset end at %s" % file.tell())
else: # offset == 0 -> no charset data. else: # offset == 0 -> no charset data.
if isCID or "CharStrings" not in parent.rawDict: if isCID or "CharStrings" not in parent.rawDict:
assert value == 0 # We get here only when processing fontDicts from the FDArray of CFF-CID fonts. Only the real topDict references the chrset. assert value == 0 # We get here only when processing fontDicts from the FDArray of CFF-CID fonts. Only the real topDict references the chrset.
charset = None charset = None
elif value == 0: elif value == 0:
@ -821,7 +821,7 @@ class CharsetConverter(object):
class CharsetCompiler(object): class CharsetCompiler(object):
def __init__(self, strings, charset, parent): def __init__(self, strings, charset, parent):
assert charset[0] == '.notdef' assert charset[0] == '.notdef'
isCID = hasattr(parent.dictObj, "ROS") isCID = hasattr(parent.dictObj, "ROS")
@ -832,13 +832,13 @@ class CharsetCompiler(object):
else: else:
self.data = data0 self.data = data0
self.parent = parent self.parent = parent
def setPos(self, pos, endPos): def setPos(self, pos, endPos):
self.parent.rawDict["charset"] = pos self.parent.rawDict["charset"] = pos
def getDataLength(self): def getDataLength(self):
return len(self.data) return len(self.data)
def toFile(self, file): def toFile(self, file):
file.write(self.data) file.write(self.data)
@ -871,7 +871,7 @@ def packCharset(charset, isCID, strings):
getNameID = getCIDfromName getNameID = getCIDfromName
else: else:
getNameID = getSIDfromName getNameID = getSIDfromName
for name in charset[1:]: for name in charset[1:]:
SID = getNameID(name, strings) SID = getNameID(name, strings)
if first is None: if first is None:
@ -888,7 +888,7 @@ def packCharset(charset, isCID, strings):
if nLeft > 255: if nLeft > 255:
fmt = 2 fmt = 2
ranges.append((first, nLeft)) ranges.append((first, nLeft))
data = [packCard8(fmt)] data = [packCard8(fmt)]
if fmt == 1: if fmt == 1:
nLeftFunc = packCard8 nLeftFunc = packCard8
@ -944,10 +944,10 @@ class EncodingCompiler(object):
def setPos(self, pos, endPos): def setPos(self, pos, endPos):
self.parent.rawDict["Encoding"] = pos self.parent.rawDict["Encoding"] = pos
def getDataLength(self): def getDataLength(self):
return len(self.data) return len(self.data)
def toFile(self, file): def toFile(self, file):
file.write(self.data) file.write(self.data)
@ -1047,7 +1047,7 @@ def packEncoding0(charset, encoding, strings):
for name in charset[1:]: for name in charset[1:]:
code = m.get(name) code = m.get(name)
codes.append(code) codes.append(code)
while codes and codes[-1] is None: while codes and codes[-1] is None:
codes.pop() codes.pop()
@ -1079,7 +1079,7 @@ def packEncoding1(charset, encoding, strings):
end = code end = code
nLeft = end - first nLeft = end - first
ranges.append((first, nLeft)) ranges.append((first, nLeft))
# remove unencoded glyphs at the end. # remove unencoded glyphs at the end.
while ranges and ranges[-1][0] == -1: while ranges and ranges[-1][0] == -1:
ranges.pop() ranges.pop()
@ -1138,7 +1138,7 @@ class FDSelectConverter(object):
numGlyphs = None numGlyphs = None
fdSelect = FDSelect(file, numGlyphs, fmt) fdSelect = FDSelect(file, numGlyphs, fmt)
return fdSelect return fdSelect
def packFDSelect0(fdSelectArray): def packFDSelect0(fdSelectArray):
fmt = 0 fmt = 0
@ -1161,7 +1161,7 @@ def packFDSelect3(fdSelectArray):
fdRanges.append([i, fdIndex]) fdRanges.append([i, fdIndex])
lastFDIndex = fdIndex lastFDIndex = fdIndex
sentinelGID = i + 1 sentinelGID = i + 1
data = [packCard8(fmt)] data = [packCard8(fmt)]
data.append(packCard16( len(fdRanges) )) data.append(packCard16( len(fdRanges) ))
for fdRange in fdRanges: for fdRange in fdRanges:
@ -1172,7 +1172,7 @@ def packFDSelect3(fdSelectArray):
class FDSelectCompiler(object): class FDSelectCompiler(object):
def __init__(self, fdSelect, parent): def __init__(self, fdSelect, parent):
fmt = fdSelect.format fmt = fdSelect.format
fdSelectArray = fdSelect.gidArray fdSelectArray = fdSelect.gidArray
@ -1192,13 +1192,13 @@ class FDSelectCompiler(object):
fdSelect.format = 3 fdSelect.format = 3
self.parent = parent self.parent = parent
def setPos(self, pos, endPos): def setPos(self, pos, endPos):
self.parent.rawDict["FDSelect"] = pos self.parent.rawDict["FDSelect"] = pos
def getDataLength(self): def getDataLength(self):
return len(self.data) return len(self.data)
def toFile(self, file): def toFile(self, file):
file.write(self.data) file.write(self.data)
@ -1309,7 +1309,7 @@ class PrivateDictDecompiler(psCharStrings.DictDecompiler):
class DictCompiler(object): class DictCompiler(object):
def __init__(self, dictObj, strings, parent): def __init__(self, dictObj, strings, parent):
assert isinstance(strings, IndexedStrings) assert isinstance(strings, IndexedStrings)
self.dictObj = dictObj self.dictObj = dictObj
@ -1326,13 +1326,13 @@ class DictCompiler(object):
continue continue
rawDict[name] = value rawDict[name] = value
self.rawDict = rawDict self.rawDict = rawDict
def setPos(self, pos, endPos): def setPos(self, pos, endPos):
pass pass
def getDataLength(self): def getDataLength(self):
return len(self.compile("getDataLength")) return len(self.compile("getDataLength"))
def compile(self, reason): def compile(self, reason):
if DEBUG: if DEBUG:
print("-- compiling %s for %s" % (self.__class__.__name__, reason)) print("-- compiling %s for %s" % (self.__class__.__name__, reason))
@ -1357,10 +1357,10 @@ class DictCompiler(object):
data.append(arghandler(value)) data.append(arghandler(value))
data.append(op) data.append(op)
return bytesjoin(data) return bytesjoin(data)
def toFile(self, file): def toFile(self, file):
file.write(self.compile("toFile")) file.write(self.compile("toFile"))
def arg_number(self, num): def arg_number(self, num):
return encodeNumber(num) return encodeNumber(num)
def arg_SID(self, s): def arg_SID(self, s):
@ -1390,9 +1390,9 @@ def encodeNumber(num):
class TopDictCompiler(DictCompiler): class TopDictCompiler(DictCompiler):
opcodes = buildOpcodeDict(topDictOperators) opcodes = buildOpcodeDict(topDictOperators)
def getChildren(self, strings): def getChildren(self, strings):
children = [] children = []
if hasattr(self.dictObj, "charset") and self.dictObj.charset: if hasattr(self.dictObj, "charset") and self.dictObj.charset:
@ -1435,9 +1435,9 @@ class TopDictCompiler(DictCompiler):
class FontDictCompiler(DictCompiler): class FontDictCompiler(DictCompiler):
opcodes = buildOpcodeDict(topDictOperators) opcodes = buildOpcodeDict(topDictOperators)
def getChildren(self, strings): def getChildren(self, strings):
children = [] children = []
if hasattr(self.dictObj, "Private"): if hasattr(self.dictObj, "Private"):
@ -1448,14 +1448,14 @@ class FontDictCompiler(DictCompiler):
class PrivateDictCompiler(DictCompiler): class PrivateDictCompiler(DictCompiler):
opcodes = buildOpcodeDict(privateDictOperators) opcodes = buildOpcodeDict(privateDictOperators)
def setPos(self, pos, endPos): def setPos(self, pos, endPos):
size = endPos - pos size = endPos - pos
self.parent.rawDict["Private"] = size, pos self.parent.rawDict["Private"] = size, pos
self.pos = pos self.pos = pos
def getChildren(self, strings): def getChildren(self, strings):
children = [] children = []
if hasattr(self.dictObj, "Subrs"): if hasattr(self.dictObj, "Subrs"):
@ -1464,7 +1464,7 @@ class PrivateDictCompiler(DictCompiler):
class BaseDict(object): class BaseDict(object):
def __init__(self, strings=None, file=None, offset=None): def __init__(self, strings=None, file=None, offset=None):
self.rawDict = {} self.rawDict = {}
if DEBUG: if DEBUG:
@ -1473,7 +1473,7 @@ class BaseDict(object):
self.offset = offset self.offset = offset
self.strings = strings self.strings = strings
self.skipNames = [] self.skipNames = []
def decompile(self, data): def decompile(self, data):
if DEBUG: if DEBUG:
print(" length %s is %s" % (self.__class__.__name__, len(data))) print(" length %s is %s" % (self.__class__.__name__, len(data)))
@ -1481,13 +1481,13 @@ class BaseDict(object):
dec.decompile(data) dec.decompile(data)
self.rawDict = dec.getDict() self.rawDict = dec.getDict()
self.postDecompile() self.postDecompile()
def postDecompile(self): def postDecompile(self):
pass pass
def getCompiler(self, strings, parent): def getCompiler(self, strings, parent):
return self.compilerClass(self, strings, parent) return self.compilerClass(self, strings, parent)
def __getattr__(self, name): def __getattr__(self, name):
value = self.rawDict.get(name) value = self.rawDict.get(name)
if value is None: if value is None:
@ -1498,7 +1498,7 @@ class BaseDict(object):
value = conv.read(self, value) value = conv.read(self, value)
setattr(self, name, value) setattr(self, name, value)
return value return value
def toXML(self, xmlWriter, progress): def toXML(self, xmlWriter, progress):
for name in self.order: for name in self.order:
if name in self.skipNames: if name in self.skipNames:
@ -1508,7 +1508,7 @@ class BaseDict(object):
continue continue
conv = self.converters[name] conv = self.converters[name]
conv.xmlWrite(xmlWriter, name, value, progress) conv.xmlWrite(xmlWriter, name, value, progress)
def fromXML(self, name, attrs, content): def fromXML(self, name, attrs, content):
conv = self.converters[name] conv = self.converters[name]
value = conv.xmlRead(name, attrs, content, self) value = conv.xmlRead(name, attrs, content, self)
@ -1516,20 +1516,20 @@ class BaseDict(object):
class TopDict(BaseDict): class TopDict(BaseDict):
defaults = buildDefaults(topDictOperators) defaults = buildDefaults(topDictOperators)
converters = buildConverters(topDictOperators) converters = buildConverters(topDictOperators)
order = buildOrder(topDictOperators) order = buildOrder(topDictOperators)
decompilerClass = TopDictDecompiler decompilerClass = TopDictDecompiler
compilerClass = TopDictCompiler compilerClass = TopDictCompiler
def __init__(self, strings=None, file=None, offset=None, GlobalSubrs=None): def __init__(self, strings=None, file=None, offset=None, GlobalSubrs=None):
BaseDict.__init__(self, strings, file, offset) BaseDict.__init__(self, strings, file, offset)
self.GlobalSubrs = GlobalSubrs self.GlobalSubrs = GlobalSubrs
def getGlyphOrder(self): def getGlyphOrder(self):
return self.charset return self.charset
def postDecompile(self): def postDecompile(self):
offset = self.rawDict.get("CharStrings") offset = self.rawDict.get("CharStrings")
if offset is None: if offset is None:
@ -1537,7 +1537,7 @@ class TopDict(BaseDict):
# get the number of glyphs beforehand. # get the number of glyphs beforehand.
self.file.seek(offset) self.file.seek(offset)
self.numGlyphs = readCard16(self.file) self.numGlyphs = readCard16(self.file)
def toXML(self, xmlWriter, progress): def toXML(self, xmlWriter, progress):
if hasattr(self, "CharStrings"): if hasattr(self, "CharStrings"):
self.decompileAllCharStrings(progress) self.decompileAllCharStrings(progress)
@ -1549,7 +1549,7 @@ class TopDict(BaseDict):
self.skipNames = ['CIDFontVersion', 'CIDFontRevision', 'CIDFontType', self.skipNames = ['CIDFontVersion', 'CIDFontRevision', 'CIDFontType',
'CIDCount'] 'CIDCount']
BaseDict.toXML(self, xmlWriter, progress) BaseDict.toXML(self, xmlWriter, progress)
def decompileAllCharStrings(self, progress): def decompileAllCharStrings(self, progress):
# XXX only when doing ttdump -i? # XXX only when doing ttdump -i?
i = 0 i = 0
@ -1567,20 +1567,20 @@ class TopDict(BaseDict):
class FontDict(BaseDict): class FontDict(BaseDict):
defaults = buildDefaults(topDictOperators) defaults = buildDefaults(topDictOperators)
converters = buildConverters(topDictOperators) converters = buildConverters(topDictOperators)
order = buildOrder(topDictOperators) order = buildOrder(topDictOperators)
decompilerClass = None decompilerClass = None
compilerClass = FontDictCompiler compilerClass = FontDictCompiler
def __init__(self, strings=None, file=None, offset=None, GlobalSubrs=None): def __init__(self, strings=None, file=None, offset=None, GlobalSubrs=None):
BaseDict.__init__(self, strings, file, offset) BaseDict.__init__(self, strings, file, offset)
self.GlobalSubrs = GlobalSubrs self.GlobalSubrs = GlobalSubrs
def getGlyphOrder(self): def getGlyphOrder(self):
return self.charset return self.charset
def toXML(self, xmlWriter, progress): def toXML(self, xmlWriter, progress):
self.skipNames = ['Encoding'] self.skipNames = ['Encoding']
BaseDict.toXML(self, xmlWriter, progress) BaseDict.toXML(self, xmlWriter, progress)
@ -1595,28 +1595,28 @@ class PrivateDict(BaseDict):
class IndexedStrings(object): class IndexedStrings(object):
"""SID -> string mapping.""" """SID -> string mapping."""
def __init__(self, file=None): def __init__(self, file=None):
if file is None: if file is None:
strings = [] strings = []
else: else:
strings = [tostr(s, encoding="latin1") for s in Index(file)] strings = [tostr(s, encoding="latin1") for s in Index(file)]
self.strings = strings self.strings = strings
def getCompiler(self): def getCompiler(self):
return IndexedStringsCompiler(self, None, None) return IndexedStringsCompiler(self, None, None)
def __len__(self): def __len__(self):
return len(self.strings) return len(self.strings)
def __getitem__(self, SID): def __getitem__(self, SID):
if SID < cffStandardStringCount: if SID < cffStandardStringCount:
return cffStandardStrings[SID] return cffStandardStrings[SID]
else: else:
return self.strings[SID - cffStandardStringCount] return self.strings[SID - cffStandardStringCount]
def getSID(self, s): def getSID(self, s):
if not hasattr(self, "stringMapping"): if not hasattr(self, "stringMapping"):
self.buildStringMapping() self.buildStringMapping()
@ -1629,10 +1629,10 @@ class IndexedStrings(object):
self.strings.append(s) self.strings.append(s)
self.stringMapping[s] = SID self.stringMapping[s] = SID
return SID return SID
def getStrings(self): def getStrings(self):
return self.strings return self.strings
def buildStringMapping(self): def buildStringMapping(self):
self.stringMapping = {} self.stringMapping = {}
for index in range(len(self.strings)): for index in range(len(self.strings)):
@ -1642,68 +1642,68 @@ class IndexedStrings(object):
# The 391 Standard Strings as used in the CFF format. # The 391 Standard Strings as used in the CFF format.
# from Adobe Technical None #5176, version 1.0, 18 March 1998 # from Adobe Technical None #5176, version 1.0, 18 March 1998
cffStandardStrings = ['.notdef', 'space', 'exclam', 'quotedbl', 'numbersign', cffStandardStrings = ['.notdef', 'space', 'exclam', 'quotedbl', 'numbersign',
'dollar', 'percent', 'ampersand', 'quoteright', 'parenleft', 'parenright', 'dollar', 'percent', 'ampersand', 'quoteright', 'parenleft', 'parenright',
'asterisk', 'plus', 'comma', 'hyphen', 'period', 'slash', 'zero', 'one', 'asterisk', 'plus', 'comma', 'hyphen', 'period', 'slash', 'zero', 'one',
'two', 'three', 'four', 'five', 'six', 'seven', 'eight', 'nine', 'colon', 'two', 'three', 'four', 'five', 'six', 'seven', 'eight', 'nine', 'colon',
'semicolon', 'less', 'equal', 'greater', 'question', 'at', 'A', 'B', 'C', 'semicolon', 'less', 'equal', 'greater', 'question', 'at', 'A', 'B', 'C',
'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R',
'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', 'bracketleft', 'backslash', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', 'bracketleft', 'backslash',
'bracketright', 'asciicircum', 'underscore', 'quoteleft', 'a', 'b', 'c', 'bracketright', 'asciicircum', 'underscore', 'quoteleft', 'a', 'b', 'c',
'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r',
's', 't', 'u', 'v', 'w', 'x', 'y', 'z', 'braceleft', 'bar', 'braceright', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', 'braceleft', 'bar', 'braceright',
'asciitilde', 'exclamdown', 'cent', 'sterling', 'fraction', 'yen', 'florin', 'asciitilde', 'exclamdown', 'cent', 'sterling', 'fraction', 'yen', 'florin',
'section', 'currency', 'quotesingle', 'quotedblleft', 'guillemotleft', 'section', 'currency', 'quotesingle', 'quotedblleft', 'guillemotleft',
'guilsinglleft', 'guilsinglright', 'fi', 'fl', 'endash', 'dagger', 'guilsinglleft', 'guilsinglright', 'fi', 'fl', 'endash', 'dagger',
'daggerdbl', 'periodcentered', 'paragraph', 'bullet', 'quotesinglbase', 'daggerdbl', 'periodcentered', 'paragraph', 'bullet', 'quotesinglbase',
'quotedblbase', 'quotedblright', 'guillemotright', 'ellipsis', 'perthousand', 'quotedblbase', 'quotedblright', 'guillemotright', 'ellipsis', 'perthousand',
'questiondown', 'grave', 'acute', 'circumflex', 'tilde', 'macron', 'breve', 'questiondown', 'grave', 'acute', 'circumflex', 'tilde', 'macron', 'breve',
'dotaccent', 'dieresis', 'ring', 'cedilla', 'hungarumlaut', 'ogonek', 'caron', 'dotaccent', 'dieresis', 'ring', 'cedilla', 'hungarumlaut', 'ogonek', 'caron',
'emdash', 'AE', 'ordfeminine', 'Lslash', 'Oslash', 'OE', 'ordmasculine', 'ae', 'emdash', 'AE', 'ordfeminine', 'Lslash', 'Oslash', 'OE', 'ordmasculine', 'ae',
'dotlessi', 'lslash', 'oslash', 'oe', 'germandbls', 'onesuperior', 'dotlessi', 'lslash', 'oslash', 'oe', 'germandbls', 'onesuperior',
'logicalnot', 'mu', 'trademark', 'Eth', 'onehalf', 'plusminus', 'Thorn', 'logicalnot', 'mu', 'trademark', 'Eth', 'onehalf', 'plusminus', 'Thorn',
'onequarter', 'divide', 'brokenbar', 'degree', 'thorn', 'threequarters', 'onequarter', 'divide', 'brokenbar', 'degree', 'thorn', 'threequarters',
'twosuperior', 'registered', 'minus', 'eth', 'multiply', 'threesuperior', 'twosuperior', 'registered', 'minus', 'eth', 'multiply', 'threesuperior',
'copyright', 'Aacute', 'Acircumflex', 'Adieresis', 'Agrave', 'Aring', 'copyright', 'Aacute', 'Acircumflex', 'Adieresis', 'Agrave', 'Aring',
'Atilde', 'Ccedilla', 'Eacute', 'Ecircumflex', 'Edieresis', 'Egrave', 'Atilde', 'Ccedilla', 'Eacute', 'Ecircumflex', 'Edieresis', 'Egrave',
'Iacute', 'Icircumflex', 'Idieresis', 'Igrave', 'Ntilde', 'Oacute', 'Iacute', 'Icircumflex', 'Idieresis', 'Igrave', 'Ntilde', 'Oacute',
'Ocircumflex', 'Odieresis', 'Ograve', 'Otilde', 'Scaron', 'Uacute', 'Ocircumflex', 'Odieresis', 'Ograve', 'Otilde', 'Scaron', 'Uacute',
'Ucircumflex', 'Udieresis', 'Ugrave', 'Yacute', 'Ydieresis', 'Zcaron', 'Ucircumflex', 'Udieresis', 'Ugrave', 'Yacute', 'Ydieresis', 'Zcaron',
'aacute', 'acircumflex', 'adieresis', 'agrave', 'aring', 'atilde', 'ccedilla', 'aacute', 'acircumflex', 'adieresis', 'agrave', 'aring', 'atilde', 'ccedilla',
'eacute', 'ecircumflex', 'edieresis', 'egrave', 'iacute', 'icircumflex', 'eacute', 'ecircumflex', 'edieresis', 'egrave', 'iacute', 'icircumflex',
'idieresis', 'igrave', 'ntilde', 'oacute', 'ocircumflex', 'odieresis', 'idieresis', 'igrave', 'ntilde', 'oacute', 'ocircumflex', 'odieresis',
'ograve', 'otilde', 'scaron', 'uacute', 'ucircumflex', 'udieresis', 'ugrave', 'ograve', 'otilde', 'scaron', 'uacute', 'ucircumflex', 'udieresis', 'ugrave',
'yacute', 'ydieresis', 'zcaron', 'exclamsmall', 'Hungarumlautsmall', 'yacute', 'ydieresis', 'zcaron', 'exclamsmall', 'Hungarumlautsmall',
'dollaroldstyle', 'dollarsuperior', 'ampersandsmall', 'Acutesmall', 'dollaroldstyle', 'dollarsuperior', 'ampersandsmall', 'Acutesmall',
'parenleftsuperior', 'parenrightsuperior', 'twodotenleader', 'onedotenleader', 'parenleftsuperior', 'parenrightsuperior', 'twodotenleader', 'onedotenleader',
'zerooldstyle', 'oneoldstyle', 'twooldstyle', 'threeoldstyle', 'fouroldstyle', 'zerooldstyle', 'oneoldstyle', 'twooldstyle', 'threeoldstyle', 'fouroldstyle',
'fiveoldstyle', 'sixoldstyle', 'sevenoldstyle', 'eightoldstyle', 'fiveoldstyle', 'sixoldstyle', 'sevenoldstyle', 'eightoldstyle',
'nineoldstyle', 'commasuperior', 'threequartersemdash', 'periodsuperior', 'nineoldstyle', 'commasuperior', 'threequartersemdash', 'periodsuperior',
'questionsmall', 'asuperior', 'bsuperior', 'centsuperior', 'dsuperior', 'questionsmall', 'asuperior', 'bsuperior', 'centsuperior', 'dsuperior',
'esuperior', 'isuperior', 'lsuperior', 'msuperior', 'nsuperior', 'osuperior', 'esuperior', 'isuperior', 'lsuperior', 'msuperior', 'nsuperior', 'osuperior',
'rsuperior', 'ssuperior', 'tsuperior', 'ff', 'ffi', 'ffl', 'parenleftinferior', 'rsuperior', 'ssuperior', 'tsuperior', 'ff', 'ffi', 'ffl', 'parenleftinferior',
'parenrightinferior', 'Circumflexsmall', 'hyphensuperior', 'Gravesmall', 'parenrightinferior', 'Circumflexsmall', 'hyphensuperior', 'Gravesmall',
'Asmall', 'Bsmall', 'Csmall', 'Dsmall', 'Esmall', 'Fsmall', 'Gsmall', 'Hsmall', 'Asmall', 'Bsmall', 'Csmall', 'Dsmall', 'Esmall', 'Fsmall', 'Gsmall', 'Hsmall',
'Ismall', 'Jsmall', 'Ksmall', 'Lsmall', 'Msmall', 'Nsmall', 'Osmall', 'Psmall', 'Ismall', 'Jsmall', 'Ksmall', 'Lsmall', 'Msmall', 'Nsmall', 'Osmall', 'Psmall',
'Qsmall', 'Rsmall', 'Ssmall', 'Tsmall', 'Usmall', 'Vsmall', 'Wsmall', 'Xsmall', 'Qsmall', 'Rsmall', 'Ssmall', 'Tsmall', 'Usmall', 'Vsmall', 'Wsmall', 'Xsmall',
'Ysmall', 'Zsmall', 'colonmonetary', 'onefitted', 'rupiah', 'Tildesmall', 'Ysmall', 'Zsmall', 'colonmonetary', 'onefitted', 'rupiah', 'Tildesmall',
'exclamdownsmall', 'centoldstyle', 'Lslashsmall', 'Scaronsmall', 'Zcaronsmall', 'exclamdownsmall', 'centoldstyle', 'Lslashsmall', 'Scaronsmall', 'Zcaronsmall',
'Dieresissmall', 'Brevesmall', 'Caronsmall', 'Dotaccentsmall', 'Macronsmall', 'Dieresissmall', 'Brevesmall', 'Caronsmall', 'Dotaccentsmall', 'Macronsmall',
'figuredash', 'hypheninferior', 'Ogoneksmall', 'Ringsmall', 'Cedillasmall', 'figuredash', 'hypheninferior', 'Ogoneksmall', 'Ringsmall', 'Cedillasmall',
'questiondownsmall', 'oneeighth', 'threeeighths', 'fiveeighths', 'seveneighths', 'questiondownsmall', 'oneeighth', 'threeeighths', 'fiveeighths', 'seveneighths',
'onethird', 'twothirds', 'zerosuperior', 'foursuperior', 'fivesuperior', 'onethird', 'twothirds', 'zerosuperior', 'foursuperior', 'fivesuperior',
'sixsuperior', 'sevensuperior', 'eightsuperior', 'ninesuperior', 'zeroinferior', 'sixsuperior', 'sevensuperior', 'eightsuperior', 'ninesuperior', 'zeroinferior',
'oneinferior', 'twoinferior', 'threeinferior', 'fourinferior', 'fiveinferior', 'oneinferior', 'twoinferior', 'threeinferior', 'fourinferior', 'fiveinferior',
'sixinferior', 'seveninferior', 'eightinferior', 'nineinferior', 'centinferior', 'sixinferior', 'seveninferior', 'eightinferior', 'nineinferior', 'centinferior',
'dollarinferior', 'periodinferior', 'commainferior', 'Agravesmall', 'dollarinferior', 'periodinferior', 'commainferior', 'Agravesmall',
'Aacutesmall', 'Acircumflexsmall', 'Atildesmall', 'Adieresissmall', 'Aringsmall', 'Aacutesmall', 'Acircumflexsmall', 'Atildesmall', 'Adieresissmall', 'Aringsmall',
'AEsmall', 'Ccedillasmall', 'Egravesmall', 'Eacutesmall', 'Ecircumflexsmall', 'AEsmall', 'Ccedillasmall', 'Egravesmall', 'Eacutesmall', 'Ecircumflexsmall',
'Edieresissmall', 'Igravesmall', 'Iacutesmall', 'Icircumflexsmall', 'Edieresissmall', 'Igravesmall', 'Iacutesmall', 'Icircumflexsmall',
'Idieresissmall', 'Ethsmall', 'Ntildesmall', 'Ogravesmall', 'Oacutesmall', 'Idieresissmall', 'Ethsmall', 'Ntildesmall', 'Ogravesmall', 'Oacutesmall',
'Ocircumflexsmall', 'Otildesmall', 'Odieresissmall', 'OEsmall', 'Oslashsmall', 'Ocircumflexsmall', 'Otildesmall', 'Odieresissmall', 'OEsmall', 'Oslashsmall',
'Ugravesmall', 'Uacutesmall', 'Ucircumflexsmall', 'Udieresissmall', 'Ugravesmall', 'Uacutesmall', 'Ucircumflexsmall', 'Udieresissmall',
'Yacutesmall', 'Thornsmall', 'Ydieresissmall', '001.000', '001.001', '001.002', 'Yacutesmall', 'Thornsmall', 'Ydieresissmall', '001.000', '001.001', '001.002',
'001.003', 'Black', 'Bold', 'Book', 'Light', 'Medium', 'Regular', 'Roman', '001.003', 'Black', 'Bold', 'Book', 'Light', 'Medium', 'Regular', 'Roman',
'Semibold' 'Semibold'
] ]

View File

@ -2,38 +2,38 @@ from __future__ import print_function, division, absolute_import
from fontTools.misc.py23 import * from fontTools.misc.py23 import *
MacRoman = [ MacRoman = [
'NUL', 'Eth', 'eth', 'Lslash', 'lslash', 'Scaron', 'scaron', 'Yacute', 'NUL', 'Eth', 'eth', 'Lslash', 'lslash', 'Scaron', 'scaron', 'Yacute',
'yacute', 'HT', 'LF', 'Thorn', 'thorn', 'CR', 'Zcaron', 'zcaron', 'DLE', 'DC1', 'yacute', 'HT', 'LF', 'Thorn', 'thorn', 'CR', 'Zcaron', 'zcaron', 'DLE', 'DC1',
'DC2', 'DC3', 'DC4', 'onehalf', 'onequarter', 'onesuperior', 'threequarters', 'DC2', 'DC3', 'DC4', 'onehalf', 'onequarter', 'onesuperior', 'threequarters',
'threesuperior', 'twosuperior', 'brokenbar', 'minus', 'multiply', 'RS', 'US', 'threesuperior', 'twosuperior', 'brokenbar', 'minus', 'multiply', 'RS', 'US',
'space', 'exclam', 'quotedbl', 'numbersign', 'dollar', 'percent', 'ampersand', 'space', 'exclam', 'quotedbl', 'numbersign', 'dollar', 'percent', 'ampersand',
'quotesingle', 'parenleft', 'parenright', 'asterisk', 'plus', 'comma', 'quotesingle', 'parenleft', 'parenright', 'asterisk', 'plus', 'comma',
'hyphen', 'period', 'slash', 'zero', 'one', 'two', 'three', 'four', 'five', 'hyphen', 'period', 'slash', 'zero', 'one', 'two', 'three', 'four', 'five',
'six', 'seven', 'eight', 'nine', 'colon', 'semicolon', 'less', 'equal', 'six', 'seven', 'eight', 'nine', 'colon', 'semicolon', 'less', 'equal',
'greater', 'question', 'at', 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'greater', 'question', 'at', 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J',
'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z',
'bracketleft', 'backslash', 'bracketright', 'asciicircum', 'underscore', 'bracketleft', 'backslash', 'bracketright', 'asciicircum', 'underscore',
'grave', 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'grave', 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n',
'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', 'braceleft', 'bar', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', 'braceleft', 'bar',
'braceright', 'asciitilde', 'DEL', 'Adieresis', 'Aring', 'Ccedilla', 'Eacute', 'braceright', 'asciitilde', 'DEL', 'Adieresis', 'Aring', 'Ccedilla', 'Eacute',
'Ntilde', 'Odieresis', 'Udieresis', 'aacute', 'agrave', 'acircumflex', 'Ntilde', 'Odieresis', 'Udieresis', 'aacute', 'agrave', 'acircumflex',
'adieresis', 'atilde', 'aring', 'ccedilla', 'eacute', 'egrave', 'ecircumflex', 'adieresis', 'atilde', 'aring', 'ccedilla', 'eacute', 'egrave', 'ecircumflex',
'edieresis', 'iacute', 'igrave', 'icircumflex', 'idieresis', 'ntilde', 'edieresis', 'iacute', 'igrave', 'icircumflex', 'idieresis', 'ntilde',
'oacute', 'ograve', 'ocircumflex', 'odieresis', 'otilde', 'uacute', 'ugrave', 'oacute', 'ograve', 'ocircumflex', 'odieresis', 'otilde', 'uacute', 'ugrave',
'ucircumflex', 'udieresis', 'dagger', 'degree', 'cent', 'sterling', 'section', 'ucircumflex', 'udieresis', 'dagger', 'degree', 'cent', 'sterling', 'section',
'bullet', 'paragraph', 'germandbls', 'registered', 'copyright', 'trademark', 'bullet', 'paragraph', 'germandbls', 'registered', 'copyright', 'trademark',
'acute', 'dieresis', 'notequal', 'AE', 'Oslash', 'infinity', 'plusminus', 'acute', 'dieresis', 'notequal', 'AE', 'Oslash', 'infinity', 'plusminus',
'lessequal', 'greaterequal', 'yen', 'mu', 'partialdiff', 'summation', 'lessequal', 'greaterequal', 'yen', 'mu', 'partialdiff', 'summation',
'product', 'pi', 'integral', 'ordfeminine', 'ordmasculine', 'Omega', 'ae', 'product', 'pi', 'integral', 'ordfeminine', 'ordmasculine', 'Omega', 'ae',
'oslash', 'questiondown', 'exclamdown', 'logicalnot', 'radical', 'florin', 'oslash', 'questiondown', 'exclamdown', 'logicalnot', 'radical', 'florin',
'approxequal', 'Delta', 'guillemotleft', 'guillemotright', 'ellipsis', 'approxequal', 'Delta', 'guillemotleft', 'guillemotright', 'ellipsis',
'nbspace', 'Agrave', 'Atilde', 'Otilde', 'OE', 'oe', 'endash', 'emdash', 'nbspace', 'Agrave', 'Atilde', 'Otilde', 'OE', 'oe', 'endash', 'emdash',
'quotedblleft', 'quotedblright', 'quoteleft', 'quoteright', 'divide', 'lozenge', 'quotedblleft', 'quotedblright', 'quoteleft', 'quoteright', 'divide', 'lozenge',
'ydieresis', 'Ydieresis', 'fraction', 'currency', 'guilsinglleft', 'ydieresis', 'Ydieresis', 'fraction', 'currency', 'guilsinglleft',
'guilsinglright', 'fi', 'fl', 'daggerdbl', 'periodcentered', 'quotesinglbase', 'guilsinglright', 'fi', 'fl', 'daggerdbl', 'periodcentered', 'quotesinglbase',
'quotedblbase', 'perthousand', 'Acircumflex', 'Ecircumflex', 'Aacute', 'quotedblbase', 'perthousand', 'Acircumflex', 'Ecircumflex', 'Aacute',
'Edieresis', 'Egrave', 'Iacute', 'Icircumflex', 'Idieresis', 'Igrave', 'Oacute', 'Edieresis', 'Egrave', 'Iacute', 'Icircumflex', 'Idieresis', 'Igrave', 'Oacute',
'Ocircumflex', 'apple', 'Ograve', 'Uacute', 'Ucircumflex', 'Ugrave', 'dotlessi', 'Ocircumflex', 'apple', 'Ograve', 'Uacute', 'Ucircumflex', 'Ugrave', 'dotlessi',
'circumflex', 'tilde', 'macron', 'breve', 'dotaccent', 'ring', 'cedilla', 'circumflex', 'tilde', 'macron', 'breve', 'dotaccent', 'ring', 'cedilla',
'hungarumlaut', 'ogonek', 'caron' 'hungarumlaut', 'ogonek', 'caron'
] ]

View File

@ -808,7 +808,7 @@ class Merger(object):
try: try:
mergeLogic = logic['*'] mergeLogic = logic['*']
except KeyError: except KeyError:
raise Exception("Don't know how to merge key %s of class %s" % raise Exception("Don't know how to merge key %s of class %s" %
(key, returnTable.__class__.__name__)) (key, returnTable.__class__.__name__))
if mergeLogic is NotImplemented: if mergeLogic is NotImplemented:
continue continue

View File

@ -43,7 +43,7 @@ def pointInRect(p, rect):
return (xMin <= x <= xMax) and (yMin <= y <= yMax) return (xMin <= x <= xMax) and (yMin <= y <= yMax)
def pointsInRect(array, rect): def pointsInRect(array, rect):
"""Find out which points or array are inside rect. """Find out which points or array are inside rect.
Returns an array with a boolean for each point. Returns an array with a boolean for each point.
""" """
if len(array) < 1: if len(array) < 1:
@ -59,7 +59,7 @@ def vectorLength(vector):
def asInt16(array): def asInt16(array):
"""Round and cast to 16 bit integer.""" """Round and cast to 16 bit integer."""
return [int(math.floor(i+0.5)) for i in array] return [int(math.floor(i+0.5)) for i in array]
def normRect(rect): def normRect(rect):
"""Normalize the rectangle so that the following holds: """Normalize the rectangle so that the following holds:

View File

@ -62,7 +62,7 @@ def calcCubicBounds(pt1, pt2, pt3, pt4):
xRoots = [t for t in solveQuadratic(ax3, bx2, cx) if 0 <= t < 1] xRoots = [t for t in solveQuadratic(ax3, bx2, cx) if 0 <= t < 1]
yRoots = [t for t in solveQuadratic(ay3, by2, cy) if 0 <= t < 1] yRoots = [t for t in solveQuadratic(ay3, by2, cy) if 0 <= t < 1]
roots = xRoots + yRoots roots = xRoots + yRoots
points = [(ax*t*t*t + bx*t*t + cx * t + dx, ay*t*t*t + by*t*t + cy * t + dy) for t in roots] + [pt1, pt4] points = [(ax*t*t*t + bx*t*t + cx * t + dx, ay*t*t*t + by*t*t + cy * t + dy) for t in roots] + [pt1, pt4]
return calcBounds(points) return calcBounds(points)
@ -220,7 +220,7 @@ def _splitQuadraticAtT(a, b, c, *ts):
b1y = (2*ay*t1 + by) * delta b1y = (2*ay*t1 + by) * delta
c1x = ax*t1**2 + bx*t1 + cx c1x = ax*t1**2 + bx*t1 + cx
c1y = ay*t1**2 + by*t1 + cy c1y = ay*t1**2 + by*t1 + cy
pt1, pt2, pt3 = calcQuadraticPoints((a1x, a1y), (b1x, b1y), (c1x, c1y)) pt1, pt2, pt3 = calcQuadraticPoints((a1x, a1y), (b1x, b1y), (c1x, c1y))
segments.append((pt1, pt2, pt3)) segments.append((pt1, pt2, pt3))
return segments return segments
@ -306,7 +306,7 @@ def solveCubic(a, b, c, d):
a1 = b/a a1 = b/a
a2 = c/a a2 = c/a
a3 = d/a a3 = d/a
Q = (a1*a1 - 3.0*a2)/9.0 Q = (a1*a1 - 3.0*a2)/9.0
R = (2.0*a1*a1*a1 - 9.0*a1*a2 + 27.0*a3)/54.0 R = (2.0*a1*a1*a1 - 9.0*a1*a2 + 27.0*a3)/54.0
R2_Q3 = R*R - Q*Q*Q R2_Q3 = R*R - Q*Q*Q

View File

@ -1,4 +1,4 @@
"""fontTools.misc.eexec.py -- Module implementing the eexec and """fontTools.misc.eexec.py -- Module implementing the eexec and
charstring encryption algorithm as used by PostScript Type 1 fonts. charstring encryption algorithm as used by PostScript Type 1 fonts.
""" """

View File

@ -14,7 +14,7 @@ def fixedToFloat(value, precisionBits):
that has the shortest decimal reprentation. Eg. to convert a that has the shortest decimal reprentation. Eg. to convert a
fixed number in a 2.14 format, use precisionBits=14. This is fixed number in a 2.14 format, use precisionBits=14. This is
pretty slow compared to a simple division. Use sporadically. pretty slow compared to a simple division. Use sporadically.
>>> "%g" % fixedToFloat(13107, 14) >>> "%g" % fixedToFloat(13107, 14)
'0.8' '0.8'
>>> "%g" % fixedToFloat(0, 14) >>> "%g" % fixedToFloat(0, 14)

View File

@ -53,18 +53,18 @@ _FCBPBFormat = """
""" """
class ParamBlock(object): class ParamBlock(object):
"""Wrapper for the very low level FCBPB record.""" """Wrapper for the very low level FCBPB record."""
def __init__(self, refNum): def __init__(self, refNum):
self.__fileName = array.array("c", "\0" * 64) self.__fileName = array.array("c", "\0" * 64)
sstruct.unpack(_FCBPBFormat, sstruct.unpack(_FCBPBFormat,
"\0" * sstruct.calcsize(_FCBPBFormat), self) "\0" * sstruct.calcsize(_FCBPBFormat), self)
self.ioNamePtr = self.__fileName.buffer_info()[0] self.ioNamePtr = self.__fileName.buffer_info()[0]
self.ioRefNum = refNum self.ioRefNum = refNum
self.ioVRefNum = GetVRefNum(refNum) self.ioVRefNum = GetVRefNum(refNum)
self.__haveInfo = 0 self.__haveInfo = 0
def getInfo(self): def getInfo(self):
if self.__haveInfo: if self.__haveInfo:
return return
@ -76,18 +76,18 @@ class ParamBlock(object):
raise Res.Error("can't get file info", err) raise Res.Error("can't get file info", err)
sstruct.unpack(_FCBPBFormat, buf.tostring(), self) sstruct.unpack(_FCBPBFormat, buf.tostring(), self)
self.__haveInfo = 1 self.__haveInfo = 1
def getFileName(self): def getFileName(self):
self.getInfo() self.getInfo()
data = self.__fileName.tostring() data = self.__fileName.tostring()
return data[1:byteord(data[0])+1] return data[1:byteord(data[0])+1]
def getFSSpec(self): def getFSSpec(self):
self.getInfo() self.getInfo()
vRefNum = self.ioVRefNum vRefNum = self.ioVRefNum
parID = self.ioFCBParID parID = self.ioFCBParID
return macfs.FSSpec((vRefNum, parID, self.getFileName())) return macfs.FSSpec((vRefNum, parID, self.getFileName()))
def getPath(self): def getPath(self):
return self.getFSSpec().as_pathname() return self.getFSSpec().as_pathname()

View File

@ -1,4 +1,4 @@
"""psCharStrings.py -- module implementing various kinds of CharStrings: """psCharStrings.py -- module implementing various kinds of CharStrings:
CFF dictionary data and Type1/Type2 CharStrings. CFF dictionary data and Type1/Type2 CharStrings.
""" """
@ -81,7 +81,7 @@ cffDictOperandEncoding[30] = read_realNumber
cffDictOperandEncoding[255] = read_reserved cffDictOperandEncoding[255] = read_reserved
realNibbles = ['0', '1', '2', '3', '4', '5', '6', '7', '8', '9', realNibbles = ['0', '1', '2', '3', '4', '5', '6', '7', '8', '9',
'.', 'E', 'E-', None, '-'] '.', 'E', 'E-', None, '-']
realNibblesDict = dict((v,i) for i,v in enumerate(realNibbles)) realNibblesDict = dict((v,i) for i,v in enumerate(realNibbles))
@ -171,7 +171,7 @@ def getIntEncoder(format):
else: else:
assert format == "t2" assert format == "t2"
fourByteOp = None fourByteOp = None
def encodeInt(value, fourByteOp=fourByteOp, bytechr=bytechr, def encodeInt(value, fourByteOp=fourByteOp, bytechr=bytechr,
pack=struct.pack, unpack=struct.unpack): pack=struct.pack, unpack=struct.unpack):
if -107 <= value <= 107: if -107 <= value <= 107:
@ -203,7 +203,7 @@ def getIntEncoder(format):
else: else:
code = fourByteOp + pack(">l", value) code = fourByteOp + pack(">l", value)
return code return code
return encodeInt return encodeInt
@ -243,10 +243,10 @@ class CharStringCompileError(Exception): pass
class T2CharString(ByteCodeBase): class T2CharString(ByteCodeBase):
operandEncoding = t2OperandEncoding operandEncoding = t2OperandEncoding
operators, opcodes = buildOperatorDict(t2Operators) operators, opcodes = buildOperatorDict(t2Operators)
def __init__(self, bytecode=None, program=None, private=None, globalSubrs=None): def __init__(self, bytecode=None, program=None, private=None, globalSubrs=None):
if program is None: if program is None:
program = [] program = []
@ -254,16 +254,16 @@ class T2CharString(ByteCodeBase):
self.program = program self.program = program
self.private = private self.private = private
self.globalSubrs = globalSubrs if globalSubrs is not None else [] self.globalSubrs = globalSubrs if globalSubrs is not None else []
def __repr__(self): def __repr__(self):
if self.bytecode is None: if self.bytecode is None:
return "<%s (source) at %x>" % (self.__class__.__name__, id(self)) return "<%s (source) at %x>" % (self.__class__.__name__, id(self))
else: else:
return "<%s (bytecode) at %x>" % (self.__class__.__name__, id(self)) return "<%s (bytecode) at %x>" % (self.__class__.__name__, id(self))
def getIntEncoder(self): def getIntEncoder(self):
return encodeIntT2 return encodeIntT2
def getFixedEncoder(self): def getFixedEncoder(self):
return encodeFixed return encodeFixed
@ -273,14 +273,14 @@ class T2CharString(ByteCodeBase):
subrs = getattr(self.private, "Subrs", []) subrs = getattr(self.private, "Subrs", [])
decompiler = SimpleT2Decompiler(subrs, self.globalSubrs) decompiler = SimpleT2Decompiler(subrs, self.globalSubrs)
decompiler.execute(self) decompiler.execute(self)
def draw(self, pen): def draw(self, pen):
subrs = getattr(self.private, "Subrs", []) subrs = getattr(self.private, "Subrs", [])
extractor = T2OutlineExtractor(pen, subrs, self.globalSubrs, extractor = T2OutlineExtractor(pen, subrs, self.globalSubrs,
self.private.nominalWidthX, self.private.defaultWidthX) self.private.nominalWidthX, self.private.defaultWidthX)
extractor.execute(self) extractor.execute(self)
self.width = extractor.width self.width = extractor.width
def compile(self): def compile(self):
if self.bytecode is not None: if self.bytecode is not None:
return return
@ -318,18 +318,18 @@ class T2CharString(ByteCodeBase):
print(bytecode) print(bytecode)
raise raise
self.setBytecode(bytecode) self.setBytecode(bytecode)
def needsDecompilation(self): def needsDecompilation(self):
return self.bytecode is not None return self.bytecode is not None
def setProgram(self, program): def setProgram(self, program):
self.program = program self.program = program
self.bytecode = None self.bytecode = None
def setBytecode(self, bytecode): def setBytecode(self, bytecode):
self.bytecode = bytecode self.bytecode = bytecode
self.program = None self.program = None
def getToken(self, index, def getToken(self, index,
len=len, byteord=byteord, basestring=basestring, len=len, byteord=byteord, basestring=basestring,
isinstance=isinstance): isinstance=isinstance):
@ -347,7 +347,7 @@ class T2CharString(ByteCodeBase):
index = index + 1 index = index + 1
isOperator = isinstance(token, basestring) isOperator = isinstance(token, basestring)
return token, isOperator, index return token, isOperator, index
def getBytes(self, index, nBytes): def getBytes(self, index, nBytes):
if self.bytecode is not None: if self.bytecode is not None:
newIndex = index + nBytes newIndex = index + nBytes
@ -358,10 +358,10 @@ class T2CharString(ByteCodeBase):
index = index + 1 index = index + 1
assert len(bytes) == nBytes assert len(bytes) == nBytes
return bytes, index return bytes, index
def handle_operator(self, operator): def handle_operator(self, operator):
return operator return operator
def toXML(self, xmlWriter): def toXML(self, xmlWriter):
from fontTools.misc.textTools import num2binary from fontTools.misc.textTools import num2binary
if self.bytecode is not None: if self.bytecode is not None:
@ -389,7 +389,7 @@ class T2CharString(ByteCodeBase):
args = [] args = []
else: else:
args.append(token) args.append(token)
def fromXML(self, name, attrs, content): def fromXML(self, name, attrs, content):
from fontTools.misc.textTools import binary2num, readHex from fontTools.misc.textTools import binary2num, readHex
if attrs.get("raw"): if attrs.get("raw"):
@ -454,10 +454,10 @@ t1Operators = [
] ]
class T1CharString(T2CharString): class T1CharString(T2CharString):
operandEncoding = t1OperandEncoding operandEncoding = t1OperandEncoding
operators, opcodes = buildOperatorDict(t1Operators) operators, opcodes = buildOperatorDict(t1Operators)
def __init__(self, bytecode=None, program=None, subrs=None): def __init__(self, bytecode=None, program=None, subrs=None):
if program is None: if program is None:
program = [] program = []
@ -491,20 +491,20 @@ class T1CharString(T2CharString):
class SimpleT2Decompiler(object): class SimpleT2Decompiler(object):
def __init__(self, localSubrs, globalSubrs): def __init__(self, localSubrs, globalSubrs):
self.localSubrs = localSubrs self.localSubrs = localSubrs
self.localBias = calcSubrBias(localSubrs) self.localBias = calcSubrBias(localSubrs)
self.globalSubrs = globalSubrs self.globalSubrs = globalSubrs
self.globalBias = calcSubrBias(globalSubrs) self.globalBias = calcSubrBias(globalSubrs)
self.reset() self.reset()
def reset(self): def reset(self):
self.callingStack = [] self.callingStack = []
self.operandStack = [] self.operandStack = []
self.hintCount = 0 self.hintCount = 0
self.hintMaskBytes = 0 self.hintMaskBytes = 0
def execute(self, charString): def execute(self, charString):
self.callingStack.append(charString) self.callingStack.append(charString)
needsDecompilation = charString.needsDecompilation() needsDecompilation = charString.needsDecompilation()
@ -538,24 +538,24 @@ class SimpleT2Decompiler(object):
"seac"), "illegal CharString" "seac"), "illegal CharString"
charString.setProgram(program) charString.setProgram(program)
del self.callingStack[-1] del self.callingStack[-1]
def pop(self): def pop(self):
value = self.operandStack[-1] value = self.operandStack[-1]
del self.operandStack[-1] del self.operandStack[-1]
return value return value
def popall(self): def popall(self):
stack = self.operandStack[:] stack = self.operandStack[:]
self.operandStack[:] = [] self.operandStack[:] = []
return stack return stack
def push(self, value): def push(self, value):
self.operandStack.append(value) self.operandStack.append(value)
def op_return(self, index): def op_return(self, index):
if self.operandStack: if self.operandStack:
pass pass
def op_endchar(self, index): def op_endchar(self, index):
pass pass
@ -566,12 +566,12 @@ class SimpleT2Decompiler(object):
subrIndex = self.pop() subrIndex = self.pop()
subr = self.localSubrs[subrIndex+self.localBias] subr = self.localSubrs[subrIndex+self.localBias]
self.execute(subr) self.execute(subr)
def op_callgsubr(self, index): def op_callgsubr(self, index):
subrIndex = self.pop() subrIndex = self.pop()
subr = self.globalSubrs[subrIndex+self.globalBias] subr = self.globalSubrs[subrIndex+self.globalBias]
self.execute(subr) self.execute(subr)
def op_hstem(self, index): def op_hstem(self, index):
self.countHints() self.countHints()
def op_vstem(self, index): def op_vstem(self, index):
@ -580,16 +580,16 @@ class SimpleT2Decompiler(object):
self.countHints() self.countHints()
def op_vstemhm(self, index): def op_vstemhm(self, index):
self.countHints() self.countHints()
def op_hintmask(self, index): def op_hintmask(self, index):
if not self.hintMaskBytes: if not self.hintMaskBytes:
self.countHints() self.countHints()
self.hintMaskBytes = (self.hintCount + 7) // 8 self.hintMaskBytes = (self.hintCount + 7) // 8
hintMaskBytes, index = self.callingStack[-1].getBytes(index, self.hintMaskBytes) hintMaskBytes, index = self.callingStack[-1].getBytes(index, self.hintMaskBytes)
return hintMaskBytes, index return hintMaskBytes, index
op_cntrmask = op_hintmask op_cntrmask = op_hintmask
def countHints(self): def countHints(self):
args = self.popall() args = self.popall()
self.hintCount = self.hintCount + len(args) // 2 self.hintCount = self.hintCount + len(args) // 2
@ -641,13 +641,13 @@ class SimpleT2Decompiler(object):
raise NotImplementedError raise NotImplementedError
class T2OutlineExtractor(SimpleT2Decompiler): class T2OutlineExtractor(SimpleT2Decompiler):
def __init__(self, pen, localSubrs, globalSubrs, nominalWidthX, defaultWidthX): def __init__(self, pen, localSubrs, globalSubrs, nominalWidthX, defaultWidthX):
SimpleT2Decompiler.__init__(self, localSubrs, globalSubrs) SimpleT2Decompiler.__init__(self, localSubrs, globalSubrs)
self.pen = pen self.pen = pen
self.nominalWidthX = nominalWidthX self.nominalWidthX = nominalWidthX
self.defaultWidthX = defaultWidthX self.defaultWidthX = defaultWidthX
def reset(self): def reset(self):
SimpleT2Decompiler.reset(self) SimpleT2Decompiler.reset(self)
self.hints = [] self.hints = []
@ -655,13 +655,13 @@ class T2OutlineExtractor(SimpleT2Decompiler):
self.width = 0 self.width = 0
self.currentPoint = (0, 0) self.currentPoint = (0, 0)
self.sawMoveTo = 0 self.sawMoveTo = 0
def _nextPoint(self, point): def _nextPoint(self, point):
x, y = self.currentPoint x, y = self.currentPoint
point = x + point[0], y + point[1] point = x + point[0], y + point[1]
self.currentPoint = point self.currentPoint = point
return point return point
def rMoveTo(self, point): def rMoveTo(self, point):
self.pen.moveTo(self._nextPoint(point)) self.pen.moveTo(self._nextPoint(point))
self.sawMoveTo = 1 self.sawMoveTo = 1
@ -676,12 +676,12 @@ class T2OutlineExtractor(SimpleT2Decompiler):
self.rMoveTo((0, 0)) self.rMoveTo((0, 0))
nextPoint = self._nextPoint nextPoint = self._nextPoint
self.pen.curveTo(nextPoint(pt1), nextPoint(pt2), nextPoint(pt3)) self.pen.curveTo(nextPoint(pt1), nextPoint(pt2), nextPoint(pt3))
def closePath(self): def closePath(self):
if self.sawMoveTo: if self.sawMoveTo:
self.pen.closePath() self.pen.closePath()
self.sawMoveTo = 0 self.sawMoveTo = 0
def endPath(self): def endPath(self):
# In T2 there are no open paths, so always do a closePath when # In T2 there are no open paths, so always do a closePath when
# finishing a sub path. # finishing a sub path.
@ -697,11 +697,11 @@ class T2OutlineExtractor(SimpleT2Decompiler):
self.width = self.defaultWidthX self.width = self.defaultWidthX
self.gotWidth = 1 self.gotWidth = 1
return args return args
def countHints(self): def countHints(self):
args = self.popallWidth() args = self.popallWidth()
self.hintCount = self.hintCount + len(args) // 2 self.hintCount = self.hintCount + len(args) // 2
# #
# hint operators # hint operators
# #
@ -717,7 +717,7 @@ class T2OutlineExtractor(SimpleT2Decompiler):
# self.countHints() # self.countHints()
#def op_cntrmask(self, index): #def op_cntrmask(self, index):
# self.countHints() # self.countHints()
# #
# path constructors, moveto # path constructors, moveto
# #
@ -742,7 +742,7 @@ class T2OutlineExtractor(SimpleT2Decompiler):
self.pen.addComponent(baseGlyph, (1, 0, 0, 1, 0, 0)) self.pen.addComponent(baseGlyph, (1, 0, 0, 1, 0, 0))
accentGlyph = StandardEncoding[achar] accentGlyph = StandardEncoding[achar]
self.pen.addComponent(accentGlyph, (1, 0, 0, 1, adx, ady)) self.pen.addComponent(accentGlyph, (1, 0, 0, 1, adx, ady))
# #
# path constructors, lines # path constructors, lines
# #
@ -751,12 +751,12 @@ class T2OutlineExtractor(SimpleT2Decompiler):
for i in range(0, len(args), 2): for i in range(0, len(args), 2):
point = args[i:i+2] point = args[i:i+2]
self.rLineTo(point) self.rLineTo(point)
def op_hlineto(self, index): def op_hlineto(self, index):
self.alternatingLineto(1) self.alternatingLineto(1)
def op_vlineto(self, index): def op_vlineto(self, index):
self.alternatingLineto(0) self.alternatingLineto(0)
# #
# path constructors, curves # path constructors, curves
# #
@ -766,7 +766,7 @@ class T2OutlineExtractor(SimpleT2Decompiler):
for i in range(0, len(args), 6): for i in range(0, len(args), 6):
dxa, dya, dxb, dyb, dxc, dyc, = args[i:i+6] dxa, dya, dxb, dyb, dxc, dyc, = args[i:i+6]
self.rCurveTo((dxa, dya), (dxb, dyb), (dxc, dyc)) self.rCurveTo((dxa, dya), (dxb, dyb), (dxc, dyc))
def op_rcurveline(self, index): def op_rcurveline(self, index):
"""{dxa dya dxb dyb dxc dyc}+ dxd dyd rcurveline""" """{dxa dya dxb dyb dxc dyc}+ dxd dyd rcurveline"""
args = self.popall() args = self.popall()
@ -774,7 +774,7 @@ class T2OutlineExtractor(SimpleT2Decompiler):
dxb, dyb, dxc, dyc, dxd, dyd = args[i:i+6] dxb, dyb, dxc, dyc, dxd, dyd = args[i:i+6]
self.rCurveTo((dxb, dyb), (dxc, dyc), (dxd, dyd)) self.rCurveTo((dxb, dyb), (dxc, dyc), (dxd, dyd))
self.rLineTo(args[-2:]) self.rLineTo(args[-2:])
def op_rlinecurve(self, index): def op_rlinecurve(self, index):
"""{dxa dya}+ dxb dyb dxc dyc dxd dyd rlinecurve""" """{dxa dya}+ dxb dyb dxc dyc dxd dyd rlinecurve"""
args = self.popall() args = self.popall()
@ -783,7 +783,7 @@ class T2OutlineExtractor(SimpleT2Decompiler):
self.rLineTo(lineArgs[i:i+2]) self.rLineTo(lineArgs[i:i+2])
dxb, dyb, dxc, dyc, dxd, dyd = args[-6:] dxb, dyb, dxc, dyc, dxd, dyd = args[-6:]
self.rCurveTo((dxb, dyb), (dxc, dyc), (dxd, dyd)) self.rCurveTo((dxb, dyb), (dxc, dyc), (dxd, dyd))
def op_vvcurveto(self, index): def op_vvcurveto(self, index):
"dx1? {dya dxb dyb dyc}+ vvcurveto" "dx1? {dya dxb dyb dyc}+ vvcurveto"
args = self.popall() args = self.popall()
@ -796,7 +796,7 @@ class T2OutlineExtractor(SimpleT2Decompiler):
dya, dxb, dyb, dyc = args[i:i+4] dya, dxb, dyb, dyc = args[i:i+4]
self.rCurveTo((dx1, dya), (dxb, dyb), (0, dyc)) self.rCurveTo((dx1, dya), (dxb, dyb), (0, dyc))
dx1 = 0 dx1 = 0
def op_hhcurveto(self, index): def op_hhcurveto(self, index):
"""dy1? {dxa dxb dyb dxc}+ hhcurveto""" """dy1? {dxa dxb dyb dxc}+ hhcurveto"""
args = self.popall() args = self.popall()
@ -809,7 +809,7 @@ class T2OutlineExtractor(SimpleT2Decompiler):
dxa, dxb, dyb, dxc = args[i:i+4] dxa, dxb, dyb, dxc = args[i:i+4]
self.rCurveTo((dxa, dy1), (dxb, dyb), (dxc, 0)) self.rCurveTo((dxa, dy1), (dxb, dyb), (dxc, 0))
dy1 = 0 dy1 = 0
def op_vhcurveto(self, index): def op_vhcurveto(self, index):
"""dy1 dx2 dy2 dx3 {dxa dxb dyb dyc dyd dxe dye dxf}* dyf? vhcurveto (30) """dy1 dx2 dy2 dx3 {dxa dxb dyb dyc dyd dxe dye dxf}* dyf? vhcurveto (30)
{dya dxb dyb dxc dxd dxe dye dyf}+ dxf? vhcurveto {dya dxb dyb dxc dxd dxe dye dyf}+ dxf? vhcurveto
@ -819,7 +819,7 @@ class T2OutlineExtractor(SimpleT2Decompiler):
args = self.vcurveto(args) args = self.vcurveto(args)
if args: if args:
args = self.hcurveto(args) args = self.hcurveto(args)
def op_hvcurveto(self, index): def op_hvcurveto(self, index):
"""dx1 dx2 dy2 dy3 {dya dxb dyb dxc dxd dxe dye dyf}* dxf? """dx1 dx2 dy2 dy3 {dya dxb dyb dxc dxd dxe dye dyf}* dxf?
{dxa dxb dyb dyc dyd dxe dye dxf}+ dyf? {dxa dxb dyb dyc dyd dxe dye dxf}+ dyf?
@ -829,7 +829,7 @@ class T2OutlineExtractor(SimpleT2Decompiler):
args = self.hcurveto(args) args = self.hcurveto(args)
if args: if args:
args = self.vcurveto(args) args = self.vcurveto(args)
# #
# path constructors, flex # path constructors, flex
# #
@ -862,13 +862,13 @@ class T2OutlineExtractor(SimpleT2Decompiler):
dy6 = d6 dy6 = d6
self.rCurveTo((dx1, dy1), (dx2, dy2), (dx3, dy3)) self.rCurveTo((dx1, dy1), (dx2, dy2), (dx3, dy3))
self.rCurveTo((dx4, dy4), (dx5, dy5), (dx6, dy6)) self.rCurveTo((dx4, dy4), (dx5, dy5), (dx6, dy6))
# #
# MultipleMaster. Well... # MultipleMaster. Well...
# #
def op_blend(self, index): def op_blend(self, index):
self.popall() self.popall()
# misc # misc
def op_and(self, index): def op_and(self, index):
raise NotImplementedError raise NotImplementedError
@ -921,7 +921,7 @@ class T2OutlineExtractor(SimpleT2Decompiler):
raise NotImplementedError raise NotImplementedError
def op_roll(self, index): def op_roll(self, index):
raise NotImplementedError raise NotImplementedError
# #
# miscellaneous helpers # miscellaneous helpers
# #
@ -934,7 +934,7 @@ class T2OutlineExtractor(SimpleT2Decompiler):
point = (0, arg) point = (0, arg)
self.rLineTo(point) self.rLineTo(point)
isHorizontal = not isHorizontal isHorizontal = not isHorizontal
def vcurveto(self, args): def vcurveto(self, args):
dya, dxb, dyb, dxc = args[:4] dya, dxb, dyb, dxc = args[:4]
args = args[4:] args = args[4:]
@ -945,7 +945,7 @@ class T2OutlineExtractor(SimpleT2Decompiler):
dyc = 0 dyc = 0
self.rCurveTo((0, dya), (dxb, dyb), (dxc, dyc)) self.rCurveTo((0, dya), (dxb, dyb), (dxc, dyc))
return args return args
def hcurveto(self, args): def hcurveto(self, args):
dxa, dxb, dyb, dyc = args[:4] dxa, dxb, dyb, dyc = args[:4]
args = args[4:] args = args[4:]
@ -959,18 +959,18 @@ class T2OutlineExtractor(SimpleT2Decompiler):
class T1OutlineExtractor(T2OutlineExtractor): class T1OutlineExtractor(T2OutlineExtractor):
def __init__(self, pen, subrs): def __init__(self, pen, subrs):
self.pen = pen self.pen = pen
self.subrs = subrs self.subrs = subrs
self.reset() self.reset()
def reset(self): def reset(self):
self.flexing = 0 self.flexing = 0
self.width = 0 self.width = 0
self.sbx = 0 self.sbx = 0
T2OutlineExtractor.reset(self) T2OutlineExtractor.reset(self)
def endPath(self): def endPath(self):
if self.sawMoveTo: if self.sawMoveTo:
self.pen.endPath() self.pen.endPath()
@ -978,11 +978,11 @@ class T1OutlineExtractor(T2OutlineExtractor):
def popallWidth(self, evenOdd=0): def popallWidth(self, evenOdd=0):
return self.popall() return self.popall()
def exch(self): def exch(self):
stack = self.operandStack stack = self.operandStack
stack[-1], stack[-2] = stack[-2], stack[-1] stack[-1], stack[-2] = stack[-2], stack[-1]
# #
# path constructors # path constructors
# #
@ -1012,10 +1012,10 @@ class T1OutlineExtractor(T2OutlineExtractor):
args = self.popall() args = self.popall()
x, y = args x, y = args
self.currentPoint = x, y self.currentPoint = x, y
def op_endchar(self, index): def op_endchar(self, index):
self.endPath() self.endPath()
def op_hsbw(self, index): def op_hsbw(self, index):
sbx, wx = self.popall() sbx, wx = self.popall()
self.width = wx self.width = wx
@ -1023,7 +1023,7 @@ class T1OutlineExtractor(T2OutlineExtractor):
self.currentPoint = sbx, self.currentPoint[1] self.currentPoint = sbx, self.currentPoint[1]
def op_sbw(self, index): def op_sbw(self, index):
self.popall() # XXX self.popall() # XXX
# #
def op_callsubr(self, index): def op_callsubr(self, index):
subrIndex = self.pop() subrIndex = self.pop()
@ -1041,12 +1041,12 @@ class T1OutlineExtractor(T2OutlineExtractor):
# ignore... # ignore...
def op_pop(self, index): def op_pop(self, index):
pass # ignore... pass # ignore...
def doFlex(self): def doFlex(self):
finaly = self.pop() finaly = self.pop()
finalx = self.pop() finalx = self.pop()
self.pop() # flex height is unused self.pop() # flex height is unused
p3y = self.pop() p3y = self.pop()
p3x = self.pop() p3x = self.pop()
bcp4y = self.pop() bcp4y = self.pop()
@ -1061,7 +1061,7 @@ class T1OutlineExtractor(T2OutlineExtractor):
bcp1x = self.pop() bcp1x = self.pop()
rpy = self.pop() rpy = self.pop()
rpx = self.pop() rpx = self.pop()
# call rrcurveto # call rrcurveto
self.push(bcp1x+rpx) self.push(bcp1x+rpx)
self.push(bcp1y+rpy) self.push(bcp1y+rpy)
@ -1070,7 +1070,7 @@ class T1OutlineExtractor(T2OutlineExtractor):
self.push(p2x) self.push(p2x)
self.push(p2y) self.push(p2y)
self.op_rrcurveto(None) self.op_rrcurveto(None)
# call rrcurveto # call rrcurveto
self.push(bcp3x) self.push(bcp3x)
self.push(bcp3y) self.push(bcp3y)
@ -1079,11 +1079,11 @@ class T1OutlineExtractor(T2OutlineExtractor):
self.push(p3x) self.push(p3x)
self.push(p3y) self.push(p3y)
self.op_rrcurveto(None) self.op_rrcurveto(None)
# Push back final coords so subr 0 can find them # Push back final coords so subr 0 can find them
self.push(finalx) self.push(finalx)
self.push(finaly) self.push(finaly)
def op_dotsection(self, index): def op_dotsection(self, index):
self.popall() # XXX self.popall() # XXX
def op_hstem3(self, index): def op_hstem3(self, index):
@ -1102,18 +1102,18 @@ class T1OutlineExtractor(T2OutlineExtractor):
class DictDecompiler(ByteCodeBase): class DictDecompiler(ByteCodeBase):
operandEncoding = cffDictOperandEncoding operandEncoding = cffDictOperandEncoding
def __init__(self, strings): def __init__(self, strings):
self.stack = [] self.stack = []
self.strings = strings self.strings = strings
self.dict = {} self.dict = {}
def getDict(self): def getDict(self):
assert len(self.stack) == 0, "non-empty stack" assert len(self.stack) == 0, "non-empty stack"
return self.dict return self.dict
def decompile(self, data): def decompile(self, data):
index = 0 index = 0
lenData = len(data) lenData = len(data)
@ -1125,17 +1125,17 @@ class DictDecompiler(ByteCodeBase):
value, index = handler(self, b0, data, index) value, index = handler(self, b0, data, index)
if value is not None: if value is not None:
push(value) push(value)
def pop(self): def pop(self):
value = self.stack[-1] value = self.stack[-1]
del self.stack[-1] del self.stack[-1]
return value return value
def popall(self): def popall(self):
args = self.stack[:] args = self.stack[:]
del self.stack[:] del self.stack[:]
return args return args
def handle_operator(self, operator): def handle_operator(self, operator):
operator, argType = operator operator, argType = operator
if isinstance(argType, type(())): if isinstance(argType, type(())):
@ -1148,7 +1148,7 @@ class DictDecompiler(ByteCodeBase):
arghandler = getattr(self, "arg_" + argType) arghandler = getattr(self, "arg_" + argType)
value = arghandler(operator) value = arghandler(operator)
self.dict[operator] = value self.dict[operator] = value
def arg_number(self, name): def arg_number(self, name):
return self.pop() return self.pop()
def arg_SID(self, name): def arg_SID(self, name):

View File

@ -39,7 +39,7 @@ class PSError(Exception): pass
class PSTokenizer(StringIO): class PSTokenizer(StringIO):
def getnexttoken(self, def getnexttoken(self,
# localize some stuff, for performance # localize some stuff, for performance
len=len, len=len,
@ -47,9 +47,9 @@ class PSTokenizer(StringIO):
stringmatch=stringRE.match, stringmatch=stringRE.match,
hexstringmatch=hexstringRE.match, hexstringmatch=hexstringRE.match,
commentmatch=commentRE.match, commentmatch=commentRE.match,
endmatch=endofthingRE.match, endmatch=endofthingRE.match,
whitematch=skipwhiteRE.match): whitematch=skipwhiteRE.match):
_, nextpos = whitematch(self.buf, self.pos).span() _, nextpos = whitematch(self.buf, self.pos).span()
self.pos = nextpos self.pos = nextpos
if self.pos >= self.len: if self.pos >= self.len:
@ -94,11 +94,11 @@ class PSTokenizer(StringIO):
token = buf[pos:nextpos] token = buf[pos:nextpos]
self.pos = pos + len(token) self.pos = pos + len(token)
return tokentype, token return tokentype, token
def skipwhite(self, whitematch=skipwhiteRE.match): def skipwhite(self, whitematch=skipwhiteRE.match):
_, nextpos = whitematch(self.buf, self.pos).span() _, nextpos = whitematch(self.buf, self.pos).span()
self.pos = nextpos self.pos = nextpos
def starteexec(self): def starteexec(self):
self.pos = self.pos + 1 self.pos = self.pos + 1
#self.skipwhite() #self.skipwhite()
@ -106,13 +106,13 @@ class PSTokenizer(StringIO):
self.buf, R = eexec.decrypt(self.dirtybuf, 55665) self.buf, R = eexec.decrypt(self.dirtybuf, 55665)
self.len = len(self.buf) self.len = len(self.buf)
self.pos = 4 self.pos = 4
def stopeexec(self): def stopeexec(self):
if not hasattr(self, 'dirtybuf'): if not hasattr(self, 'dirtybuf'):
return return
self.buf = self.dirtybuf self.buf = self.dirtybuf
del self.dirtybuf del self.dirtybuf
def flush(self): def flush(self):
if self.buflist: if self.buflist:
self.buf = self.buf + "".join(self.buflist) self.buf = self.buf + "".join(self.buflist)
@ -120,7 +120,7 @@ class PSTokenizer(StringIO):
class PSInterpreter(PSOperators): class PSInterpreter(PSOperators):
def __init__(self): def __init__(self):
systemdict = {} systemdict = {}
userdict = {} userdict = {}
@ -129,7 +129,7 @@ class PSInterpreter(PSOperators):
self.proclevel = 0 self.proclevel = 0
self.procmark = ps_procmark() self.procmark = ps_procmark()
self.fillsystemdict() self.fillsystemdict()
def fillsystemdict(self): def fillsystemdict(self):
systemdict = self.dictstack[0] systemdict = self.dictstack[0]
systemdict['['] = systemdict['mark'] = self.mark = ps_mark() systemdict['['] = systemdict['mark'] = self.mark = ps_mark()
@ -139,7 +139,7 @@ class PSInterpreter(PSOperators):
systemdict['StandardEncoding'] = ps_array(ps_StandardEncoding) systemdict['StandardEncoding'] = ps_array(ps_StandardEncoding)
systemdict['FontDirectory'] = ps_dict({}) systemdict['FontDirectory'] = ps_dict({})
self.suckoperators(systemdict, self.__class__) self.suckoperators(systemdict, self.__class__)
def suckoperators(self, systemdict, klass): def suckoperators(self, systemdict, klass):
for name in dir(klass): for name in dir(klass):
attr = getattr(self, name) attr = getattr(self, name)
@ -148,7 +148,7 @@ class PSInterpreter(PSOperators):
systemdict[name] = ps_operator(name, attr) systemdict[name] = ps_operator(name, attr)
for baseclass in klass.__bases__: for baseclass in klass.__bases__:
self.suckoperators(systemdict, baseclass) self.suckoperators(systemdict, baseclass)
def interpret(self, data, getattr=getattr): def interpret(self, data, getattr=getattr):
tokenizer = self.tokenizer = PSTokenizer(data) tokenizer = self.tokenizer = PSTokenizer(data)
getnexttoken = tokenizer.getnexttoken getnexttoken = tokenizer.getnexttoken
@ -177,7 +177,7 @@ class PSInterpreter(PSOperators):
print('>>>') print('>>>')
print(self.tokenizer.buf[self.tokenizer.pos:self.tokenizer.pos+50]) print(self.tokenizer.buf[self.tokenizer.pos:self.tokenizer.pos+50])
print('- - - - - - -') print('- - - - - - -')
def handle_object(self, object): def handle_object(self, object):
if not (self.proclevel or object.literal or object.type == 'proceduretype'): if not (self.proclevel or object.literal or object.type == 'proceduretype'):
if object.type != 'operatortype': if object.type != 'operatortype':
@ -191,21 +191,21 @@ class PSInterpreter(PSOperators):
object.function() object.function()
else: else:
self.push(object) self.push(object)
def call_procedure(self, proc): def call_procedure(self, proc):
handle_object = self.handle_object handle_object = self.handle_object
for item in proc.value: for item in proc.value:
handle_object(item) handle_object(item)
def resolve_name(self, name): def resolve_name(self, name):
dictstack = self.dictstack dictstack = self.dictstack
for i in range(len(dictstack)-1, -1, -1): for i in range(len(dictstack)-1, -1, -1):
if name in dictstack[i]: if name in dictstack[i]:
return dictstack[i][name] return dictstack[i][name]
raise PSError('name error: ' + str(name)) raise PSError('name error: ' + str(name))
def do_token(self, token, def do_token(self, token,
int=int, int=int,
float=float, float=float,
ps_name=ps_name, ps_name=ps_name,
ps_integer=ps_integer, ps_integer=ps_integer,
@ -231,16 +231,16 @@ class PSInterpreter(PSOperators):
return ps_real(num) return ps_real(num)
else: else:
return ps_integer(num) return ps_integer(num)
def do_comment(self, token): def do_comment(self, token):
pass pass
def do_literal(self, token): def do_literal(self, token):
return ps_literal(token[1:]) return ps_literal(token[1:])
def do_string(self, token): def do_string(self, token):
return ps_string(token[1:-1]) return ps_string(token[1:-1])
def do_hexstring(self, token): def do_hexstring(self, token):
hexStr = "".join(token[1:-1].split()) hexStr = "".join(token[1:-1].split())
if len(hexStr) % 2: if len(hexStr) % 2:
@ -250,7 +250,7 @@ class PSInterpreter(PSOperators):
cleanstr.append(chr(int(hexStr[i:i+2], 16))) cleanstr.append(chr(int(hexStr[i:i+2], 16)))
cleanstr = "".join(cleanstr) cleanstr = "".join(cleanstr)
return ps_string(cleanstr) return ps_string(cleanstr)
def do_special(self, token): def do_special(self, token):
if token == '{': if token == '{':
self.proclevel = self.proclevel + 1 self.proclevel = self.proclevel + 1
@ -271,10 +271,10 @@ class PSInterpreter(PSOperators):
return ps_name(']') return ps_name(']')
else: else:
raise PSTokenError('huh?') raise PSTokenError('huh?')
def push(self, object): def push(self, object):
self.stack.append(object) self.stack.append(object)
def pop(self, *types): def pop(self, *types):
stack = self.stack stack = self.stack
if not stack: if not stack:
@ -285,7 +285,7 @@ class PSInterpreter(PSOperators):
raise PSError('typecheck, expected %s, found %s' % (repr(types), object.type)) raise PSError('typecheck, expected %s, found %s' % (repr(types), object.type))
del stack[-1] del stack[-1]
return object return object
def do_makearray(self): def do_makearray(self):
array = [] array = []
while 1: while 1:
@ -295,7 +295,7 @@ class PSInterpreter(PSOperators):
array.append(topobject) array.append(topobject)
array.reverse() array.reverse()
self.push(ps_array(array)) self.push(ps_array(array))
def close(self): def close(self):
"""Remove circular references.""" """Remove circular references."""
del self.stack del self.stack

View File

@ -5,23 +5,23 @@ _accessstrings = {0: "", 1: "readonly", 2: "executeonly", 3: "noaccess"}
class ps_object: class ps_object:
literal = 1 literal = 1
access = 0 access = 0
value = None value = None
def __init__(self, value): def __init__(self, value):
self.value = value self.value = value
self.type = self.__class__.__name__[3:] + "type" self.type = self.__class__.__name__[3:] + "type"
def __repr__(self): def __repr__(self):
return "<%s %s>" % (self.__class__.__name__[3:], repr(self.value)) return "<%s %s>" % (self.__class__.__name__[3:], repr(self.value))
class ps_operator(ps_object): class ps_operator(ps_object):
literal = 0 literal = 0
def __init__(self, name, function): def __init__(self, name, function):
self.name = name self.name = name
self.function = function self.function = function
@ -171,7 +171,7 @@ class ps_dict(ps_object):
return "<dict>" return "<dict>"
class ps_mark(ps_object): class ps_mark(ps_object):
def __init__(self): def __init__(self):
self.value = 'mark' self.value = 'mark'
self.type = self.__class__.__name__[3:] + "type" self.type = self.__class__.__name__[3:] + "type"
@ -205,17 +205,17 @@ class ps_real(ps_object):
class PSOperators: class PSOperators:
def ps_def(self): def ps_def(self):
obj = self.pop() obj = self.pop()
name = self.pop() name = self.pop()
self.dictstack[-1][name.value] = obj self.dictstack[-1][name.value] = obj
def ps_bind(self): def ps_bind(self):
proc = self.pop('proceduretype') proc = self.pop('proceduretype')
self.proc_bind(proc) self.proc_bind(proc)
self.push(proc) self.push(proc)
def proc_bind(self, proc): def proc_bind(self, proc):
for i in range(len(proc.value)): for i in range(len(proc.value)):
item = proc.value[i] item = proc.value[i]
@ -230,7 +230,7 @@ class PSOperators:
else: else:
if obj.type == 'operatortype': if obj.type == 'operatortype':
proc.value[i] = obj proc.value[i] = obj
def ps_exch(self): def ps_exch(self):
if len(self.stack) < 2: if len(self.stack) < 2:
raise RuntimeError('stack underflow') raise RuntimeError('stack underflow')
@ -238,49 +238,49 @@ class PSOperators:
obj2 = self.pop() obj2 = self.pop()
self.push(obj1) self.push(obj1)
self.push(obj2) self.push(obj2)
def ps_dup(self): def ps_dup(self):
if not self.stack: if not self.stack:
raise RuntimeError('stack underflow') raise RuntimeError('stack underflow')
self.push(self.stack[-1]) self.push(self.stack[-1])
def ps_exec(self): def ps_exec(self):
obj = self.pop() obj = self.pop()
if obj.type == 'proceduretype': if obj.type == 'proceduretype':
self.call_procedure(obj) self.call_procedure(obj)
else: else:
self.handle_object(obj) self.handle_object(obj)
def ps_count(self): def ps_count(self):
self.push(ps_integer(len(self.stack))) self.push(ps_integer(len(self.stack)))
def ps_eq(self): def ps_eq(self):
any1 = self.pop() any1 = self.pop()
any2 = self.pop() any2 = self.pop()
self.push(ps_boolean(any1.value == any2.value)) self.push(ps_boolean(any1.value == any2.value))
def ps_ne(self): def ps_ne(self):
any1 = self.pop() any1 = self.pop()
any2 = self.pop() any2 = self.pop()
self.push(ps_boolean(any1.value != any2.value)) self.push(ps_boolean(any1.value != any2.value))
def ps_cvx(self): def ps_cvx(self):
obj = self.pop() obj = self.pop()
obj.literal = 0 obj.literal = 0
self.push(obj) self.push(obj)
def ps_matrix(self): def ps_matrix(self):
matrix = [ps_real(1.0), ps_integer(0), ps_integer(0), ps_real(1.0), ps_integer(0), ps_integer(0)] matrix = [ps_real(1.0), ps_integer(0), ps_integer(0), ps_real(1.0), ps_integer(0), ps_integer(0)]
self.push(ps_array(matrix)) self.push(ps_array(matrix))
def ps_string(self): def ps_string(self):
num = self.pop('integertype').value num = self.pop('integertype').value
self.push(ps_string('\0' * num)) self.push(ps_string('\0' * num))
def ps_type(self): def ps_type(self):
obj = self.pop() obj = self.pop()
self.push(ps_string(obj.type)) self.push(ps_string(obj.type))
def ps_store(self): def ps_store(self):
value = self.pop() value = self.pop()
key = self.pop() key = self.pop()
@ -290,38 +290,38 @@ class PSOperators:
self.dictstack[i][name] = value self.dictstack[i][name] = value
break break
self.dictstack[-1][name] = value self.dictstack[-1][name] = value
def ps_where(self): def ps_where(self):
name = self.pop() name = self.pop()
# XXX # XXX
self.push(ps_boolean(0)) self.push(ps_boolean(0))
def ps_systemdict(self): def ps_systemdict(self):
self.push(ps_dict(self.dictstack[0])) self.push(ps_dict(self.dictstack[0]))
def ps_userdict(self): def ps_userdict(self):
self.push(ps_dict(self.dictstack[1])) self.push(ps_dict(self.dictstack[1]))
def ps_currentdict(self): def ps_currentdict(self):
self.push(ps_dict(self.dictstack[-1])) self.push(ps_dict(self.dictstack[-1]))
def ps_currentfile(self): def ps_currentfile(self):
self.push(ps_file(self.tokenizer)) self.push(ps_file(self.tokenizer))
def ps_eexec(self): def ps_eexec(self):
f = self.pop('filetype').value f = self.pop('filetype').value
f.starteexec() f.starteexec()
def ps_closefile(self): def ps_closefile(self):
f = self.pop('filetype').value f = self.pop('filetype').value
f.skipwhite() f.skipwhite()
f.stopeexec() f.stopeexec()
def ps_cleartomark(self): def ps_cleartomark(self):
obj = self.pop() obj = self.pop()
while obj != self.mark: while obj != self.mark:
obj = self.pop() obj = self.pop()
def ps_readstring(self, def ps_readstring(self,
ps_boolean=ps_boolean, ps_boolean=ps_boolean,
len=len): len=len):
@ -335,17 +335,17 @@ class PSOperators:
s.value = newstr s.value = newstr
self.push(s) self.push(s)
self.push(ps_boolean(len(oldstr) == len(newstr))) self.push(ps_boolean(len(oldstr) == len(newstr)))
def ps_known(self): def ps_known(self):
key = self.pop() key = self.pop()
d = self.pop('dicttype', 'fonttype') d = self.pop('dicttype', 'fonttype')
self.push(ps_boolean(key.value in d.value)) self.push(ps_boolean(key.value in d.value))
def ps_if(self): def ps_if(self):
proc = self.pop('proceduretype') proc = self.pop('proceduretype')
if self.pop('booleantype').value: if self.pop('booleantype').value:
self.call_procedure(proc) self.call_procedure(proc)
def ps_ifelse(self): def ps_ifelse(self):
proc2 = self.pop('proceduretype') proc2 = self.pop('proceduretype')
proc1 = self.pop('proceduretype') proc1 = self.pop('proceduretype')
@ -353,36 +353,36 @@ class PSOperators:
self.call_procedure(proc1) self.call_procedure(proc1)
else: else:
self.call_procedure(proc2) self.call_procedure(proc2)
def ps_readonly(self): def ps_readonly(self):
obj = self.pop() obj = self.pop()
if obj.access < 1: if obj.access < 1:
obj.access = 1 obj.access = 1
self.push(obj) self.push(obj)
def ps_executeonly(self): def ps_executeonly(self):
obj = self.pop() obj = self.pop()
if obj.access < 2: if obj.access < 2:
obj.access = 2 obj.access = 2
self.push(obj) self.push(obj)
def ps_noaccess(self): def ps_noaccess(self):
obj = self.pop() obj = self.pop()
if obj.access < 3: if obj.access < 3:
obj.access = 3 obj.access = 3
self.push(obj) self.push(obj)
def ps_not(self): def ps_not(self):
obj = self.pop('booleantype', 'integertype') obj = self.pop('booleantype', 'integertype')
if obj.type == 'booleantype': if obj.type == 'booleantype':
self.push(ps_boolean(not obj.value)) self.push(ps_boolean(not obj.value))
else: else:
self.push(ps_integer(~obj.value)) self.push(ps_integer(~obj.value))
def ps_print(self): def ps_print(self):
str = self.pop('stringtype') str = self.pop('stringtype')
print('PS output --->', str.value) print('PS output --->', str.value)
def ps_anchorsearch(self): def ps_anchorsearch(self):
seek = self.pop('stringtype') seek = self.pop('stringtype')
s = self.pop('stringtype') s = self.pop('stringtype')
@ -394,22 +394,22 @@ class PSOperators:
else: else:
self.push(s) self.push(s)
self.push(ps_boolean(0)) self.push(ps_boolean(0))
def ps_array(self): def ps_array(self):
num = self.pop('integertype') num = self.pop('integertype')
array = ps_array([None] * num.value) array = ps_array([None] * num.value)
self.push(array) self.push(array)
def ps_astore(self): def ps_astore(self):
array = self.pop('arraytype') array = self.pop('arraytype')
for i in range(len(array.value)-1, -1, -1): for i in range(len(array.value)-1, -1, -1):
array.value[i] = self.pop() array.value[i] = self.pop()
self.push(array) self.push(array)
def ps_load(self): def ps_load(self):
name = self.pop() name = self.pop()
self.push(self.resolve_name(name.value)) self.push(self.resolve_name(name.value))
def ps_put(self): def ps_put(self):
obj1 = self.pop() obj1 = self.pop()
obj2 = self.pop() obj2 = self.pop()
@ -422,7 +422,7 @@ class PSOperators:
elif tp == 'stringtype': elif tp == 'stringtype':
index = obj2.value index = obj2.value
obj3.value = obj3.value[:index] + chr(obj1.value) + obj3.value[index+1:] obj3.value = obj3.value[:index] + chr(obj1.value) + obj3.value[index+1:]
def ps_get(self): def ps_get(self):
obj1 = self.pop() obj1 = self.pop()
if obj1.value == "Encoding": if obj1.value == "Encoding":
@ -437,7 +437,7 @@ class PSOperators:
self.push(ps_integer(ord(obj2.value[obj1.value]))) self.push(ps_integer(ord(obj2.value[obj1.value])))
else: else:
assert False, "shouldn't get here" assert False, "shouldn't get here"
def ps_getinterval(self): def ps_getinterval(self):
obj1 = self.pop('integertype') obj1 = self.pop('integertype')
obj2 = self.pop('integertype') obj2 = self.pop('integertype')
@ -447,7 +447,7 @@ class PSOperators:
self.push(ps_array(obj3.value[obj2.value:obj2.value + obj1.value])) self.push(ps_array(obj3.value[obj2.value:obj2.value + obj1.value]))
elif tp == 'stringtype': elif tp == 'stringtype':
self.push(ps_string(obj3.value[obj2.value:obj2.value + obj1.value])) self.push(ps_string(obj3.value[obj2.value:obj2.value + obj1.value]))
def ps_putinterval(self): def ps_putinterval(self):
obj1 = self.pop('arraytype', 'stringtype') obj1 = self.pop('arraytype', 'stringtype')
obj2 = self.pop('integertype') obj2 = self.pop('integertype')
@ -460,16 +460,16 @@ class PSOperators:
newstr = newstr + obj1.value newstr = newstr + obj1.value
newstr = newstr + obj3.value[obj2.value + len(obj1.value):] newstr = newstr + obj3.value[obj2.value + len(obj1.value):]
obj3.value = newstr obj3.value = newstr
def ps_cvn(self): def ps_cvn(self):
self.push(ps_name(self.pop('stringtype').value)) self.push(ps_name(self.pop('stringtype').value))
def ps_index(self): def ps_index(self):
n = self.pop('integertype').value n = self.pop('integertype').value
if n < 0: if n < 0:
raise RuntimeError('index may not be negative') raise RuntimeError('index may not be negative')
self.push(self.stack[-1-n]) self.push(self.stack[-1-n])
def ps_for(self): def ps_for(self):
proc = self.pop('proceduretype') proc = self.pop('proceduretype')
limit = self.pop('integertype', 'realtype').value limit = self.pop('integertype', 'realtype').value
@ -488,7 +488,7 @@ class PSOperators:
self.push(ps_integer(i)) self.push(ps_integer(i))
self.call_procedure(proc) self.call_procedure(proc)
i = i + increment i = i + increment
def ps_forall(self): def ps_forall(self):
proc = self.pop('proceduretype') proc = self.pop('proceduretype')
obj = self.pop('arraytype', 'stringtype', 'dicttype') obj = self.pop('arraytype', 'stringtype', 'dicttype')
@ -505,36 +505,36 @@ class PSOperators:
for key, value in obj.value.items(): for key, value in obj.value.items():
self.push(ps_name(key)) self.push(ps_name(key))
self.push(value) self.push(value)
self.call_procedure(proc) self.call_procedure(proc)
def ps_definefont(self): def ps_definefont(self):
font = self.pop('dicttype') font = self.pop('dicttype')
name = self.pop() name = self.pop()
font = ps_font(font.value) font = ps_font(font.value)
self.dictstack[0]['FontDirectory'].value[name.value] = font self.dictstack[0]['FontDirectory'].value[name.value] = font
self.push(font) self.push(font)
def ps_findfont(self): def ps_findfont(self):
name = self.pop() name = self.pop()
font = self.dictstack[0]['FontDirectory'].value[name.value] font = self.dictstack[0]['FontDirectory'].value[name.value]
self.push(font) self.push(font)
def ps_pop(self): def ps_pop(self):
self.pop() self.pop()
def ps_dict(self): def ps_dict(self):
self.pop('integertype') self.pop('integertype')
self.push(ps_dict({})) self.push(ps_dict({}))
def ps_begin(self): def ps_begin(self):
self.dictstack.append(self.pop('dicttype').value) self.dictstack.append(self.pop('dicttype').value)
def ps_end(self): def ps_end(self):
if len(self.dictstack) > 2: if len(self.dictstack) > 2:
del self.dictstack[-1] del self.dictstack[-1]
else: else:
raise RuntimeError('dictstack underflow') raise RuntimeError('dictstack underflow')
notdef = '.notdef' notdef = '.notdef'
from fontTools.encodings.StandardEncoding import StandardEncoding from fontTools.encodings.StandardEncoding import StandardEncoding
ps_StandardEncoding = list(map(ps_name, StandardEncoding)) ps_StandardEncoding = list(map(ps_name, StandardEncoding))

View File

@ -1,44 +1,44 @@
"""sstruct.py -- SuperStruct """sstruct.py -- SuperStruct
Higher level layer on top of the struct module, enabling to Higher level layer on top of the struct module, enabling to
bind names to struct elements. The interface is similar to bind names to struct elements. The interface is similar to
struct, except the objects passed and returned are not tuples struct, except the objects passed and returned are not tuples
(or argument lists), but dictionaries or instances. (or argument lists), but dictionaries or instances.
Just like struct, we use fmt strings to describe a data Just like struct, we use fmt strings to describe a data
structure, except we use one line per element. Lines are structure, except we use one line per element. Lines are
separated by newlines or semi-colons. Each line contains separated by newlines or semi-colons. Each line contains
either one of the special struct characters ('@', '=', '<', either one of the special struct characters ('@', '=', '<',
'>' or '!') or a 'name:formatchar' combo (eg. 'myFloat:f'). '>' or '!') or a 'name:formatchar' combo (eg. 'myFloat:f').
Repetitions, like the struct module offers them are not useful Repetitions, like the struct module offers them are not useful
in this context, except for fixed length strings (eg. 'myInt:5h' in this context, except for fixed length strings (eg. 'myInt:5h'
is not allowed but 'myString:5s' is). The 'x' fmt character is not allowed but 'myString:5s' is). The 'x' fmt character
(pad byte) is treated as 'special', since it is by definition (pad byte) is treated as 'special', since it is by definition
anonymous. Extra whitespace is allowed everywhere. anonymous. Extra whitespace is allowed everywhere.
The sstruct module offers one feature that the "normal" struct The sstruct module offers one feature that the "normal" struct
module doesn't: support for fixed point numbers. These are spelled module doesn't: support for fixed point numbers. These are spelled
as "n.mF", where n is the number of bits before the point, and m as "n.mF", where n is the number of bits before the point, and m
the number of bits after the point. Fixed point numbers get the number of bits after the point. Fixed point numbers get
converted to floats. converted to floats.
pack(fmt, object): pack(fmt, object):
'object' is either a dictionary or an instance (or actually 'object' is either a dictionary or an instance (or actually
anything that has a __dict__ attribute). If it is a dictionary, anything that has a __dict__ attribute). If it is a dictionary,
its keys are used for names. If it is an instance, it's its keys are used for names. If it is an instance, it's
attributes are used to grab struct elements from. Returns attributes are used to grab struct elements from. Returns
a string containing the data. a string containing the data.
unpack(fmt, data, object=None) unpack(fmt, data, object=None)
If 'object' is omitted (or None), a new dictionary will be If 'object' is omitted (or None), a new dictionary will be
returned. If 'object' is a dictionary, it will be used to add returned. If 'object' is a dictionary, it will be used to add
struct elements to. If it is an instance (or in fact anything struct elements to. If it is an instance (or in fact anything
that has a __dict__ attribute), an attribute will be added for that has a __dict__ attribute), an attribute will be added for
each struct element. In the latter two cases, 'object' itself each struct element. In the latter two cases, 'object' itself
is returned. is returned.
unpack2(fmt, data, object=None) unpack2(fmt, data, object=None)
Convenience function. Same as unpack, except data may be longer Convenience function. Same as unpack, except data may be longer
than needed. The returned value is a tuple: (object, leftoverdata). than needed. The returned value is a tuple: (object, leftoverdata).
calcsize(fmt) calcsize(fmt)
@ -174,7 +174,7 @@ def _test():
# comments are allowed # comments are allowed
> # big endian (see documentation for struct) > # big endian (see documentation for struct)
# empty lines are allowed: # empty lines are allowed:
ashort: h ashort: h
along: l along: l
abyte: b # a byte abyte: b # a byte
@ -183,14 +183,14 @@ def _test():
afloat: f; adouble: d # multiple "statements" are allowed afloat: f; adouble: d # multiple "statements" are allowed
afixed: 16.16F afixed: 16.16F
""" """
print('size:', calcsize(fmt)) print('size:', calcsize(fmt))
class foo(object): class foo(object):
pass pass
i = foo() i = foo()
i.ashort = 0x7fff i.ashort = 0x7fff
i.along = 0x7fffffff i.along = 0x7fffffff
i.abyte = 0x7f i.abyte = 0x7f
@ -199,7 +199,7 @@ def _test():
i.afloat = 0.5 i.afloat = 0.5
i.adouble = 0.5 i.adouble = 0.5
i.afixed = 1.5 i.afixed = 1.5
data = pack(fmt, i) data = pack(fmt, i)
print('data:', repr(data)) print('data:', repr(data))
print(unpack(fmt, data)) print(unpack(fmt, data))

View File

@ -66,10 +66,10 @@ def binary2num(bin):
def caselessSort(alist): def caselessSort(alist):
"""Return a sorted copy of a list. If there are only strings """Return a sorted copy of a list. If there are only strings
in the list, it will not consider case. in the list, it will not consider case.
""" """
try: try:
return sorted(alist, key=lambda a: (a.lower(), a)) return sorted(alist, key=lambda a: (a.lower(), a))
except TypeError: except TypeError:

View File

@ -12,7 +12,7 @@ BUFSIZE = 0x4000
class XMLReader(object): class XMLReader(object):
def __init__(self, fileName, ttFont, progress=None, quiet=False): def __init__(self, fileName, ttFont, progress=None, quiet=False):
self.ttFont = ttFont self.ttFont = ttFont
self.fileName = fileName self.fileName = fileName
@ -21,7 +21,7 @@ class XMLReader(object):
self.root = None self.root = None
self.contentStack = [] self.contentStack = []
self.stackSize = 0 self.stackSize = 0
def read(self): def read(self):
if self.progress: if self.progress:
import stat import stat
@ -29,14 +29,14 @@ class XMLReader(object):
file = open(self.fileName) file = open(self.fileName)
self._parseFile(file) self._parseFile(file)
file.close() file.close()
def _parseFile(self, file): def _parseFile(self, file):
from xml.parsers.expat import ParserCreate from xml.parsers.expat import ParserCreate
parser = ParserCreate() parser = ParserCreate()
parser.StartElementHandler = self._startElementHandler parser.StartElementHandler = self._startElementHandler
parser.EndElementHandler = self._endElementHandler parser.EndElementHandler = self._endElementHandler
parser.CharacterDataHandler = self._characterDataHandler parser.CharacterDataHandler = self._characterDataHandler
pos = 0 pos = 0
while True: while True:
chunk = file.read(BUFSIZE) chunk = file.read(BUFSIZE)
@ -47,7 +47,7 @@ class XMLReader(object):
if self.progress: if self.progress:
self.progress.set(pos // 100) self.progress.set(pos // 100)
parser.Parse(chunk, 0) parser.Parse(chunk, 0)
def _startElementHandler(self, name, attrs): def _startElementHandler(self, name, attrs):
stackSize = self.stackSize stackSize = self.stackSize
self.stackSize = stackSize + 1 self.stackSize = stackSize + 1
@ -100,11 +100,11 @@ class XMLReader(object):
l = [] l = []
self.contentStack[-1].append((name, attrs, l)) self.contentStack[-1].append((name, attrs, l))
self.contentStack.append(l) self.contentStack.append(l)
def _characterDataHandler(self, data): def _characterDataHandler(self, data):
if self.stackSize > 1: if self.stackSize > 1:
self.contentStack[-1].append(data) self.contentStack[-1].append(data)
def _endElementHandler(self, name): def _endElementHandler(self, name):
self.stackSize = self.stackSize - 1 self.stackSize = self.stackSize - 1
del self.contentStack[-1] del self.contentStack[-1]
@ -117,15 +117,15 @@ class XMLReader(object):
class ProgressPrinter(object): class ProgressPrinter(object):
def __init__(self, title, maxval=100): def __init__(self, title, maxval=100):
print(title) print(title)
def set(self, val, maxval=None): def set(self, val, maxval=None):
pass pass
def increment(self, val=1): def increment(self, val=1):
pass pass
def setLabel(self, text): def setLabel(self, text):
print(text) print(text)

View File

@ -10,7 +10,7 @@ INDENT = " "
class XMLWriter(object): class XMLWriter(object):
def __init__(self, fileOrPath, indentwhite=INDENT, idlefunc=None, encoding="utf_8"): def __init__(self, fileOrPath, indentwhite=INDENT, idlefunc=None, encoding="utf_8"):
if encoding.lower().replace('-','').replace('_','') != 'utf8': if encoding.lower().replace('-','').replace('_','') != 'utf8':
raise Exception('Only UTF-8 encoding is supported.') raise Exception('Only UTF-8 encoding is supported.')
@ -41,10 +41,10 @@ class XMLWriter(object):
self.idlecounter = 0 self.idlecounter = 0
self._writeraw('<?xml version="1.0" encoding="UTF-8"?>') self._writeraw('<?xml version="1.0" encoding="UTF-8"?>')
self.newline() self.newline()
def close(self): def close(self):
self.file.close() self.file.close()
def write(self, string, indent=True): def write(self, string, indent=True):
"""Writes text.""" """Writes text."""
self._writeraw(escape(string), indent=indent) self._writeraw(escape(string), indent=indent)
@ -63,7 +63,7 @@ class XMLWriter(object):
def write_noindent(self, string): def write_noindent(self, string):
"""Writes text without indentation.""" """Writes text without indentation."""
self._writeraw(escape(string), indent=False) self._writeraw(escape(string), indent=False)
def _writeraw(self, data, indent=True, strip=False): def _writeraw(self, data, indent=True, strip=False):
"""Writes bytes, possibly indented.""" """Writes bytes, possibly indented."""
if indent and self.needindent: if indent and self.needindent:
@ -73,7 +73,7 @@ class XMLWriter(object):
if (strip): if (strip):
s = s.strip() s = s.strip()
self.file.write(s) self.file.write(s)
def newline(self): def newline(self):
self.file.write(self.newlinestr) self.file.write(self.newlinestr)
self.needindent = 1 self.needindent = 1
@ -81,7 +81,7 @@ class XMLWriter(object):
if not idlecounter % 100 and self.idlefunc is not None: if not idlecounter % 100 and self.idlefunc is not None:
self.idlefunc() self.idlefunc()
self.idlecounter = idlecounter + 1 self.idlecounter = idlecounter + 1
def comment(self, data): def comment(self, data):
data = escape(data) data = escape(data)
lines = data.split("\n") lines = data.split("\n")
@ -90,26 +90,26 @@ class XMLWriter(object):
self.newline() self.newline()
self._writeraw(" " + line) self._writeraw(" " + line)
self._writeraw(" -->") self._writeraw(" -->")
def simpletag(self, _TAG_, *args, **kwargs): def simpletag(self, _TAG_, *args, **kwargs):
attrdata = self.stringifyattrs(*args, **kwargs) attrdata = self.stringifyattrs(*args, **kwargs)
data = "<%s%s/>" % (_TAG_, attrdata) data = "<%s%s/>" % (_TAG_, attrdata)
self._writeraw(data) self._writeraw(data)
def begintag(self, _TAG_, *args, **kwargs): def begintag(self, _TAG_, *args, **kwargs):
attrdata = self.stringifyattrs(*args, **kwargs) attrdata = self.stringifyattrs(*args, **kwargs)
data = "<%s%s>" % (_TAG_, attrdata) data = "<%s%s>" % (_TAG_, attrdata)
self._writeraw(data) self._writeraw(data)
self.stack.append(_TAG_) self.stack.append(_TAG_)
self.indent() self.indent()
def endtag(self, _TAG_): def endtag(self, _TAG_):
assert self.stack and self.stack[-1] == _TAG_, "nonmatching endtag" assert self.stack and self.stack[-1] == _TAG_, "nonmatching endtag"
del self.stack[-1] del self.stack[-1]
self.dedent() self.dedent()
data = "</%s>" % _TAG_ data = "</%s>" % _TAG_
self._writeraw(data) self._writeraw(data)
def dumphex(self, data): def dumphex(self, data):
linelength = 16 linelength = 16
hexlinelength = linelength * 2 hexlinelength = linelength * 2
@ -123,14 +123,14 @@ class XMLWriter(object):
white = " " white = " "
self._writeraw(line) self._writeraw(line)
self.newline() self.newline()
def indent(self): def indent(self):
self.indentlevel = self.indentlevel + 1 self.indentlevel = self.indentlevel + 1
def dedent(self): def dedent(self):
assert self.indentlevel > 0 assert self.indentlevel > 0
self.indentlevel = self.indentlevel - 1 self.indentlevel = self.indentlevel - 1
def stringifyattrs(self, *args, **kwargs): def stringifyattrs(self, *args, **kwargs):
if kwargs: if kwargs:
assert not args assert not args
@ -144,7 +144,7 @@ class XMLWriter(object):
for attr, value in attributes: for attr, value in attributes:
data = data + ' %s="%s"' % (attr, escapeattr(str(value))) data = data + ' %s="%s"' % (attr, escapeattr(str(value)))
return data return data
def escape(data): def escape(data):
data = tostr(data, 'utf_8') data = tostr(data, 'utf_8')

View File

@ -3,13 +3,13 @@
Functions for reading and writing raw Type 1 data: Functions for reading and writing raw Type 1 data:
read(path) read(path)
reads any Type 1 font file, returns the raw data and a type indicator: reads any Type 1 font file, returns the raw data and a type indicator:
'LWFN', 'PFB' or 'OTHER', depending on the format of the file pointed 'LWFN', 'PFB' or 'OTHER', depending on the format of the file pointed
to by 'path'. to by 'path'.
Raises an error when the file does not contain valid Type 1 data. Raises an error when the file does not contain valid Type 1 data.
write(path, data, kind='OTHER', dohex=False) write(path, data, kind='OTHER', dohex=False)
writes raw Type 1 data to the file pointed to by 'path'. writes raw Type 1 data to the file pointed to by 'path'.
'kind' can be one of 'LWFN', 'PFB' or 'OTHER'; it defaults to 'OTHER'. 'kind' can be one of 'LWFN', 'PFB' or 'OTHER'; it defaults to 'OTHER'.
'dohex' is a flag which determines whether the eexec encrypted 'dohex' is a flag which determines whether the eexec encrypted
part should be written as hexadecimal or binary, but only if kind part should be written as hexadecimal or binary, but only if kind
@ -37,49 +37,49 @@ except ImportError:
else: else:
haveMacSupport = 1 haveMacSupport = 1
import MacOS import MacOS
class T1Error(Exception): pass class T1Error(Exception): pass
class T1Font(object): class T1Font(object):
"""Type 1 font class. """Type 1 font class.
Uses a minimal interpeter that supports just about enough PS to parse Uses a minimal interpeter that supports just about enough PS to parse
Type 1 fonts. Type 1 fonts.
""" """
def __init__(self, path=None): def __init__(self, path=None):
if path is not None: if path is not None:
self.data, type = read(path) self.data, type = read(path)
else: else:
pass # XXX pass # XXX
def saveAs(self, path, type): def saveAs(self, path, type):
write(path, self.getData(), type) write(path, self.getData(), type)
def getData(self): def getData(self):
# XXX Todo: if the data has been converted to Python object, # XXX Todo: if the data has been converted to Python object,
# recreate the PS stream # recreate the PS stream
return self.data return self.data
def getGlyphSet(self): def getGlyphSet(self):
"""Return a generic GlyphSet, which is a dict-like object """Return a generic GlyphSet, which is a dict-like object
mapping glyph names to glyph objects. The returned glyph objects mapping glyph names to glyph objects. The returned glyph objects
have a .draw() method that supports the Pen protocol, and will have a .draw() method that supports the Pen protocol, and will
have an attribute named 'width', but only *after* the .draw() method have an attribute named 'width', but only *after* the .draw() method
has been called. has been called.
In the case of Type 1, the GlyphSet is simply the CharStrings dict. In the case of Type 1, the GlyphSet is simply the CharStrings dict.
""" """
return self["CharStrings"] return self["CharStrings"]
def __getitem__(self, key): def __getitem__(self, key):
if not hasattr(self, "font"): if not hasattr(self, "font"):
self.parse() self.parse()
return self.font[key] return self.font[key]
def parse(self): def parse(self):
from fontTools.misc import psLib from fontTools.misc import psLib
from fontTools.misc import psCharStrings from fontTools.misc import psCharStrings
@ -135,7 +135,7 @@ def write(path, data, kind='OTHER', dohex=False):
pass pass
# -- internal -- # -- internal --
LWFNCHUNKSIZE = 2000 LWFNCHUNKSIZE = 2000
HEXLINELENGTH = 80 HEXLINELENGTH = 80
@ -203,7 +203,7 @@ def readOther(path):
data = f.read() data = f.read()
f.close() f.close()
assertType1(data) assertType1(data)
chunks = findEncryptedChunks(data) chunks = findEncryptedChunks(data)
data = [] data = []
for isEncrypted, chunk in chunks: for isEncrypted, chunk in chunks:

View File

@ -1,6 +1,6 @@
"""fontTools.ttLib -- a package for dealing with TrueType fonts. """fontTools.ttLib -- a package for dealing with TrueType fonts.
This package offers translators to convert TrueType fonts to Python This package offers translators to convert TrueType fonts to Python
objects and vice versa, and additionally from Python to TTX (an XML-based objects and vice versa, and additionally from Python to TTX (an XML-based
text format) and vice versa. text format) and vice versa.
@ -37,7 +37,7 @@ Dumping 'prep' table...
>>> tt2.importXML("afont.ttx") >>> tt2.importXML("afont.ttx")
>>> tt2['maxp'].numGlyphs >>> tt2['maxp'].numGlyphs
242 242
>>> >>>
""" """
@ -60,39 +60,39 @@ class TTLibError(Exception): pass
class TTFont(object): class TTFont(object):
"""The main font object. It manages file input and output, and offers """The main font object. It manages file input and output, and offers
a convenient way of accessing tables. a convenient way of accessing tables.
Tables will be only decompiled when necessary, ie. when they're actually Tables will be only decompiled when necessary, ie. when they're actually
accessed. This means that simple operations can be extremely fast. accessed. This means that simple operations can be extremely fast.
""" """
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=False, sfntVersion="\000\001\000\000", flavor=None, checkChecksums=False,
verbose=False, recalcBBoxes=True, allowVID=False, ignoreDecompileErrors=False, verbose=False, recalcBBoxes=True, allowVID=False, ignoreDecompileErrors=False,
recalcTimestamp=True, fontNumber=-1, lazy=None, quiet=False): recalcTimestamp=True, fontNumber=-1, lazy=None, quiet=False):
"""The constructor can be called with a few different arguments. """The constructor can be called with a few different arguments.
When reading a font from disk, 'file' should be either a pathname When reading a font from disk, 'file' should be either a pathname
pointing to a file, or a readable file object. pointing to a file, or a readable file object.
It we're running on a Macintosh, 'res_name_or_index' maybe an sfnt It we're running on a Macintosh, 'res_name_or_index' maybe an sfnt
resource name or an sfnt resource index number or zero. The latter resource name or an sfnt resource index number or zero. The latter
case will cause TTLib to autodetect whether the file is a flat file case will cause TTLib to autodetect whether the file is a flat file
or a suitcase. (If it's a suitcase, only the first 'sfnt' resource or a suitcase. (If it's a suitcase, only the first 'sfnt' resource
will be read!) will be read!)
The 'checkChecksums' argument is used to specify how sfnt The 'checkChecksums' argument is used to specify how sfnt
checksums are treated upon reading a file from disk: checksums are treated upon reading a file from disk:
0: don't check (default) 0: don't check (default)
1: check, print warnings if a wrong checksum is found 1: check, print warnings if a wrong checksum is found
2: check, raise an exception if a wrong checksum is found. 2: check, raise an exception if a wrong checksum is found.
The TTFont constructor can also be called without a 'file' The TTFont constructor can also be called without a 'file'
argument: this is the way to create a new empty font. argument: this is the way to create a new empty font.
In this case you can optionally supply the 'sfntVersion' argument, In this case you can optionally supply the 'sfntVersion' argument,
and a 'flavor' which can be None, or 'woff'. and a 'flavor' which can be None, or 'woff'.
If the recalcBBoxes argument is false, a number of things will *not* If the recalcBBoxes argument is false, a number of things will *not*
be recalculated upon save/compile: be recalculated upon save/compile:
1) glyph bounding boxes 1) glyph bounding boxes
@ -100,8 +100,8 @@ class TTFont(object):
3) hhea min/max values 3) hhea min/max values
(1) is needed for certain kinds of CJK fonts (ask Werner Lemberg ;-). (1) is needed for certain kinds of CJK fonts (ask Werner Lemberg ;-).
Additionally, upon importing an TTX file, this option cause glyphs Additionally, upon importing an TTX file, this option cause glyphs
to be compiled right away. This should reduce memory consumption to be compiled right away. This should reduce memory consumption
greatly, and therefore should have some impact on the time needed greatly, and therefore should have some impact on the time needed
to parse/compile large fonts. to parse/compile large fonts.
If the recalcTimestamp argument is false, the modified timestamp in the If the recalcTimestamp argument is false, the modified timestamp in the
@ -126,7 +126,7 @@ class TTFont(object):
access only. If it is set to False, many data structures are loaded access only. If it is set to False, many data structures are loaded
immediately. The default is lazy=None which is somewhere in between. immediately. The default is lazy=None which is somewhere in between.
""" """
from fontTools.ttLib import sfnt from fontTools.ttLib import sfnt
self.verbose = verbose self.verbose = verbose
self.quiet = quiet self.quiet = quiet
@ -169,19 +169,19 @@ class TTFont(object):
self.sfntVersion = self.reader.sfntVersion self.sfntVersion = self.reader.sfntVersion
self.flavor = self.reader.flavor self.flavor = self.reader.flavor
self.flavorData = self.reader.flavorData self.flavorData = self.reader.flavorData
def close(self): def close(self):
"""If we still have a reader object, close it.""" """If we still have a reader object, close it."""
if self.reader is not None: if self.reader is not None:
self.reader.close() self.reader.close()
def save(self, file, makeSuitcase=False, reorderTables=True): def save(self, file, makeSuitcase=False, reorderTables=True):
"""Save the font to disk. Similarly to the constructor, """Save the font to disk. Similarly to the constructor,
the 'file' argument can be either a pathname or a writable the 'file' argument can be either a pathname or a writable
file object. file object.
On the Mac, if makeSuitcase is true, a suitcase (resource fork) On the Mac, if makeSuitcase is true, a suitcase (resource fork)
file will we made instead of a flat .ttf file. file will we made instead of a flat .ttf file.
""" """
from fontTools.ttLib import sfnt from fontTools.ttLib import sfnt
if not hasattr(file, "write"): if not hasattr(file, "write"):
@ -197,7 +197,7 @@ class TTFont(object):
else: else:
# assume "file" is a writable file object # assume "file" is a writable file object
closeStream = 0 closeStream = 0
tags = list(self.keys()) tags = list(self.keys())
if "GlyphOrder" in tags: if "GlyphOrder" in tags:
tags.remove("GlyphOrder") tags.remove("GlyphOrder")
@ -208,11 +208,11 @@ class TTFont(object):
else: else:
tmp = file tmp = file
writer = sfnt.SFNTWriter(tmp, numTables, self.sfntVersion, self.flavor, self.flavorData) writer = sfnt.SFNTWriter(tmp, numTables, self.sfntVersion, self.flavor, self.flavorData)
done = [] done = []
for tag in tags: for tag in tags:
self._writeTable(tag, writer, done) self._writeTable(tag, writer, done)
writer.close() writer.close()
if reorderTables: if reorderTables:
@ -223,7 +223,7 @@ class TTFont(object):
if closeStream: if closeStream:
file.close() file.close()
def saveXML(self, fileOrPath, progress=None, quiet=False, def saveXML(self, fileOrPath, progress=None, quiet=False,
tables=None, skipTables=None, splitTables=False, disassembleInstructions=True, tables=None, skipTables=None, splitTables=False, disassembleInstructions=True,
bitmapGlyphDataFormat='raw'): bitmapGlyphDataFormat='raw'):
@ -236,7 +236,7 @@ class TTFont(object):
""" """
from fontTools import version from fontTools import version
from fontTools.misc import xmlWriter from fontTools.misc import xmlWriter
self.disassembleInstructions = disassembleInstructions self.disassembleInstructions = disassembleInstructions
self.bitmapGlyphDataFormat = bitmapGlyphDataFormat self.bitmapGlyphDataFormat = bitmapGlyphDataFormat
if not tables: if not tables:
@ -253,19 +253,19 @@ class TTFont(object):
idlefunc = getattr(progress, "idle", None) idlefunc = getattr(progress, "idle", None)
else: else:
idlefunc = None idlefunc = None
writer = xmlWriter.XMLWriter(fileOrPath, idlefunc=idlefunc) writer = xmlWriter.XMLWriter(fileOrPath, idlefunc=idlefunc)
writer.begintag("ttFont", sfntVersion=repr(self.sfntVersion)[1:-1], writer.begintag("ttFont", sfntVersion=repr(self.sfntVersion)[1:-1],
ttLibVersion=version) ttLibVersion=version)
writer.newline() writer.newline()
if not splitTables: if not splitTables:
writer.newline() writer.newline()
else: else:
# 'fileOrPath' must now be a path # 'fileOrPath' must now be a path
path, ext = os.path.splitext(fileOrPath) path, ext = os.path.splitext(fileOrPath)
fileNameTemplate = path + ".%s" + ext fileNameTemplate = path + ".%s" + ext
for i in range(numTables): for i in range(numTables):
if progress: if progress:
progress.set(i) progress.set(i)
@ -292,7 +292,7 @@ class TTFont(object):
writer.close() writer.close()
if self.verbose: if self.verbose:
debugmsg("Done dumping TTX") debugmsg("Done dumping TTX")
def _tableToXML(self, writer, tag, progress, quiet): def _tableToXML(self, writer, tag, progress, quiet):
if tag in self: if tag in self:
table = self[tag] table = self[tag]
@ -324,7 +324,7 @@ class TTFont(object):
writer.endtag(xmlTag) writer.endtag(xmlTag)
writer.newline() writer.newline()
writer.newline() writer.newline()
def importXML(self, file, progress=None, quiet=False): def importXML(self, file, progress=None, quiet=False):
"""Import a TTX file (an XML-based text format), so as to recreate """Import a TTX file (an XML-based text format), so as to recreate
a font object. a font object.
@ -340,12 +340,12 @@ class TTFont(object):
reader = xmlReader.XMLReader(file, self, progress, quiet) reader = xmlReader.XMLReader(file, self, progress, quiet)
reader.read() reader.read()
def isLoaded(self, tag): def isLoaded(self, tag):
"""Return true if the table identified by 'tag' has been """Return true if the table identified by 'tag' has been
decompiled and loaded into memory.""" decompiled and loaded into memory."""
return tag in self.tables return tag in self.tables
def has_key(self, tag): def has_key(self, tag):
if self.isLoaded(tag): if self.isLoaded(tag):
return True return True
@ -355,9 +355,9 @@ class TTFont(object):
return True return True
else: else:
return False return False
__contains__ = has_key __contains__ = has_key
def keys(self): def keys(self):
keys = list(self.tables.keys()) keys = list(self.tables.keys())
if self.reader: if self.reader:
@ -369,10 +369,10 @@ class TTFont(object):
keys.remove("GlyphOrder") keys.remove("GlyphOrder")
keys = sortedTagList(keys) keys = sortedTagList(keys)
return ["GlyphOrder"] + keys return ["GlyphOrder"] + keys
def __len__(self): def __len__(self):
return len(list(self.keys())) return len(list(self.keys()))
def __getitem__(self, tag): def __getitem__(self, tag):
tag = Tag(tag) tag = Tag(tag)
try: try:
@ -409,10 +409,10 @@ class TTFont(object):
return table return table
else: else:
raise KeyError("'%s' table not found" % tag) raise KeyError("'%s' table not found" % tag)
def __setitem__(self, tag, table): def __setitem__(self, tag, table):
self.tables[Tag(tag)] = table self.tables[Tag(tag)] = table
def __delitem__(self, tag): def __delitem__(self, tag):
if tag not in self: if tag not in self:
raise KeyError("'%s' table not found" % tag) raise KeyError("'%s' table not found" % tag)
@ -426,10 +426,10 @@ class TTFont(object):
return self[tag] return self[tag]
except KeyError: except KeyError:
return default return default
def setGlyphOrder(self, glyphOrder): def setGlyphOrder(self, glyphOrder):
self.glyphOrder = glyphOrder self.glyphOrder = glyphOrder
def getGlyphOrder(self): def getGlyphOrder(self):
try: try:
return self.glyphOrder return self.glyphOrder
@ -444,7 +444,7 @@ class TTFont(object):
if glyphOrder is None: if glyphOrder is None:
# #
# No names found in the 'post' table. # No names found in the 'post' table.
# Try to create glyph names from the unicode cmap (if available) # Try to create glyph names from the unicode cmap (if available)
# in combination with the Adobe Glyph List (AGL). # in combination with the Adobe Glyph List (AGL).
# #
self._getGlyphNamesFromCmap() self._getGlyphNamesFromCmap()
@ -453,7 +453,7 @@ class TTFont(object):
else: else:
self._getGlyphNamesFromCmap() self._getGlyphNamesFromCmap()
return self.glyphOrder return self.glyphOrder
def _getGlyphNamesFromCmap(self): def _getGlyphNamesFromCmap(self):
# #
# This is rather convoluted, but then again, it's an interesting problem: # This is rather convoluted, but then again, it's an interesting problem:
@ -527,19 +527,19 @@ class TTFont(object):
# restore partially loaded cmap, so it can continue loading # restore partially loaded cmap, so it can continue loading
# using the proper names. # using the proper names.
self.tables['cmap'] = cmapLoading self.tables['cmap'] = cmapLoading
def getGlyphNames(self): def getGlyphNames(self):
"""Get a list of glyph names, sorted alphabetically.""" """Get a list of glyph names, sorted alphabetically."""
glyphNames = sorted(self.getGlyphOrder()[:]) glyphNames = sorted(self.getGlyphOrder()[:])
return glyphNames return glyphNames
def getGlyphNames2(self): def getGlyphNames2(self):
"""Get a list of glyph names, sorted alphabetically, """Get a list of glyph names, sorted alphabetically,
but not case sensitive. but not case sensitive.
""" """
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, requireReal=False):
try: try:
return self.getGlyphOrder()[glyphID] return self.getGlyphOrder()[glyphID]
@ -549,7 +549,7 @@ class TTFont(object):
# the cmap table than there are glyphs. I don't think it's legal... # the cmap table than there are glyphs. I don't think it's legal...
return "glyph%.5d" % glyphID return "glyph%.5d" % glyphID
else: else:
# user intends virtual GID support # user intends virtual GID support
try: try:
glyphName = self.VIDDict[glyphID] glyphName = self.VIDDict[glyphID]
except KeyError: except KeyError:
@ -579,7 +579,7 @@ class TTFont(object):
except (NameError, ValueError): except (NameError, ValueError):
raise KeyError(glyphName) raise KeyError(glyphName)
else: else:
# user intends virtual GID support # user intends virtual GID support
try: try:
glyphID = self.reverseVIDDict[glyphName] glyphID = self.reverseVIDDict[glyphName]
except KeyError: except KeyError:
@ -612,9 +612,9 @@ class TTFont(object):
glyphOrder = self.getGlyphOrder() glyphOrder = self.getGlyphOrder()
for glyphID in range(len(glyphOrder)): for glyphID in range(len(glyphOrder)):
d[glyphOrder[glyphID]] = glyphID d[glyphOrder[glyphID]] = glyphID
def _writeTable(self, tag, writer, done): def _writeTable(self, tag, writer, done):
"""Internal helper function for self.save(). Keeps track of """Internal helper function for self.save(). Keeps track of
inter-table dependencies. inter-table dependencies.
""" """
if tag in done: if tag in done:
@ -631,7 +631,7 @@ class TTFont(object):
debugmsg("writing '%s' table to disk" % tag) debugmsg("writing '%s' table to disk" % tag)
writer[tag] = tabledata writer[tag] = tabledata
done.append(tag) done.append(tag)
def getTableData(self, tag): def getTableData(self, tag):
"""Returns raw table data, whether compiled or directly read from disk. """Returns raw table data, whether compiled or directly read from disk.
""" """
@ -646,13 +646,13 @@ class TTFont(object):
return self.reader[tag] return self.reader[tag]
else: else:
raise KeyError(tag) raise KeyError(tag)
def getGlyphSet(self, preferCFF=True): def getGlyphSet(self, preferCFF=True):
"""Return a generic GlyphSet, which is a dict-like object """Return a generic GlyphSet, which is a dict-like object
mapping glyph names to glyph objects. The returned glyph objects mapping glyph names to glyph objects. The returned glyph objects
have a .draw() method that supports the Pen protocol, and will have a .draw() method that supports the Pen protocol, and will
have an attribute named 'width'. have an attribute named 'width'.
If the font is CFF-based, the outlines will be taken from the 'CFF ' If the font is CFF-based, the outlines will be taken from the 'CFF '
table. Otherwise the outlines will be taken from the 'glyf' table. table. Otherwise the outlines will be taken from the 'glyf' table.
If the font contains both a 'CFF ' and a 'glyf' table, you can use If the font contains both a 'CFF ' and a 'glyf' table, you can use
@ -672,22 +672,22 @@ class TTFont(object):
class _TTGlyphSet(object): class _TTGlyphSet(object):
"""Generic dict-like GlyphSet class that pulls metrics from hmtx and """Generic dict-like GlyphSet class that pulls metrics from hmtx and
glyph shape from TrueType or CFF. glyph shape from TrueType or CFF.
""" """
def __init__(self, ttFont, glyphs, glyphType): def __init__(self, ttFont, glyphs, glyphType):
self._glyphs = glyphs self._glyphs = glyphs
self._hmtx = ttFont['hmtx'] self._hmtx = ttFont['hmtx']
self._glyphType = glyphType self._glyphType = glyphType
def keys(self): def keys(self):
return list(self._glyphs.keys()) return list(self._glyphs.keys())
def has_key(self, glyphName): def has_key(self, glyphName):
return glyphName in self._glyphs return glyphName in self._glyphs
__contains__ = has_key __contains__ = has_key
def __getitem__(self, glyphName): def __getitem__(self, glyphName):
@ -700,12 +700,12 @@ class _TTGlyphSet(object):
return default return default
class _TTGlyph(object): class _TTGlyph(object):
"""Wrapper for a TrueType glyph that supports the Pen protocol, meaning """Wrapper for a TrueType glyph that supports the Pen protocol, meaning
that it has a .draw() method that takes a pen object as its only that it has a .draw() method that takes a pen object as its only
argument. Additionally there is a 'width' attribute. argument. Additionally there is a 'width' attribute.
""" """
def __init__(self, glyphset, glyph, metrics): def __init__(self, glyphset, glyph, metrics):
self._glyphset = glyphset self._glyphset = glyphset
self._glyph = glyph self._glyph = glyph
@ -733,14 +733,14 @@ class _TTGlyphGlyf(_TTGlyph):
class GlyphOrder(object): class GlyphOrder(object):
"""A pseudo table. The glyph order isn't in the font as a separate """A pseudo table. The glyph order isn't in the font as a separate
table, but it's nice to present it as such in the TTX format. table, but it's nice to present it as such in the TTX format.
""" """
def __init__(self, tag=None): def __init__(self, tag=None):
pass pass
def toXML(self, writer, ttFont): def toXML(self, writer, ttFont):
glyphOrder = ttFont.getGlyphOrder() glyphOrder = ttFont.getGlyphOrder()
writer.comment("The 'id' attribute is only for humans; " writer.comment("The 'id' attribute is only for humans; "
@ -750,7 +750,7 @@ class GlyphOrder(object):
glyphName = glyphOrder[i] glyphName = glyphOrder[i]
writer.simpletag("GlyphID", id=i, name=glyphName) writer.simpletag("GlyphID", id=i, name=glyphName)
writer.newline() writer.newline()
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 = []
@ -760,7 +760,7 @@ class GlyphOrder(object):
def getTableModule(tag): def getTableModule(tag):
"""Fetch the packer/unpacker module for a table. """Fetch the packer/unpacker module for a table.
Return None when no module is found. Return None when no module is found.
""" """
from . import tables from . import tables
@ -781,7 +781,7 @@ def getTableModule(tag):
def getTableClass(tag): def getTableClass(tag):
"""Fetch the packer/unpacker class for a table. """Fetch the packer/unpacker class for a table.
Return None when no class is found. Return None when no class is found.
""" """
module = getTableModule(tag) module = getTableModule(tag)
@ -819,14 +819,14 @@ def _escapechar(c):
def tagToIdentifier(tag): def tagToIdentifier(tag):
"""Convert a table tag to a valid (but UGLY) python identifier, """Convert a table tag to a valid (but UGLY) python identifier,
as well as a filename that's guaranteed to be unique even on a as well as a filename that's guaranteed to be unique even on a
caseless file system. Each character is mapped to two characters. caseless file system. Each character is mapped to two characters.
Lowercase letters get an underscore before the letter, uppercase Lowercase letters get an underscore before the letter, uppercase
letters get an underscore after the letter. Trailing spaces are letters get an underscore after the letter. Trailing spaces are
trimmed. Illegal characters are escaped as two hex bytes. If the trimmed. Illegal characters are escaped as two hex bytes. If the
result starts with a number (as the result of a hex escape), an result starts with a number (as the result of a hex escape), an
extra underscore is prepended. Examples: extra underscore is prepended. Examples:
'glyf' -> '_g_l_y_f' 'glyf' -> '_g_l_y_f'
'cvt ' -> '_c_v_t' 'cvt ' -> '_c_v_t'
'OS/2' -> 'O_S_2f_2' 'OS/2' -> 'O_S_2f_2'

View File

@ -35,7 +35,7 @@ def getSFNTResIndices(path):
def openTTFonts(path): def openTTFonts(path):
"""Given a pathname, return a list of TTFont objects. In the case """Given a pathname, return a list of TTFont objects. In the case
of a flat TTF/OTF file, the list will contain just one font object; of a flat TTF/OTF file, the list will contain just one font object;
but in the case of a Mac font suitcase it will contain as many but in the case of a Mac font suitcase it will contain as many
font objects as there are sfnt resources in the file. font objects as there are sfnt resources in the file.
@ -54,9 +54,9 @@ def openTTFonts(path):
class SFNTResourceReader(object): class SFNTResourceReader(object):
"""Simple (Mac-only) read-only file wrapper for 'sfnt' resources.""" """Simple (Mac-only) read-only file wrapper for 'sfnt' resources."""
def __init__(self, path, res_name_or_index): def __init__(self, path, res_name_or_index):
resref = MyOpenResFile(path) resref = MyOpenResFile(path)
Res.UseResFile(resref) Res.UseResFile(resref)
@ -67,16 +67,16 @@ class SFNTResourceReader(object):
self.file = StringIO(res.data) self.file = StringIO(res.data)
Res.CloseResFile(resref) Res.CloseResFile(resref)
self.name = path self.name = path
def __getattr__(self, attr): def __getattr__(self, attr):
# cheap inheritance # cheap inheritance
return getattr(self.file, attr) return getattr(self.file, attr)
class SFNTResourceWriter(object): class SFNTResourceWriter(object):
"""Simple (Mac-only) file wrapper for 'sfnt' resources.""" """Simple (Mac-only) file wrapper for 'sfnt' resources."""
def __init__(self, path, ttFont, res_id=None): def __init__(self, path, ttFont, res_id=None):
self.file = StringIO() self.file = StringIO()
self.name = path self.name = path
@ -97,7 +97,7 @@ class SFNTResourceWriter(object):
if self.familyname[i] != self.psname[i]: if self.familyname[i] != self.psname[i]:
break break
self.familyname = self.psname[:i] self.familyname = self.psname[:i]
self.ttFont = ttFont self.ttFont = ttFont
self.res_id = res_id self.res_id = res_id
if os.path.exists(self.name): if os.path.exists(self.name):
@ -105,7 +105,7 @@ class SFNTResourceWriter(object):
# XXX datafork support # XXX datafork support
Res.FSpCreateResFile(self.name, 'DMOV', 'FFIL', 0) Res.FSpCreateResFile(self.name, 'DMOV', 'FFIL', 0)
self.resref = Res.FSOpenResFile(self.name, 3) # exclusive read/write permission self.resref = Res.FSOpenResFile(self.name, 3) # exclusive read/write permission
def close(self): def close(self):
if self.closed: if self.closed:
return return
@ -121,20 +121,20 @@ class SFNTResourceWriter(object):
self.res_id = Res.Unique1ID('sfnt') self.res_id = Res.Unique1ID('sfnt')
res.AddResource('sfnt', self.res_id, self.fullname) res.AddResource('sfnt', self.res_id, self.fullname)
res.ChangedResource() res.ChangedResource()
self.createFond() self.createFond()
del self.ttFont del self.ttFont
Res.CloseResFile(self.resref) Res.CloseResFile(self.resref)
self.file.close() self.file.close()
self.closed = 1 self.closed = 1
def createFond(self): def createFond(self):
fond_res = Res.Resource("") fond_res = Res.Resource("")
fond_res.AddResource('FOND', self.res_id, self.fullname) fond_res.AddResource('FOND', self.res_id, self.fullname)
from fontTools import fondLib from fontTools import fondLib
fond = fondLib.FontFamily(fond_res, "w") fond = fondLib.FontFamily(fond_res, "w")
fond.ffFirstChar = 0 fond.ffFirstChar = 0
fond.ffLastChar = 255 fond.ffLastChar = 255
fond.fondClass = 0 fond.fondClass = 0
@ -155,16 +155,16 @@ class SFNTResourceWriter(object):
fond.changed = 1 fond.changed = 1
fond.glyphTableOffset = 0 fond.glyphTableOffset = 0
fond.styleMappingReserved = 0 fond.styleMappingReserved = 0
# calc: # calc:
scale = 4096 / self.ttFont['head'].unitsPerEm scale = 4096 / self.ttFont['head'].unitsPerEm
fond.ffAscent = scale * self.ttFont['hhea'].ascent fond.ffAscent = scale * self.ttFont['hhea'].ascent
fond.ffDescent = scale * self.ttFont['hhea'].descent fond.ffDescent = scale * self.ttFont['hhea'].descent
fond.ffWidMax = scale * self.ttFont['hhea'].advanceWidthMax fond.ffWidMax = scale * self.ttFont['hhea'].advanceWidthMax
fond.ffFamilyName = self.familyname fond.ffFamilyName = self.familyname
fond.psNames = {0: self.psname} fond.psNames = {0: self.psname}
fond.widthTables = {} fond.widthTables = {}
fond.kernTables = {} fond.kernTables = {}
cmap = self.ttFont['cmap'].getcmap(1, 0) cmap = self.ttFont['cmap'].getcmap(1, 0)
@ -189,11 +189,11 @@ class SFNTResourceWriter(object):
fondwidths[names[name]] = scale * width fondwidths[names[name]] = scale * width
fond.widthTables = {0: fondwidths} fond.widthTables = {0: fondwidths}
fond.save() fond.save()
def __del__(self): def __del__(self):
if not self.closed: if not self.closed:
self.close() self.close()
def __getattr__(self, attr): def __getattr__(self, attr):
# cheap inheritance # cheap inheritance
return getattr(self.file, attr) return getattr(self.file, attr)

View File

@ -4,10 +4,10 @@ Defines two public classes:
SFNTReader SFNTReader
SFNTWriter SFNTWriter
(Normally you don't have to use these classes explicitly; they are (Normally you don't have to use these classes explicitly; they are
used automatically by ttLib.TTFont.) used automatically by ttLib.TTFont.)
The reading and writing of sfnt files is separated in two distinct The reading and writing of sfnt files is separated in two distinct
classes, since whenever to number of tables changes or whenever classes, since whenever to number of tables changes or whenever
a table's length chages you need to rewrite the whole file anyway. a table's length chages you need to rewrite the whole file anyway.
""" """
@ -20,7 +20,7 @@ import struct
class SFNTReader(object): class SFNTReader(object):
def __init__(self, file, checkChecksums=1, fontNumber=-1): def __init__(self, file, checkChecksums=1, fontNumber=-1):
self.file = file self.file = file
self.checkChecksums = checkChecksums self.checkChecksums = checkChecksums
@ -66,10 +66,10 @@ class SFNTReader(object):
return tag in self.tables return tag in self.tables
__contains__ = has_key __contains__ = has_key
def keys(self): def keys(self):
return self.tables.keys() return self.tables.keys()
def __getitem__(self, tag): def __getitem__(self, tag):
"""Fetch the raw table data.""" """Fetch the raw table data."""
entry = self.tables[Tag(tag)] entry = self.tables[Tag(tag)]
@ -87,16 +87,16 @@ class SFNTReader(object):
# Be friendly, and just print a warning. # Be friendly, and just print a warning.
print("bad checksum for '%s' table" % tag) print("bad checksum for '%s' table" % tag)
return data return data
def __delitem__(self, tag): def __delitem__(self, tag):
del self.tables[Tag(tag)] del self.tables[Tag(tag)]
def close(self): def close(self):
self.file.close() self.file.close()
class SFNTWriter(object): class SFNTWriter(object):
def __init__(self, file, numTables, sfntVersion="\000\001\000\000", def __init__(self, file, numTables, sfntVersion="\000\001\000\000",
flavor=None, flavorData=None): flavor=None, flavorData=None):
self.file = file self.file = file
@ -113,7 +113,7 @@ class SFNTWriter(object):
self.signature = "wOFF" self.signature = "wOFF"
# to calculate WOFF checksum adjustment, we also need the original SFNT offsets # to calculate WOFF checksum adjustment, we also need the original SFNT offsets
self.origNextTableOffset = sfntDirectorySize + numTables * sfntDirectoryEntrySize self.origNextTableOffset = sfntDirectorySize + numTables * sfntDirectoryEntrySize
else: else:
assert not self.flavor, "Unknown flavor '%s'" % self.flavor assert not self.flavor, "Unknown flavor '%s'" % self.flavor
self.directoryFormat = sfntDirectoryFormat self.directoryFormat = sfntDirectoryFormat
@ -128,7 +128,7 @@ class SFNTWriter(object):
# make sure we're actually where we want to be. (old cStringIO bug) # make sure we're actually where we want to be. (old cStringIO bug)
self.file.write(b'\0' * (self.nextTableOffset - self.file.tell())) self.file.write(b'\0' * (self.nextTableOffset - self.file.tell()))
self.tables = {} self.tables = {}
def __setitem__(self, tag, data): def __setitem__(self, tag, data):
"""Write raw table data to disk.""" """Write raw table data to disk."""
if tag in self.tables: if tag in self.tables:
@ -157,9 +157,9 @@ class SFNTWriter(object):
# in the font. # in the font.
self.file.write(b'\0' * (self.nextTableOffset - self.file.tell())) self.file.write(b'\0' * (self.nextTableOffset - self.file.tell()))
assert self.nextTableOffset == self.file.tell() assert self.nextTableOffset == self.file.tell()
self.tables[tag] = entry self.tables[tag] = entry
def close(self): def close(self):
"""All tables must have been written to disk. Now write the """All tables must have been written to disk. Now write the
directory. directory.
@ -214,9 +214,9 @@ class SFNTWriter(object):
else: else:
assert not self.flavor, "Unknown flavor '%s'" % self.flavor assert not self.flavor, "Unknown flavor '%s'" % self.flavor
pass pass
directory = sstruct.pack(self.directoryFormat, self) directory = sstruct.pack(self.directoryFormat, self)
self.file.seek(self.directorySize) self.file.seek(self.directorySize)
seenHead = 0 seenHead = 0
for tag, entry in tables: for tag, entry in tables:
@ -332,19 +332,19 @@ woffDirectoryEntrySize = sstruct.calcsize(woffDirectoryEntryFormat)
class DirectoryEntry(object): class DirectoryEntry(object):
def __init__(self): def __init__(self):
self.uncompressed = False # if True, always embed entry raw self.uncompressed = False # if True, always embed entry raw
def fromFile(self, file): def fromFile(self, file):
sstruct.unpack(self.format, file.read(self.formatSize), self) sstruct.unpack(self.format, file.read(self.formatSize), self)
def fromString(self, str): def fromString(self, str):
sstruct.unpack(self.format, str, self) sstruct.unpack(self.format, str, self)
def toString(self): def toString(self):
return sstruct.pack(self.format, self) return sstruct.pack(self.format, self)
def __repr__(self): def __repr__(self):
if hasattr(self, "tag"): if hasattr(self, "tag"):
return "<%s '%s' at %x>" % (self.__class__.__name__, self.tag, id(self)) return "<%s '%s' at %x>" % (self.__class__.__name__, self.tag, id(self))
@ -439,9 +439,9 @@ def calcChecksum(data):
Optionally takes a 'start' argument, which allows you to Optionally takes a 'start' argument, which allows you to
calculate a checksum in chunks by feeding it a previous calculate a checksum in chunks by feeding it a previous
result. result.
If the data length is not a multiple of four, it assumes If the data length is not a multiple of four, it assumes
it is to be padded with null byte. it is to be padded with null byte.
>>> print(calcChecksum(b"abcd")) >>> print(calcChecksum(b"abcd"))
1633837924 1633837924

View File

@ -13,262 +13,262 @@ from fontTools.misc.py23 import *
# #
standardGlyphOrder = [ standardGlyphOrder = [
".notdef", # 0 ".notdef", # 0
".null", # 1 ".null", # 1
"nonmarkingreturn", # 2 "nonmarkingreturn", # 2
"space", # 3 "space", # 3
"exclam", # 4 "exclam", # 4
"quotedbl", # 5 "quotedbl", # 5
"numbersign", # 6 "numbersign", # 6
"dollar", # 7 "dollar", # 7
"percent", # 8 "percent", # 8
"ampersand", # 9 "ampersand", # 9
"quotesingle", # 10 "quotesingle", # 10
"parenleft", # 11 "parenleft", # 11
"parenright", # 12 "parenright", # 12
"asterisk", # 13 "asterisk", # 13
"plus", # 14 "plus", # 14
"comma", # 15 "comma", # 15
"hyphen", # 16 "hyphen", # 16
"period", # 17 "period", # 17
"slash", # 18 "slash", # 18
"zero", # 19 "zero", # 19
"one", # 20 "one", # 20
"two", # 21 "two", # 21
"three", # 22 "three", # 22
"four", # 23 "four", # 23
"five", # 24 "five", # 24
"six", # 25 "six", # 25
"seven", # 26 "seven", # 26
"eight", # 27 "eight", # 27
"nine", # 28 "nine", # 28
"colon", # 29 "colon", # 29
"semicolon", # 30 "semicolon", # 30
"less", # 31 "less", # 31
"equal", # 32 "equal", # 32
"greater", # 33 "greater", # 33
"question", # 34 "question", # 34
"at", # 35 "at", # 35
"A", # 36 "A", # 36
"B", # 37 "B", # 37
"C", # 38 "C", # 38
"D", # 39 "D", # 39
"E", # 40 "E", # 40
"F", # 41 "F", # 41
"G", # 42 "G", # 42
"H", # 43 "H", # 43
"I", # 44 "I", # 44
"J", # 45 "J", # 45
"K", # 46 "K", # 46
"L", # 47 "L", # 47
"M", # 48 "M", # 48
"N", # 49 "N", # 49
"O", # 50 "O", # 50
"P", # 51 "P", # 51
"Q", # 52 "Q", # 52
"R", # 53 "R", # 53
"S", # 54 "S", # 54
"T", # 55 "T", # 55
"U", # 56 "U", # 56
"V", # 57 "V", # 57
"W", # 58 "W", # 58
"X", # 59 "X", # 59
"Y", # 60 "Y", # 60
"Z", # 61 "Z", # 61
"bracketleft", # 62 "bracketleft", # 62
"backslash", # 63 "backslash", # 63
"bracketright", # 64 "bracketright", # 64
"asciicircum", # 65 "asciicircum", # 65
"underscore", # 66 "underscore", # 66
"grave", # 67 "grave", # 67
"a", # 68 "a", # 68
"b", # 69 "b", # 69
"c", # 70 "c", # 70
"d", # 71 "d", # 71
"e", # 72 "e", # 72
"f", # 73 "f", # 73
"g", # 74 "g", # 74
"h", # 75 "h", # 75
"i", # 76 "i", # 76
"j", # 77 "j", # 77
"k", # 78 "k", # 78
"l", # 79 "l", # 79
"m", # 80 "m", # 80
"n", # 81 "n", # 81
"o", # 82 "o", # 82
"p", # 83 "p", # 83
"q", # 84 "q", # 84
"r", # 85 "r", # 85
"s", # 86 "s", # 86
"t", # 87 "t", # 87
"u", # 88 "u", # 88
"v", # 89 "v", # 89
"w", # 90 "w", # 90
"x", # 91 "x", # 91
"y", # 92 "y", # 92
"z", # 93 "z", # 93
"braceleft", # 94 "braceleft", # 94
"bar", # 95 "bar", # 95
"braceright", # 96 "braceright", # 96
"asciitilde", # 97 "asciitilde", # 97
"Adieresis", # 98 "Adieresis", # 98
"Aring", # 99 "Aring", # 99
"Ccedilla", # 100 "Ccedilla", # 100
"Eacute", # 101 "Eacute", # 101
"Ntilde", # 102 "Ntilde", # 102
"Odieresis", # 103 "Odieresis", # 103
"Udieresis", # 104 "Udieresis", # 104
"aacute", # 105 "aacute", # 105
"agrave", # 106 "agrave", # 106
"acircumflex", # 107 "acircumflex", # 107
"adieresis", # 108 "adieresis", # 108
"atilde", # 109 "atilde", # 109
"aring", # 110 "aring", # 110
"ccedilla", # 111 "ccedilla", # 111
"eacute", # 112 "eacute", # 112
"egrave", # 113 "egrave", # 113
"ecircumflex", # 114 "ecircumflex", # 114
"edieresis", # 115 "edieresis", # 115
"iacute", # 116 "iacute", # 116
"igrave", # 117 "igrave", # 117
"icircumflex", # 118 "icircumflex", # 118
"idieresis", # 119 "idieresis", # 119
"ntilde", # 120 "ntilde", # 120
"oacute", # 121 "oacute", # 121
"ograve", # 122 "ograve", # 122
"ocircumflex", # 123 "ocircumflex", # 123
"odieresis", # 124 "odieresis", # 124
"otilde", # 125 "otilde", # 125
"uacute", # 126 "uacute", # 126
"ugrave", # 127 "ugrave", # 127
"ucircumflex", # 128 "ucircumflex", # 128
"udieresis", # 129 "udieresis", # 129
"dagger", # 130 "dagger", # 130
"degree", # 131 "degree", # 131
"cent", # 132 "cent", # 132
"sterling", # 133 "sterling", # 133
"section", # 134 "section", # 134
"bullet", # 135 "bullet", # 135
"paragraph", # 136 "paragraph", # 136
"germandbls", # 137 "germandbls", # 137
"registered", # 138 "registered", # 138
"copyright", # 139 "copyright", # 139
"trademark", # 140 "trademark", # 140
"acute", # 141 "acute", # 141
"dieresis", # 142 "dieresis", # 142
"notequal", # 143 "notequal", # 143
"AE", # 144 "AE", # 144
"Oslash", # 145 "Oslash", # 145
"infinity", # 146 "infinity", # 146
"plusminus", # 147 "plusminus", # 147
"lessequal", # 148 "lessequal", # 148
"greaterequal", # 149 "greaterequal", # 149
"yen", # 150 "yen", # 150
"mu", # 151 "mu", # 151
"partialdiff", # 152 "partialdiff", # 152
"summation", # 153 "summation", # 153
"product", # 154 "product", # 154
"pi", # 155 "pi", # 155
"integral", # 156 "integral", # 156
"ordfeminine", # 157 "ordfeminine", # 157
"ordmasculine", # 158 "ordmasculine", # 158
"Omega", # 159 "Omega", # 159
"ae", # 160 "ae", # 160
"oslash", # 161 "oslash", # 161
"questiondown", # 162 "questiondown", # 162
"exclamdown", # 163 "exclamdown", # 163
"logicalnot", # 164 "logicalnot", # 164
"radical", # 165 "radical", # 165
"florin", # 166 "florin", # 166
"approxequal", # 167 "approxequal", # 167
"Delta", # 168 "Delta", # 168
"guillemotleft", # 169 "guillemotleft", # 169
"guillemotright", # 170 "guillemotright", # 170
"ellipsis", # 171 "ellipsis", # 171
"nonbreakingspace", # 172 "nonbreakingspace", # 172
"Agrave", # 173 "Agrave", # 173
"Atilde", # 174 "Atilde", # 174
"Otilde", # 175 "Otilde", # 175
"OE", # 176 "OE", # 176
"oe", # 177 "oe", # 177
"endash", # 178 "endash", # 178
"emdash", # 179 "emdash", # 179
"quotedblleft", # 180 "quotedblleft", # 180
"quotedblright", # 181 "quotedblright", # 181
"quoteleft", # 182 "quoteleft", # 182
"quoteright", # 183 "quoteright", # 183
"divide", # 184 "divide", # 184
"lozenge", # 185 "lozenge", # 185
"ydieresis", # 186 "ydieresis", # 186
"Ydieresis", # 187 "Ydieresis", # 187
"fraction", # 188 "fraction", # 188
"currency", # 189 "currency", # 189
"guilsinglleft", # 190 "guilsinglleft", # 190
"guilsinglright", # 191 "guilsinglright", # 191
"fi", # 192 "fi", # 192
"fl", # 193 "fl", # 193
"daggerdbl", # 194 "daggerdbl", # 194
"periodcentered", # 195 "periodcentered", # 195
"quotesinglbase", # 196 "quotesinglbase", # 196
"quotedblbase", # 197 "quotedblbase", # 197
"perthousand", # 198 "perthousand", # 198
"Acircumflex", # 199 "Acircumflex", # 199
"Ecircumflex", # 200 "Ecircumflex", # 200
"Aacute", # 201 "Aacute", # 201
"Edieresis", # 202 "Edieresis", # 202
"Egrave", # 203 "Egrave", # 203
"Iacute", # 204 "Iacute", # 204
"Icircumflex", # 205 "Icircumflex", # 205
"Idieresis", # 206 "Idieresis", # 206
"Igrave", # 207 "Igrave", # 207
"Oacute", # 208 "Oacute", # 208
"Ocircumflex", # 209 "Ocircumflex", # 209
"apple", # 210 "apple", # 210
"Ograve", # 211 "Ograve", # 211
"Uacute", # 212 "Uacute", # 212
"Ucircumflex", # 213 "Ucircumflex", # 213
"Ugrave", # 214 "Ugrave", # 214
"dotlessi", # 215 "dotlessi", # 215
"circumflex", # 216 "circumflex", # 216
"tilde", # 217 "tilde", # 217
"macron", # 218 "macron", # 218
"breve", # 219 "breve", # 219
"dotaccent", # 220 "dotaccent", # 220
"ring", # 221 "ring", # 221
"cedilla", # 222 "cedilla", # 222
"hungarumlaut", # 223 "hungarumlaut", # 223
"ogonek", # 224 "ogonek", # 224
"caron", # 225 "caron", # 225
"Lslash", # 226 "Lslash", # 226
"lslash", # 227 "lslash", # 227
"Scaron", # 228 "Scaron", # 228
"scaron", # 229 "scaron", # 229
"Zcaron", # 230 "Zcaron", # 230
"zcaron", # 231 "zcaron", # 231
"brokenbar", # 232 "brokenbar", # 232
"Eth", # 233 "Eth", # 233
"eth", # 234 "eth", # 234
"Yacute", # 235 "Yacute", # 235
"yacute", # 236 "yacute", # 236
"Thorn", # 237 "Thorn", # 237
"thorn", # 238 "thorn", # 238
"minus", # 239 "minus", # 239
"multiply", # 240 "multiply", # 240
"onesuperior", # 241 "onesuperior", # 241
"twosuperior", # 242 "twosuperior", # 242
"threesuperior", # 243 "threesuperior", # 243
"onehalf", # 244 "onehalf", # 244
"onequarter", # 245 "onequarter", # 245
"threequarters", # 246 "threequarters", # 246
"franc", # 247 "franc", # 247
"Gbreve", # 248 "Gbreve", # 248
"gbreve", # 249 "gbreve", # 249
"Idotaccent", # 250 "Idotaccent", # 250
"Scedilla", # 251 "Scedilla", # 251
"scedilla", # 252 "scedilla", # 252
"Cacute", # 253 "Cacute", # 253
"cacute", # 254 "cacute", # 254
"Ccaron", # 255 "Ccaron", # 255
"ccaron", # 256 "ccaron", # 256
"dcroat" # 257 "dcroat" # 257
] ]

View File

@ -53,6 +53,6 @@ class BitmapGlyphMetrics(object):
class BigGlyphMetrics(BitmapGlyphMetrics): class BigGlyphMetrics(BitmapGlyphMetrics):
binaryFormat = bigGlyphMetricsFormat binaryFormat = bigGlyphMetricsFormat
class SmallGlyphMetrics(BitmapGlyphMetrics): class SmallGlyphMetrics(BitmapGlyphMetrics):
binaryFormat = smallGlyphMetricsFormat binaryFormat = smallGlyphMetricsFormat

View File

@ -5,42 +5,42 @@ from . import DefaultTable
class table_C_F_F_(DefaultTable.DefaultTable): class table_C_F_F_(DefaultTable.DefaultTable):
def __init__(self, tag): def __init__(self, tag):
DefaultTable.DefaultTable.__init__(self, tag) DefaultTable.DefaultTable.__init__(self, tag)
self.cff = cffLib.CFFFontSet() self.cff = cffLib.CFFFontSet()
self._gaveGlyphOrder = False self._gaveGlyphOrder = False
def decompile(self, data, otFont): def decompile(self, data, otFont):
self.cff.decompile(StringIO(data), otFont) self.cff.decompile(StringIO(data), otFont)
assert len(self.cff) == 1, "can't deal with multi-font CFF tables." assert len(self.cff) == 1, "can't deal with multi-font CFF tables."
def compile(self, otFont): def compile(self, otFont):
f = StringIO() f = StringIO()
self.cff.compile(f, otFont) self.cff.compile(f, otFont)
return f.getvalue() return f.getvalue()
def haveGlyphNames(self): def haveGlyphNames(self):
if hasattr(self.cff[self.cff.fontNames[0]], "ROS"): if hasattr(self.cff[self.cff.fontNames[0]], "ROS"):
return False # CID-keyed font return False # CID-keyed font
else: else:
return True return True
def getGlyphOrder(self): def getGlyphOrder(self):
if self._gaveGlyphOrder: if self._gaveGlyphOrder:
from fontTools import ttLib from fontTools import ttLib
raise ttLib.TTLibError("illegal use of getGlyphOrder()") raise ttLib.TTLibError("illegal use of getGlyphOrder()")
self._gaveGlyphOrder = True self._gaveGlyphOrder = True
return self.cff[self.cff.fontNames[0]].getGlyphOrder() return self.cff[self.cff.fontNames[0]].getGlyphOrder()
def setGlyphOrder(self, glyphOrder): def setGlyphOrder(self, glyphOrder):
pass pass
# XXX # XXX
#self.cff[self.cff.fontNames[0]].setGlyphOrder(glyphOrder) #self.cff[self.cff.fontNames[0]].setGlyphOrder(glyphOrder)
def toXML(self, writer, otFont, progress=None): def toXML(self, writer, otFont, progress=None):
self.cff.toXML(writer, progress) self.cff.toXML(writer, progress)
def fromXML(self, name, attrs, content, otFont): def fromXML(self, name, attrs, content, otFont):
if not hasattr(self, "cff"): if not hasattr(self, "cff"):
self.cff = cffLib.CFFFontSet() self.cff = cffLib.CFFFontSet()

View File

@ -123,7 +123,7 @@ class table_C_O_L_R_(DefaultTable.DefaultTable):
if glyphSelector not in self.ColorLayers: if glyphSelector not in self.ColorLayers:
return None return None
return self.ColorLayers[glyphSelector] return self.ColorLayers[glyphSelector]
def __setitem__(self, glyphSelector, value): def __setitem__(self, glyphSelector, value):

View File

@ -40,7 +40,7 @@ DSIG_SignatureBlockFormat = """
# #
class table_D_S_I_G_(DefaultTable.DefaultTable): class table_D_S_I_G_(DefaultTable.DefaultTable):
def decompile(self, data, ttFont): def decompile(self, data, ttFont):
dummy, newData = sstruct.unpack2(DSIG_HeaderFormat, data, self) dummy, newData = sstruct.unpack2(DSIG_HeaderFormat, data, self)
assert self.ulVersion == 1, "DSIG ulVersion must be 1" assert self.ulVersion == 1, "DSIG ulVersion must be 1"
@ -55,7 +55,7 @@ class table_D_S_I_G_(DefaultTable.DefaultTable):
assert sigrec.usReserved1 == 0, "DSIG signature record #%d usReserverd1 must be 0" % n assert sigrec.usReserved1 == 0, "DSIG signature record #%d usReserverd1 must be 0" % n
assert sigrec.usReserved2 == 0, "DSIG signature record #%d usReserverd2 must be 0" % n assert sigrec.usReserved2 == 0, "DSIG signature record #%d usReserverd2 must be 0" % n
sigrec.pkcs7 = newData[:sigrec.cbSignature] sigrec.pkcs7 = newData[:sigrec.cbSignature]
def compile(self, ttFont): def compile(self, ttFont):
packed = sstruct.pack(DSIG_HeaderFormat, self) packed = sstruct.pack(DSIG_HeaderFormat, self)
headers = [packed] headers = [packed]
@ -76,7 +76,7 @@ class table_D_S_I_G_(DefaultTable.DefaultTable):
# Pad to even bytes # Pad to even bytes
data.append(b'\0') data.append(b'\0')
return bytesjoin(headers+data) return bytesjoin(headers+data)
def toXML(self, xmlWriter, ttFont): def toXML(self, xmlWriter, ttFont):
xmlWriter.comment("note that the Digital Signature will be invalid after recompilation!") xmlWriter.comment("note that the Digital Signature will be invalid after recompilation!")
xmlWriter.newline() xmlWriter.newline()
@ -85,7 +85,7 @@ class table_D_S_I_G_(DefaultTable.DefaultTable):
xmlWriter.newline() xmlWriter.newline()
sigrec.toXML(xmlWriter, ttFont) sigrec.toXML(xmlWriter, ttFont)
xmlWriter.newline() xmlWriter.newline()
def fromXML(self, name, attrs, content, ttFont): def fromXML(self, name, attrs, content, ttFont):
if name == "tableHeader": if name == "tableHeader":
self.signatureRecords = [] self.signatureRecords = []
@ -115,7 +115,7 @@ def b64encode(b):
class SignatureRecord(object): class SignatureRecord(object):
def __repr__(self): def __repr__(self):
return "<%s: %s>" % (self.__class__.__name__, self.__dict__) return "<%s: %s>" % (self.__class__.__name__, self.__dict__)
def toXML(self, writer, ttFont): def toXML(self, writer, ttFont):
writer.begintag(self.__class__.__name__, format=self.ulFormat) writer.begintag(self.__class__.__name__, format=self.ulFormat)
writer.newline() writer.newline()
@ -123,7 +123,7 @@ class SignatureRecord(object):
writer.write_noindent(b64encode(self.pkcs7)) writer.write_noindent(b64encode(self.pkcs7))
writer.write_noindent("-----END PKCS7-----\n") writer.write_noindent("-----END PKCS7-----\n")
writer.endtag(self.__class__.__name__) writer.endtag(self.__class__.__name__)
def fromXML(self, name, attrs, content, ttFont): def fromXML(self, name, attrs, content, ttFont):
self.ulFormat = safeEval(attrs["format"]) self.ulFormat = safeEval(attrs["format"])
self.usReserved1 = safeEval(attrs.get("reserved1", "0")) self.usReserved1 = safeEval(attrs.get("reserved1", "0"))

View File

@ -3,20 +3,20 @@ from fontTools.misc.py23 import *
from fontTools.ttLib import getClassTag from fontTools.ttLib import getClassTag
class DefaultTable(object): class DefaultTable(object):
dependencies = [] dependencies = []
def __init__(self, tag=None): def __init__(self, tag=None):
if tag is None: if tag is None:
tag = getClassTag(self.__class__) tag = getClassTag(self.__class__)
self.tableTag = Tag(tag) self.tableTag = Tag(tag)
def decompile(self, data, ttFont): def decompile(self, data, ttFont):
self.data = data self.data = data
def compile(self, ttFont): def compile(self, ttFont):
return self.data return self.data
def toXML(self, writer, ttFont, progress=None): def toXML(self, writer, ttFont, progress=None):
if hasattr(self, "ERROR"): if hasattr(self, "ERROR"):
writer.comment("An error occurred during the decompilation of this table") writer.comment("An error occurred during the decompilation of this table")
@ -28,17 +28,17 @@ class DefaultTable(object):
writer.dumphex(self.compile(ttFont)) writer.dumphex(self.compile(ttFont))
writer.endtag("hexdata") writer.endtag("hexdata")
writer.newline() writer.newline()
def fromXML(self, name, attrs, content, ttFont): def fromXML(self, name, attrs, content, ttFont):
from fontTools.misc.textTools import readHex from fontTools.misc.textTools import readHex
from fontTools import ttLib from fontTools import ttLib
if name != "hexdata": if name != "hexdata":
raise ttLib.TTLibError("can't handle '%s' element" % name) raise ttLib.TTLibError("can't handle '%s' element" % name)
self.decompile(readHex(content), ttFont) self.decompile(readHex(content), ttFont)
def __repr__(self): def __repr__(self):
return "<'%s' table at %x>" % (self.tableTag, id(self)) return "<'%s' table at %x>" % (self.tableTag, id(self))
def __ne__(self, other): def __ne__(self, other):
return not self.__eq__(other) return not self.__eq__(other)
def __eq__(self, other): def __eq__(self, other):

View File

@ -13,7 +13,7 @@ GMAPFormat = """
recordsOffset: H recordsOffset: H
fontNameLength: H fontNameLength: H
""" """
# psFontName is a byte string which follows the record above. This is zero padded # psFontName is a byte string which follows the record above. This is zero padded
# to the beginning of the records array. The recordsOffsst is 32 bit aligned. # to the beginning of the records array. The recordsOffsst is 32 bit aligned.
GMAPRecordFormat1 = """ GMAPRecordFormat1 = """
@ -71,9 +71,9 @@ class GMAPRecord(object):
class table_G_M_A_P_(DefaultTable.DefaultTable): class table_G_M_A_P_(DefaultTable.DefaultTable):
dependencies = [] dependencies = []
def decompile(self, data, ttFont): def decompile(self, data, ttFont):
dummy, newData = sstruct.unpack2(GMAPFormat, data, self) dummy, newData = sstruct.unpack2(GMAPFormat, data, self)
self.psFontName = tostr(newData[:self.fontNameLength]) self.psFontName = tostr(newData[:self.fontNameLength])
@ -108,7 +108,7 @@ class table_G_M_A_P_(DefaultTable.DefaultTable):
writer.newline() writer.newline()
for gmapRecord in self.gmapRecords: for gmapRecord in self.gmapRecords:
gmapRecord.toXML(writer, ttFont) gmapRecord.toXML(writer, ttFont)
def fromXML(self, name, attrs, content, ttFont): def fromXML(self, name, attrs, content, ttFont):
if name == "GMAPRecord": if name == "GMAPRecord":
if not hasattr(self, "gmapRecords"): if not hasattr(self, "gmapRecords"):
@ -124,5 +124,5 @@ class table_G_M_A_P_(DefaultTable.DefaultTable):
value = attrs["value"] value = attrs["value"]
if name == "PSFontName": if name == "PSFontName":
self.psFontName = value self.psFontName = value
else: else:
setattr(self, name, safeEval(value)) setattr(self, name, safeEval(value))

View File

@ -13,12 +13,12 @@ GPKGFormat = """
numGMAPs: H numGMAPs: H
numGlyplets: H numGlyplets: H
""" """
# psFontName is a byte string which follows the record above. This is zero padded # psFontName is a byte string which follows the record above. This is zero padded
# to the beginning of the records array. The recordsOffsst is 32 bit aligned. # to the beginning of the records array. The recordsOffsst is 32 bit aligned.
class table_G_P_K_G_(DefaultTable.DefaultTable): class table_G_P_K_G_(DefaultTable.DefaultTable):
def decompile(self, data, ttFont): def decompile(self, data, ttFont):
dummy, newData = sstruct.unpack2(GPKGFormat, data, self) dummy, newData = sstruct.unpack2(GPKGFormat, data, self)
@ -74,7 +74,7 @@ class table_G_P_K_G_(DefaultTable.DefaultTable):
dataList += self.glyphlets dataList += self.glyphlets
data = bytesjoin(dataList) data = bytesjoin(dataList)
return data return data
def toXML(self, writer, ttFont): def toXML(self, writer, ttFont):
writer.comment("Most of this table will be recalculated by the compiler") writer.comment("Most of this table will be recalculated by the compiler")
writer.newline() writer.newline()
@ -125,5 +125,5 @@ class table_G_P_K_G_(DefaultTable.DefaultTable):
itemName, itemAttrs, itemContent = element itemName, itemAttrs, itemContent = element
if itemName == "hexdata": if itemName == "hexdata":
self.glyphlets.append(readHex(itemContent)) self.glyphlets.append(readHex(itemContent))
else: else:
setattr(self, name, safeEval(value)) setattr(self, name, safeEval(value))

View File

@ -10,7 +10,7 @@ import array
# XXX back to normal eventually. # XXX back to normal eventually.
class table_L_T_S_H_(DefaultTable.DefaultTable): class table_L_T_S_H_(DefaultTable.DefaultTable):
def decompile(self, data, ttFont): def decompile(self, data, ttFont):
version, numGlyphs = struct.unpack(">HH", data[:4]) version, numGlyphs = struct.unpack(">HH", data[:4])
data = data[4:] data = data[4:]
@ -23,7 +23,7 @@ class table_L_T_S_H_(DefaultTable.DefaultTable):
self.yPels = {} self.yPels = {}
for i in range(numGlyphs): for i in range(numGlyphs):
self.yPels[ttFont.getGlyphName(i)] = yPels[i] self.yPels[ttFont.getGlyphName(i)] = yPels[i]
def compile(self, ttFont): def compile(self, ttFont):
version = 0 version = 0
names = list(self.yPels.keys()) names = list(self.yPels.keys())
@ -35,13 +35,13 @@ class table_L_T_S_H_(DefaultTable.DefaultTable):
yPels[ttFont.getGlyphID(name)] = self.yPels[name] yPels[ttFont.getGlyphID(name)] = self.yPels[name]
yPels = array.array("B", yPels) yPels = array.array("B", yPels)
return struct.pack(">HH", version, numGlyphs) + yPels.tostring() return struct.pack(">HH", version, numGlyphs) + yPels.tostring()
def toXML(self, writer, ttFont): def toXML(self, writer, ttFont):
names = sorted(self.yPels.keys()) names = sorted(self.yPels.keys())
for name in names: for name in names:
writer.simpletag("yPel", name=name, value=self.yPels[name]) writer.simpletag("yPel", name=name, value=self.yPels[name])
writer.newline() writer.newline()
def fromXML(self, name, attrs, content, ttFont): def fromXML(self, name, attrs, content, ttFont):
if not hasattr(self, "yPels"): if not hasattr(self, "yPels"):
self.yPels = {} self.yPels = {}

View File

@ -26,19 +26,19 @@ METAGlyphRecordFormat = """
nMetaEntry: H nMetaEntry: H
""" """
# This record is followd by a variable data length field: # This record is followd by a variable data length field:
# USHORT or ULONG hdrOffset # USHORT or ULONG hdrOffset
# Offset from start of META table to the beginning # Offset from start of META table to the beginning
# of this glyphs array of ns Metadata string entries. # of this glyphs array of ns Metadata string entries.
# Size determined by metaFlags field # Size determined by metaFlags field
# METAGlyphRecordFormat entries must be sorted by glyph ID # METAGlyphRecordFormat entries must be sorted by glyph ID
METAStringRecordFormat = """ METAStringRecordFormat = """
> # big endian > # big endian
labelID: H labelID: H
stringLen: H stringLen: H
""" """
# This record is followd by a variable data length field: # This record is followd by a variable data length field:
# USHORT or ULONG stringOffset # USHORT or ULONG stringOffset
# METAStringRecordFormat entries must be sorted in order of labelID # METAStringRecordFormat entries must be sorted in order of labelID
# There may be more than one entry with the same labelID # There may be more than one entry with the same labelID
# There may be more than one strign with the same content. # There may be more than one strign with the same content.
@ -69,9 +69,9 @@ def getLabelString(labelID):
class table_M_E_T_A_(DefaultTable.DefaultTable): class table_M_E_T_A_(DefaultTable.DefaultTable):
dependencies = [] dependencies = []
def decompile(self, data, ttFont): def decompile(self, data, ttFont):
dummy, newData = sstruct.unpack2(METAHeaderFormat, data, self) dummy, newData = sstruct.unpack2(METAHeaderFormat, data, self)
self.glyphRecords = [] self.glyphRecords = []
@ -97,8 +97,8 @@ class table_M_E_T_A_(DefaultTable.DefaultTable):
newData = newData[4:] newData = newData[4:]
stringRec.string = data[stringRec.offset:stringRec.offset + stringRec.stringLen] stringRec.string = data[stringRec.offset:stringRec.offset + stringRec.stringLen]
glyphRecord.stringRecs.append(stringRec) glyphRecord.stringRecs.append(stringRec)
self.glyphRecords.append(glyphRecord) self.glyphRecords.append(glyphRecord)
def compile(self, ttFont): def compile(self, ttFont):
offsetOK = 0 offsetOK = 0
self.nMetaRecs = len(self.glyphRecords) self.nMetaRecs = len(self.glyphRecords)
@ -117,12 +117,12 @@ class table_M_E_T_A_(DefaultTable.DefaultTable):
offsetOK = -1 offsetOK = -1
break break
metaData = metaData + glyphRec.compile(self) metaData = metaData + glyphRec.compile(self)
stringRecsOffset = stringRecsOffset + (glyphRec.nMetaEntry * stringRecSize) stringRecsOffset = stringRecsOffset + (glyphRec.nMetaEntry * stringRecSize)
# this will be the String Record offset for the next GlyphRecord. # this will be the String Record offset for the next GlyphRecord.
if offsetOK == -1: if offsetOK == -1:
offsetOK = 0 offsetOK = 0
continue continue
# metaData now contains the header and all of the GlyphRecords. Its length should bw # metaData now contains the header and all of the GlyphRecords. Its length should bw
# the offset to the first StringRecord. # the offset to the first StringRecord.
stringOffset = stringRecsOffset stringOffset = stringRecsOffset
@ -139,7 +139,7 @@ class table_M_E_T_A_(DefaultTable.DefaultTable):
if offsetOK == -1: if offsetOK == -1:
offsetOK = 0 offsetOK = 0
continue continue
if ((self.metaFlags & 1) == 1) and (stringOffset < 65536): if ((self.metaFlags & 1) == 1) and (stringOffset < 65536):
self.metaFlags = self.metaFlags - 1 self.metaFlags = self.metaFlags - 1
continue continue
@ -152,9 +152,9 @@ class table_M_E_T_A_(DefaultTable.DefaultTable):
for stringRec in glyphRec.stringRecs: for stringRec in glyphRec.stringRecs:
assert (stringRec.offset == len(metaData)), "String offset did not compile correctly! for string:" + str(stringRec.string) assert (stringRec.offset == len(metaData)), "String offset did not compile correctly! for string:" + str(stringRec.string)
metaData = metaData + stringRec.string metaData = metaData + stringRec.string
return metaData return metaData
def toXML(self, writer, ttFont): def toXML(self, writer, ttFont):
writer.comment("Lengths and number of entries in this table will be recalculated by the compiler") writer.comment("Lengths and number of entries in this table will be recalculated by the compiler")
writer.newline() writer.newline()
@ -165,7 +165,7 @@ class table_M_E_T_A_(DefaultTable.DefaultTable):
writer.newline() writer.newline()
for glyphRec in self.glyphRecords: for glyphRec in self.glyphRecords:
glyphRec.toXML(writer, ttFont) glyphRec.toXML(writer, ttFont)
def fromXML(self, name, attrs, content, ttFont): def fromXML(self, name, attrs, content, ttFont):
if name == "GlyphRecord": if name == "GlyphRecord":
if not hasattr(self, "glyphRecords"): if not hasattr(self, "glyphRecords"):
@ -179,7 +179,7 @@ class table_M_E_T_A_(DefaultTable.DefaultTable):
glyphRec.fromXML(name, attrs, content, ttFont) glyphRec.fromXML(name, attrs, content, ttFont)
glyphRec.offset = -1 glyphRec.offset = -1
glyphRec.nMetaEntry = len(glyphRec.stringRecs) glyphRec.nMetaEntry = len(glyphRec.stringRecs)
else: else:
setattr(self, name, safeEval(attrs["value"])) setattr(self, name, safeEval(attrs["value"]))
@ -189,7 +189,7 @@ class GlyphRecord(object):
self.nMetaEntry = -1 self.nMetaEntry = -1
self.offset = -1 self.offset = -1
self.stringRecs = [] self.stringRecs = []
def toXML(self, writer, ttFont): def toXML(self, writer, ttFont):
writer.begintag("GlyphRecord") writer.begintag("GlyphRecord")
writer.newline() writer.newline()
@ -211,7 +211,7 @@ class GlyphRecord(object):
continue continue
stringRec.fromXML(name, attrs, content, ttFont) stringRec.fromXML(name, attrs, content, ttFont)
stringRec.stringLen = len(stringRec.string) stringRec.stringLen = len(stringRec.string)
else: else:
setattr(self, name, safeEval(attrs["value"])) setattr(self, name, safeEval(attrs["value"]))
def compile(self, parentTable): def compile(self, parentTable):
@ -222,7 +222,7 @@ class GlyphRecord(object):
datum = struct.pack(">L", self.offset) datum = struct.pack(">L", self.offset)
data = data + datum data = data + datum
return data return data
def __repr__(self): def __repr__(self):
return "GlyphRecord[ glyphID: " + str(self.glyphID) + ", nMetaEntry: " + str(self.nMetaEntry) + ", offset: " + str(self.offset) + " ]" return "GlyphRecord[ glyphID: " + str(self.glyphID) + ", nMetaEntry: " + str(self.nMetaEntry) + ", offset: " + str(self.offset) + " ]"
@ -244,12 +244,12 @@ def mapXMLToUTF8(string):
while string[i] != ";": while string[i] != ";":
i = i+1 i = i+1
valStr = string[j:i] valStr = string[j:i]
uString = uString + unichr(eval('0x' + valStr)) uString = uString + unichr(eval('0x' + valStr))
else: else:
uString = uString + unichr(byteord(string[i])) uString = uString + unichr(byteord(string[i]))
i = i +1 i = i +1
return uString.encode('utf_8') return uString.encode('utf_8')
@ -298,7 +298,7 @@ class StringRecord(object):
datum = struct.pack(">L", self.offset) datum = struct.pack(">L", self.offset)
data = data + datum data = data + datum
return data return data
def __repr__(self): def __repr__(self):
return "StringRecord [ labelID: " + str(self.labelID) + " aka " + getLabelString(self.labelID) \ return "StringRecord [ labelID: " + str(self.labelID) + " aka " + getLabelString(self.labelID) \
+ ", offset: " + str(self.offset) + ", length: " + str(self.stringLen) + ", string: " +self.string + " ]" + ", offset: " + str(self.offset) + ", length: " + str(self.stringLen) + ", string: " +self.string + " ]"

View File

@ -22,13 +22,13 @@ panoseFormat = """
""" """
class Panose(object): class Panose(object):
def toXML(self, writer, ttFont): def toXML(self, writer, ttFont):
formatstring, names, fixes = sstruct.getformat(panoseFormat) formatstring, names, fixes = sstruct.getformat(panoseFormat)
for name in names: for name in names:
writer.simpletag(name, value=getattr(self, name)) writer.simpletag(name, value=getattr(self, name))
writer.newline() writer.newline()
def fromXML(self, name, attrs, content, ttFont): def fromXML(self, name, attrs, content, ttFont):
setattr(self, name, safeEval(attrs["value"])) setattr(self, name, safeEval(attrs["value"]))
@ -98,9 +98,9 @@ OS2_format_5_addition = bigendian + OS2_format_5_addition
class table_O_S_2f_2(DefaultTable.DefaultTable): class table_O_S_2f_2(DefaultTable.DefaultTable):
"""the OS/2 table""" """the OS/2 table"""
def decompile(self, data, ttFont): def decompile(self, data, ttFont):
dummy, data = sstruct.unpack2(OS2_format_0, data, self) dummy, data = sstruct.unpack2(OS2_format_0, data, self)
@ -119,7 +119,7 @@ class table_O_S_2f_2(DefaultTable.DefaultTable):
warnings.warn("too much 'OS/2' table data") warnings.warn("too much 'OS/2' table data")
self.panose = sstruct.unpack(panoseFormat, self.panose, Panose()) self.panose = sstruct.unpack(panoseFormat, self.panose, Panose())
def compile(self, ttFont): def compile(self, ttFont):
panose = self.panose panose = self.panose
self.panose = sstruct.pack(panoseFormat, self.panose) self.panose = sstruct.pack(panoseFormat, self.panose)
@ -139,7 +139,7 @@ class table_O_S_2f_2(DefaultTable.DefaultTable):
raise ttLib.TTLibError("unknown format for OS/2 table: version %s" % self.version) raise ttLib.TTLibError("unknown format for OS/2 table: version %s" % self.version)
self.panose = panose self.panose = panose
return data return data
def toXML(self, writer, ttFont): def toXML(self, writer, ttFont):
if self.version == 1: if self.version == 1:
format = OS2_format_1 format = OS2_format_1
@ -157,7 +157,7 @@ class table_O_S_2f_2(DefaultTable.DefaultTable):
writer.newline() writer.newline()
value.toXML(writer, ttFont) value.toXML(writer, ttFont)
writer.endtag("panose") writer.endtag("panose")
elif name in ("ulUnicodeRange1", "ulUnicodeRange2", elif name in ("ulUnicodeRange1", "ulUnicodeRange2",
"ulUnicodeRange3", "ulUnicodeRange4", "ulUnicodeRange3", "ulUnicodeRange4",
"ulCodePageRange1", "ulCodePageRange2"): "ulCodePageRange1", "ulCodePageRange2"):
writer.simpletag(name, value=num2binary(value)) writer.simpletag(name, value=num2binary(value))
@ -168,7 +168,7 @@ class table_O_S_2f_2(DefaultTable.DefaultTable):
else: else:
writer.simpletag(name, value=value) writer.simpletag(name, value=value)
writer.newline() writer.newline()
def fromXML(self, name, attrs, content, ttFont): def fromXML(self, name, attrs, content, ttFont):
if name == "panose": if name == "panose":
self.panose = panose = Panose() self.panose = panose = Panose()
@ -176,7 +176,7 @@ class table_O_S_2f_2(DefaultTable.DefaultTable):
if isinstance(element, tuple): if isinstance(element, tuple):
name, attrs, content = element name, attrs, content = element
panose.fromXML(name, attrs, content, ttFont) panose.fromXML(name, attrs, content, ttFont)
elif name in ("ulUnicodeRange1", "ulUnicodeRange2", elif name in ("ulUnicodeRange1", "ulUnicodeRange2",
"ulUnicodeRange3", "ulUnicodeRange4", "ulUnicodeRange3", "ulUnicodeRange4",
"ulCodePageRange1", "ulCodePageRange2", "ulCodePageRange1", "ulCodePageRange2",
"fsType", "fsSelection"): "fsType", "fsSelection"):

View File

@ -22,22 +22,22 @@ SINGFormat = """
class table_S_I_N_G_(DefaultTable.DefaultTable): class table_S_I_N_G_(DefaultTable.DefaultTable):
dependencies = [] dependencies = []
def decompile(self, data, ttFont): def decompile(self, data, ttFont):
dummy, rest = sstruct.unpack2(SINGFormat, data, self) dummy, rest = sstruct.unpack2(SINGFormat, data, self)
self.uniqueName = self.decompileUniqueName(self.uniqueName) self.uniqueName = self.decompileUniqueName(self.uniqueName)
self.nameLength = byteord(self.nameLength) self.nameLength = byteord(self.nameLength)
assert len(rest) == self.nameLength assert len(rest) == self.nameLength
self.baseGlyphName = tostr(rest) self.baseGlyphName = tostr(rest)
rawMETAMD5 = self.METAMD5 rawMETAMD5 = self.METAMD5
self.METAMD5 = "[" + hex(byteord(self.METAMD5[0])) self.METAMD5 = "[" + hex(byteord(self.METAMD5[0]))
for char in rawMETAMD5[1:]: for char in rawMETAMD5[1:]:
self.METAMD5 = self.METAMD5 + ", " + hex(byteord(char)) self.METAMD5 = self.METAMD5 + ", " + hex(byteord(char))
self.METAMD5 = self.METAMD5 + "]" self.METAMD5 = self.METAMD5 + "]"
def decompileUniqueName(self, data): def decompileUniqueName(self, data):
name = "" name = ""
for char in data: for char in data:
@ -67,7 +67,7 @@ class table_S_I_N_G_(DefaultTable.DefaultTable):
data = sstruct.pack(SINGFormat, d) data = sstruct.pack(SINGFormat, d)
data = data + tobytes(self.baseGlyphName) data = data + tobytes(self.baseGlyphName)
return data return data
def compilecompileUniqueName(self, name, length): def compilecompileUniqueName(self, name, length):
nameLen = len(name) nameLen = len(name)
if length <= nameLen: if length <= nameLen:
@ -86,7 +86,7 @@ class table_S_I_N_G_(DefaultTable.DefaultTable):
writer.newline() writer.newline()
writer.simpletag("baseGlyphName", value=self.baseGlyphName) writer.simpletag("baseGlyphName", value=self.baseGlyphName)
writer.newline() writer.newline()
def fromXML(self, name, attrs, content, ttFont): def fromXML(self, name, attrs, content, ttFont):
value = attrs["value"] value = attrs["value"]
if name in ["uniqueName", "METAMD5", "baseGlyphName"]: if name in ["uniqueName", "METAMD5", "baseGlyphName"]:

View File

@ -46,7 +46,7 @@ The XML format is:
</colorPalettes> </colorPalettes>
</SVG> </SVG>
Color values must be less than 256. Color values must be less than 256.
The number of color records in each </colorPalette> must be the same as The number of color records in each </colorPalette> must be the same as
the number of <colorParamUINameID> elements. the number of <colorParamUINameID> elements.
@ -93,13 +93,13 @@ colorRecord_format_0 = """
class table_S_V_G_(DefaultTable.DefaultTable): class table_S_V_G_(DefaultTable.DefaultTable):
def decompile(self, data, ttFont): def decompile(self, data, ttFont):
self.docList = None self.docList = None
self.colorPalettes = None self.colorPalettes = None
pos = 0 pos = 0
self.version = struct.unpack(">H", data[pos:pos+2])[0] self.version = struct.unpack(">H", data[pos:pos+2])[0]
if self.version == 1: if self.version == 1:
self.decompile_format_1(data, ttFont) self.decompile_format_1(data, ttFont)
else: else:
@ -355,7 +355,7 @@ class ColorPalettes(object):
class ColorPalette(object): class ColorPalette(object):
def __init__(self): def __init__(self):
self.uiNameID = None # USHORT. name table ID that describes user interface strings associated with this color palette. self.uiNameID = None # USHORT. name table ID that describes user interface strings associated with this color palette.
self.paletteColors = [] # list of ColorRecords self.paletteColors = [] # list of ColorRecords
def fromXML(self, name, attrs, content, ttFont): def fromXML(self, name, attrs, content, ttFont):

View File

@ -6,13 +6,13 @@ import struct
tsi0Format = '>HHl' tsi0Format = '>HHl'
def fixlongs(glyphID, textLength, textOffset): def fixlongs(glyphID, textLength, textOffset):
return int(glyphID), int(textLength), textOffset return int(glyphID), int(textLength), textOffset
class table_T_S_I__0(DefaultTable.DefaultTable): class table_T_S_I__0(DefaultTable.DefaultTable):
dependencies = ["TSI1"] dependencies = ["TSI1"]
def decompile(self, data, ttFont): def decompile(self, data, ttFont):
numGlyphs = ttFont['maxp'].numGlyphs numGlyphs = ttFont['maxp'].numGlyphs
indices = [] indices = []
@ -25,7 +25,7 @@ class table_T_S_I__0(DefaultTable.DefaultTable):
assert indices[-5] == (0XFFFE, 0, -1409540300), "bad magic number" # 0xABFC1F34 assert indices[-5] == (0XFFFE, 0, -1409540300), "bad magic number" # 0xABFC1F34
self.indices = indices[:-5] self.indices = indices[:-5]
self.extra_indices = indices[-4:] self.extra_indices = indices[-4:]
def compile(self, ttFont): def compile(self, ttFont):
if not hasattr(self, "indices"): if not hasattr(self, "indices"):
# We have no corresponding table (TSI1 or TSI3); let's return # We have no corresponding table (TSI1 or TSI3); let's return
@ -38,12 +38,12 @@ class table_T_S_I__0(DefaultTable.DefaultTable):
for index, textLength, textOffset in self.extra_indices: for index, textLength, textOffset in self.extra_indices:
data = data + struct.pack(tsi0Format, index, textLength, textOffset) data = data + struct.pack(tsi0Format, index, textLength, textOffset)
return data return data
def set(self, indices, extra_indices): def set(self, indices, extra_indices):
# gets called by 'TSI1' or 'TSI3' # gets called by 'TSI1' or 'TSI3'
self.indices = indices self.indices = indices
self.extra_indices = extra_indices self.extra_indices = extra_indices
def toXML(self, writer, ttFont): def toXML(self, writer, ttFont):
writer.comment("This table will be calculated by the compiler") writer.comment("This table will be calculated by the compiler")
writer.newline() writer.newline()

View File

@ -3,11 +3,11 @@ from fontTools.misc.py23 import *
from . import DefaultTable from . import DefaultTable
class table_T_S_I__1(DefaultTable.DefaultTable): class table_T_S_I__1(DefaultTable.DefaultTable):
extras = {0xfffa: "ppgm", 0xfffb: "cvt", 0xfffc: "reserved", 0xfffd: "fpgm"} extras = {0xfffa: "ppgm", 0xfffb: "cvt", 0xfffc: "reserved", 0xfffd: "fpgm"}
indextable = "TSI0" indextable = "TSI0"
def decompile(self, data, ttFont): def decompile(self, data, ttFont):
indextable = ttFont[self.indextable] indextable = ttFont[self.indextable]
self.glyphPrograms = {} self.glyphPrograms = {}
@ -22,7 +22,7 @@ class table_T_S_I__1(DefaultTable.DefaultTable):
assert len(text) == textLength assert len(text) == textLength
if text: if text:
self.glyphPrograms[ttFont.getGlyphName(glyphID)] = text self.glyphPrograms[ttFont.getGlyphName(glyphID)] = text
self.extraPrograms = {} self.extraPrograms = {}
for i in range(len(indextable.extra_indices)): for i in range(len(indextable.extra_indices)):
extraCode, textLength, textOffset = indextable.extra_indices[i] extraCode, textLength, textOffset = indextable.extra_indices[i]
@ -35,7 +35,7 @@ class table_T_S_I__1(DefaultTable.DefaultTable):
assert len(text) == textLength assert len(text) == textLength
if text: if text:
self.extraPrograms[self.extras[extraCode]] = text self.extraPrograms[self.extras[extraCode]] = text
def compile(self, ttFont): def compile(self, ttFont):
if not hasattr(self, "glyphPrograms"): if not hasattr(self, "glyphPrograms"):
self.glyphPrograms = {} self.glyphPrograms = {}
@ -43,7 +43,7 @@ class table_T_S_I__1(DefaultTable.DefaultTable):
data = b'' data = b''
indextable = ttFont[self.indextable] indextable = ttFont[self.indextable]
glyphNames = ttFont.getGlyphOrder() glyphNames = ttFont.getGlyphOrder()
indices = [] indices = []
for i in range(len(glyphNames)): for i in range(len(glyphNames)):
if len(data) % 2: if len(data) % 2:
@ -58,7 +58,7 @@ class table_T_S_I__1(DefaultTable.DefaultTable):
textLength = 0x8000 # XXX ??? textLength = 0x8000 # XXX ???
indices.append((i, textLength, len(data))) indices.append((i, textLength, len(data)))
data = data + text data = data + text
extra_indices = [] extra_indices = []
codes = sorted(self.extras.items()) codes = sorted(self.extras.items())
for i in range(len(codes)): for i in range(len(codes)):
@ -76,7 +76,7 @@ class table_T_S_I__1(DefaultTable.DefaultTable):
data = data + text data = data + text
indextable.set(indices, extra_indices) indextable.set(indices, extra_indices)
return data return data
def toXML(self, writer, ttFont): def toXML(self, writer, ttFont):
names = sorted(self.glyphPrograms.keys()) names = sorted(self.glyphPrograms.keys())
writer.newline() writer.newline()
@ -103,7 +103,7 @@ class table_T_S_I__1(DefaultTable.DefaultTable):
writer.endtag("extraProgram") writer.endtag("extraProgram")
writer.newline() writer.newline()
writer.newline() writer.newline()
def fromXML(self, name, attrs, content, ttFont): def fromXML(self, name, attrs, content, ttFont):
if not hasattr(self, "glyphPrograms"): if not hasattr(self, "glyphPrograms"):
self.glyphPrograms = {} self.glyphPrograms = {}

View File

@ -5,5 +5,5 @@ from fontTools import ttLib
superclass = ttLib.getTableClass("TSI0") superclass = ttLib.getTableClass("TSI0")
class table_T_S_I__2(superclass): class table_T_S_I__2(superclass):
dependencies = ["TSI3"] dependencies = ["TSI3"]

View File

@ -5,7 +5,7 @@ from fontTools import ttLib
superclass = ttLib.getTableClass("TSI1") superclass = ttLib.getTableClass("TSI1")
class table_T_S_I__3(superclass): class table_T_S_I__3(superclass):
extras = {0xfffa: "reserved0", 0xfffb: "reserved1", 0xfffc: "reserved2", 0xfffd: "reserved3"} extras = {0xfffa: "reserved0", 0xfffb: "reserved1", 0xfffc: "reserved2", 0xfffd: "reserved3"}
indextable = "TSI2" indextable = "TSI2"

View File

@ -7,7 +7,7 @@ import array
class table_T_S_I__5(DefaultTable.DefaultTable): class table_T_S_I__5(DefaultTable.DefaultTable):
def decompile(self, data, ttFont): def decompile(self, data, ttFont):
numGlyphs = ttFont['maxp'].numGlyphs numGlyphs = ttFont['maxp'].numGlyphs
assert len(data) == 2 * numGlyphs assert len(data) == 2 * numGlyphs
@ -18,7 +18,7 @@ class table_T_S_I__5(DefaultTable.DefaultTable):
self.glyphGrouping = {} self.glyphGrouping = {}
for i in range(numGlyphs): for i in range(numGlyphs):
self.glyphGrouping[ttFont.getGlyphName(i)] = a[i] self.glyphGrouping[ttFont.getGlyphName(i)] = a[i]
def compile(self, ttFont): def compile(self, ttFont):
glyphNames = ttFont.getGlyphOrder() glyphNames = ttFont.getGlyphOrder()
a = array.array("H") a = array.array("H")
@ -27,13 +27,13 @@ class table_T_S_I__5(DefaultTable.DefaultTable):
if sys.byteorder != "big": if sys.byteorder != "big":
a.byteswap() a.byteswap()
return a.tostring() return a.tostring()
def toXML(self, writer, ttFont): def toXML(self, writer, ttFont):
names = sorted(self.glyphGrouping.keys()) names = sorted(self.glyphGrouping.keys())
for glyphName in names: for glyphName in names:
writer.simpletag("glyphgroup", name=glyphName, value=self.glyphGrouping[glyphName]) writer.simpletag("glyphgroup", name=glyphName, value=self.glyphGrouping[glyphName])
writer.newline() writer.newline()
def fromXML(self, name, attrs, content, ttFont): def fromXML(self, name, attrs, content, ttFont):
if not hasattr(self, "glyphGrouping"): if not hasattr(self, "glyphGrouping"):
self.glyphGrouping = {} self.glyphGrouping = {}

View File

@ -20,7 +20,7 @@ VDMX_RatRangeFmt = """
yStartRatio: B # Starting y-Ratio value yStartRatio: B # Starting y-Ratio value
yEndRatio: B # Ending y-Ratio value yEndRatio: B # Ending y-Ratio value
""" """
# followed by an array of offset[numRatios] from start of VDMX table to the # followed by an array of offset[numRatios] from start of VDMX table to the
# VDMX Group for this ratio range (offsets will be re-calculated on compile); # VDMX Group for this ratio range (offsets will be re-calculated on compile);
# followed by an array of Group[numRecs] records; # followed by an array of Group[numRecs] records;
VDMX_GroupFmt = """ VDMX_GroupFmt = """

View File

@ -41,7 +41,7 @@ class table_V_O_R_G_(DefaultTable.DefaultTable):
vorgs = list(self.VOriginRecords.values()) vorgs = list(self.VOriginRecords.values())
names = list(self.VOriginRecords.keys()) names = list(self.VOriginRecords.keys())
nameMap = ttFont.getReverseGlyphMap() nameMap = ttFont.getReverseGlyphMap()
lenRecords = len(vorgs) lenRecords = len(vorgs)
try: try:
gids = map(operator.getitem, [nameMap]*lenRecords, names) gids = map(operator.getitem, [nameMap]*lenRecords, names)
except KeyError: except KeyError:
@ -100,7 +100,7 @@ class table_V_O_R_G_(DefaultTable.DefaultTable):
if glyphSelector not in self.VOriginRecords: if glyphSelector not in self.VOriginRecords:
return self.defaultVertOriginY return self.defaultVertOriginY
return self.VOriginRecords[glyphSelector] return self.VOriginRecords[glyphSelector]
def __setitem__(self, glyphSelector, value): def __setitem__(self, glyphSelector, value):

View File

@ -12,14 +12,14 @@ import operator
class table__c_m_a_p(DefaultTable.DefaultTable): class table__c_m_a_p(DefaultTable.DefaultTable):
def getcmap(self, platformID, platEncID): def getcmap(self, platformID, platEncID):
for subtable in self.tables: for subtable in self.tables:
if (subtable.platformID == platformID and if (subtable.platformID == platformID and
subtable.platEncID == platEncID): subtable.platEncID == platEncID):
return subtable return subtable
return None # not found return None # not found
def decompile(self, data, ttFont): def decompile(self, data, ttFont):
tableVersion, numSubTables = struct.unpack(">HH", data[:4]) tableVersion, numSubTables = struct.unpack(">HH", data[:4])
self.tableVersion = int(tableVersion) self.tableVersion = int(tableVersion)
@ -34,7 +34,7 @@ class table__c_m_a_p(DefaultTable.DefaultTable):
format, reserved, length = struct.unpack(">HHL", data[offset:offset+8]) format, reserved, length = struct.unpack(">HHL", data[offset:offset+8])
elif format in [14]: elif format in [14]:
format, length = struct.unpack(">HL", data[offset:offset+6]) format, length = struct.unpack(">HL", data[offset:offset+6])
if not length: if not length:
print("Error: cmap subtable is reported as having zero length: platformID %s, platEncID %s, format %s offset %s. Skipping table." % (platformID, platEncID,format, offset)) print("Error: cmap subtable is reported as having zero length: platformID %s, platEncID %s, format %s offset %s. Skipping table." % (platformID, platEncID,format, offset))
continue continue
@ -53,7 +53,7 @@ class table__c_m_a_p(DefaultTable.DefaultTable):
else: else:
seenOffsets[offset] = i seenOffsets[offset] = i
tables.append(table) tables.append(table)
def compile(self, ttFont): def compile(self, ttFont):
self.tables.sort() # sort according to the spec; see CmapSubtable.__lt__() self.tables.sort() # sort according to the spec; see CmapSubtable.__lt__()
numSubTables = len(self.tables) numSubTables = len(self.tables)
@ -74,13 +74,13 @@ class table__c_m_a_p(DefaultTable.DefaultTable):
tableData = tableData + chunk tableData = tableData + chunk
data = data + struct.pack(">HHl", table.platformID, table.platEncID, offset) data = data + struct.pack(">HHl", table.platformID, table.platEncID, offset)
return data + tableData return data + tableData
def toXML(self, writer, ttFont): def toXML(self, writer, ttFont):
writer.simpletag("tableVersion", version=self.tableVersion) writer.simpletag("tableVersion", version=self.tableVersion)
writer.newline() writer.newline()
for table in self.tables: for table in self.tables:
table.toXML(writer, ttFont) table.toXML(writer, ttFont)
def fromXML(self, name, attrs, content, ttFont): def fromXML(self, name, attrs, content, ttFont):
if name == "tableVersion": if name == "tableVersion":
self.tableVersion = safeEval(attrs["version"]) self.tableVersion = safeEval(attrs["version"])
@ -101,7 +101,7 @@ class table__c_m_a_p(DefaultTable.DefaultTable):
class CmapSubtable(object): class CmapSubtable(object):
def __init__(self, format): def __init__(self, format):
self.format = format self.format = format
self.data = None self.data = None
@ -118,7 +118,7 @@ class CmapSubtable(object):
# just return the original data. Also avoids recursion when # just return the original data. Also avoids recursion when
# called with an attribute that the cmap subtable doesn't have. # called with an attribute that the cmap subtable doesn't have.
return getattr(self, attr) return getattr(self, attr)
def decompileHeader(self, data, ttFont): def decompileHeader(self, data, ttFont):
format, length, language = struct.unpack(">HHH", data[:6]) format, length, language = struct.unpack(">HHH", data[:6])
assert len(data) == length, "corrupt cmap table format %d (data length: %d, header length: %d)" % (format, len(data), length) assert len(data) == length, "corrupt cmap table format %d (data length: %d, header length: %d)" % (format, len(data), length)
@ -166,7 +166,7 @@ class CmapSubtable(object):
if isUnicode: if isUnicode:
writer.comment(Unicode[code]) writer.comment(Unicode[code])
writer.newline() writer.newline()
def __lt__(self, other): def __lt__(self, other):
if not isinstance(other, CmapSubtable): if not isinstance(other, CmapSubtable):
return NotImplemented return NotImplemented
@ -186,7 +186,7 @@ class CmapSubtable(object):
class cmap_format_0(CmapSubtable): class cmap_format_0(CmapSubtable):
def decompile(self, data, ttFont): def decompile(self, data, ttFont):
# we usually get here indirectly from the subtable __getattr__ function, in which case both args must be None. # we usually get here indirectly from the subtable __getattr__ function, in which case both args must be None.
# If not, someone is calling the subtable decompile() directly, and must provide both args. # If not, someone is calling the subtable decompile() directly, and must provide both args.
@ -218,7 +218,7 @@ class cmap_format_0(CmapSubtable):
data = struct.pack(">HHH", 0, 262, self.language) + glyphIdArray.tostring() data = struct.pack(">HHH", 0, 262, self.language) + glyphIdArray.tostring()
assert len(data) == 262 assert len(data) == 262
return data return data
def fromXML(self, name, attrs, content, ttFont): def fromXML(self, name, attrs, content, ttFont):
self.language = safeEval(attrs["language"]) self.language = safeEval(attrs["language"])
if not hasattr(self, "cmap"): if not hasattr(self, "cmap"):
@ -241,9 +241,9 @@ class SubHeader(object):
self.idDelta = None self.idDelta = None
self.idRangeOffset = None self.idRangeOffset = None
self.glyphIndexArray = [] self.glyphIndexArray = []
class cmap_format_2(CmapSubtable): class cmap_format_2(CmapSubtable):
def setIDDelta(self, subHeader): def setIDDelta(self, subHeader):
subHeader.idDelta = 0 subHeader.idDelta = 0
# find the minGI which is not zero. # find the minGI which is not zero.
@ -253,13 +253,13 @@ class cmap_format_2(CmapSubtable):
minGI = gid minGI = gid
# The lowest gid in glyphIndexArray, after subtracting idDelta, must be 1. # The lowest gid in glyphIndexArray, after subtracting idDelta, must be 1.
# idDelta is a short, and must be between -32K and 32K. minGI can be between 1 and 64K. # idDelta is a short, and must be between -32K and 32K. minGI can be between 1 and 64K.
# We would like to pick an idDelta such that the first glyphArray GID is 1, # We would like to pick an idDelta such that the first glyphArray GID is 1,
# so that we are more likely to be able to combine glypharray GID subranges. # so that we are more likely to be able to combine glypharray GID subranges.
# This means that we have a problem when minGI is > 32K # This means that we have a problem when minGI is > 32K
# Since the final gi is reconstructed from the glyphArray GID by: # Since the final gi is reconstructed from the glyphArray GID by:
# (short)finalGID = (gid + idDelta) % 0x10000), # (short)finalGID = (gid + idDelta) % 0x10000),
# we can get from a glypharray GID of 1 to a final GID of 65K by subtracting 2, and casting the # we can get from a glypharray GID of 1 to a final GID of 65K by subtracting 2, and casting the
# negative number to an unsigned short. # negative number to an unsigned short.
if (minGI > 1): if (minGI > 1):
if minGI > 0x7FFF: if minGI > 0x7FFF:
@ -269,8 +269,8 @@ class cmap_format_2(CmapSubtable):
idDelta = subHeader.idDelta idDelta = subHeader.idDelta
for i in range(subHeader.entryCount): for i in range(subHeader.entryCount):
gid = subHeader.glyphIndexArray[i] gid = subHeader.glyphIndexArray[i]
if gid > 0: if gid > 0:
subHeader.glyphIndexArray[i] = gid - idDelta subHeader.glyphIndexArray[i] = gid - idDelta
def decompile(self, data, ttFont): def decompile(self, data, ttFont):
# we usually get here indirectly from the subtable __getattr__ function, in which case both args must be None. # we usually get here indirectly from the subtable __getattr__ function, in which case both args must be None.
@ -291,7 +291,7 @@ class cmap_format_2(CmapSubtable):
allKeys.byteswap() allKeys.byteswap()
subHeaderKeys = [ key//8 for key in allKeys] subHeaderKeys = [ key//8 for key in allKeys]
maxSubHeaderindex = max(subHeaderKeys) maxSubHeaderindex = max(subHeaderKeys)
#Load subHeaders #Load subHeaders
subHeaderList = [] subHeaderList = []
pos = 0 pos = 0
@ -307,14 +307,14 @@ class cmap_format_2(CmapSubtable):
giList.byteswap() giList.byteswap()
subHeader.glyphIndexArray = giList subHeader.glyphIndexArray = giList
subHeaderList.append(subHeader) subHeaderList.append(subHeader)
# How this gets processed. # How this gets processed.
# Charcodes may be one or two bytes. # Charcodes may be one or two bytes.
# The first byte of a charcode is mapped through the subHeaderKeys, to select # The first byte of a charcode is mapped through the subHeaderKeys, to select
# a subHeader. For any subheader but 0, the next byte is then mapped through the # a subHeader. For any subheader but 0, the next byte is then mapped through the
# selected subheader. If subheader Index 0 is selected, then the byte itself is # selected subheader. If subheader Index 0 is selected, then the byte itself is
# mapped through the subheader, and there is no second byte. # mapped through the subheader, and there is no second byte.
# Then assume that the subsequent byte is the first byte of the next charcode,and repeat. # Then assume that the subsequent byte is the first byte of the next charcode,and repeat.
# #
# Each subheader references a range in the glyphIndexArray whose length is entryCount. # Each subheader references a range in the glyphIndexArray whose length is entryCount.
# The range in glyphIndexArray referenced by a sunheader may overlap with the range in glyphIndexArray # The range in glyphIndexArray referenced by a sunheader may overlap with the range in glyphIndexArray
# referenced by another subheader. # referenced by another subheader.
@ -326,7 +326,7 @@ class cmap_format_2(CmapSubtable):
# firstChar and EntryCount values. If the byte value is outside the subrange, then the glyphIndex is zero # firstChar and EntryCount values. If the byte value is outside the subrange, then the glyphIndex is zero
# (e.g. glyph not in font). # (e.g. glyph not in font).
# If the byte index is in the subrange, then an offset index is calculated as (byteIndex - firstChar). # If the byte index is in the subrange, then an offset index is calculated as (byteIndex - firstChar).
# The index to glyphIndex mapping is a subrange of the glyphIndexArray. You find the start of the subrange by # The index to glyphIndex mapping is a subrange of the glyphIndexArray. You find the start of the subrange by
# counting idRangeOffset bytes from the idRangeOffset word. The first value in this subrange is the # counting idRangeOffset bytes from the idRangeOffset word. The first value in this subrange is the
# glyphIndex for the index firstChar. The offset index should then be used in this array to get the glyphIndex. # glyphIndex for the index firstChar. The offset index should then be used in this array to get the glyphIndex.
# Example for Logocut-Medium # Example for Logocut-Medium
@ -340,10 +340,10 @@ class cmap_format_2(CmapSubtable):
# [257], [1]=2 from charcode [129, 65] # [257], [1]=2 from charcode [129, 65]
# [258], [2]=3 from charcode [129, 66] # [258], [2]=3 from charcode [129, 66]
# [259], [3]=4 from charcode [129, 67] # [259], [3]=4 from charcode [129, 67]
# So, the glyphIndex = 3 from the array. Then if idDelta is not zero and the glyph ID is not zero, # So, the glyphIndex = 3 from the array. Then if idDelta is not zero and the glyph ID is not zero,
# add it to the glyphID to get the final glyphIndex # add it to the glyphID to get the final glyphIndex
# value. In this case the final glyph index = 3+ 42 -> 45 for the final glyphIndex. Whew! # value. In this case the final glyph index = 3+ 42 -> 45 for the final glyphIndex. Whew!
self.data = b"" self.data = b""
self.cmap = cmap = {} self.cmap = cmap = {}
notdefGI = 0 notdefGI = 0
@ -374,7 +374,7 @@ class cmap_format_2(CmapSubtable):
continue continue
cmap[charCode] = gi cmap[charCode] = gi
# If not subHeader.entryCount, then all char codes with this first byte are # If not subHeader.entryCount, then all char codes with this first byte are
# mapped to .notdef. We can skip this subtable, and leave the glyphs un-encoded, which is the # mapped to .notdef. We can skip this subtable, and leave the glyphs un-encoded, which is the
# same as mapping it to .notdef. # same as mapping it to .notdef.
# cmap values are GID's. # cmap values are GID's.
glyphOrder = self.ttFont.getGlyphOrder() glyphOrder = self.ttFont.getGlyphOrder()
@ -387,7 +387,7 @@ class cmap_format_2(CmapSubtable):
getGlyphName = self.ttFont.getGlyphName getGlyphName = self.ttFont.getGlyphName
names = list(map(getGlyphName, gids )) names = list(map(getGlyphName, gids ))
list(map(operator.setitem, [cmap]*lenCmap, charCodes, names)) list(map(operator.setitem, [cmap]*lenCmap, charCodes, names))
def compile(self, ttFont): def compile(self, ttFont):
if self.data: if self.data:
return struct.pack(">HHH", self.format, self.length, self.language) + self.data return struct.pack(">HHH", self.format, self.length, self.language) + self.data
@ -398,7 +398,7 @@ class cmap_format_2(CmapSubtable):
charCodes = [item[0] for item in items] charCodes = [item[0] for item in items]
names = [item[1] for item in items] names = [item[1] for item in items]
nameMap = ttFont.getReverseGlyphMap() nameMap = ttFont.getReverseGlyphMap()
lenCharCodes = len(charCodes) lenCharCodes = len(charCodes)
try: try:
gids = list(map(operator.getitem, [nameMap]*lenCharCodes, names)) gids = list(map(operator.getitem, [nameMap]*lenCharCodes, names))
except KeyError: except KeyError:
@ -423,8 +423,8 @@ class cmap_format_2(CmapSubtable):
gids.append(gid) gids.append(gid)
# Process the (char code to gid) item list in char code order. # Process the (char code to gid) item list in char code order.
# By definition, all one byte char codes map to subheader 0. # By definition, all one byte char codes map to subheader 0.
# For all the two byte char codes, we assume that the first byte maps maps to the empty subhead (with an entry count of 0, # For all the two byte char codes, we assume that the first byte maps maps to the empty subhead (with an entry count of 0,
# which defines all char codes in its range to map to notdef) unless proven otherwise. # which defines all char codes in its range to map to notdef) unless proven otherwise.
# Note that since the char code items are processed in char code order, all the char codes with the # Note that since the char code items are processed in char code order, all the char codes with the
# same first byte are in sequential order. # same first byte are in sequential order.
@ -443,7 +443,7 @@ class cmap_format_2(CmapSubtable):
subHeader.idDelta = 0 subHeader.idDelta = 0
subHeader.idRangeOffset = 0 subHeader.idRangeOffset = 0
subHeaderList.append(subHeader) subHeaderList.append(subHeader)
lastFirstByte = -1 lastFirstByte = -1
items = zip(charCodes, gids) items = zip(charCodes, gids)
for charCode, gid in items: for charCode, gid in items:
@ -480,7 +480,7 @@ class cmap_format_2(CmapSubtable):
subHeader.glyphIndexArray.append(notdefGI) subHeader.glyphIndexArray.append(notdefGI)
subHeader.glyphIndexArray.append(gid) subHeader.glyphIndexArray.append(gid)
subHeader.entryCount = subHeader.entryCount + codeDiff + 1 subHeader.entryCount = subHeader.entryCount + codeDiff + 1
# fix GI's and iDelta of last subheader that we we added to the subheader array. # fix GI's and iDelta of last subheader that we we added to the subheader array.
self.setIDDelta(subHeader) self.setIDDelta(subHeader)
@ -497,12 +497,12 @@ class cmap_format_2(CmapSubtable):
subHeaderKeys[index] = emptySubheadIndex subHeaderKeys[index] = emptySubheadIndex
# Since this is the last subheader, the GlyphIndex Array starts two bytes after the start of the # Since this is the last subheader, the GlyphIndex Array starts two bytes after the start of the
# idRangeOffset word of this subHeader. We can safely point to the first entry in the GlyphIndexArray, # idRangeOffset word of this subHeader. We can safely point to the first entry in the GlyphIndexArray,
# since the first subrange of the GlyphIndexArray is for subHeader 0, which always starts with # since the first subrange of the GlyphIndexArray is for subHeader 0, which always starts with
# charcode 0 and GID 0. # charcode 0 and GID 0.
idRangeOffset = (len(subHeaderList)-1)*8 + 2 # offset to beginning of glyphIDArray from first subheader idRangeOffset. idRangeOffset = (len(subHeaderList)-1)*8 + 2 # offset to beginning of glyphIDArray from first subheader idRangeOffset.
subheadRangeLen = len(subHeaderList) -1 # skip last special empty-set subheader; we've already hardocodes its idRangeOffset to 2. subheadRangeLen = len(subHeaderList) -1 # skip last special empty-set subheader; we've already hardocodes its idRangeOffset to 2.
for index in range(subheadRangeLen): for index in range(subheadRangeLen):
subHeader = subHeaderList[index] subHeader = subHeaderList[index]
subHeader.idRangeOffset = 0 subHeader.idRangeOffset = 0
for j in range(index): for j in range(index):
@ -511,7 +511,7 @@ class cmap_format_2(CmapSubtable):
subHeader.idRangeOffset = prevSubhead.idRangeOffset - (index-j)*8 subHeader.idRangeOffset = prevSubhead.idRangeOffset - (index-j)*8
subHeader.glyphIndexArray = [] subHeader.glyphIndexArray = []
break break
if subHeader.idRangeOffset == 0: # didn't find one. if subHeader.idRangeOffset == 0: # didn't find one.
subHeader.idRangeOffset = idRangeOffset subHeader.idRangeOffset = idRangeOffset
idRangeOffset = (idRangeOffset - 8) + subHeader.entryCount*2 # one less subheader, one more subArray. idRangeOffset = (idRangeOffset - 8) + subHeader.entryCount*2 # one less subheader, one more subArray.
else: else:
@ -564,17 +564,17 @@ def splitRange(startCode, endCode, cmap):
# to do well with the fonts I tested: none became bigger, many became smaller. # to do well with the fonts I tested: none became bigger, many became smaller.
if startCode == endCode: if startCode == endCode:
return [], [endCode] return [], [endCode]
lastID = cmap[startCode] lastID = cmap[startCode]
lastCode = startCode lastCode = startCode
inOrder = None inOrder = None
orderedBegin = None orderedBegin = None
subRanges = [] subRanges = []
# Gather subranges in which the glyph IDs are consecutive. # Gather subranges in which the glyph IDs are consecutive.
for code in range(startCode + 1, endCode + 1): for code in range(startCode + 1, endCode + 1):
glyphID = cmap[code] glyphID = cmap[code]
if glyphID - 1 == lastID: if glyphID - 1 == lastID:
if inOrder is None or not inOrder: if inOrder is None or not inOrder:
inOrder = 1 inOrder = 1
@ -584,14 +584,14 @@ def splitRange(startCode, endCode, cmap):
inOrder = 0 inOrder = 0
subRanges.append((orderedBegin, lastCode)) subRanges.append((orderedBegin, lastCode))
orderedBegin = None orderedBegin = None
lastID = glyphID lastID = glyphID
lastCode = code lastCode = code
if inOrder: if inOrder:
subRanges.append((orderedBegin, lastCode)) subRanges.append((orderedBegin, lastCode))
assert lastCode == endCode assert lastCode == endCode
# Now filter out those new subranges that would only make the data bigger. # Now filter out those new subranges that would only make the data bigger.
# A new segment cost 8 bytes, not using a new segment costs 2 bytes per # A new segment cost 8 bytes, not using a new segment costs 2 bytes per
# character. # character.
@ -606,15 +606,15 @@ def splitRange(startCode, endCode, cmap):
if (e - b + 1) > threshold: if (e - b + 1) > threshold:
newRanges.append((b, e)) newRanges.append((b, e))
subRanges = newRanges subRanges = newRanges
if not subRanges: if not subRanges:
return [], [endCode] return [], [endCode]
if subRanges[0][0] != startCode: if subRanges[0][0] != startCode:
subRanges.insert(0, (startCode, subRanges[0][0] - 1)) subRanges.insert(0, (startCode, subRanges[0][0] - 1))
if subRanges[-1][1] != endCode: if subRanges[-1][1] != endCode:
subRanges.append((subRanges[-1][1] + 1, endCode)) subRanges.append((subRanges[-1][1] + 1, endCode))
# Fill the "holes" in the segments list -- those are the segments in which # Fill the "holes" in the segments list -- those are the segments in which
# the glyph IDs are _not_ consecutive. # the glyph IDs are _not_ consecutive.
i = 1 i = 1
@ -623,7 +623,7 @@ def splitRange(startCode, endCode, cmap):
subRanges.insert(i, (subRanges[i-1][1] + 1, subRanges[i][0] - 1)) subRanges.insert(i, (subRanges[i-1][1] + 1, subRanges[i][0] - 1))
i = i + 1 i = i + 1
i = i + 1 i = i + 1
# Transform the ranges into startCode/endCode lists. # Transform the ranges into startCode/endCode lists.
start = [] start = []
end = [] end = []
@ -631,13 +631,13 @@ def splitRange(startCode, endCode, cmap):
start.append(b) start.append(b)
end.append(e) end.append(e)
start.pop(0) start.pop(0)
assert len(start) + 1 == len(end) assert len(start) + 1 == len(end)
return start, end return start, end
class cmap_format_4(CmapSubtable): class cmap_format_4(CmapSubtable):
def decompile(self, data, ttFont): def decompile(self, data, ttFont):
# we usually get here indirectly from the subtable __getattr__ function, in which case both args must be None. # we usually get here indirectly from the subtable __getattr__ function, in which case both args must be None.
# If not, someone is calling the subtable decompile() directly, and must provide both args. # If not, someone is calling the subtable decompile() directly, and must provide both args.
@ -651,14 +651,14 @@ class cmap_format_4(CmapSubtable):
struct.unpack(">4H", data[:8]) struct.unpack(">4H", data[:8])
data = data[8:] data = data[8:]
segCount = segCountX2 // 2 segCount = segCountX2 // 2
allCodes = array.array("H") allCodes = array.array("H")
allCodes.fromstring(data) allCodes.fromstring(data)
self.data = data = None self.data = data = None
if sys.byteorder != "big": if sys.byteorder != "big":
allCodes.byteswap() allCodes.byteswap()
# divide the data # divide the data
endCode = allCodes[:segCount] endCode = allCodes[:segCount]
allCodes = allCodes[segCount+1:] # the +1 is skipping the reservedPad field allCodes = allCodes[segCount+1:] # the +1 is skipping the reservedPad field
@ -707,7 +707,7 @@ class cmap_format_4(CmapSubtable):
def compile(self, ttFont): def compile(self, ttFont):
if self.data: if self.data:
return struct.pack(">HHH", self.format, self.length, self.language) + self.data return struct.pack(">HHH", self.format, self.length, self.language) + self.data
charCodes = list(self.cmap.keys()) charCodes = list(self.cmap.keys())
lenCharCodes = len(charCodes) lenCharCodes = len(charCodes)
if lenCharCodes == 0: if lenCharCodes == 0:
@ -737,11 +737,11 @@ class cmap_format_4(CmapSubtable):
gid = ttFont.getGlyphID(name) gid = ttFont.getGlyphID(name)
except: except:
raise KeyError(name) raise KeyError(name)
gids.append(gid) gids.append(gid)
cmap = {} # code:glyphID mapping cmap = {} # code:glyphID mapping
list(map(operator.setitem, [cmap]*len(charCodes), charCodes, gids)) list(map(operator.setitem, [cmap]*len(charCodes), charCodes, gids))
# Build startCode and endCode lists. # Build startCode and endCode lists.
# Split the char codes in ranges of consecutive char codes, then split # Split the char codes in ranges of consecutive char codes, then split
# each range in more ranges of consecutive/not consecutive glyph IDs. # each range in more ranges of consecutive/not consecutive glyph IDs.
@ -763,7 +763,7 @@ class cmap_format_4(CmapSubtable):
endCode.extend(end) endCode.extend(end)
startCode.append(0xffff) startCode.append(0xffff)
endCode.append(0xffff) endCode.append(0xffff)
# build up rest of cruft # build up rest of cruft
idDelta = [] idDelta = []
idRangeOffset = [] idRangeOffset = []
@ -782,12 +782,12 @@ class cmap_format_4(CmapSubtable):
glyphIndexArray.extend(indices) glyphIndexArray.extend(indices)
idDelta.append(1) # 0xffff + 1 == (tadaa!) 0. So this end code maps to .notdef idDelta.append(1) # 0xffff + 1 == (tadaa!) 0. So this end code maps to .notdef
idRangeOffset.append(0) idRangeOffset.append(0)
# Insane. # Insane.
segCount = len(endCode) segCount = len(endCode)
segCountX2 = segCount * 2 segCountX2 = segCount * 2
searchRange, entrySelector, rangeShift = getSearchRange(segCount, 2) searchRange, entrySelector, rangeShift = getSearchRange(segCount, 2)
charCodeArray = array.array("H", endCode + [0] + startCode) charCodeArray = array.array("H", endCode + [0] + startCode)
idDeltaArray = array.array("H", idDelta) idDeltaArray = array.array("H", idDelta)
restArray = array.array("H", idRangeOffset + glyphIndexArray) restArray = array.array("H", idRangeOffset + glyphIndexArray)
@ -798,10 +798,10 @@ class cmap_format_4(CmapSubtable):
data = charCodeArray.tostring() + idDeltaArray.tostring() + restArray.tostring() data = charCodeArray.tostring() + idDeltaArray.tostring() + restArray.tostring()
length = struct.calcsize(cmap_format_4_format) + len(data) length = struct.calcsize(cmap_format_4_format) + len(data)
header = struct.pack(cmap_format_4_format, self.format, length, self.language, header = struct.pack(cmap_format_4_format, self.format, length, self.language,
segCountX2, searchRange, entrySelector, rangeShift) segCountX2, searchRange, entrySelector, rangeShift)
return header + data return header + data
def fromXML(self, name, attrs, content, ttFont): def fromXML(self, name, attrs, content, ttFont):
self.language = safeEval(attrs["language"]) self.language = safeEval(attrs["language"])
if not hasattr(self, "cmap"): if not hasattr(self, "cmap"):
@ -818,7 +818,7 @@ class cmap_format_4(CmapSubtable):
class cmap_format_6(CmapSubtable): class cmap_format_6(CmapSubtable):
def decompile(self, data, ttFont): def decompile(self, data, ttFont):
# we usually get here indirectly from the subtable __getattr__ function, in which case both args must be None. # we usually get here indirectly from the subtable __getattr__ function, in which case both args must be None.
# If not, someone is calling the subtable decompile() directly, and must provide both args. # If not, someone is calling the subtable decompile() directly, and must provide both args.
@ -849,7 +849,7 @@ class cmap_format_6(CmapSubtable):
getGlyphName = self.ttFont.getGlyphName getGlyphName = self.ttFont.getGlyphName
names = list(map(getGlyphName, glyphIndexArray )) names = list(map(getGlyphName, glyphIndexArray ))
list(map(operator.setitem, [cmap]*lenArray, charCodes, names)) list(map(operator.setitem, [cmap]*lenArray, charCodes, names))
def compile(self, ttFont): def compile(self, ttFont):
if self.data: if self.data:
return struct.pack(">HHH", self.format, self.length, self.language) + self.data return struct.pack(">HHH", self.format, self.length, self.language) + self.data
@ -867,10 +867,10 @@ class cmap_format_6(CmapSubtable):
else: else:
data = b"" data = b""
firstCode = 0 firstCode = 0
header = struct.pack(">HHHHH", header = struct.pack(">HHHHH",
6, len(data) + 10, self.language, firstCode, len(codes)) 6, len(data) + 10, self.language, firstCode, len(codes))
return header + data return header + data
def fromXML(self, name, attrs, content, ttFont): def fromXML(self, name, attrs, content, ttFont):
self.language = safeEval(attrs["language"]) self.language = safeEval(attrs["language"])
if not hasattr(self, "cmap"): if not hasattr(self, "cmap"):
@ -887,7 +887,7 @@ class cmap_format_6(CmapSubtable):
class cmap_format_12_or_13(CmapSubtable): class cmap_format_12_or_13(CmapSubtable):
def __init__(self, format): def __init__(self, format):
self.format = format self.format = format
self.reserved = 0 self.reserved = 0
@ -933,12 +933,12 @@ class cmap_format_12_or_13(CmapSubtable):
getGlyphName = self.ttFont.getGlyphName getGlyphName = self.ttFont.getGlyphName
names = list(map(getGlyphName, gids )) names = list(map(getGlyphName, gids ))
list(map(operator.setitem, [cmap]*lenCmap, charCodes, names)) list(map(operator.setitem, [cmap]*lenCmap, charCodes, names))
def compile(self, ttFont): def compile(self, ttFont):
if self.data: if self.data:
return struct.pack(">HHLLL", self.format, self.reserved, self.length, self.language, self.nGroups) + self.data return struct.pack(">HHLLL", self.format, self.reserved, self.length, self.language, self.nGroups) + self.data
charCodes = list(self.cmap.keys()) charCodes = list(self.cmap.keys())
lenCharCodes = len(charCodes) lenCharCodes = len(charCodes)
names = list(self.cmap.values()) names = list(self.cmap.values())
nameMap = ttFont.getReverseGlyphMap() nameMap = ttFont.getReverseGlyphMap()
try: try:
@ -963,7 +963,7 @@ class cmap_format_12_or_13(CmapSubtable):
raise KeyError(name) raise KeyError(name)
gids.append(gid) gids.append(gid)
cmap = {} # code:glyphID mapping cmap = {} # code:glyphID mapping
list(map(operator.setitem, [cmap]*len(charCodes), charCodes, gids)) list(map(operator.setitem, [cmap]*len(charCodes), charCodes, gids))
@ -992,7 +992,7 @@ class cmap_format_12_or_13(CmapSubtable):
lengthSubtable = len(data) +16 lengthSubtable = len(data) +16
assert len(data) == (nGroups*12) == (lengthSubtable-16) assert len(data) == (nGroups*12) == (lengthSubtable-16)
return struct.pack(">HHLLL", self.format, self.reserved, lengthSubtable, self.language, nGroups) + data return struct.pack(">HHLLL", self.format, self.reserved, lengthSubtable, self.language, nGroups) + data
def toXML(self, writer, ttFont): def toXML(self, writer, ttFont):
writer.begintag(self.__class__.__name__, [ writer.begintag(self.__class__.__name__, [
("platformID", self.platformID), ("platformID", self.platformID),
@ -1008,7 +1008,7 @@ class cmap_format_12_or_13(CmapSubtable):
self._writeCodes(codes, writer) self._writeCodes(codes, writer)
writer.endtag(self.__class__.__name__) writer.endtag(self.__class__.__name__)
writer.newline() writer.newline()
def fromXML(self, name, attrs, content, ttFont): def fromXML(self, name, attrs, content, ttFont):
self.format = safeEval(attrs["format"]) self.format = safeEval(attrs["format"])
self.reserved = safeEval(attrs["reserved"]) self.reserved = safeEval(attrs["reserved"])
@ -1079,12 +1079,12 @@ class cmap_format_14(CmapSubtable):
else: else:
assert (data is None and ttFont is None), "Need both data and ttFont arguments" assert (data is None and ttFont is None), "Need both data and ttFont arguments"
data = self.data data = self.data
self.cmap = {} # so that clients that expect this to exist in a cmap table won't fail. self.cmap = {} # so that clients that expect this to exist in a cmap table won't fail.
uvsDict = {} uvsDict = {}
recOffset = 0 recOffset = 0
for n in range(self.numVarSelectorRecords): for n in range(self.numVarSelectorRecords):
uvs, defOVSOffset, nonDefUVSOffset = struct.unpack(">3sLL", data[recOffset:recOffset +11]) uvs, defOVSOffset, nonDefUVSOffset = struct.unpack(">3sLL", data[recOffset:recOffset +11])
recOffset += 11 recOffset += 11
varUVS = cvtToUVS(uvs) varUVS = cvtToUVS(uvs)
if defOVSOffset: if defOVSOffset:
@ -1103,7 +1103,7 @@ class cmap_format_14(CmapSubtable):
uvsDict[varUVS].extend(localUVList) uvsDict[varUVS].extend(localUVList)
except KeyError: except KeyError:
uvsDict[varUVS] = list(localUVList) uvsDict[varUVS] = list(localUVList)
if nonDefUVSOffset: if nonDefUVSOffset:
startOffset = nonDefUVSOffset - 10 startOffset = nonDefUVSOffset - 10
numRecs, = struct.unpack(">L", data[startOffset:startOffset+4]) numRecs, = struct.unpack(">L", data[startOffset:startOffset+4])
@ -1119,9 +1119,9 @@ class cmap_format_14(CmapSubtable):
uvsDict[varUVS].extend(localUVList) uvsDict[varUVS].extend(localUVList)
except KeyError: except KeyError:
uvsDict[varUVS] = localUVList uvsDict[varUVS] = localUVList
self.uvsDict = uvsDict self.uvsDict = uvsDict
def toXML(self, writer, ttFont): def toXML(self, writer, ttFont):
writer.begintag(self.__class__.__name__, [ writer.begintag(self.__class__.__name__, [
("platformID", self.platformID), ("platformID", self.platformID),
@ -1154,7 +1154,7 @@ class cmap_format_14(CmapSubtable):
self.cmap = {} # so that clients that expect this to exist in a cmap table won't fail. self.cmap = {} # so that clients that expect this to exist in a cmap table won't fail.
if not hasattr(self, "uvsDict"): if not hasattr(self, "uvsDict"):
self.uvsDict = {} self.uvsDict = {}
uvsDict = self.uvsDict uvsDict = self.uvsDict
for element in content: for element in content:
if not isinstance(element, tuple): if not isinstance(element, tuple):
@ -1201,7 +1201,7 @@ class cmap_format_14(CmapSubtable):
lastUV = defEntry lastUV = defEntry
defRecs.append(rec) defRecs.append(rec)
cnt = 0 cnt = 0
rec = struct.pack(">3sB", cvtFromUVS(lastUV), cnt) rec = struct.pack(">3sB", cvtFromUVS(lastUV), cnt)
defRecs.append(rec) defRecs.append(rec)
@ -1226,20 +1226,20 @@ class cmap_format_14(CmapSubtable):
data.append(ndrec) data.append(ndrec)
else: else:
nonDefUVSOffset = 0 nonDefUVSOffset = 0
vrec = struct.pack(">3sLL", cvtFromUVS(uvs), defOVSOffset, nonDefUVSOffset) vrec = struct.pack(">3sLL", cvtFromUVS(uvs), defOVSOffset, nonDefUVSOffset)
varSelectorRecords.append(vrec) varSelectorRecords.append(vrec)
data = bytesjoin(varSelectorRecords) + bytesjoin(data) data = bytesjoin(varSelectorRecords) + bytesjoin(data)
self.length = 10 + len(data) self.length = 10 + len(data)
headerdata = struct.pack(">HLL", self.format, self.length, self.numVarSelectorRecords) headerdata = struct.pack(">HLL", self.format, self.length, self.numVarSelectorRecords)
self.data = headerdata + data self.data = headerdata + data
return self.data return self.data
class cmap_format_unknown(CmapSubtable): class cmap_format_unknown(CmapSubtable):
def toXML(self, writer, ttFont): def toXML(self, writer, ttFont):
cmapName = self.__class__.__name__[:12] + str(self.format) cmapName = self.__class__.__name__[:12] + str(self.format)
writer.begintag(cmapName, [ writer.begintag(cmapName, [
@ -1250,15 +1250,15 @@ class cmap_format_unknown(CmapSubtable):
writer.dumphex(self.data) writer.dumphex(self.data)
writer.endtag(cmapName) writer.endtag(cmapName)
writer.newline() writer.newline()
def fromXML(self, name, attrs, content, ttFont): def fromXML(self, name, attrs, content, ttFont):
self.data = readHex(content) self.data = readHex(content)
self.cmap = {} self.cmap = {}
def decompileHeader(self, data, ttFont): def decompileHeader(self, data, ttFont):
self.language = 0 # dummy value self.language = 0 # dummy value
self.data = data self.data = data
def decompile(self, data, ttFont): def decompile(self, data, ttFont):
# we usually get here indirectly from the subtable __getattr__ function, in which case both args must be None. # we usually get here indirectly from the subtable __getattr__ function, in which case both args must be None.
# If not, someone is calling the subtable decompile() directly, and must provide both args. # If not, someone is calling the subtable decompile() directly, and must provide both args.

View File

@ -6,26 +6,26 @@ import sys
import array import array
class table__c_v_t(DefaultTable.DefaultTable): class table__c_v_t(DefaultTable.DefaultTable):
def decompile(self, data, ttFont): def decompile(self, data, ttFont):
values = array.array("h") values = array.array("h")
values.fromstring(data) values.fromstring(data)
if sys.byteorder != "big": if sys.byteorder != "big":
values.byteswap() values.byteswap()
self.values = values self.values = values
def compile(self, ttFont): def compile(self, ttFont):
values = self.values[:] values = self.values[:]
if sys.byteorder != "big": if sys.byteorder != "big":
values.byteswap() values.byteswap()
return values.tostring() return values.tostring()
def toXML(self, writer, ttFont): def toXML(self, writer, ttFont):
for i in range(len(self.values)): for i in range(len(self.values)):
value = self.values[i] value = self.values[i]
writer.simpletag("cv", value=value, index=i) writer.simpletag("cv", value=value, index=i)
writer.newline() writer.newline()
def fromXML(self, name, attrs, content, ttFont): def fromXML(self, name, attrs, content, ttFont):
if not hasattr(self, "values"): if not hasattr(self, "values"):
self.values = array.array("h") self.values = array.array("h")
@ -35,15 +35,15 @@ class table__c_v_t(DefaultTable.DefaultTable):
for i in range(1 + index - len(self.values)): for i in range(1 + index - len(self.values)):
self.values.append(0) self.values.append(0)
self.values[index] = value self.values[index] = value
def __len__(self): def __len__(self):
return len(self.values) return len(self.values)
def __getitem__(self, index): def __getitem__(self, index):
return self.values[index] return self.values[index]
def __setitem__(self, index, value): def __setitem__(self, index, value):
self.values[index] = value self.values[index] = value
def __delitem__(self, index): def __delitem__(self, index):
del self.values[index] del self.values[index]

View File

@ -4,23 +4,23 @@ from . import DefaultTable
from . import ttProgram from . import ttProgram
class table__f_p_g_m(DefaultTable.DefaultTable): class table__f_p_g_m(DefaultTable.DefaultTable):
def decompile(self, data, ttFont): def decompile(self, data, ttFont):
program = ttProgram.Program() program = ttProgram.Program()
program.fromBytecode(data) program.fromBytecode(data)
self.program = program self.program = program
def compile(self, ttFont): def compile(self, ttFont):
return self.program.getBytecode() return self.program.getBytecode()
def toXML(self, writer, ttFont): def toXML(self, writer, ttFont):
self.program.toXML(writer, ttFont) self.program.toXML(writer, ttFont)
writer.newline() writer.newline()
def fromXML(self, name, attrs, content, ttFont): def fromXML(self, name, attrs, content, ttFont):
program = ttProgram.Program() program = ttProgram.Program()
program.fromXML(name, attrs, content, ttFont) program.fromXML(name, attrs, content, ttFont)
self.program = program self.program = program
def __len__(self): def __len__(self):
return len(self.program) return len(self.program)

View File

@ -11,7 +11,7 @@ GASP_DOGRAY = 0x0002
GASP_GRIDFIT = 0x0001 GASP_GRIDFIT = 0x0001
class table__g_a_s_p(DefaultTable.DefaultTable): class table__g_a_s_p(DefaultTable.DefaultTable):
def decompile(self, data, ttFont): def decompile(self, data, ttFont):
self.version, numRanges = struct.unpack(">HH", data[:4]) self.version, numRanges = struct.unpack(">HH", data[:4])
assert 0 <= self.version <= 1, "unknown 'gasp' format: %s" % self.version assert 0 <= self.version <= 1, "unknown 'gasp' format: %s" % self.version
@ -22,7 +22,7 @@ class table__g_a_s_p(DefaultTable.DefaultTable):
self.gaspRange[int(rangeMaxPPEM)] = int(rangeGaspBehavior) self.gaspRange[int(rangeMaxPPEM)] = int(rangeGaspBehavior)
data = data[4:] data = data[4:]
assert not data, "too much data" assert not data, "too much data"
def compile(self, ttFont): def compile(self, ttFont):
version = 0 # ignore self.version version = 0 # ignore self.version
numRanges = len(self.gaspRange) numRanges = len(self.gaspRange)
@ -34,7 +34,7 @@ class table__g_a_s_p(DefaultTable.DefaultTable):
version = 1 version = 1
data = struct.pack(">HH", version, numRanges) + data data = struct.pack(">HH", version, numRanges) + data
return data return data
def toXML(self, writer, ttFont): def toXML(self, writer, ttFont):
items = sorted(self.gaspRange.items()) items = sorted(self.gaspRange.items())
for rangeMaxPPEM, rangeGaspBehavior in items: for rangeMaxPPEM, rangeGaspBehavior in items:
@ -42,7 +42,7 @@ class table__g_a_s_p(DefaultTable.DefaultTable):
("rangeMaxPPEM", rangeMaxPPEM), ("rangeMaxPPEM", rangeMaxPPEM),
("rangeGaspBehavior", rangeGaspBehavior)]) ("rangeGaspBehavior", rangeGaspBehavior)])
writer.newline() writer.newline()
def fromXML(self, name, attrs, content, ttFont): def fromXML(self, name, attrs, content, ttFont):
if name != "gaspRange": if name != "gaspRange":
return return

View File

@ -16,20 +16,20 @@ import array
import warnings import warnings
# #
# The Apple and MS rasterizers behave differently for # The Apple and MS rasterizers behave differently for
# scaled composite components: one does scale first and then translate # scaled composite components: one does scale first and then translate
# and the other does it vice versa. MS defined some flags to indicate # and the other does it vice versa. MS defined some flags to indicate
# the difference, but it seems nobody actually _sets_ those flags. # the difference, but it seems nobody actually _sets_ those flags.
# #
# Funny thing: Apple seems to _only_ do their thing in the # Funny thing: Apple seems to _only_ do their thing in the
# WE_HAVE_A_SCALE (eg. Chicago) case, and not when it's WE_HAVE_AN_X_AND_Y_SCALE # WE_HAVE_A_SCALE (eg. Chicago) case, and not when it's WE_HAVE_AN_X_AND_Y_SCALE
# (eg. Charcoal)... # (eg. Charcoal)...
# #
SCALE_COMPONENT_OFFSET_DEFAULT = 0 # 0 == MS, 1 == Apple SCALE_COMPONENT_OFFSET_DEFAULT = 0 # 0 == MS, 1 == Apple
class table__g_l_y_f(DefaultTable.DefaultTable): class table__g_l_y_f(DefaultTable.DefaultTable):
def decompile(self, data, ttFont): def decompile(self, data, ttFont):
loca = ttFont['loca'] loca = ttFont['loca']
last = int(loca[0]) last = int(loca[0])
@ -57,7 +57,7 @@ class table__g_l_y_f(DefaultTable.DefaultTable):
if ttFont.lazy is False: # Be lazy for None and True if ttFont.lazy is False: # Be lazy for None and True
for glyph in self.glyphs.values(): for glyph in self.glyphs.values():
glyph.expand(self) glyph.expand(self)
def compile(self, ttFont): def compile(self, ttFont):
if not hasattr(self, "glyphOrder"): if not hasattr(self, "glyphOrder"):
self.glyphOrder = ttFont.getGlyphOrder() self.glyphOrder = ttFont.getGlyphOrder()
@ -92,7 +92,7 @@ class table__g_l_y_f(DefaultTable.DefaultTable):
ttFont['loca'].set(locations) ttFont['loca'].set(locations)
ttFont['maxp'].numGlyphs = len(self.glyphs) ttFont['maxp'].numGlyphs = len(self.glyphs)
return data return data
def toXML(self, writer, ttFont, progress=None): def toXML(self, writer, ttFont, progress=None):
writer.newline() writer.newline()
glyphNames = ttFont.getGlyphNames() glyphNames = ttFont.getGlyphNames()
@ -125,7 +125,7 @@ class table__g_l_y_f(DefaultTable.DefaultTable):
writer.comment("contains no outline data") writer.comment("contains no outline data")
writer.newline() writer.newline()
writer.newline() writer.newline()
def fromXML(self, name, attrs, content, ttFont): def fromXML(self, name, attrs, content, ttFont):
if name != "TTGlyph": if name != "TTGlyph":
return return
@ -147,39 +147,39 @@ class table__g_l_y_f(DefaultTable.DefaultTable):
glyph.fromXML(name, attrs, content, ttFont) glyph.fromXML(name, attrs, content, ttFont)
if not ttFont.recalcBBoxes: if not ttFont.recalcBBoxes:
glyph.compact(self, 0) glyph.compact(self, 0)
def setGlyphOrder(self, glyphOrder): def setGlyphOrder(self, glyphOrder):
self.glyphOrder = glyphOrder self.glyphOrder = glyphOrder
def getGlyphName(self, glyphID): def getGlyphName(self, glyphID):
return self.glyphOrder[glyphID] return self.glyphOrder[glyphID]
def getGlyphID(self, glyphName): def getGlyphID(self, glyphName):
# XXX optimize with reverse dict!!! # XXX optimize with reverse dict!!!
return self.glyphOrder.index(glyphName) return self.glyphOrder.index(glyphName)
def keys(self): def keys(self):
return self.glyphs.keys() return self.glyphs.keys()
def has_key(self, glyphName): def has_key(self, glyphName):
return glyphName in self.glyphs return glyphName in self.glyphs
__contains__ = has_key __contains__ = has_key
def __getitem__(self, glyphName): def __getitem__(self, glyphName):
glyph = self.glyphs[glyphName] glyph = self.glyphs[glyphName]
glyph.expand(self) glyph.expand(self)
return glyph return glyph
def __setitem__(self, glyphName, glyph): def __setitem__(self, glyphName, glyph):
self.glyphs[glyphName] = glyph self.glyphs[glyphName] = glyph
if glyphName not in self.glyphOrder: if glyphName not in self.glyphOrder:
self.glyphOrder.append(glyphName) self.glyphOrder.append(glyphName)
def __delitem__(self, glyphName): def __delitem__(self, glyphName):
del self.glyphs[glyphName] del self.glyphs[glyphName]
self.glyphOrder.remove(glyphName) self.glyphOrder.remove(glyphName)
def __len__(self): def __len__(self):
assert len(self.glyphOrder) == len(self.glyphs) assert len(self.glyphOrder) == len(self.glyphs)
return len(self.glyphs) return len(self.glyphs)
@ -267,35 +267,35 @@ def flagEncodeCoords(flag, x, y, xBytes, yBytes):
flagEncodeCoord(flag, flagYsame|flagYShort, y, yBytes) flagEncodeCoord(flag, flagYsame|flagYShort, y, yBytes)
ARG_1_AND_2_ARE_WORDS = 0x0001 # if set args are words otherwise they are bytes ARG_1_AND_2_ARE_WORDS = 0x0001 # if set args are words otherwise they are bytes
ARGS_ARE_XY_VALUES = 0x0002 # if set args are xy values, otherwise they are points ARGS_ARE_XY_VALUES = 0x0002 # if set args are xy values, otherwise they are points
ROUND_XY_TO_GRID = 0x0004 # for the xy values if above is true ROUND_XY_TO_GRID = 0x0004 # for the xy values if above is true
WE_HAVE_A_SCALE = 0x0008 # Sx = Sy, otherwise scale == 1.0 WE_HAVE_A_SCALE = 0x0008 # Sx = Sy, otherwise scale == 1.0
NON_OVERLAPPING = 0x0010 # set to same value for all components (obsolete!) NON_OVERLAPPING = 0x0010 # set to same value for all components (obsolete!)
MORE_COMPONENTS = 0x0020 # indicates at least one more glyph after this one MORE_COMPONENTS = 0x0020 # indicates at least one more glyph after this one
WE_HAVE_AN_X_AND_Y_SCALE = 0x0040 # Sx, Sy WE_HAVE_AN_X_AND_Y_SCALE = 0x0040 # Sx, Sy
WE_HAVE_A_TWO_BY_TWO = 0x0080 # t00, t01, t10, t11 WE_HAVE_A_TWO_BY_TWO = 0x0080 # t00, t01, t10, t11
WE_HAVE_INSTRUCTIONS = 0x0100 # instructions follow WE_HAVE_INSTRUCTIONS = 0x0100 # instructions follow
USE_MY_METRICS = 0x0200 # apply these metrics to parent glyph USE_MY_METRICS = 0x0200 # apply these metrics to parent glyph
OVERLAP_COMPOUND = 0x0400 # used by Apple in GX fonts OVERLAP_COMPOUND = 0x0400 # used by Apple in GX fonts
SCALED_COMPONENT_OFFSET = 0x0800 # composite designed to have the component offset scaled (designed for Apple) SCALED_COMPONENT_OFFSET = 0x0800 # composite designed to have the component offset scaled (designed for Apple)
UNSCALED_COMPONENT_OFFSET = 0x1000 # composite designed not to have the component offset scaled (designed for MS) UNSCALED_COMPONENT_OFFSET = 0x1000 # composite designed not to have the component offset scaled (designed for MS)
class Glyph(object): class Glyph(object):
def __init__(self, data=""): def __init__(self, data=""):
if not data: if not data:
# empty char # empty char
self.numberOfContours = 0 self.numberOfContours = 0
return return
self.data = data self.data = data
def compact(self, glyfTable, recalcBBoxes=True): def compact(self, glyfTable, recalcBBoxes=True):
data = self.compile(glyfTable, recalcBBoxes) data = self.compile(glyfTable, recalcBBoxes)
self.__dict__.clear() self.__dict__.clear()
self.data = data self.data = data
def expand(self, glyfTable): def expand(self, glyfTable):
if not hasattr(self, "data"): if not hasattr(self, "data"):
# already unpacked # already unpacked
@ -310,7 +310,7 @@ class Glyph(object):
self.decompileComponents(data, glyfTable) self.decompileComponents(data, glyfTable)
else: else:
self.decompileCoordinates(data) self.decompileCoordinates(data)
def compile(self, glyfTable, recalcBBoxes=True): def compile(self, glyfTable, recalcBBoxes=True):
if hasattr(self, "data"): if hasattr(self, "data"):
return self.data return self.data
@ -324,7 +324,7 @@ class Glyph(object):
else: else:
data = data + self.compileCoordinates() data = data + self.compileCoordinates()
return data return data
def toXML(self, writer, ttFont): def toXML(self, writer, ttFont):
if self.isComposite(): if self.isComposite():
for compo in self.components: for compo in self.components:
@ -341,7 +341,7 @@ class Glyph(object):
writer.newline() writer.newline()
for j in range(last, self.endPtsOfContours[i] + 1): for j in range(last, self.endPtsOfContours[i] + 1):
writer.simpletag("pt", [ writer.simpletag("pt", [
("x", self.coordinates[j][0]), ("x", self.coordinates[j][0]),
("y", self.coordinates[j][1]), ("y", self.coordinates[j][1]),
("on", self.flags[j] & flagOnCurve)]) ("on", self.flags[j] & flagOnCurve)])
writer.newline() writer.newline()
@ -353,7 +353,7 @@ class Glyph(object):
self.program.toXML(writer, ttFont) self.program.toXML(writer, ttFont)
writer.endtag("instructions") writer.endtag("instructions")
writer.newline() writer.newline()
def fromXML(self, name, attrs, content, ttFont): def fromXML(self, name, attrs, content, ttFont):
if name == "contour": if name == "contour":
if self.numberOfContours < 0: if self.numberOfContours < 0:
@ -394,7 +394,7 @@ class Glyph(object):
continue continue
name, attrs, content = element name, attrs, content = element
self.program.fromXML(name, attrs, content, ttFont) self.program.fromXML(name, attrs, content, ttFont)
def getCompositeMaxpValues(self, glyfTable, maxComponentDepth=1): def getCompositeMaxpValues(self, glyfTable, maxComponentDepth=1):
assert self.isComposite() assert self.isComposite()
nContours = 0 nContours = 0
@ -411,11 +411,11 @@ class Glyph(object):
nPoints = nPoints + nP nPoints = nPoints + nP
nContours = nContours + nC nContours = nContours + nC
return nPoints, nContours, maxComponentDepth return nPoints, nContours, maxComponentDepth
def getMaxpValues(self): def getMaxpValues(self):
assert self.numberOfContours > 0 assert self.numberOfContours > 0
return len(self.coordinates), len(self.endPtsOfContours) return len(self.coordinates), len(self.endPtsOfContours)
def decompileComponents(self, data, glyfTable): def decompileComponents(self, data, glyfTable):
self.components = [] self.components = []
more = 1 more = 1
@ -433,16 +433,16 @@ class Glyph(object):
data = data[numInstructions:] data = data[numInstructions:]
if len(data) >= 4: if len(data) >= 4:
warnings.warn("too much glyph data at the end of composite glyph: %d excess bytes" % len(data)) warnings.warn("too much glyph data at the end of composite glyph: %d excess bytes" % len(data))
def decompileCoordinates(self, data): def decompileCoordinates(self, data):
endPtsOfContours = array.array("h") endPtsOfContours = array.array("h")
endPtsOfContours.fromstring(data[:2*self.numberOfContours]) endPtsOfContours.fromstring(data[:2*self.numberOfContours])
if sys.byteorder != "big": if sys.byteorder != "big":
endPtsOfContours.byteswap() endPtsOfContours.byteswap()
self.endPtsOfContours = endPtsOfContours.tolist() self.endPtsOfContours = endPtsOfContours.tolist()
data = data[2*self.numberOfContours:] data = data[2*self.numberOfContours:]
instructionLength, = struct.unpack(">h", data[:2]) instructionLength, = struct.unpack(">h", data[:2])
data = data[2:] data = data[2:]
self.program = ttProgram.Program() self.program = ttProgram.Program()
@ -451,7 +451,7 @@ class Glyph(object):
nCoordinates = self.endPtsOfContours[-1] + 1 nCoordinates = self.endPtsOfContours[-1] + 1
flags, xCoordinates, yCoordinates = \ flags, xCoordinates, yCoordinates = \
self.decompileCoordinatesRaw(nCoordinates, data) self.decompileCoordinatesRaw(nCoordinates, data)
# fill in repetitions and apply signs # fill in repetitions and apply signs
self.coordinates = coordinates = GlyphCoordinates.zeros(nCoordinates) self.coordinates = coordinates = GlyphCoordinates.zeros(nCoordinates)
xIndex = 0 xIndex = 0
@ -528,7 +528,7 @@ class Glyph(object):
xCoordinates = struct.unpack(xFormat, data[:xDataLen]) xCoordinates = struct.unpack(xFormat, data[:xDataLen])
yCoordinates = struct.unpack(yFormat, data[xDataLen:xDataLen+yDataLen]) yCoordinates = struct.unpack(yFormat, data[xDataLen:xDataLen+yDataLen])
return flags, xCoordinates, yCoordinates return flags, xCoordinates, yCoordinates
def compileComponents(self, glyfTable): def compileComponents(self, glyfTable):
data = b"" data = b""
lastcomponent = len(self.components) - 1 lastcomponent = len(self.components) - 1
@ -544,7 +544,7 @@ class Glyph(object):
instructions = self.program.getBytecode() instructions = self.program.getBytecode()
data = data + struct.pack(">h", len(instructions)) + instructions data = data + struct.pack(">h", len(instructions)) + instructions
return data return data
def compileCoordinates(self): def compileCoordinates(self):
assert len(self.coordinates) == len(self.flags) assert len(self.coordinates) == len(self.flags)
data = [] data = []
@ -676,7 +676,7 @@ class Glyph(object):
compressedYs = compressedYs.tostring() compressedYs = compressedYs.tostring()
return (compressedFlags, compressedXs, compressedYs) return (compressedFlags, compressedXs, compressedYs)
def recalcBounds(self, glyfTable): def recalcBounds(self, glyfTable):
coords, endPts, flags = self.getCoordinates(glyfTable) coords, endPts, flags = self.getCoordinates(glyfTable)
if len(coords) > 0: if len(coords) > 0:
@ -734,19 +734,19 @@ class Glyph(object):
self.xMin, self.yMin, self.xMax, self.yMax = calcIntBounds(coords) self.xMin, self.yMin, self.xMax, self.yMax = calcIntBounds(coords)
else: else:
self.xMin, self.yMin, self.xMax, self.yMax = (0, 0, 0, 0) self.xMin, self.yMin, self.xMax, self.yMax = (0, 0, 0, 0)
def isComposite(self): def isComposite(self):
"""Can be called on compact or expanded glyph.""" """Can be called on compact or expanded glyph."""
if hasattr(self, "data") and self.data: if hasattr(self, "data") and self.data:
return struct.unpack(">h", self.data[:2])[0] == -1 return struct.unpack(">h", self.data[:2])[0] == -1
else: else:
return self.numberOfContours == -1 return self.numberOfContours == -1
def __getitem__(self, componentIndex): def __getitem__(self, componentIndex):
if not self.isComposite(): if not self.isComposite():
raise ttLib.TTLibError("can't use glyph as sequence") raise ttLib.TTLibError("can't use glyph as sequence")
return self.components[componentIndex] return self.components[componentIndex]
def getCoordinates(self, glyfTable): def getCoordinates(self, glyfTable):
if self.numberOfContours > 0: if self.numberOfContours > 0:
return self.coordinates, self.endPtsOfContours, self.flags return self.coordinates, self.endPtsOfContours, self.flags
@ -765,7 +765,7 @@ class Glyph(object):
move = x1-x2, y1-y2 move = x1-x2, y1-y2
else: else:
move = compo.x, compo.y move = compo.x, compo.y
coordinates = GlyphCoordinates(coordinates) coordinates = GlyphCoordinates(coordinates)
if not hasattr(compo, "transform"): if not hasattr(compo, "transform"):
coordinates.translate(move) coordinates.translate(move)
@ -962,14 +962,14 @@ class Glyph(object):
class GlyphComponent(object): class GlyphComponent(object):
def __init__(self): def __init__(self):
pass pass
def getComponentInfo(self): def getComponentInfo(self):
"""Return the base glyph name and a transform.""" """Return the base glyph name and a transform."""
# XXX Ignoring self.firstPt & self.lastpt for now: I need to implement # XXX Ignoring self.firstPt & self.lastpt for now: I need to implement
# something equivalent in fontTools.objects.glyph (I'd rather not # something equivalent in fontTools.objects.glyph (I'd rather not
# convert it to an absolute offset, since it is valuable information). # convert it to an absolute offset, since it is valuable information).
# This method will now raise "AttributeError: x" on glyphs that use # This method will now raise "AttributeError: x" on glyphs that use
# this TT feature. # this TT feature.
@ -979,7 +979,7 @@ class GlyphComponent(object):
else: else:
trans = (1, 0, 0, 1, self.x, self.y) trans = (1, 0, 0, 1, self.x, self.y)
return self.glyphName, trans return self.glyphName, trans
def decompile(self, data, glyfTable): def decompile(self, data, glyfTable):
flags, glyphID = struct.unpack(">HH", data[:4]) flags, glyphID = struct.unpack(">HH", data[:4])
self.flags = int(flags) self.flags = int(flags)
@ -987,7 +987,7 @@ class GlyphComponent(object):
self.glyphName = glyfTable.getGlyphName(int(glyphID)) self.glyphName = glyfTable.getGlyphName(int(glyphID))
#print ">>", reprflag(self.flags) #print ">>", reprflag(self.flags)
data = data[4:] data = data[4:]
if self.flags & ARG_1_AND_2_ARE_WORDS: if self.flags & ARG_1_AND_2_ARE_WORDS:
if self.flags & ARGS_ARE_XY_VALUES: if self.flags & ARGS_ARE_XY_VALUES:
self.x, self.y = struct.unpack(">hh", data[:4]) self.x, self.y = struct.unpack(">hh", data[:4])
@ -1002,7 +1002,7 @@ class GlyphComponent(object):
x, y = struct.unpack(">BB", data[:2]) x, y = struct.unpack(">BB", data[:2])
self.firstPt, self.secondPt = int(x), int(y) self.firstPt, self.secondPt = int(x), int(y)
data = data[2:] data = data[2:]
if self.flags & WE_HAVE_A_SCALE: if self.flags & WE_HAVE_A_SCALE:
scale, = struct.unpack(">h", data[:2]) scale, = struct.unpack(">h", data[:2])
self.transform = [[fi2fl(scale,14), 0], [0, fi2fl(scale,14)]] # fixed 2.14 self.transform = [[fi2fl(scale,14), 0], [0, fi2fl(scale,14)]] # fixed 2.14
@ -1012,30 +1012,30 @@ class GlyphComponent(object):
self.transform = [[fi2fl(xscale,14), 0], [0, fi2fl(yscale,14)]] # fixed 2.14 self.transform = [[fi2fl(xscale,14), 0], [0, fi2fl(yscale,14)]] # fixed 2.14
data = data[4:] data = data[4:]
elif self.flags & WE_HAVE_A_TWO_BY_TWO: elif self.flags & WE_HAVE_A_TWO_BY_TWO:
(xscale, scale01, (xscale, scale01,
scale10, yscale) = struct.unpack(">hhhh", data[:8]) scale10, yscale) = struct.unpack(">hhhh", data[:8])
self.transform = [[fi2fl(xscale,14), fi2fl(scale01,14)], self.transform = [[fi2fl(xscale,14), fi2fl(scale01,14)],
[fi2fl(scale10,14), fi2fl(yscale,14)]] # fixed 2.14 [fi2fl(scale10,14), fi2fl(yscale,14)]] # fixed 2.14
data = data[8:] data = data[8:]
more = self.flags & MORE_COMPONENTS more = self.flags & MORE_COMPONENTS
haveInstructions = self.flags & WE_HAVE_INSTRUCTIONS haveInstructions = self.flags & WE_HAVE_INSTRUCTIONS
self.flags = self.flags & (ROUND_XY_TO_GRID | USE_MY_METRICS | self.flags = self.flags & (ROUND_XY_TO_GRID | USE_MY_METRICS |
SCALED_COMPONENT_OFFSET | UNSCALED_COMPONENT_OFFSET | SCALED_COMPONENT_OFFSET | UNSCALED_COMPONENT_OFFSET |
NON_OVERLAPPING) NON_OVERLAPPING)
return more, haveInstructions, data return more, haveInstructions, data
def compile(self, more, haveInstructions, glyfTable): def compile(self, more, haveInstructions, glyfTable):
data = b"" data = b""
# reset all flags we will calculate ourselves # reset all flags we will calculate ourselves
flags = self.flags & (ROUND_XY_TO_GRID | USE_MY_METRICS | flags = self.flags & (ROUND_XY_TO_GRID | USE_MY_METRICS |
SCALED_COMPONENT_OFFSET | UNSCALED_COMPONENT_OFFSET | SCALED_COMPONENT_OFFSET | UNSCALED_COMPONENT_OFFSET |
NON_OVERLAPPING) NON_OVERLAPPING)
if more: if more:
flags = flags | MORE_COMPONENTS flags = flags | MORE_COMPONENTS
if haveInstructions: if haveInstructions:
flags = flags | WE_HAVE_INSTRUCTIONS flags = flags | WE_HAVE_INSTRUCTIONS
if hasattr(self, "firstPt"): if hasattr(self, "firstPt"):
if (0 <= self.firstPt <= 255) and (0 <= self.secondPt <= 255): if (0 <= self.firstPt <= 255) and (0 <= self.secondPt <= 255):
data = data + struct.pack(">BB", self.firstPt, self.secondPt) data = data + struct.pack(">BB", self.firstPt, self.secondPt)
@ -1049,33 +1049,33 @@ class GlyphComponent(object):
else: else:
data = data + struct.pack(">hh", self.x, self.y) data = data + struct.pack(">hh", self.x, self.y)
flags = flags | ARG_1_AND_2_ARE_WORDS flags = flags | ARG_1_AND_2_ARE_WORDS
if hasattr(self, "transform"): if hasattr(self, "transform"):
transform = [[fl2fi(x,14) for x in row] for row in self.transform] transform = [[fl2fi(x,14) for x in row] for row in self.transform]
if transform[0][1] or transform[1][0]: if transform[0][1] or transform[1][0]:
flags = flags | WE_HAVE_A_TWO_BY_TWO flags = flags | WE_HAVE_A_TWO_BY_TWO
data = data + struct.pack(">hhhh", data = data + struct.pack(">hhhh",
transform[0][0], transform[0][1], transform[0][0], transform[0][1],
transform[1][0], transform[1][1]) transform[1][0], transform[1][1])
elif transform[0][0] != transform[1][1]: elif transform[0][0] != transform[1][1]:
flags = flags | WE_HAVE_AN_X_AND_Y_SCALE flags = flags | WE_HAVE_AN_X_AND_Y_SCALE
data = data + struct.pack(">hh", data = data + struct.pack(">hh",
transform[0][0], transform[1][1]) transform[0][0], transform[1][1])
else: else:
flags = flags | WE_HAVE_A_SCALE flags = flags | WE_HAVE_A_SCALE
data = data + struct.pack(">h", data = data + struct.pack(">h",
transform[0][0]) transform[0][0])
glyphID = glyfTable.getGlyphID(self.glyphName) glyphID = glyfTable.getGlyphID(self.glyphName)
return struct.pack(">HH", flags, glyphID) + data return struct.pack(">HH", flags, glyphID) + data
def toXML(self, writer, ttFont): def toXML(self, writer, ttFont):
attrs = [("glyphName", self.glyphName)] attrs = [("glyphName", self.glyphName)]
if not hasattr(self, "firstPt"): if not hasattr(self, "firstPt"):
attrs = attrs + [("x", self.x), ("y", self.y)] attrs = attrs + [("x", self.x), ("y", self.y)]
else: else:
attrs = attrs + [("firstPt", self.firstPt), ("secondPt", self.secondPt)] attrs = attrs + [("firstPt", self.firstPt), ("secondPt", self.secondPt)]
if hasattr(self, "transform"): if hasattr(self, "transform"):
transform = self.transform transform = self.transform
if transform[0][1] or transform[1][0]: if transform[0][1] or transform[1][0]:
@ -1092,7 +1092,7 @@ class GlyphComponent(object):
attrs = attrs + [("flags", hex(self.flags))] attrs = attrs + [("flags", hex(self.flags))]
writer.simpletag("component", attrs) writer.simpletag("component", attrs)
writer.newline() writer.newline()
def fromXML(self, name, attrs, content, ttFont): def fromXML(self, name, attrs, content, ttFont):
self.glyphName = attrs["glyphName"] self.glyphName = attrs["glyphName"]
if "firstPt" in attrs: if "firstPt" in attrs:
@ -1115,7 +1115,7 @@ class GlyphComponent(object):
scale = safeEval(attrs["scale"]) scale = safeEval(attrs["scale"])
self.transform = [[scale, 0], [0, scale]] self.transform = [[scale, 0], [0, scale]]
self.flags = safeEval(attrs["flags"]) self.flags = safeEval(attrs["flags"])
def __ne__(self, other): def __ne__(self, other):
return not self.__eq__(other) return not self.__eq__(other)
def __eq__(self, other): def __eq__(self, other):

View File

@ -11,7 +11,7 @@ hdmxHeaderFormat = """
""" """
class table__h_d_m_x(DefaultTable.DefaultTable): class table__h_d_m_x(DefaultTable.DefaultTable):
def decompile(self, data, ttFont): def decompile(self, data, ttFont):
numGlyphs = ttFont['maxp'].numGlyphs numGlyphs = ttFont['maxp'].numGlyphs
glyphOrder = ttFont.getGlyphOrder() glyphOrder = ttFont.getGlyphOrder()
@ -26,7 +26,7 @@ class table__h_d_m_x(DefaultTable.DefaultTable):
self.hdmx[ppem] = widths self.hdmx[ppem] = widths
data = data[self.recordSize:] data = data[self.recordSize:]
assert len(data) == 0, "too much hdmx data" assert len(data) == 0, "too much hdmx data"
def compile(self, ttFont): def compile(self, ttFont):
self.version = 0 self.version = 0
numGlyphs = ttFont['maxp'].numGlyphs numGlyphs = ttFont['maxp'].numGlyphs
@ -43,7 +43,7 @@ class table__h_d_m_x(DefaultTable.DefaultTable):
data = data + bytechr(width) data = data + bytechr(width)
data = data + pad data = data + pad
return data return data
def toXML(self, writer, ttFont): def toXML(self, writer, ttFont):
writer.begintag("hdmxData") writer.begintag("hdmxData")
writer.newline() writer.newline()
@ -72,7 +72,7 @@ class table__h_d_m_x(DefaultTable.DefaultTable):
writer.newline() writer.newline()
writer.endtag("hdmxData") writer.endtag("hdmxData")
writer.newline() writer.newline()
def fromXML(self, name, attrs, content, ttFont): def fromXML(self, name, attrs, content, ttFont):
if name != "hdmxData": if name != "hdmxData":
return return

View File

@ -30,9 +30,9 @@ headFormat = """
""" """
class table__h_e_a_d(DefaultTable.DefaultTable): class table__h_e_a_d(DefaultTable.DefaultTable):
dependencies = ['maxp', 'loca'] dependencies = ['maxp', 'loca']
def decompile(self, data, ttFont): def decompile(self, data, ttFont):
dummy, rest = sstruct.unpack2(headFormat, data, self) dummy, rest = sstruct.unpack2(headFormat, data, self)
if rest: if rest:
@ -55,13 +55,13 @@ class table__h_e_a_d(DefaultTable.DefaultTable):
warnings.warn("'%s' timestamp seems very low; regarding as unix timestamp" % stamp) warnings.warn("'%s' timestamp seems very low; regarding as unix timestamp" % stamp)
value += 0x7C259DC0 value += 0x7C259DC0
setattr(self, stamp, value) setattr(self, stamp, value)
def compile(self, ttFont): def compile(self, ttFont):
if ttFont.recalcTimestamp: if ttFont.recalcTimestamp:
self.modified = timestampNow() self.modified = timestampNow()
data = sstruct.pack(headFormat, self) data = sstruct.pack(headFormat, self)
return data return data
def toXML(self, writer, ttFont): def toXML(self, writer, ttFont):
writer.comment("Most of this table will be recalculated by the compiler") writer.comment("Most of this table will be recalculated by the compiler")
writer.newline() writer.newline()
@ -80,7 +80,7 @@ class table__h_e_a_d(DefaultTable.DefaultTable):
value = num2binary(value, 16) value = num2binary(value, 16)
writer.simpletag(name, value=value) writer.simpletag(name, value=value)
writer.newline() writer.newline()
def fromXML(self, name, attrs, content, ttFont): def fromXML(self, name, attrs, content, ttFont):
value = attrs["value"] value = attrs["value"]
if name in ("created", "modified"): if name in ("created", "modified"):

View File

@ -31,15 +31,15 @@ class table__h_h_e_a(DefaultTable.DefaultTable):
# Note: Keep in sync with table__v_h_e_a # Note: Keep in sync with table__v_h_e_a
dependencies = ['hmtx', 'glyf'] dependencies = ['hmtx', 'glyf']
def decompile(self, data, ttFont): def decompile(self, data, ttFont):
sstruct.unpack(hheaFormat, data, self) sstruct.unpack(hheaFormat, data, self)
def compile(self, ttFont): def compile(self, ttFont):
if ttFont.isLoaded('glyf') and ttFont.recalcBBoxes: if ttFont.isLoaded('glyf') and ttFont.recalcBBoxes:
self.recalc(ttFont) self.recalc(ttFont)
return sstruct.pack(hheaFormat, self) return sstruct.pack(hheaFormat, self)
def recalc(self, ttFont): def recalc(self, ttFont):
hmtxTable = ttFont['hmtx'] hmtxTable = ttFont['hmtx']
if 'glyf' in ttFont: if 'glyf' in ttFont:
@ -79,13 +79,13 @@ class table__h_h_e_a(DefaultTable.DefaultTable):
else: else:
# XXX CFF recalc... # XXX CFF recalc...
pass pass
def toXML(self, writer, ttFont): def toXML(self, writer, ttFont):
formatstring, names, fixes = sstruct.getformat(hheaFormat) formatstring, names, fixes = sstruct.getformat(hheaFormat)
for name in names: for name in names:
value = getattr(self, name) value = getattr(self, name)
writer.simpletag(name, value=value) writer.simpletag(name, value=value)
writer.newline() writer.newline()
def fromXML(self, name, attrs, content, ttFont): def fromXML(self, name, attrs, content, ttFont):
setattr(self, name, safeEval(attrs["value"])) setattr(self, name, safeEval(attrs["value"]))

View File

@ -8,12 +8,12 @@ import warnings
class table__h_m_t_x(DefaultTable.DefaultTable): class table__h_m_t_x(DefaultTable.DefaultTable):
headerTag = 'hhea' headerTag = 'hhea'
advanceName = 'width' advanceName = 'width'
sideBearingName = 'lsb' sideBearingName = 'lsb'
numberOfMetricsName = 'numberOfHMetrics' numberOfMetricsName = 'numberOfHMetrics'
def decompile(self, data, ttFont): def decompile(self, data, ttFont):
numGlyphs = ttFont['maxp'].numGlyphs numGlyphs = ttFont['maxp'].numGlyphs
numberOfMetrics = int(getattr(ttFont[self.headerTag], self.numberOfMetricsName)) numberOfMetrics = int(getattr(ttFont[self.headerTag], self.numberOfMetricsName))
@ -41,7 +41,7 @@ class table__h_m_t_x(DefaultTable.DefaultTable):
for i in range(numberOfSideBearings): for i in range(numberOfSideBearings):
glyphName = glyphOrder[i + numberOfMetrics] glyphName = glyphOrder[i + numberOfMetrics]
self.metrics[glyphName] = [lastAdvance, sideBearings[i]] self.metrics[glyphName] = [lastAdvance, sideBearings[i]]
def compile(self, ttFont): def compile(self, ttFont):
metrics = [] metrics = []
for glyphName in ttFont.getGlyphOrder(): for glyphName in ttFont.getGlyphOrder():
@ -58,7 +58,7 @@ class table__h_m_t_x(DefaultTable.DefaultTable):
additionalMetrics = [sb for advance, sb in additionalMetrics] additionalMetrics = [sb for advance, sb in additionalMetrics]
metrics = metrics[:lastIndex] metrics = metrics[:lastIndex]
setattr(ttFont[self.headerTag], self.numberOfMetricsName, len(metrics)) setattr(ttFont[self.headerTag], self.numberOfMetricsName, len(metrics))
allMetrics = [] allMetrics = []
for item in metrics: for item in metrics:
allMetrics.extend(item) allMetrics.extend(item)
@ -66,36 +66,36 @@ class table__h_m_t_x(DefaultTable.DefaultTable):
if sys.byteorder != "big": if sys.byteorder != "big":
allMetrics.byteswap() allMetrics.byteswap()
data = allMetrics.tostring() data = allMetrics.tostring()
additionalMetrics = array.array("h", additionalMetrics) additionalMetrics = array.array("h", additionalMetrics)
if sys.byteorder != "big": if sys.byteorder != "big":
additionalMetrics.byteswap() additionalMetrics.byteswap()
data = data + additionalMetrics.tostring() data = data + additionalMetrics.tostring()
return data return data
def toXML(self, writer, ttFont): def toXML(self, writer, ttFont):
names = sorted(self.metrics.keys()) names = sorted(self.metrics.keys())
for glyphName in names: for glyphName in names:
advance, sb = self.metrics[glyphName] advance, sb = self.metrics[glyphName]
writer.simpletag("mtx", [ writer.simpletag("mtx", [
("name", glyphName), ("name", glyphName),
(self.advanceName, advance), (self.advanceName, advance),
(self.sideBearingName, sb), (self.sideBearingName, sb),
]) ])
writer.newline() writer.newline()
def fromXML(self, name, attrs, content, ttFont): def fromXML(self, name, attrs, content, ttFont):
if not hasattr(self, "metrics"): if not hasattr(self, "metrics"):
self.metrics = {} self.metrics = {}
if name == "mtx": if name == "mtx":
self.metrics[attrs["name"]] = [safeEval(attrs[self.advanceName]), self.metrics[attrs["name"]] = [safeEval(attrs[self.advanceName]),
safeEval(attrs[self.sideBearingName])] safeEval(attrs[self.sideBearingName])]
def __delitem__(self, glyphName): def __delitem__(self, glyphName):
del self.metrics[glyphName] del self.metrics[glyphName]
def __getitem__(self, glyphName): def __getitem__(self, glyphName):
return self.metrics[glyphName] return self.metrics[glyphName]
def __setitem__(self, glyphName, advance_sb_pair): def __setitem__(self, glyphName, advance_sb_pair):
self.metrics[glyphName] = tuple(advance_sb_pair) self.metrics[glyphName] = tuple(advance_sb_pair)

View File

@ -9,13 +9,13 @@ import warnings
class table__k_e_r_n(DefaultTable.DefaultTable): class table__k_e_r_n(DefaultTable.DefaultTable):
def getkern(self, format): def getkern(self, format):
for subtable in self.kernTables: for subtable in self.kernTables:
if subtable.version == format: if subtable.version == format:
return subtable return subtable
return None # not found return None # not found
def decompile(self, data, ttFont): def decompile(self, data, ttFont):
version, nTables = struct.unpack(">HH", data[:4]) version, nTables = struct.unpack(">HH", data[:4])
apple = False apple = False
@ -46,7 +46,7 @@ class table__k_e_r_n(DefaultTable.DefaultTable):
subtable.decompile(data[:length], ttFont) subtable.decompile(data[:length], ttFont)
self.kernTables.append(subtable) self.kernTables.append(subtable)
data = data[length:] data = data[length:]
def compile(self, ttFont): def compile(self, ttFont):
if hasattr(self, "kernTables"): if hasattr(self, "kernTables"):
nTables = len(self.kernTables) nTables = len(self.kernTables)
@ -61,13 +61,13 @@ class table__k_e_r_n(DefaultTable.DefaultTable):
for subtable in self.kernTables: for subtable in self.kernTables:
data = data + subtable.compile(ttFont) data = data + subtable.compile(ttFont)
return data return data
def toXML(self, writer, ttFont): def toXML(self, writer, ttFont):
writer.simpletag("version", value=self.version) writer.simpletag("version", value=self.version)
writer.newline() writer.newline()
for subtable in self.kernTables: for subtable in self.kernTables:
subtable.toXML(writer, ttFont) subtable.toXML(writer, ttFont)
def fromXML(self, name, attrs, content, ttFont): def fromXML(self, name, attrs, content, ttFont):
if name == "version": if name == "version":
self.version = safeEval(attrs["value"]) self.version = safeEval(attrs["value"])
@ -86,7 +86,7 @@ class table__k_e_r_n(DefaultTable.DefaultTable):
class KernTable_format_0(object): class KernTable_format_0(object):
def decompile(self, data, ttFont): def decompile(self, data, ttFont):
version, length, coverage = (0,0,0) version, length, coverage = (0,0,0)
if not self.apple: if not self.apple:
@ -96,12 +96,12 @@ class KernTable_format_0(object):
version, length, coverage = struct.unpack(">LHH", data[:8]) version, length, coverage = struct.unpack(">LHH", data[:8])
data = data[8:] data = data[8:]
self.version, self.coverage = int(version), int(coverage) self.version, self.coverage = int(version), int(coverage)
self.kernTable = kernTable = {} self.kernTable = kernTable = {}
nPairs, searchRange, entrySelector, rangeShift = struct.unpack(">HHHH", data[:8]) nPairs, searchRange, entrySelector, rangeShift = struct.unpack(">HHHH", data[:8])
data = data[8:] data = data[8:]
for k in range(nPairs): for k in range(nPairs):
if len(data) < 6: if len(data) < 6:
# buggy kern table # buggy kern table
@ -113,19 +113,19 @@ class KernTable_format_0(object):
kernTable[(ttFont.getGlyphName(left), ttFont.getGlyphName(right))] = value kernTable[(ttFont.getGlyphName(left), ttFont.getGlyphName(right))] = value
if len(data): if len(data):
warnings.warn("excess data in 'kern' subtable: %d bytes" % len(data)) warnings.warn("excess data in 'kern' subtable: %d bytes" % len(data))
def compile(self, ttFont): def compile(self, ttFont):
nPairs = len(self.kernTable) nPairs = len(self.kernTable)
searchRange, entrySelector, rangeShift = getSearchRange(nPairs, 6) searchRange, entrySelector, rangeShift = getSearchRange(nPairs, 6)
data = struct.pack(">HHHH", nPairs, searchRange, entrySelector, rangeShift) data = struct.pack(">HHHH", nPairs, searchRange, entrySelector, rangeShift)
# yeehee! (I mean, turn names into indices) # yeehee! (I mean, turn names into indices)
getGlyphID = ttFont.getGlyphID getGlyphID = ttFont.getGlyphID
kernTable = sorted((getGlyphID(left), getGlyphID(right), value) for ((left,right),value) in self.kernTable.items()) kernTable = sorted((getGlyphID(left), getGlyphID(right), value) for ((left,right),value) in self.kernTable.items())
for left, right, value in kernTable: for left, right, value in kernTable:
data = data + struct.pack(">HHh", left, right, value) data = data + struct.pack(">HHh", left, right, value)
return struct.pack(">HHH", self.version, len(data) + 6, self.coverage) + data return struct.pack(">HHH", self.version, len(data) + 6, self.coverage) + data
def toXML(self, writer, ttFont): def toXML(self, writer, ttFont):
writer.begintag("kernsubtable", coverage=self.coverage, format=0) writer.begintag("kernsubtable", coverage=self.coverage, format=0)
writer.newline() writer.newline()
@ -139,7 +139,7 @@ class KernTable_format_0(object):
writer.newline() writer.newline()
writer.endtag("kernsubtable") writer.endtag("kernsubtable")
writer.newline() writer.newline()
def fromXML(self, name, attrs, content, ttFont): def fromXML(self, name, attrs, content, ttFont):
self.coverage = safeEval(attrs["coverage"]) self.coverage = safeEval(attrs["coverage"])
self.version = safeEval(attrs["format"]) self.version = safeEval(attrs["format"])
@ -150,28 +150,28 @@ class KernTable_format_0(object):
continue continue
name, attrs, content = element name, attrs, content = element
self.kernTable[(attrs["l"], attrs["r"])] = safeEval(attrs["v"]) self.kernTable[(attrs["l"], attrs["r"])] = safeEval(attrs["v"])
def __getitem__(self, pair): def __getitem__(self, pair):
return self.kernTable[pair] return self.kernTable[pair]
def __setitem__(self, pair, value): def __setitem__(self, pair, value):
self.kernTable[pair] = value self.kernTable[pair] = value
def __delitem__(self, pair): def __delitem__(self, pair):
del self.kernTable[pair] del self.kernTable[pair]
class KernTable_format_unkown(object): class KernTable_format_unkown(object):
def __init__(self, format): def __init__(self, format):
self.format = format self.format = format
def decompile(self, data, ttFont): def decompile(self, data, ttFont):
self.data = data self.data = data
def compile(self, ttFont): def compile(self, ttFont):
return self.data return self.data
def toXML(self, writer, ttFont): def toXML(self, writer, ttFont):
writer.begintag("kernsubtable", format=self.format) writer.begintag("kernsubtable", format=self.format)
writer.newline() writer.newline()
@ -180,7 +180,7 @@ class KernTable_format_unkown(object):
writer.dumphex(self.data) writer.dumphex(self.data)
writer.endtag("kernsubtable") writer.endtag("kernsubtable")
writer.newline() writer.newline()
def fromXML(self, name, attrs, content, ttFont): def fromXML(self, name, attrs, content, ttFont):
self.decompile(readHex(content), ttFont) self.decompile(readHex(content), ttFont)

View File

@ -6,9 +6,9 @@ import array
import warnings import warnings
class table__l_o_c_a(DefaultTable.DefaultTable): class table__l_o_c_a(DefaultTable.DefaultTable):
dependencies = ['glyf'] dependencies = ['glyf']
def decompile(self, data, ttFont): def decompile(self, data, ttFont):
longFormat = ttFont['head'].indexToLocFormat longFormat = ttFont['head'].indexToLocFormat
if longFormat: if longFormat:
@ -27,7 +27,7 @@ class table__l_o_c_a(DefaultTable.DefaultTable):
if len(locations) < (ttFont['maxp'].numGlyphs + 1): if len(locations) < (ttFont['maxp'].numGlyphs + 1):
warnings.warn("corrupt 'loca' table, or wrong numGlyphs in 'maxp': %d %d" % (len(locations) - 1, ttFont['maxp'].numGlyphs)) warnings.warn("corrupt 'loca' table, or wrong numGlyphs in 'maxp': %d %d" % (len(locations) - 1, ttFont['maxp'].numGlyphs))
self.locations = locations self.locations = locations
def compile(self, ttFont): def compile(self, ttFont):
try: try:
max_location = max(self.locations) max_location = max(self.locations)
@ -45,16 +45,16 @@ class table__l_o_c_a(DefaultTable.DefaultTable):
if sys.byteorder != "big": if sys.byteorder != "big":
locations.byteswap() locations.byteswap()
return locations.tostring() return locations.tostring()
def set(self, locations): def set(self, locations):
self.locations = array.array("I", locations) self.locations = array.array("I", locations)
def toXML(self, writer, ttFont): def toXML(self, writer, ttFont):
writer.comment("The 'loca' table will be calculated by the compiler") writer.comment("The 'loca' table will be calculated by the compiler")
writer.newline() writer.newline()
def __getitem__(self, index): def __getitem__(self, index):
return self.locations[index] return self.locations[index]
def __len__(self): def __len__(self):
return len(self.locations) return len(self.locations)

View File

@ -29,16 +29,16 @@ maxpFormat_1_0_add = """
class table__m_a_x_p(DefaultTable.DefaultTable): class table__m_a_x_p(DefaultTable.DefaultTable):
dependencies = ['glyf'] dependencies = ['glyf']
def decompile(self, data, ttFont): def decompile(self, data, ttFont):
dummy, data = sstruct.unpack2(maxpFormat_0_5, data, self) dummy, data = sstruct.unpack2(maxpFormat_0_5, data, self)
self.numGlyphs = int(self.numGlyphs) self.numGlyphs = int(self.numGlyphs)
if self.tableVersion != 0x00005000: if self.tableVersion != 0x00005000:
dummy, data = sstruct.unpack2(maxpFormat_1_0_add, data, self) dummy, data = sstruct.unpack2(maxpFormat_1_0_add, data, self)
assert len(data) == 0 assert len(data) == 0
def compile(self, ttFont): def compile(self, ttFont):
if 'glyf' in ttFont: if 'glyf' in ttFont:
if ttFont.isLoaded('glyf') and ttFont.recalcBBoxes: if ttFont.isLoaded('glyf') and ttFont.recalcBBoxes:
@ -52,7 +52,7 @@ class table__m_a_x_p(DefaultTable.DefaultTable):
if self.tableVersion == 0x00010000: if self.tableVersion == 0x00010000:
data = data + sstruct.pack(maxpFormat_1_0_add, self) data = data + sstruct.pack(maxpFormat_1_0_add, self)
return data return data
def recalc(self, ttFont): def recalc(self, ttFont):
"""Recalculate the font bounding box, and most other maxp values except """Recalculate the font bounding box, and most other maxp values except
for the TT instructions values. Also recalculate the value of bit 1 for the TT instructions values. Also recalculate the value of bit 1
@ -112,14 +112,14 @@ class table__m_a_x_p(DefaultTable.DefaultTable):
headTable.flags = headTable.flags | 0x2 headTable.flags = headTable.flags | 0x2
else: else:
headTable.flags = headTable.flags & ~0x2 headTable.flags = headTable.flags & ~0x2
def testrepr(self): def testrepr(self):
items = sorted(self.__dict__.items()) items = sorted(self.__dict__.items())
print(". . . . . . . . .") print(". . . . . . . . .")
for combo in items: for combo in items:
print(" %s: %s" % combo) print(" %s: %s" % combo)
print(". . . . . . . . .") print(". . . . . . . . .")
def toXML(self, writer, ttFont): def toXML(self, writer, ttFont):
if self.tableVersion != 0x00005000: if self.tableVersion != 0x00005000:
writer.comment("Most of this table will be recalculated by the compiler") writer.comment("Most of this table will be recalculated by the compiler")
@ -134,6 +134,6 @@ class table__m_a_x_p(DefaultTable.DefaultTable):
value = hex(value) value = hex(value)
writer.simpletag(name, value=value) writer.simpletag(name, value=value)
writer.newline() writer.newline()
def fromXML(self, name, attrs, content, ttFont): def fromXML(self, name, attrs, content, ttFont):
setattr(self, name, safeEval(attrs["value"])) setattr(self, name, safeEval(attrs["value"]))

View File

@ -20,7 +20,7 @@ nameRecordSize = sstruct.calcsize(nameRecordFormat)
class table__n_a_m_e(DefaultTable.DefaultTable): class table__n_a_m_e(DefaultTable.DefaultTable):
def decompile(self, data, ttFont): def decompile(self, data, ttFont):
format, n, stringOffset = struct.unpack(">HHH", data[:6]) format, n, stringOffset = struct.unpack(">HHH", data[:6])
expectedStringOffset = 6 + n * nameRecordSize expectedStringOffset = 6 + n * nameRecordSize
@ -43,7 +43,7 @@ class table__n_a_m_e(DefaultTable.DefaultTable):
# print name.__dict__ # print name.__dict__
del name.offset, name.length del name.offset, name.length
self.names.append(name) self.names.append(name)
def compile(self, ttFont): def compile(self, ttFont):
if not hasattr(self, "names"): if not hasattr(self, "names"):
# only happens when there are NO name table entries read # only happens when there are NO name table entries read
@ -67,11 +67,11 @@ class table__n_a_m_e(DefaultTable.DefaultTable):
stringData = bytesjoin([stringData, string]) stringData = bytesjoin([stringData, string])
data = data + sstruct.pack(nameRecordFormat, name) data = data + sstruct.pack(nameRecordFormat, name)
return data + stringData return data + stringData
def toXML(self, writer, ttFont): def toXML(self, writer, ttFont):
for name in self.names: for name in self.names:
name.toXML(writer, ttFont) name.toXML(writer, ttFont)
def fromXML(self, name, attrs, content, ttFont): def fromXML(self, name, attrs, content, ttFont):
if name != "namerecord": if name != "namerecord":
return # ignore unknown tags return # ignore unknown tags
@ -80,11 +80,11 @@ class table__n_a_m_e(DefaultTable.DefaultTable):
name = NameRecord() name = NameRecord()
self.names.append(name) self.names.append(name)
name.fromXML(name, attrs, content, ttFont) name.fromXML(name, attrs, content, ttFont)
def getName(self, nameID, platformID, platEncID, langID=None): def getName(self, nameID, platformID, platEncID, langID=None):
for namerecord in self.names: for namerecord in self.names:
if ( namerecord.nameID == nameID and if ( namerecord.nameID == nameID and
namerecord.platformID == platformID and namerecord.platformID == platformID and
namerecord.platEncID == platEncID): namerecord.platEncID == platEncID):
if langID is None or namerecord.langID == langID: if langID is None or namerecord.langID == langID:
return namerecord return namerecord
@ -202,7 +202,7 @@ class NameRecord(object):
writer.newline() writer.newline()
writer.endtag("namerecord") writer.endtag("namerecord")
writer.newline() writer.newline()
def fromXML(self, name, attrs, content, ttFont): def fromXML(self, name, attrs, content, ttFont):
self.nameID = safeEval(attrs["nameID"]) self.nameID = safeEval(attrs["nameID"])
self.platformID = safeEval(attrs["platformID"]) self.platformID = safeEval(attrs["platformID"])
@ -215,7 +215,7 @@ class NameRecord(object):
else: else:
# This is the inverse of write8bit... # This is the inverse of write8bit...
self.string = s.encode("latin1") self.string = s.encode("latin1")
def __lt__(self, other): def __lt__(self, other):
if type(self) != type(other): if type(self) != type(other):
return NotImplemented return NotImplemented
@ -236,7 +236,7 @@ class NameRecord(object):
getattr(other, "string", None), getattr(other, "string", None),
) )
return selfTuple < otherTuple return selfTuple < otherTuple
def __repr__(self): def __repr__(self):
return "<NameRecord NameID=%d; PlatformID=%d; LanguageID=%d>" % ( return "<NameRecord NameID=%d; PlatformID=%d; LanguageID=%d>" % (
self.nameID, self.platformID, self.langID) self.nameID, self.platformID, self.langID)

View File

@ -13,7 +13,7 @@ import array
postFormat = """ postFormat = """
> >
formatType: 16.16F formatType: 16.16F
italicAngle: 16.16F # italic angle in degrees italicAngle: 16.16F # italic angle in degrees
underlinePosition: h underlinePosition: h
underlineThickness: h underlineThickness: h
isFixedPitch: L isFixedPitch: L
@ -27,7 +27,7 @@ postFormatSize = sstruct.calcsize(postFormat)
class table__p_o_s_t(DefaultTable.DefaultTable): class table__p_o_s_t(DefaultTable.DefaultTable):
def decompile(self, data, ttFont): def decompile(self, data, ttFont):
sstruct.unpack(postFormat, data[:postFormatSize], self) sstruct.unpack(postFormat, data[:postFormatSize], self)
data = data[postFormatSize:] data = data[postFormatSize:]
@ -42,7 +42,7 @@ class table__p_o_s_t(DefaultTable.DefaultTable):
else: else:
# supported format # supported format
raise ttLib.TTLibError("'post' table format %f not supported" % self.formatType) raise ttLib.TTLibError("'post' table format %f not supported" % self.formatType)
def compile(self, ttFont): def compile(self, ttFont):
data = sstruct.pack(postFormat, self) data = sstruct.pack(postFormat, self)
if self.formatType == 1.0: if self.formatType == 1.0:
@ -57,7 +57,7 @@ class table__p_o_s_t(DefaultTable.DefaultTable):
# supported format # supported format
raise ttLib.TTLibError("'post' table format %f not supported" % self.formatType) raise ttLib.TTLibError("'post' table format %f not supported" % self.formatType)
return data return data
def getGlyphOrder(self): def getGlyphOrder(self):
"""This function will get called by a ttLib.TTFont instance. """This function will get called by a ttLib.TTFont instance.
Do not call this function yourself, use TTFont().getGlyphOrder() Do not call this function yourself, use TTFont().getGlyphOrder()
@ -68,10 +68,10 @@ class table__p_o_s_t(DefaultTable.DefaultTable):
glyphOrder = self.glyphOrder glyphOrder = self.glyphOrder
del self.glyphOrder del self.glyphOrder
return glyphOrder return glyphOrder
def decode_format_1_0(self, data, ttFont): def decode_format_1_0(self, data, ttFont):
self.glyphOrder = standardGlyphOrder[:ttFont["maxp"].numGlyphs] self.glyphOrder = standardGlyphOrder[:ttFont["maxp"].numGlyphs]
def decode_format_2_0(self, data, ttFont): def decode_format_2_0(self, data, ttFont):
numGlyphs, = struct.unpack(">H", data[:2]) numGlyphs, = struct.unpack(">H", data[:2])
numGlyphs = int(numGlyphs) numGlyphs = int(numGlyphs)
@ -103,7 +103,7 @@ class table__p_o_s_t(DefaultTable.DefaultTable):
name = standardGlyphOrder[index] name = standardGlyphOrder[index]
glyphOrder[glyphID] = name glyphOrder[glyphID] = name
self.build_psNameMapping(ttFont) self.build_psNameMapping(ttFont)
def build_psNameMapping(self, ttFont): def build_psNameMapping(self, ttFont):
mapping = {} mapping = {}
allNames = {} allNames = {}
@ -125,12 +125,12 @@ class table__p_o_s_t(DefaultTable.DefaultTable):
mapping[glyphName] = psName mapping[glyphName] = psName
self.mapping = mapping self.mapping = mapping
def decode_format_3_0(self, data, ttFont): def decode_format_3_0(self, data, ttFont):
# Setting self.glyphOrder to None will cause the TTFont object # Setting self.glyphOrder to None will cause the TTFont object
# try and construct glyph names from a Unicode cmap table. # try and construct glyph names from a Unicode cmap table.
self.glyphOrder = None self.glyphOrder = None
def decode_format_4_0(self, data, ttFont): def decode_format_4_0(self, data, ttFont):
from fontTools import agl from fontTools import agl
numGlyphs = ttFont['maxp'].numGlyphs numGlyphs = ttFont['maxp'].numGlyphs
@ -178,7 +178,7 @@ class table__p_o_s_t(DefaultTable.DefaultTable):
if sys.byteorder != "big": if sys.byteorder != "big":
indices.byteswap() indices.byteswap()
return struct.pack(">H", numGlyphs) + indices.tostring() + packPStrings(extraNames) return struct.pack(">H", numGlyphs) + indices.tostring() + packPStrings(extraNames)
def encode_format_4_0(self, ttFont): def encode_format_4_0(self, ttFont):
from fontTools import agl from fontTools import agl
numGlyphs = ttFont['maxp'].numGlyphs numGlyphs = ttFont['maxp'].numGlyphs
@ -235,7 +235,7 @@ class table__p_o_s_t(DefaultTable.DefaultTable):
writer.dumphex(self.data) writer.dumphex(self.data)
writer.endtag("hexdata") writer.endtag("hexdata")
writer.newline() writer.newline()
def fromXML(self, name, attrs, content, ttFont): def fromXML(self, name, attrs, content, ttFont):
if name not in ("psNames", "extraNames", "hexdata"): if name not in ("psNames", "extraNames", "hexdata"):
setattr(self, name, safeEval(attrs["value"])) setattr(self, name, safeEval(attrs["value"]))

View File

@ -30,14 +30,14 @@ class table__v_h_e_a(DefaultTable.DefaultTable):
# Note: Keep in sync with table__h_h_e_a # Note: Keep in sync with table__h_h_e_a
dependencies = ['vmtx', 'glyf'] dependencies = ['vmtx', 'glyf']
def decompile(self, data, ttFont): def decompile(self, data, ttFont):
sstruct.unpack(vheaFormat, data, self) sstruct.unpack(vheaFormat, data, self)
def compile(self, ttFont): def compile(self, ttFont):
self.recalc(ttFont) self.recalc(ttFont)
return sstruct.pack(vheaFormat, self) return sstruct.pack(vheaFormat, self)
def recalc(self, ttFont): def recalc(self, ttFont):
vtmxTable = ttFont['vmtx'] vtmxTable = ttFont['vmtx']
if 'glyf' in ttFont: if 'glyf' in ttFont:
@ -77,13 +77,13 @@ class table__v_h_e_a(DefaultTable.DefaultTable):
else: else:
# XXX CFF recalc... # XXX CFF recalc...
pass pass
def toXML(self, writer, ttFont): def toXML(self, writer, ttFont):
formatstring, names, fixes = sstruct.getformat(vheaFormat) formatstring, names, fixes = sstruct.getformat(vheaFormat)
for name in names: for name in names:
value = getattr(self, name) value = getattr(self, name)
writer.simpletag(name, value=value) writer.simpletag(name, value=value)
writer.newline() writer.newline()
def fromXML(self, name, attrs, content, ttFont): def fromXML(self, name, attrs, content, ttFont):
setattr(self, name, safeEval(attrs["value"])) setattr(self, name, safeEval(attrs["value"]))

View File

@ -5,7 +5,7 @@ from fontTools import ttLib
superclass = ttLib.getTableClass("hmtx") superclass = ttLib.getTableClass("hmtx")
class table__v_m_t_x(superclass): class table__v_m_t_x(superclass):
headerTag = 'vhea' headerTag = 'vhea'
advanceName = 'height' advanceName = 'height'
sideBearingName = 'tsb' sideBearingName = 'tsb'

View File

@ -4,7 +4,7 @@ from . import DefaultTable
class asciiTable(DefaultTable.DefaultTable): class asciiTable(DefaultTable.DefaultTable):
def toXML(self, writer, ttFont): def toXML(self, writer, ttFont):
data = tostr(self.data) data = tostr(self.data)
# removing null bytes. XXX needed?? # removing null bytes. XXX needed??
@ -16,7 +16,7 @@ class asciiTable(DefaultTable.DefaultTable):
writer.newline() writer.newline()
writer.endtag("source") writer.endtag("source")
writer.newline() writer.newline()
def fromXML(self, name, attrs, content, ttFont): def fromXML(self, name, attrs, content, ttFont):
lines = strjoin(content).replace("\r", "\n").split("\n") lines = strjoin(content).replace("\r", "\n").split("\n")
self.data = tobytes("\r".join(lines[1:-1])) self.data = tobytes("\r".join(lines[1:-1]))

View File

@ -23,12 +23,12 @@ class OTLOffsetOverflowError(Exception):
class BaseTTXConverter(DefaultTable): class BaseTTXConverter(DefaultTable):
"""Generic base class for TTX table converters. It functions as an """Generic base class for TTX table converters. It functions as an
adapter between the TTX (ttLib actually) table model and the model adapter between the TTX (ttLib actually) table model and the model
we use for OpenType tables, which is necessarily subtly different. we use for OpenType tables, which is necessarily subtly different.
""" """
def decompile(self, data, font): def decompile(self, data, font):
from . import otTables from . import otTables
cachingStats = None if True else {} cachingStats = None if True else {}
@ -51,15 +51,15 @@ class BaseTTXConverter(DefaultTable):
break break
print(v, k) print(v, k)
print("---", len(stats)) print("---", len(stats))
def compile(self, font): def compile(self, font):
""" Create a top-level OTFWriter for the GPOS/GSUB table. """ Create a top-level OTFWriter for the GPOS/GSUB table.
Call the compile method for the the table Call the compile method for the the table
for each 'converter' record in the table converter list for each 'converter' record in the table converter list
call converter's write method for each item in the value. call converter's write method for each item in the value.
- For simple items, the write method adds a string to the - For simple items, the write method adds a string to the
writer's self.items list. writer's self.items list.
- For Struct/Table/Subtable items, it add first adds new writer to the - For Struct/Table/Subtable items, it add first adds new writer to the
to the writer's self.items, then calls the item's compile method. to the writer's self.items, then calls the item's compile method.
This creates a tree of writers, rooted at the GUSB/GPOS writer, with This creates a tree of writers, rooted at the GUSB/GPOS writer, with
each writer representing a table, and the writer.items list containing each writer representing a table, and the writer.items list containing
@ -71,7 +71,7 @@ class BaseTTXConverter(DefaultTable):
Traverse the flat list of tables again, calling getData each get the data in the table, now that Traverse the flat list of tables again, calling getData each get the data in the table, now that
pos's and offset are known. pos's and offset are known.
If a lookup subtable overflows an offset, we have to start all over. If a lookup subtable overflows an offset, we have to start all over.
""" """
class GlobalState(object): class GlobalState(object):
def __init__(self, tableType): def __init__(self, tableType):
@ -106,7 +106,7 @@ class BaseTTXConverter(DefaultTable):
def toXML(self, writer, font): def toXML(self, writer, font):
self.table.toXML2(writer, font) self.table.toXML2(writer, font)
def fromXML(self, name, attrs, content, font): def fromXML(self, name, attrs, content, font):
from . import otTables from . import otTables
if not hasattr(self, "table"): if not hasattr(self, "table"):
@ -169,7 +169,7 @@ class OTTableReader(object):
value, = struct.unpack(">L", self.data[pos:newpos]) value, = struct.unpack(">L", self.data[pos:newpos])
self.pos = newpos self.pos = newpos
return value return value
def readTag(self): def readTag(self):
pos = self.pos pos = self.pos
newpos = pos + 4 newpos = pos + 4
@ -188,9 +188,9 @@ class OTTableReader(object):
class OTTableWriter(object): class OTTableWriter(object):
"""Helper class to gather and assemble data for OpenType tables.""" """Helper class to gather and assemble data for OpenType tables."""
def __init__(self, globalState, localState=None): def __init__(self, globalState, localState=None):
self.items = [] self.items = []
self.pos = None self.pos = None
@ -207,7 +207,7 @@ class OTTableWriter(object):
return self.localState[name] return self.localState[name]
# assembler interface # assembler interface
def getAllData(self): def getAllData(self):
"""Assemble all data, including all subtables.""" """Assemble all data, including all subtables."""
self._doneWriting() self._doneWriting()
@ -235,7 +235,7 @@ class OTTableWriter(object):
data.append(tableData) data.append(tableData)
return bytesjoin(data) return bytesjoin(data)
def getDataLength(self): def getDataLength(self):
"""Return the length of this table in bytes, without subtables.""" """Return the length of this table in bytes, without subtables."""
l = 0 l = 0
@ -248,7 +248,7 @@ class OTTableWriter(object):
else: else:
l = l + len(item) l = l + len(item)
return l return l
def getData(self): def getData(self):
"""Assemble the data for this writer/table, without subtables.""" """Assemble the data for this writer/table, without subtables."""
items = list(self.items) # make a shallow copy items = list(self.items) # make a shallow copy
@ -256,7 +256,7 @@ class OTTableWriter(object):
numItems = len(items) numItems = len(items)
for i in range(numItems): for i in range(numItems):
item = items[i] item = items[i]
if hasattr(item, "getData"): if hasattr(item, "getData"):
if item.longOffset: if item.longOffset:
items[i] = packULong(item.pos - pos) items[i] = packULong(item.pos - pos)
@ -271,7 +271,7 @@ class OTTableWriter(object):
# overflow is within a subTable. Life is more complicated. # overflow is within a subTable. Life is more complicated.
# If we split the sub-table just before the current item, we may still suffer overflow. # If we split the sub-table just before the current item, we may still suffer overflow.
# This is because duplicate table merging is done only within an Extension subTable tree; # This is because duplicate table merging is done only within an Extension subTable tree;
# when we split the subtable in two, some items may no longer be duplicates. # when we split the subtable in two, some items may no longer be duplicates.
# Get worst case by adding up all the item lengths, depth first traversal. # Get worst case by adding up all the item lengths, depth first traversal.
# and then report the first item that overflows a short. # and then report the first item that overflows a short.
def getDeepItemLength(table): def getDeepItemLength(table):
@ -282,36 +282,36 @@ class OTTableWriter(object):
else: else:
length = len(table) length = len(table)
return length return length
length = self.getDataLength() length = self.getDataLength()
if hasattr(self, "sortCoverageLast") and item.name == "Coverage": if hasattr(self, "sortCoverageLast") and item.name == "Coverage":
# Coverage is first in the item list, but last in the table list, # Coverage is first in the item list, but last in the table list,
# The original overflow is really in the item list. Skip the Coverage # The original overflow is really in the item list. Skip the Coverage
# table in the following test. # table in the following test.
items = items[i+1:] items = items[i+1:]
for j in range(len(items)): for j in range(len(items)):
item = items[j] item = items[j]
length = length + getDeepItemLength(item) length = length + getDeepItemLength(item)
if length > 65535: if length > 65535:
break break
overflowErrorRecord = self.getOverflowErrorRecord(item) overflowErrorRecord = self.getOverflowErrorRecord(item)
raise OTLOffsetOverflowError(overflowErrorRecord) raise OTLOffsetOverflowError(overflowErrorRecord)
return bytesjoin(items) return bytesjoin(items)
def __hash__(self): def __hash__(self):
# only works after self._doneWriting() has been called # only works after self._doneWriting() has been called
return hash(self.items) return hash(self.items)
def __ne__(self, other): def __ne__(self, other):
return not self.__eq__(other) return not self.__eq__(other)
def __eq__(self, other): def __eq__(self, other):
if type(self) != type(other): if type(self) != type(other):
return NotImplemented return NotImplemented
return self.items == other.items return self.items == other.items
def _doneWriting(self, internedTables=None): def _doneWriting(self, internedTables=None):
# Convert CountData references to data string items # Convert CountData references to data string items
# collapse duplicate table references to a unique entry # collapse duplicate table references to a unique entry
@ -324,7 +324,7 @@ class OTTableWriter(object):
internedTables = {} internedTables = {}
items = self.items items = self.items
iRange = list(range(len(items))) iRange = list(range(len(items)))
if hasattr(self, "Extension"): if hasattr(self, "Extension"):
newTree = 1 newTree = 1
else: else:
@ -344,12 +344,12 @@ class OTTableWriter(object):
else: else:
internedTables[item] = item internedTables[item] = item
self.items = tuple(items) self.items = tuple(items)
def _gatherTables(self, tables=None, extTables=None, done=None): def _gatherTables(self, tables=None, extTables=None, done=None):
# Convert table references in self.items tree to a flat # Convert table references in self.items tree to a flat
# list of tables in depth-first traversal order. # list of tables in depth-first traversal order.
# "tables" are OTTableWriter objects. # "tables" are OTTableWriter objects.
# We do the traversal in reverse order at each level, in order to # We do the traversal in reverse order at each level, in order to
# resolve duplicate references to be the last reference in the list of tables. # resolve duplicate references to be the last reference in the list of tables.
# For extension lookups, duplicate references can be merged only within the # For extension lookups, duplicate references can be merged only within the
# writer tree under the extension lookup. # writer tree under the extension lookup.
@ -406,9 +406,9 @@ class OTTableWriter(object):
tables.append(self) tables.append(self)
return tables, extTables return tables, extTables
# interface for gathering data, as used by table.compile() # interface for gathering data, as used by table.compile()
def getSubWriter(self): def getSubWriter(self):
subwriter = self.__class__(self.globalState, self.localState) subwriter = self.__class__(self.globalState, self.localState)
subwriter.parent = self # because some subtables have idential values, we discard subwriter.parent = self # because some subtables have idential values, we discard
@ -416,11 +416,11 @@ class OTTableWriter(object):
# subtable writers can have more than one parent writer. # subtable writers can have more than one parent writer.
# But we just care about first one right now. # But we just care about first one right now.
return subwriter return subwriter
def writeUShort(self, value): def writeUShort(self, value):
assert 0 <= value < 0x10000 assert 0 <= value < 0x10000
self.items.append(struct.pack(">H", value)) self.items.append(struct.pack(">H", value))
def writeShort(self, value): def writeShort(self, value):
self.items.append(struct.pack(">h", value)) self.items.append(struct.pack(">h", value))
@ -428,30 +428,30 @@ class OTTableWriter(object):
assert 0 <= value < 0x1000000 assert 0 <= value < 0x1000000
b = struct.pack(">L", value) b = struct.pack(">L", value)
self.items.append(b[1:]) self.items.append(b[1:])
def writeLong(self, value): def writeLong(self, value):
self.items.append(struct.pack(">l", value)) self.items.append(struct.pack(">l", value))
def writeULong(self, value): def writeULong(self, value):
self.items.append(struct.pack(">L", value)) self.items.append(struct.pack(">L", value))
def writeTag(self, tag): def writeTag(self, tag):
tag = Tag(tag).tobytes() tag = Tag(tag).tobytes()
assert len(tag) == 4 assert len(tag) == 4
self.items.append(tag) self.items.append(tag)
def writeSubTable(self, subWriter): def writeSubTable(self, subWriter):
self.items.append(subWriter) self.items.append(subWriter)
def writeCountReference(self, table, name): def writeCountReference(self, table, name):
ref = CountReference(table, name) ref = CountReference(table, name)
self.items.append(ref) self.items.append(ref)
return ref return ref
def writeStruct(self, format, values): def writeStruct(self, format, values):
data = struct.pack(*(format,) + values) data = struct.pack(*(format,) + values)
self.items.append(data) self.items.append(data)
def writeData(self, data): def writeData(self, data):
self.items.append(data) self.items.append(data)
@ -528,13 +528,13 @@ class BaseTable(object):
raise AttributeError(attr) raise AttributeError(attr)
"""Generic base class for all OpenType (sub)tables.""" """Generic base class for all OpenType (sub)tables."""
def getConverters(self): def getConverters(self):
return self.converters return self.converters
def getConverterByName(self, name): def getConverterByName(self, name):
return self.convertersByName[name] return self.convertersByName[name]
def decompile(self, reader, font): def decompile(self, reader, font):
self.readFormat(reader) self.readFormat(reader)
table = {} table = {}
@ -624,19 +624,19 @@ class BaseTable(object):
conv.write(writer, font, table, value) conv.write(writer, font, table, value)
if conv.isPropagated: if conv.isPropagated:
writer[conv.name] = value writer[conv.name] = value
def readFormat(self, reader): def readFormat(self, reader):
pass pass
def writeFormat(self, writer): def writeFormat(self, writer):
pass pass
def postRead(self, table, font): def postRead(self, table, font):
self.__dict__.update(table) self.__dict__.update(table)
def preWrite(self, font): def preWrite(self, font):
return self.__dict__.copy() return self.__dict__.copy()
def toXML(self, xmlWriter, font, attrs=None, name=None): def toXML(self, xmlWriter, font, attrs=None, name=None):
tableName = name if name else self.__class__.__name__ tableName = name if name else self.__class__.__name__
if attrs is None: if attrs is None:
@ -648,7 +648,7 @@ class BaseTable(object):
self.toXML2(xmlWriter, font) self.toXML2(xmlWriter, font)
xmlWriter.endtag(tableName) xmlWriter.endtag(tableName)
xmlWriter.newline() xmlWriter.newline()
def toXML2(self, xmlWriter, font): def toXML2(self, xmlWriter, font):
# Simpler variant of toXML, *only* for the top level tables (like GPOS, GSUB). # Simpler variant of toXML, *only* for the top level tables (like GPOS, GSUB).
# This is because in TTX our parent writes our main tag, and in otBase.py we # This is because in TTX our parent writes our main tag, and in otBase.py we
@ -665,7 +665,7 @@ class BaseTable(object):
continue continue
value = getattr(self, conv.name) value = getattr(self, conv.name)
conv.xmlWrite(xmlWriter, font, value, conv.name, []) conv.xmlWrite(xmlWriter, font, value, conv.name, [])
def fromXML(self, name, attrs, content, font): def fromXML(self, name, attrs, content, font):
try: try:
conv = self.getConverterByName(name) conv = self.getConverterByName(name)
@ -680,7 +680,7 @@ class BaseTable(object):
seq.append(value) seq.append(value)
else: else:
setattr(self, conv.name, value) setattr(self, conv.name, value)
def __ne__(self, other): def __ne__(self, other):
return not self.__eq__(other) return not self.__eq__(other)
def __eq__(self, other): def __eq__(self, other):
@ -694,20 +694,20 @@ class BaseTable(object):
class FormatSwitchingBaseTable(BaseTable): class FormatSwitchingBaseTable(BaseTable):
"""Minor specialization of BaseTable, for tables that have multiple """Minor specialization of BaseTable, for tables that have multiple
formats, eg. CoverageFormat1 vs. CoverageFormat2.""" formats, eg. CoverageFormat1 vs. CoverageFormat2."""
def getConverters(self): def getConverters(self):
return self.converters[self.Format] return self.converters[self.Format]
def getConverterByName(self, name): def getConverterByName(self, name):
return self.convertersByName[self.Format][name] return self.convertersByName[self.Format][name]
def readFormat(self, reader): def readFormat(self, reader):
self.Format = reader.readUShort() self.Format = reader.readUShort()
assert self.Format != 0, (self, reader.pos, len(reader.data)) assert self.Format != 0, (self, reader.pos, len(reader.data))
def writeFormat(self, writer): def writeFormat(self, writer):
writer.writeUShort(self.Format) writer.writeUShort(self.Format)
@ -754,7 +754,7 @@ valueRecordFormatDict = _buildDict()
class ValueRecordFactory(object): class ValueRecordFactory(object):
"""Given a format code, this object convert ValueRecords.""" """Given a format code, this object convert ValueRecords."""
def __init__(self, valueFormat): def __init__(self, valueFormat):
@ -763,7 +763,7 @@ class ValueRecordFactory(object):
if valueFormat & mask: if valueFormat & mask:
format.append((name, isDevice, signed)) format.append((name, isDevice, signed))
self.format = format self.format = format
def readValueRecord(self, reader, font): def readValueRecord(self, reader, font):
format = self.format format = self.format
if not format: if not format:
@ -784,7 +784,7 @@ class ValueRecordFactory(object):
value = None value = None
setattr(valueRecord, name, value) setattr(valueRecord, name, value)
return valueRecord return valueRecord
def writeValueRecord(self, writer, font, valueRecord): def writeValueRecord(self, writer, font, valueRecord):
for name, isDevice, signed in self.format: for name, isDevice, signed in self.format:
value = getattr(valueRecord, name, 0) value = getattr(valueRecord, name, 0)
@ -802,15 +802,15 @@ class ValueRecordFactory(object):
class ValueRecord(object): class ValueRecord(object):
# see ValueRecordFactory # see ValueRecordFactory
def getFormat(self): def getFormat(self):
format = 0 format = 0
for name in self.__dict__.keys(): for name in self.__dict__.keys():
format = format | valueRecordFormatDict[name][0] format = format | valueRecordFormatDict[name][0]
return format return format
def toXML(self, xmlWriter, font, valueName, attrs=None): def toXML(self, xmlWriter, font, valueName, attrs=None):
if attrs is None: if attrs is None:
simpleItems = [] simpleItems = []
@ -836,7 +836,7 @@ class ValueRecord(object):
else: else:
xmlWriter.simpletag(valueName, simpleItems) xmlWriter.simpletag(valueName, simpleItems)
xmlWriter.newline() xmlWriter.newline()
def fromXML(self, name, attrs, content, font): def fromXML(self, name, attrs, content, font):
from . import otTables from . import otTables
for k, v in attrs.items(): for k, v in attrs.items():
@ -852,7 +852,7 @@ class ValueRecord(object):
name2, attrs2, content2 = elem2 name2, attrs2, content2 = elem2
value.fromXML(name2, attrs2, content2, font) value.fromXML(name2, attrs2, content2, font)
setattr(self, name, value) setattr(self, name, value)
def __ne__(self, other): def __ne__(self, other):
return not self.__eq__(other) return not self.__eq__(other)
def __eq__(self, other): def __eq__(self, other):

View File

@ -51,10 +51,10 @@ def buildConverters(tableSpec, tableNamespace):
class BaseConverter(object): class BaseConverter(object):
"""Base class for converter objects. Apart from the constructor, this """Base class for converter objects. Apart from the constructor, this
is an abstract class.""" is an abstract class."""
def __init__(self, name, repeat, aux, tableClass): def __init__(self, name, repeat, aux, tableClass):
self.name = name self.name = name
self.repeat = repeat self.repeat = repeat
@ -63,19 +63,19 @@ class BaseConverter(object):
self.isCount = name.endswith("Count") self.isCount = name.endswith("Count")
self.isLookupType = name.endswith("LookupType") self.isLookupType = name.endswith("LookupType")
self.isPropagated = name in ["ClassCount", "Class2Count", "FeatureTag", "SettingsCount", "AxisCount"] self.isPropagated = name in ["ClassCount", "Class2Count", "FeatureTag", "SettingsCount", "AxisCount"]
def read(self, reader, font, tableDict): def read(self, reader, font, tableDict):
"""Read a value from the reader.""" """Read a value from the reader."""
raise NotImplementedError(self) raise NotImplementedError(self)
def write(self, writer, font, tableDict, value, repeatIndex=None): def write(self, writer, font, tableDict, value, repeatIndex=None):
"""Write a value to the writer.""" """Write a value to the writer."""
raise NotImplementedError(self) raise NotImplementedError(self)
def xmlRead(self, attrs, content, font): def xmlRead(self, attrs, content, font):
"""Read a value from XML.""" """Read a value from XML."""
raise NotImplementedError(self) raise NotImplementedError(self)
def xmlWrite(self, xmlWriter, font, value, name, attrs): def xmlWrite(self, xmlWriter, font, value, name, attrs):
"""Write a value to XML.""" """Write a value to XML."""
raise NotImplementedError(self) raise NotImplementedError(self)
@ -191,15 +191,15 @@ class Version(BaseConverter):
class Struct(BaseConverter): class Struct(BaseConverter):
def read(self, reader, font, tableDict): def read(self, reader, font, tableDict):
table = self.tableClass() table = self.tableClass()
table.decompile(reader, font) table.decompile(reader, font)
return table return table
def write(self, writer, font, tableDict, value, repeatIndex=None): def write(self, writer, font, tableDict, value, repeatIndex=None):
value.compile(writer, font) value.compile(writer, font)
def xmlWrite(self, xmlWriter, font, value, name, attrs): def xmlWrite(self, xmlWriter, font, value, name, attrs):
if value is None: if value is None:
if attrs: if attrs:
@ -212,7 +212,7 @@ class Struct(BaseConverter):
pass # NULL table, ignore pass # NULL table, ignore
else: else:
value.toXML(xmlWriter, font, attrs, name=name) value.toXML(xmlWriter, font, attrs, name=name)
def xmlRead(self, attrs, content, font): def xmlRead(self, attrs, content, font):
if "empty" in attrs and safeEval(attrs["empty"]): if "empty" in attrs and safeEval(attrs["empty"]):
return None return None
@ -241,7 +241,7 @@ class Table(Struct):
writer.writeULong(0) writer.writeULong(0)
else: else:
writer.writeUShort(0) writer.writeUShort(0)
def read(self, reader, font, tableDict): def read(self, reader, font, tableDict):
offset = self.readOffset(reader) offset = self.readOffset(reader)
if offset == 0: if offset == 0:
@ -259,7 +259,7 @@ class Table(Struct):
else: else:
table.decompile(reader, font) table.decompile(reader, font)
return table return table
def write(self, writer, font, tableDict, value, repeatIndex=None): def write(self, writer, font, tableDict, value, repeatIndex=None):
if value is None: if value is None:
self.writeNullOffset(writer) self.writeNullOffset(writer)
@ -287,7 +287,7 @@ class SubTable(Table):
class ExtSubTable(LTable, SubTable): class ExtSubTable(LTable, SubTable):
def write(self, writer, font, tableDict, value, repeatIndex=None): def write(self, writer, font, tableDict, value, repeatIndex=None):
writer.Extension = 1 # actually, mere presence of the field flags it as an Ext Subtable writer. writer.Extension = 1 # actually, mere presence of the field flags it as an Ext Subtable writer.
Table.write(self, writer, font, tableDict, value, repeatIndex) Table.write(self, writer, font, tableDict, value, repeatIndex)
@ -329,7 +329,7 @@ class ValueRecord(ValueFormat):
class DeltaValue(BaseConverter): class DeltaValue(BaseConverter):
def read(self, reader, font, tableDict): def read(self, reader, font, tableDict):
StartSize = tableDict["StartSize"] StartSize = tableDict["StartSize"]
EndSize = tableDict["EndSize"] EndSize = tableDict["EndSize"]
@ -340,7 +340,7 @@ class DeltaValue(BaseConverter):
minusOffset = 1 << nBits minusOffset = 1 << nBits
mask = (1 << nBits) - 1 mask = (1 << nBits) - 1
signMask = 1 << (nBits - 1) signMask = 1 << (nBits - 1)
DeltaValue = [] DeltaValue = []
tmp, shift = 0, 0 tmp, shift = 0, 0
for i in range(nItems): for i in range(nItems):
@ -352,7 +352,7 @@ class DeltaValue(BaseConverter):
value = value - minusOffset value = value - minusOffset
DeltaValue.append(value) DeltaValue.append(value)
return DeltaValue return DeltaValue
def write(self, writer, font, tableDict, value, repeatIndex=None): def write(self, writer, font, tableDict, value, repeatIndex=None):
StartSize = tableDict["StartSize"] StartSize = tableDict["StartSize"]
EndSize = tableDict["EndSize"] EndSize = tableDict["EndSize"]
@ -363,7 +363,7 @@ class DeltaValue(BaseConverter):
nBits = 1 << DeltaFormat nBits = 1 << DeltaFormat
assert len(DeltaValue) == nItems assert len(DeltaValue) == nItems
mask = (1 << nBits) - 1 mask = (1 << nBits) - 1
tmp, shift = 0, 16 tmp, shift = 0, 16
for value in DeltaValue: for value in DeltaValue:
shift = shift - nBits shift = shift - nBits
@ -373,11 +373,11 @@ class DeltaValue(BaseConverter):
tmp, shift = 0, 16 tmp, shift = 0, 16
if shift != 16: if shift != 16:
writer.writeUShort(tmp) writer.writeUShort(tmp)
def xmlWrite(self, xmlWriter, font, value, name, attrs): def xmlWrite(self, xmlWriter, font, value, name, attrs):
xmlWriter.simpletag(name, attrs + [("value", value)]) xmlWriter.simpletag(name, attrs + [("value", value)])
xmlWriter.newline() xmlWriter.newline()
def xmlRead(self, attrs, content, font): def xmlRead(self, attrs, content, font):
return safeEval(attrs["value"]) return safeEval(attrs["value"])

View File

@ -33,9 +33,9 @@ class FeatureParamsCharacterVariants(FeatureParams):
pass pass
class Coverage(FormatSwitchingBaseTable): class Coverage(FormatSwitchingBaseTable):
# manual implementation to get rid of glyphID dependencies # manual implementation to get rid of glyphID dependencies
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? # TODO only allow glyphs that are valid?
@ -75,7 +75,7 @@ class Coverage(FormatSwitchingBaseTable):
else: else:
assert 0, "unknown format: %s" % self.Format assert 0, "unknown format: %s" % self.Format
del self.Format # Don't need this anymore del self.Format # Don't need this anymore
def preWrite(self, font): def preWrite(self, font):
glyphs = getattr(self, "glyphs", None) glyphs = getattr(self, "glyphs", None)
if glyphs is None: if glyphs is None:
@ -87,7 +87,7 @@ class Coverage(FormatSwitchingBaseTable):
# 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 = [getGlyphID(glyphName) for glyphName in glyphs ]
brokenOrder = sorted(glyphIDs) != glyphIDs brokenOrder = sorted(glyphIDs) != glyphIDs
last = glyphIDs[0] last = glyphIDs[0]
ranges = [[last]] ranges = [[last]]
for glyphID in glyphIDs[1:]: for glyphID in glyphIDs[1:]:
@ -96,7 +96,7 @@ class Coverage(FormatSwitchingBaseTable):
ranges.append([glyphID]) ranges.append([glyphID])
last = glyphID last = glyphID
ranges[-1].append(last) ranges[-1].append(last)
if brokenOrder or len(ranges) * 3 < len(glyphs): # 3 words vs. 1 word if brokenOrder or len(ranges) * 3 < len(glyphs): # 3 words vs. 1 word
# Format 2 is more compact # Format 2 is more compact
index = 0 index = 0
@ -120,12 +120,12 @@ class Coverage(FormatSwitchingBaseTable):
# fallthrough; Format 1 is more compact # fallthrough; Format 1 is more compact
self.Format = format self.Format = format
return rawTable return rawTable
def toXML2(self, xmlWriter, font): def toXML2(self, xmlWriter, font):
for glyphName in getattr(self, "glyphs", []): for glyphName in getattr(self, "glyphs", []):
xmlWriter.simpletag("Glyph", value=glyphName) xmlWriter.simpletag("Glyph", value=glyphName)
xmlWriter.newline() xmlWriter.newline()
def fromXML(self, name, attrs, content, font): def fromXML(self, name, attrs, content, font):
glyphs = getattr(self, "glyphs", None) glyphs = getattr(self, "glyphs", None)
if glyphs is None: if glyphs is None:
@ -161,7 +161,7 @@ class SingleSubst(FormatSwitchingBaseTable):
assert 0, "unknown format: %s" % self.Format assert 0, "unknown format: %s" % self.Format
self.mapping = mapping self.mapping = mapping
del self.Format # Don't need this anymore del self.Format # Don't need this anymore
def preWrite(self, font): def preWrite(self, font):
mapping = getattr(self, "mapping", None) mapping = getattr(self, "mapping", None)
if mapping is None: if mapping is None:
@ -200,14 +200,14 @@ class SingleSubst(FormatSwitchingBaseTable):
else: else:
rawTable["Substitute"] = subst rawTable["Substitute"] = subst
return rawTable return rawTable
def toXML2(self, xmlWriter, font): def toXML2(self, xmlWriter, font):
items = sorted(self.mapping.items()) items = sorted(self.mapping.items())
for inGlyph, outGlyph in items: for inGlyph, outGlyph in items:
xmlWriter.simpletag("Substitution", xmlWriter.simpletag("Substitution",
[("in", inGlyph), ("out", outGlyph)]) [("in", inGlyph), ("out", outGlyph)])
xmlWriter.newline() xmlWriter.newline()
def fromXML(self, name, attrs, content, font): def fromXML(self, name, attrs, content, font):
mapping = getattr(self, "mapping", None) mapping = getattr(self, "mapping", None)
if mapping is None: if mapping is None:
@ -217,7 +217,7 @@ class SingleSubst(FormatSwitchingBaseTable):
class ClassDef(FormatSwitchingBaseTable): class ClassDef(FormatSwitchingBaseTable):
def postRead(self, rawTable, font): def postRead(self, rawTable, font):
classDefs = {} classDefs = {}
glyphOrder = font.getGlyphOrder() glyphOrder = font.getGlyphOrder()
@ -266,7 +266,7 @@ class ClassDef(FormatSwitchingBaseTable):
assert 0, "unknown format: %s" % self.Format assert 0, "unknown format: %s" % self.Format
self.classDefs = classDefs self.classDefs = classDefs
del self.Format # Don't need this anymore del self.Format # Don't need this anymore
def preWrite(self, font): def preWrite(self, font):
classDefs = getattr(self, "classDefs", None) classDefs = getattr(self, "classDefs", None)
if classDefs is None: if classDefs is None:
@ -316,13 +316,13 @@ class ClassDef(FormatSwitchingBaseTable):
rawTable = {"StartGlyph": startGlyphName, "ClassValueArray": classes} rawTable = {"StartGlyph": startGlyphName, "ClassValueArray": classes}
self.Format = format self.Format = format
return rawTable return rawTable
def toXML2(self, xmlWriter, font): def toXML2(self, xmlWriter, font):
items = sorted(self.classDefs.items()) items = sorted(self.classDefs.items())
for glyphName, cls in items: for glyphName, cls in items:
xmlWriter.simpletag("ClassDef", [("glyph", glyphName), ("class", cls)]) xmlWriter.simpletag("ClassDef", [("glyph", glyphName), ("class", cls)])
xmlWriter.newline() xmlWriter.newline()
def fromXML(self, name, attrs, content, font): def fromXML(self, name, attrs, content, font):
classDefs = getattr(self, "classDefs", None) classDefs = getattr(self, "classDefs", None)
if classDefs is None: if classDefs is None:
@ -332,7 +332,7 @@ class ClassDef(FormatSwitchingBaseTable):
class AlternateSubst(FormatSwitchingBaseTable): class AlternateSubst(FormatSwitchingBaseTable):
def postRead(self, rawTable, font): def postRead(self, rawTable, font):
alternates = {} alternates = {}
if self.Format == 1: if self.Format == 1:
@ -346,7 +346,7 @@ class AlternateSubst(FormatSwitchingBaseTable):
assert 0, "unknown format: %s" % self.Format assert 0, "unknown format: %s" % self.Format
self.alternates = alternates self.alternates = alternates
del self.Format # Don't need this anymore del self.Format # Don't need this anymore
def preWrite(self, font): def preWrite(self, font):
self.Format = 1 self.Format = 1
alternates = getattr(self, "alternates", None) alternates = getattr(self, "alternates", None)
@ -370,9 +370,9 @@ class AlternateSubst(FormatSwitchingBaseTable):
# Also useful in that when splitting a sub-table because of an offset overflow # Also useful in that when splitting a sub-table because of an offset overflow
# I don't need to calculate the change in the subtable offset due to the change in the coverage table size. # I don't need to calculate the change in the subtable offset due to the change in the coverage table size.
# Allows packing more rules in subtable. # Allows packing more rules in subtable.
self.sortCoverageLast = 1 self.sortCoverageLast = 1
return {"Coverage": cov, "AlternateSet": alternates} return {"Coverage": cov, "AlternateSet": alternates}
def toXML2(self, xmlWriter, font): def toXML2(self, xmlWriter, font):
items = sorted(self.alternates.items()) items = sorted(self.alternates.items())
for glyphName, alternates in items: for glyphName, alternates in items:
@ -383,7 +383,7 @@ class AlternateSubst(FormatSwitchingBaseTable):
xmlWriter.newline() xmlWriter.newline()
xmlWriter.endtag("AlternateSet") xmlWriter.endtag("AlternateSet")
xmlWriter.newline() xmlWriter.newline()
def fromXML(self, name, attrs, content, font): def fromXML(self, name, attrs, content, font):
alternates = getattr(self, "alternates", None) alternates = getattr(self, "alternates", None)
if alternates is None: if alternates is None:
@ -400,7 +400,7 @@ class AlternateSubst(FormatSwitchingBaseTable):
class LigatureSubst(FormatSwitchingBaseTable): class LigatureSubst(FormatSwitchingBaseTable):
def postRead(self, rawTable, font): def postRead(self, rawTable, font):
ligatures = {} ligatures = {}
if self.Format == 1: if self.Format == 1:
@ -413,7 +413,7 @@ class LigatureSubst(FormatSwitchingBaseTable):
assert 0, "unknown format: %s" % self.Format assert 0, "unknown format: %s" % self.Format
self.ligatures = ligatures self.ligatures = ligatures
del self.Format # Don't need this anymore del self.Format # Don't need this anymore
def preWrite(self, font): def preWrite(self, font):
self.Format = 1 self.Format = 1
ligatures = getattr(self, "ligatures", None) ligatures = getattr(self, "ligatures", None)
@ -438,9 +438,9 @@ class LigatureSubst(FormatSwitchingBaseTable):
# Useful in that when splitting a sub-table because of an offset overflow # Useful in that when splitting a sub-table because of an offset overflow
# I don't need to calculate the change in subtabl offset due to the coverage table size. # I don't need to calculate the change in subtabl offset due to the coverage table size.
# Allows packing more rules in subtable. # Allows packing more rules in subtable.
self.sortCoverageLast = 1 self.sortCoverageLast = 1
return {"Coverage": cov, "LigatureSet": ligSets} return {"Coverage": cov, "LigatureSet": ligSets}
def toXML2(self, xmlWriter, font): def toXML2(self, xmlWriter, font):
items = sorted(self.ligatures.items()) items = sorted(self.ligatures.items())
for glyphName, ligSets in items: for glyphName, ligSets in items:
@ -452,7 +452,7 @@ class LigatureSubst(FormatSwitchingBaseTable):
xmlWriter.newline() xmlWriter.newline()
xmlWriter.endtag("LigatureSet") xmlWriter.endtag("LigatureSet")
xmlWriter.newline() xmlWriter.newline()
def fromXML(self, name, attrs, content, font): def fromXML(self, name, attrs, content, font):
ligatures = getattr(self, "ligatures", None) ligatures = getattr(self, "ligatures", None)
if ligatures is None: if ligatures is None:
@ -513,7 +513,7 @@ _equivalents = {
def fixLookupOverFlows(ttf, overflowRecord): def fixLookupOverFlows(ttf, overflowRecord):
""" Either the offset from the LookupList to a lookup overflowed, or """ Either the offset from the LookupList to a lookup overflowed, or
an offset from a lookup to a subtable overflowed. an offset from a lookup to a subtable overflowed.
The table layout is: The table layout is:
GPSO/GUSB GPSO/GUSB
Script List Script List
@ -532,7 +532,7 @@ def fixLookupOverFlows(ttf, overflowRecord):
SubTable[n] and contents SubTable[n] and contents
If the offset to a lookup overflowed (SubTableIndex is None) If the offset to a lookup overflowed (SubTableIndex is None)
we must promote the *previous* lookup to an Extension type. we must promote the *previous* lookup to an Extension type.
If the offset from a lookup to subtable overflowed, then we must promote it If the offset from a lookup to subtable overflowed, then we must promote it
to an Extension Lookup type. to an Extension Lookup type.
""" """
ok = 0 ok = 0
@ -554,7 +554,7 @@ def fixLookupOverFlows(ttf, overflowRecord):
if lookupIndex < 0: if lookupIndex < 0:
return ok return ok
lookup = lookups[lookupIndex] lookup = lookups[lookupIndex]
for si in range(len(lookup.SubTable)): for si in range(len(lookup.SubTable)):
subTable = lookup.SubTable[si] subTable = lookup.SubTable[si]
extSubTableClass = lookupTypes[overflowRecord.tableType][extType] extSubTableClass = lookupTypes[overflowRecord.tableType][extType]
@ -570,7 +570,7 @@ def splitAlternateSubst(oldSubTable, newSubTable, overflowRecord):
newSubTable.Format = oldSubTable.Format newSubTable.Format = oldSubTable.Format
if hasattr(oldSubTable, 'sortCoverageLast'): if hasattr(oldSubTable, 'sortCoverageLast'):
newSubTable.sortCoverageLast = oldSubTable.sortCoverageLast newSubTable.sortCoverageLast = oldSubTable.sortCoverageLast
oldAlts = sorted(oldSubTable.alternates.items()) oldAlts = sorted(oldSubTable.alternates.items())
oldLen = len(oldAlts) oldLen = len(oldAlts)
@ -580,7 +580,7 @@ def splitAlternateSubst(oldSubTable, newSubTable, overflowRecord):
newLen = oldLen//2 newLen = oldLen//2
elif overflowRecord.itemName == 'AlternateSet': elif overflowRecord.itemName == 'AlternateSet':
# We just need to back up by two items # We just need to back up by two items
# from the overflowed AlternateSet index to make sure the offset # from the overflowed AlternateSet index to make sure the offset
# to the Coverage table doesn't overflow. # to the Coverage table doesn't overflow.
newLen = overflowRecord.itemIndex - 1 newLen = overflowRecord.itemIndex - 1
@ -607,7 +607,7 @@ def splitLigatureSubst(oldSubTable, newSubTable, overflowRecord):
newLen = oldLen//2 newLen = oldLen//2
elif overflowRecord.itemName == 'LigatureSet': elif overflowRecord.itemName == 'LigatureSet':
# We just need to back up by two items # We just need to back up by two items
# from the overflowed AlternateSet index to make sure the offset # from the overflowed AlternateSet index to make sure the offset
# to the Coverage table doesn't overflow. # to the Coverage table doesn't overflow.
newLen = overflowRecord.itemIndex - 1 newLen = overflowRecord.itemIndex - 1
@ -647,7 +647,7 @@ splitTable = { 'GSUB': {
} }
def fixSubTableOverFlows(ttf, overflowRecord): def fixSubTableOverFlows(ttf, overflowRecord):
""" """
An offset has overflowed within a sub-table. We need to divide this subtable into smaller parts. An offset has overflowed within a sub-table. We need to divide this subtable into smaller parts.
""" """
ok = 0 ok = 0
@ -694,10 +694,10 @@ def fixSubTableOverFlows(ttf, overflowRecord):
def _buildClasses(): def _buildClasses():
import re import re
from .otData import otData from .otData import otData
formatPat = re.compile("([A-Za-z0-9]+)Format(\d+)$") formatPat = re.compile("([A-Za-z0-9]+)Format(\d+)$")
namespace = globals() namespace = globals()
# populate module with classes # populate module with classes
for name, table in otData: for name, table in otData:
baseClass = BaseTable baseClass = BaseTable
@ -710,12 +710,12 @@ def _buildClasses():
# the class doesn't exist yet, so the base implementation is used. # the class doesn't exist yet, so the base implementation is used.
cls = type(name, (baseClass,), {}) cls = type(name, (baseClass,), {})
namespace[name] = cls namespace[name] = cls
for base, alts in _equivalents.items(): for base, alts in _equivalents.items():
base = namespace[base] base = namespace[base]
for alt in alts: for alt in alts:
namespace[alt] = type(alt, (base,), {}) namespace[alt] = type(alt, (base,), {})
global lookupTypes global lookupTypes
lookupTypes = { lookupTypes = {
'GSUB': { 'GSUB': {
@ -753,7 +753,7 @@ def _buildClasses():
featureParamTypes['ss%02d' % i] = FeatureParamsStylisticSet featureParamTypes['ss%02d' % i] = FeatureParamsStylisticSet
for i in range(1, 99+1): for i in range(1, 99+1):
featureParamTypes['cv%02d' % i] = FeatureParamsCharacterVariants featureParamTypes['cv%02d' % i] = FeatureParamsCharacterVariants
# add converters to classes # add converters to classes
from .otConverters import buildConverters from .otConverters import buildConverters
for name, table in otData: for name, table in otData:

View File

@ -31,7 +31,7 @@ class Glyph(object):
self.rawdata = rawdata self.rawdata = rawdata
self.graphicType = graphicType self.graphicType = graphicType
self.imageData = imageData self.imageData = imageData
# fix self.graphicType if it is null terminated or too short # fix self.graphicType if it is null terminated or too short
if self.graphicType is not None: if self.graphicType is not None:
if self.graphicType[-1] == "\0": if self.graphicType[-1] == "\0":

View File

@ -199,30 +199,30 @@ def _skipWhite(data, pos):
class Program(object): class Program(object):
def __init__(self): def __init__(self):
pass pass
def fromBytecode(self, bytecode): def fromBytecode(self, bytecode):
self.bytecode = array.array("B", bytecode) self.bytecode = array.array("B", bytecode)
if hasattr(self, "assembly"): if hasattr(self, "assembly"):
del self.assembly del self.assembly
def fromAssembly(self, assembly): def fromAssembly(self, assembly):
self.assembly = assembly self.assembly = assembly
if hasattr(self, "bytecode"): if hasattr(self, "bytecode"):
del self.bytecode del self.bytecode
def getBytecode(self): def getBytecode(self):
if not hasattr(self, "bytecode"): if not hasattr(self, "bytecode"):
self._assemble() self._assemble()
return self.bytecode.tostring() return self.bytecode.tostring()
def getAssembly(self, preserve=False): def getAssembly(self, preserve=False):
if not hasattr(self, "assembly"): if not hasattr(self, "assembly"):
self._disassemble(preserve=preserve) self._disassemble(preserve=preserve)
return self.assembly return self.assembly
def toXML(self, writer, ttFont): def toXML(self, writer, ttFont):
if not hasattr (ttFont, "disassembleInstructions") or ttFont.disassembleInstructions: if not hasattr (ttFont, "disassembleInstructions") or ttFont.disassembleInstructions:
assembly = self.getAssembly() assembly = self.getAssembly()
@ -255,7 +255,7 @@ class Program(object):
writer.newline() writer.newline()
writer.dumphex(self.getBytecode()) writer.dumphex(self.getBytecode())
writer.endtag("bytecode") writer.endtag("bytecode")
def fromXML(self, name, attrs, content, ttFont): def fromXML(self, name, attrs, content, ttFont):
if name == "assembly": if name == "assembly":
self.fromAssembly(strjoin(content)) self.fromAssembly(strjoin(content))
@ -264,7 +264,7 @@ class Program(object):
else: else:
assert name == "bytecode" assert name == "bytecode"
self.fromBytecode(readHex(content)) self.fromBytecode(readHex(content))
def _assemble(self): def _assemble(self):
assembly = self.assembly assembly = self.assembly
if isinstance(assembly, type([])): if isinstance(assembly, type([])):
@ -282,7 +282,7 @@ class Program(object):
if comment: if comment:
pos = _skipWhite(assembly, pos) pos = _skipWhite(assembly, pos)
continue continue
arg = arg.strip() arg = arg.strip()
if mnemonic.startswith("INSTR"): if mnemonic.startswith("INSTR"):
# Unknown instruction # Unknown instruction
@ -383,11 +383,11 @@ class Program(object):
push(value) push(value)
pos = _skipWhite(assembly, pos) pos = _skipWhite(assembly, pos)
if bytecode: if bytecode:
assert max(bytecode) < 256 and min(bytecode) >= 0 assert max(bytecode) < 256 and min(bytecode) >= 0
self.bytecode = array.array("B", bytecode) self.bytecode = array.array("B", bytecode)
def _disassemble(self, preserve=False): def _disassemble(self, preserve=False):
assembly = [] assembly = []
i = 0 i = 0
@ -456,7 +456,7 @@ def _test():
""" """
bc = b"""@;:9876543210/.-,+*)(\'&%$#"! \037\036\035\034\033\032\031\030\027\026\025\024\023\022\021\020\017\016\015\014\013\012\011\010\007\006\005\004\003\002\001\000,\001\260\030CXEj\260\031C`\260F#D#\020 \260FN\360M/\260\000\022\033!#\0213Y-,\001\260\030CX\260\005+\260\000\023K\260\024PX\261\000@8Y\260\006+\033!#\0213Y-,\001\260\030CXN\260\003%\020\362!\260\000\022M\033 E\260\004%\260\004%#Jad\260(RX!#\020\326\033\260\003%\020\362!\260\000\022YY-,\260\032CX!!\033\260\002%\260\002%I\260\003%\260\003%Ja d\260\020PX!!!\033\260\003%\260\003%I\260\000PX\260\000PX\270\377\3428!\033\260\0208!Y\033\260\000RX\260\0368!\033\270\377\3608!YYYY-,\001\260\030CX\260\005+\260\000\023K\260\024PX\271\000\000\377\3008Y\260\006+\033!#\0213Y-,N\001\212\020\261F\031CD\260\000\024\261\000F\342\260\000\025\271\000\000\377\3608\000\260\000<\260(+\260\002%\020\260\000<-,\001\030\260\000/\260\001\024\362\260\001\023\260\001\025M\260\000\022-,\001\260\030CX\260\005+\260\000\023\271\000\000\377\3408\260\006+\033!#\0213Y-,\001\260\030CXEdj#Edi\260\031Cd``\260F#D#\020 \260F\360/\260\000\022\033!! \212 \212RX\0213\033!!YY-,\001\261\013\012C#Ce\012-,\000\261\012\013C#C\013-,\000\260F#p\261\001F>\001\260F#p\261\002FE:\261\002\000\010\015-,\260\022+\260\002%E\260\002%Ej\260@\213`\260\002%#D!!!-,\260\023+\260\002%E\260\002%Ej\270\377\300\214`\260\002%#D!!!-,\260\000\260\022+!!!-,\260\000\260\023+!!!-,\001\260\006C\260\007Ce\012-, i\260@a\260\000\213 \261,\300\212\214\270\020\000b`+\014d#da\\X\260\003aY-,\261\000\003%EhT\260\034KPZX\260\003%E\260\003%E`h \260\004%#D\260\004%#D\033\260\003% Eh \212#D\260\003%Eh`\260\003%#DY-,\260\003% Eh \212#D\260\003%Edhe`\260\004%\260\001`#D-,\260\011CX\207!\300\033\260\022CX\207E\260\021+\260G#D\260Gz\344\033\003\212E\030i \260G#D\212\212\207 \260\240QX\260\021+\260G#D\260Gz\344\033!\260Gz\344YYY\030-, \212E#Eh`D-,EjB-,\001\030/-,\001\260\030CX\260\004%\260\004%Id#Edi\260@\213a \260\200bj\260\002%\260\002%a\214\260\031C`\260F#D!\212\020\260F\366!\033!!!!Y-,\001\260\030CX\260\002%E\260\002%Ed`j\260\003%Eja \260\004%Ej \212\213e\260\004%#D\214\260\003%#D!!\033 EjD EjDY-,\001 E\260\000U\260\030CZXEh#Ei\260@\213a \260\200bj \212#a \260\003%\213e\260\004%#D\214\260\003%#D!!\033!!\260\031+Y-,\001\212\212Ed#EdadB-,\260\004%\260\004%\260\031+\260\030CX\260\004%\260\004%\260\003%\260\033+\001\260\002%C\260@T\260\002%C\260\000TZX\260\003% E\260@aDY\260\002%C\260\000T\260\002%C\260@TZX\260\004% E\260@`DYY!!!!-,\001KRXC\260\002%E#aD\033!!Y-,\001KRXC\260\002%E#`D\033!!Y-,KRXED\033!!Y-,\001 \260\003%#I\260@`\260 c \260\000RX#\260\002%8#\260\002%e8\000\212c8\033!!!!!Y\001-,KPXED\033!!Y-,\001\260\005%\020# \212\365\000\260\001`#\355\354-,\001\260\005%\020# \212\365\000\260\001a#\355\354-,\001\260\006%\020\365\000\355\354-,F#F`\212\212F# F\212`\212a\270\377\200b# \020#\212\261KK\212pE` \260\000PX\260\001a\270\377\272\213\033\260F\214Y\260\020`h\001:-, E\260\003%FRX\260\002%F ha\260\003%\260\003%?#!8\033!\021Y-, E\260\003%FPX\260\002%F ha\260\003%\260\003%?#!8\033!\021Y-,\000\260\007C\260\006C\013-,\212\020\354-,\260\014CX!\033 F\260\000RX\270\377\3608\033\260\0208YY-, \260\000UX\270\020\000c\260\003%Ed\260\003%Eda\260\000SX\260\002\033\260@a\260\003Y%EiSXED\033!!Y\033!\260\002%E\260\002%Ead\260(QXED\033!!YY-,!!\014d#d\213\270@\000b-,!\260\200QX\014d#d\213\270 \000b\033\262\000@/+Y\260\002`-,!\260\300QX\014d#d\213\270\025Ub\033\262\000\200/+Y\260\002`-,\014d#d\213\270@\000b`#!-,KSX\260\004%\260\004%Id#Edi\260@\213a \260\200bj\260\002%\260\002%a\214\260F#D!\212\020\260F\366!\033!\212\021#\022 9/Y-,\260\002%\260\002%Id\260\300TX\270\377\3708\260\0108\033!!Y-,\260\023CX\003\033\002Y-,\260\023CX\002\033\003Y-,\260\012+#\020 <\260\027+-,\260\002%\270\377\3608\260(+\212\020# \320#\260\020+\260\005CX\300\033<Y \020\021\260\000\022\001-,KS#KQZX8\033!!Y-,\001\260\002%\020\320#\311\001\260\001\023\260\000\024\020\260\001<\260\001\026-,\001\260\000\023\260\001\260\003%I\260\003\0278\260\001\023-,KS#KQZX E\212`D\033!!Y-, 9/-""" bc = b"""@;:9876543210/.-,+*)(\'&%$#"! \037\036\035\034\033\032\031\030\027\026\025\024\023\022\021\020\017\016\015\014\013\012\011\010\007\006\005\004\003\002\001\000,\001\260\030CXEj\260\031C`\260F#D#\020 \260FN\360M/\260\000\022\033!#\0213Y-,\001\260\030CX\260\005+\260\000\023K\260\024PX\261\000@8Y\260\006+\033!#\0213Y-,\001\260\030CXN\260\003%\020\362!\260\000\022M\033 E\260\004%\260\004%#Jad\260(RX!#\020\326\033\260\003%\020\362!\260\000\022YY-,\260\032CX!!\033\260\002%\260\002%I\260\003%\260\003%Ja d\260\020PX!!!\033\260\003%\260\003%I\260\000PX\260\000PX\270\377\3428!\033\260\0208!Y\033\260\000RX\260\0368!\033\270\377\3608!YYYY-,\001\260\030CX\260\005+\260\000\023K\260\024PX\271\000\000\377\3008Y\260\006+\033!#\0213Y-,N\001\212\020\261F\031CD\260\000\024\261\000F\342\260\000\025\271\000\000\377\3608\000\260\000<\260(+\260\002%\020\260\000<-,\001\030\260\000/\260\001\024\362\260\001\023\260\001\025M\260\000\022-,\001\260\030CX\260\005+\260\000\023\271\000\000\377\3408\260\006+\033!#\0213Y-,\001\260\030CXEdj#Edi\260\031Cd``\260F#D#\020 \260F\360/\260\000\022\033!! \212 \212RX\0213\033!!YY-,\001\261\013\012C#Ce\012-,\000\261\012\013C#C\013-,\000\260F#p\261\001F>\001\260F#p\261\002FE:\261\002\000\010\015-,\260\022+\260\002%E\260\002%Ej\260@\213`\260\002%#D!!!-,\260\023+\260\002%E\260\002%Ej\270\377\300\214`\260\002%#D!!!-,\260\000\260\022+!!!-,\260\000\260\023+!!!-,\001\260\006C\260\007Ce\012-, i\260@a\260\000\213 \261,\300\212\214\270\020\000b`+\014d#da\\X\260\003aY-,\261\000\003%EhT\260\034KPZX\260\003%E\260\003%E`h \260\004%#D\260\004%#D\033\260\003% Eh \212#D\260\003%Eh`\260\003%#DY-,\260\003% Eh \212#D\260\003%Edhe`\260\004%\260\001`#D-,\260\011CX\207!\300\033\260\022CX\207E\260\021+\260G#D\260Gz\344\033\003\212E\030i \260G#D\212\212\207 \260\240QX\260\021+\260G#D\260Gz\344\033!\260Gz\344YYY\030-, \212E#Eh`D-,EjB-,\001\030/-,\001\260\030CX\260\004%\260\004%Id#Edi\260@\213a \260\200bj\260\002%\260\002%a\214\260\031C`\260F#D!\212\020\260F\366!\033!!!!Y-,\001\260\030CX\260\002%E\260\002%Ed`j\260\003%Eja \260\004%Ej \212\213e\260\004%#D\214\260\003%#D!!\033 EjD EjDY-,\001 E\260\000U\260\030CZXEh#Ei\260@\213a \260\200bj \212#a \260\003%\213e\260\004%#D\214\260\003%#D!!\033!!\260\031+Y-,\001\212\212Ed#EdadB-,\260\004%\260\004%\260\031+\260\030CX\260\004%\260\004%\260\003%\260\033+\001\260\002%C\260@T\260\002%C\260\000TZX\260\003% E\260@aDY\260\002%C\260\000T\260\002%C\260@TZX\260\004% E\260@`DYY!!!!-,\001KRXC\260\002%E#aD\033!!Y-,\001KRXC\260\002%E#`D\033!!Y-,KRXED\033!!Y-,\001 \260\003%#I\260@`\260 c \260\000RX#\260\002%8#\260\002%e8\000\212c8\033!!!!!Y\001-,KPXED\033!!Y-,\001\260\005%\020# \212\365\000\260\001`#\355\354-,\001\260\005%\020# \212\365\000\260\001a#\355\354-,\001\260\006%\020\365\000\355\354-,F#F`\212\212F# F\212`\212a\270\377\200b# \020#\212\261KK\212pE` \260\000PX\260\001a\270\377\272\213\033\260F\214Y\260\020`h\001:-, E\260\003%FRX\260\002%F ha\260\003%\260\003%?#!8\033!\021Y-, E\260\003%FPX\260\002%F ha\260\003%\260\003%?#!8\033!\021Y-,\000\260\007C\260\006C\013-,\212\020\354-,\260\014CX!\033 F\260\000RX\270\377\3608\033\260\0208YY-, \260\000UX\270\020\000c\260\003%Ed\260\003%Eda\260\000SX\260\002\033\260@a\260\003Y%EiSXED\033!!Y\033!\260\002%E\260\002%Ead\260(QXED\033!!YY-,!!\014d#d\213\270@\000b-,!\260\200QX\014d#d\213\270 \000b\033\262\000@/+Y\260\002`-,!\260\300QX\014d#d\213\270\025Ub\033\262\000\200/+Y\260\002`-,\014d#d\213\270@\000b`#!-,KSX\260\004%\260\004%Id#Edi\260@\213a \260\200bj\260\002%\260\002%a\214\260F#D!\212\020\260F\366!\033!\212\021#\022 9/Y-,\260\002%\260\002%Id\260\300TX\270\377\3708\260\0108\033!!Y-,\260\023CX\003\033\002Y-,\260\023CX\002\033\003Y-,\260\012+#\020 <\260\027+-,\260\002%\270\377\3608\260(+\212\020# \320#\260\020+\260\005CX\300\033<Y \020\021\260\000\022\001-,KS#KQZX8\033!!Y-,\001\260\002%\020\320#\311\001\260\001\023\260\000\024\020\260\001<\260\001\026-,\001\260\000\023\260\001\260\003%I\260\003\0278\260\001\023-,KS#KQZX E\212`D\033!!Y-, 9/-"""
p = Program() p = Program()
p.fromBytecode(bc) p.fromBytecode(bc)
asm = p.getAssembly(preserve=True) asm = p.getAssembly(preserve=True)

View File

@ -82,7 +82,7 @@ def usage():
print(__doc__ % version) print(__doc__ % version)
sys.exit(2) sys.exit(2)
numberAddedRE = re.compile("#\d+$") numberAddedRE = re.compile("#\d+$")
opentypeheaderRE = re.compile('''sfntVersion=['"]OTTO["']''') opentypeheaderRE = re.compile('''sfntVersion=['"]OTTO["']''')
@ -262,13 +262,13 @@ def parseOptions(args):
rawOptions, files = getopt.getopt(args, "ld:o:fvqht:x:sim:z:baey:") rawOptions, files = getopt.getopt(args, "ld:o:fvqht:x:sim:z:baey:")
except getopt.GetoptError: except getopt.GetoptError:
usage() usage()
if not files: if not files:
usage() usage()
options = Options(rawOptions, len(files)) options = Options(rawOptions, len(files))
jobs = [] jobs = []
for input in files: for input in files:
tp = guessFileType(input) tp = guessFileType(input)
if tp in ("OTF", "TTF", "TTC", "WOFF"): if tp in ("OTF", "TTF", "TTC", "WOFF"):
@ -286,7 +286,7 @@ def parseOptions(args):
else: else:
print('Unknown file type: "%s"' % input) print('Unknown file type: "%s"' % input)
continue continue
if options.outputFile: if options.outputFile:
output = options.outputFile output = options.outputFile
else: else:
@ -329,7 +329,7 @@ def main(args):
waitForKeyPress() waitForKeyPress()
else: else:
raise raise
if __name__ == "__main__": if __name__ == "__main__":
main(sys.argv[1:]) main(sys.argv[1:])