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