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