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:
parent
0011bb6910
commit
f2cf9c5d6d
@ -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
|
||||||
|
|
||||||
|
|
||||||
|
@ -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
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user