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