• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1from __future__ import print_function, division, absolute_import
2from fontTools.misc.py23 import *
3from fontTools import ttLib
4from fontTools.misc import sstruct
5from fontTools.misc.fixedTools import fixedToFloat, floatToFixed
6from fontTools.misc.textTools import safeEval
7from fontTools.ttLib import TTLibError
8from . import DefaultTable
9import array
10import struct
11import logging
12
13
14log = logging.getLogger(__name__)
15
16# Apple's documentation of 'avar':
17# https://developer.apple.com/fonts/TrueType-Reference-Manual/RM06/Chap6avar.html
18
19AVAR_HEADER_FORMAT = """
20    > # big endian
21    majorVersion:  H
22    minorVersion:  H
23    reserved:      H
24    axisCount:     H
25"""
26assert sstruct.calcsize(AVAR_HEADER_FORMAT) == 8, sstruct.calcsize(AVAR_HEADER_FORMAT)
27
28
29class table__a_v_a_r(DefaultTable.DefaultTable):
30
31    dependencies = ["fvar"]
32
33    def __init__(self, tag=None):
34        DefaultTable.DefaultTable.__init__(self, tag)
35        self.segments = {}
36
37    def compile(self, ttFont):
38        axisTags = [axis.axisTag for axis in ttFont["fvar"].axes]
39        header = {
40            "majorVersion": 1,
41            "minorVersion": 0,
42            "reserved": 0,
43            "axisCount": len(axisTags)
44        }
45        result = [sstruct.pack(AVAR_HEADER_FORMAT, header)]
46        for axis in axisTags:
47            mappings = sorted(self.segments[axis].items())
48            result.append(struct.pack(">H", len(mappings)))
49            for key, value in mappings:
50                fixedKey = floatToFixed(key, 14)
51                fixedValue = floatToFixed(value, 14)
52                result.append(struct.pack(">hh", fixedKey, fixedValue))
53        return bytesjoin(result)
54
55    def decompile(self, data, ttFont):
56        axisTags = [axis.axisTag for axis in ttFont["fvar"].axes]
57        header = {}
58        headerSize = sstruct.calcsize(AVAR_HEADER_FORMAT)
59        header = sstruct.unpack(AVAR_HEADER_FORMAT, data[0:headerSize])
60        majorVersion = header["majorVersion"]
61        if majorVersion != 1:
62            raise TTLibError("unsupported 'avar' version %d" % majorVersion)
63        pos = headerSize
64        for axis in axisTags:
65            segments = self.segments[axis] = {}
66            numPairs = struct.unpack(">H", data[pos:pos+2])[0]
67            pos = pos + 2
68            for _ in range(numPairs):
69                fromValue, toValue = struct.unpack(">hh", data[pos:pos+4])
70                segments[fixedToFloat(fromValue, 14)] = fixedToFloat(toValue, 14)
71                pos = pos + 4
72
73    def toXML(self, writer, ttFont):
74        axisTags = [axis.axisTag for axis in ttFont["fvar"].axes]
75        for axis in axisTags:
76            writer.begintag("segment", axis=axis)
77            writer.newline()
78            for key, value in sorted(self.segments[axis].items()):
79                # roundtrip float -> fixed -> float to normalize TTX output
80                # as dumped after decompiling or straight from varLib
81                key = fixedToFloat(floatToFixed(key, 14), 14)
82                value = fixedToFloat(floatToFixed(value, 14), 14)
83                writer.simpletag("mapping", **{"from": key, "to": value})
84                writer.newline()
85            writer.endtag("segment")
86            writer.newline()
87
88    def fromXML(self, name, attrs, content, ttFont):
89        if name == "segment":
90            axis = attrs["axis"]
91            segment = self.segments[axis] = {}
92            for element in content:
93                if isinstance(element, tuple):
94                    elementName, elementAttrs, _ = element
95                    if elementName == "mapping":
96                        fromValue = safeEval(elementAttrs["from"])
97                        toValue = safeEval(elementAttrs["to"])
98                        if fromValue in segment:
99                            log.warning("duplicate entry for %s in axis '%s'",
100                                        fromValue, axis)
101                        segment[fromValue] = toValue
102