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