1"""psCharStrings.py -- module implementing various kinds of CharStrings:
2CFF dictionary data and Type1/Type2 CharStrings.
3"""
4
5from __future__ import print_function, division, absolute_import
6from fontTools.misc.py23 import *
7import struct
8
9
10DEBUG = 0
11
12
13t1OperandEncoding = [None] * 256
14t1OperandEncoding[0:32] = (32) * ["do_operator"]
15t1OperandEncoding[32:247] = (247 - 32) * ["read_byte"]
16t1OperandEncoding[247:251] = (251 - 247) * ["read_smallInt1"]
17t1OperandEncoding[251:255] = (255 - 251) * ["read_smallInt2"]
18t1OperandEncoding[255] = "read_longInt"
19assert len(t1OperandEncoding) == 256
20
21t2OperandEncoding = t1OperandEncoding[:]
22t2OperandEncoding[28] = "read_shortInt"
23t2OperandEncoding[255] = "read_fixed1616"
24
25cffDictOperandEncoding = t2OperandEncoding[:]
26cffDictOperandEncoding[29] = "read_longInt"
27cffDictOperandEncoding[30] = "read_realNumber"
28cffDictOperandEncoding[255] = "reserved"
29
30
31realNibbles = ['0', '1', '2', '3', '4', '5', '6', '7', '8', '9',
32		'.', 'E', 'E-', None, '-']
33realNibblesDict = {}
34for _i in range(len(realNibbles)):
35	realNibblesDict[realNibbles[_i]] = _i
36
37
38class ByteCodeBase(object):
39
40	def read_byte(self, b0, data, index):
41		return b0 - 139, index
42
43	def read_smallInt1(self, b0, data, index):
44		b1 = byteord(data[index])
45		return (b0-247)*256 + b1 + 108, index+1
46
47	def read_smallInt2(self, b0, data, index):
48		b1 = byteord(data[index])
49		return -(b0-251)*256 - b1 - 108, index+1
50
51	def read_shortInt(self, b0, data, index):
52		value, = struct.unpack(">h", data[index:index+2])
53		return value, index+2
54
55	def read_longInt(self, b0, data, index):
56		value, = struct.unpack(">l", data[index:index+4])
57		return value, index+4
58
59	def read_fixed1616(self, b0, data, index):
60		value, = struct.unpack(">l", data[index:index+4])
61		return value / 65536, index+4
62
63	def read_realNumber(self, b0, data, index):
64		number = ''
65		while True:
66			b = byteord(data[index])
67			index = index + 1
68			nibble0 = (b & 0xf0) >> 4
69			nibble1 = b & 0x0f
70			if nibble0 == 0xf:
71				break
72			number = number + realNibbles[nibble0]
73			if nibble1 == 0xf:
74				break
75			number = number + realNibbles[nibble1]
76		return float(number), index
77
78
79def buildOperatorDict(operatorList):
80	oper = {}
81	opc = {}
82	for item in operatorList:
83		if len(item) == 2:
84			oper[item[0]] = item[1]
85		else:
86			oper[item[0]] = item[1:]
87		if isinstance(item[0], tuple):
88			opc[item[1]] = item[0]
89		else:
90			opc[item[1]] = (item[0],)
91	return oper, opc
92
93
94t2Operators = [
95#	opcode     name
96	(1,        'hstem'),
97	(3,        'vstem'),
98	(4,        'vmoveto'),
99	(5,        'rlineto'),
100	(6,        'hlineto'),
101	(7,        'vlineto'),
102	(8,        'rrcurveto'),
103	(10,       'callsubr'),
104	(11,       'return'),
105	(14,       'endchar'),
106	(16,       'blend'),
107	(18,       'hstemhm'),
108	(19,       'hintmask'),
109	(20,       'cntrmask'),
110	(21,       'rmoveto'),
111	(22,       'hmoveto'),
112	(23,       'vstemhm'),
113	(24,       'rcurveline'),
114	(25,       'rlinecurve'),
115	(26,       'vvcurveto'),
116	(27,       'hhcurveto'),
117#	(28,       'shortint'),  # not really an operator
118	(29,       'callgsubr'),
119	(30,       'vhcurveto'),
120	(31,       'hvcurveto'),
121	((12, 0),  'ignore'),  # dotsection. Yes, there a few very early OTF/CFF
122	                   # fonts with this deprecated operator. Just ignore it.
123	((12, 3),  'and'),
124	((12, 4),  'or'),
125	((12, 5),  'not'),
126	((12, 8),  'store'),
127	((12, 9),  'abs'),
128	((12, 10), 'add'),
129	((12, 11), 'sub'),
130	((12, 12), 'div'),
131	((12, 13), 'load'),
132	((12, 14), 'neg'),
133	((12, 15), 'eq'),
134	((12, 18), 'drop'),
135	((12, 20), 'put'),
136	((12, 21), 'get'),
137	((12, 22), 'ifelse'),
138	((12, 23), 'random'),
139	((12, 24), 'mul'),
140	((12, 26), 'sqrt'),
141	((12, 27), 'dup'),
142	((12, 28), 'exch'),
143	((12, 29), 'index'),
144	((12, 30), 'roll'),
145	((12, 34), 'hflex'),
146	((12, 35), 'flex'),
147	((12, 36), 'hflex1'),
148	((12, 37), 'flex1'),
149]
150
151
152def getIntEncoder(format):
153	if format == "cff":
154		fourByteOp = bytechr(29)
155	elif format == "t1":
156		fourByteOp = bytechr(255)
157	else:
158		assert format == "t2"
159		fourByteOp = None
160
161	def encodeInt(value, fourByteOp=fourByteOp, bytechr=bytechr,
162			pack=struct.pack, unpack=struct.unpack):
163		if -107 <= value <= 107:
164			code = bytechr(value + 139)
165		elif 108 <= value <= 1131:
166			value = value - 108
167			code = bytechr((value >> 8) + 247) + bytechr(value & 0xFF)
168		elif -1131 <= value <= -108:
169			value = -value - 108
170			code = bytechr((value >> 8) + 251) + bytechr(value & 0xFF)
171		elif fourByteOp is None:
172			# T2 only supports 2 byte ints
173			if -32768 <= value <= 32767:
174				code = bytechr(28) + pack(">h", value)
175			else:
176				# Backwards compatible hack: due to a previous bug in FontTools,
177				# 16.16 fixed numbers were written out as 4-byte ints. When
178				# these numbers were small, they were wrongly written back as
179				# small ints instead of 4-byte ints, breaking round-tripping.
180				# This here workaround doesn't do it any better, since we can't
181				# distinguish anymore between small ints that were supposed to
182				# be small fixed numbers and small ints that were just small
183				# ints. Hence the warning.
184				import sys
185				sys.stderr.write("Warning: 4-byte T2 number got passed to the "
186					"IntType handler. This should happen only when reading in "
187					"old XML files.\n")
188				code = bytechr(255) + pack(">l", value)
189		else:
190			code = fourByteOp + pack(">l", value)
191		return code
192
193	return encodeInt
194
195
196encodeIntCFF = getIntEncoder("cff")
197encodeIntT1 = getIntEncoder("t1")
198encodeIntT2 = getIntEncoder("t2")
199
200def encodeFixed(f, pack=struct.pack):
201	# For T2 only
202	return b"\xff" + pack(">l", int(round(f * 65536)))
203
204def encodeFloat(f):
205	# For CFF only, used in cffLib
206	s = str(f).upper()
207	if s[:2] == "0.":
208		s = s[1:]
209	elif s[:3] == "-0.":
210		s = "-" + s[2:]
211	nibbles = []
212	while s:
213		c = s[0]
214		s = s[1:]
215		if c == "E" and s[:1] == "-":
216			s = s[1:]
217			c = "E-"
218		nibbles.append(realNibblesDict[c])
219	nibbles.append(0xf)
220	if len(nibbles) % 2:
221		nibbles.append(0xf)
222	d = bytechr(30)
223	for i in range(0, len(nibbles), 2):
224		d = d + bytechr(nibbles[i] << 4 | nibbles[i+1])
225	return d
226
227
228class CharStringCompileError(Exception): pass
229
230
231class T2CharString(ByteCodeBase):
232
233	operandEncoding = t2OperandEncoding
234	operators, opcodes = buildOperatorDict(t2Operators)
235
236	def __init__(self, bytecode=None, program=None, private=None, globalSubrs=None):
237		if program is None:
238			program = []
239		self.bytecode = bytecode
240		self.program = program
241		self.private = private
242		self.globalSubrs = globalSubrs if globalSubrs is not None else []
243
244	def __repr__(self):
245		if self.bytecode is None:
246			return "<%s (source) at %x>" % (self.__class__.__name__, id(self))
247		else:
248			return "<%s (bytecode) at %x>" % (self.__class__.__name__, id(self))
249
250	def getIntEncoder(self):
251		return encodeIntT2
252
253	def getFixedEncoder(self):
254		return encodeFixed
255
256	def decompile(self):
257		if not self.needsDecompilation():
258			return
259		subrs = getattr(self.private, "Subrs", [])
260		decompiler = SimpleT2Decompiler(subrs, self.globalSubrs)
261		decompiler.execute(self)
262
263	def draw(self, pen):
264		subrs = getattr(self.private, "Subrs", [])
265		extractor = T2OutlineExtractor(pen, subrs, self.globalSubrs,
266				self.private.nominalWidthX, self.private.defaultWidthX)
267		extractor.execute(self)
268		self.width = extractor.width
269
270	def compile(self):
271		if self.bytecode is not None:
272			return
273		assert self.program, "illegal CharString: decompiled to empty program"
274		assert self.program[-1] in ("endchar", "return", "callsubr", "callgsubr",
275				"seac"), "illegal CharString"
276		bytecode = []
277		opcodes = self.opcodes
278		program = self.program
279		encodeInt = self.getIntEncoder()
280		encodeFixed = self.getFixedEncoder()
281		i = 0
282		end = len(program)
283		while i < end:
284			token = program[i]
285			i = i + 1
286			tp = type(token)
287			if issubclass(tp, basestring):
288				try:
289					bytecode.extend(bytechr(b) for b in opcodes[token])
290				except KeyError:
291					raise CharStringCompileError("illegal operator: %s" % token)
292				if token in ('hintmask', 'cntrmask'):
293					bytecode.append(program[i])  # hint mask
294					i = i + 1
295			elif tp == int:
296				bytecode.append(encodeInt(token))
297			elif tp == float:
298				bytecode.append(encodeFixed(token))
299			else:
300				assert 0, "unsupported type: %s" % tp
301		try:
302			bytecode = bytesjoin(bytecode)
303		except TypeError:
304			print(bytecode)
305			raise
306		self.setBytecode(bytecode)
307
308	def needsDecompilation(self):
309		return self.bytecode is not None
310
311	def setProgram(self, program):
312		self.program = program
313		self.bytecode = None
314
315	def setBytecode(self, bytecode):
316		self.bytecode = bytecode
317		self.program = None
318
319	def getToken(self, index,
320			len=len, byteord=byteord, getattr=getattr, type=type, StringType=str):
321		if self.bytecode is not None:
322			if index >= len(self.bytecode):
323				return None, 0, 0
324			b0 = byteord(self.bytecode[index])
325			index = index + 1
326			code = self.operandEncoding[b0]
327			handler = getattr(self, code)
328			token, index = handler(b0, self.bytecode, index)
329		else:
330			if index >= len(self.program):
331				return None, 0, 0
332			token = self.program[index]
333			index = index + 1
334		isOperator = isinstance(token, StringType)
335		return token, isOperator, index
336
337	def getBytes(self, index, nBytes):
338		if self.bytecode is not None:
339			newIndex = index + nBytes
340			bytes = self.bytecode[index:newIndex]
341			index = newIndex
342		else:
343			bytes = self.program[index]
344			index = index + 1
345		assert len(bytes) == nBytes
346		return bytes, index
347
348	def do_operator(self, b0, data, index):
349		if b0 == 12:
350			op = (b0, byteord(data[index]))
351			index = index+1
352		else:
353			op = b0
354		operator = self.operators[op]
355		return operator, index
356
357	def toXML(self, xmlWriter):
358		from fontTools.misc.textTools import num2binary
359		if self.bytecode is not None:
360			xmlWriter.dumphex(self.bytecode)
361		else:
362			index = 0
363			args = []
364			while True:
365				token, isOperator, index = self.getToken(index)
366				if token is None:
367					break
368				if isOperator:
369					args = [str(arg) for arg in args]
370					if token in ('hintmask', 'cntrmask'):
371						hintMask, isOperator, index = self.getToken(index)
372						bits = []
373						for byte in hintMask:
374							bits.append(num2binary(byteord(byte), 8))
375						hintMask = strjoin(bits)
376						line = ' '.join(args + [token, hintMask])
377					else:
378						line = ' '.join(args + [token])
379					xmlWriter.write(line)
380					xmlWriter.newline()
381					args = []
382				else:
383					args.append(token)
384
385	def fromXML(self, name, attrs, content):
386		from fontTools.misc.textTools import binary2num, readHex
387		if attrs.get("raw"):
388			self.setBytecode(readHex(content))
389			return
390		content = strjoin(content)
391		content = content.split()
392		program = []
393		end = len(content)
394		i = 0
395		while i < end:
396			token = content[i]
397			i = i + 1
398			try:
399				token = int(token)
400			except ValueError:
401				try:
402					token = float(token)
403				except ValueError:
404					program.append(token)
405					if token in ('hintmask', 'cntrmask'):
406						mask = content[i]
407						maskBytes = b""
408						for j in range(0, len(mask), 8):
409							maskBytes = maskBytes + bytechr(binary2num(mask[j:j+8]))
410						program.append(maskBytes)
411						i = i + 1
412				else:
413					program.append(token)
414			else:
415				program.append(token)
416		self.setProgram(program)
417
418
419t1Operators = [
420#	opcode     name
421	(1,        'hstem'),
422	(3,        'vstem'),
423	(4,        'vmoveto'),
424	(5,        'rlineto'),
425	(6,        'hlineto'),
426	(7,        'vlineto'),
427	(8,        'rrcurveto'),
428	(9,        'closepath'),
429	(10,       'callsubr'),
430	(11,       'return'),
431	(13,       'hsbw'),
432	(14,       'endchar'),
433	(21,       'rmoveto'),
434	(22,       'hmoveto'),
435	(30,       'vhcurveto'),
436	(31,       'hvcurveto'),
437	((12, 0),  'dotsection'),
438	((12, 1),  'vstem3'),
439	((12, 2),  'hstem3'),
440	((12, 6),  'seac'),
441	((12, 7),  'sbw'),
442	((12, 12), 'div'),
443	((12, 16), 'callothersubr'),
444	((12, 17), 'pop'),
445	((12, 33), 'setcurrentpoint'),
446]
447
448class T1CharString(T2CharString):
449
450	operandEncoding = t1OperandEncoding
451	operators, opcodes = buildOperatorDict(t1Operators)
452
453	def __init__(self, bytecode=None, program=None, subrs=None):
454		if program is None:
455			program = []
456		self.bytecode = bytecode
457		self.program = program
458		self.subrs = subrs
459
460	def getIntEncoder(self):
461		return encodeIntT1
462
463	def getFixedEncoder(self):
464		def encodeFixed(value):
465			raise TypeError("Type 1 charstrings don't support floating point operands")
466
467	def decompile(self):
468		if self.bytecode is None:
469			return
470		program = []
471		index = 0
472		while True:
473			token, isOperator, index = self.getToken(index)
474			if token is None:
475				break
476			program.append(token)
477		self.setProgram(program)
478
479	def draw(self, pen):
480		extractor = T1OutlineExtractor(pen, self.subrs)
481		extractor.execute(self)
482		self.width = extractor.width
483
484
485class SimpleT2Decompiler(object):
486
487	def __init__(self, localSubrs, globalSubrs):
488		self.localSubrs = localSubrs
489		self.localBias = calcSubrBias(localSubrs)
490		self.globalSubrs = globalSubrs
491		self.globalBias = calcSubrBias(globalSubrs)
492		self.reset()
493
494	def reset(self):
495		self.callingStack = []
496		self.operandStack = []
497		self.hintCount = 0
498		self.hintMaskBytes = 0
499
500	def execute(self, charString):
501		self.callingStack.append(charString)
502		needsDecompilation = charString.needsDecompilation()
503		if needsDecompilation:
504			program = []
505			pushToProgram = program.append
506		else:
507			pushToProgram = lambda x: None
508		pushToStack = self.operandStack.append
509		index = 0
510		while True:
511			token, isOperator, index = charString.getToken(index)
512			if token is None:
513				break  # we're done!
514			pushToProgram(token)
515			if isOperator:
516				handlerName = "op_" + token
517				if hasattr(self, handlerName):
518					handler = getattr(self, handlerName)
519					rv = handler(index)
520					if rv:
521						hintMaskBytes, index = rv
522						pushToProgram(hintMaskBytes)
523				else:
524					self.popall()
525			else:
526				pushToStack(token)
527		if needsDecompilation:
528			assert program, "illegal CharString: decompiled to empty program"
529			assert program[-1] in ("endchar", "return", "callsubr", "callgsubr",
530					"seac"), "illegal CharString"
531			charString.setProgram(program)
532		del self.callingStack[-1]
533
534	def pop(self):
535		value = self.operandStack[-1]
536		del self.operandStack[-1]
537		return value
538
539	def popall(self):
540		stack = self.operandStack[:]
541		self.operandStack[:] = []
542		return stack
543
544	def push(self, value):
545		self.operandStack.append(value)
546
547	def op_return(self, index):
548		if self.operandStack:
549			pass
550
551	def op_endchar(self, index):
552		pass
553
554	def op_ignore(self, index):
555		pass
556
557	def op_callsubr(self, index):
558		subrIndex = self.pop()
559		subr = self.localSubrs[subrIndex+self.localBias]
560		self.execute(subr)
561
562	def op_callgsubr(self, index):
563		subrIndex = self.pop()
564		subr = self.globalSubrs[subrIndex+self.globalBias]
565		self.execute(subr)
566
567	def op_hstem(self, index):
568		self.countHints()
569	def op_vstem(self, index):
570		self.countHints()
571	def op_hstemhm(self, index):
572		self.countHints()
573	def op_vstemhm(self, index):
574		self.countHints()
575
576	def op_hintmask(self, index):
577		if not self.hintMaskBytes:
578			self.countHints()
579			self.hintMaskBytes = (self.hintCount + 7) // 8
580		hintMaskBytes, index = self.callingStack[-1].getBytes(index, self.hintMaskBytes)
581		return hintMaskBytes, index
582
583	op_cntrmask = op_hintmask
584
585	def countHints(self):
586		args = self.popall()
587		self.hintCount = self.hintCount + len(args) // 2
588
589	# misc
590	def op_and(self, index):
591		raise NotImplementedError
592	def op_or(self, index):
593		raise NotImplementedError
594	def op_not(self, index):
595		raise NotImplementedError
596	def op_store(self, index):
597		raise NotImplementedError
598	def op_abs(self, index):
599		raise NotImplementedError
600	def op_add(self, index):
601		raise NotImplementedError
602	def op_sub(self, index):
603		raise NotImplementedError
604	def op_div(self, index):
605		raise NotImplementedError
606	def op_load(self, index):
607		raise NotImplementedError
608	def op_neg(self, index):
609		raise NotImplementedError
610	def op_eq(self, index):
611		raise NotImplementedError
612	def op_drop(self, index):
613		raise NotImplementedError
614	def op_put(self, index):
615		raise NotImplementedError
616	def op_get(self, index):
617		raise NotImplementedError
618	def op_ifelse(self, index):
619		raise NotImplementedError
620	def op_random(self, index):
621		raise NotImplementedError
622	def op_mul(self, index):
623		raise NotImplementedError
624	def op_sqrt(self, index):
625		raise NotImplementedError
626	def op_dup(self, index):
627		raise NotImplementedError
628	def op_exch(self, index):
629		raise NotImplementedError
630	def op_index(self, index):
631		raise NotImplementedError
632	def op_roll(self, index):
633		raise NotImplementedError
634
635class T2OutlineExtractor(SimpleT2Decompiler):
636
637	def __init__(self, pen, localSubrs, globalSubrs, nominalWidthX, defaultWidthX):
638		SimpleT2Decompiler.__init__(self, localSubrs, globalSubrs)
639		self.pen = pen
640		self.nominalWidthX = nominalWidthX
641		self.defaultWidthX = defaultWidthX
642
643	def reset(self):
644		SimpleT2Decompiler.reset(self)
645		self.hints = []
646		self.gotWidth = 0
647		self.width = 0
648		self.currentPoint = (0, 0)
649		self.sawMoveTo = 0
650
651	def _nextPoint(self, point):
652		x, y = self.currentPoint
653		point = x + point[0], y + point[1]
654		self.currentPoint = point
655		return point
656
657	def rMoveTo(self, point):
658		self.pen.moveTo(self._nextPoint(point))
659		self.sawMoveTo = 1
660
661	def rLineTo(self, point):
662		if not self.sawMoveTo:
663			self.rMoveTo((0, 0))
664		self.pen.lineTo(self._nextPoint(point))
665
666	def rCurveTo(self, pt1, pt2, pt3):
667		if not self.sawMoveTo:
668			self.rMoveTo((0, 0))
669		nextPoint = self._nextPoint
670		self.pen.curveTo(nextPoint(pt1), nextPoint(pt2), nextPoint(pt3))
671
672	def closePath(self):
673		if self.sawMoveTo:
674			self.pen.closePath()
675		self.sawMoveTo = 0
676
677	def endPath(self):
678		# In T2 there are no open paths, so always do a closePath when
679		# finishing a sub path.
680		self.closePath()
681
682	def popallWidth(self, evenOdd=0):
683		args = self.popall()
684		if not self.gotWidth:
685			if evenOdd ^ (len(args) % 2):
686				self.width = self.nominalWidthX + args[0]
687				args = args[1:]
688			else:
689				self.width = self.defaultWidthX
690			self.gotWidth = 1
691		return args
692
693	def countHints(self):
694		args = self.popallWidth()
695		self.hintCount = self.hintCount + len(args) // 2
696
697	#
698	# hint operators
699	#
700	#def op_hstem(self, index):
701	#	self.countHints()
702	#def op_vstem(self, index):
703	#	self.countHints()
704	#def op_hstemhm(self, index):
705	#	self.countHints()
706	#def op_vstemhm(self, index):
707	#	self.countHints()
708	#def op_hintmask(self, index):
709	#	self.countHints()
710	#def op_cntrmask(self, index):
711	#	self.countHints()
712
713	#
714	# path constructors, moveto
715	#
716	def op_rmoveto(self, index):
717		self.endPath()
718		self.rMoveTo(self.popallWidth())
719	def op_hmoveto(self, index):
720		self.endPath()
721		self.rMoveTo((self.popallWidth(1)[0], 0))
722	def op_vmoveto(self, index):
723		self.endPath()
724		self.rMoveTo((0, self.popallWidth(1)[0]))
725	def op_endchar(self, index):
726		self.endPath()
727		args = self.popallWidth()
728		if args:
729			from fontTools.encodings.StandardEncoding import StandardEncoding
730			# endchar can do seac accent bulding; The T2 spec says it's deprecated,
731			# but recent software that shall remain nameless does output it.
732			adx, ady, bchar, achar = args
733			baseGlyph = StandardEncoding[bchar]
734			self.pen.addComponent(baseGlyph, (1, 0, 0, 1, 0, 0))
735			accentGlyph = StandardEncoding[achar]
736			self.pen.addComponent(accentGlyph, (1, 0, 0, 1, adx, ady))
737
738	#
739	# path constructors, lines
740	#
741	def op_rlineto(self, index):
742		args = self.popall()
743		for i in range(0, len(args), 2):
744			point = args[i:i+2]
745			self.rLineTo(point)
746
747	def op_hlineto(self, index):
748		self.alternatingLineto(1)
749	def op_vlineto(self, index):
750		self.alternatingLineto(0)
751
752	#
753	# path constructors, curves
754	#
755	def op_rrcurveto(self, index):
756		"""{dxa dya dxb dyb dxc dyc}+ rrcurveto"""
757		args = self.popall()
758		for i in range(0, len(args), 6):
759			dxa, dya, dxb, dyb, dxc, dyc, = args[i:i+6]
760			self.rCurveTo((dxa, dya), (dxb, dyb), (dxc, dyc))
761
762	def op_rcurveline(self, index):
763		"""{dxa dya dxb dyb dxc dyc}+ dxd dyd rcurveline"""
764		args = self.popall()
765		for i in range(0, len(args)-2, 6):
766			dxb, dyb, dxc, dyc, dxd, dyd = args[i:i+6]
767			self.rCurveTo((dxb, dyb), (dxc, dyc), (dxd, dyd))
768		self.rLineTo(args[-2:])
769
770	def op_rlinecurve(self, index):
771		"""{dxa dya}+ dxb dyb dxc dyc dxd dyd rlinecurve"""
772		args = self.popall()
773		lineArgs = args[:-6]
774		for i in range(0, len(lineArgs), 2):
775			self.rLineTo(lineArgs[i:i+2])
776		dxb, dyb, dxc, dyc, dxd, dyd = args[-6:]
777		self.rCurveTo((dxb, dyb), (dxc, dyc), (dxd, dyd))
778
779	def op_vvcurveto(self, index):
780		"dx1? {dya dxb dyb dyc}+ vvcurveto"
781		args = self.popall()
782		if len(args) % 2:
783			dx1 = args[0]
784			args = args[1:]
785		else:
786			dx1 = 0
787		for i in range(0, len(args), 4):
788			dya, dxb, dyb, dyc = args[i:i+4]
789			self.rCurveTo((dx1, dya), (dxb, dyb), (0, dyc))
790			dx1 = 0
791
792	def op_hhcurveto(self, index):
793		"""dy1? {dxa dxb dyb dxc}+ hhcurveto"""
794		args = self.popall()
795		if len(args) % 2:
796			dy1 = args[0]
797			args = args[1:]
798		else:
799			dy1 = 0
800		for i in range(0, len(args), 4):
801			dxa, dxb, dyb, dxc = args[i:i+4]
802			self.rCurveTo((dxa, dy1), (dxb, dyb), (dxc, 0))
803			dy1 = 0
804
805	def op_vhcurveto(self, index):
806		"""dy1 dx2 dy2 dx3 {dxa dxb dyb dyc dyd dxe dye dxf}* dyf? vhcurveto (30)
807		{dya dxb dyb dxc dxd dxe dye dyf}+ dxf? vhcurveto
808		"""
809		args = self.popall()
810		while args:
811			args = self.vcurveto(args)
812			if args:
813				args = self.hcurveto(args)
814
815	def op_hvcurveto(self, index):
816		"""dx1 dx2 dy2 dy3 {dya dxb dyb dxc dxd dxe dye dyf}* dxf?
817		{dxa dxb dyb dyc dyd dxe dye dxf}+ dyf?
818		"""
819		args = self.popall()
820		while args:
821			args = self.hcurveto(args)
822			if args:
823				args = self.vcurveto(args)
824
825	#
826	# path constructors, flex
827	#
828	def op_hflex(self, index):
829		dx1, dx2, dy2, dx3, dx4, dx5, dx6 = self.popall()
830		dy1 = dy3 = dy4 = dy6 = 0
831		dy5 = -dy2
832		self.rCurveTo((dx1, dy1), (dx2, dy2), (dx3, dy3))
833		self.rCurveTo((dx4, dy4), (dx5, dy5), (dx6, dy6))
834	def op_flex(self, index):
835		dx1, dy1, dx2, dy2, dx3, dy3, dx4, dy4, dx5, dy5, dx6, dy6, fd = self.popall()
836		self.rCurveTo((dx1, dy1), (dx2, dy2), (dx3, dy3))
837		self.rCurveTo((dx4, dy4), (dx5, dy5), (dx6, dy6))
838	def op_hflex1(self, index):
839		dx1, dy1, dx2, dy2, dx3, dx4, dx5, dy5, dx6 = self.popall()
840		dy3 = dy4 = 0
841		dy6 = -(dy1 + dy2 + dy3 + dy4 + dy5)
842
843		self.rCurveTo((dx1, dy1), (dx2, dy2), (dx3, dy3))
844		self.rCurveTo((dx4, dy4), (dx5, dy5), (dx6, dy6))
845	def op_flex1(self, index):
846		dx1, dy1, dx2, dy2, dx3, dy3, dx4, dy4, dx5, dy5, d6 = self.popall()
847		dx = dx1 + dx2 + dx3 + dx4 + dx5
848		dy = dy1 + dy2 + dy3 + dy4 + dy5
849		if abs(dx) > abs(dy):
850			dx6 = d6
851			dy6 = -dy
852		else:
853			dx6 = -dx
854			dy6 = d6
855		self.rCurveTo((dx1, dy1), (dx2, dy2), (dx3, dy3))
856		self.rCurveTo((dx4, dy4), (dx5, dy5), (dx6, dy6))
857
858	#
859	# MultipleMaster. Well...
860	#
861	def op_blend(self, index):
862		self.popall()
863
864	# misc
865	def op_and(self, index):
866		raise NotImplementedError
867	def op_or(self, index):
868		raise NotImplementedError
869	def op_not(self, index):
870		raise NotImplementedError
871	def op_store(self, index):
872		raise NotImplementedError
873	def op_abs(self, index):
874		raise NotImplementedError
875	def op_add(self, index):
876		raise NotImplementedError
877	def op_sub(self, index):
878		raise NotImplementedError
879	def op_div(self, index):
880		num2 = self.pop()
881		num1 = self.pop()
882		d1 = num1//num2
883		d2 = num1/num2
884		if d1 == d2:
885			self.push(d1)
886		else:
887			self.push(d2)
888	def op_load(self, index):
889		raise NotImplementedError
890	def op_neg(self, index):
891		raise NotImplementedError
892	def op_eq(self, index):
893		raise NotImplementedError
894	def op_drop(self, index):
895		raise NotImplementedError
896	def op_put(self, index):
897		raise NotImplementedError
898	def op_get(self, index):
899		raise NotImplementedError
900	def op_ifelse(self, index):
901		raise NotImplementedError
902	def op_random(self, index):
903		raise NotImplementedError
904	def op_mul(self, index):
905		raise NotImplementedError
906	def op_sqrt(self, index):
907		raise NotImplementedError
908	def op_dup(self, index):
909		raise NotImplementedError
910	def op_exch(self, index):
911		raise NotImplementedError
912	def op_index(self, index):
913		raise NotImplementedError
914	def op_roll(self, index):
915		raise NotImplementedError
916
917	#
918	# miscellaneous helpers
919	#
920	def alternatingLineto(self, isHorizontal):
921		args = self.popall()
922		for arg in args:
923			if isHorizontal:
924				point = (arg, 0)
925			else:
926				point = (0, arg)
927			self.rLineTo(point)
928			isHorizontal = not isHorizontal
929
930	def vcurveto(self, args):
931		dya, dxb, dyb, dxc = args[:4]
932		args = args[4:]
933		if len(args) == 1:
934			dyc = args[0]
935			args = []
936		else:
937			dyc = 0
938		self.rCurveTo((0, dya), (dxb, dyb), (dxc, dyc))
939		return args
940
941	def hcurveto(self, args):
942		dxa, dxb, dyb, dyc = args[:4]
943		args = args[4:]
944		if len(args) == 1:
945			dxc = args[0]
946			args = []
947		else:
948			dxc = 0
949		self.rCurveTo((dxa, 0), (dxb, dyb), (dxc, dyc))
950		return args
951
952
953class T1OutlineExtractor(T2OutlineExtractor):
954
955	def __init__(self, pen, subrs):
956		self.pen = pen
957		self.subrs = subrs
958		self.reset()
959
960	def reset(self):
961		self.flexing = 0
962		self.width = 0
963		self.sbx = 0
964		T2OutlineExtractor.reset(self)
965
966	def endPath(self):
967		if self.sawMoveTo:
968			self.pen.endPath()
969		self.sawMoveTo = 0
970
971	def popallWidth(self, evenOdd=0):
972		return self.popall()
973
974	def exch(self):
975		stack = self.operandStack
976		stack[-1], stack[-2] = stack[-2], stack[-1]
977
978	#
979	# path constructors
980	#
981	def op_rmoveto(self, index):
982		if self.flexing:
983			return
984		self.endPath()
985		self.rMoveTo(self.popall())
986	def op_hmoveto(self, index):
987		if self.flexing:
988			# We must add a parameter to the stack if we are flexing
989			self.push(0)
990			return
991		self.endPath()
992		self.rMoveTo((self.popall()[0], 0))
993	def op_vmoveto(self, index):
994		if self.flexing:
995			# We must add a parameter to the stack if we are flexing
996			self.push(0)
997			self.exch()
998			return
999		self.endPath()
1000		self.rMoveTo((0, self.popall()[0]))
1001	def op_closepath(self, index):
1002		self.closePath()
1003	def op_setcurrentpoint(self, index):
1004		args = self.popall()
1005		x, y = args
1006		self.currentPoint = x, y
1007
1008	def op_endchar(self, index):
1009		self.endPath()
1010
1011	def op_hsbw(self, index):
1012		sbx, wx = self.popall()
1013		self.width = wx
1014		self.sbx = sbx
1015		self.currentPoint = sbx, self.currentPoint[1]
1016	def op_sbw(self, index):
1017		self.popall()  # XXX
1018
1019	#
1020	def op_callsubr(self, index):
1021		subrIndex = self.pop()
1022		subr = self.subrs[subrIndex]
1023		self.execute(subr)
1024	def op_callothersubr(self, index):
1025		subrIndex = self.pop()
1026		nArgs = self.pop()
1027		#print nArgs, subrIndex, "callothersubr"
1028		if subrIndex == 0 and nArgs == 3:
1029			self.doFlex()
1030			self.flexing = 0
1031		elif subrIndex == 1 and nArgs == 0:
1032			self.flexing = 1
1033		# ignore...
1034	def op_pop(self, index):
1035		pass  # ignore...
1036
1037	def doFlex(self):
1038		finaly = self.pop()
1039		finalx = self.pop()
1040		self.pop()	# flex height is unused
1041
1042		p3y = self.pop()
1043		p3x = self.pop()
1044		bcp4y = self.pop()
1045		bcp4x = self.pop()
1046		bcp3y = self.pop()
1047		bcp3x = self.pop()
1048		p2y = self.pop()
1049		p2x = self.pop()
1050		bcp2y = self.pop()
1051		bcp2x = self.pop()
1052		bcp1y = self.pop()
1053		bcp1x = self.pop()
1054		rpy = self.pop()
1055		rpx = self.pop()
1056
1057		# call rrcurveto
1058		self.push(bcp1x+rpx)
1059		self.push(bcp1y+rpy)
1060		self.push(bcp2x)
1061		self.push(bcp2y)
1062		self.push(p2x)
1063		self.push(p2y)
1064		self.op_rrcurveto(None)
1065
1066		# call rrcurveto
1067		self.push(bcp3x)
1068		self.push(bcp3y)
1069		self.push(bcp4x)
1070		self.push(bcp4y)
1071		self.push(p3x)
1072		self.push(p3y)
1073		self.op_rrcurveto(None)
1074
1075		# Push back final coords so subr 0 can find them
1076		self.push(finalx)
1077		self.push(finaly)
1078
1079	def op_dotsection(self, index):
1080		self.popall()  # XXX
1081	def op_hstem3(self, index):
1082		self.popall()  # XXX
1083	def op_seac(self, index):
1084		"asb adx ady bchar achar seac"
1085		from fontTools.encodings.StandardEncoding import StandardEncoding
1086		asb, adx, ady, bchar, achar = self.popall()
1087		baseGlyph = StandardEncoding[bchar]
1088		self.pen.addComponent(baseGlyph, (1, 0, 0, 1, 0, 0))
1089		accentGlyph = StandardEncoding[achar]
1090		adx = adx + self.sbx - asb  # seac weirdness
1091		self.pen.addComponent(accentGlyph, (1, 0, 0, 1, adx, ady))
1092	def op_vstem3(self, index):
1093		self.popall()  # XXX
1094
1095
1096class DictDecompiler(ByteCodeBase):
1097
1098	operandEncoding = cffDictOperandEncoding
1099
1100	def __init__(self, strings):
1101		self.stack = []
1102		self.strings = strings
1103		self.dict = {}
1104
1105	def getDict(self):
1106		assert len(self.stack) == 0, "non-empty stack"
1107		return self.dict
1108
1109	def decompile(self, data):
1110		index = 0
1111		lenData = len(data)
1112		push = self.stack.append
1113		while index < lenData:
1114			b0 = byteord(data[index])
1115			index = index + 1
1116			code = self.operandEncoding[b0]
1117			handler = getattr(self, code)
1118			value, index = handler(b0, data, index)
1119			if value is not None:
1120				push(value)
1121
1122	def pop(self):
1123		value = self.stack[-1]
1124		del self.stack[-1]
1125		return value
1126
1127	def popall(self):
1128		args = self.stack[:]
1129		del self.stack[:]
1130		return args
1131
1132	def do_operator(self, b0, data, index):
1133		if b0 == 12:
1134			op = (b0, byteord(data[index]))
1135			index = index+1
1136		else:
1137			op = b0
1138		operator, argType = self.operators[op]
1139		self.handle_operator(operator, argType)
1140		return None, index
1141
1142	def handle_operator(self, operator, argType):
1143		if isinstance(argType, type(())):
1144			value = ()
1145			for i in range(len(argType)-1, -1, -1):
1146				arg = argType[i]
1147				arghandler = getattr(self, "arg_" + arg)
1148				value = (arghandler(operator),) + value
1149		else:
1150			arghandler = getattr(self, "arg_" + argType)
1151			value = arghandler(operator)
1152		self.dict[operator] = value
1153
1154	def arg_number(self, name):
1155		return self.pop()
1156	def arg_SID(self, name):
1157		return self.strings[self.pop()]
1158	def arg_array(self, name):
1159		return self.popall()
1160	def arg_delta(self, name):
1161		out = []
1162		current = 0
1163		for v in self.popall():
1164			current = current + v
1165			out.append(current)
1166		return out
1167
1168
1169def calcSubrBias(subrs):
1170	nSubrs = len(subrs)
1171	if nSubrs < 1240:
1172		bias = 107
1173	elif nSubrs < 33900:
1174		bias = 1131
1175	else:
1176		bias = 32768
1177	return bias
1178