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