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