Today, Apple has kindly fixed a bug in the [specification of the meta table](https://developer.apple.com/fonts/TrueType-Reference-Manual/RM06/Chap6meta.html). The existing fonttools implementation matches the revised specification. Therefore, the comments about fonttools intentionally deviating from Apple's spec can be removed.
100 lines
3.4 KiB
Python
100 lines
3.4 KiB
Python
from __future__ import print_function, division, absolute_import
|
|
from fontTools.misc.py23 import *
|
|
from fontTools.misc import sstruct
|
|
from fontTools.misc.textTools import readHex
|
|
from fontTools.ttLib import TTLibError
|
|
from . import DefaultTable
|
|
|
|
# Apple's documentation of 'meta':
|
|
# https://developer.apple.com/fonts/TrueType-Reference-Manual/RM06/Chap6meta.html
|
|
|
|
META_HEADER_FORMAT = """
|
|
> # big endian
|
|
version: L
|
|
flags: L
|
|
dataOffset: L
|
|
numDataMaps: L
|
|
"""
|
|
|
|
|
|
DATA_MAP_FORMAT = """
|
|
> # big endian
|
|
tag: 4s
|
|
dataOffset: L
|
|
dataLength: L
|
|
"""
|
|
|
|
|
|
class table__m_e_t_a(DefaultTable.DefaultTable):
|
|
def __init__(self, tag="meta"):
|
|
DefaultTable.DefaultTable.__init__(self, tag)
|
|
self.data = {}
|
|
|
|
def decompile(self, data, ttFont):
|
|
headerSize = sstruct.calcsize(META_HEADER_FORMAT)
|
|
header = sstruct.unpack(META_HEADER_FORMAT, data[0 : headerSize])
|
|
if header["version"] != 1:
|
|
raise TTLibError("unsupported 'meta' version %d" %
|
|
header["version"])
|
|
dataMapSize = sstruct.calcsize(DATA_MAP_FORMAT)
|
|
for i in range(header["numDataMaps"]):
|
|
dataMapOffset = headerSize + i * dataMapSize
|
|
dataMap = sstruct.unpack(
|
|
DATA_MAP_FORMAT,
|
|
data[dataMapOffset : dataMapOffset + dataMapSize])
|
|
tag = dataMap["tag"]
|
|
offset = dataMap["dataOffset"]
|
|
self.data[tag] = data[offset : offset + dataMap["dataLength"]]
|
|
if tag in ["dlng", "slng"]:
|
|
self.data[tag] = self.data[tag].decode("utf-8")
|
|
|
|
def compile(self, ttFont):
|
|
keys = sorted(self.data.keys())
|
|
headerSize = sstruct.calcsize(META_HEADER_FORMAT)
|
|
dataOffset = headerSize + len(keys) * sstruct.calcsize(DATA_MAP_FORMAT)
|
|
header = sstruct.pack(META_HEADER_FORMAT, {
|
|
"version": 1,
|
|
"flags": 0,
|
|
"dataOffset": dataOffset,
|
|
"numDataMaps": len(keys)
|
|
})
|
|
dataMaps = []
|
|
dataBlocks = []
|
|
for tag in keys:
|
|
if tag in ["dlng", "slng"]:
|
|
data = self.data[tag].encode("utf-8")
|
|
else:
|
|
data = self.data[tag]
|
|
dataMaps.append(sstruct.pack(DATA_MAP_FORMAT, {
|
|
"tag": tag,
|
|
"dataOffset": dataOffset,
|
|
"dataLength": len(data)
|
|
}))
|
|
dataBlocks.append(data)
|
|
dataOffset += len(data)
|
|
return bytesjoin([header] + dataMaps + dataBlocks)
|
|
|
|
def toXML(self, writer, ttFont, progress=None):
|
|
for tag in sorted(self.data.keys()):
|
|
if tag in ["dlng", "slng"]:
|
|
writer.begintag("text", tag=tag)
|
|
writer.newline()
|
|
writer.write(self.data[tag])
|
|
writer.newline()
|
|
writer.endtag("text")
|
|
writer.newline()
|
|
else:
|
|
writer.begintag("hexdata", tag=tag)
|
|
writer.newline()
|
|
writer.dumphex(self.data[tag])
|
|
writer.endtag("hexdata")
|
|
writer.newline()
|
|
|
|
def fromXML(self, name, attrs, content, ttFont):
|
|
if name == "hexdata":
|
|
self.data[attrs["tag"]] = readHex(content)
|
|
elif name == "text" and attrs["tag"] in ["dlng", "slng"]:
|
|
self.data[attrs["tag"]] = strjoin(content).strip()
|
|
else:
|
|
raise TTLibError("can't handle '%s' element" % name)
|