1from fontTools.misc import sstruct 2from fontTools.misc.fixedTools import floatToFixedToStr 3from fontTools.misc.textTools import safeEval 4# from itertools import * 5from functools import partial 6from . import DefaultTable 7from . import grUtils 8import struct 9 10 11Glat_format_0 = """ 12 > # big endian 13 version: 16.16F 14""" 15 16Glat_format_3 = """ 17 > 18 version: 16.16F 19 compression:L # compression scheme or reserved 20""" 21 22Glat_format_1_entry = """ 23 > 24 attNum: B # Attribute number of first attribute 25 num: B # Number of attributes in this run 26""" 27Glat_format_23_entry = """ 28 > 29 attNum: H # Attribute number of first attribute 30 num: H # Number of attributes in this run 31""" 32 33Glat_format_3_octabox_metrics = """ 34 > 35 subboxBitmap: H # Which subboxes exist on 4x4 grid 36 diagNegMin: B # Defines minimum negatively-sloped diagonal (si) 37 diagNegMax: B # Defines maximum negatively-sloped diagonal (sa) 38 diagPosMin: B # Defines minimum positively-sloped diagonal (di) 39 diagPosMax: B # Defines maximum positively-sloped diagonal (da) 40""" 41 42Glat_format_3_subbox_entry = """ 43 > 44 left: B # xi 45 right: B # xa 46 bottom: B # yi 47 top: B # ya 48 diagNegMin: B # Defines minimum negatively-sloped diagonal (si) 49 diagNegMax: B # Defines maximum negatively-sloped diagonal (sa) 50 diagPosMin: B # Defines minimum positively-sloped diagonal (di) 51 diagPosMax: B # Defines maximum positively-sloped diagonal (da) 52""" 53 54class _Object() : 55 pass 56 57class _Dict(dict) : 58 pass 59 60class table_G__l_a_t(DefaultTable.DefaultTable): 61 ''' 62 Support Graphite Glat tables 63 ''' 64 65 def __init__(self, tag=None): 66 DefaultTable.DefaultTable.__init__(self, tag) 67 self.scheme = 0 68 69 def decompile(self, data, ttFont): 70 sstruct.unpack2(Glat_format_0, data, self) 71 self.version = float(floatToFixedToStr(self.version, precisionBits=16)) 72 if self.version <= 1.9: 73 decoder = partial(self.decompileAttributes12,fmt=Glat_format_1_entry) 74 elif self.version <= 2.9: 75 decoder = partial(self.decompileAttributes12,fmt=Glat_format_23_entry) 76 elif self.version >= 3.0: 77 (data, self.scheme) = grUtils.decompress(data) 78 sstruct.unpack2(Glat_format_3, data, self) 79 self.hasOctaboxes = (self.compression & 1) == 1 80 decoder = self.decompileAttributes3 81 82 gloc = ttFont['Gloc'] 83 self.attributes = {} 84 count = 0 85 for s,e in zip(gloc,gloc[1:]): 86 self.attributes[ttFont.getGlyphName(count)] = decoder(data[s:e]) 87 count += 1 88 89 def decompileAttributes12(self, data, fmt): 90 attributes = _Dict() 91 while len(data) > 3: 92 e, data = sstruct.unpack2(fmt, data, _Object()) 93 keys = range(e.attNum, e.attNum+e.num) 94 if len(data) >= 2 * e.num : 95 vals = struct.unpack_from(('>%dh' % e.num), data) 96 attributes.update(zip(keys,vals)) 97 data = data[2*e.num:] 98 return attributes 99 100 def decompileAttributes3(self, data): 101 if self.hasOctaboxes: 102 o, data = sstruct.unpack2(Glat_format_3_octabox_metrics, data, _Object()) 103 numsub = bin(o.subboxBitmap).count("1") 104 o.subboxes = [] 105 for b in range(numsub): 106 if len(data) >= 8 : 107 subbox, data = sstruct.unpack2(Glat_format_3_subbox_entry, 108 data, _Object()) 109 o.subboxes.append(subbox) 110 attrs = self.decompileAttributes12(data, Glat_format_23_entry) 111 if self.hasOctaboxes: 112 attrs.octabox = o 113 return attrs 114 115 def compile(self, ttFont): 116 data = sstruct.pack(Glat_format_0, self) 117 if self.version <= 1.9: 118 encoder = partial(self.compileAttributes12, fmt=Glat_format_1_entry) 119 elif self.version <= 2.9: 120 encoder = partial(self.compileAttributes12, fmt=Glat_format_1_entry) 121 elif self.version >= 3.0: 122 self.compression = (self.scheme << 27) + (1 if self.hasOctaboxes else 0) 123 data = sstruct.pack(Glat_format_3, self) 124 encoder = self.compileAttributes3 125 126 glocs = [] 127 for n in range(len(self.attributes)): 128 glocs.append(len(data)) 129 data += encoder(self.attributes[ttFont.getGlyphName(n)]) 130 glocs.append(len(data)) 131 ttFont['Gloc'].set(glocs) 132 133 if self.version >= 3.0: 134 data = grUtils.compress(self.scheme, data) 135 return data 136 137 def compileAttributes12(self, attrs, fmt): 138 data = b"" 139 for e in grUtils.entries(attrs): 140 data += sstruct.pack(fmt, {'attNum' : e[0], 'num' : e[1]}) + \ 141 struct.pack(('>%dh' % len(e[2])), *e[2]) 142 return data 143 144 def compileAttributes3(self, attrs): 145 if self.hasOctaboxes: 146 o = attrs.octabox 147 data = sstruct.pack(Glat_format_3_octabox_metrics, o) 148 numsub = bin(o.subboxBitmap).count("1") 149 for b in range(numsub) : 150 data += sstruct.pack(Glat_format_3_subbox_entry, o.subboxes[b]) 151 else: 152 data = "" 153 return data + self.compileAttributes12(attrs, Glat_format_23_entry) 154 155 def toXML(self, writer, ttFont): 156 writer.simpletag('version', version=self.version, compressionScheme=self.scheme) 157 writer.newline() 158 for n, a in sorted(self.attributes.items(), key=lambda x:ttFont.getGlyphID(x[0])): 159 writer.begintag('glyph', name=n) 160 writer.newline() 161 if hasattr(a, 'octabox'): 162 o = a.octabox 163 formatstring, names, fixes = sstruct.getformat(Glat_format_3_octabox_metrics) 164 vals = {} 165 for k in names: 166 if k == 'subboxBitmap': continue 167 vals[k] = "{:.3f}%".format(getattr(o, k) * 100. / 255) 168 vals['bitmap'] = "{:0X}".format(o.subboxBitmap) 169 writer.begintag('octaboxes', **vals) 170 writer.newline() 171 formatstring, names, fixes = sstruct.getformat(Glat_format_3_subbox_entry) 172 for s in o.subboxes: 173 vals = {} 174 for k in names: 175 vals[k] = "{:.3f}%".format(getattr(s, k) * 100. / 255) 176 writer.simpletag('octabox', **vals) 177 writer.newline() 178 writer.endtag('octaboxes') 179 writer.newline() 180 for k, v in sorted(a.items()): 181 writer.simpletag('attribute', index=k, value=v) 182 writer.newline() 183 writer.endtag('glyph') 184 writer.newline() 185 186 def fromXML(self, name, attrs, content, ttFont): 187 if name == 'version' : 188 self.version = float(safeEval(attrs['version'])) 189 self.scheme = int(safeEval(attrs['compressionScheme'])) 190 if name != 'glyph' : return 191 if not hasattr(self, 'attributes'): 192 self.attributes = {} 193 gname = attrs['name'] 194 attributes = _Dict() 195 for element in content: 196 if not isinstance(element, tuple): continue 197 tag, attrs, subcontent = element 198 if tag == 'attribute' : 199 k = int(safeEval(attrs['index'])) 200 v = int(safeEval(attrs['value'])) 201 attributes[k]=v 202 elif tag == 'octaboxes': 203 self.hasOctaboxes = True 204 o = _Object() 205 o.subboxBitmap = int(attrs['bitmap'], 16) 206 o.subboxes = [] 207 del attrs['bitmap'] 208 for k, v in attrs.items(): 209 setattr(o, k, int(float(v[:-1]) * 255. / 100. + 0.5)) 210 for element in subcontent: 211 if not isinstance(element, tuple): continue 212 (tag, attrs, subcontent) = element 213 so = _Object() 214 for k, v in attrs.items(): 215 setattr(so, k, int(float(v[:-1]) * 255. / 100. + 0.5)) 216 o.subboxes.append(so) 217 attributes.octabox = o 218 self.attributes[gname] = attributes 219