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