first working version of CFF/T2 compiler; needs cleanup/refactoring, and doesn't import from XML yet; hardly tested.

git-svn-id: svn://svn.code.sf.net/p/fonttools/code/trunk@253 4cde692c-a291-49d1-8350-778aa11640f8
This commit is contained in:
jvr 2002-05-23 21:50:36 +00:00
parent 0011bb6910
commit f2cf9c5d6d
2 changed files with 474 additions and 55 deletions

View File

@ -1,12 +1,12 @@
"""cffLib.py -- read/write tools for Adobe CFF fonts.""" """cffLib.py -- read/write tools for Adobe CFF fonts."""
# #
# $Id: cffLib.py,v 1.20 2002-05-18 20:07:01 jvr Exp $ # $Id: cffLib.py,v 1.21 2002-05-23 21:50:36 jvr Exp $
# #
import struct, sstruct import struct, sstruct
import string import string
import types from types import FloatType, ListType, TupleType
from fontTools.misc import psCharStrings from fontTools.misc import psCharStrings
@ -30,10 +30,11 @@ class CFFFontSet:
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)
self.fontNames = list(Index(file, "fontNames")) self.fontNames = list(Index(file, "fontNames"))
self.topDictIndex = TopDictIndex(file) self.topDictIndex = TopDictIndex(file)
self.strings = IndexedStrings(file) self.strings = IndexedStrings(file)
self.GlobalSubrs = CharStringIndex(file, name="GlobalSubrsIndex") self.GlobalSubrs = GlobalSubrsIndex(file, name="GlobalSubrsIndex")
self.topDictIndex.strings = self.strings self.topDictIndex.strings = self.strings
self.topDictIndex.GlobalSubrs = self.GlobalSubrs self.topDictIndex.GlobalSubrs = self.GlobalSubrs
@ -53,9 +54,25 @@ class CFFFontSet:
raise KeyError, name raise KeyError, name
return self.topDictIndex[index] return self.topDictIndex[index]
def compile(self): def compile(self, file):
strings = IndexedStrings() strings = IndexedStrings()
XXXX writer = CFFWriter()
writer.add(sstruct.pack(cffHeaderFormat, self))
fontNames = Index()
for name in self.fontNames:
fontNames.append(name)
writer.add(fontNames.getCompiler(strings, None))
topCompiler = self.topDictIndex.getCompiler(strings, None)
writer.add(topCompiler)
writer.add(strings.getCompiler())
writer.add(self.GlobalSubrs.getCompiler(strings, None))
for child in topCompiler.getChildren(strings):
writer.add(child)
print writer.data
writer.toFile(file)
def toXML(self, xmlWriter, progress=None): def toXML(self, xmlWriter, progress=None):
xmlWriter.newline() xmlWriter.newline()
@ -78,13 +95,163 @@ class CFFFontSet:
xxx xxx
class CFFWriter:
def __init__(self):
self.data = []
def add(self, table):
self.data.append(table)
def toFile(self, file):
lastPosList = None
count = 1
while 1:
print "XXX iteration", count
count += 1
pos = 0
posList = [pos]
for item in self.data:
if hasattr(item, "setPos"):
item.setPos(pos)
if hasattr(item, "getDataLength"):
pos = pos + item.getDataLength()
else:
pos = pos + len(item)
posList.append(pos)
if posList == lastPosList:
break
lastPosList = posList
begin = file.tell()
posList = [0]
for item in self.data:
if hasattr(item, "toFile"):
item.toFile(file)
else:
file.write(item)
posList.append(file.tell() - begin)
if posList != lastPosList:
print "++++"
print posList
print lastPosList
assert posList == lastPosList
def calcOffSize(largestOffset):
if largestOffset < 0x100:
offSize = 1
elif largestOffset < 0x10000:
offSize = 2
elif largestOffset < 0x1000000:
offSize = 3
else:
offSize = 4
return offSize
class IndexCompiler:
def __init__(self, items, strings, parent):
self.items = self.getItems(items, strings)
self.parent = parent
def getItems(self, items, strings):
return items
def getOffsets(self):
pos = 1
offsets = [pos]
for item in self.items:
if hasattr(item, "getDataLength"):
pos = pos + item.getDataLength()
else:
pos = pos + len(item)
offsets.append(pos)
return offsets
def getDataLength(self):
lastOffset = self.getOffsets()[-1]
offSize = calcOffSize(lastOffset)
dataLength = (
2 + # count
1 + # offSize
(len(self.items) + 1) * offSize + # the offsets
lastOffset - 1 # size of object data
)
return dataLength
def toFile(self, file):
size = self.getDataLength()
start = file.tell()
offsets = self.getOffsets()
writeCard16(file, len(self.items))
offSize = calcOffSize(offsets[-1])
writeCard8(file, offSize)
offSize = -offSize
pack = struct.pack
for offset in offsets:
binOffset = pack(">l", offset)[offSize:]
assert len(binOffset) == -offSize
file.write(binOffset)
for item in self.items:
if hasattr(item, "toFile"):
item.toFile(file)
else:
file.write(item)
assert start + size == file.tell()
class IndexedStringsCompiler(IndexCompiler):
def getItems(self, items, strings):
return items.strings
class TopDictIndexCompiler(IndexCompiler):
def getItems(self, items, strings):
out = []
for item in items:
out.append(item.getCompiler(strings, self))
return out
def getChildren(self, strings):
children = []
for topDict in self.items:
children.extend(topDict.getChildren(strings))
return children
class GlobalSubrsCompiler(IndexCompiler):
def getItems(self, items, strings):
out = []
for cs in items:
cs.compile()
out.append(cs.bytecode)
return out
class SubrsCompiler(GlobalSubrsCompiler):
def setPos(self, pos):
offset = pos - self.parent.pos
self.parent.rawDict["Subrs"] = offset
class CharStringsCompiler(GlobalSubrsCompiler):
def setPos(self, pos):
self.parent.rawDict["CharStrings"] = pos
class Index: class Index:
"""This class represents what the CFF spec calls an INDEX.""" """This class represents what the CFF spec calls an INDEX."""
def __init__(self, file, name=None): compilerClass = IndexCompiler
def __init__(self, file=None, name=None):
if name is None: if name is None:
name = self.__class__.__name__ name = self.__class__.__name__
if file is None:
self.items = []
return
if DEBUG: if DEBUG:
print "loading %s at %s" % (name, file.tell()) print "loading %s at %s" % (name, file.tell())
self.file = file self.file = file
@ -92,11 +259,11 @@ class Index:
self.count = count self.count = count
self.items = [None] * count self.items = [None] * count
if count == 0: if count == 0:
self.offsets = [] self.items = []
return return
offSize = readCard8(file) offSize = readCard8(file)
if DEBUG: if DEBUG:
print "index count: %s offSize: %s" % (count, offSize) print " index count: %s offSize: %s" % (count, offSize)
assert offSize <= 4, "offSize too large: %s" % offSize assert offSize <= 4, "offSize too large: %s" % offSize
self.offsets = offsets = [] self.offsets = offsets = []
pad = '\0' * (4 - offSize) pad = '\0' * (4 - offSize)
@ -107,9 +274,11 @@ class Index:
offsets.append(int(offset)) offsets.append(int(offset))
self.offsetBase = file.tell() - 1 self.offsetBase = file.tell() - 1
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:
print " end of %s at %s" % (name, file.tell())
def __len__(self): def __len__(self):
return self.count return len(self.items)
def __getitem__(self, index): def __getitem__(self, index):
item = self.items[index] item = self.items[index]
@ -128,10 +297,18 @@ class Index:
def produceItem(self, index, data, file, offset, size): def produceItem(self, index, data, file, offset, size):
return data return data
def append(self, item):
self.items.append(item)
class CharStringIndex(Index): def getCompiler(self, strings, parent):
return self.compilerClass(self, strings, parent)
def __init__(self, file, globalSubrs=None, private=None, fdSelect=None, fdArray=None,
class GlobalSubrsIndex(Index):
compilerClass = GlobalSubrsCompiler
def __init__(self, file=None, globalSubrs=None, private=None, fdSelect=None, fdArray=None,
name=None): name=None):
Index.__init__(self, file, name) Index.__init__(self, file, name)
self.globalSubrs = globalSubrs self.globalSubrs = globalSubrs
@ -169,9 +346,14 @@ class CharStringIndex(Index):
sel = fdSelect[index] sel = fdSelect[index]
return self[index], sel return self[index], sel
class SubrsIndex(GlobalSubrsIndex):
compilerClass = SubrsCompiler
class TopDictIndex(Index): class TopDictIndex(Index):
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)
@ -189,7 +371,7 @@ class TopDictIndex(Index):
class CharStrings: class CharStrings:
def __init__(self, file, charset, globalSubrs, private, fdSelect, fdArray): def __init__(self, file, charset, globalSubrs, private, fdSelect, fdArray):
self.charStringsIndex = CharStringIndex(file, globalSubrs, private, fdSelect, fdArray) self.charStringsIndex = SubrsIndex(file, globalSubrs, private, fdSelect, fdArray)
self.nameToIndex = nameToIndex = {} self.nameToIndex = nameToIndex = {}
for i in range(len(charset)): for i in range(len(charset)):
nameToIndex[charset[i]] = i nameToIndex[charset[i]] = i
@ -237,12 +419,34 @@ def readCard16(file):
value, = struct.unpack(">H", file.read(2)) value, = struct.unpack(">H", file.read(2))
return value return value
def writeCard8(file, value):
file.write(chr(value))
def writeCard16(file, value):
file.write(struct.pack(">H", value))
def packCard8(value):
return chr(value)
def packCard16(value):
return struct.pack(">H", value)
def buildOperatorDict(table): def buildOperatorDict(table):
d = {} d = {}
for op, name, arg, default, conv in table: for op, name, arg, default, conv in table:
d[op] = (name, arg) d[op] = (name, arg)
return d return d
def buildOpcodeDict(table):
d = {}
for op, name, arg, default, conv in table:
if type(op) == TupleType:
op = chr(op[0]) + chr(op[1])
else:
op = chr(op)
d[name] = (op, arg)
return d
def buildOrder(table): def buildOrder(table):
l = [] l = []
for op, name, arg, default, conv in table: for op, name, arg, default, conv in table:
@ -266,6 +470,8 @@ def buildConverters(table):
class BaseConverter: class BaseConverter:
def read(self, parent, value): def read(self, parent, value):
return value return value
def write(self, parent, value):
return value
def xmlWrite(self, xmlWriter, name, value): def xmlWrite(self, xmlWriter, name, value):
xmlWriter.begintag(name) xmlWriter.begintag(name)
xmlWriter.newline() xmlWriter.newline()
@ -283,12 +489,16 @@ class PrivateDictConverter(BaseConverter):
len(data) == size len(data) == size
pr.decompile(data) pr.decompile(data)
return pr return pr
def write(self, parent, value):
return (0, 0) # dummy value
class SubrsConverter(BaseConverter): class SubrsConverter(BaseConverter):
def read(self, parent, value): def read(self, parent, value):
file = parent.file file = parent.file
file.seek(parent.offset + value) # Offset(self) file.seek(parent.offset + value) # Offset(self)
return CharStringIndex(file, name="SubrsIndex") return SubrsIndex(file, name="SubrsIndex")
def write(self, parent, value):
return 0 # dummy value
class CharStringsConverter(BaseConverter): class CharStringsConverter(BaseConverter):
def read(self, parent, value): def read(self, parent, value):
@ -303,6 +513,8 @@ class CharStringsConverter(BaseConverter):
private = parent.Private private = parent.Private
file.seek(value) # Offset(0) file.seek(value) # Offset(0)
return CharStrings(file, charset, globalSubrs, private, fdSelect, fdArray) return CharStrings(file, charset, globalSubrs, private, fdSelect, fdArray)
def write(self, parent, value):
return 0 # dummy value
class CharsetConverter: class CharsetConverter:
def read(self, parent, value): def read(self, parent, value):
@ -311,14 +523,18 @@ class CharsetConverter:
numGlyphs = parent.numGlyphs numGlyphs = parent.numGlyphs
file = parent.file file = parent.file
file.seek(value) file.seek(value)
if DEBUG:
print "loading charset at %s" % value
format = readCard8(file) format = readCard8(file)
if format == 0: if format == 0:
raise NotImplementedError charset =parseCharset0(numGlyphs, file, parent.strings)
elif format == 1 or format == 2: elif format == 1 or format == 2:
charset = parseCharset(numGlyphs, file, parent.strings, isCID, format) charset = parseCharset(numGlyphs, file, parent.strings, isCID, format)
else: else:
raise NotImplementedError raise NotImplementedError
assert len(charset) == numGlyphs assert len(charset) == numGlyphs
if DEBUG:
print " charset end at %s" % file.tell()
else: else:
if isCID or not hasattr(parent, "CharStrings"): if isCID or not hasattr(parent, "CharStrings"):
assert value == 0 assert value == 0
@ -335,12 +551,42 @@ class CharsetConverter:
# 2: ExpertSubset # 2: ExpertSubset
charset = None # charset = None #
return charset return charset
def write(self, parent, value):
return 0 # dummy value
def xmlWrite(self, xmlWriter, name, value): def xmlWrite(self, xmlWriter, name, value):
# XXX GlyphOrder needs to be stored *somewhere*, but not here... # XXX GlyphOrder needs to be stored *somewhere*, but not here...
xmlWriter.simpletag("charset", value=value) xmlWriter.simpletag("charset", value=value)
xmlWriter.newline() xmlWriter.newline()
class CharsetCompiler:
def __init__(self, strings, charset, parent):
assert charset[0] == '.notdef'
format = 0 # XXX!
data = [packCard8(format)]
for name in charset[1:]:
data.append(packCard16(strings.getSID(name)))
self.data = "".join(data)
self.parent = parent
def setPos(self, pos):
self.parent.rawDict["charset"] = pos
def getDataLength(self):
return len(self.data)
def toFile(self, file):
file.write(self.data)
def parseCharset0(numGlyphs, file, strings):
charset = [".notdef"]
for i in range(numGlyphs - 1):
SID = readCard16(file)
charset.append(strings[SID])
return charset
def parseCharset(numGlyphs, file, strings, isCID, format): def parseCharset(numGlyphs, file, strings, isCID, format):
charset = ['.notdef'] charset = ['.notdef']
count = 1 count = 1
@ -405,14 +651,15 @@ class FDSelectConverter:
class ROSConverter(BaseConverter): class ROSConverter(BaseConverter):
def xmlWrite(self, xmlWriter, name, value): def xmlWrite(self, xmlWriter, name, value):
registry, order, supplement = value registry, order, supplement = value
xmlWriter.simpletag(name, [('registry', registry), ('order', order), xmlWriter.simpletag(name, [('Registry', registry), ('Order', order),
('supplement', supplement)]) ('Supplement', supplement)])
xmlWriter.newline() xmlWriter.newline()
topDictOperators = [ topDictOperators = [
# opcode name argument type default converter # opcode name argument type default converter
((12, 30), 'ROS', ('SID','SID','number'), None, ROSConverter()), ((12, 30), 'ROS', ('SID','SID','number'), None, ROSConverter()),
((12, 20), 'SyntheticBase', 'number', None, None),
(0, 'version', 'SID', None, None), (0, 'version', 'SID', None, None),
(1, 'Notice', 'SID', None, None), (1, 'Notice', 'SID', None, None),
((12, 0), 'Copyright', 'SID', None, None), ((12, 0), 'Copyright', 'SID', None, None),
@ -432,7 +679,6 @@ topDictOperators = [
((12, 8), 'StrokeWidth', 'number', 0, None), ((12, 8), 'StrokeWidth', 'number', 0, None),
(14, 'XUID', 'array', None, None), (14, 'XUID', 'array', None, None),
(15, 'charset', 'number', 0, CharsetConverter()), (15, 'charset', 'number', 0, CharsetConverter()),
((12, 20), 'SyntheticBase', 'number', None, None),
((12, 21), 'PostScript', 'SID', None, None), ((12, 21), 'PostScript', 'SID', None, None),
((12, 22), 'BaseFontName', 'SID', None, None), ((12, 22), 'BaseFontName', 'SID', None, None),
((12, 23), 'BaseFontBlend', 'delta', None, None), ((12, 23), 'BaseFontBlend', 'delta', None, None),
@ -462,6 +708,8 @@ privateDictOperators = [
((12, 12), 'StemSnapH', 'delta', None, None), ((12, 12), 'StemSnapH', 'delta', None, None),
((12, 13), 'StemSnapV', 'delta', None, None), ((12, 13), 'StemSnapV', 'delta', None, None),
((12, 14), 'ForceBold', 'number', 0, None), ((12, 14), 'ForceBold', 'number', 0, None),
((12, 15), 'ForceBoldThreshold', 'number', None, None), # deprecated
((12, 16), 'lenIV', 'number', None, None), # deprecated
((12, 17), 'LanguageGroup', 'number', 0, None), ((12, 17), 'LanguageGroup', 'number', 0, None),
((12, 18), 'ExpansionFactor', 'number', 0.06, None), ((12, 18), 'ExpansionFactor', 'number', 0.06, None),
((12, 19), 'initialRandomSeed', 'number', 0, None), ((12, 19), 'initialRandomSeed', 'number', 0, None),
@ -479,20 +727,138 @@ class PrivateDictDecompiler(psCharStrings.DictDecompiler):
operators = buildOperatorDict(privateDictOperators) operators = buildOperatorDict(privateDictOperators)
class DictCompiler:
def __init__(self, dictObj, strings, parent):
assert isinstance(strings, IndexedStrings)
self.dictObj = dictObj
self.strings = strings
self.parent = parent
rawDict = {}
for name in dictObj.order:
value = getattr(dictObj, name, None)
if value is None:
continue
conv = dictObj.converters[name]
if conv:
value = conv.write(dictObj, value)
if value == dictObj.defaults.get(name):
continue
rawDict[name] = value
self.rawDict = rawDict
def setPos(self, pos):
pass
def getDataLength(self):
return len(self.compile())
def compile(self):
rawDict = self.rawDict
data = []
for name in self.dictObj.order:
value = rawDict.get(name)
if value is None:
continue
op, argType = self.opcodes[name]
if type(argType) == TupleType:
l = len(argType)
assert len(value) == l, "value doesn't match arg type"
for i in range(l):
arg = argType[l - i - 1]
v = value[i]
arghandler = getattr(self, "arg_" + arg)
data.append(arghandler(v))
else:
arghandler = getattr(self, "arg_" + argType)
data.append(arghandler(value))
data.append(op)
return "".join(data)
def toFile(self, file):
file.write(self.compile())
def arg_number(self, num):
return encodeNumber(num)
def arg_SID(self, s):
return psCharStrings.encodeIntCFF(self.strings.getSID(s))
def arg_array(self, value):
data = []
for num in value:
data.append(encodeNumber(num))
return "".join(data)
def arg_delta(self, value):
out = []
last = 0
for v in value:
out.append(v - last)
last = v
data = []
for num in out:
data.append(encodeNumber(num))
return "".join(data)
def encodeNumber(num):
if type(num) == FloatType:
return psCharStrings.encodeFloat(num)
else:
return psCharStrings.encodeIntCFF(num)
class TopDictCompiler(DictCompiler):
opcodes = buildOpcodeDict(topDictOperators)
def getChildren(self, strings):
children = []
if hasattr(self.dictObj, "charset"):
children.append(CharsetCompiler(strings, self.dictObj.charset, self))
if hasattr(self.dictObj, "CharStrings"):
items = []
charStrings = self.dictObj.CharStrings
for name in self.dictObj.charset:
items.append(charStrings[name])
charStringsComp = CharStringsCompiler(items, strings, self)
children.append(charStringsComp)
if hasattr(self.dictObj, "Private"):
privComp = self.dictObj.Private.getCompiler(strings, self)
children.append(privComp)
children.extend(privComp.getChildren(strings))
return children
class PrivateDictCompiler(DictCompiler):
opcodes = buildOpcodeDict(privateDictOperators)
def setPos(self, pos):
size = len(self.compile())
self.parent.rawDict["Private"] = size, pos
self.pos = pos
def getChildren(self, strings):
children = []
if hasattr(self.dictObj, "Subrs"):
children.append(self.dictObj.Subrs.getCompiler(strings, self))
return children
class BaseDict: class BaseDict:
def __init__(self, strings, file, offset): def __init__(self, strings, file, offset):
self.rawDict = {} self.rawDict = {}
if DEBUG: if DEBUG:
print "loading %s at %s" % (self, offset) print "loading %s at %s" % (self.__class__.__name__, offset)
self.file = file self.file = file
self.offset = offset self.offset = offset
self.strings = strings self.strings = strings
self.skipNames = [] self.skipNames = []
def decompile(self, data): def decompile(self, data):
dec = self.decompiler(self.strings) if DEBUG:
print " length %s is %s" % (self.__class__.__name__, len(data))
dec = self.decompilerClass(self.strings)
dec.decompile(data) dec.decompile(data)
self.rawDict = dec.getDict() self.rawDict = dec.getDict()
self.postDecompile() self.postDecompile()
@ -500,6 +866,9 @@ class BaseDict:
def postDecompile(self): def postDecompile(self):
pass pass
def getCompiler(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:
@ -523,7 +892,7 @@ class BaseDict:
if conv is not None: if conv is not None:
conv.xmlWrite(xmlWriter, name, value) conv.xmlWrite(xmlWriter, name, value)
else: else:
if isinstance(value, types.ListType): if isinstance(value, ListType):
value = " ".join(map(str, value)) value = " ".join(map(str, value))
xmlWriter.simpletag(name, value=value) xmlWriter.simpletag(name, value=value)
xmlWriter.newline() xmlWriter.newline()
@ -534,7 +903,8 @@ class TopDict(BaseDict):
defaults = buildDefaults(topDictOperators) defaults = buildDefaults(topDictOperators)
converters = buildConverters(topDictOperators) converters = buildConverters(topDictOperators)
order = buildOrder(topDictOperators) order = buildOrder(topDictOperators)
decompiler = TopDictDecompiler decompilerClass = TopDictDecompiler
compilerClass = TopDictCompiler
def __init__(self, strings, file, offset, GlobalSubrs): def __init__(self, strings, file, offset, GlobalSubrs):
BaseDict.__init__(self, strings, file, offset) BaseDict.__init__(self, strings, file, offset)
@ -570,7 +940,8 @@ class PrivateDict(BaseDict):
defaults = buildDefaults(privateDictOperators) defaults = buildDefaults(privateDictOperators)
converters = buildConverters(privateDictOperators) converters = buildConverters(privateDictOperators)
order = buildOrder(privateDictOperators) order = buildOrder(privateDictOperators)
decompiler = PrivateDictDecompiler decompilerClass = PrivateDictDecompiler
compilerClass = PrivateDictCompiler
class IndexedStrings: class IndexedStrings:
@ -584,6 +955,12 @@ class IndexedStrings:
strings = list(Index(file, "IndexedStrings")) strings = list(Index(file, "IndexedStrings"))
self.strings = strings self.strings = strings
def getCompiler(self):
return IndexedStringsCompiler(self, None, None)
def __len__(self):
return len(self.strings)
def __getitem__(self, SID): def __getitem__(self, SID):
if SID < cffStandardStringCount: if SID < cffStandardStringCount:
return cffStandardStrings[SID] return cffStandardStrings[SID]
@ -595,7 +972,7 @@ class IndexedStrings:
self.buildStringMapping() self.buildStringMapping()
if cffStandardStringMapping.has_key(s): if cffStandardStringMapping.has_key(s):
SID = cffStandardStringMapping[s] SID = cffStandardStringMapping[s]
if self.stringMapping.has_key(s): elif self.stringMapping.has_key(s):
SID = self.stringMapping[s] SID = self.stringMapping[s]
else: else:
SID = len(self.strings) + cffStandardStringCount SID = len(self.strings) + cffStandardStringCount
@ -687,4 +1064,3 @@ cffStandardStringMapping = {}
for _i in range(cffStandardStringCount): for _i in range(cffStandardStringCount):
cffStandardStringMapping[cffStandardStrings[_i]] = _i cffStandardStringMapping[cffStandardStrings[_i]] = _i

View File

@ -29,9 +29,12 @@ cffDictOperandEncoding[255] = "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 = {}
for _i in range(len(realNibbles)):
realNibblesDict[realNibbles[_i]] = _i
class ByteCodeDecompilerBase: class ByteCodeBase:
def read_byte(self, b0, data, index): def read_byte(self, b0, data, index):
return b0 - 139, index return b0 - 139, index
@ -140,7 +143,64 @@ t2Operators = [
((12, 37), 'flex1'), ((12, 37), 'flex1'),
] ]
class T2CharString(ByteCodeDecompilerBase):
def getIntEncoder(format):
fourByteOp = chr(255)
isT1 = 0
if format == "cff":
fourByteOp = chr(29)
elif format == "t1":
isT1 = 1
else:
assert format == "t2"
def encodeInt(value, fourByteOp=fourByteOp, isT1=isT1,
chr=chr, pack=struct.pack, unpack=struct.unpack):
if -107 <= value <= 107:
code = chr(value + 139)
elif 108 <= value <= 1131:
value = value - 108
code = chr((value >> 8) + 247) + chr(value & 0xFF)
elif -1131 <= value <= -108:
value = -value - 108
code = chr((value >> 8) + 251) + chr(value & 0xFF)
elif not isT1 and -32768 <= value <= 32767:
code = chr(28) + pack(">h", value)
else:
code = fourByteOp + pack(">l", value)
return code
return encodeInt
encodeIntCFF = getIntEncoder("cff")
encodeIntT1 = getIntEncoder("t1")
encodeIntT2 = getIntEncoder("t2")
def encodeFloat(f):
s = str(f).upper()
if s[:2] == "0.":
s = s[1:]
elif s[:3] == "-0.":
s = "-" + s[2:]
nibbles = []
while s:
c = s[0]
s = s[1:]
if c == "E" and s[:1] == "-":
s = s[1:]
c = "E-"
nibbles.append(realNibblesDict[c])
nibbles.append(0xf)
if len(nibbles) % 2:
nibbles.append(0xf)
d = chr(30)
for i in range(0, len(nibbles), 2):
d = d + chr(nibbles[i] << 4 | nibbles[i+1])
return d
class T2CharString(ByteCodeBase):
operandEncoding = t2OperandEncoding operandEncoding = t2OperandEncoding
operators, opcodes = buildOperatorDict(t2Operators) operators, opcodes = buildOperatorDict(t2Operators)
@ -178,41 +238,24 @@ class T2CharString(ByteCodeDecompilerBase):
bytecode.extend(map(chr, opcodes[token])) bytecode.extend(map(chr, opcodes[token]))
else: else:
bytecode.append(token) # hint mask bytecode.append(token) # hint mask
elif tp == types.FloatType:
# only in CFF
raise NotImplementedError
elif tp == types.IntType: elif tp == types.IntType:
# XXX factor out, is largely OK for CFF dicts, too. bytecode.append(encodeIntT2(token))
if -107 <= token <= 107:
code = chr(token + 139)
elif 108 <= token <= 1131:
token = token - 108
code = chr((token >> 8) + 247) + chr(token & 0xFF)
elif -1131 <= token <= -108:
token = -token - 108
code = chr((token >> 8) + 251) + chr(token & 0xFF)
elif -32768 <= token <= 32767:
# XXX T2/CFF-specific: doesn't exist in T1
code = chr(28) + struct.pack(">h", token)
else: else:
# XXX T1/T2-specific: different opcode in CFF assert 0, "unsupported type: %s" % tp
code = chr(255) + struct.pack(">l", token)
bytecode.append(code)
else:
assert 0
bytecode = "".join(bytecode) bytecode = "".join(bytecode)
if DEBUG: self.setBytecode(bytecode)
assert bytecode == self.__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
if DEBUG:
self.__bytecode = self.bytecode
self.bytecode = None self.bytecode = None
def setBytecode(self, bytecode):
self.bytecode = bytecode
self.program = None
def getToken(self, index, def getToken(self, index,
len=len, ord=ord, getattr=getattr, type=type, StringType=types.StringType): len=len, ord=ord, getattr=getattr, type=type, StringType=types.StringType):
if self.bytecode is not None: if self.bytecode is not None:
@ -847,7 +890,7 @@ class T1OutlineExtractor(T2OutlineExtractor):
self.popall() # XXX self.popall() # XXX
class DictDecompiler(ByteCodeDecompilerBase): class DictDecompiler(ByteCodeBase):
operandEncoding = cffDictOperandEncoding operandEncoding = cffDictOperandEncoding