major refactoring, now evaluates everything lazily, so it should be really fast if you only need (say) the glyph order.
git-svn-id: svn://svn.code.sf.net/p/fonttools/code/trunk@241 4cde692c-a291-49d1-8350-778aa11640f8
This commit is contained in:
parent
357d71b438
commit
4756b3a040
@ -1,7 +1,7 @@
|
||||
"""cffLib.py -- read/write tools for Adobe CFF fonts."""
|
||||
|
||||
#
|
||||
# $Id: cffLib.py,v 1.12 2002-05-15 07:41:30 jvr Exp $
|
||||
# $Id: cffLib.py,v 1.13 2002-05-16 18:17:32 jvr Exp $
|
||||
#
|
||||
|
||||
import struct, sstruct
|
||||
@ -20,36 +20,34 @@ cffHeaderFormat = """
|
||||
class CFFFontSet:
|
||||
|
||||
def __init__(self):
|
||||
self.fonts = {}
|
||||
pass
|
||||
|
||||
def decompile(self, file):
|
||||
sstruct.unpack(cffHeaderFormat, file.read(4), self)
|
||||
assert self.major == 1 and self.minor == 0, \
|
||||
"unknown CFF format: %d.%d" % (self.major, self.minor)
|
||||
|
||||
self.fontNames = readINDEX(file)
|
||||
topDicts = readINDEX(file)
|
||||
strings = IndexedStrings(readINDEX(file))
|
||||
globalSubrs = readINDEX(file)
|
||||
|
||||
file.seek(4)
|
||||
xfnames = Index(file)
|
||||
xtopds = Index(file)
|
||||
xstrings = Index(file)
|
||||
xgsubrs = Index(file)
|
||||
|
||||
assert xfnames.toList() == self.fontNames
|
||||
assert xtopds.toList() == topDicts
|
||||
assert xstrings.toList() == strings.strings
|
||||
assert xgsubrs.toList() == globalSubrs
|
||||
|
||||
self.GlobalSubrs = map(psCharStrings.T2CharString, globalSubrs)
|
||||
|
||||
for i in range(len(topDicts)):
|
||||
font = self.fonts[self.fontNames[i]] = CFFFont()
|
||||
self.fontNames = list(Index(file))
|
||||
self.topDictIndex = TopDictIndex(file)
|
||||
self.strings = IndexedStrings(list(Index(file)))
|
||||
self.GlobalSubrs = SubrsIndex(file)
|
||||
self.topDictIndex.strings = self.strings
|
||||
|
||||
def __len__(self):
|
||||
return len(self.fontNames)
|
||||
|
||||
def keys(self):
|
||||
return self.fontNames[:]
|
||||
|
||||
def __getitem__(self, name):
|
||||
try:
|
||||
index = self.fontNames.index(name)
|
||||
except ValueError:
|
||||
raise KeyError, name
|
||||
font = self.topDictIndex[index]
|
||||
if not hasattr(font, "GlobalSubrs"):
|
||||
font.GlobalSubrs = self.GlobalSubrs
|
||||
file.seek(0)
|
||||
font.decompile(file, topDicts[i], strings, self)
|
||||
return font
|
||||
|
||||
def compile(self):
|
||||
strings = IndexedStrings()
|
||||
@ -60,21 +58,14 @@ class CFFFontSet:
|
||||
for fontName in self.fontNames:
|
||||
xmlWriter.begintag("CFFFont", name=fontName)
|
||||
xmlWriter.newline()
|
||||
font = self.fonts[fontName]
|
||||
font = self[fontName]
|
||||
font.toXML(xmlWriter, progress)
|
||||
xmlWriter.endtag("CFFFont")
|
||||
xmlWriter.newline()
|
||||
xmlWriter.newline()
|
||||
xmlWriter.begintag("GlobalSubrs")
|
||||
xmlWriter.newline()
|
||||
for i in range(len(self.GlobalSubrs)):
|
||||
xmlWriter.newline()
|
||||
xmlWriter.begintag("CharString", index=i)
|
||||
xmlWriter.newline()
|
||||
self.GlobalSubrs[i].toXML(xmlWriter)
|
||||
xmlWriter.endtag("CharString")
|
||||
xmlWriter.newline()
|
||||
xmlWriter.newline()
|
||||
self.GlobalSubrs.toXML(xmlWriter, progress)
|
||||
xmlWriter.endtag("GlobalSubrs")
|
||||
xmlWriter.newline()
|
||||
xmlWriter.newline()
|
||||
@ -83,102 +74,347 @@ class CFFFontSet:
|
||||
xxx
|
||||
|
||||
|
||||
class CFFFont:
|
||||
class Index:
|
||||
|
||||
def __init__(self):
|
||||
pass
|
||||
"""This class represents what the CFF spec calls an INDEX."""
|
||||
|
||||
def __getattr__(self, attr):
|
||||
if not topDictDefaults.has_key(attr):
|
||||
raise AttributeError, attr
|
||||
return topDictDefaults[attr]
|
||||
def __init__(self, file):
|
||||
self.file = file
|
||||
count, = struct.unpack(">H", file.read(2))
|
||||
self.count = count
|
||||
self.items = [None] * count
|
||||
if count == 0:
|
||||
self.offsets = []
|
||||
return
|
||||
offSize = ord(file.read(1))
|
||||
self.offsets = offsets = []
|
||||
pad = '\0' * (4 - offSize)
|
||||
for index in range(count+1):
|
||||
chunk = file.read(offSize)
|
||||
chunk = pad + chunk
|
||||
offset, = struct.unpack(">L", chunk)
|
||||
offsets.append(int(offset))
|
||||
self.offsetBase = file.tell() - 1
|
||||
file.seek(self.offsetBase + offsets[-1]) # pretend we've read the whole lot
|
||||
|
||||
def fromDict(self, d):
|
||||
self.__dict__.update(d)
|
||||
def __len__(self):
|
||||
return self.count
|
||||
|
||||
def decompile(self, file, topDictData, strings, fontSet):
|
||||
top = TopDictDecompiler(strings)
|
||||
top.decompile(topDictData)
|
||||
self.fromDict(top.getDict())
|
||||
|
||||
if hasattr(self, "ROS"):
|
||||
isCID = 1
|
||||
# XXX CID subFonts
|
||||
offset = self.FDArray
|
||||
file.seek(offset)
|
||||
fontDicts = readINDEX(file)
|
||||
subFonts = self.subFonts = []
|
||||
for topDictData in fontDicts:
|
||||
subFont = CFFFont()
|
||||
subFonts.append(subFont)
|
||||
subFont.decompile(file, topDictData, strings, None)
|
||||
# XXX
|
||||
else:
|
||||
isCID = 0
|
||||
size, offset = self.Private
|
||||
file.seek(offset)
|
||||
privateData = file.read(size)
|
||||
file.seek(offset)
|
||||
assert len(privateData) == size
|
||||
self.Private = PrivateDict()
|
||||
self.Private.decompile(file, privateData, strings)
|
||||
|
||||
if hasattr(self, "CharStrings"):
|
||||
file.seek(self.CharStrings)
|
||||
rawCharStrings = Index(file)
|
||||
nGlyphs = len(rawCharStrings)
|
||||
|
||||
# get charset (or rather: get glyphNames)
|
||||
if self.charset > 2:
|
||||
file.seek(self.charset)
|
||||
def __getitem__(self, index):
|
||||
item = self.items[index]
|
||||
if item is not None:
|
||||
return item
|
||||
offset = self.offsets[index] + self.offsetBase
|
||||
size = self.offsets[index+1] - self.offsets[index]
|
||||
file = self.file
|
||||
file.seek(offset)
|
||||
data = file.read(size)
|
||||
assert len(data) == size
|
||||
item = self.produceItem(data, file, offset, size)
|
||||
self.items[index] = item
|
||||
return item
|
||||
|
||||
def produceItem(self, data, file, offset, size):
|
||||
return data
|
||||
|
||||
|
||||
class SubrsIndex(Index):
|
||||
|
||||
def produceItem(self, data, file, offset, size):
|
||||
return psCharStrings.T2CharString(data)
|
||||
|
||||
def toXML(self, xmlWriter, progress):
|
||||
for i in range(len(self)):
|
||||
xmlWriter.begintag("CharString", index=i)
|
||||
xmlWriter.newline()
|
||||
self[i].toXML(xmlWriter)
|
||||
xmlWriter.endtag("CharString")
|
||||
xmlWriter.newline()
|
||||
|
||||
|
||||
class CharStrings:
|
||||
|
||||
def __init__(self, file, charset):
|
||||
self.charStringsIndex = SubrsIndex(file)
|
||||
self.nameToIndex = nameToIndex = {}
|
||||
for i in range(len(charset)):
|
||||
nameToIndex[charset[i]] = i
|
||||
|
||||
def keys(self):
|
||||
return self.nameToIndex.keys()
|
||||
|
||||
def has_key(self, name):
|
||||
return self.nameToIndex.has_key(name)
|
||||
|
||||
def __getitem__(self, name):
|
||||
index = self.nameToIndex[name]
|
||||
return self.charStringsIndex[index]
|
||||
|
||||
def toXML(self, xmlWriter, progress):
|
||||
names = self.keys()
|
||||
names.sort()
|
||||
for name in names:
|
||||
xmlWriter.begintag("CharString", name=name)
|
||||
xmlWriter.newline()
|
||||
self[name].toXML(xmlWriter)
|
||||
xmlWriter.endtag("CharString")
|
||||
xmlWriter.newline()
|
||||
|
||||
|
||||
class TopDictIndex(Index):
|
||||
def produceItem(self, data, file, offset, size):
|
||||
top = TopDict(self.strings, file, offset)
|
||||
top.decompile(data)
|
||||
return top
|
||||
|
||||
|
||||
def buildOperatorDict(table):
|
||||
d = {}
|
||||
for op, name, arg, default, conv in table:
|
||||
d[op] = (name, arg)
|
||||
return d
|
||||
|
||||
def buildOrder(table):
|
||||
l = []
|
||||
for op, name, arg, default, conv in table:
|
||||
l.append(name)
|
||||
return l
|
||||
|
||||
def buildDefaults(table):
|
||||
d = {}
|
||||
for op, name, arg, default, conv in table:
|
||||
if default is not None:
|
||||
d[name] = default
|
||||
return d
|
||||
|
||||
def buildConverters(table):
|
||||
d = {}
|
||||
for op, name, arg, default, conv in table:
|
||||
d[name] = conv
|
||||
return d
|
||||
|
||||
|
||||
class PrivateDictConverter:
|
||||
def read(self, parent, value):
|
||||
size, offset = value
|
||||
file = parent.file
|
||||
pr = PrivateDict(parent.strings, file, offset)
|
||||
file.seek(offset)
|
||||
data = file.read(size)
|
||||
len(data) == size
|
||||
pr.decompile(data)
|
||||
return pr
|
||||
def xmlWrite(self, xmlWriter, name, value):
|
||||
xmlWriter.begintag(name)
|
||||
xmlWriter.newline()
|
||||
value.toXML(xmlWriter, None)
|
||||
xmlWriter.endtag(name)
|
||||
xmlWriter.newline()
|
||||
|
||||
class SubrsConverter(PrivateDictConverter):
|
||||
def read(self, parent, value):
|
||||
file = parent.file
|
||||
file.seek(parent.offset + value) # Offset(self)
|
||||
return SubrsIndex(file)
|
||||
|
||||
class CharStringsConverter(PrivateDictConverter):
|
||||
def read(self, parent, value):
|
||||
file = parent.file
|
||||
file.seek(value) # Offset(0)
|
||||
return CharStrings(file, parent.charset)
|
||||
|
||||
class CharsetConverter:
|
||||
def read(self, parent, value):
|
||||
isCID = hasattr(parent, "ROS")
|
||||
if value > 2:
|
||||
numGlyphs = parent.numGlyphs
|
||||
file = parent.file
|
||||
file.seek(value)
|
||||
format = ord(file.read(1))
|
||||
if format == 0:
|
||||
raise NotImplementedError
|
||||
elif format == 1:
|
||||
charset = parseCharsetFormat1(nGlyphs, file, strings, isCID)
|
||||
elif format == 2:
|
||||
charset = parseCharsetFormat2(nGlyphs, file, strings, isCID)
|
||||
elif format == 1 or format == 2:
|
||||
charset = parseCharset(numGlyphs, file, parent.strings, isCID, format)
|
||||
elif format == 3:
|
||||
raise NotImplementedError
|
||||
else:
|
||||
raise NotImplementedError
|
||||
self.charset = charset
|
||||
assert len(charset) == nGlyphs
|
||||
assert len(charset) == numGlyphs
|
||||
else:
|
||||
if isCID:
|
||||
assert value == 0
|
||||
charset = None
|
||||
elif value == 0:
|
||||
charset = ISOAdobe
|
||||
elif value == 1:
|
||||
charset = Expert
|
||||
elif value == 2:
|
||||
charset = ExpertSubset
|
||||
# self.charset:
|
||||
# 0: ISOAdobe (or CID font!)
|
||||
# 1: Expert
|
||||
# 2: ExpertSubset
|
||||
pass
|
||||
|
||||
if hasattr(self, "CharStrings"):
|
||||
self.CharStrings = charStrings = {}
|
||||
if self.CharstringType == 2:
|
||||
# Type 2 CharStrings
|
||||
charStringClass = psCharStrings.T2CharString
|
||||
else:
|
||||
# Type 1 CharStrings
|
||||
charStringClass = psCharStrings.T1CharString
|
||||
for i in range(nGlyphs):
|
||||
charStrings[charset[i]] = charStringClass(rawCharStrings[i])
|
||||
assert len(charStrings) == nGlyphs
|
||||
|
||||
encoding = self.Encoding
|
||||
if encoding > 1:
|
||||
# encoding is an offset from the beginning of 'data' to an encoding subtable
|
||||
raise NotImplementedError
|
||||
self.Encoding = encoding
|
||||
charset = None #
|
||||
return charset
|
||||
def xmlWrite(self, xmlWriter, name, value):
|
||||
# XXX GlyphOrder needs to be stored *somewhere*, but not here...
|
||||
xmlWriter.simpletag("charset", value=value)
|
||||
xmlWriter.newline()
|
||||
|
||||
|
||||
def parseCharset(numGlyphs, file, strings, isCID, format):
|
||||
charset = ['.notdef']
|
||||
count = 1
|
||||
if format == 1:
|
||||
def nLeftFunc(file):
|
||||
return ord(file.read(1))
|
||||
else:
|
||||
def nLeftFunc(file):
|
||||
return struct.unpack(">H", file.read(2))[0]
|
||||
while count < numGlyphs:
|
||||
first, = struct.unpack(">H", file.read(2))
|
||||
nLeft = nLeftFunc(file)
|
||||
if isCID:
|
||||
for CID in range(first, first+nLeft+1):
|
||||
charset.append(CID)
|
||||
else:
|
||||
# self.Encoding:
|
||||
# 0 Standard Encoding
|
||||
# 1 Expert Encoding
|
||||
pass
|
||||
for SID in range(first, first+nLeft+1):
|
||||
charset.append(strings[SID])
|
||||
count = count + nLeft + 1
|
||||
return charset
|
||||
|
||||
|
||||
topDictOperators = [
|
||||
# opcode name argument type default converter
|
||||
(0, 'version', 'SID', None, None),
|
||||
(1, 'Notice', 'SID', None, None),
|
||||
((12, 0), 'Copyright', 'SID', None, None),
|
||||
(2, 'FullName', 'SID', None, None),
|
||||
(3, 'FamilyName', 'SID', None, None),
|
||||
(4, 'Weight', 'SID', None, None),
|
||||
((12, 1), 'isFixedPitch', 'number', 0, None),
|
||||
((12, 2), 'ItalicAngle', 'number', 0, None),
|
||||
((12, 3), 'UnderlinePosition', 'number', None, None),
|
||||
((12, 4), 'UnderlineThickness', 'number', 50, None),
|
||||
((12, 5), 'PaintType', 'number', 0, None),
|
||||
((12, 6), 'CharstringType', 'number', 2, None),
|
||||
((12, 7), 'FontMatrix', 'array', [0.001,0,0,0.001,0,0], None),
|
||||
(13, 'UniqueID', 'number', None, None),
|
||||
(5, 'FontBBox', 'array', [0,0,0,0], None),
|
||||
((12, 8), 'StrokeWidth', 'number', 0, None),
|
||||
(14, 'XUID', 'array', None, None),
|
||||
(15, 'charset', 'number', 0, CharsetConverter()),
|
||||
(16, 'Encoding', 'number', 0, None),
|
||||
(18, 'Private', ('number','number'), None, PrivateDictConverter()),
|
||||
(17, 'CharStrings', 'number', None, CharStringsConverter()), # XXX
|
||||
((12, 20), 'SyntheticBase', 'number', None, None),
|
||||
((12, 21), 'PostScript', 'SID', None, None),
|
||||
((12, 22), 'BaseFontName', 'SID', None, None),
|
||||
((12, 23), 'BaseFontBlend', 'delta', None, None),
|
||||
((12, 30), 'ROS', ('SID','SID','number'), None, None),
|
||||
((12, 31), 'CIDFontVersion', 'number', 0, None),
|
||||
((12, 32), 'CIDFontRevision', 'number', 0, None),
|
||||
((12, 33), 'CIDFontType', 'number', 0, None),
|
||||
((12, 34), 'CIDCount', 'number', 8720, None),
|
||||
((12, 35), 'UIDBase', 'number', None, None),
|
||||
((12, 36), 'FDArray', 'number', None, None),
|
||||
((12, 37), 'FDSelect', 'number', None, None),
|
||||
((12, 38), 'FontName', 'SID', None, None),
|
||||
]
|
||||
|
||||
privateDictOperators = [
|
||||
# opcode name argument type default converter
|
||||
(6, 'BlueValues', 'delta', None, None),
|
||||
(7, 'OtherBlues', 'delta', None, None),
|
||||
(8, 'FamilyBlues', 'delta', None, None),
|
||||
(9, 'FamilyOtherBlues', 'delta', None, None),
|
||||
((12, 9), 'BlueScale', 'number', 0.039625, None),
|
||||
((12, 10), 'BlueShift', 'number', 7, None),
|
||||
((12, 11), 'BlueFuzz', 'number', 1, None),
|
||||
(10, 'StdHW', 'number', None, None),
|
||||
(11, 'StdVW', 'number', None, None),
|
||||
((12, 12), 'StemSnapH', 'delta', None, None),
|
||||
((12, 13), 'StemSnapV', 'delta', None, None),
|
||||
((12, 14), 'ForceBold', 'number', 0, None),
|
||||
((12, 17), 'LanguageGroup', 'number', 0, None),
|
||||
((12, 18), 'ExpansionFactor', 'number', 0.06, None),
|
||||
((12, 19), 'initialRandomSeed', 'number', 0, None),
|
||||
(20, 'defaultWidthX', 'number', 0, None),
|
||||
(21, 'nominalWidthX', 'number', 0, None),
|
||||
(19, 'Subrs', 'number', None, SubrsConverter()),
|
||||
]
|
||||
|
||||
|
||||
class TopDictDecompiler(psCharStrings.DictDecompiler):
|
||||
operators = buildOperatorDict(topDictOperators)
|
||||
|
||||
|
||||
class PrivateDictDecompiler(psCharStrings.DictDecompiler):
|
||||
operators = buildOperatorDict(privateDictOperators)
|
||||
|
||||
|
||||
|
||||
class BaseDict:
|
||||
|
||||
def __init__(self, strings, file, offset):
|
||||
self.rawDict = {}
|
||||
self.file = file
|
||||
self.offset = offset
|
||||
self.strings = strings
|
||||
|
||||
def decompile(self, data):
|
||||
dec = self.decompiler(self.strings)
|
||||
dec.decompile(data)
|
||||
self.rawDict = dec.getDict()
|
||||
self.postDecompile()
|
||||
|
||||
def postDecompile(self):
|
||||
pass
|
||||
|
||||
def __getattr__(self, name):
|
||||
value = self.rawDict.get(name)
|
||||
if value is None:
|
||||
value = self.defaults.get(name)
|
||||
if value is None:
|
||||
raise AttributeError, name
|
||||
conv = self.converters[name]
|
||||
if conv is not None:
|
||||
value = conv.read(self, value)
|
||||
setattr(self, name, value)
|
||||
return value
|
||||
|
||||
def toXML(self, xmlWriter, progress):
|
||||
for name in self.order:
|
||||
value = getattr(self, name, None)
|
||||
if value is None:
|
||||
continue
|
||||
conv = self.converters.get(name)
|
||||
if conv is not None:
|
||||
conv.xmlWrite(xmlWriter, name, value)
|
||||
else:
|
||||
if isinstance(value, types.ListType):
|
||||
value = " ".join(map(str, value))
|
||||
xmlWriter.simpletag(name, value=value)
|
||||
xmlWriter.newline()
|
||||
|
||||
|
||||
class TopDict(BaseDict):
|
||||
|
||||
defaults = buildDefaults(topDictOperators)
|
||||
converters = buildConverters(topDictOperators)
|
||||
order = buildOrder(topDictOperators)
|
||||
decompiler = TopDictDecompiler
|
||||
|
||||
def getGlyphOrder(self):
|
||||
return self.charset
|
||||
|
||||
def setGlyphOrder(self, glyphOrder):
|
||||
self.charset = glyphOrder
|
||||
def postDecompile(self):
|
||||
offset = self.rawDict.get("CharStrings")
|
||||
if offset is None:
|
||||
return
|
||||
# get the number of glyphs beforehand.
|
||||
self.file.seek(offset)
|
||||
self.numGlyphs, = struct.unpack(">H", self.file.read(2))
|
||||
|
||||
def decompileAllCharStrings(self):
|
||||
if self.CharstringType == 2:
|
||||
@ -192,301 +428,17 @@ class CFFFont:
|
||||
# Type 1 CharStrings
|
||||
for charString in self.CharStrings.values():
|
||||
charString.decompile()
|
||||
|
||||
def toXML(self, xmlWriter, progress=None):
|
||||
xmlWriter.newline()
|
||||
genericToXML(self, topDictOrder, {'CharStrings': 'CharString'}, xmlWriter)
|
||||
|
||||
|
||||
class PrivateDict:
|
||||
|
||||
def __init__(self):
|
||||
pass
|
||||
|
||||
def decompile(self, file, privateData, strings):
|
||||
p = PrivateDictDecompiler(strings)
|
||||
p.decompile(privateData)
|
||||
self.fromDict(p.getDict())
|
||||
|
||||
# get local subrs
|
||||
if hasattr(self, "Subrs"):
|
||||
file.seek(self.Subrs, 1)
|
||||
localSubrs = Index(file)
|
||||
self.Subrs = map(psCharStrings.T2CharString, localSubrs)
|
||||
else:
|
||||
self.Subrs = []
|
||||
|
||||
def toXML(self, xmlWriter):
|
||||
xmlWriter.newline()
|
||||
genericToXML(self, privateDictOrder, {'Subrs': 'CharString'}, xmlWriter)
|
||||
|
||||
def __getattr__(self, attr):
|
||||
if not privateDictDefaults.has_key(attr):
|
||||
raise AttributeError, attr
|
||||
return privateDictDefaults[attr]
|
||||
|
||||
def fromDict(self, d):
|
||||
self.__dict__.update(d)
|
||||
class PrivateDict(BaseDict):
|
||||
defaults = buildDefaults(privateDictOperators)
|
||||
converters = buildConverters(privateDictOperators)
|
||||
order = buildOrder(privateDictOperators)
|
||||
decompiler = PrivateDictDecompiler
|
||||
|
||||
|
||||
def readINDEX(file):
|
||||
count, = struct.unpack(">H", file.read(2))
|
||||
if count == 0:
|
||||
return []
|
||||
offSize = ord(file.read(1))
|
||||
offsets = []
|
||||
for index in range(count+1):
|
||||
chunk = file.read(offSize)
|
||||
chunk = '\0' * (4 - offSize) + chunk
|
||||
offset, = struct.unpack(">L", chunk)
|
||||
offset = int(offset)
|
||||
offsets.append(offset)
|
||||
offsetBase = file.tell() - 1
|
||||
prev = offsets[0]
|
||||
stuff = []
|
||||
for next in offsets[1:]:
|
||||
assert offsetBase + prev == file.tell()
|
||||
chunk = file.read(next - prev)
|
||||
assert len(chunk) == next - prev
|
||||
stuff.append(chunk)
|
||||
prev = next
|
||||
return stuff
|
||||
|
||||
|
||||
class Index:
|
||||
|
||||
def __init__(self, file):
|
||||
self.file = file
|
||||
count, = struct.unpack(">H", file.read(2))
|
||||
self.count = count
|
||||
if count == 0:
|
||||
self.offsets = []
|
||||
return
|
||||
offSize = ord(file.read(1))
|
||||
self.offsets = offsets = []
|
||||
pad = '\0' * (4 - offSize)
|
||||
for index in range(count+1):
|
||||
chunk = file.read(offSize)
|
||||
chunk = pad + chunk
|
||||
offset, = struct.unpack(">L", chunk)
|
||||
offsets.append(int(offset))
|
||||
self.offsetBase = file.tell() - 1
|
||||
file.seek(self.offsetBase + offsets[-1])
|
||||
|
||||
def __len__(self):
|
||||
return self.count
|
||||
|
||||
def __getitem__(self, index):
|
||||
offset = self.offsets[index] + self.offsetBase
|
||||
size = self.offsets[index+1] - self.offsets[index]
|
||||
return FileString(self.file, offset, size)
|
||||
|
||||
def toList(self):
|
||||
l = []
|
||||
for item in self:
|
||||
l.append(item[:])
|
||||
return l
|
||||
|
||||
|
||||
class FileString:
|
||||
|
||||
def __init__(self, file, offset, size):
|
||||
self.file = file
|
||||
self.offset = offset
|
||||
self.size = size
|
||||
self.string = None
|
||||
|
||||
def __len__(self):
|
||||
return self.size
|
||||
|
||||
def __getitem__(self, index):
|
||||
return self.get()[index]
|
||||
|
||||
def __getslice__(self, i, j):
|
||||
return self.get()[i:j]
|
||||
|
||||
def get(self):
|
||||
if self.string is None:
|
||||
self.file.seek(self.offset)
|
||||
self.string = self.file.read(self.size)
|
||||
return self.string
|
||||
|
||||
|
||||
def getItems(o):
|
||||
if hasattr(o, "items"):
|
||||
items = o.items()
|
||||
items.sort()
|
||||
return "name", items
|
||||
else:
|
||||
return "index", map(None, range(len(o)), o)
|
||||
|
||||
|
||||
def genericToXML(obj, order, arrayTypes, xmlWriter):
|
||||
for name in order:
|
||||
value = getattr(obj, name, None)
|
||||
if value is None:
|
||||
continue
|
||||
if hasattr(value, "toXML"):
|
||||
xmlWriter.newline()
|
||||
xmlWriter.begintag(name)
|
||||
xmlWriter.newline()
|
||||
value.toXML(xmlWriter)
|
||||
xmlWriter.endtag(name)
|
||||
xmlWriter.newline()
|
||||
xmlWriter.newline()
|
||||
elif arrayTypes.has_key(name):
|
||||
typeName = arrayTypes[name]
|
||||
xmlWriter.newline()
|
||||
xmlWriter.begintag(name)
|
||||
xmlWriter.newline()
|
||||
xmlWriter.newline()
|
||||
label, items = getItems(value)
|
||||
for k, v in items:
|
||||
xmlWriter.begintag(typeName, [(label, k)])
|
||||
xmlWriter.newline()
|
||||
v.toXML(xmlWriter)
|
||||
xmlWriter.endtag(typeName)
|
||||
xmlWriter.newline()
|
||||
xmlWriter.newline()
|
||||
xmlWriter.endtag(name)
|
||||
xmlWriter.newline()
|
||||
xmlWriter.newline()
|
||||
else:
|
||||
if isinstance(value, types.ListType):
|
||||
value = " ".join(map(str, value))
|
||||
xmlWriter.simpletag(name, value=value)
|
||||
xmlWriter.newline()
|
||||
|
||||
|
||||
def parseCharsetFormat1(nGlyphs, file, strings, isCID):
|
||||
charset = ['.notdef']
|
||||
count = 1
|
||||
while count < nGlyphs:
|
||||
first, = struct.unpack(">H", file.read(2))
|
||||
nLeft = ord(file.read(1))
|
||||
if isCID:
|
||||
for CID in range(first, first+nLeft+1):
|
||||
charset.append(CID)
|
||||
else:
|
||||
for SID in range(first, first+nLeft+1):
|
||||
charset.append(strings[SID])
|
||||
count = count + nLeft + 1
|
||||
return charset
|
||||
|
||||
|
||||
def parseCharsetFormat2(nGlyphs, file, strings, isCID):
|
||||
charset = ['.notdef']
|
||||
count = 1
|
||||
while count < nGlyphs:
|
||||
first, = struct.unpack(">H", file.read(2))
|
||||
nLeft, = struct.unpack(">H", file.read(2))
|
||||
if isCID:
|
||||
for CID in range(first, first+nLeft+1):
|
||||
charset.append(CID)
|
||||
else:
|
||||
for SID in range(first, first+nLeft+1):
|
||||
charset.append(strings[SID])
|
||||
count = count + nLeft + 1
|
||||
return charset
|
||||
|
||||
|
||||
def buildOperatorDict(table):
|
||||
d = {}
|
||||
for row in table:
|
||||
d[row[0]] = row[1:3]
|
||||
return d
|
||||
|
||||
def buildOrder(table):
|
||||
l = []
|
||||
for row in table:
|
||||
l.append(row[1])
|
||||
return l
|
||||
|
||||
def buildDefaults(table):
|
||||
d = {}
|
||||
for row in table:
|
||||
if row[3] is not None:
|
||||
d[row[1]] = row[3]
|
||||
return d
|
||||
|
||||
|
||||
topDictOperators = [
|
||||
# opcode name argument type default
|
||||
(0, 'version', 'SID', None),
|
||||
(1, 'Notice', 'SID', None),
|
||||
((12, 0), 'Copyright', 'SID', None),
|
||||
(2, 'FullName', 'SID', None),
|
||||
(3, 'FamilyName', 'SID', None),
|
||||
(4, 'Weight', 'SID', None),
|
||||
((12, 1), 'isFixedPitch', 'number', 0),
|
||||
((12, 2), 'ItalicAngle', 'number', 0),
|
||||
((12, 3), 'UnderlinePosition', 'number', None),
|
||||
((12, 4), 'UnderlineThickness', 'number', 50),
|
||||
((12, 5), 'PaintType', 'number', 0),
|
||||
((12, 6), 'CharstringType', 'number', 2),
|
||||
((12, 7), 'FontMatrix', 'array', [0.001,0,0,0.001,0,0]),
|
||||
(13, 'UniqueID', 'number', None),
|
||||
(5, 'FontBBox', 'array', [0,0,0,0]),
|
||||
((12, 8), 'StrokeWidth', 'number', 0),
|
||||
(14, 'XUID', 'array', None),
|
||||
(15, 'charset', 'number', 0),
|
||||
(16, 'Encoding', 'number', 0),
|
||||
(18, 'Private', ('number','number'), None),
|
||||
(17, 'CharStrings', 'number', None),
|
||||
((12, 20), 'SyntheticBase', 'number', None),
|
||||
((12, 21), 'PostScript', 'SID', None),
|
||||
((12, 22), 'BaseFontName', 'SID', None),
|
||||
((12, 23), 'BaseFontBlend', 'delta', None),
|
||||
((12, 30), 'ROS', ('SID','SID','number'), None),
|
||||
((12, 31), 'CIDFontVersion', 'number', 0),
|
||||
((12, 32), 'CIDFontRevision', 'number', 0),
|
||||
((12, 33), 'CIDFontType', 'number', 0),
|
||||
((12, 34), 'CIDCount', 'number', 8720),
|
||||
((12, 35), 'UIDBase', 'number', None),
|
||||
((12, 36), 'FDArray', 'number', None),
|
||||
((12, 37), 'FDSelect', 'number', None),
|
||||
((12, 38), 'FontName', 'SID', None),
|
||||
]
|
||||
|
||||
topDictDefaults = buildDefaults(topDictOperators)
|
||||
topDictOrder = buildOrder(topDictOperators)
|
||||
|
||||
class TopDictDecompiler(psCharStrings.DictDecompiler):
|
||||
|
||||
operators = buildOperatorDict(topDictOperators)
|
||||
dictDefaults = topDictDefaults
|
||||
|
||||
|
||||
privateDictOperators = [
|
||||
# opcode name argument type default
|
||||
(6, 'BlueValues', 'delta', None),
|
||||
(7, 'OtherBlues', 'delta', None),
|
||||
(8, 'FamilyBlues', 'delta', None),
|
||||
(9, 'FamilyOtherBlues', 'delta', None),
|
||||
((12, 9), 'BlueScale', 'number', 0.039625),
|
||||
((12, 10), 'BlueShift', 'number', 7),
|
||||
((12, 11), 'BlueFuzz', 'number', 1),
|
||||
(10, 'StdHW', 'number', None),
|
||||
(11, 'StdVW', 'number', None),
|
||||
((12, 12), 'StemSnapH', 'delta', None),
|
||||
((12, 13), 'StemSnapV', 'delta', None),
|
||||
((12, 14), 'ForceBold', 'number', 0),
|
||||
((12, 17), 'LanguageGroup', 'number', 0),
|
||||
((12, 18), 'ExpansionFactor', 'number', 0.06),
|
||||
((12, 19), 'initialRandomSeed', 'number', 0),
|
||||
(20, 'defaultWidthX', 'number', 0),
|
||||
(21, 'nominalWidthX', 'number', 0),
|
||||
(19, 'Subrs', 'number', None),
|
||||
]
|
||||
|
||||
privateDictDefaults = buildDefaults(privateDictOperators)
|
||||
privateDictOrder = buildOrder(privateDictOperators)
|
||||
|
||||
class PrivateDictDecompiler(psCharStrings.DictDecompiler):
|
||||
|
||||
operators = buildOperatorDict(privateDictOperators)
|
||||
dictDefaults = privateDictDefaults
|
||||
|
||||
# SID
|
||||
|
||||
class IndexedStrings:
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user