# Source code for fontTools.misc.psOperators

    _accessstrings = {0: "", 1: "readonly", 2: "executeonly", 3: "noaccess"}

    class ps_object(object):
        literal = 1
        access = 0
        value = None

        def __init__(self, value):
            self.value = value
            self.type = self.__class__.__name__[3:] + "type"

        def __repr__(self):
            return "<%s %s>" % (self.__class__.__name__[3:], repr(self.value))

    class ps_operator(ps_object):
        literal = 0

        def __init__(self, name, function):
            self.name = name
            self.function = function
            self.type = self.__class__.__name__[3:] + "type"

        def __repr__(self):
            return "<operator %s>" % self.name

    class ps_procedure(ps_object):
        literal = 0

        def __repr__(self):
            return "<procedure>"

        def __str__(self):
            psstring = "{"
            for i in range(len(self.value)):
                if i:
                    psstring = psstring + " " + str(self.value[i])
                else:
                    psstring = psstring + str(self.value[i])
            return psstring + "}"

    class ps_name(ps_object):
        literal = 0

        def __str__(self):
            if self.literal:
                return "/" + self.value
            else:
                return self.value

    class ps_literal(ps_object):
        def __str__(self):
            return "/" + self.value

    class ps_array(ps_object):
        def __str__(self):
            psstring = "["
            for i in range(len(self.value)):
                item = self.value[i]
                access = _accessstrings[item.access]
                if access:
                    access = " " + access
                if i:
                    psstring = psstring + " " + str(item) + access
                else:
                    psstring = psstring + str(item) + access
            return psstring + "]"

        def __repr__(self):
            return "<array>"

    _type1_pre_eexec_order = [
        "FontInfo",
        "FontName",
        "Encoding",
        "PaintType",
        "FontType",
        "FontMatrix",
        "FontBBox",
        "UniqueID",
        "Metrics",
        "StrokeWidth",
    ]

    _type1_fontinfo_order = [
        "version",
        "Notice",
        "FullName",
        "FamilyName",
        "Weight",
        "ItalicAngle",
        "isFixedPitch",
        "UnderlinePosition",
        "UnderlineThickness",
    ]

    _type1_post_eexec_order = ["Private", "CharStrings", "FID"]

    def _type1_item_repr(key, value):
        psstring = ""
        access = _accessstrings[value.access]
        if access:
            access = access + " "
        if key == "CharStrings":
            psstring = psstring + "/%s %s def\n" % (
                key,
                _type1_CharString_repr(value.value),
            )
        elif key == "Encoding":
            psstring = psstring + _type1_Encoding_repr(value, access)
        else:
            psstring = psstring + "/%s %s %sdef\n" % (str(key), str(value), access)
        return psstring

    def _type1_Encoding_repr(encoding, access):
        encoding = encoding.value
        psstring = "/Encoding 256 array\n0 1 255 {1 index exch /.notdef put} for\n"
        for i in range(256):
            name = encoding[i].value
            if name != ".notdef":
                psstring = psstring + "dup %d /%s put\n" % (i, name)
        return psstring + access + "def\n"

    def _type1_CharString_repr(charstrings):
        items = sorted(charstrings.items())
        return "xxx"

    class ps_font(ps_object):
        def __str__(self):
            psstring = "%d dict dup begin\n" % len(self.value)
            for key in _type1_pre_eexec_order:
                try:
                    value = self.value[key]
                except KeyError:
                    pass
                else:
                    psstring = psstring + _type1_item_repr(key, value)
            items = sorted(self.value.items())
            for key, value in items:
                if key not in _type1_pre_eexec_order + _type1_post_eexec_order:
                    psstring = psstring + _type1_item_repr(key, value)
            psstring = psstring + "currentdict end\ncurrentfile eexec\ndup "
            for key in _type1_post_eexec_order:
                try:
                    value = self.value[key]
                except KeyError:
                    pass
                else:
                    psstring = psstring + _type1_item_repr(key, value)
            return (
                psstring
                + "dup/FontName get exch definefont pop\nmark currentfile closefile\n"
                + 8 * (64 * "0" + "\n")
                + "cleartomark"
                + "\n"
            )

        def __repr__(self):
            return "<font>"

    class ps_file(ps_object):
        pass

    class ps_dict(ps_object):
        def __str__(self):
            psstring = "%d dict dup begin\n" % len(self.value)
            items = sorted(self.value.items())
            for key, value in items:
                access = _accessstrings[value.access]
                if access:
                    access = access + " "
                psstring = psstring + "/%s %s %sdef\n" % (str(key), str(value), access)
            return psstring + "end "

        def __repr__(self):
            return "<dict>"

    class ps_mark(ps_object):
        def __init__(self):
            self.value = "mark"
            self.type = self.__class__.__name__[3:] + "type"

    class ps_procmark(ps_object):
        def __init__(self):
            self.value = "procmark"
            self.type = self.__class__.__name__[3:] + "type"

    class ps_null(ps_object):
        def __init__(self):
            self.type = self.__class__.__name__[3:] + "type"

    class ps_boolean(ps_object):
        def __str__(self):
            if self.value:
                return "true"
            else:
                return "false"

    class ps_string(ps_object):
        def __str__(self):
            return "(%s)" % repr(self.value)[1:-1]

    class ps_integer(ps_object):
        def __str__(self):
            return repr(self.value)

    class ps_real(ps_object):
        def __str__(self):
            return repr(self.value)

    class PSOperators(object):

        def ps_def(self):
            obj = self.pop()
            name = self.pop()
            self.dictstack[-1][name.value] = obj

        def ps_bind(self):
            proc = self.pop("proceduretype")
            self.proc_bind(proc)
            self.push(proc)

        def proc_bind(self, proc):
            for i in range(len(proc.value)):
                item = proc.value[i]
                if item.type == "proceduretype":
                    self.proc_bind(item)
                else:
                    if not item.literal:
                        try:
                            obj = self.resolve_name(item.value)
                        except:
                            pass
                        else:
                            if obj.type == "operatortype":
                                proc.value[i] = obj

        def ps_exch(self):
            if len(self.stack) < 2:
                raise RuntimeError("stack underflow")
            obj1 = self.pop()
            obj2 = self.pop()
            self.push(obj1)
            self.push(obj2)

        def ps_dup(self):
            if not self.stack:
                raise RuntimeError("stack underflow")
            self.push(self.stack[-1])

        def ps_exec(self):
            obj = self.pop()
            if obj.type == "proceduretype":
                self.call_procedure(obj)
            else:
                self.handle_object(obj)

        def ps_count(self):
            self.push(ps_integer(len(self.stack)))

        def ps_eq(self):
            any1 = self.pop()
            any2 = self.pop()
            self.push(ps_boolean(any1.value == any2.value))

        def ps_ne(self):
            any1 = self.pop()
            any2 = self.pop()
            self.push(ps_boolean(any1.value != any2.value))

        def ps_cvx(self):
            obj = self.pop()
            obj.literal = 0
            self.push(obj)

        def ps_matrix(self):
            matrix = [
                ps_real(1.0),
                ps_integer(0),
                ps_integer(0),
                ps_real(1.0),
                ps_integer(0),
                ps_integer(0),
            ]
            self.push(ps_array(matrix))

        def ps_string(self):
            num = self.pop("integertype").value
            self.push(ps_string("\0" * num))

        def ps_type(self):
            obj = self.pop()
            self.push(ps_string(obj.type))

        def ps_store(self):
            value = self.pop()
            key = self.pop()
            name = key.value
            for i in range(len(self.dictstack) - 1, -1, -1):
                if name in self.dictstack[i]:
                    self.dictstack[i][name] = value
                    break
            self.dictstack[-1][name] = value

        def ps_where(self):
            name = self.pop()
            # XXX
            self.push(ps_boolean(0))

        def ps_systemdict(self):
            self.push(ps_dict(self.dictstack[0]))

        def ps_userdict(self):
            self.push(ps_dict(self.dictstack[1]))

        def ps_currentdict(self):
            self.push(ps_dict(self.dictstack[-1]))

        def ps_currentfile(self):
            self.push(ps_file(self.tokenizer))

        def ps_eexec(self):
            f = self.pop("filetype").value
            f.starteexec()

        def ps_closefile(self):
            f = self.pop("filetype").value
            f.skipwhite()
            f.stopeexec()

        def ps_cleartomark(self):
            obj = self.pop()
            while obj != self.mark:
                obj = self.pop()

        def ps_readstring(self, ps_boolean=ps_boolean, len=len):
            s = self.pop("stringtype")
            oldstr = s.value
            f = self.pop("filetype")
            # pad = file.value.read(1)
            # for StringIO, this is faster
            f.value.pos = f.value.pos + 1
            newstr = f.value.read(len(oldstr))
            s.value = newstr
            self.push(s)
            self.push(ps_boolean(len(oldstr) == len(newstr)))

        def ps_known(self):
            key = self.pop()
            d = self.pop("dicttype", "fonttype")
            self.push(ps_boolean(key.value in d.value))

        def ps_if(self):
            proc = self.pop("proceduretype")
            if self.pop("booleantype").value:
                self.call_procedure(proc)

        def ps_ifelse(self):
            proc2 = self.pop("proceduretype")
            proc1 = self.pop("proceduretype")
            if self.pop("booleantype").value:
                self.call_procedure(proc1)
            else:
                self.call_procedure(proc2)

        def ps_readonly(self):
            obj = self.pop()
            if obj.access < 1:
                obj.access = 1
            self.push(obj)

        def ps_executeonly(self):
            obj = self.pop()
            if obj.access < 2:
                obj.access = 2
            self.push(obj)

        def ps_noaccess(self):
            obj = self.pop()
            if obj.access < 3:
                obj.access = 3
            self.push(obj)

        def ps_not(self):
            obj = self.pop("booleantype", "integertype")
            if obj.type == "booleantype":
                self.push(ps_boolean(not obj.value))
            else:
                self.push(ps_integer(~obj.value))

        def ps_print(self):
            str = self.pop("stringtype")
            print("PS output --->", str.value)

        def ps_anchorsearch(self):
            seek = self.pop("stringtype")
            s = self.pop("stringtype")
            seeklen = len(seek.value)
            if s.value[:seeklen] == seek.value:
                self.push(ps_string(s.value[seeklen:]))
                self.push(seek)
                self.push(ps_boolean(1))
            else:
                self.push(s)
                self.push(ps_boolean(0))

        def ps_array(self):
            num = self.pop("integertype")
            array = ps_array([None] * num.value)
            self.push(array)

        def ps_astore(self):
            array = self.pop("arraytype")
            for i in range(len(array.value) - 1, -1, -1):
                array.value[i] = self.pop()
            self.push(array)

        def ps_load(self):
            name = self.pop()
            self.push(self.resolve_name(name.value))

        def ps_put(self):
            obj1 = self.pop()
            obj2 = self.pop()
            obj3 = self.pop("arraytype", "dicttype", "stringtype", "proceduretype")
            tp = obj3.type
            if tp == "arraytype" or tp == "proceduretype":
                obj3.value[obj2.value] = obj1
            elif tp == "dicttype":
                obj3.value[obj2.value] = obj1
            elif tp == "stringtype":
                index = obj2.value
                obj3.value = obj3.value[:index] + chr(obj1.value) + obj3.value[index + 1 :]

        def ps_get(self):
            obj1 = self.pop()
            if obj1.value == "Encoding":
                pass
            obj2 = self.pop(
                "arraytype", "dicttype", "stringtype", "proceduretype", "fonttype"
            )
            tp = obj2.type
            if tp in ("arraytype", "proceduretype"):
                self.push(obj2.value[obj1.value])
            elif tp in ("dicttype", "fonttype"):
                self.push(obj2.value[obj1.value])
            elif tp == "stringtype":
                self.push(ps_integer(ord(obj2.value[obj1.value])))
            else:
                assert False, "shouldn't get here"

        def ps_getinterval(self):
            obj1 = self.pop("integertype")
            obj2 = self.pop("integertype")
            obj3 = self.pop("arraytype", "stringtype")
            tp = obj3.type
            if tp == "arraytype":
                self.push(ps_array(obj3.value[obj2.value : obj2.value + obj1.value]))
            elif tp == "stringtype":
                self.push(ps_string(obj3.value[obj2.value : obj2.value + obj1.value]))

        def ps_putinterval(self):
            obj1 = self.pop("arraytype", "stringtype")
            obj2 = self.pop("integertype")
            obj3 = self.pop("arraytype", "stringtype")
            tp = obj3.type
            if tp == "arraytype":
                obj3.value[obj2.value : obj2.value + len(obj1.value)] = obj1.value
            elif tp == "stringtype":
                newstr = obj3.value[: obj2.value]
                newstr = newstr + obj1.value
                newstr = newstr + obj3.value[obj2.value + len(obj1.value) :]
                obj3.value = newstr

        def ps_cvn(self):
            self.push(ps_name(self.pop("stringtype").value))

        def ps_index(self):
            n = self.pop("integertype").value
            if n < 0:
                raise RuntimeError("index may not be negative")
            self.push(self.stack[-1 - n])

        def ps_for(self):
            proc = self.pop("proceduretype")
            limit = self.pop("integertype", "realtype").value
            increment = self.pop("integertype", "realtype").value
            i = self.pop("integertype", "realtype").value
            while 1:
                if increment > 0:
                    if i > limit:
                        break
                else:
                    if i < limit:
                        break
                if type(i) == type(0.0):
                    self.push(ps_real(i))
                else:
                    self.push(ps_integer(i))
                self.call_procedure(proc)
                i = i + increment

        def ps_forall(self):
            proc = self.pop("proceduretype")
            obj = self.pop("arraytype", "stringtype", "dicttype")
            tp = obj.type
            if tp == "arraytype":
                for item in obj.value:
                    self.push(item)
                    self.call_procedure(proc)
            elif tp == "stringtype":
                for item in obj.value:
                    self.push(ps_integer(ord(item)))
                    self.call_procedure(proc)
            elif tp == "dicttype":
                for key, value in obj.value.items():
                    self.push(ps_name(key))
                    self.push(value)
                    self.call_procedure(proc)

        def ps_definefont(self):
            font = self.pop("dicttype")
            name = self.pop()
            font = ps_font(font.value)
            self.dictstack[0]["FontDirectory"].value[name.value] = font
            self.push(font)

        def ps_findfont(self):
            name = self.pop()
            font = self.dictstack[0]["FontDirectory"].value[name.value]
            self.push(font)

        def ps_pop(self):
            self.pop()

        def ps_dict(self):
            self.pop("integertype")
            self.push(ps_dict({}))

        def ps_begin(self):
            self.dictstack.append(self.pop("dicttype").value)

        def ps_end(self):
            if len(self.dictstack) > 2:
                del self.dictstack[-1]
            else:
                raise RuntimeError("dictstack underflow")

    notdef = ".notdef"
    from fontTools.encodings.StandardEncoding import StandardEncoding

    ps_StandardEncoding = list(map(ps_name, StandardEncoding))