diff options
Diffstat (limited to 'Lib/fontTools/ttLib/tables/_p_o_s_t.py')
-rw-r--r-- | Lib/fontTools/ttLib/tables/_p_o_s_t.py | 490 |
1 files changed, 253 insertions, 237 deletions
diff --git a/Lib/fontTools/ttLib/tables/_p_o_s_t.py b/Lib/fontTools/ttLib/tables/_p_o_s_t.py index c54b87f0..dba63711 100644 --- a/Lib/fontTools/ttLib/tables/_p_o_s_t.py +++ b/Lib/fontTools/ttLib/tables/_p_o_s_t.py @@ -27,266 +27,282 @@ postFormatSize = sstruct.calcsize(postFormat) class table__p_o_s_t(DefaultTable.DefaultTable): + def decompile(self, data, ttFont): + sstruct.unpack(postFormat, data[:postFormatSize], self) + data = data[postFormatSize:] + if self.formatType == 1.0: + self.decode_format_1_0(data, ttFont) + elif self.formatType == 2.0: + self.decode_format_2_0(data, ttFont) + elif self.formatType == 3.0: + self.decode_format_3_0(data, ttFont) + elif self.formatType == 4.0: + self.decode_format_4_0(data, ttFont) + else: + # supported format + raise ttLib.TTLibError( + "'post' table format %f not supported" % self.formatType + ) - def decompile(self, data, ttFont): - sstruct.unpack(postFormat, data[:postFormatSize], self) - data = data[postFormatSize:] - if self.formatType == 1.0: - self.decode_format_1_0(data, ttFont) - elif self.formatType == 2.0: - self.decode_format_2_0(data, ttFont) - elif self.formatType == 3.0: - self.decode_format_3_0(data, ttFont) - elif self.formatType == 4.0: - self.decode_format_4_0(data, ttFont) - else: - # supported format - raise ttLib.TTLibError("'post' table format %f not supported" % self.formatType) + def compile(self, ttFont): + data = sstruct.pack(postFormat, self) + if self.formatType == 1.0: + pass # we're done + elif self.formatType == 2.0: + data = data + self.encode_format_2_0(ttFont) + elif self.formatType == 3.0: + pass # we're done + elif self.formatType == 4.0: + data = data + self.encode_format_4_0(ttFont) + else: + # supported format + raise ttLib.TTLibError( + "'post' table format %f not supported" % self.formatType + ) + return data - def compile(self, ttFont): - data = sstruct.pack(postFormat, self) - if self.formatType == 1.0: - pass # we're done - elif self.formatType == 2.0: - data = data + self.encode_format_2_0(ttFont) - elif self.formatType == 3.0: - pass # we're done - elif self.formatType == 4.0: - data = data + self.encode_format_4_0(ttFont) - else: - # supported format - raise ttLib.TTLibError("'post' table format %f not supported" % self.formatType) - return data + def getGlyphOrder(self): + """This function will get called by a ttLib.TTFont instance. + Do not call this function yourself, use TTFont().getGlyphOrder() + or its relatives instead! + """ + if not hasattr(self, "glyphOrder"): + raise ttLib.TTLibError("illegal use of getGlyphOrder()") + glyphOrder = self.glyphOrder + del self.glyphOrder + return glyphOrder - def getGlyphOrder(self): - """This function will get called by a ttLib.TTFont instance. - Do not call this function yourself, use TTFont().getGlyphOrder() - or its relatives instead! - """ - if not hasattr(self, "glyphOrder"): - raise ttLib.TTLibError("illegal use of getGlyphOrder()") - glyphOrder = self.glyphOrder - del self.glyphOrder - return glyphOrder + def decode_format_1_0(self, data, ttFont): + self.glyphOrder = standardGlyphOrder[: ttFont["maxp"].numGlyphs] - def decode_format_1_0(self, data, ttFont): - self.glyphOrder = standardGlyphOrder[:ttFont["maxp"].numGlyphs] + def decode_format_2_0(self, data, ttFont): + (numGlyphs,) = struct.unpack(">H", data[:2]) + numGlyphs = int(numGlyphs) + if numGlyphs > ttFont["maxp"].numGlyphs: + # Assume the numGlyphs field is bogus, so sync with maxp. + # I've seen this in one font, and if the assumption is + # wrong elsewhere, well, so be it: it's hard enough to + # work around _one_ non-conforming post format... + numGlyphs = ttFont["maxp"].numGlyphs + data = data[2:] + indices = array.array("H") + indices.frombytes(data[: 2 * numGlyphs]) + if sys.byteorder != "big": + indices.byteswap() + data = data[2 * numGlyphs :] + maxIndex = max(indices) + self.extraNames = extraNames = unpackPStrings(data, maxIndex - 257) + self.glyphOrder = glyphOrder = [""] * int(ttFont["maxp"].numGlyphs) + for glyphID in range(numGlyphs): + index = indices[glyphID] + if index > 257: + try: + name = extraNames[index - 258] + except IndexError: + name = "" + else: + # fetch names from standard list + name = standardGlyphOrder[index] + glyphOrder[glyphID] = name + self.build_psNameMapping(ttFont) - def decode_format_2_0(self, data, ttFont): - numGlyphs, = struct.unpack(">H", data[:2]) - numGlyphs = int(numGlyphs) - if numGlyphs > ttFont['maxp'].numGlyphs: - # Assume the numGlyphs field is bogus, so sync with maxp. - # I've seen this in one font, and if the assumption is - # wrong elsewhere, well, so be it: it's hard enough to - # work around _one_ non-conforming post format... - numGlyphs = ttFont['maxp'].numGlyphs - data = data[2:] - indices = array.array("H") - indices.frombytes(data[:2*numGlyphs]) - if sys.byteorder != "big": indices.byteswap() - data = data[2*numGlyphs:] - maxIndex = max(indices) - self.extraNames = extraNames = unpackPStrings(data, maxIndex-257) - self.glyphOrder = glyphOrder = [""] * int(ttFont['maxp'].numGlyphs) - for glyphID in range(numGlyphs): - index = indices[glyphID] - if index > 257: - try: - name = extraNames[index-258] - except IndexError: - name = "" - else: - # fetch names from standard list - name = standardGlyphOrder[index] - glyphOrder[glyphID] = name - self.build_psNameMapping(ttFont) + def build_psNameMapping(self, ttFont): + mapping = {} + allNames = {} + for i in range(ttFont["maxp"].numGlyphs): + glyphName = psName = self.glyphOrder[i] + if glyphName == "": + glyphName = "glyph%.5d" % i + if glyphName in allNames: + # make up a new glyphName that's unique + n = allNames[glyphName] + while (glyphName + "#" + str(n)) in allNames: + n += 1 + allNames[glyphName] = n + 1 + glyphName = glyphName + "#" + str(n) - def build_psNameMapping(self, ttFont): - mapping = {} - allNames = {} - for i in range(ttFont['maxp'].numGlyphs): - glyphName = psName = self.glyphOrder[i] - if glyphName == "": - glyphName = "glyph%.5d" % i - if glyphName in allNames: - # make up a new glyphName that's unique - n = allNames[glyphName] - while (glyphName + "#" + str(n)) in allNames: - n += 1 - allNames[glyphName] = n + 1 - glyphName = glyphName + "#" + str(n) + self.glyphOrder[i] = glyphName + allNames[glyphName] = 1 + if glyphName != psName: + mapping[glyphName] = psName - self.glyphOrder[i] = glyphName - allNames[glyphName] = 1 - if glyphName != psName: - mapping[glyphName] = psName + self.mapping = mapping - self.mapping = mapping + def decode_format_3_0(self, data, ttFont): + # Setting self.glyphOrder to None will cause the TTFont object + # try and construct glyph names from a Unicode cmap table. + self.glyphOrder = None - def decode_format_3_0(self, data, ttFont): - # Setting self.glyphOrder to None will cause the TTFont object - # try and construct glyph names from a Unicode cmap table. - self.glyphOrder = None + def decode_format_4_0(self, data, ttFont): + from fontTools import agl - def decode_format_4_0(self, data, ttFont): - from fontTools import agl - numGlyphs = ttFont['maxp'].numGlyphs - indices = array.array("H") - indices.frombytes(data) - if sys.byteorder != "big": indices.byteswap() - # In some older fonts, the size of the post table doesn't match - # the number of glyphs. Sometimes it's bigger, sometimes smaller. - self.glyphOrder = glyphOrder = [''] * int(numGlyphs) - for i in range(min(len(indices),numGlyphs)): - if indices[i] == 0xFFFF: - self.glyphOrder[i] = '' - elif indices[i] in agl.UV2AGL: - self.glyphOrder[i] = agl.UV2AGL[indices[i]] - else: - self.glyphOrder[i] = "uni%04X" % indices[i] - self.build_psNameMapping(ttFont) + numGlyphs = ttFont["maxp"].numGlyphs + indices = array.array("H") + indices.frombytes(data) + if sys.byteorder != "big": + indices.byteswap() + # In some older fonts, the size of the post table doesn't match + # the number of glyphs. Sometimes it's bigger, sometimes smaller. + self.glyphOrder = glyphOrder = [""] * int(numGlyphs) + for i in range(min(len(indices), numGlyphs)): + if indices[i] == 0xFFFF: + self.glyphOrder[i] = "" + elif indices[i] in agl.UV2AGL: + self.glyphOrder[i] = agl.UV2AGL[indices[i]] + else: + self.glyphOrder[i] = "uni%04X" % indices[i] + self.build_psNameMapping(ttFont) - def encode_format_2_0(self, ttFont): - numGlyphs = ttFont['maxp'].numGlyphs - glyphOrder = ttFont.getGlyphOrder() - assert len(glyphOrder) == numGlyphs - indices = array.array("H") - extraDict = {} - extraNames = self.extraNames = [ - n for n in self.extraNames if n not in standardGlyphOrder] - for i in range(len(extraNames)): - extraDict[extraNames[i]] = i - for glyphID in range(numGlyphs): - glyphName = glyphOrder[glyphID] - if glyphName in self.mapping: - psName = self.mapping[glyphName] - else: - psName = glyphName - if psName in extraDict: - index = 258 + extraDict[psName] - elif psName in standardGlyphOrder: - index = standardGlyphOrder.index(psName) - else: - index = 258 + len(extraNames) - extraDict[psName] = len(extraNames) - extraNames.append(psName) - indices.append(index) - if sys.byteorder != "big": indices.byteswap() - return struct.pack(">H", numGlyphs) + indices.tobytes() + packPStrings(extraNames) + def encode_format_2_0(self, ttFont): + numGlyphs = ttFont["maxp"].numGlyphs + glyphOrder = ttFont.getGlyphOrder() + assert len(glyphOrder) == numGlyphs + indices = array.array("H") + extraDict = {} + extraNames = self.extraNames = [ + n for n in self.extraNames if n not in standardGlyphOrder + ] + for i in range(len(extraNames)): + extraDict[extraNames[i]] = i + for glyphID in range(numGlyphs): + glyphName = glyphOrder[glyphID] + if glyphName in self.mapping: + psName = self.mapping[glyphName] + else: + psName = glyphName + if psName in extraDict: + index = 258 + extraDict[psName] + elif psName in standardGlyphOrder: + index = standardGlyphOrder.index(psName) + else: + index = 258 + len(extraNames) + extraDict[psName] = len(extraNames) + extraNames.append(psName) + indices.append(index) + if sys.byteorder != "big": + indices.byteswap() + return ( + struct.pack(">H", numGlyphs) + indices.tobytes() + packPStrings(extraNames) + ) - def encode_format_4_0(self, ttFont): - from fontTools import agl - numGlyphs = ttFont['maxp'].numGlyphs - glyphOrder = ttFont.getGlyphOrder() - assert len(glyphOrder) == numGlyphs - indices = array.array("H") - for glyphID in glyphOrder: - glyphID = glyphID.split('#')[0] - if glyphID in agl.AGL2UV: - indices.append(agl.AGL2UV[glyphID]) - elif len(glyphID) == 7 and glyphID[:3] == 'uni': - indices.append(int(glyphID[3:],16)) - else: - indices.append(0xFFFF) - if sys.byteorder != "big": indices.byteswap() - return indices.tobytes() + def encode_format_4_0(self, ttFont): + from fontTools import agl - def toXML(self, writer, ttFont): - formatstring, names, fixes = sstruct.getformat(postFormat) - for name in names: - value = getattr(self, name) - writer.simpletag(name, value=value) - writer.newline() - if hasattr(self, "mapping"): - writer.begintag("psNames") - writer.newline() - writer.comment("This file uses unique glyph names based on the information\n" - "found in the 'post' table. Since these names might not be unique,\n" - "we have to invent artificial names in case of clashes. In order to\n" - "be able to retain the original information, we need a name to\n" - "ps name mapping for those cases where they differ. That's what\n" - "you see below.\n") - writer.newline() - items = sorted(self.mapping.items()) - for name, psName in items: - writer.simpletag("psName", name=name, psName=psName) - writer.newline() - writer.endtag("psNames") - writer.newline() - if hasattr(self, "extraNames"): - writer.begintag("extraNames") - writer.newline() - writer.comment("following are the name that are not taken from the standard Mac glyph order") - writer.newline() - for name in self.extraNames: - writer.simpletag("psName", name=name) - writer.newline() - writer.endtag("extraNames") - writer.newline() - if hasattr(self, "data"): - writer.begintag("hexdata") - writer.newline() - writer.dumphex(self.data) - writer.endtag("hexdata") - writer.newline() + numGlyphs = ttFont["maxp"].numGlyphs + glyphOrder = ttFont.getGlyphOrder() + assert len(glyphOrder) == numGlyphs + indices = array.array("H") + for glyphID in glyphOrder: + glyphID = glyphID.split("#")[0] + if glyphID in agl.AGL2UV: + indices.append(agl.AGL2UV[glyphID]) + elif len(glyphID) == 7 and glyphID[:3] == "uni": + indices.append(int(glyphID[3:], 16)) + else: + indices.append(0xFFFF) + if sys.byteorder != "big": + indices.byteswap() + return indices.tobytes() - def fromXML(self, name, attrs, content, ttFont): - if name not in ("psNames", "extraNames", "hexdata"): - setattr(self, name, safeEval(attrs["value"])) - elif name == "psNames": - self.mapping = {} - for element in content: - if not isinstance(element, tuple): - continue - name, attrs, content = element - if name == "psName": - self.mapping[attrs["name"]] = attrs["psName"] - elif name == "extraNames": - self.extraNames = [] - for element in content: - if not isinstance(element, tuple): - continue - name, attrs, content = element - if name == "psName": - self.extraNames.append(attrs["name"]) - else: - self.data = readHex(content) + def toXML(self, writer, ttFont): + formatstring, names, fixes = sstruct.getformat(postFormat) + for name in names: + value = getattr(self, name) + writer.simpletag(name, value=value) + writer.newline() + if hasattr(self, "mapping"): + writer.begintag("psNames") + writer.newline() + writer.comment( + "This file uses unique glyph names based on the information\n" + "found in the 'post' table. Since these names might not be unique,\n" + "we have to invent artificial names in case of clashes. In order to\n" + "be able to retain the original information, we need a name to\n" + "ps name mapping for those cases where they differ. That's what\n" + "you see below.\n" + ) + writer.newline() + items = sorted(self.mapping.items()) + for name, psName in items: + writer.simpletag("psName", name=name, psName=psName) + writer.newline() + writer.endtag("psNames") + writer.newline() + if hasattr(self, "extraNames"): + writer.begintag("extraNames") + writer.newline() + writer.comment( + "following are the name that are not taken from the standard Mac glyph order" + ) + writer.newline() + for name in self.extraNames: + writer.simpletag("psName", name=name) + writer.newline() + writer.endtag("extraNames") + writer.newline() + if hasattr(self, "data"): + writer.begintag("hexdata") + writer.newline() + writer.dumphex(self.data) + writer.endtag("hexdata") + writer.newline() + + def fromXML(self, name, attrs, content, ttFont): + if name not in ("psNames", "extraNames", "hexdata"): + setattr(self, name, safeEval(attrs["value"])) + elif name == "psNames": + self.mapping = {} + for element in content: + if not isinstance(element, tuple): + continue + name, attrs, content = element + if name == "psName": + self.mapping[attrs["name"]] = attrs["psName"] + elif name == "extraNames": + self.extraNames = [] + for element in content: + if not isinstance(element, tuple): + continue + name, attrs, content = element + if name == "psName": + self.extraNames.append(attrs["name"]) + else: + self.data = readHex(content) def unpackPStrings(data, n): - # extract n Pascal strings from data. - # if there is not enough data, use "" + # extract n Pascal strings from data. + # if there is not enough data, use "" - strings = [] - index = 0 - dataLen = len(data) + strings = [] + index = 0 + dataLen = len(data) - for _ in range(n): - if dataLen <= index: - length = 0 - else: - length = byteord(data[index]) - index += 1 + for _ in range(n): + if dataLen <= index: + length = 0 + else: + length = byteord(data[index]) + index += 1 - if dataLen <= index + length - 1: - name = "" - else: - name = tostr(data[index:index+length], encoding="latin1") - strings.append (name) - index += length + if dataLen <= index + length - 1: + name = "" + else: + name = tostr(data[index : index + length], encoding="latin1") + strings.append(name) + index += length - if index < dataLen: - log.warning("%d extra bytes in post.stringData array", dataLen - index) + if index < dataLen: + log.warning("%d extra bytes in post.stringData array", dataLen - index) - elif dataLen < index: - log.warning("not enough data in post.stringData array") + elif dataLen < index: + log.warning("not enough data in post.stringData array") - return strings + return strings def packPStrings(strings): - data = b"" - for s in strings: - data = data + bytechr(len(s)) + tobytes(s, encoding="latin1") - return data + data = b"" + for s in strings: + data = data + bytechr(len(s)) + tobytes(s, encoding="latin1") + return data |