|
"""fontTools.misc.textTools.py -- miscellaneous routines.""" |
|
|
|
|
|
import ast |
|
import string |
|
|
|
|
|
|
|
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) |
|
|