textTools.py 3.4 KB

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