xref: /aosp_15_r20/external/fonttools/Lib/fontTools/misc/textTools.py (revision e1fe3e4ad2793916b15cccdc4a7da52a7e1dd0e9)
1"""fontTools.misc.textTools.py -- miscellaneous routines."""
2
3import ast
4import string
5
6
7# alias kept for backward compatibility
8safeEval = ast.literal_eval
9
10
11class Tag(str):
12    @staticmethod
13    def transcode(blob):
14        if isinstance(blob, bytes):
15            blob = blob.decode("latin-1")
16        return blob
17
18    def __new__(self, content):
19        return str.__new__(self, self.transcode(content))
20
21    def __ne__(self, other):
22        return not self.__eq__(other)
23
24    def __eq__(self, other):
25        return str.__eq__(self, self.transcode(other))
26
27    def __hash__(self):
28        return str.__hash__(self)
29
30    def tobytes(self):
31        return self.encode("latin-1")
32
33
34def readHex(content):
35    """Convert a list of hex strings to binary data."""
36    return deHexStr(strjoin(chunk for chunk in content if isinstance(chunk, str)))
37
38
39def deHexStr(hexdata):
40    """Convert a hex string to binary data."""
41    hexdata = strjoin(hexdata.split())
42    if len(hexdata) % 2:
43        hexdata = hexdata + "0"
44    data = []
45    for i in range(0, len(hexdata), 2):
46        data.append(bytechr(int(hexdata[i : i + 2], 16)))
47    return bytesjoin(data)
48
49
50def hexStr(data):
51    """Convert binary data to a hex string."""
52    h = string.hexdigits
53    r = ""
54    for c in data:
55        i = byteord(c)
56        r = r + h[(i >> 4) & 0xF] + h[i & 0xF]
57    return r
58
59
60def num2binary(l, bits=32):
61    items = []
62    binary = ""
63    for i in range(bits):
64        if l & 0x1:
65            binary = "1" + binary
66        else:
67            binary = "0" + binary
68        l = l >> 1
69        if not ((i + 1) % 8):
70            items.append(binary)
71            binary = ""
72    if binary:
73        items.append(binary)
74    items.reverse()
75    assert l in (0, -1), "number doesn't fit in number of bits"
76    return " ".join(items)
77
78
79def binary2num(bin):
80    bin = strjoin(bin.split())
81    l = 0
82    for digit in bin:
83        l = l << 1
84        if digit != "0":
85            l = l | 0x1
86    return l
87
88
89def caselessSort(alist):
90    """Return a sorted copy of a list. If there are only strings
91    in the list, it will not consider case.
92    """
93
94    try:
95        return sorted(alist, key=lambda a: (a.lower(), a))
96    except TypeError:
97        return sorted(alist)
98
99
100def pad(data, size):
101    r"""Pad byte string 'data' with null bytes until its length is a
102    multiple of 'size'.
103
104    >>> len(pad(b'abcd', 4))
105    4
106    >>> len(pad(b'abcde', 2))
107    6
108    >>> len(pad(b'abcde', 4))
109    8
110    >>> pad(b'abcdef', 4) == b'abcdef\x00\x00'
111    True
112    """
113    data = tobytes(data)
114    if size > 1:
115        remainder = len(data) % size
116        if remainder:
117            data += b"\0" * (size - remainder)
118    return data
119
120
121def tostr(s, encoding="ascii", errors="strict"):
122    if not isinstance(s, str):
123        return s.decode(encoding, errors)
124    else:
125        return s
126
127
128def tobytes(s, encoding="ascii", errors="strict"):
129    if isinstance(s, str):
130        return s.encode(encoding, errors)
131    else:
132        return bytes(s)
133
134
135def bytechr(n):
136    return bytes([n])
137
138
139def byteord(c):
140    return c if isinstance(c, int) else ord(c)
141
142
143def strjoin(iterable, joiner=""):
144    return tostr(joiner).join(iterable)
145
146
147def bytesjoin(iterable, joiner=b""):
148    return tobytes(joiner).join(tobytes(item) for item in iterable)
149
150
151if __name__ == "__main__":
152    import doctest, sys
153
154    sys.exit(doctest.testmod().failed)
155