_p_o_s_t.py revision 180ace6a5ff1399ec53bc696e8bef7cce6eef39a
1import sys 2from fontTools.ttLib.standardGlyphOrder import standardGlyphOrder 3from . import DefaultTable 4import struct 5from fontTools.misc import sstruct 6import array 7from fontTools import ttLib 8from fontTools.misc.textTools import safeEval, readHex 9from types import TupleType 10 11 12postFormat = """ 13 > 14 formatType: 16.16F 15 italicAngle: 16.16F # italic angle in degrees 16 underlinePosition: h 17 underlineThickness: h 18 isFixedPitch: L 19 minMemType42: L # minimum memory if TrueType font is downloaded 20 maxMemType42: L # maximum memory if TrueType font is downloaded 21 minMemType1: L # minimum memory if Type1 font is downloaded 22 maxMemType1: L # maximum memory if Type1 font is downloaded 23""" 24 25postFormatSize = sstruct.calcsize(postFormat) 26 27 28class table__p_o_s_t(DefaultTable.DefaultTable): 29 30 def decompile(self, data, ttFont): 31 sstruct.unpack(postFormat, data[:postFormatSize], self) 32 data = data[postFormatSize:] 33 if self.formatType == 1.0: 34 self.decode_format_1_0(data, ttFont) 35 elif self.formatType == 2.0: 36 self.decode_format_2_0(data, ttFont) 37 elif self.formatType == 3.0: 38 self.decode_format_3_0(data, ttFont) 39 else: 40 # supported format 41 raise ttLib.TTLibError, "'post' table format %f not supported" % self.formatType 42 43 def compile(self, ttFont): 44 data = sstruct.pack(postFormat, self) 45 if self.formatType == 1.0: 46 pass # we're done 47 elif self.formatType == 2.0: 48 data = data + self.encode_format_2_0(ttFont) 49 elif self.formatType == 3.0: 50 pass # we're done 51 else: 52 # supported format 53 raise ttLib.TTLibError, "'post' table format %f not supported" % self.formatType 54 return data 55 56 def getGlyphOrder(self): 57 """This function will get called by a ttLib.TTFont instance. 58 Do not call this function yourself, use TTFont().getGlyphOrder() 59 or its relatives instead! 60 """ 61 if not hasattr(self, "glyphOrder"): 62 raise ttLib.TTLibError, "illegal use of getGlyphOrder()" 63 glyphOrder = self.glyphOrder 64 del self.glyphOrder 65 return glyphOrder 66 67 def decode_format_1_0(self, data, ttFont): 68 self.glyphOrder = standardGlyphOrder[:ttFont["maxp"].numGlyphs] 69 70 def decode_format_2_0(self, data, ttFont): 71 numGlyphs, = struct.unpack(">H", data[:2]) 72 numGlyphs = int(numGlyphs) 73 if numGlyphs > ttFont['maxp'].numGlyphs: 74 # Assume the numGlyphs field is bogus, so sync with maxp. 75 # I've seen this in one font, and if the assumption is 76 # wrong elsewhere, well, so be it: it's hard enough to 77 # work around _one_ non-conforming post format... 78 numGlyphs = ttFont['maxp'].numGlyphs 79 data = data[2:] 80 indices = array.array("H") 81 indices.fromstring(data[:2*numGlyphs]) 82 if sys.byteorder != "big": 83 indices.byteswap() 84 data = data[2*numGlyphs:] 85 self.extraNames = extraNames = unpackPStrings(data) 86 self.glyphOrder = glyphOrder = [None] * int(ttFont['maxp'].numGlyphs) 87 for glyphID in range(numGlyphs): 88 index = indices[glyphID] 89 if index > 257: 90 name = extraNames[index-258] 91 else: 92 # fetch names from standard list 93 name = standardGlyphOrder[index] 94 glyphOrder[glyphID] = name 95 #AL990511: code added to handle the case of new glyphs without 96 # entries into the 'post' table 97 if numGlyphs < ttFont['maxp'].numGlyphs: 98 for i in range(numGlyphs, ttFont['maxp'].numGlyphs): 99 glyphOrder[i] = "glyph#%.5d" % i 100 self.extraNames.append(glyphOrder[i]) 101 self.build_psNameMapping(ttFont) 102 103 def build_psNameMapping(self, ttFont): 104 mapping = {} 105 allNames = {} 106 for i in range(ttFont['maxp'].numGlyphs): 107 glyphName = psName = self.glyphOrder[i] 108 if glyphName in allNames: 109 # make up a new glyphName that's unique 110 n = allNames[glyphName] 111 allNames[glyphName] = n + 1 112 glyphName = glyphName + "#" + `n` 113 self.glyphOrder[i] = glyphName 114 mapping[glyphName] = psName 115 else: 116 allNames[glyphName] = 1 117 self.mapping = mapping 118 119 def decode_format_3_0(self, data, ttFont): 120 # Setting self.glyphOrder to None will cause the TTFont object 121 # try and construct glyph names from a Unicode cmap table. 122 self.glyphOrder = None 123 124 def encode_format_2_0(self, ttFont): 125 numGlyphs = ttFont['maxp'].numGlyphs 126 glyphOrder = ttFont.getGlyphOrder() 127 assert len(glyphOrder) == numGlyphs 128 indices = array.array("H") 129 extraDict = {} 130 extraNames = self.extraNames 131 for i in range(len(extraNames)): 132 extraDict[extraNames[i]] = i 133 for glyphID in range(numGlyphs): 134 glyphName = glyphOrder[glyphID] 135 if glyphName in self.mapping: 136 psName = self.mapping[glyphName] 137 else: 138 psName = glyphName 139 if psName in extraDict: 140 index = 258 + extraDict[psName] 141 elif psName in standardGlyphOrder: 142 index = standardGlyphOrder.index(psName) 143 else: 144 index = 258 + len(extraNames) 145 extraDict[psName] = len(extraNames) 146 extraNames.append(psName) 147 indices.append(index) 148 if sys.byteorder != "big": 149 indices.byteswap() 150 return struct.pack(">H", numGlyphs) + indices.tostring() + packPStrings(extraNames) 151 152 def toXML(self, writer, ttFont): 153 formatstring, names, fixes = sstruct.getformat(postFormat) 154 for name in names: 155 value = getattr(self, name) 156 writer.simpletag(name, value=value) 157 writer.newline() 158 if hasattr(self, "mapping"): 159 writer.begintag("psNames") 160 writer.newline() 161 writer.comment("This file uses unique glyph names based on the information\n" 162 "found in the 'post' table. Since these names might not be unique,\n" 163 "we have to invent artificial names in case of clashes. In order to\n" 164 "be able to retain the original information, we need a name to\n" 165 "ps name mapping for those cases where they differ. That's what\n" 166 "you see below.\n") 167 writer.newline() 168 items = self.mapping.items() 169 items.sort() 170 for name, psName in items: 171 writer.simpletag("psName", name=name, psName=psName) 172 writer.newline() 173 writer.endtag("psNames") 174 writer.newline() 175 if hasattr(self, "extraNames"): 176 writer.begintag("extraNames") 177 writer.newline() 178 writer.comment("following are the name that are not taken from the standard Mac glyph order") 179 writer.newline() 180 for name in self.extraNames: 181 writer.simpletag("psName", name=name) 182 writer.newline() 183 writer.endtag("extraNames") 184 writer.newline() 185 if hasattr(self, "data"): 186 writer.begintag("hexdata") 187 writer.newline() 188 writer.dumphex(self.data) 189 writer.endtag("hexdata") 190 writer.newline() 191 192 def fromXML(self, (name, attrs, content), ttFont): 193 if name not in ("psNames", "extraNames", "hexdata"): 194 setattr(self, name, safeEval(attrs["value"])) 195 elif name == "psNames": 196 self.mapping = {} 197 for element in content: 198 if type(element) != TupleType: 199 continue 200 name, attrs, content = element 201 if name == "psName": 202 self.mapping[attrs["name"]] = attrs["psName"] 203 elif name == "extraNames": 204 self.extraNames = [] 205 for element in content: 206 if type(element) != TupleType: 207 continue 208 name, attrs, content = element 209 if name == "psName": 210 self.extraNames.append(attrs["name"]) 211 else: 212 self.data = readHex(content) 213 214 215def unpackPStrings(data): 216 strings = [] 217 index = 0 218 dataLen = len(data) 219 while index < dataLen: 220 length = ord(data[index]) 221 strings.append(data[index+1:index+1+length]) 222 index = index + 1 + length 223 return strings 224 225 226def packPStrings(strings): 227 data = "" 228 for s in strings: 229 data = data + chr(len(s)) + s 230 return data 231 232