_h_d_m_x.py 3.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119
  1. from fontTools.misc import sstruct
  2. from fontTools.misc.textTools import bytechr, byteord, strjoin
  3. from . import DefaultTable
  4. import array
  5. from collections.abc import Mapping
  6. hdmxHeaderFormat = """
  7. > # big endian!
  8. version: H
  9. numRecords: H
  10. recordSize: l
  11. """
  12. class _GlyphnamedList(Mapping):
  13. def __init__(self, reverseGlyphOrder, data):
  14. self._array = data
  15. self._map = dict(reverseGlyphOrder)
  16. def __getitem__(self, k):
  17. return self._array[self._map[k]]
  18. def __len__(self):
  19. return len(self._map)
  20. def __iter__(self):
  21. return iter(self._map)
  22. def keys(self):
  23. return self._map.keys()
  24. class table__h_d_m_x(DefaultTable.DefaultTable):
  25. def decompile(self, data, ttFont):
  26. numGlyphs = ttFont["maxp"].numGlyphs
  27. glyphOrder = ttFont.getGlyphOrder()
  28. dummy, data = sstruct.unpack2(hdmxHeaderFormat, data, self)
  29. self.hdmx = {}
  30. for i in range(self.numRecords):
  31. ppem = byteord(data[0])
  32. maxSize = byteord(data[1])
  33. widths = _GlyphnamedList(
  34. ttFont.getReverseGlyphMap(), array.array("B", data[2 : 2 + numGlyphs])
  35. )
  36. self.hdmx[ppem] = widths
  37. data = data[self.recordSize :]
  38. assert len(data) == 0, "too much hdmx data"
  39. def compile(self, ttFont):
  40. self.version = 0
  41. numGlyphs = ttFont["maxp"].numGlyphs
  42. glyphOrder = ttFont.getGlyphOrder()
  43. self.recordSize = 4 * ((2 + numGlyphs + 3) // 4)
  44. pad = (self.recordSize - 2 - numGlyphs) * b"\0"
  45. self.numRecords = len(self.hdmx)
  46. data = sstruct.pack(hdmxHeaderFormat, self)
  47. items = sorted(self.hdmx.items())
  48. for ppem, widths in items:
  49. data = data + bytechr(ppem) + bytechr(max(widths.values()))
  50. for glyphID in range(len(glyphOrder)):
  51. width = widths[glyphOrder[glyphID]]
  52. data = data + bytechr(width)
  53. data = data + pad
  54. return data
  55. def toXML(self, writer, ttFont):
  56. writer.begintag("hdmxData")
  57. writer.newline()
  58. ppems = sorted(self.hdmx.keys())
  59. records = []
  60. format = ""
  61. for ppem in ppems:
  62. widths = self.hdmx[ppem]
  63. records.append(widths)
  64. format = format + "%4d"
  65. glyphNames = ttFont.getGlyphOrder()[:]
  66. glyphNames.sort()
  67. maxNameLen = max(map(len, glyphNames))
  68. format = "%" + repr(maxNameLen) + "s:" + format + " ;"
  69. writer.write(format % (("ppem",) + tuple(ppems)))
  70. writer.newline()
  71. writer.newline()
  72. for glyphName in glyphNames:
  73. row = []
  74. for ppem in ppems:
  75. widths = self.hdmx[ppem]
  76. row.append(widths[glyphName])
  77. if ";" in glyphName:
  78. glyphName = "\\x3b".join(glyphName.split(";"))
  79. writer.write(format % ((glyphName,) + tuple(row)))
  80. writer.newline()
  81. writer.endtag("hdmxData")
  82. writer.newline()
  83. def fromXML(self, name, attrs, content, ttFont):
  84. if name != "hdmxData":
  85. return
  86. content = strjoin(content)
  87. lines = content.split(";")
  88. topRow = lines[0].split()
  89. assert topRow[0] == "ppem:", "illegal hdmx format"
  90. ppems = list(map(int, topRow[1:]))
  91. self.hdmx = hdmx = {}
  92. for ppem in ppems:
  93. hdmx[ppem] = {}
  94. lines = (line.split() for line in lines[1:])
  95. for line in lines:
  96. if not line:
  97. continue
  98. assert line[0][-1] == ":", "illegal hdmx format"
  99. glyphName = line[0][:-1]
  100. if "\\" in glyphName:
  101. from fontTools.misc.textTools import safeEval
  102. glyphName = safeEval('"""' + glyphName + '"""')
  103. line = list(map(int, line[1:]))
  104. assert len(line) == len(ppems), "illegal hdmx format"
  105. for i in range(len(ppems)):
  106. hdmx[ppems[i]][glyphName] = line[i]