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

1from fontTools.misc import sstruct 

2from fontTools.misc.textTools import bytechr, byteord, strjoin 

3from . import DefaultTable 

4import array 

5from collections.abc import Mapping 

6 

7hdmxHeaderFormat = """ 

8 > # big endian! 

9 version: H 

10 numRecords: H 

11 recordSize: l 

12""" 

13 

14 

15class _GlyphnamedList(Mapping): 

16 def __init__(self, reverseGlyphOrder, data): 

17 self._array = data 

18 self._map = dict(reverseGlyphOrder) 

19 

20 def __getitem__(self, k): 

21 return self._array[self._map[k]] 

22 

23 def __len__(self): 

24 return len(self._map) 

25 

26 def __iter__(self): 

27 return iter(self._map) 

28 

29 def keys(self): 

30 return self._map.keys() 

31 

32 

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" 

48 

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 

65 

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() 

94 

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 

114 

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]