| """fontTools.misc.textTools.py -- miscellaneous routines.""" |
| |
| import ast |
| import string |
| |
| |
| # alias kept for backward compatibility |
| safeEval = ast.literal_eval |
| |
| |
| class Tag(str): |
| @staticmethod |
| def transcode(blob): |
| if isinstance(blob, bytes): |
| blob = blob.decode("latin-1") |
| return blob |
| |
| def __new__(self, content): |
| return str.__new__(self, self.transcode(content)) |
| |
| def __ne__(self, other): |
| return not self.__eq__(other) |
| |
| def __eq__(self, other): |
| return str.__eq__(self, self.transcode(other)) |
| |
| def __hash__(self): |
| return str.__hash__(self) |
| |
| def tobytes(self): |
| return self.encode("latin-1") |
| |
| |
| def readHex(content): |
| """Convert a list of hex strings to binary data.""" |
| return deHexStr(strjoin(chunk for chunk in content if isinstance(chunk, str))) |
| |
| |
| def deHexStr(hexdata): |
| """Convert a hex string to binary data.""" |
| hexdata = strjoin(hexdata.split()) |
| if len(hexdata) % 2: |
| hexdata = hexdata + "0" |
| data = [] |
| for i in range(0, len(hexdata), 2): |
| data.append(bytechr(int(hexdata[i : i + 2], 16))) |
| return bytesjoin(data) |
| |
| |
| def hexStr(data): |
| """Convert binary data to a hex string.""" |
| h = string.hexdigits |
| r = "" |
| for c in data: |
| i = byteord(c) |
| r = r + h[(i >> 4) & 0xF] + h[i & 0xF] |
| return r |
| |
| |
| def num2binary(l, bits=32): |
| items = [] |
| binary = "" |
| for i in range(bits): |
| if l & 0x1: |
| binary = "1" + binary |
| else: |
| binary = "0" + binary |
| l = l >> 1 |
| if not ((i + 1) % 8): |
| items.append(binary) |
| binary = "" |
| if binary: |
| items.append(binary) |
| items.reverse() |
| assert l in (0, -1), "number doesn't fit in number of bits" |
| return " ".join(items) |
| |
| |
| def binary2num(bin): |
| bin = strjoin(bin.split()) |
| l = 0 |
| for digit in bin: |
| l = l << 1 |
| if digit != "0": |
| l = l | 0x1 |
| return l |
| |
| |
| def caselessSort(alist): |
| """Return a sorted copy of a list. If there are only strings |
| in the list, it will not consider case. |
| """ |
| |
| try: |
| return sorted(alist, key=lambda a: (a.lower(), a)) |
| except TypeError: |
| return sorted(alist) |
| |
| |
| def pad(data, size): |
| r"""Pad byte string 'data' with null bytes until its length is a |
| multiple of 'size'. |
| |
| >>> len(pad(b'abcd', 4)) |
| 4 |
| >>> len(pad(b'abcde', 2)) |
| 6 |
| >>> len(pad(b'abcde', 4)) |
| 8 |
| >>> pad(b'abcdef', 4) == b'abcdef\x00\x00' |
| True |
| """ |
| data = tobytes(data) |
| if size > 1: |
| remainder = len(data) % size |
| if remainder: |
| data += b"\0" * (size - remainder) |
| return data |
| |
| |
| def tostr(s, encoding="ascii", errors="strict"): |
| if not isinstance(s, str): |
| return s.decode(encoding, errors) |
| else: |
| return s |
| |
| |
| def tobytes(s, encoding="ascii", errors="strict"): |
| if isinstance(s, str): |
| return s.encode(encoding, errors) |
| else: |
| return bytes(s) |
| |
| |
| def bytechr(n): |
| return bytes([n]) |
| |
| |
| def byteord(c): |
| return c if isinstance(c, int) else ord(c) |
| |
| |
| def strjoin(iterable, joiner=""): |
| return tostr(joiner).join(iterable) |
| |
| |
| def bytesjoin(iterable, joiner=b""): |
| return tobytes(joiner).join(tobytes(item) for item in iterable) |
| |
| |
| if __name__ == "__main__": |
| import doctest, sys |
| |
| sys.exit(doctest.testmod().failed) |