• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1from fontTools.misc import sstruct
2from fontTools.misc.textTools import bytesjoin, strjoin, readHex
3from fontTools.ttLib import TTLibError
4from . import DefaultTable
5
6# Apple's documentation of 'meta':
7# https://developer.apple.com/fonts/TrueType-Reference-Manual/RM06/Chap6meta.html
8
9META_HEADER_FORMAT = """
10    > # big endian
11    version:     L
12    flags:       L
13    dataOffset:  L
14    numDataMaps: L
15"""
16
17
18DATA_MAP_FORMAT = """
19    > # big endian
20    tag:         4s
21    dataOffset:  L
22    dataLength:  L
23"""
24
25
26class table__m_e_t_a(DefaultTable.DefaultTable):
27    def __init__(self, tag=None):
28        DefaultTable.DefaultTable.__init__(self, tag)
29        self.data = {}
30
31    def decompile(self, data, ttFont):
32        headerSize = sstruct.calcsize(META_HEADER_FORMAT)
33        header = sstruct.unpack(META_HEADER_FORMAT, data[0:headerSize])
34        if header["version"] != 1:
35            raise TTLibError("unsupported 'meta' version %d" % header["version"])
36        dataMapSize = sstruct.calcsize(DATA_MAP_FORMAT)
37        for i in range(header["numDataMaps"]):
38            dataMapOffset = headerSize + i * dataMapSize
39            dataMap = sstruct.unpack(
40                DATA_MAP_FORMAT, data[dataMapOffset : dataMapOffset + dataMapSize]
41            )
42            tag = dataMap["tag"]
43            offset = dataMap["dataOffset"]
44            self.data[tag] = data[offset : offset + dataMap["dataLength"]]
45            if tag in ["dlng", "slng"]:
46                self.data[tag] = self.data[tag].decode("utf-8")
47
48    def compile(self, ttFont):
49        keys = sorted(self.data.keys())
50        headerSize = sstruct.calcsize(META_HEADER_FORMAT)
51        dataOffset = headerSize + len(keys) * sstruct.calcsize(DATA_MAP_FORMAT)
52        header = sstruct.pack(
53            META_HEADER_FORMAT,
54            {
55                "version": 1,
56                "flags": 0,
57                "dataOffset": dataOffset,
58                "numDataMaps": len(keys),
59            },
60        )
61        dataMaps = []
62        dataBlocks = []
63        for tag in keys:
64            if tag in ["dlng", "slng"]:
65                data = self.data[tag].encode("utf-8")
66            else:
67                data = self.data[tag]
68            dataMaps.append(
69                sstruct.pack(
70                    DATA_MAP_FORMAT,
71                    {"tag": tag, "dataOffset": dataOffset, "dataLength": len(data)},
72                )
73            )
74            dataBlocks.append(data)
75            dataOffset += len(data)
76        return bytesjoin([header] + dataMaps + dataBlocks)
77
78    def toXML(self, writer, ttFont):
79        for tag in sorted(self.data.keys()):
80            if tag in ["dlng", "slng"]:
81                writer.begintag("text", tag=tag)
82                writer.newline()
83                writer.write(self.data[tag])
84                writer.newline()
85                writer.endtag("text")
86                writer.newline()
87            else:
88                writer.begintag("hexdata", tag=tag)
89                writer.newline()
90                data = self.data[tag]
91                if min(data) >= 0x20 and max(data) <= 0x7E:
92                    writer.comment("ascii: " + data.decode("ascii"))
93                    writer.newline()
94                writer.dumphex(data)
95                writer.endtag("hexdata")
96                writer.newline()
97
98    def fromXML(self, name, attrs, content, ttFont):
99        if name == "hexdata":
100            self.data[attrs["tag"]] = readHex(content)
101        elif name == "text" and attrs["tag"] in ["dlng", "slng"]:
102            self.data[attrs["tag"]] = strjoin(content).strip()
103        else:
104            raise TTLibError("can't handle '%s' element" % name)
105