1_accessstrings = {0: "", 1: "readonly", 2: "executeonly", 3: "noaccess"}
2
3
4class ps_object(object):
5
6	literal = 1
7	access = 0
8	value = None
9
10	def __init__(self, value):
11		self.value = value
12		self.type = self.__class__.__name__[3:] + "type"
13
14	def __repr__(self):
15		return "<%s %s>" % (self.__class__.__name__[3:], repr(self.value))
16
17
18class ps_operator(ps_object):
19
20	literal = 0
21
22	def __init__(self, name, function):
23		self.name = name
24		self.function = function
25		self.type = self.__class__.__name__[3:] + "type"
26	def __repr__(self):
27		return "<operator %s>" % self.name
28
29class ps_procedure(ps_object):
30	literal = 0
31	def __repr__(self):
32		return "<procedure>"
33	def __str__(self):
34		psstring = '{'
35		for i in range(len(self.value)):
36			if i:
37				psstring = psstring + ' ' + str(self.value[i])
38			else:
39				psstring = psstring + str(self.value[i])
40		return psstring + '}'
41
42class ps_name(ps_object):
43	literal = 0
44	def __str__(self):
45		if self.literal:
46			return '/' + self.value
47		else:
48			return self.value
49
50class ps_literal(ps_object):
51	def __str__(self):
52		return '/' + self.value
53
54class ps_array(ps_object):
55	def __str__(self):
56		psstring = '['
57		for i in range(len(self.value)):
58			item = self.value[i]
59			access = _accessstrings[item.access]
60			if access:
61				access = ' ' + access
62			if i:
63				psstring = psstring + ' ' + str(item) + access
64			else:
65				psstring = psstring + str(item) + access
66		return psstring + ']'
67	def __repr__(self):
68		return "<array>"
69
70_type1_pre_eexec_order = [
71		"FontInfo",
72		"FontName",
73		"Encoding",
74		"PaintType",
75		"FontType",
76		"FontMatrix",
77		"FontBBox",
78		"UniqueID",
79		"Metrics",
80		"StrokeWidth"
81	]
82
83_type1_fontinfo_order = [
84		"version",
85		"Notice",
86		"FullName",
87		"FamilyName",
88		"Weight",
89		"ItalicAngle",
90		"isFixedPitch",
91		"UnderlinePosition",
92		"UnderlineThickness"
93	]
94
95_type1_post_eexec_order = [
96		"Private",
97		"CharStrings",
98		"FID"
99	]
100
101def _type1_item_repr(key, value):
102	psstring = ""
103	access = _accessstrings[value.access]
104	if access:
105		access = access + ' '
106	if key == 'CharStrings':
107		psstring = psstring + "/%s %s def\n" % (key, _type1_CharString_repr(value.value))
108	elif key == 'Encoding':
109		psstring = psstring + _type1_Encoding_repr(value, access)
110	else:
111		psstring = psstring + "/%s %s %sdef\n" % (str(key), str(value), access)
112	return psstring
113
114def _type1_Encoding_repr(encoding, access):
115	encoding = encoding.value
116	psstring = "/Encoding 256 array\n0 1 255 {1 index exch /.notdef put} for\n"
117	for i in range(256):
118		name = encoding[i].value
119		if name != '.notdef':
120			psstring = psstring + "dup %d /%s put\n" % (i, name)
121	return psstring + access + "def\n"
122
123def _type1_CharString_repr(charstrings):
124	items = sorted(charstrings.items())
125	return 'xxx'
126
127class ps_font(ps_object):
128	def __str__(self):
129		psstring = "%d dict dup begin\n" % len(self.value)
130		for key in _type1_pre_eexec_order:
131			try:
132				value = self.value[key]
133			except KeyError:
134				pass
135			else:
136				psstring = psstring + _type1_item_repr(key, value)
137		items = sorted(self.value.items())
138		for key, value in items:
139			if key not in _type1_pre_eexec_order + _type1_post_eexec_order:
140				psstring = psstring + _type1_item_repr(key, value)
141		psstring = psstring + "currentdict end\ncurrentfile eexec\ndup "
142		for key in _type1_post_eexec_order:
143			try:
144				value = self.value[key]
145			except KeyError:
146				pass
147			else:
148				psstring = psstring + _type1_item_repr(key, value)
149		return psstring + 'dup/FontName get exch definefont pop\nmark currentfile closefile\n' + \
150				8 * (64 * '0' + '\n') + 'cleartomark' + '\n'
151	def __repr__(self):
152		return '<font>'
153
154class ps_file(ps_object):
155	pass
156
157class ps_dict(ps_object):
158	def __str__(self):
159		psstring = "%d dict dup begin\n" % len(self.value)
160		items = sorted(self.value.items())
161		for key, value in items:
162			access = _accessstrings[value.access]
163			if access:
164				access = access + ' '
165			psstring = psstring + "/%s %s %sdef\n" % (str(key), str(value), access)
166		return psstring + 'end '
167	def __repr__(self):
168		return "<dict>"
169
170class ps_mark(ps_object):
171	def __init__(self):
172		self.value = 'mark'
173		self.type = self.__class__.__name__[3:] + "type"
174
175class ps_procmark(ps_object):
176	def __init__(self):
177		self.value = 'procmark'
178		self.type = self.__class__.__name__[3:] + "type"
179
180class ps_null(ps_object):
181	def __init__(self):
182		self.type = self.__class__.__name__[3:] + "type"
183
184class ps_boolean(ps_object):
185	def __str__(self):
186		if self.value:
187			return 'true'
188		else:
189			return 'false'
190
191class ps_string(ps_object):
192	def __str__(self):
193		return "(%s)" % repr(self.value)[1:-1]
194
195class ps_integer(ps_object):
196	def __str__(self):
197		return repr(self.value)
198
199class ps_real(ps_object):
200	def __str__(self):
201		return repr(self.value)
202
203
204class PSOperators(object):
205
206	def ps_def(self):
207		obj = self.pop()
208		name = self.pop()
209		self.dictstack[-1][name.value] = obj
210
211	def ps_bind(self):
212		proc = self.pop('proceduretype')
213		self.proc_bind(proc)
214		self.push(proc)
215
216	def proc_bind(self, proc):
217		for i in range(len(proc.value)):
218			item = proc.value[i]
219			if item.type == 'proceduretype':
220				self.proc_bind(item)
221			else:
222				if not item.literal:
223					try:
224						obj = self.resolve_name(item.value)
225					except:
226						pass
227					else:
228						if obj.type == 'operatortype':
229							proc.value[i] = obj
230
231	def ps_exch(self):
232		if len(self.stack) < 2:
233			raise RuntimeError('stack underflow')
234		obj1 = self.pop()
235		obj2 = self.pop()
236		self.push(obj1)
237		self.push(obj2)
238
239	def ps_dup(self):
240		if not self.stack:
241			raise RuntimeError('stack underflow')
242		self.push(self.stack[-1])
243
244	def ps_exec(self):
245		obj = self.pop()
246		if obj.type == 'proceduretype':
247			self.call_procedure(obj)
248		else:
249			self.handle_object(obj)
250
251	def ps_count(self):
252		self.push(ps_integer(len(self.stack)))
253
254	def ps_eq(self):
255		any1 = self.pop()
256		any2 = self.pop()
257		self.push(ps_boolean(any1.value == any2.value))
258
259	def ps_ne(self):
260		any1 = self.pop()
261		any2 = self.pop()
262		self.push(ps_boolean(any1.value != any2.value))
263
264	def ps_cvx(self):
265		obj = self.pop()
266		obj.literal = 0
267		self.push(obj)
268
269	def ps_matrix(self):
270		matrix = [ps_real(1.0), ps_integer(0), ps_integer(0), ps_real(1.0), ps_integer(0), ps_integer(0)]
271		self.push(ps_array(matrix))
272
273	def ps_string(self):
274		num = self.pop('integertype').value
275		self.push(ps_string('\0' * num))
276
277	def ps_type(self):
278		obj = self.pop()
279		self.push(ps_string(obj.type))
280
281	def ps_store(self):
282		value = self.pop()
283		key = self.pop()
284		name = key.value
285		for i in range(len(self.dictstack)-1, -1, -1):
286			if name in self.dictstack[i]:
287				self.dictstack[i][name] = value
288				break
289		self.dictstack[-1][name] = value
290
291	def ps_where(self):
292		name = self.pop()
293		# XXX
294		self.push(ps_boolean(0))
295
296	def ps_systemdict(self):
297		self.push(ps_dict(self.dictstack[0]))
298
299	def ps_userdict(self):
300		self.push(ps_dict(self.dictstack[1]))
301
302	def ps_currentdict(self):
303		self.push(ps_dict(self.dictstack[-1]))
304
305	def ps_currentfile(self):
306		self.push(ps_file(self.tokenizer))
307
308	def ps_eexec(self):
309		f = self.pop('filetype').value
310		f.starteexec()
311
312	def ps_closefile(self):
313		f = self.pop('filetype').value
314		f.skipwhite()
315		f.stopeexec()
316
317	def ps_cleartomark(self):
318		obj = self.pop()
319		while obj != self.mark:
320			obj = self.pop()
321
322	def ps_readstring(self,
323			ps_boolean=ps_boolean,
324			len=len):
325		s = self.pop('stringtype')
326		oldstr = s.value
327		f = self.pop('filetype')
328		#pad = file.value.read(1)
329		# for StringIO, this is faster
330		f.value.pos = f.value.pos + 1
331		newstr = f.value.read(len(oldstr))
332		s.value = newstr
333		self.push(s)
334		self.push(ps_boolean(len(oldstr) == len(newstr)))
335
336	def ps_known(self):
337		key = self.pop()
338		d = self.pop('dicttype', 'fonttype')
339		self.push(ps_boolean(key.value in d.value))
340
341	def ps_if(self):
342		proc = self.pop('proceduretype')
343		if self.pop('booleantype').value:
344			self.call_procedure(proc)
345
346	def ps_ifelse(self):
347		proc2 = self.pop('proceduretype')
348		proc1 = self.pop('proceduretype')
349		if self.pop('booleantype').value:
350			self.call_procedure(proc1)
351		else:
352			self.call_procedure(proc2)
353
354	def ps_readonly(self):
355		obj = self.pop()
356		if obj.access < 1:
357			obj.access = 1
358		self.push(obj)
359
360	def ps_executeonly(self):
361		obj = self.pop()
362		if obj.access < 2:
363			obj.access = 2
364		self.push(obj)
365
366	def ps_noaccess(self):
367		obj = self.pop()
368		if obj.access < 3:
369			obj.access = 3
370		self.push(obj)
371
372	def ps_not(self):
373		obj = self.pop('booleantype', 'integertype')
374		if obj.type == 'booleantype':
375			self.push(ps_boolean(not obj.value))
376		else:
377			self.push(ps_integer(~obj.value))
378
379	def ps_print(self):
380		str = self.pop('stringtype')
381		print('PS output --->', str.value)
382
383	def ps_anchorsearch(self):
384		seek = self.pop('stringtype')
385		s = self.pop('stringtype')
386		seeklen = len(seek.value)
387		if s.value[:seeklen] == seek.value:
388			self.push(ps_string(s.value[seeklen:]))
389			self.push(seek)
390			self.push(ps_boolean(1))
391		else:
392			self.push(s)
393			self.push(ps_boolean(0))
394
395	def ps_array(self):
396		num = self.pop('integertype')
397		array = ps_array([None] * num.value)
398		self.push(array)
399
400	def ps_astore(self):
401		array = self.pop('arraytype')
402		for i in range(len(array.value)-1, -1, -1):
403			array.value[i] = self.pop()
404		self.push(array)
405
406	def ps_load(self):
407		name = self.pop()
408		self.push(self.resolve_name(name.value))
409
410	def ps_put(self):
411		obj1 = self.pop()
412		obj2 = self.pop()
413		obj3 = self.pop('arraytype', 'dicttype', 'stringtype', 'proceduretype')
414		tp = obj3.type
415		if tp == 'arraytype' or tp == 'proceduretype':
416			obj3.value[obj2.value] = obj1
417		elif tp == 'dicttype':
418			obj3.value[obj2.value] = obj1
419		elif tp == 'stringtype':
420			index = obj2.value
421			obj3.value = obj3.value[:index] + chr(obj1.value) + obj3.value[index+1:]
422
423	def ps_get(self):
424		obj1 = self.pop()
425		if obj1.value == "Encoding":
426			pass
427		obj2 = self.pop('arraytype', 'dicttype', 'stringtype', 'proceduretype', 'fonttype')
428		tp = obj2.type
429		if tp in ('arraytype', 'proceduretype'):
430			self.push(obj2.value[obj1.value])
431		elif tp in ('dicttype', 'fonttype'):
432			self.push(obj2.value[obj1.value])
433		elif tp == 'stringtype':
434			self.push(ps_integer(ord(obj2.value[obj1.value])))
435		else:
436			assert False, "shouldn't get here"
437
438	def ps_getinterval(self):
439		obj1 = self.pop('integertype')
440		obj2 = self.pop('integertype')
441		obj3 = self.pop('arraytype', 'stringtype')
442		tp = obj3.type
443		if tp == 'arraytype':
444			self.push(ps_array(obj3.value[obj2.value:obj2.value + obj1.value]))
445		elif tp == 'stringtype':
446			self.push(ps_string(obj3.value[obj2.value:obj2.value + obj1.value]))
447
448	def ps_putinterval(self):
449		obj1 = self.pop('arraytype', 'stringtype')
450		obj2 = self.pop('integertype')
451		obj3 = self.pop('arraytype', 'stringtype')
452		tp = obj3.type
453		if tp == 'arraytype':
454			obj3.value[obj2.value:obj2.value + len(obj1.value)] = obj1.value
455		elif tp == 'stringtype':
456			newstr = obj3.value[:obj2.value]
457			newstr = newstr + obj1.value
458			newstr = newstr + obj3.value[obj2.value + len(obj1.value):]
459			obj3.value = newstr
460
461	def ps_cvn(self):
462		self.push(ps_name(self.pop('stringtype').value))
463
464	def ps_index(self):
465		n = self.pop('integertype').value
466		if n < 0:
467			raise RuntimeError('index may not be negative')
468		self.push(self.stack[-1-n])
469
470	def ps_for(self):
471		proc = self.pop('proceduretype')
472		limit = self.pop('integertype', 'realtype').value
473		increment = self.pop('integertype', 'realtype').value
474		i = self.pop('integertype', 'realtype').value
475		while 1:
476			if increment > 0:
477				if i > limit:
478					break
479			else:
480				if i < limit:
481					break
482			if type(i) == type(0.0):
483				self.push(ps_real(i))
484			else:
485				self.push(ps_integer(i))
486			self.call_procedure(proc)
487			i = i + increment
488
489	def ps_forall(self):
490		proc = self.pop('proceduretype')
491		obj = self.pop('arraytype', 'stringtype', 'dicttype')
492		tp = obj.type
493		if tp == 'arraytype':
494			for item in obj.value:
495				self.push(item)
496				self.call_procedure(proc)
497		elif tp == 'stringtype':
498			for item in obj.value:
499				self.push(ps_integer(ord(item)))
500				self.call_procedure(proc)
501		elif tp == 'dicttype':
502			for key, value in obj.value.items():
503				self.push(ps_name(key))
504				self.push(value)
505				self.call_procedure(proc)
506
507	def ps_definefont(self):
508		font = self.pop('dicttype')
509		name = self.pop()
510		font = ps_font(font.value)
511		self.dictstack[0]['FontDirectory'].value[name.value] = font
512		self.push(font)
513
514	def ps_findfont(self):
515		name = self.pop()
516		font = self.dictstack[0]['FontDirectory'].value[name.value]
517		self.push(font)
518
519	def ps_pop(self):
520		self.pop()
521
522	def ps_dict(self):
523		self.pop('integertype')
524		self.push(ps_dict({}))
525
526	def ps_begin(self):
527		self.dictstack.append(self.pop('dicttype').value)
528
529	def ps_end(self):
530		if len(self.dictstack) > 2:
531			del self.dictstack[-1]
532		else:
533			raise RuntimeError('dictstack underflow')
534
535notdef = '.notdef'
536from fontTools.encodings.StandardEncoding import StandardEncoding
537ps_StandardEncoding = list(map(ps_name, StandardEncoding))
538