def compile(self, ttFont): keys = sorted(self.data.keys()) headerSize = sstruct.calcsize(META_HEADER_FORMAT) dataOffset = headerSize + len(keys) * sstruct.calcsize(DATA_MAP_FORMAT) header = sstruct.pack(META_HEADER_FORMAT, { "version": 1, "flags": 0, "dataOffset": dataOffset, "numDataMaps": len(keys) }) dataMaps = [] dataBlocks = [] for tag in keys: if tag in ["dlng", "slng"]: data = self.data[tag].encode("utf-8") else: data = self.data[tag] dataMaps.append(sstruct.pack(DATA_MAP_FORMAT, { "tag": tag, "dataOffset": dataOffset, "dataLength": len(data) })) dataBlocks.append(data) dataOffset += len(data) return bytesjoin([header] + dataMaps + dataBlocks)
def compile(self, ttFont): self.updateFirstAndLastCharIndex(ttFont) panose = self.panose head = ttFont["head"] if (self.fsSelection & 1) and not (head.macStyle & 1<<1): log.warning("fsSelection bit 0 (italic) and " "head table macStyle bit 1 (italic) should match") if (self.fsSelection & 1<<5) and not (head.macStyle & 1): log.warning("fsSelection bit 5 (bold) and " "head table macStyle bit 0 (bold) should match") if (self.fsSelection & 1<<6) and (self.fsSelection & 1 + (1<<5)): log.warning("fsSelection bit 6 (regular) is set, " "bits 0 (italic) and 5 (bold) must be clear") if self.version < 4 and self.fsSelection & 0b1110000000: log.warning("fsSelection bits 7, 8 and 9 are only defined in " "OS/2 table version 4 and up: version %s", self.version) self.panose = sstruct.pack(panoseFormat, self.panose) if self.version == 0: data = sstruct.pack(OS2_format_0, self) elif self.version == 1: data = sstruct.pack(OS2_format_1, self) elif self.version in (2, 3, 4): data = sstruct.pack(OS2_format_2, self) elif self.version == 5: d = self.__dict__.copy() d['usLowerOpticalPointSize'] = round(self.usLowerOpticalPointSize * 20) d['usUpperOpticalPointSize'] = round(self.usUpperOpticalPointSize * 20) data = sstruct.pack(OS2_format_5, d) else: from fontemon_blender_addon.fontTools import ttLib raise ttLib.TTLibError("unknown format for OS/2 table: version %s" % self.version) self.panose = panose return data
def compile(self, ttFont): self.glyphDataOffsets = b"" self.bitmapData = b"" glyphOrder = ttFont.getGlyphOrder() # first glyph starts right after the header currentGlyphDataOffset = sbixStrikeHeaderFormatSize + sbixGlyphDataOffsetFormatSize * (len(glyphOrder) + 1) for glyphName in glyphOrder: if glyphName in self.glyphs: # we have glyph data for this glyph current_glyph = self.glyphs[glyphName] else: # must add empty glyph data record for this glyph current_glyph = Glyph(glyphName=glyphName) current_glyph.compile(ttFont) current_glyph.glyphDataOffset = currentGlyphDataOffset self.bitmapData += current_glyph.rawdata currentGlyphDataOffset += len(current_glyph.rawdata) self.glyphDataOffsets += sstruct.pack(sbixGlyphDataOffsetFormat, current_glyph) # add last "offset", really the end address of the last glyph data record dummy = Glyph() dummy.glyphDataOffset = currentGlyphDataOffset self.glyphDataOffsets += sstruct.pack(sbixGlyphDataOffsetFormat, dummy) # pack header self.data = sstruct.pack(sbixStrikeHeaderFormat, self) # add offsets and image data after header self.data += self.glyphDataOffsets + self.bitmapData
def compile(self, ttFont): dataList = [] dataList.append(sstruct.pack(bigGlyphMetricsFormat, self.metrics)) dataList.append(struct.pack(">H", len(self.componentArray))) for curComponent in self.componentArray: curComponent.glyphCode = ttFont.getGlyphID(curComponent.name) dataList.append(sstruct.pack(ebdtComponentFormat, curComponent)) return bytesjoin(dataList)
def compileAttributes3(self, attrs): if self.hasOctaboxes: o = attrs.octabox data = sstruct.pack(Glat_format_3_octabox_metrics, o) numsub = bin(o.subboxBitmap).count("1") for b in range(numsub): data += sstruct.pack(Glat_format_3_subbox_entry, o.subboxes[b]) else: data = "" return data + self.compileAttributes12(attrs, Glat_format_23_entry)
def compile(self, ttFont): if 'glyf' in ttFont: if ttFont.isLoaded('glyf') and ttFont.recalcBBoxes: self.recalc(ttFont) else: pass # CFF self.numGlyphs = len(ttFont.getGlyphOrder()) if self.tableVersion != 0x00005000: self.tableVersion = 0x00010000 data = sstruct.pack(maxpFormat_0_5, self) if self.tableVersion == 0x00010000: data = data + sstruct.pack(maxpFormat_1_0_add, self) return data
def compile(self, ttFont): if ttFont.recalcBBoxes: # For TT-flavored fonts, xMin, yMin, xMax and yMax are set in table__m_a_x_p.recalc(). if 'CFF ' in ttFont: topDict = ttFont['CFF '].cff.topDictIndex[0] self.xMin, self.yMin, self.xMax, self.yMax = intRect( topDict.FontBBox) elif 'CFF2' in ttFont: topDict = ttFont['CFF2'].cff.topDictIndex[0] charStrings = topDict.CharStrings fontBBox = None for charString in charStrings.values(): bounds = charString.calcBounds(charStrings) if bounds is not None: if fontBBox is not None: fontBBox = unionRect(fontBBox, bounds) else: fontBBox = bounds if fontBBox is not None: self.xMin, self.yMin, self.xMax, self.yMax = intRect( fontBBox) if ttFont.recalcTimestamp: self.modified = timestampNow() data = sstruct.pack(headFormat, self) return data
def _calcMasterChecksum(self, directory): # calculate checkSumAdjustment tags = list(self.tables.keys()) checksums = [] for i in range(len(tags)): checksums.append(self.tables[tags[i]].checkSum) if self.DirectoryEntry != SFNTDirectoryEntry: # Create a SFNT directory for checksum calculation purposes from fontemon_blender_addon.fontTools.ttLib import getSearchRange self.searchRange, self.entrySelector, self.rangeShift = getSearchRange(self.numTables, 16) directory = sstruct.pack(sfntDirectoryFormat, self) tables = sorted(self.tables.items()) for tag, entry in tables: sfntEntry = SFNTDirectoryEntry() sfntEntry.tag = entry.tag sfntEntry.checkSum = entry.checkSum sfntEntry.offset = entry.origOffset sfntEntry.length = entry.origLength directory = directory + sfntEntry.toString() directory_end = sfntDirectorySize + len(self.tables) * sfntDirectoryEntrySize assert directory_end == len(directory) checksums.append(calcChecksum(directory)) checksum = sum(checksums) & 0xffffffff # BiboAfba! checksumadjustment = (0xB1B0AFBA - checksum) & 0xffffffff return checksumadjustment
def compile(self, ttFont): axisTags = [axis.axisTag for axis in ttFont["fvar"].axes] sharedTuples = tv.compileSharedTuples( axisTags, itertools.chain(*self.variations.values())) sharedTupleIndices = {coord: i for i, coord in enumerate(sharedTuples)} sharedTupleSize = sum([len(c) for c in sharedTuples]) compiledGlyphs = self.compileGlyphs_(ttFont, axisTags, sharedTupleIndices) offset = 0 offsets = [] for glyph in compiledGlyphs: offsets.append(offset) offset += len(glyph) offsets.append(offset) compiledOffsets, tableFormat = self.compileOffsets_(offsets) header = {} header["version"] = self.version header["reserved"] = self.reserved header["axisCount"] = len(axisTags) header["sharedTupleCount"] = len(sharedTuples) header["offsetToSharedTuples"] = GVAR_HEADER_SIZE + len( compiledOffsets) header["glyphCount"] = len(compiledGlyphs) header["flags"] = tableFormat header["offsetToGlyphVariationData"] = header[ "offsetToSharedTuples"] + sharedTupleSize compiledHeader = sstruct.pack(GVAR_HEADER_FORMAT, header) result = [compiledHeader, compiledOffsets] result.extend(sharedTuples) result.extend(compiledGlyphs) return bytesjoin(result)
def compile(self, ttFont): fdat = b"" vdat = b"" offset = 0 for f, v in sorted(self.features.items(), key=lambda x: x[1].index): fnum = grUtils.tag2num(f) if self.version >= 2.0: fdat += struct.pack(">LHHLHH", grUtils.tag2num(f), len(v.settings), 0, offset * 4 + 12 + 16 * len(self.features), v.flags, v.label) elif fnum > 65535: # self healing for alphabetic ids self.version = 2.0 return self.compile(ttFont) else: fdat += struct.pack(">HHLHH", grUtils.tag2num(f), len(v.settings), offset * 4 + 12 + 12 * len(self.features), v.flags, v.label) for s, l in sorted(v.settings.items(), key=lambda x: (-1, x[1]) if x[0] == v.default else x): vdat += struct.pack(">HH", s, l) offset += len(v.settings) hdr = sstruct.pack(Feat_hdr_format, self) return hdr + struct.pack('>HHL', len(self.features), 0, 0) + fdat + vdat
def compile(self, ttFont): self.numGMAPs = len(self.GMAPs) self.numGlyplets = len(self.glyphlets) GMAPoffsets = [0] * (self.numGMAPs + 1) glyphletOffsets = [0] * (self.numGlyplets + 1) dataList = [sstruct.pack(GPKGFormat, self)] pos = len( dataList[0]) + (self.numGMAPs + 1) * 4 + (self.numGlyplets + 1) * 4 GMAPoffsets[0] = pos for i in range(1, self.numGMAPs + 1): pos += len(self.GMAPs[i - 1]) GMAPoffsets[i] = pos gmapArray = array.array("I", GMAPoffsets) if sys.byteorder != "big": gmapArray.byteswap() dataList.append(gmapArray.tobytes()) glyphletOffsets[0] = pos for i in range(1, self.numGlyplets + 1): pos += len(self.glyphlets[i - 1]) glyphletOffsets[i] = pos glyphletArray = array.array("I", glyphletOffsets) if sys.byteorder != "big": glyphletArray.byteswap() dataList.append(glyphletArray.tobytes()) dataList += self.GMAPs dataList += self.glyphlets data = bytesjoin(dataList) return data
def compile(self, ttFont, version=2.0): self.numPasses = len(self.passes) self.numJLevels = len(self.jLevels) self.numCritFeatures = len(self.critFeatures) numPseudo = len(self.pMap) data = b"" if version >= 3.0: hdroffset = sstruct.calcsize(Silf_part1_format_v3) else: hdroffset = 0 data += sstruct.pack(Silf_part1_format, self) for j in self.jLevels: data += sstruct.pack(Silf_justify_format, j) data += sstruct.pack(Silf_part2_format, self) if self.numCritFeatures: data += struct.pack((">%dH" % self.numCritFeaturs), *self.critFeatures) data += struct.pack("BB", 0, len(self.scriptTags)) if len(self.scriptTags): tdata = [ struct.pack("4s", x.encode("ascii")) for x in self.scriptTags ] data += b"".join(tdata) data += struct.pack(">H", self.lbGID) self.passOffset = len(data) data1 = grUtils.bininfo(numPseudo, 6) currpos = hdroffset + len(data) + 4 * (self.numPasses + 1) self.pseudosOffset = currpos + len(data1) for u, p in sorted(self.pMap.items()): data1 += struct.pack((">LH" if version >= 3.0 else ">HH"), u, ttFont.getGlyphID(p)) data1 += self.classes.compile(ttFont, version) currpos += len(data1) data2 = b"" datao = b"" for i, p in enumerate(self.passes): base = currpos + len(data2) datao += struct.pack(">L", base) data2 += p.compile(ttFont, base, version) datao += struct.pack(">L", currpos + len(data2)) if version >= 3.0: data3 = sstruct.pack(Silf_part1_format_v3, self) else: data3 = b"" return data3 + data + datao + data1 + data2
def compile(self, ttFont): if self.UV is None: self.UV = 0 nameLen = len(self.name) if nameLen < 32: self.name = self.name + "\0"*(32 - nameLen) data = sstruct.pack(GMAPRecordFormat1, self) return data
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)
def compile(self, ttFont): self.numSilf = len(self.silfs) if self.version < 3.0: hdr = sstruct.pack(Silf_hdr_format, self) hdr += struct.pack(">HH", self.numSilf, 0) else: hdr = sstruct.pack(Silf_hdr_format_3, self) offset = len(hdr) + 4 * self.numSilf data = b"" for s in self.silfs: hdr += struct.pack(">L", offset) subdata = s.compile(ttFont, self.version) offset += len(subdata) data += subdata if self.version >= 5.0: return grUtils.compress(self.scheme, hdr + data) return hdr + data
def compile(self, ttFont): if ttFont.recalcBBoxes and (ttFont.isLoaded('glyf') or ttFont.isLoaded('CFF ') or ttFont.isLoaded('CFF2')): # LFDS modified pass # self.recalc(ttFont) self.tableVersion = fi2ve(self.tableVersion) return sstruct.pack(hheaFormat, self)
def compile(self, ttFont): sbixData = b"" self.numStrikes = len(self.strikes) sbixHeader = sstruct.pack(sbixHeaderFormat, self) # calculate offset to start of first strike setOffset = sbixHeaderFormatSize + sbixStrikeOffsetFormatSize * self.numStrikes for si in sorted(self.strikes.keys()): current_strike = self.strikes[si] current_strike.compile(ttFont) # append offset to this strike to table header current_strike.strikeOffset = setOffset sbixHeader += sstruct.pack(sbixStrikeOffsetFormat, current_strike) setOffset += len(current_strike.data) sbixData += current_strike.data return sbixHeader + sbixData
def compile(self, ttFont): self.recordsCount = len(self.gmapRecords) self.fontNameLength = len(self.psFontName) self.recordsOffset = 4 * (((self.fontNameLength + 12) + 3) // 4) data = sstruct.pack(GMAPFormat, self) data = data + tobytes(self.psFontName) data = data + b"\0" * (self.recordsOffset - len(data)) for record in self.gmapRecords: data = data + record.compile(ttFont) return data
def writeTTCHeader(file, numFonts): self = SimpleNamespace() self.TTCTag = 'ttcf' self.Version = 0x00010000 self.numFonts = numFonts file.seek(0) file.write(sstruct.pack(ttcHeaderFormat, self)) offset = file.tell() file.write(struct.pack(">%dL" % self.numFonts, *([0] * self.numFonts))) return offset
def compile(self, ttFont): self.imageDataOffset = min(next(iter(zip(*self.locations)))) dataList = [EblcIndexSubTable.compile(self, ttFont)] dataList.append(struct.pack(">L", self.imageSize)) dataList.append(sstruct.pack(bigGlyphMetricsFormat, self.metrics)) glyphIds = list(map(ttFont.getGlyphID, self.names)) dataList.append(struct.pack(">L", len(glyphIds))) dataList += [struct.pack(">H", curId) for curId in glyphIds] if len(glyphIds) % 2 == 1: dataList.append(struct.pack(">H", 0)) return bytesjoin(dataList)
def compile(self, ttFont): glyphIds = list(map(ttFont.getGlyphID, self.names)) # Make sure all the ids are consecutive. This is required by Format 2. assert glyphIds == list( range(self.firstGlyphIndex, self.lastGlyphIndex + 1)), "Format 2 ids must be consecutive." self.imageDataOffset = min(next(iter(zip(*self.locations)))) dataList = [EblcIndexSubTable.compile(self, ttFont)] dataList.append(struct.pack(">L", self.imageSize)) dataList.append(sstruct.pack(bigGlyphMetricsFormat, self.metrics)) return bytesjoin(dataList)
def compile(self, ttFont): if not (self.version == 0 or self.version == 1): from fontemon_blender_addon.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
def compile(self, ttFont): if self.glyphName is None: from fontemon_blender_addon.fontTools import ttLib raise ttLib.TTLibError("Can't compile Glyph without glyph name") # TODO: if ttFont has no maxp, cmap etc., ignore glyph names and compile by index? # (needed if you just want to compile the sbix table on its own) self.gid = struct.pack(">H", ttFont.getGlyphID(self.glyphName)) if self.graphicType is None: self.rawdata = b"" else: self.rawdata = sstruct.pack(sbixGlyphHeaderFormat, self) + self.imageData
def compile(self, ttFont): d = self.__dict__.copy() d["nameLength"] = bytechr(len(self.baseGlyphName)) d["uniqueName"] = self.compilecompileUniqueName(self.uniqueName, 28) METAMD5List = eval(self.METAMD5) d["METAMD5"] = b"" for val in METAMD5List: d["METAMD5"] += bytechr(val) assert (len(d["METAMD5"]) == 16 ), "Failed to pack 16 byte MD5 hash in SING table" data = sstruct.pack(SINGFormat, d) data = data + tobytes(self.baseGlyphName) return data
def compile(self, ttFont): packed = sstruct.pack(DSIG_HeaderFormat, self) headers = [packed] offset = len( packed) + self.usNumSigs * sstruct.calcsize(DSIG_SignatureFormat) data = [] for sigrec in self.signatureRecords: # first pack signature block sigrec.cbSignature = len(sigrec.pkcs7) packed = sstruct.pack(DSIG_SignatureBlockFormat, sigrec) + sigrec.pkcs7 data.append(packed) # update redundant length field sigrec.ulLength = len(packed) # update running table offset sigrec.ulOffset = offset headers.append(sstruct.pack(DSIG_SignatureFormat, sigrec)) offset += sigrec.ulLength if offset % 2: # Pad to even bytes data.append(b'\0') return bytesjoin(headers + data)
def compile(self, ttFont): ldat = b"" fdat = b"" offset = len(self.langs) for c, inf in sorted(self.langs.items()): ldat += struct.pack(">4sHH", c.encode('utf8'), len(inf), 8 * offset + 20) for fid, val in inf: fdat += struct.pack(">LHH", fid, val, 0) offset += len(inf) ldat += struct.pack(">LHH", 0x80808080, 0, 8 * offset + 20) return sstruct.pack(Sill_hdr, self) + grUtils.bininfo(len(self.langs)) + \ ldat + fdat
def compile(self, ttFont, base, version=2.0): # build it all up backwards oActions = reduce(lambda a, x: (a[0] + len(x), a[1] + [a[0]]), self.actions + [b""], (0, []))[1] oConstraints = reduce(lambda a, x: (a[0] + len(x), a[1] + [a[0]]), self.ruleConstraints + [b""], (1, []))[1] constraintCode = b"\000" + b"".join(self.ruleConstraints) transes = [] for t in self.stateTrans: if sys.byteorder != "big": t.byteswap() transes.append(t.tobytes()) if sys.byteorder != "big": t.byteswap() if not len(transes): self.startStates = [0] oRuleMap = reduce(lambda a, x: (a[0] + len(x), a[1] + [a[0]]), self.rules + [[]], (0, []))[1] passRanges = [] gidcolmap = dict([(ttFont.getGlyphID(x[0]), x[1]) for x in self.colMap.items()]) for e in grUtils.entries(gidcolmap, sameval=True): if e[1]: passRanges.append((e[0], e[0] + e[1] - 1, e[2][0])) self.numRules = len(self.actions) self.fsmOffset = (sstruct.calcsize(Silf_pass_format) + 8 + len(passRanges) * 6 + len(oRuleMap) * 2 + 2 * oRuleMap[-1] + 2 + 2 * len(self.startStates) + 3 * self.numRules + 3 + 4 * self.numRules + 4) self.pcCode = self.fsmOffset + 2 * self.numTransitional * self.numColumns + 1 + base self.rcCode = self.pcCode + len(self.passConstraints) self.aCode = self.rcCode + len(constraintCode) self.oDebug = 0 # now generate output data = sstruct.pack(Silf_pass_format, self) data += grUtils.bininfo(len(passRanges), 6) data += b"".join(struct.pack(">3H", *p) for p in passRanges) data += struct.pack((">%dH" % len(oRuleMap)), *oRuleMap) flatrules = reduce(lambda a, x: a + x, self.rules, []) data += struct.pack((">%dH" % oRuleMap[-1]), *flatrules) data += struct.pack("BB", self.minRulePreContext, self.maxRulePreContext) data += struct.pack((">%dH" % len(self.startStates)), *self.startStates) data += struct.pack((">%dH" % self.numRules), *self.ruleSortKeys) data += struct.pack(("%dB" % self.numRules), *self.rulePreContexts) data += struct.pack(">BH", self.collisionThreshold, len(self.passConstraints)) data += struct.pack((">%dH" % (self.numRules + 1)), *oConstraints) data += struct.pack((">%dH" % (self.numRules + 1)), *oActions) return data + b"".join(transes) + struct.pack("B", 0) + \ self.passConstraints + constraintCode + b"".join(self.actions)
def compile(self, ttFont): dataList = [] dataList.append(sstruct.pack(ebdtTableVersionFormat, self)) dataSize = len(dataList[0]) # Keep a dict of glyphs that have been seen so they aren't remade. # This dict maps the id of the BitmapGlyph to the interval # in the data. glyphDict = {} # Go through the bitmap glyph data. Just in case the data for a glyph # changed the size metrics should be recalculated. There are a variety # of formats and they get stored in the EBLC table. That is why # recalculation is defered to the EblcIndexSubTable class and just # pass what is known about bitmap glyphs from this particular table. locator = ttFont[self.__class__.locatorName] for curStrike, curGlyphDict in zip(locator.strikes, self.strikeData): for curIndexSubTable in curStrike.indexSubTables: dataLocations = [] for curName in curIndexSubTable.names: # Handle the data placement based on seeing the glyph or not. # Just save a reference to the location if the glyph has already # been saved in compile. This code assumes that glyphs will only # be referenced multiple times from indexFormat5. By luck the # code may still work when referencing poorly ordered fonts with # duplicate references. If there is a font that is unlucky the # respective compile methods for the indexSubTables will fail # their assertions. All fonts seem to follow this assumption. # More complicated packing may be needed if a counter-font exists. glyph = curGlyphDict[curName] objectId = id(glyph) if objectId not in glyphDict: data = glyph.compile(ttFont) data = curIndexSubTable.padBitmapData(data) startByte = dataSize dataSize += len(data) endByte = dataSize dataList.append(data) dataLoc = (startByte, endByte) glyphDict[objectId] = dataLoc else: dataLoc = glyphDict[objectId] dataLocations.append(dataLoc) # Just use the new data locations in the indexSubTable. # The respective compile implementations will take care # of any of the problems in the convertion that may arise. curIndexSubTable.locations = dataLocations return bytesjoin(dataList)
def compile(self, ttFont): data = sstruct.pack(Glat_format_0, self) if self.version <= 1.9: encoder = partial(self.compileAttributes12, fmt=Glat_format_1_entry) elif self.version <= 2.9: encoder = partial(self.compileAttributes12, fmt=Glat_format_1_entry) elif self.version >= 3.0: self.compression = (self.scheme << 27) + (1 if self.hasOctaboxes else 0) data = sstruct.pack(Glat_format_3, self) encoder = self.compileAttributes3 glocs = [] for n in range(len(self.attributes)): glocs.append(len(data)) data += encoder(self.attributes[ttFont.getGlyphName(n)]) glocs.append(len(data)) ttFont['Gloc'].set(glocs) if self.version >= 3.0: data = grUtils.compress(self.scheme, data) return data
def compile(self, ttFont, useSharedPoints=False): tupleVariationCount, tuples, data = compileTupleVariationStore( variations=[v for v in self.variations if v.hasImpact()], pointCount=len(ttFont["cvt "].values), axisTags=[axis.axisTag for axis in ttFont["fvar"].axes], sharedTupleIndices={}, useSharedPoints=useSharedPoints) header = { "majorVersion": self.majorVersion, "minorVersion": self.minorVersion, "tupleVariationCount": tupleVariationCount, "offsetToData": CVAR_HEADER_SIZE + len(tuples), } return bytesjoin( [sstruct.pack(CVAR_HEADER_FORMAT, header), tuples, data])