1from fontTools.misc import sstruct 2from fontTools.misc.textTools import tobytes, tostr, safeEval 3from . import DefaultTable 4 5GMAPFormat = """ 6 > # big endian 7 tableVersionMajor: H 8 tableVersionMinor: H 9 flags: H 10 recordsCount: H 11 recordsOffset: H 12 fontNameLength: H 13""" 14# psFontName is a byte string which follows the record above. This is zero padded 15# to the beginning of the records array. The recordsOffsst is 32 bit aligned. 16 17GMAPRecordFormat1 = """ 18 > # big endian 19 UV: L 20 cid: H 21 gid: H 22 ggid: H 23 name: 32s 24""" 25 26 27class GMAPRecord(object): 28 def __init__(self, uv=0, cid=0, gid=0, ggid=0, name=""): 29 self.UV = uv 30 self.cid = cid 31 self.gid = gid 32 self.ggid = ggid 33 self.name = name 34 35 def toXML(self, writer, ttFont): 36 writer.begintag("GMAPRecord") 37 writer.newline() 38 writer.simpletag("UV", value=self.UV) 39 writer.newline() 40 writer.simpletag("cid", value=self.cid) 41 writer.newline() 42 writer.simpletag("gid", value=self.gid) 43 writer.newline() 44 writer.simpletag("glyphletGid", value=self.gid) 45 writer.newline() 46 writer.simpletag("GlyphletName", value=self.name) 47 writer.newline() 48 writer.endtag("GMAPRecord") 49 writer.newline() 50 51 def fromXML(self, name, attrs, content, ttFont): 52 value = attrs["value"] 53 if name == "GlyphletName": 54 self.name = value 55 else: 56 setattr(self, name, safeEval(value)) 57 58 def compile(self, ttFont): 59 if self.UV is None: 60 self.UV = 0 61 nameLen = len(self.name) 62 if nameLen < 32: 63 self.name = self.name + "\0" * (32 - nameLen) 64 data = sstruct.pack(GMAPRecordFormat1, self) 65 return data 66 67 def __repr__(self): 68 return ( 69 "GMAPRecord[ UV: " 70 + str(self.UV) 71 + ", cid: " 72 + str(self.cid) 73 + ", gid: " 74 + str(self.gid) 75 + ", ggid: " 76 + str(self.ggid) 77 + ", Glyphlet Name: " 78 + str(self.name) 79 + " ]" 80 ) 81 82 83class table_G_M_A_P_(DefaultTable.DefaultTable): 84 dependencies = [] 85 86 def decompile(self, data, ttFont): 87 dummy, newData = sstruct.unpack2(GMAPFormat, data, self) 88 self.psFontName = tostr(newData[: self.fontNameLength]) 89 assert ( 90 self.recordsOffset % 4 91 ) == 0, "GMAP error: recordsOffset is not 32 bit aligned." 92 newData = data[self.recordsOffset :] 93 self.gmapRecords = [] 94 for i in range(self.recordsCount): 95 gmapRecord, newData = sstruct.unpack2( 96 GMAPRecordFormat1, newData, GMAPRecord() 97 ) 98 gmapRecord.name = gmapRecord.name.strip("\0") 99 self.gmapRecords.append(gmapRecord) 100 101 def compile(self, ttFont): 102 self.recordsCount = len(self.gmapRecords) 103 self.fontNameLength = len(self.psFontName) 104 self.recordsOffset = 4 * (((self.fontNameLength + 12) + 3) // 4) 105 data = sstruct.pack(GMAPFormat, self) 106 data = data + tobytes(self.psFontName) 107 data = data + b"\0" * (self.recordsOffset - len(data)) 108 for record in self.gmapRecords: 109 data = data + record.compile(ttFont) 110 return data 111 112 def toXML(self, writer, ttFont): 113 writer.comment("Most of this table will be recalculated by the compiler") 114 writer.newline() 115 formatstring, names, fixes = sstruct.getformat(GMAPFormat) 116 for name in names: 117 value = getattr(self, name) 118 writer.simpletag(name, value=value) 119 writer.newline() 120 writer.simpletag("PSFontName", value=self.psFontName) 121 writer.newline() 122 for gmapRecord in self.gmapRecords: 123 gmapRecord.toXML(writer, ttFont) 124 125 def fromXML(self, name, attrs, content, ttFont): 126 if name == "GMAPRecord": 127 if not hasattr(self, "gmapRecords"): 128 self.gmapRecords = [] 129 gmapRecord = GMAPRecord() 130 self.gmapRecords.append(gmapRecord) 131 for element in content: 132 if isinstance(element, str): 133 continue 134 name, attrs, content = element 135 gmapRecord.fromXML(name, attrs, content, ttFont) 136 else: 137 value = attrs["value"] 138 if name == "PSFontName": 139 self.psFontName = value 140 else: 141 setattr(self, name, safeEval(value)) 142