aboutsummaryrefslogtreecommitdiffstats
path: root/contrib/python/fonttools/fontTools/ttLib/tables/T_S_I__1.py
blob: a9d04a09b027dcba994d576a33676bc37b18565f (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
""" TSI{0,1,2,3,5} are private tables used by Microsoft Visual TrueType (VTT)
tool to store its hinting source data.

TSI1 contains the text of the glyph programs in the form of low-level assembly
code, as well as the 'extra' programs 'fpgm', 'ppgm' (i.e. 'prep'), and 'cvt'.
"""

from . import DefaultTable
from fontTools.misc.loggingTools import LogMixin
from fontTools.misc.textTools import strjoin, tobytes, tostr


class table_T_S_I__1(LogMixin, DefaultTable.DefaultTable):
    extras = {0xFFFA: "ppgm", 0xFFFB: "cvt", 0xFFFC: "reserved", 0xFFFD: "fpgm"}

    indextable = "TSI0"

    def decompile(self, data, ttFont):
        totalLength = len(data)
        indextable = ttFont[self.indextable]
        for indices, isExtra in zip(
            (indextable.indices, indextable.extra_indices), (False, True)
        ):
            programs = {}
            for i, (glyphID, textLength, textOffset) in enumerate(indices):
                if isExtra:
                    name = self.extras[glyphID]
                else:
                    name = ttFont.getGlyphName(glyphID)
                if textOffset > totalLength:
                    self.log.warning("textOffset > totalLength; %r skipped" % name)
                    continue
                if textLength < 0x8000:
                    # If the length stored in the record is less than 32768, then use
                    # that as the length of the record.
                    pass
                elif textLength == 0x8000:
                    # If the length is 32768, compute the actual length as follows:
                    isLast = i == (len(indices) - 1)
                    if isLast:
                        if isExtra:
                            # For the last "extra" record (the very last record of the
                            # table), the length is the difference between the total
                            # length of the TSI1 table and the textOffset of the final
                            # record.
                            nextTextOffset = totalLength
                        else:
                            # For the last "normal" record (the last record just prior
                            # to the record containing the "magic number"), the length
                            # is the difference between the textOffset of the record
                            # following the "magic number" (0xFFFE) record (i.e. the
                            # first "extra" record), and the textOffset of the last
                            # "normal" record.
                            nextTextOffset = indextable.extra_indices[0][2]
                    else:
                        # For all other records with a length of 0x8000, the length is
                        # the difference between the textOffset of the record in
                        # question and the textOffset of the next record.
                        nextTextOffset = indices[i + 1][2]
                    assert nextTextOffset >= textOffset, "entries not sorted by offset"
                    if nextTextOffset > totalLength:
                        self.log.warning(
                            "nextTextOffset > totalLength; %r truncated" % name
                        )
                        nextTextOffset = totalLength
                    textLength = nextTextOffset - textOffset
                else:
                    from fontTools import ttLib

                    raise ttLib.TTLibError(
                        "%r textLength (%d) must not be > 32768" % (name, textLength)
                    )
                text = data[textOffset : textOffset + textLength]
                assert len(text) == textLength
                text = tostr(text, encoding="utf-8")
                if text:
                    programs[name] = text
            if isExtra:
                self.extraPrograms = programs
            else:
                self.glyphPrograms = programs

    def compile(self, ttFont):
        if not hasattr(self, "glyphPrograms"):
            self.glyphPrograms = {}
            self.extraPrograms = {}
        data = b""
        indextable = ttFont[self.indextable]
        glyphNames = ttFont.getGlyphOrder()

        indices = []
        for i in range(len(glyphNames)):
            if len(data) % 2:
                data = (
                    data + b"\015"
                )  # align on 2-byte boundaries, fill with return chars. Yum.
            name = glyphNames[i]
            if name in self.glyphPrograms:
                text = tobytes(self.glyphPrograms[name], encoding="utf-8")
            else:
                text = b""
            textLength = len(text)
            if textLength >= 0x8000:
                textLength = 0x8000
            indices.append((i, textLength, len(data)))
            data = data + text

        extra_indices = []
        codes = sorted(self.extras.items())
        for i in range(len(codes)):
            if len(data) % 2:
                data = (
                    data + b"\015"
                )  # align on 2-byte boundaries, fill with return chars.
            code, name = codes[i]
            if name in self.extraPrograms:
                text = tobytes(self.extraPrograms[name], encoding="utf-8")
            else:
                text = b""
            textLength = len(text)
            if textLength >= 0x8000:
                textLength = 0x8000
            extra_indices.append((code, textLength, len(data)))
            data = data + text
        indextable.set(indices, extra_indices)
        return data

    def toXML(self, writer, ttFont):
        names = sorted(self.glyphPrograms.keys())
        writer.newline()
        for name in names:
            text = self.glyphPrograms[name]
            if not text:
                continue
            writer.begintag("glyphProgram", name=name)
            writer.newline()
            writer.write_noindent(text.replace("\r", "\n"))
            writer.newline()
            writer.endtag("glyphProgram")
            writer.newline()
            writer.newline()
        extra_names = sorted(self.extraPrograms.keys())
        for name in extra_names:
            text = self.extraPrograms[name]
            if not text:
                continue
            writer.begintag("extraProgram", name=name)
            writer.newline()
            writer.write_noindent(text.replace("\r", "\n"))
            writer.newline()
            writer.endtag("extraProgram")
            writer.newline()
            writer.newline()

    def fromXML(self, name, attrs, content, ttFont):
        if not hasattr(self, "glyphPrograms"):
            self.glyphPrograms = {}
            self.extraPrograms = {}
        lines = strjoin(content).replace("\r", "\n").split("\n")
        text = "\r".join(lines[1:-1])
        if name == "glyphProgram":
            self.glyphPrograms[attrs["name"]] = text
        elif name == "extraProgram":
            self.extraPrograms[attrs["name"]] = text