Source code for fontTools.ttLib.tables.V_D_M_X_

from . import DefaultTable
from fontTools.misc import sstruct
from fontTools.misc.textTools import safeEval
import struct

VDMX_HeaderFmt = """
	>                 # big endian
	version:     H    # Version number (0 or 1)
	numRecs:     H    # Number of VDMX groups present
	numRatios:   H    # Number of aspect ratio groupings
"""
# the VMDX header is followed by an array of RatRange[numRatios] (i.e. aspect
# ratio ranges);
VDMX_RatRangeFmt = """
	>                 # big endian
	bCharSet:    B    # Character set
	xRatio:      B    # Value to use for x-Ratio
	yStartRatio: B    # Starting y-Ratio value
	yEndRatio:   B    # Ending y-Ratio value
"""
# followed by an array of offset[numRatios] from start of VDMX table to the
# VDMX Group for this ratio range (offsets will be re-calculated on compile);
# followed by an array of Group[numRecs] records;
VDMX_GroupFmt = """
	>                 # big endian
	recs:        H    # Number of height records in this group
	startsz:     B    # Starting yPelHeight
	endsz:       B    # Ending yPelHeight
"""
# followed by an array of vTable[recs] records.
VDMX_vTableFmt = """
	>                 # big endian
	yPelHeight:  H    # yPelHeight to which values apply
	yMax:        h    # Maximum value (in pels) for this yPelHeight
	yMin:        h    # Minimum value (in pels) for this yPelHeight
"""


[docs] class table_V_D_M_X_(DefaultTable.DefaultTable):
[docs] def decompile(self, data, ttFont): pos = 0 # track current position from to start of VDMX table dummy, data = sstruct.unpack2(VDMX_HeaderFmt, data, self) pos += sstruct.calcsize(VDMX_HeaderFmt) self.ratRanges = [] for i in range(self.numRatios): ratio, data = sstruct.unpack2(VDMX_RatRangeFmt, data) pos += sstruct.calcsize(VDMX_RatRangeFmt) # the mapping between a ratio and a group is defined further below ratio["groupIndex"] = None self.ratRanges.append(ratio) lenOffset = struct.calcsize(">H") _offsets = [] # temporarily store offsets to groups for i in range(self.numRatios): offset = struct.unpack(">H", data[0:lenOffset])[0] data = data[lenOffset:] pos += lenOffset _offsets.append(offset) self.groups = [] for groupIndex in range(self.numRecs): # the offset to this group from beginning of the VDMX table currOffset = pos group, data = sstruct.unpack2(VDMX_GroupFmt, data) # the group lenght and bounding sizes are re-calculated on compile recs = group.pop("recs") startsz = group.pop("startsz") endsz = group.pop("endsz") pos += sstruct.calcsize(VDMX_GroupFmt) for j in range(recs): vTable, data = sstruct.unpack2(VDMX_vTableFmt, data) vTableLength = sstruct.calcsize(VDMX_vTableFmt) pos += vTableLength # group is a dict of (yMax, yMin) tuples keyed by yPelHeight group[vTable["yPelHeight"]] = (vTable["yMax"], vTable["yMin"]) # make sure startsz and endsz match the calculated values minSize = min(group.keys()) maxSize = max(group.keys()) assert ( startsz == minSize ), "startsz (%s) must equal min yPelHeight (%s): group %d" % ( group.startsz, minSize, groupIndex, ) assert ( endsz == maxSize ), "endsz (%s) must equal max yPelHeight (%s): group %d" % ( group.endsz, maxSize, groupIndex, ) self.groups.append(group) # match the defined offsets with the current group's offset for offsetIndex, offsetValue in enumerate(_offsets): # when numRecs < numRatios there can more than one ratio range # sharing the same VDMX group if currOffset == offsetValue: # map the group with the ratio range thas has the same # index as the offset to that group (it took me a while..) self.ratRanges[offsetIndex]["groupIndex"] = groupIndex # check that all ratio ranges have a group for i in range(self.numRatios): ratio = self.ratRanges[i] if ratio["groupIndex"] is None: from fontTools import ttLib raise ttLib.TTLibError("no group defined for ratRange %d" % i)
def _getOffsets(self): """ Calculate offsets to VDMX_Group records. For each ratRange return a list of offset values from the beginning of the VDMX table to a VDMX_Group. """ lenHeader = sstruct.calcsize(VDMX_HeaderFmt) lenRatRange = sstruct.calcsize(VDMX_RatRangeFmt) lenOffset = struct.calcsize(">H") lenGroupHeader = sstruct.calcsize(VDMX_GroupFmt) lenVTable = sstruct.calcsize(VDMX_vTableFmt) # offset to the first group pos = lenHeader + self.numRatios * lenRatRange + self.numRatios * lenOffset groupOffsets = [] for group in self.groups: groupOffsets.append(pos) lenGroup = lenGroupHeader + len(group) * lenVTable pos += lenGroup # offset to next group offsets = [] for ratio in self.ratRanges: groupIndex = ratio["groupIndex"] offsets.append(groupOffsets[groupIndex]) return offsets
[docs] def compile(self, ttFont): if not (self.version == 0 or self.version == 1): from fontTools import ttLib raise ttLib.TTLibError( "unknown format for VDMX table: version %s" % self.version ) data = sstruct.pack(VDMX_HeaderFmt, self) for ratio in self.ratRanges: data += sstruct.pack(VDMX_RatRangeFmt, ratio) # recalculate offsets to VDMX groups for offset in self._getOffsets(): data += struct.pack(">H", offset) for group in self.groups: recs = len(group) startsz = min(group.keys()) endsz = max(group.keys()) gHeader = {"recs": recs, "startsz": startsz, "endsz": endsz} data += sstruct.pack(VDMX_GroupFmt, gHeader) for yPelHeight, (yMax, yMin) in sorted(group.items()): vTable = {"yPelHeight": yPelHeight, "yMax": yMax, "yMin": yMin} data += sstruct.pack(VDMX_vTableFmt, vTable) return data
[docs] def toXML(self, writer, ttFont): writer.simpletag("version", value=self.version) writer.newline() writer.begintag("ratRanges") writer.newline() for ratio in self.ratRanges: groupIndex = ratio["groupIndex"] writer.simpletag( "ratRange", bCharSet=ratio["bCharSet"], xRatio=ratio["xRatio"], yStartRatio=ratio["yStartRatio"], yEndRatio=ratio["yEndRatio"], groupIndex=groupIndex, ) writer.newline() writer.endtag("ratRanges") writer.newline() writer.begintag("groups") writer.newline() for groupIndex in range(self.numRecs): group = self.groups[groupIndex] recs = len(group) startsz = min(group.keys()) endsz = max(group.keys()) writer.begintag("group", index=groupIndex) writer.newline() writer.comment("recs=%d, startsz=%d, endsz=%d" % (recs, startsz, endsz)) writer.newline() for yPelHeight, (yMax, yMin) in sorted(group.items()): writer.simpletag( "record", [("yPelHeight", yPelHeight), ("yMax", yMax), ("yMin", yMin)], ) writer.newline() writer.endtag("group") writer.newline() writer.endtag("groups") writer.newline()
[docs] def fromXML(self, name, attrs, content, ttFont): if name == "version": self.version = safeEval(attrs["value"]) elif name == "ratRanges": if not hasattr(self, "ratRanges"): self.ratRanges = [] for element in content: if not isinstance(element, tuple): continue name, attrs, content = element if name == "ratRange": if not hasattr(self, "numRatios"): self.numRatios = 1 else: self.numRatios += 1 ratio = { "bCharSet": safeEval(attrs["bCharSet"]), "xRatio": safeEval(attrs["xRatio"]), "yStartRatio": safeEval(attrs["yStartRatio"]), "yEndRatio": safeEval(attrs["yEndRatio"]), "groupIndex": safeEval(attrs["groupIndex"]), } self.ratRanges.append(ratio) elif name == "groups": if not hasattr(self, "groups"): self.groups = [] for element in content: if not isinstance(element, tuple): continue name, attrs, content = element if name == "group": if not hasattr(self, "numRecs"): self.numRecs = 1 else: self.numRecs += 1 group = {} for element in content: if not isinstance(element, tuple): continue name, attrs, content = element if name == "record": yPelHeight = safeEval(attrs["yPelHeight"]) yMax = safeEval(attrs["yMax"]) yMin = safeEval(attrs["yMin"]) group[yPelHeight] = (yMax, yMin) self.groups.append(group)