psCharStrings.py revision f2cf9c5d6d503e16ee43dc9b617d96aed38806a8
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 T2CharString(ByteCodeBase):
204
205	operandEncoding = t2OperandEncoding
206	operators, opcodes = buildOperatorDict(t2Operators)
207
208	def __init__(self, bytecode=None, program=None, subrs=None, globalSubrs=None):
209		if program is None:
210			program = []
211		self.bytecode = bytecode
212		self.program = program
213		self.subrs = subrs
214		self.globalSubrs = globalSubrs
215
216	def __repr__(self):
217		if self.bytecode is None:
218			return "<%s (source) at %x>" % (self.__class__.__name__, id(self))
219		else:
220			return "<%s (bytecode) at %x>" % (self.__class__.__name__, id(self))
221
222	def decompile(self):
223		if not self.needsDecompilation():
224			return
225		decompiler = SimpleT2Decompiler(self.subrs, self.globalSubrs)
226		decompiler.reset()
227		decompiler.execute(self)
228
229	def compile(self):
230		if self.bytecode is not None:
231			return
232		bytecode = []
233		opcodes = self.opcodes
234		for token in self.program:
235			tp = type(token)
236			if tp == types.StringType:
237				if opcodes.has_key(token):
238					bytecode.extend(map(chr, opcodes[token]))
239				else:
240					bytecode.append(token)  # hint mask
241			elif tp == types.IntType:
242				bytecode.append(encodeIntT2(token))
243			else:
244				assert 0, "unsupported type: %s" % tp
245		bytecode = "".join(bytecode)
246		self.setBytecode(bytecode)
247
248	def needsDecompilation(self):
249		return self.bytecode is not None
250
251	def setProgram(self, program):
252		self.program = program
253		self.bytecode = None
254
255	def setBytecode(self, bytecode):
256		self.bytecode = bytecode
257		self.program = None
258
259	def getToken(self, index,
260			len=len, ord=ord, getattr=getattr, type=type, StringType=types.StringType):
261		if self.bytecode is not None:
262			if index >= len(self.bytecode):
263				return None, 0, 0
264			b0 = ord(self.bytecode[index])
265			index = index + 1
266			code = self.operandEncoding[b0]
267			handler = getattr(self, code)
268			token, index = handler(b0, self.bytecode, index)
269		else:
270			if index >= len(self.program):
271				return None, 0, 0
272			token = self.program[index]
273			index = index + 1
274		isOperator = type(token) == StringType
275		return token, isOperator, index
276
277	def getBytes(self, index, nBytes):
278		if self.bytecode is not None:
279			newIndex = index + nBytes
280			bytes = self.bytecode[index:newIndex]
281			index = newIndex
282		else:
283			bytes = self.program[index]
284			index = index + 1
285		assert len(bytes) == nBytes
286		return bytes, index
287
288	def do_operator(self, b0, data, index):
289		if b0 == 12:
290			op = (b0, ord(data[index]))
291			index = index+1
292		else:
293			op = b0
294		operator = self.operators[op]
295		return operator, index
296
297	def toXML(self, xmlWriter):
298		from fontTools.misc.textTools import num2binary
299		if self.bytecode is not None:
300			xmlWriter.dumphex(self.bytecode)
301		else:
302			index = 0
303			args = []
304			while 1:
305				token, isOperator, index = self.getToken(index)
306				if token is None:
307					break
308				if isOperator:
309					args = map(str, args)
310					if token in ('hintmask', 'cntrmask'):
311						hintMask, isOperator, index = self.getToken(index)
312						bits = []
313						for byte in hintMask:
314							bits.append(num2binary(ord(byte), 8))
315						hintMask = string.join(bits, "")
316						line = string.join(args + [token, hintMask], " ")
317					else:
318						line = string.join(args + [token], " ")
319					xmlWriter.write(line)
320					xmlWriter.newline()
321					args = []
322				else:
323					args.append(token)
324
325
326t1Operators = [
327#	opcode     name
328	(1,        'hstem'),
329	(3,        'vstem'),
330	(4,        'vmoveto'),
331	(5,        'rlineto'),
332	(6,        'hlineto'),
333	(7,        'vlineto'),
334	(8,        'rrcurveto'),
335	(9,        'closepath'),
336	(10,       'callsubr'),
337	(11,       'return'),
338	(13,       'hsbw'),
339	(14,       'endchar'),
340	(21,       'rmoveto'),
341	(22,       'hmoveto'),
342	(30,       'vhcurveto'),
343	(31,       'hvcurveto'),
344	((12, 0),  'dotsection'),
345	((12, 1),  'vstem3'),
346	((12, 2),  'hstem3'),
347	((12, 6),  'seac'),
348	((12, 7),  'sbw'),
349	((12, 12), 'div'),
350	((12, 16), 'callothersubr'),
351	((12, 17), 'pop'),
352	((12, 33), 'setcurrentpoint'),
353]
354
355class T1CharString(T2CharString):
356
357	operandEncoding = t1OperandEncoding
358	operators, opcodes = buildOperatorDict(t1Operators)
359
360	def decompile(self):
361		if self.program is not None:
362			return
363		program = []
364		index = 0
365		while 1:
366			token, isOperator, index = self.getToken(index)
367			if token is None:
368				break
369			program.append(token)
370		self.setProgram(program)
371
372
373class SimpleT2Decompiler:
374
375	def __init__(self, localSubrs, globalSubrs):
376		self.localSubrs = localSubrs
377		self.localBias = calcSubrBias(localSubrs)
378		self.globalSubrs = globalSubrs
379		self.globalBias = calcSubrBias(globalSubrs)
380		self.reset()
381
382	def reset(self):
383		self.callingStack = []
384		self.operandStack = []
385		self.hintCount = 0
386		self.hintMaskBytes = 0
387
388	def execute(self, charString):
389		self.callingStack.append(charString)
390		needsDecompilation = charString.needsDecompilation()
391		if needsDecompilation:
392			program = []
393			pushToProgram = program.append
394		else:
395			pushToProgram = lambda x: None
396		pushToStack = self.operandStack.append
397		index = 0
398		while 1:
399			token, isOperator, index = charString.getToken(index)
400			if token is None:
401				break  # we're done!
402			pushToProgram(token)
403			if isOperator:
404				handlerName = "op_" + token
405				if hasattr(self, handlerName):
406					handler = getattr(self, handlerName)
407					rv = handler(index)
408					if rv:
409						hintMaskBytes, index = rv
410						pushToProgram(hintMaskBytes)
411				else:
412					self.popall()
413			else:
414				pushToStack(token)
415		if needsDecompilation:
416			charString.setProgram(program)
417			assert program[-1] in ("endchar", "return", "callsubr", "callgsubr", "seac")
418		del self.callingStack[-1]
419
420	def pop(self):
421		value = self.operandStack[-1]
422		del self.operandStack[-1]
423		return value
424
425	def popall(self):
426		stack = self.operandStack[:]
427		self.operandStack[:] = []
428		return stack
429
430	def push(self, value):
431		self.operandStack.append(value)
432
433	def op_return(self, index):
434		if self.operandStack:
435			pass
436
437	def op_endchar(self, index):
438		pass
439
440	def op_callsubr(self, index):
441		subrIndex = self.pop()
442		subr = self.localSubrs[subrIndex+self.localBias]
443		self.execute(subr)
444
445	def op_callgsubr(self, index):
446		subrIndex = self.pop()
447		subr = self.globalSubrs[subrIndex+self.globalBias]
448		self.execute(subr)
449
450	def op_hstem(self, index):
451		self.countHints()
452	def op_vstem(self, index):
453		self.countHints()
454	def op_hstemhm(self, index):
455		self.countHints()
456	def op_vstemhm(self, index):
457		self.countHints()
458
459	def op_hintmask(self, index):
460		if not self.hintMaskBytes:
461			self.countHints()
462			self.hintMaskBytes = (self.hintCount + 7) / 8
463		hintMaskBytes, index = self.callingStack[-1].getBytes(index, self.hintMaskBytes)
464		return hintMaskBytes, index
465
466	op_cntrmask = op_hintmask
467
468	def countHints(self):
469		args = self.popall()
470		self.hintCount = self.hintCount + len(args) / 2
471
472
473class T2OutlineExtractor(SimpleT2Decompiler):
474
475	def __init__(self, localSubrs, globalSubrs, nominalWidthX, defaultWidthX):
476		SimpleT2Decompiler.__init__(self, localSubrs, globalSubrs)
477		self.nominalWidthX = nominalWidthX
478		self.defaultWidthX = defaultWidthX
479
480	def reset(self):
481		import Numeric
482		SimpleT2Decompiler.reset(self)
483		self.hints = []
484		self.gotWidth = 0
485		self.width = 0
486		self.currentPoint = Numeric.array((0, 0), Numeric.Int16)
487		self.contours = []
488
489	def getContours(self):
490		return self.contours
491
492	def newPath(self):
493		self.contours.append([[], [], 0])
494
495	def closePath(self):
496		if self.contours and self.contours[-1][2] == 0:
497			self.contours[-1][2] = 1
498
499	def appendPoint(self, point, isPrimary):
500		import Numeric
501		point = self.currentPoint + Numeric.array(point, Numeric.Int16)
502		if not self.contours or self.contours[-1][2]:
503			# The subpath doesn't start with a moveto. Not sure whether
504			# this is legal, but apparently it usually works.
505			self.newPath()
506			self.appendPoint((0, 0), 1)
507		self.currentPoint = point
508		points, flags, isClosed = self.contours[-1]
509		points.append(point)
510		flags.append(isPrimary)
511
512	def popallWidth(self, evenOdd=0):
513		args = self.popall()
514		if not self.gotWidth:
515			if evenOdd ^ (len(args) % 2):
516				self.width = self.nominalWidthX + args[0]
517				args = args[1:]
518			else:
519				self.width = self.defaultWidthX
520			self.gotWidth = 1
521		return args
522
523	def countHints(self):
524		args = self.popallWidth()
525		self.hintCount = self.hintCount + len(args) / 2
526
527	#
528	# hint operators
529	#
530	#def op_hstem(self, index):
531	#	self.countHints()
532	#def op_vstem(self, index):
533	#	self.countHints()
534	#def op_hstemhm(self, index):
535	#	self.countHints()
536	#def op_vstemhm(self, index):
537	#	self.countHints()
538	#def op_hintmask(self, index):
539	#	self.countHints()
540	#def op_cntrmask(self, index):
541	#	self.countHints()
542
543	#
544	# path constructors, moveto
545	#
546	def op_rmoveto(self, index):
547		self.closePath()
548		self.newPath()
549		self.appendPoint(self.popallWidth(), 1)
550	def op_hmoveto(self, index):
551		self.closePath()
552		self.newPath()
553		self.appendPoint((self.popallWidth(1)[0], 0), 1)
554	def op_vmoveto(self, index):
555		self.closePath()
556		self.newPath()
557		self.appendPoint((0, self.popallWidth(1)[0]), 1)
558	def op_endchar(self, index):
559		self.closePath()
560
561	#
562	# path constructors, lines
563	#
564	def op_rlineto(self, index):
565		args = self.popall()
566		for i in range(0, len(args), 2):
567			point = args[i:i+2]
568			self.appendPoint(point, 1)
569
570	def op_hlineto(self, index):
571		self.alternatingLineto(1)
572	def op_vlineto(self, index):
573		self.alternatingLineto(0)
574
575	#
576	# path constructors, curves
577	#
578	def op_rrcurveto(self, index):
579		"""{dxa dya dxb dyb dxc dyc}+ rrcurveto"""
580		args = self.popall()
581		for i in range(0, len(args), 6):
582			dxa, dya, dxb, dyb, dxc, dyc, = args[i:i+6]
583			self.rrcurveto((dxa, dya), (dxb, dyb), (dxc, dyc))
584
585	def op_rcurveline(self, index):
586		"""{dxa dya dxb dyb dxc dyc}+ dxd dyd rcurveline"""
587		args = self.popall()
588		for i in range(0, len(args)-2, 6):
589			dxb, dyb, dxc, dyc, dxd, dyd = args[i:i+6]
590			self.rrcurveto((dxb, dyb), (dxc, dyc), (dxd, dyd))
591		self.appendPoint(args[-2:], 1)
592
593	def op_rlinecurve(self, index):
594		"""{dxa dya}+ dxb dyb dxc dyc dxd dyd rlinecurve"""
595		args = self.popall()
596		lineArgs = args[:-6]
597		for i in range(0, len(lineArgs), 2):
598			self.appendPoint(lineArgs[i:i+2], 1)
599		dxb, dyb, dxc, dyc, dxd, dyd = args[-6:]
600		self.rrcurveto((dxb, dyb), (dxc, dyc), (dxd, dyd))
601
602	def op_vvcurveto(self, index):
603		"dx1? {dya dxb dyb dyc}+ vvcurveto"
604		args = self.popall()
605		if len(args) % 2:
606			dx1 = args[0]
607			args = args[1:]
608		else:
609			dx1 = 0
610		for i in range(0, len(args), 4):
611			dya, dxb, dyb, dyc = args[i:i+4]
612			self.rrcurveto((dx1, dya), (dxb, dyb), (0, dyc))
613			dx1 = 0
614
615	def op_hhcurveto(self, index):
616		"""dy1? {dxa dxb dyb dxc}+ hhcurveto"""
617		args = self.popall()
618		if len(args) % 2:
619			dy1 = args[0]
620			args = args[1:]
621		else:
622			dy1 = 0
623		for i in range(0, len(args), 4):
624			dxa, dxb, dyb, dxc = args[i:i+4]
625			self.rrcurveto((dxa, dy1), (dxb, dyb), (dxc, 0))
626			dy1 = 0
627
628	def op_vhcurveto(self, index):
629		"""dy1 dx2 dy2 dx3 {dxa dxb dyb dyc dyd dxe dye dxf}* dyf? vhcurveto (30)
630		{dya dxb dyb dxc dxd dxe dye dyf}+ dxf? vhcurveto
631		"""
632		args = self.popall()
633		while args:
634			args = self.vcurveto(args)
635			if args:
636				args = self.hcurveto(args)
637
638	def op_hvcurveto(self, index):
639		"""dx1 dx2 dy2 dy3 {dya dxb dyb dxc dxd dxe dye dyf}* dxf?
640		{dxa dxb dyb dyc dyd dxe dye dxf}+ dyf?
641		"""
642		args = self.popall()
643		while args:
644			args = self.hcurveto(args)
645			if args:
646				args = self.vcurveto(args)
647
648	#
649	# path constructors, flex
650	#
651	def op_hflex(self, index):
652		XXX
653	def op_flex(self, index):
654		XXX
655	def op_hflex1(self, index):
656		XXX
657	def op_flex1(self, index):
658		XXX
659
660	#
661	# MultipleMaster. Well...
662	#
663	def op_blend(self, index):
664		XXX
665
666	# misc
667	def op_and(self, index):
668		XXX
669	def op_or(self, index):
670		XXX
671	def op_not(self, index):
672		XXX
673	def op_store(self, index):
674		XXX
675	def op_abs(self, index):
676		XXX
677	def op_add(self, index):
678		XXX
679	def op_sub(self, index):
680		XXX
681	def op_div(self, index):
682		num2 = self.pop()
683		num1 = self.pop()
684		d1 = num1/num2
685		d2 = float(num1)/num2
686		if d1 == d2:
687			self.push(d1)
688		else:
689			self.push(d2)
690	def op_load(self, index):
691		XXX
692	def op_neg(self, index):
693		XXX
694	def op_eq(self, index):
695		XXX
696	def op_drop(self, index):
697		XXX
698	def op_put(self, index):
699		XXX
700	def op_get(self, index):
701		XXX
702	def op_ifelse(self, index):
703		XXX
704	def op_random(self, index):
705		XXX
706	def op_mul(self, index):
707		XXX
708	def op_sqrt(self, index):
709		XXX
710	def op_dup(self, index):
711		XXX
712	def op_exch(self, index):
713		XXX
714	def op_index(self, index):
715		XXX
716	def op_roll(self, index):
717		XXX
718
719	#
720	# miscelaneous helpers
721	#
722	def alternatingLineto(self, isHorizontal):
723		args = self.popall()
724		for arg in args:
725			if isHorizontal:
726				point = (arg, 0)
727			else:
728				point = (0, arg)
729			self.appendPoint(point, 1)
730			isHorizontal = not isHorizontal
731
732	def rrcurveto(self, p1, p2, p3):
733		self.appendPoint(p1, 0)
734		self.appendPoint(p2, 0)
735		self.appendPoint(p3, 1)
736
737	def vcurveto(self, args):
738		dya, dxb, dyb, dxc = args[:4]
739		args = args[4:]
740		if len(args) == 1:
741			dyc = args[0]
742			args = []
743		else:
744			dyc = 0
745		self.rrcurveto((0, dya), (dxb, dyb), (dxc, dyc))
746		return args
747
748	def hcurveto(self, args):
749		dxa, dxb, dyb, dyc = args[:4]
750		args = args[4:]
751		if len(args) == 1:
752			dxc = args[0]
753			args = []
754		else:
755			dxc = 0
756		self.rrcurveto((dxa, 0), (dxb, dyb), (dxc, dyc))
757		return args
758
759
760class T1OutlineExtractor(T2OutlineExtractor):
761
762	def __init__(self, subrs):
763		self.subrs = subrs
764		self.reset()
765
766	def reset(self):
767		self.flexing = 0
768		self.width = 0
769		self.sbx = 0
770		T2OutlineExtractor.reset(self)
771
772	def popallWidth(self, evenOdd=0):
773		return self.popall()
774
775	def exch(self):
776		stack = self.operandStack
777		stack[-1], stack[-2] = stack[-2], stack[-1]
778
779	#
780	# path constructors
781	#
782	def op_rmoveto(self, index):
783		if self.flexing:
784			return
785		self.newPath()
786		self.appendPoint(self.popall(), 1)
787	def op_hmoveto(self, index):
788		if self.flexing:
789			# We must add a parameter to the stack if we are flexing
790			self.push(0)
791			return
792		self.newPath()
793		self.appendPoint((self.popall()[0], 0), 1)
794	def op_vmoveto(self, index):
795		if self.flexing:
796			# We must add a parameter to the stack if we are flexing
797			self.push(0)
798			self.exch()
799			return
800		self.newPath()
801		self.appendPoint((0, self.popall()[0]), 1)
802	def op_closepath(self, index):
803		self.closePath()
804	def op_setcurrentpoint(self, index):
805		args = self.popall()
806		x, y = args
807		self.currentPoint[0] = x
808		self.currentPoint[1] = y
809
810	def op_endchar(self, index):
811		self.closePath()
812
813	def op_hsbw(self, index):
814		sbx, wx = self.popall()
815		self.width = wx
816		self.sbx = sbx
817		self.currentPoint[0] = sbx
818	def op_sbw(self, index):
819		self.popall()  # XXX
820
821	#
822	def op_callsubr(self, index):
823		subrIndex = self.pop()
824		subr = self.subrs[subrIndex]
825		self.execute(subr)
826	def op_callothersubr(self, index):
827		subrIndex = self.pop()
828		nArgs = self.pop()
829		#print nArgs, subrIndex, "callothersubr"
830		if subrIndex == 0 and nArgs == 3:
831			self.doFlex()
832			self.flexing = 0
833		elif subrIndex == 1 and nArgs == 0:
834			self.flexing = 1
835		# ignore...
836	def op_pop(self, index):
837		pass  # ignore...
838
839	def doFlex(self):
840		finaly = self.pop()
841		finalx = self.pop()
842		self.pop()	# flex height is unused
843
844		p3y = self.pop()
845		p3x = self.pop()
846		bcp4y = self.pop()
847		bcp4x = self.pop()
848		bcp3y = self.pop()
849		bcp3x = self.pop()
850		p2y = self.pop()
851		p2x = self.pop()
852		bcp2y = self.pop()
853		bcp2x = self.pop()
854		bcp1y = self.pop()
855		bcp1x = self.pop()
856		rpy = self.pop()
857		rpx = self.pop()
858
859		# call rrcurveto
860		self.push(bcp1x+rpx)
861		self.push(bcp1y+rpy)
862		self.push(bcp2x)
863		self.push(bcp2y)
864		self.push(p2x)
865		self.push(p2y)
866		self.op_rrcurveto(None)
867
868		# call rrcurveto
869		self.push(bcp3x)
870		self.push(bcp3y)
871		self.push(bcp4x)
872		self.push(bcp4y)
873		self.push(p3x)
874		self.push(p3y)
875		self.op_rrcurveto(None)
876
877		# Push back final coords so subr 0 can find them
878		self.push(finalx)
879		self.push(finaly)
880
881	def op_dotsection(self, index):
882		self.popall()  # XXX
883	def op_hstem3(self, index):
884		self.popall()  # XXX
885	def op_seac(self, index):
886		"asb adx ady bchar achar seac"
887		asb, adx, ady, bchar, achar = self.popall()  # XXX
888		self.contours.append([(asb, adx, ady, bchar, achar), None, -1])
889	def op_vstem3(self, index):
890		self.popall()  # XXX
891
892
893class DictDecompiler(ByteCodeBase):
894
895	operandEncoding = cffDictOperandEncoding
896
897	def __init__(self, strings):
898		self.stack = []
899		self.strings = strings
900		self.dict = {}
901
902	def getDict(self):
903		assert len(self.stack) == 0, "non-empty stack"
904		return self.dict
905
906	def decompile(self, data):
907		index = 0
908		lenData = len(data)
909		push = self.stack.append
910		while index < lenData:
911			b0 = ord(data[index])
912			index = index + 1
913			code = self.operandEncoding[b0]
914			handler = getattr(self, code)
915			value, index = handler(b0, data, index)
916			if value is not None:
917				push(value)
918
919	def pop(self):
920		value = self.stack[-1]
921		del self.stack[-1]
922		return value
923
924	def popall(self):
925		all = self.stack[:]
926		del self.stack[:]
927		return all
928
929	def do_operator(self, b0, data, index):
930		if b0 == 12:
931			op = (b0, ord(data[index]))
932			index = index+1
933		else:
934			op = b0
935		operator, argType = self.operators[op]
936		self.handle_operator(operator, argType)
937		return None, index
938
939	def handle_operator(self, operator, argType):
940		if type(argType) == type(()):
941			value = ()
942			for i in range(len(argType)-1, -1, -1):
943				arg = argType[i]
944				arghandler = getattr(self, "arg_" + arg)
945				value = (arghandler(operator),) + value
946		else:
947			arghandler = getattr(self, "arg_" + argType)
948			value = arghandler(operator)
949		self.dict[operator] = value
950
951	def arg_number(self, name):
952		return self.pop()
953	def arg_SID(self, name):
954		return self.strings[self.pop()]
955	def arg_array(self, name):
956		return self.popall()
957	def arg_delta(self, name):
958		out = []
959		current = 0
960		for v in self.popall():
961			current = current + v
962			out.append(current)
963		return out
964
965
966def calcSubrBias(subrs):
967	nSubrs = len(subrs)
968	if nSubrs < 1240:
969		bias = 107
970	elif nSubrs < 33900:
971		bias = 1131
972	else:
973		bias = 32768
974	return bias
975
976