1from fontTools.misc.py23 import bytechr, byteord, strjoin 2from fontTools.misc import sstruct 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 14class _GlyphnamedList(Mapping): 15 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 32class table__h_d_m_x(DefaultTable.DefaultTable): 33 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(ttFont.getReverseGlyphMap(), array.array("B", data[2:2+numGlyphs])) 43 self.hdmx[ppem] = widths 44 data = data[self.recordSize:] 45 assert len(data) == 0, "too much hdmx data" 46 47 def compile(self, ttFont): 48 self.version = 0 49 numGlyphs = ttFont['maxp'].numGlyphs 50 glyphOrder = ttFont.getGlyphOrder() 51 self.recordSize = 4 * ((2 + numGlyphs + 3) // 4) 52 pad = (self.recordSize - 2 - numGlyphs) * b"\0" 53 self.numRecords = len(self.hdmx) 54 data = sstruct.pack(hdmxHeaderFormat, self) 55 items = sorted(self.hdmx.items()) 56 for ppem, widths in items: 57 data = data + bytechr(ppem) + bytechr(max(widths.values())) 58 for glyphID in range(len(glyphOrder)): 59 width = widths[glyphOrder[glyphID]] 60 data = data + bytechr(width) 61 data = data + pad 62 return data 63 64 def toXML(self, writer, ttFont): 65 writer.begintag("hdmxData") 66 writer.newline() 67 ppems = sorted(self.hdmx.keys()) 68 records = [] 69 format = "" 70 for ppem in ppems: 71 widths = self.hdmx[ppem] 72 records.append(widths) 73 format = format + "%4d" 74 glyphNames = ttFont.getGlyphOrder()[:] 75 glyphNames.sort() 76 maxNameLen = max(map(len, glyphNames)) 77 format = "%" + repr(maxNameLen) + 's:' + format + ' ;' 78 writer.write(format % (("ppem",) + tuple(ppems))) 79 writer.newline() 80 writer.newline() 81 for glyphName in glyphNames: 82 row = [] 83 for ppem in ppems: 84 widths = self.hdmx[ppem] 85 row.append(widths[glyphName]) 86 if ";" in glyphName: 87 glyphName = "\\x3b".join(glyphName.split(";")) 88 writer.write(format % ((glyphName,) + tuple(row))) 89 writer.newline() 90 writer.endtag("hdmxData") 91 writer.newline() 92 93 def fromXML(self, name, attrs, content, ttFont): 94 if name != "hdmxData": 95 return 96 content = strjoin(content) 97 lines = content.split(";") 98 topRow = lines[0].split() 99 assert topRow[0] == "ppem:", "illegal hdmx format" 100 ppems = list(map(int, topRow[1:])) 101 self.hdmx = hdmx = {} 102 for ppem in ppems: 103 hdmx[ppem] = {} 104 lines = (line.split() for line in lines[1:]) 105 for line in lines: 106 if not line: 107 continue 108 assert line[0][-1] == ":", "illegal hdmx format" 109 glyphName = line[0][:-1] 110 if "\\" in glyphName: 111 from fontTools.misc.textTools import safeEval 112 glyphName = safeEval('"""' + glyphName + '"""') 113 line = list(map(int, line[1:])) 114 assert len(line) == len(ppems), "illegal hdmx format" 115 for i in range(len(ppems)): 116 hdmx[ppems[i]][glyphName] = line[i] 117