• 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
948	def __repr__(self):
949		if self.bytecode is None:
950			return "<%s (source) at %x>" % (self.__class__.__name__, id(self))
951		else:
952			return "<%s (bytecode) at %x>" % (self.__class__.__name__, id(self))
953
954	def getIntEncoder(self):
955		return encodeIntT2
956
957	def getFixedEncoder(self):
958		return encodeFixed
959
960	def decompile(self):
961		if not self.needsDecompilation():
962			return
963		subrs = getattr(self.private, "Subrs", [])
964		decompiler = self.decompilerClass(subrs, self.globalSubrs, self.private)
965		decompiler.execute(self)
966
967	def draw(self, pen):
968		subrs = getattr(self.private, "Subrs", [])
969		extractor = self.outlineExtractor(pen, subrs, self.globalSubrs,
970				self.private.nominalWidthX, self.private.defaultWidthX,
971				self.private)
972		extractor.execute(self)
973		self.width = extractor.width
974
975	def calcBounds(self, glyphSet):
976		boundsPen = BoundsPen(glyphSet)
977		self.draw(boundsPen)
978		return boundsPen.bounds
979
980	def compile(self, isCFF2=False):
981		if self.bytecode is not None:
982			return
983		opcodes = self.opcodes
984		program = self.program
985
986		if isCFF2:
987			# If present, remove return and endchar operators.
988			if program and program[-1] in ("return", "endchar"):
989				program = program[:-1]
990		elif program and not isinstance(program[-1], basestring):
991			raise CharStringCompileError(
992				"T2CharString or Subr has items on the stack after last operator."
993			)
994
995		bytecode = []
996		encodeInt = self.getIntEncoder()
997		encodeFixed = self.getFixedEncoder()
998		i = 0
999		end = len(program)
1000		while i < end:
1001			token = program[i]
1002			i = i + 1
1003			if isinstance(token, basestring):
1004				try:
1005					bytecode.extend(bytechr(b) for b in opcodes[token])
1006				except KeyError:
1007					raise CharStringCompileError("illegal operator: %s" % token)
1008				if token in ('hintmask', 'cntrmask'):
1009					bytecode.append(program[i])  # hint mask
1010					i = i + 1
1011			elif isinstance(token, int):
1012				bytecode.append(encodeInt(token))
1013			elif isinstance(token, float):
1014				bytecode.append(encodeFixed(token))
1015			else:
1016				assert 0, "unsupported type: %s" % type(token)
1017		try:
1018			bytecode = bytesjoin(bytecode)
1019		except TypeError:
1020			log.error(bytecode)
1021			raise
1022		self.setBytecode(bytecode)
1023
1024	def needsDecompilation(self):
1025		return self.bytecode is not None
1026
1027	def setProgram(self, program):
1028		self.program = program
1029		self.bytecode = None
1030
1031	def setBytecode(self, bytecode):
1032		self.bytecode = bytecode
1033		self.program = None
1034
1035	def getToken(self, index,
1036			len=len, byteord=byteord, basestring=basestring,
1037			isinstance=isinstance):
1038		if self.bytecode is not None:
1039			if index >= len(self.bytecode):
1040				return None, 0, 0
1041			b0 = byteord(self.bytecode[index])
1042			index = index + 1
1043			handler = self.operandEncoding[b0]
1044			token, index = handler(self, b0, self.bytecode, index)
1045		else:
1046			if index >= len(self.program):
1047				return None, 0, 0
1048			token = self.program[index]
1049			index = index + 1
1050		isOperator = isinstance(token, basestring)
1051		return token, isOperator, index
1052
1053	def getBytes(self, index, nBytes):
1054		if self.bytecode is not None:
1055			newIndex = index + nBytes
1056			bytes = self.bytecode[index:newIndex]
1057			index = newIndex
1058		else:
1059			bytes = self.program[index]
1060			index = index + 1
1061		assert len(bytes) == nBytes
1062		return bytes, index
1063
1064	def handle_operator(self, operator):
1065		return operator
1066
1067	def toXML(self, xmlWriter):
1068		from fontTools.misc.textTools import num2binary
1069		if self.bytecode is not None:
1070			xmlWriter.dumphex(self.bytecode)
1071		else:
1072			index = 0
1073			args = []
1074			while True:
1075				token, isOperator, index = self.getToken(index)
1076				if token is None:
1077					break
1078				if isOperator:
1079					args = [str(arg) for arg in args]
1080					if token in ('hintmask', 'cntrmask'):
1081						hintMask, isOperator, index = self.getToken(index)
1082						bits = []
1083						for byte in hintMask:
1084							bits.append(num2binary(byteord(byte), 8))
1085						hintMask = strjoin(bits)
1086						line = ' '.join(args + [token, hintMask])
1087					else:
1088						line = ' '.join(args + [token])
1089					xmlWriter.write(line)
1090					xmlWriter.newline()
1091					args = []
1092				else:
1093					args.append(token)
1094			if args:
1095				# NOTE: only CFF2 charstrings/subrs can have numeric arguments on
1096				# the stack after the last operator. Compiling this would fail if
1097				# this is part of CFF 1.0 table.
1098				args = [str(arg) for arg in args]
1099				line = ' '.join(args)
1100				xmlWriter.write(line)
1101
1102	def fromXML(self, name, attrs, content):
1103		from fontTools.misc.textTools import binary2num, readHex
1104		if attrs.get("raw"):
1105			self.setBytecode(readHex(content))
1106			return
1107		content = strjoin(content)
1108		content = content.split()
1109		program = []
1110		end = len(content)
1111		i = 0
1112		while i < end:
1113			token = content[i]
1114			i = i + 1
1115			try:
1116				token = int(token)
1117			except ValueError:
1118				try:
1119					token = float(token)
1120				except ValueError:
1121					program.append(token)
1122					if token in ('hintmask', 'cntrmask'):
1123						mask = content[i]
1124						maskBytes = b""
1125						for j in range(0, len(mask), 8):
1126							maskBytes = maskBytes + bytechr(binary2num(mask[j:j+8]))
1127						program.append(maskBytes)
1128						i = i + 1
1129				else:
1130					program.append(token)
1131			else:
1132				program.append(token)
1133		self.setProgram(program)
1134
1135class T1CharString(T2CharString):
1136
1137	operandEncoding = t1OperandEncoding
1138	operators, opcodes = buildOperatorDict(t1Operators)
1139
1140	def __init__(self, bytecode=None, program=None, subrs=None):
1141		if program is None:
1142			program = []
1143		self.bytecode = bytecode
1144		self.program = program
1145		self.subrs = subrs
1146
1147	def getIntEncoder(self):
1148		return encodeIntT1
1149
1150	def getFixedEncoder(self):
1151		def encodeFixed(value):
1152			raise TypeError("Type 1 charstrings don't support floating point operands")
1153
1154	def decompile(self):
1155		if self.bytecode is None:
1156			return
1157		program = []
1158		index = 0
1159		while True:
1160			token, isOperator, index = self.getToken(index)
1161			if token is None:
1162				break
1163			program.append(token)
1164		self.setProgram(program)
1165
1166	def draw(self, pen):
1167		extractor = T1OutlineExtractor(pen, self.subrs)
1168		extractor.execute(self)
1169		self.width = extractor.width
1170
1171class DictDecompiler(object):
1172
1173	operandEncoding = cffDictOperandEncoding
1174
1175	def __init__(self, strings, parent=None):
1176		self.stack = []
1177		self.strings = strings
1178		self.dict = {}
1179		self.parent = parent
1180
1181	def getDict(self):
1182		assert len(self.stack) == 0, "non-empty stack"
1183		return self.dict
1184
1185	def decompile(self, data):
1186		index = 0
1187		lenData = len(data)
1188		push = self.stack.append
1189		while index < lenData:
1190			b0 = byteord(data[index])
1191			index = index + 1
1192			handler = self.operandEncoding[b0]
1193			value, index = handler(self, b0, data, index)
1194			if value is not None:
1195				push(value)
1196	def pop(self):
1197		value = self.stack[-1]
1198		del self.stack[-1]
1199		return value
1200
1201	def popall(self):
1202		args = self.stack[:]
1203		del self.stack[:]
1204		return args
1205
1206	def handle_operator(self, operator):
1207		operator, argType = operator
1208		if isinstance(argType, tuple):
1209			value = ()
1210			for i in range(len(argType)-1, -1, -1):
1211				arg = argType[i]
1212				arghandler = getattr(self, "arg_" + arg)
1213				value = (arghandler(operator),) + value
1214		else:
1215			arghandler = getattr(self, "arg_" + argType)
1216			value = arghandler(operator)
1217		if operator == "blend":
1218			self.stack.extend(value)
1219		else:
1220			self.dict[operator] = value
1221
1222	def arg_number(self, name):
1223		if isinstance(self.stack[0], list):
1224			out = self.arg_blend_number(self.stack)
1225		else:
1226			out = self.pop()
1227		return out
1228
1229	def arg_blend_number(self, name):
1230		out = []
1231		blendArgs = self.pop()
1232		numMasters = len(blendArgs)
1233		out.append(blendArgs)
1234		out.append("blend")
1235		dummy = self.popall()
1236		return blendArgs
1237
1238	def arg_SID(self, name):
1239		return self.strings[self.pop()]
1240	def arg_array(self, name):
1241		return self.popall()
1242	def arg_blendList(self, name):
1243		"""
1244		There may be non-blend args at the top of the stack. We first calculate
1245		where the blend args start in the stack. These are the last
1246		numMasters*numBlends) +1 args.
1247		The blend args starts with numMasters relative coordinate values, the  BlueValues in the list from the default master font. This is followed by
1248		numBlends list of values. Each of  value in one of these lists is the
1249		Variable Font delta for the matching region.
1250
1251		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
1252		the delta values. We then convert the default values, the first item in each entry, to an absolute value.
1253		"""
1254		vsindex = self.dict.get('vsindex', 0)
1255		numMasters = self.parent.getNumRegions(vsindex) + 1 # only a PrivateDict has blended ops.
1256		numBlends = self.pop()
1257		args = self.popall()
1258		numArgs = len(args)
1259		# The spec says that there should be no non-blended Blue Values,.
1260		assert(numArgs == numMasters * numBlends)
1261		value = [None]*numBlends
1262		numDeltas = numMasters-1
1263		i = 0
1264		prevVal = 0
1265		while i < numBlends:
1266			newVal = args[i] + prevVal
1267			prevVal = newVal
1268			masterOffset = numBlends + (i* numDeltas)
1269			blendList = [newVal] + args[masterOffset:masterOffset+numDeltas]
1270			value[i] = blendList
1271			i += 1
1272		return value
1273
1274	def arg_delta(self, name):
1275		valueList = self.popall()
1276		out = []
1277		if valueList and isinstance(valueList[0], list):
1278			# arg_blendList() has already converted these to absolute values.
1279			out = valueList
1280		else:
1281			current = 0
1282			for v in valueList:
1283				current = current + v
1284				out.append(current)
1285		return out
1286
1287
1288def calcSubrBias(subrs):
1289	nSubrs = len(subrs)
1290	if nSubrs < 1240:
1291		bias = 107
1292	elif nSubrs < 33900:
1293		bias = 1131
1294	else:
1295		bias = 32768
1296	return bias
1297