git-svn-id: svn://svn.code.sf.net/p/fonttools/code/trunk@2 4cde692c-a291-49d1-8350-778aa11640f8
581 lines
15 KiB
Python
581 lines
15 KiB
Python
import string
|
|
|
|
|
|
_accessstrings = {0: "", 1: "readonly", 2: "executeonly", 3: "noaccess"}
|
|
|
|
class ps_object:
|
|
|
|
literal = 1
|
|
access = 0
|
|
value = None
|
|
|
|
def __init__(self, value):
|
|
self.value = value
|
|
self.type = self.__class__.__name__[3:] + "type"
|
|
|
|
def __repr__(self):
|
|
return "<%s %s>" % (self.__class__.__name__[3:], repr(self.value))
|
|
|
|
|
|
class ps_operator(ps_object):
|
|
|
|
literal = 0
|
|
|
|
def __init__(self, name, function):
|
|
self.name = name
|
|
self.function = function
|
|
self.type = self.__class__.__name__[3:] + "type"
|
|
def __repr__(self):
|
|
return "<operator %s>" % self.name
|
|
|
|
class ps_procedure(ps_object):
|
|
literal = 0
|
|
def __repr__(self):
|
|
return "<procedure>"
|
|
def __str__(self):
|
|
psstring = '{'
|
|
for i in range(len(self.value)):
|
|
if i:
|
|
psstring = psstring + ' ' + str(self.value[i])
|
|
else:
|
|
psstring = psstring + str(self.value[i])
|
|
return psstring + '}'
|
|
|
|
class ps_name(ps_object):
|
|
literal = 0
|
|
def __str__(self):
|
|
if self.literal:
|
|
return '/' + self.value
|
|
else:
|
|
return self.value
|
|
|
|
class ps_literal(ps_object):
|
|
def __str__(self):
|
|
return '/' + self.value
|
|
|
|
class ps_array(ps_object):
|
|
def __str__(self):
|
|
psstring = '['
|
|
for i in range(len(self.value)):
|
|
item = self.value[i]
|
|
access = _accessstrings[item.access]
|
|
if access:
|
|
access = ' ' + access
|
|
if i:
|
|
psstring = psstring + ' ' + str(item) + access
|
|
else:
|
|
psstring = psstring + str(item) + access
|
|
return psstring + ']'
|
|
def __repr__(self):
|
|
return "<array>"
|
|
|
|
_type1_pre_eexec_order = [
|
|
"FontInfo",
|
|
"FontName",
|
|
"Encoding",
|
|
"PaintType",
|
|
"FontType",
|
|
"FontMatrix",
|
|
"FontBBox",
|
|
"UniqueID",
|
|
"Metrics",
|
|
"StrokeWidth"
|
|
]
|
|
|
|
_type1_fontinfo_order = [
|
|
"version",
|
|
"Notice",
|
|
"FullName",
|
|
"FamilyName",
|
|
"Weight",
|
|
"ItalicAngle",
|
|
"isFixedPitch",
|
|
"UnderlinePosition",
|
|
"UnderlineThickness"
|
|
]
|
|
|
|
_type1_post_eexec_order = [
|
|
"Private",
|
|
"CharStrings",
|
|
"FID"
|
|
]
|
|
|
|
def _type1_item_repr(key, value):
|
|
psstring = ""
|
|
access = _accessstrings[value.access]
|
|
if access:
|
|
access = access + ' '
|
|
if key == 'CharStrings':
|
|
psstring = psstring + "/%s %s def\n" % (key, _type1_CharString_repr(value.value))
|
|
elif key == 'Encoding':
|
|
psstring = psstring + _type1_Encoding_repr(value, access)
|
|
else:
|
|
psstring = psstring + "/%s %s %sdef\n" % (str(key), str(value), access)
|
|
return psstring
|
|
|
|
def _type1_Encoding_repr(encoding, access):
|
|
encoding = encoding.value
|
|
psstring = "/Encoding 256 array\n0 1 255 {1 index exch /.notdef put} for\n"
|
|
for i in range(256):
|
|
name = encoding[i].value
|
|
if name <> '.notdef':
|
|
psstring = psstring + "dup %d /%s put\n" % (i, name)
|
|
return psstring + access + "def\n"
|
|
|
|
def _type1_CharString_repr(charstrings):
|
|
items = charstrings.items()
|
|
items.sort()
|
|
return 'xxx'
|
|
|
|
class ps_font(ps_object):
|
|
def __str__(self):
|
|
psstring = "%d dict dup begin\n" % len(self.value)
|
|
for key in _type1_pre_eexec_order:
|
|
try:
|
|
value = self.value[key]
|
|
except KeyError:
|
|
pass
|
|
else:
|
|
psstring = psstring + _type1_item_repr(key, value)
|
|
items = self.value.items()
|
|
items.sort()
|
|
for key, value in items:
|
|
if key not in _type1_pre_eexec_order + _type1_post_eexec_order:
|
|
psstring = psstring + _type1_item_repr(key, value)
|
|
psstring = psstring + "currentdict end\ncurrentfile eexec\ndup "
|
|
for key in _type1_post_eexec_order:
|
|
try:
|
|
value = self.value[key]
|
|
except KeyError:
|
|
pass
|
|
else:
|
|
psstring = psstring + _type1_item_repr(key, value)
|
|
return psstring + 'dup/FontName get exch definefont pop\nmark currentfile closefile\n' + \
|
|
8 * (64 * '0' + '\n') + 'cleartomark' + '\n'
|
|
def __repr__(self):
|
|
return '<font>'
|
|
|
|
class ps_file(ps_object):
|
|
pass
|
|
|
|
class ps_dict(ps_object):
|
|
def __str__(self):
|
|
psstring = "%d dict dup begin\n" % len(self.value)
|
|
items = self.value.items()
|
|
items.sort()
|
|
dictrepr = "%d dict dup begin\n" % len(items)
|
|
for key, value in items:
|
|
access = _accessstrings[value.access]
|
|
if access:
|
|
access = access + ' '
|
|
psstring = psstring + "/%s %s %sdef\n" % (str(key), str(value), access)
|
|
return psstring + 'end '
|
|
def __repr__(self):
|
|
return "<dict>"
|
|
|
|
class ps_mark(ps_object):
|
|
def __init__(self):
|
|
self.value = 'mark'
|
|
self.type = self.__class__.__name__[3:] + "type"
|
|
|
|
class ps_procmark(ps_object):
|
|
def __init__(self):
|
|
self.value = 'procmark'
|
|
self.type = self.__class__.__name__[3:] + "type"
|
|
|
|
class ps_null(ps_object):
|
|
def __init__(self):
|
|
self.type = self.__class__.__name__[3:] + "type"
|
|
|
|
class ps_boolean(ps_object):
|
|
def __str__(self):
|
|
if self.value:
|
|
return 'true'
|
|
else:
|
|
return 'false'
|
|
|
|
class ps_string(ps_object):
|
|
def __str__(self):
|
|
return "(%s)" % `self.value`[1:-1]
|
|
|
|
class ps_integer(ps_object):
|
|
def __str__(self):
|
|
return `self.value`
|
|
|
|
class ps_real(ps_object):
|
|
def __str__(self):
|
|
return `self.value`
|
|
|
|
|
|
class PSOperators:
|
|
|
|
def ps_def(self):
|
|
object = self.pop()
|
|
name = self.pop()
|
|
self.dictstack[-1][name.value] = object
|
|
|
|
def ps_bind(self):
|
|
proc = self.pop('proceduretype')
|
|
self.proc_bind(proc)
|
|
self.push(proc)
|
|
|
|
def proc_bind(self, proc):
|
|
for i in range(len(proc.value)):
|
|
item = proc.value[i]
|
|
if item.type == 'proceduretype':
|
|
self.proc_bind(item)
|
|
else:
|
|
if not item.literal:
|
|
try:
|
|
object = self.resolve_name(item.value)
|
|
except:
|
|
pass
|
|
else:
|
|
if object.type == 'operatortype':
|
|
proc.value[i] = object
|
|
|
|
def ps_exch(self):
|
|
if len(self.stack) < 2:
|
|
raise RuntimeError, 'stack underflow'
|
|
obj1 = self.pop()
|
|
obj2 = self.pop()
|
|
self.push(obj1)
|
|
self.push(obj2)
|
|
|
|
def ps_dup(self):
|
|
if not self.stack:
|
|
raise RuntimeError, 'stack underflow'
|
|
self.push(self.stack[-1])
|
|
|
|
def ps_exec(self):
|
|
object = self.pop()
|
|
if object.type == 'proceduretype':
|
|
self.call_procedure(object)
|
|
else:
|
|
self.handle_object(object)
|
|
|
|
def ps_count(self):
|
|
self.push(ps_integer(len(self.stack)))
|
|
|
|
def ps_eq(self):
|
|
any1 = self.pop()
|
|
any2 = self.pop()
|
|
self.push(ps_boolean(any1.value == any2.value))
|
|
|
|
def ps_ne(self):
|
|
any1 = self.pop()
|
|
any2 = self.pop()
|
|
self.push(ps_boolean(any1.value <> any2.value))
|
|
|
|
def ps_cvx(self):
|
|
obj = self.pop()
|
|
obj.literal = 0
|
|
self.push(obj)
|
|
|
|
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)]
|
|
self.push(ps_array(matrix))
|
|
|
|
def ps_string(self):
|
|
num = self.pop('integertype').value
|
|
self.push(ps_string('\0' * num))
|
|
|
|
def ps_type(self):
|
|
obj = self.pop()
|
|
self.push(ps_string(obj.type))
|
|
|
|
def ps_store(self):
|
|
value = self.pop()
|
|
key = self.pop()
|
|
name = key.value
|
|
for i in range(len(self.dictstack)-1, -1, -1):
|
|
if self.dictstack[i].has_key(name):
|
|
self.dictstack[i][name] = value
|
|
break
|
|
self.dictstack[-1][name] = value
|
|
|
|
def ps_where(self):
|
|
name = self.pop()
|
|
# XXX
|
|
self.push(ps_boolean(0))
|
|
|
|
def ps_systemdict(self):
|
|
self.push(ps_dict(self.dictstack[0]))
|
|
|
|
def ps_userdict(self):
|
|
self.push(ps_dict(self.dictstack[1]))
|
|
|
|
def ps_currentdict(self):
|
|
self.push(ps_dict(self.dictstack[-1]))
|
|
|
|
def ps_currentfile(self):
|
|
self.push(ps_file(self.tokenizer))
|
|
|
|
def ps_eexec(self):
|
|
file = self.pop('filetype').value
|
|
file.starteexec()
|
|
|
|
def ps_closefile(self):
|
|
file = self.pop('filetype').value
|
|
file.skipwhite()
|
|
file.stopeexec()
|
|
|
|
def ps_cleartomark(self):
|
|
obj = self.pop()
|
|
while obj <> self.mark:
|
|
obj = self.pop()
|
|
|
|
def ps_readstring(self,
|
|
ps_boolean = ps_boolean,
|
|
len = len):
|
|
string = self.pop('stringtype')
|
|
oldstr = string.value
|
|
file = self.pop('filetype')
|
|
#pad = file.value.read(1)
|
|
# for StringIO, this is faster
|
|
file.value.pos = file.value.pos + 1
|
|
newstr = file.value.read(len(oldstr))
|
|
string.value = newstr
|
|
self.push(string)
|
|
self.push(ps_boolean(len(oldstr) == len(newstr)))
|
|
|
|
def ps_known(self):
|
|
key = self.pop()
|
|
dict = self.pop('dicttype', 'fonttype')
|
|
self.push(ps_boolean(dict.value.has_key(key.value)))
|
|
|
|
def ps_if(self):
|
|
proc = self.pop('proceduretype')
|
|
bool = self.pop('booleantype')
|
|
if bool.value:
|
|
self.call_procedure(proc)
|
|
|
|
def ps_ifelse(self):
|
|
proc2 = self.pop('proceduretype')
|
|
proc1 = self.pop('proceduretype')
|
|
bool = self.pop('booleantype')
|
|
if bool.value:
|
|
self.call_procedure(proc1)
|
|
else:
|
|
self.call_procedure(proc2)
|
|
|
|
def ps_readonly(self):
|
|
obj = self.pop()
|
|
if obj.access < 1:
|
|
obj.access = 1
|
|
self.push(obj)
|
|
|
|
def ps_executeonly(self):
|
|
obj = self.pop()
|
|
if obj.access < 2:
|
|
obj.access = 2
|
|
self.push(obj)
|
|
|
|
def ps_noaccess(self):
|
|
obj = self.pop()
|
|
if obj.access < 3:
|
|
obj.access = 3
|
|
self.push(obj)
|
|
|
|
def ps_not(self):
|
|
obj = self.pop('booleantype', 'integertype')
|
|
if obj.type == 'booleantype':
|
|
self.push(ps_boolean(not obj.value))
|
|
else:
|
|
self.push(ps_integer(~obj.value))
|
|
|
|
def ps_print(self):
|
|
str = self.pop('stringtype')
|
|
print 'PS output --->', str.value
|
|
|
|
def ps_anchorsearch(self):
|
|
seek = self.pop('stringtype')
|
|
string = self.pop('stringtype')
|
|
seeklen = len(seek.value)
|
|
if string.value[:seeklen] == seek.value:
|
|
self.push(ps_string(string.value[seeklen:]))
|
|
self.push(seek)
|
|
self.push(ps_boolean(1))
|
|
else:
|
|
self.push(string)
|
|
self.push(ps_boolean(0))
|
|
|
|
def ps_array(self):
|
|
num = self.pop('integertype')
|
|
array = ps_array([None] * num.value)
|
|
self.push(array)
|
|
|
|
def ps_astore(self):
|
|
array = self.pop('arraytype')
|
|
for i in range(len(array.value)-1, -1, -1):
|
|
array.value[i] = self.pop()
|
|
self.push(array)
|
|
|
|
def ps_load(self):
|
|
name = self.pop()
|
|
object = self.resolve_name(name.value)
|
|
self.push(object)
|
|
|
|
def ps_put(self):
|
|
obj1 = self.pop()
|
|
obj2 = self.pop()
|
|
obj3 = self.pop('arraytype', 'dicttype', 'stringtype', 'proceduretype')
|
|
tp = obj3.type
|
|
if tp == 'arraytype' or tp == 'proceduretype':
|
|
obj3.value[obj2.value] = obj1
|
|
elif tp == 'dicttype':
|
|
obj3.value[obj2.value] = obj1
|
|
elif tp == 'stringtype':
|
|
index = obj2.value
|
|
obj3.value = obj3.value[:index] + chr(obj1.value) + obj3.value[index+1:]
|
|
|
|
def ps_get(self):
|
|
obj1 = self.pop()
|
|
if obj1.value == "Encoding":
|
|
pass
|
|
obj2 = self.pop('arraytype', 'dicttype', 'stringtype', 'proceduretype', 'fonttype')
|
|
tp = obj2.type
|
|
if tp in ('arraytype', 'proceduretype'):
|
|
self.push(obj2.value[obj1.value])
|
|
elif tp in ('dicttype', 'fonttype'):
|
|
self.push(obj2.value[obj1.value])
|
|
elif tp == 'stringtype':
|
|
self.push(ps_integer(ord(obj2.value[obj1.value])))
|
|
else:
|
|
assert 0, "shouldn't get here"
|
|
|
|
def ps_getinterval(self):
|
|
obj1 = self.pop('integertype')
|
|
obj2 = self.pop('integertype')
|
|
obj3 = self.pop('arraytype', 'stringtype')
|
|
tp = obj3.type
|
|
if tp == 'arraytype':
|
|
self.push(ps_array(obj3.value[obj2.value:obj2.value + obj1.value]))
|
|
elif tp == 'stringtype':
|
|
self.push(ps_string(obj3.value[obj2.value:obj2.value + obj1.value]))
|
|
|
|
def ps_putinterval(self):
|
|
obj1 = self.pop('arraytype', 'stringtype')
|
|
obj2 = self.pop('integertype')
|
|
obj3 = self.pop('arraytype', 'stringtype')
|
|
tp = obj3.type
|
|
if tp == 'arraytype':
|
|
obj3.value[obj2.value:obj2.value + len(obj1.value)] = obj1.value
|
|
elif tp == 'stringtype':
|
|
newstr = obj3.value[:obj2.value]
|
|
newstr = newstr + obj1.value
|
|
newstr = newstr + obj3.value[obj2.value + len(obj1.value):]
|
|
obj3.value = newstr
|
|
|
|
def ps_cvn(self):
|
|
str = self.pop('stringtype')
|
|
self.push(ps_name(str.value))
|
|
|
|
def ps_index(self):
|
|
n = self.pop('integertype').value
|
|
if n < 0:
|
|
raise RuntimeError, 'index may not be negative'
|
|
self.push(self.stack[-1-n])
|
|
|
|
def ps_for(self):
|
|
proc = self.pop('proceduretype')
|
|
limit = self.pop('integertype', 'realtype').value
|
|
increment = self.pop('integertype', 'realtype').value
|
|
i = self.pop('integertype', 'realtype').value
|
|
while 1:
|
|
if increment > 0:
|
|
if i > limit:
|
|
break
|
|
else:
|
|
if i < limit:
|
|
break
|
|
if type(i) == type(0.0):
|
|
self.push(ps_real(i))
|
|
else:
|
|
self.push(ps_integer(i))
|
|
self.call_procedure(proc)
|
|
i = i + increment
|
|
|
|
def ps_forall(self):
|
|
proc = self.pop('proceduretype')
|
|
obj = self.pop('arraytype', 'stringtype', 'dicttype')
|
|
tp = obj.type
|
|
if tp == 'arraytype':
|
|
for item in obj.value:
|
|
self.push(item)
|
|
self.call_procedure(proc)
|
|
elif tp == 'stringtype':
|
|
for item in obj.value:
|
|
self.push(ps_integer(ord(item)))
|
|
self.call_procedure(proc)
|
|
elif tp == 'dicttype':
|
|
for key, value in obj.value.items():
|
|
self.push(ps_name(key))
|
|
self.push(value)
|
|
self.call_procedure(proc)
|
|
|
|
def ps_definefont(self):
|
|
font = self.pop('dicttype')
|
|
name = self.pop()
|
|
font = ps_font(font.value)
|
|
self.dictstack[0]['FontDirectory'].value[name.value] = font
|
|
self.push(font)
|
|
|
|
def ps_findfont(self):
|
|
name = self.pop()
|
|
font = self.dictstack[0]['FontDirectory'].value[name.value]
|
|
self.push(font)
|
|
|
|
def ps_pop(self):
|
|
self.pop()
|
|
|
|
def ps_dict(self):
|
|
num = self.pop('integertype')
|
|
dict = ps_dict({})
|
|
self.push(dict)
|
|
|
|
def ps_begin(self):
|
|
dict = self.pop('dicttype')
|
|
self.dictstack.append(dict.value)
|
|
|
|
def ps_end(self):
|
|
if len(self.dictstack) > 2:
|
|
del self.dictstack[-1]
|
|
else:
|
|
raise RuntimeError, 'dictstack underflow'
|
|
|
|
notdef = '.notdef'
|
|
StandardEncoding = [ notdef, notdef, notdef, notdef, notdef, notdef,
|
|
notdef, notdef, notdef, notdef, notdef, notdef, notdef, notdef,
|
|
notdef, notdef, notdef, notdef, notdef, notdef, notdef, notdef,
|
|
notdef, notdef, notdef, notdef, notdef, notdef, notdef, notdef,
|
|
notdef, notdef, 'space', 'exclam', 'quotedbl', 'numbersign', 'dollar', 'percent',
|
|
'ampersand', 'quoteright', 'parenleft', 'parenright', 'asterisk', 'plus', 'comma',
|
|
'hyphen', 'period', 'slash', 'zero', 'one', 'two', 'three', 'four', 'five', 'six',
|
|
'seven', 'eight', 'nine', 'colon', 'semicolon', 'less', 'equal', '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', 'bracketleft', 'backslash', 'bracketright',
|
|
'asciicircum', 'underscore', 'quoteleft', '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', 'braceright', 'asciitilde', notdef, notdef, notdef,
|
|
notdef, notdef, notdef, notdef, notdef, notdef, notdef, notdef,
|
|
notdef, notdef, notdef, notdef, notdef, notdef, notdef, notdef,
|
|
notdef, notdef, notdef, notdef, notdef, notdef, notdef, notdef,
|
|
notdef, notdef, notdef, notdef, notdef, notdef, notdef, 'exclamdown',
|
|
'cent', 'sterling', 'fraction', 'yen', 'florin', 'section', 'currency', 'quotesingle',
|
|
'quotedblleft', 'guillemotleft', 'guilsinglleft', 'guilsinglright', 'fi', 'fl', notdef,
|
|
'endash', 'dagger', 'daggerdbl', 'periodcentered', notdef, 'paragraph', 'bullet',
|
|
'quotesinglbase', 'quotedblbase', 'quotedblright', 'guillemotright', 'ellipsis',
|
|
'perthousand', notdef, 'questiondown', notdef, 'grave', 'acute', 'circumflex',
|
|
'tilde', 'macron', 'breve', 'dotaccent', 'dieresis', notdef, 'ring', 'cedilla',
|
|
notdef, 'hungarumlaut', 'ogonek', 'caron', 'emdash', notdef, notdef, notdef,
|
|
notdef, notdef, notdef, notdef, notdef, notdef, notdef, notdef,
|
|
notdef, notdef, notdef, notdef, notdef, 'AE', notdef, 'ordfeminine',
|
|
notdef, notdef, notdef, notdef, 'Lslash', 'Oslash', 'OE', 'ordmasculine',
|
|
notdef, notdef, notdef, notdef, notdef, 'ae', notdef, notdef,
|
|
notdef, 'dotlessi', notdef, notdef, 'lslash', 'oslash', 'oe', 'germandbls',
|
|
notdef, notdef, notdef, notdef ]
|
|
|
|
ps_StandardEncoding = map(ps_name, StandardEncoding)
|
|
|