diff options
Diffstat (limited to 'Lib/fontTools/misc/psOperators.py')
-rw-r--r-- | Lib/fontTools/misc/psOperators.py | 541 |
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)) + |