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