xref: /aosp_15_r20/external/fonttools/Lib/fontTools/misc/psOperators.py (revision e1fe3e4ad2793916b15cccdc4a7da52a7e1dd0e9)
1_accessstrings = {0: "", 1: "readonly", 2: "executeonly", 3: "noaccess"}
2
3
4class ps_object(object):
5    literal = 1
6    access = 0
7    value = None
8
9    def __init__(self, value):
10        self.value = value
11        self.type = self.__class__.__name__[3:] + "type"
12
13    def __repr__(self):
14        return "<%s %s>" % (self.__class__.__name__[3:], repr(self.value))
15
16
17class ps_operator(ps_object):
18    literal = 0
19
20    def __init__(self, name, function):
21        self.name = name
22        self.function = function
23        self.type = self.__class__.__name__[3:] + "type"
24
25    def __repr__(self):
26        return "<operator %s>" % self.name
27
28
29class ps_procedure(ps_object):
30    literal = 0
31
32    def __repr__(self):
33        return "<procedure>"
34
35    def __str__(self):
36        psstring = "{"
37        for i in range(len(self.value)):
38            if i:
39                psstring = psstring + " " + str(self.value[i])
40            else:
41                psstring = psstring + str(self.value[i])
42        return psstring + "}"
43
44
45class ps_name(ps_object):
46    literal = 0
47
48    def __str__(self):
49        if self.literal:
50            return "/" + self.value
51        else:
52            return self.value
53
54
55class ps_literal(ps_object):
56    def __str__(self):
57        return "/" + self.value
58
59
60class ps_array(ps_object):
61    def __str__(self):
62        psstring = "["
63        for i in range(len(self.value)):
64            item = self.value[i]
65            access = _accessstrings[item.access]
66            if access:
67                access = " " + access
68            if i:
69                psstring = psstring + " " + str(item) + access
70            else:
71                psstring = psstring + str(item) + access
72        return psstring + "]"
73
74    def __repr__(self):
75        return "<array>"
76
77
78_type1_pre_eexec_order = [
79    "FontInfo",
80    "FontName",
81    "Encoding",
82    "PaintType",
83    "FontType",
84    "FontMatrix",
85    "FontBBox",
86    "UniqueID",
87    "Metrics",
88    "StrokeWidth",
89]
90
91_type1_fontinfo_order = [
92    "version",
93    "Notice",
94    "FullName",
95    "FamilyName",
96    "Weight",
97    "ItalicAngle",
98    "isFixedPitch",
99    "UnderlinePosition",
100    "UnderlineThickness",
101]
102
103_type1_post_eexec_order = ["Private", "CharStrings", "FID"]
104
105
106def _type1_item_repr(key, value):
107    psstring = ""
108    access = _accessstrings[value.access]
109    if access:
110        access = access + " "
111    if key == "CharStrings":
112        psstring = psstring + "/%s %s def\n" % (
113            key,
114            _type1_CharString_repr(value.value),
115        )
116    elif key == "Encoding":
117        psstring = psstring + _type1_Encoding_repr(value, access)
118    else:
119        psstring = psstring + "/%s %s %sdef\n" % (str(key), str(value), access)
120    return psstring
121
122
123def _type1_Encoding_repr(encoding, access):
124    encoding = encoding.value
125    psstring = "/Encoding 256 array\n0 1 255 {1 index exch /.notdef put} for\n"
126    for i in range(256):
127        name = encoding[i].value
128        if name != ".notdef":
129            psstring = psstring + "dup %d /%s put\n" % (i, name)
130    return psstring + access + "def\n"
131
132
133def _type1_CharString_repr(charstrings):
134    items = sorted(charstrings.items())
135    return "xxx"
136
137
138class ps_font(ps_object):
139    def __str__(self):
140        psstring = "%d dict dup begin\n" % len(self.value)
141        for key in _type1_pre_eexec_order:
142            try:
143                value = self.value[key]
144            except KeyError:
145                pass
146            else:
147                psstring = psstring + _type1_item_repr(key, value)
148        items = sorted(self.value.items())
149        for key, value in items:
150            if key not in _type1_pre_eexec_order + _type1_post_eexec_order:
151                psstring = psstring + _type1_item_repr(key, value)
152        psstring = psstring + "currentdict end\ncurrentfile eexec\ndup "
153        for key in _type1_post_eexec_order:
154            try:
155                value = self.value[key]
156            except KeyError:
157                pass
158            else:
159                psstring = psstring + _type1_item_repr(key, value)
160        return (
161            psstring
162            + "dup/FontName get exch definefont pop\nmark currentfile closefile\n"
163            + 8 * (64 * "0" + "\n")
164            + "cleartomark"
165            + "\n"
166        )
167
168    def __repr__(self):
169        return "<font>"
170
171
172class ps_file(ps_object):
173    pass
174
175
176class ps_dict(ps_object):
177    def __str__(self):
178        psstring = "%d dict dup begin\n" % len(self.value)
179        items = sorted(self.value.items())
180        for key, value in items:
181            access = _accessstrings[value.access]
182            if access:
183                access = access + " "
184            psstring = psstring + "/%s %s %sdef\n" % (str(key), str(value), access)
185        return psstring + "end "
186
187    def __repr__(self):
188        return "<dict>"
189
190
191class ps_mark(ps_object):
192    def __init__(self):
193        self.value = "mark"
194        self.type = self.__class__.__name__[3:] + "type"
195
196
197class ps_procmark(ps_object):
198    def __init__(self):
199        self.value = "procmark"
200        self.type = self.__class__.__name__[3:] + "type"
201
202
203class ps_null(ps_object):
204    def __init__(self):
205        self.type = self.__class__.__name__[3:] + "type"
206
207
208class ps_boolean(ps_object):
209    def __str__(self):
210        if self.value:
211            return "true"
212        else:
213            return "false"
214
215
216class ps_string(ps_object):
217    def __str__(self):
218        return "(%s)" % repr(self.value)[1:-1]
219
220
221class ps_integer(ps_object):
222    def __str__(self):
223        return repr(self.value)
224
225
226class ps_real(ps_object):
227    def __str__(self):
228        return repr(self.value)
229
230
231class PSOperators(object):
232    def ps_def(self):
233        obj = self.pop()
234        name = self.pop()
235        self.dictstack[-1][name.value] = obj
236
237    def ps_bind(self):
238        proc = self.pop("proceduretype")
239        self.proc_bind(proc)
240        self.push(proc)
241
242    def proc_bind(self, proc):
243        for i in range(len(proc.value)):
244            item = proc.value[i]
245            if item.type == "proceduretype":
246                self.proc_bind(item)
247            else:
248                if not item.literal:
249                    try:
250                        obj = self.resolve_name(item.value)
251                    except:
252                        pass
253                    else:
254                        if obj.type == "operatortype":
255                            proc.value[i] = obj
256
257    def ps_exch(self):
258        if len(self.stack) < 2:
259            raise RuntimeError("stack underflow")
260        obj1 = self.pop()
261        obj2 = self.pop()
262        self.push(obj1)
263        self.push(obj2)
264
265    def ps_dup(self):
266        if not self.stack:
267            raise RuntimeError("stack underflow")
268        self.push(self.stack[-1])
269
270    def ps_exec(self):
271        obj = self.pop()
272        if obj.type == "proceduretype":
273            self.call_procedure(obj)
274        else:
275            self.handle_object(obj)
276
277    def ps_count(self):
278        self.push(ps_integer(len(self.stack)))
279
280    def ps_eq(self):
281        any1 = self.pop()
282        any2 = self.pop()
283        self.push(ps_boolean(any1.value == any2.value))
284
285    def ps_ne(self):
286        any1 = self.pop()
287        any2 = self.pop()
288        self.push(ps_boolean(any1.value != any2.value))
289
290    def ps_cvx(self):
291        obj = self.pop()
292        obj.literal = 0
293        self.push(obj)
294
295    def ps_matrix(self):
296        matrix = [
297            ps_real(1.0),
298            ps_integer(0),
299            ps_integer(0),
300            ps_real(1.0),
301            ps_integer(0),
302            ps_integer(0),
303        ]
304        self.push(ps_array(matrix))
305
306    def ps_string(self):
307        num = self.pop("integertype").value
308        self.push(ps_string("\0" * num))
309
310    def ps_type(self):
311        obj = self.pop()
312        self.push(ps_string(obj.type))
313
314    def ps_store(self):
315        value = self.pop()
316        key = self.pop()
317        name = key.value
318        for i in range(len(self.dictstack) - 1, -1, -1):
319            if name in self.dictstack[i]:
320                self.dictstack[i][name] = value
321                break
322        self.dictstack[-1][name] = value
323
324    def ps_where(self):
325        name = self.pop()
326        # XXX
327        self.push(ps_boolean(0))
328
329    def ps_systemdict(self):
330        self.push(ps_dict(self.dictstack[0]))
331
332    def ps_userdict(self):
333        self.push(ps_dict(self.dictstack[1]))
334
335    def ps_currentdict(self):
336        self.push(ps_dict(self.dictstack[-1]))
337
338    def ps_currentfile(self):
339        self.push(ps_file(self.tokenizer))
340
341    def ps_eexec(self):
342        f = self.pop("filetype").value
343        f.starteexec()
344
345    def ps_closefile(self):
346        f = self.pop("filetype").value
347        f.skipwhite()
348        f.stopeexec()
349
350    def ps_cleartomark(self):
351        obj = self.pop()
352        while obj != self.mark:
353            obj = self.pop()
354
355    def ps_readstring(self, ps_boolean=ps_boolean, len=len):
356        s = self.pop("stringtype")
357        oldstr = s.value
358        f = self.pop("filetype")
359        # pad = file.value.read(1)
360        # for StringIO, this is faster
361        f.value.pos = f.value.pos + 1
362        newstr = f.value.read(len(oldstr))
363        s.value = newstr
364        self.push(s)
365        self.push(ps_boolean(len(oldstr) == len(newstr)))
366
367    def ps_known(self):
368        key = self.pop()
369        d = self.pop("dicttype", "fonttype")
370        self.push(ps_boolean(key.value in d.value))
371
372    def ps_if(self):
373        proc = self.pop("proceduretype")
374        if self.pop("booleantype").value:
375            self.call_procedure(proc)
376
377    def ps_ifelse(self):
378        proc2 = self.pop("proceduretype")
379        proc1 = self.pop("proceduretype")
380        if self.pop("booleantype").value:
381            self.call_procedure(proc1)
382        else:
383            self.call_procedure(proc2)
384
385    def ps_readonly(self):
386        obj = self.pop()
387        if obj.access < 1:
388            obj.access = 1
389        self.push(obj)
390
391    def ps_executeonly(self):
392        obj = self.pop()
393        if obj.access < 2:
394            obj.access = 2
395        self.push(obj)
396
397    def ps_noaccess(self):
398        obj = self.pop()
399        if obj.access < 3:
400            obj.access = 3
401        self.push(obj)
402
403    def ps_not(self):
404        obj = self.pop("booleantype", "integertype")
405        if obj.type == "booleantype":
406            self.push(ps_boolean(not obj.value))
407        else:
408            self.push(ps_integer(~obj.value))
409
410    def ps_print(self):
411        str = self.pop("stringtype")
412        print("PS output --->", str.value)
413
414    def ps_anchorsearch(self):
415        seek = self.pop("stringtype")
416        s = self.pop("stringtype")
417        seeklen = len(seek.value)
418        if s.value[:seeklen] == seek.value:
419            self.push(ps_string(s.value[seeklen:]))
420            self.push(seek)
421            self.push(ps_boolean(1))
422        else:
423            self.push(s)
424            self.push(ps_boolean(0))
425
426    def ps_array(self):
427        num = self.pop("integertype")
428        array = ps_array([None] * num.value)
429        self.push(array)
430
431    def ps_astore(self):
432        array = self.pop("arraytype")
433        for i in range(len(array.value) - 1, -1, -1):
434            array.value[i] = self.pop()
435        self.push(array)
436
437    def ps_load(self):
438        name = self.pop()
439        self.push(self.resolve_name(name.value))
440
441    def ps_put(self):
442        obj1 = self.pop()
443        obj2 = self.pop()
444        obj3 = self.pop("arraytype", "dicttype", "stringtype", "proceduretype")
445        tp = obj3.type
446        if tp == "arraytype" or tp == "proceduretype":
447            obj3.value[obj2.value] = obj1
448        elif tp == "dicttype":
449            obj3.value[obj2.value] = obj1
450        elif tp == "stringtype":
451            index = obj2.value
452            obj3.value = obj3.value[:index] + chr(obj1.value) + obj3.value[index + 1 :]
453
454    def ps_get(self):
455        obj1 = self.pop()
456        if obj1.value == "Encoding":
457            pass
458        obj2 = self.pop(
459            "arraytype", "dicttype", "stringtype", "proceduretype", "fonttype"
460        )
461        tp = obj2.type
462        if tp in ("arraytype", "proceduretype"):
463            self.push(obj2.value[obj1.value])
464        elif tp in ("dicttype", "fonttype"):
465            self.push(obj2.value[obj1.value])
466        elif tp == "stringtype":
467            self.push(ps_integer(ord(obj2.value[obj1.value])))
468        else:
469            assert False, "shouldn't get here"
470
471    def ps_getinterval(self):
472        obj1 = self.pop("integertype")
473        obj2 = self.pop("integertype")
474        obj3 = self.pop("arraytype", "stringtype")
475        tp = obj3.type
476        if tp == "arraytype":
477            self.push(ps_array(obj3.value[obj2.value : obj2.value + obj1.value]))
478        elif tp == "stringtype":
479            self.push(ps_string(obj3.value[obj2.value : obj2.value + obj1.value]))
480
481    def ps_putinterval(self):
482        obj1 = self.pop("arraytype", "stringtype")
483        obj2 = self.pop("integertype")
484        obj3 = self.pop("arraytype", "stringtype")
485        tp = obj3.type
486        if tp == "arraytype":
487            obj3.value[obj2.value : obj2.value + len(obj1.value)] = obj1.value
488        elif tp == "stringtype":
489            newstr = obj3.value[: obj2.value]
490            newstr = newstr + obj1.value
491            newstr = newstr + obj3.value[obj2.value + len(obj1.value) :]
492            obj3.value = newstr
493
494    def ps_cvn(self):
495        self.push(ps_name(self.pop("stringtype").value))
496
497    def ps_index(self):
498        n = self.pop("integertype").value
499        if n < 0:
500            raise RuntimeError("index may not be negative")
501        self.push(self.stack[-1 - n])
502
503    def ps_for(self):
504        proc = self.pop("proceduretype")
505        limit = self.pop("integertype", "realtype").value
506        increment = self.pop("integertype", "realtype").value
507        i = self.pop("integertype", "realtype").value
508        while 1:
509            if increment > 0:
510                if i > limit:
511                    break
512            else:
513                if i < limit:
514                    break
515            if type(i) == type(0.0):
516                self.push(ps_real(i))
517            else:
518                self.push(ps_integer(i))
519            self.call_procedure(proc)
520            i = i + increment
521
522    def ps_forall(self):
523        proc = self.pop("proceduretype")
524        obj = self.pop("arraytype", "stringtype", "dicttype")
525        tp = obj.type
526        if tp == "arraytype":
527            for item in obj.value:
528                self.push(item)
529                self.call_procedure(proc)
530        elif tp == "stringtype":
531            for item in obj.value:
532                self.push(ps_integer(ord(item)))
533                self.call_procedure(proc)
534        elif tp == "dicttype":
535            for key, value in obj.value.items():
536                self.push(ps_name(key))
537                self.push(value)
538                self.call_procedure(proc)
539
540    def ps_definefont(self):
541        font = self.pop("dicttype")
542        name = self.pop()
543        font = ps_font(font.value)
544        self.dictstack[0]["FontDirectory"].value[name.value] = font
545        self.push(font)
546
547    def ps_findfont(self):
548        name = self.pop()
549        font = self.dictstack[0]["FontDirectory"].value[name.value]
550        self.push(font)
551
552    def ps_pop(self):
553        self.pop()
554
555    def ps_dict(self):
556        self.pop("integertype")
557        self.push(ps_dict({}))
558
559    def ps_begin(self):
560        self.dictstack.append(self.pop("dicttype").value)
561
562    def ps_end(self):
563        if len(self.dictstack) > 2:
564            del self.dictstack[-1]
565        else:
566            raise RuntimeError("dictstack underflow")
567
568
569notdef = ".notdef"
570from fontTools.encodings.StandardEncoding import StandardEncoding
571
572ps_StandardEncoding = list(map(ps_name, StandardEncoding))
573