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