Otherwise dumping avar table to TTX after it's been generated by varLib could produce different float strings than when dumping the same avar table after compiling and decompiling.
102 lines
3.9 KiB
Python
102 lines
3.9 KiB
Python
from __future__ import print_function, division, absolute_import
|
|
from fontTools.misc.py23 import *
|
|
from fontTools import ttLib
|
|
from fontTools.misc import sstruct
|
|
from fontTools.misc.fixedTools import fixedToFloat, floatToFixed
|
|
from fontTools.misc.textTools import safeEval
|
|
from fontTools.ttLib import TTLibError
|
|
from . import DefaultTable
|
|
import array
|
|
import struct
|
|
import logging
|
|
|
|
|
|
log = logging.getLogger(__name__)
|
|
|
|
# Apple's documentation of 'avar':
|
|
# https://developer.apple.com/fonts/TrueType-Reference-Manual/RM06/Chap6avar.html
|
|
|
|
AVAR_HEADER_FORMAT = """
|
|
> # big endian
|
|
majorVersion: H
|
|
minorVersion: H
|
|
reserved: H
|
|
axisCount: H
|
|
"""
|
|
assert sstruct.calcsize(AVAR_HEADER_FORMAT) == 8, sstruct.calcsize(AVAR_HEADER_FORMAT)
|
|
|
|
|
|
class table__a_v_a_r(DefaultTable.DefaultTable):
|
|
|
|
dependencies = ["fvar"]
|
|
|
|
def __init__(self, tag=None):
|
|
DefaultTable.DefaultTable.__init__(self, tag)
|
|
self.segments = {}
|
|
|
|
def compile(self, ttFont):
|
|
axisTags = [axis.axisTag for axis in ttFont["fvar"].axes]
|
|
header = {
|
|
"majorVersion": 1,
|
|
"minorVersion": 0,
|
|
"reserved": 0,
|
|
"axisCount": len(axisTags)
|
|
}
|
|
result = [sstruct.pack(AVAR_HEADER_FORMAT, header)]
|
|
for axis in axisTags:
|
|
mappings = sorted(self.segments[axis].items())
|
|
result.append(struct.pack(">H", len(mappings)))
|
|
for key, value in mappings:
|
|
fixedKey = floatToFixed(key, 14)
|
|
fixedValue = floatToFixed(value, 14)
|
|
result.append(struct.pack(">hh", fixedKey, fixedValue))
|
|
return bytesjoin(result)
|
|
|
|
def decompile(self, data, ttFont):
|
|
axisTags = [axis.axisTag for axis in ttFont["fvar"].axes]
|
|
header = {}
|
|
headerSize = sstruct.calcsize(AVAR_HEADER_FORMAT)
|
|
header = sstruct.unpack(AVAR_HEADER_FORMAT, data[0:headerSize])
|
|
majorVersion = header["majorVersion"]
|
|
if majorVersion != 1:
|
|
raise TTLibError("unsupported 'avar' version %d" % majorVersion)
|
|
pos = headerSize
|
|
for axis in axisTags:
|
|
segments = self.segments[axis] = {}
|
|
numPairs = struct.unpack(">H", data[pos:pos+2])[0]
|
|
pos = pos + 2
|
|
for _ in range(numPairs):
|
|
fromValue, toValue = struct.unpack(">hh", data[pos:pos+4])
|
|
segments[fixedToFloat(fromValue, 14)] = fixedToFloat(toValue, 14)
|
|
pos = pos + 4
|
|
|
|
def toXML(self, writer, ttFont, progress=None):
|
|
axisTags = [axis.axisTag for axis in ttFont["fvar"].axes]
|
|
for axis in axisTags:
|
|
writer.begintag("segment", axis=axis)
|
|
writer.newline()
|
|
for key, value in sorted(self.segments[axis].items()):
|
|
# roundtrip float -> fixed -> float to normalize TTX output
|
|
# as dumped after decompiling or straight from varLib
|
|
key = fixedToFloat(floatToFixed(key, 14), 14)
|
|
value = fixedToFloat(floatToFixed(value, 14), 14)
|
|
writer.simpletag("mapping", **{"from": key, "to": value})
|
|
writer.newline()
|
|
writer.endtag("segment")
|
|
writer.newline()
|
|
|
|
def fromXML(self, name, attrs, content, ttFont):
|
|
if name == "segment":
|
|
axis = attrs["axis"]
|
|
segment = self.segments[axis] = {}
|
|
for element in content:
|
|
if isinstance(element, tuple):
|
|
elementName, elementAttrs, _ = element
|
|
if elementName == "mapping":
|
|
fromValue = safeEval(elementAttrs["from"])
|
|
toValue = safeEval(elementAttrs["to"])
|
|
if fromValue in segment:
|
|
log.warning("duplicate entry for %s in axis '%s'",
|
|
fromValue, axis)
|
|
segment[fromValue] = toValue
|