aboutsummaryrefslogtreecommitdiff
path: root/Lib/fontTools/misc/psOperators.py
diff options
context:
space:
mode:
Diffstat (limited to 'Lib/fontTools/misc/psOperators.py')
-rw-r--r--Lib/fontTools/misc/psOperators.py541
1 files changed, 541 insertions, 0 deletions
diff --git a/Lib/fontTools/misc/psOperators.py b/Lib/fontTools/misc/psOperators.py
new file mode 100644
index 00000000..57cfbe83
--- /dev/null
+++ b/Lib/fontTools/misc/psOperators.py
@@ -0,0 +1,541 @@
+from __future__ import print_function, division, absolute_import
+from fontTools.misc.py23 import *
+
+_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 = 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:
+
+ 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()
+ # 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):
+ 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')
+ #pad = file.value.read(1)
+ # for StringIO, this is faster
+ 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))
+