1from fontTools.misc import sstruct 2from fontTools.misc.textTools import bytesjoin, strjoin, readHex 3from fontTools.ttLib import TTLibError 4from . import DefaultTable 5 6# Apple's documentation of 'meta': 7# https://developer.apple.com/fonts/TrueType-Reference-Manual/RM06/Chap6meta.html 8 9META_HEADER_FORMAT = """ 10 > # big endian 11 version: L 12 flags: L 13 dataOffset: L 14 numDataMaps: L 15""" 16 17 18DATA_MAP_FORMAT = """ 19 > # big endian 20 tag: 4s 21 dataOffset: L 22 dataLength: L 23""" 24 25 26class table__m_e_t_a(DefaultTable.DefaultTable): 27 def __init__(self, tag=None): 28 DefaultTable.DefaultTable.__init__(self, tag) 29 self.data = {} 30 31 def decompile(self, data, ttFont): 32 headerSize = sstruct.calcsize(META_HEADER_FORMAT) 33 header = sstruct.unpack(META_HEADER_FORMAT, data[0:headerSize]) 34 if header["version"] != 1: 35 raise TTLibError("unsupported 'meta' version %d" % header["version"]) 36 dataMapSize = sstruct.calcsize(DATA_MAP_FORMAT) 37 for i in range(header["numDataMaps"]): 38 dataMapOffset = headerSize + i * dataMapSize 39 dataMap = sstruct.unpack( 40 DATA_MAP_FORMAT, data[dataMapOffset : dataMapOffset + dataMapSize] 41 ) 42 tag = dataMap["tag"] 43 offset = dataMap["dataOffset"] 44 self.data[tag] = data[offset : offset + dataMap["dataLength"]] 45 if tag in ["dlng", "slng"]: 46 self.data[tag] = self.data[tag].decode("utf-8") 47 48 def compile(self, ttFont): 49 keys = sorted(self.data.keys()) 50 headerSize = sstruct.calcsize(META_HEADER_FORMAT) 51 dataOffset = headerSize + len(keys) * sstruct.calcsize(DATA_MAP_FORMAT) 52 header = sstruct.pack( 53 META_HEADER_FORMAT, 54 { 55 "version": 1, 56 "flags": 0, 57 "dataOffset": dataOffset, 58 "numDataMaps": len(keys), 59 }, 60 ) 61 dataMaps = [] 62 dataBlocks = [] 63 for tag in keys: 64 if tag in ["dlng", "slng"]: 65 data = self.data[tag].encode("utf-8") 66 else: 67 data = self.data[tag] 68 dataMaps.append( 69 sstruct.pack( 70 DATA_MAP_FORMAT, 71 {"tag": tag, "dataOffset": dataOffset, "dataLength": len(data)}, 72 ) 73 ) 74 dataBlocks.append(data) 75 dataOffset += len(data) 76 return bytesjoin([header] + dataMaps + dataBlocks) 77 78 def toXML(self, writer, ttFont): 79 for tag in sorted(self.data.keys()): 80 if tag in ["dlng", "slng"]: 81 writer.begintag("text", tag=tag) 82 writer.newline() 83 writer.write(self.data[tag]) 84 writer.newline() 85 writer.endtag("text") 86 writer.newline() 87 else: 88 writer.begintag("hexdata", tag=tag) 89 writer.newline() 90 data = self.data[tag] 91 if min(data) >= 0x20 and max(data) <= 0x7E: 92 writer.comment("ascii: " + data.decode("ascii")) 93 writer.newline() 94 writer.dumphex(data) 95 writer.endtag("hexdata") 96 writer.newline() 97 98 def fromXML(self, name, attrs, content, ttFont): 99 if name == "hexdata": 100 self.data[attrs["tag"]] = readHex(content) 101 elif name == "text" and attrs["tag"] in ["dlng", "slng"]: 102 self.data[attrs["tag"]] = strjoin(content).strip() 103 else: 104 raise TTLibError("can't handle '%s' element" % name) 105