|
_accessstrings = {0: "", 1: "readonly", 2: "executeonly", 3: "noaccess"} |
|
|
|
|
|
class ps_object(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 = sorted(charstrings.items()) |
|
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 = sorted(self.value.items()) |
|
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 = sorted(self.value.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)" % repr(self.value)[1:-1] |
|
|
|
|
|
class ps_integer(ps_object): |
|
def __str__(self): |
|
return repr(self.value) |
|
|
|
|
|
class ps_real(ps_object): |
|
def __str__(self): |
|
return repr(self.value) |
|
|
|
|
|
class PSOperators(object): |
|
def ps_def(self): |
|
obj = self.pop() |
|
name = self.pop() |
|
self.dictstack[-1][name.value] = obj |
|
|
|
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: |
|
obj = self.resolve_name(item.value) |
|
except: |
|
pass |
|
else: |
|
if obj.type == "operatortype": |
|
proc.value[i] = obj |
|
|
|
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): |
|
obj = self.pop() |
|
if obj.type == "proceduretype": |
|
self.call_procedure(obj) |
|
else: |
|
self.handle_object(obj) |
|
|
|
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 name in self.dictstack[i]: |
|
self.dictstack[i][name] = value |
|
break |
|
self.dictstack[-1][name] = value |
|
|
|
def ps_where(self): |
|
name = self.pop() |
|
|
|
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): |
|
f = self.pop("filetype").value |
|
f.starteexec() |
|
|
|
def ps_closefile(self): |
|
f = self.pop("filetype").value |
|
f.skipwhite() |
|
f.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): |
|
s = self.pop("stringtype") |
|
oldstr = s.value |
|
f = self.pop("filetype") |
|
|
|
|
|
f.value.pos = f.value.pos + 1 |
|
newstr = f.value.read(len(oldstr)) |
|
s.value = newstr |
|
self.push(s) |
|
self.push(ps_boolean(len(oldstr) == len(newstr))) |
|
|
|
def ps_known(self): |
|
key = self.pop() |
|
d = self.pop("dicttype", "fonttype") |
|
self.push(ps_boolean(key.value in d.value)) |
|
|
|
def ps_if(self): |
|
proc = self.pop("proceduretype") |
|
if self.pop("booleantype").value: |
|
self.call_procedure(proc) |
|
|
|
def ps_ifelse(self): |
|
proc2 = self.pop("proceduretype") |
|
proc1 = self.pop("proceduretype") |
|
if self.pop("booleantype").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") |
|
s = self.pop("stringtype") |
|
seeklen = len(seek.value) |
|
if s.value[:seeklen] == seek.value: |
|
self.push(ps_string(s.value[seeklen:])) |
|
self.push(seek) |
|
self.push(ps_boolean(1)) |
|
else: |
|
self.push(s) |
|
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() |
|
self.push(self.resolve_name(name.value)) |
|
|
|
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 False, "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): |
|
self.push(ps_name(self.pop("stringtype").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): |
|
self.pop("integertype") |
|
self.push(ps_dict({})) |
|
|
|
def ps_begin(self): |
|
self.dictstack.append(self.pop("dicttype").value) |
|
|
|
def ps_end(self): |
|
if len(self.dictstack) > 2: |
|
del self.dictstack[-1] |
|
else: |
|
raise RuntimeError("dictstack underflow") |
|
|
|
|
|
notdef = ".notdef" |
|
from fontTools.encodings.StandardEncoding import StandardEncoding |
|
|
|
ps_StandardEncoding = list(map(ps_name, StandardEncoding)) |
|
|