_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 "" % self.name class ps_procedure(ps_object): literal = 0 def __repr__(self): return "" 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 "" _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 '' 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 "" 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() # 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))