aboutsummaryrefslogtreecommitdiff
path: root/Lib/fontTools/ttLib/tables/_p_o_s_t.py
diff options
context:
space:
mode:
Diffstat (limited to 'Lib/fontTools/ttLib/tables/_p_o_s_t.py')
-rw-r--r--Lib/fontTools/ttLib/tables/_p_o_s_t.py490
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