Source code for fontTools.ttLib.tables._f_v_a_r

from fontTools.misc import sstruct
from fontTools.misc.fixedTools import (
    fixedToFloat as fi2fl,
    floatToFixed as fl2fi,
    floatToFixedToStr as fl2str,
    strToFixedToFloat as str2fl,
)
from fontTools.misc.textTools import Tag, bytesjoin, safeEval
from fontTools.ttLib import TTLibError
from . import DefaultTable
import struct


# Apple's documentation of 'fvar':
# https://developer.apple.com/fonts/TrueType-Reference-Manual/RM06/Chap6fvar.html

FVAR_HEADER_FORMAT = """
    > # big endian
    version:        L
    offsetToData:   H
    countSizePairs: H
    axisCount:      H
    axisSize:       H
    instanceCount:  H
    instanceSize:   H
"""

FVAR_AXIS_FORMAT = """
    > # big endian
    axisTag:        4s
    minValue:       16.16F
    defaultValue:   16.16F
    maxValue:       16.16F
    flags:          H
    axisNameID:         H
"""

FVAR_INSTANCE_FORMAT = """
    > # big endian
    subfamilyNameID:     H
    flags:      H
"""


[docs] class table__f_v_a_r(DefaultTable.DefaultTable): dependencies = ["name"] def __init__(self, tag=None): DefaultTable.DefaultTable.__init__(self, tag) self.axes = [] self.instances = []
[docs] def compile(self, ttFont): instanceSize = sstruct.calcsize(FVAR_INSTANCE_FORMAT) + (len(self.axes) * 4) includePostScriptNames = any( instance.postscriptNameID != 0xFFFF for instance in self.instances ) if includePostScriptNames: instanceSize += 2 header = { "version": 0x00010000, "offsetToData": sstruct.calcsize(FVAR_HEADER_FORMAT), "countSizePairs": 2, "axisCount": len(self.axes), "axisSize": sstruct.calcsize(FVAR_AXIS_FORMAT), "instanceCount": len(self.instances), "instanceSize": instanceSize, } result = [sstruct.pack(FVAR_HEADER_FORMAT, header)] result.extend([axis.compile() for axis in self.axes]) axisTags = [axis.axisTag for axis in self.axes] for instance in self.instances: result.append(instance.compile(axisTags, includePostScriptNames)) return bytesjoin(result)
[docs] def decompile(self, data, ttFont): header = {} headerSize = sstruct.calcsize(FVAR_HEADER_FORMAT) header = sstruct.unpack(FVAR_HEADER_FORMAT, data[0:headerSize]) if header["version"] != 0x00010000: raise TTLibError("unsupported 'fvar' version %04x" % header["version"]) pos = header["offsetToData"] axisSize = header["axisSize"] for _ in range(header["axisCount"]): axis = Axis() axis.decompile(data[pos : pos + axisSize]) self.axes.append(axis) pos += axisSize instanceSize = header["instanceSize"] axisTags = [axis.axisTag for axis in self.axes] for _ in range(header["instanceCount"]): instance = NamedInstance() instance.decompile(data[pos : pos + instanceSize], axisTags) self.instances.append(instance) pos += instanceSize
[docs] def toXML(self, writer, ttFont): for axis in self.axes: axis.toXML(writer, ttFont) for instance in self.instances: instance.toXML(writer, ttFont)
[docs] def fromXML(self, name, attrs, content, ttFont): if name == "Axis": axis = Axis() axis.fromXML(name, attrs, content, ttFont) self.axes.append(axis) elif name == "NamedInstance": instance = NamedInstance() instance.fromXML(name, attrs, content, ttFont) self.instances.append(instance)
[docs] def getAxes(self): return {a.axisTag: (a.minValue, a.defaultValue, a.maxValue) for a in self.axes}
[docs] class Axis(object): def __init__(self): self.axisTag = None self.axisNameID = 0 self.flags = 0 self.minValue = -1.0 self.defaultValue = 0.0 self.maxValue = 1.0
[docs] def compile(self): return sstruct.pack(FVAR_AXIS_FORMAT, self)
[docs] def decompile(self, data): sstruct.unpack2(FVAR_AXIS_FORMAT, data, self)
[docs] def toXML(self, writer, ttFont): name = ( ttFont["name"].getDebugName(self.axisNameID) if "name" in ttFont else None ) if name is not None: writer.newline() writer.comment(name) writer.newline() writer.begintag("Axis") writer.newline() for tag, value in [ ("AxisTag", self.axisTag), ("Flags", "0x%X" % self.flags), ("MinValue", fl2str(self.minValue, 16)), ("DefaultValue", fl2str(self.defaultValue, 16)), ("MaxValue", fl2str(self.maxValue, 16)), ("AxisNameID", str(self.axisNameID)), ]: writer.begintag(tag) writer.write(value) writer.endtag(tag) writer.newline() writer.endtag("Axis") writer.newline()
[docs] def fromXML(self, name, _attrs, content, ttFont): assert name == "Axis" for tag, _, value in filter(lambda t: type(t) is tuple, content): value = "".join(value) if tag == "AxisTag": self.axisTag = Tag(value) elif tag in {"Flags", "MinValue", "DefaultValue", "MaxValue", "AxisNameID"}: setattr( self, tag[0].lower() + tag[1:], str2fl(value, 16) if tag.endswith("Value") else safeEval(value), )
[docs] class NamedInstance(object): def __init__(self): self.subfamilyNameID = 0 self.postscriptNameID = 0xFFFF self.flags = 0 self.coordinates = {}
[docs] def compile(self, axisTags, includePostScriptName): result = [sstruct.pack(FVAR_INSTANCE_FORMAT, self)] for axis in axisTags: fixedCoord = fl2fi(self.coordinates[axis], 16) result.append(struct.pack(">l", fixedCoord)) if includePostScriptName: result.append(struct.pack(">H", self.postscriptNameID)) return bytesjoin(result)
[docs] def decompile(self, data, axisTags): sstruct.unpack2(FVAR_INSTANCE_FORMAT, data, self) pos = sstruct.calcsize(FVAR_INSTANCE_FORMAT) for axis in axisTags: value = struct.unpack(">l", data[pos : pos + 4])[0] self.coordinates[axis] = fi2fl(value, 16) pos += 4 if pos + 2 <= len(data): self.postscriptNameID = struct.unpack(">H", data[pos : pos + 2])[0] else: self.postscriptNameID = 0xFFFF
[docs] def toXML(self, writer, ttFont): name = ( ttFont["name"].getDebugName(self.subfamilyNameID) if "name" in ttFont else None ) if name is not None: writer.newline() writer.comment(name) writer.newline() psname = ( ttFont["name"].getDebugName(self.postscriptNameID) if "name" in ttFont else None ) if psname is not None: writer.comment("PostScript: " + psname) writer.newline() if self.postscriptNameID == 0xFFFF: writer.begintag( "NamedInstance", flags=("0x%X" % self.flags), subfamilyNameID=self.subfamilyNameID, ) else: writer.begintag( "NamedInstance", flags=("0x%X" % self.flags), subfamilyNameID=self.subfamilyNameID, postscriptNameID=self.postscriptNameID, ) writer.newline() for axis in ttFont["fvar"].axes: writer.simpletag( "coord", axis=axis.axisTag, value=fl2str(self.coordinates[axis.axisTag], 16), ) writer.newline() writer.endtag("NamedInstance") writer.newline()
[docs] def fromXML(self, name, attrs, content, ttFont): assert name == "NamedInstance" self.subfamilyNameID = safeEval(attrs["subfamilyNameID"]) self.flags = safeEval(attrs.get("flags", "0")) if "postscriptNameID" in attrs: self.postscriptNameID = safeEval(attrs["postscriptNameID"]) else: self.postscriptNameID = 0xFFFF for tag, elementAttrs, _ in filter(lambda t: type(t) is tuple, content): if tag == "coord": value = str2fl(elementAttrs["value"], 16) self.coordinates[elementAttrs["axis"]] = value