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