def compile(self, offset): nTracks = len(self) sizes = self.sizes() nSizes = len(sizes) # offset to the start of the size subtable offset += TRACK_DATA_FORMAT_SIZE + TRACK_TABLE_ENTRY_FORMAT_SIZE*nTracks trackDataHeader = sstruct.pack( TRACK_DATA_FORMAT, {'nTracks': nTracks, 'nSizes': nSizes, 'sizeTableOffset': offset}) entryDataList = [] perSizeDataList = [] # offset to per-size tracking values offset += SIZE_VALUE_FORMAT_SIZE*nSizes # sort track table entries by track value for track, entry in sorted(self.items()): assert entry.nameIndex is not None entry.track = track entry.offset = offset entryDataList += [sstruct.pack(TRACK_TABLE_ENTRY_FORMAT, entry)] # sort per-size values by size for size, value in sorted(entry.items()): perSizeDataList += [struct.pack(PER_SIZE_VALUE_FORMAT, value)] offset += PER_SIZE_VALUE_FORMAT_SIZE*nSizes # sort size values sizeDataList = [struct.pack(SIZE_VALUE_FORMAT, fl2fi(sv, 16)) for sv in sorted(sizes)] data = bytesjoin([trackDataHeader] + entryDataList + sizeDataList + perSizeDataList) return data
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 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 = "" self.bitmapData = "" 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): 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): 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): panose = self.panose 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) else: from fontTools import ttLib raise ttLib.TTLibError, "unknown format for OS/2 table: version %s" % self.version self.panose = panose return data
def transform(self, ttFont): """ Return transformed 'glyf' data """ self.numGlyphs = len(self.glyphs) if not hasattr(self, "glyphOrder"): try: self.glyphOrder = ttFont.getGlyphOrder() except: self.glyphOrder = None if self.glyphOrder is None: self.glyphOrder = [".notdef"] self.glyphOrder.extend(["glyph%.5d" % i for i in range(1, self.numGlyphs)]) if len(self.glyphOrder) != self.numGlyphs: raise TTLibError( "incorrect glyphOrder: expected %d glyphs, found %d" % (len(self.glyphOrder), self.numGlyphs)) if 'maxp' in ttFont: ttFont['maxp'].numGlyphs = self.numGlyphs self.indexFormat = ttFont['head'].indexToLocFormat for stream in self.subStreams: setattr(self, stream, b"") bboxBitmapSize = ((self.numGlyphs + 31) >> 5) << 2 self.bboxBitmap = array.array('B', [0]*bboxBitmapSize) for glyphID in range(self.numGlyphs): self._encodeGlyph(glyphID) self.bboxStream = self.bboxBitmap.tostring() + self.bboxStream for stream in self.subStreams: setattr(self, stream + 'Size', len(getattr(self, stream))) self.version = 0 data = sstruct.pack(woff2GlyfTableFormat, self) data += bytesjoin([getattr(self, s) for s in self.subStreams]) 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 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): offsetOK = 0 self.nMetaRecs = len(self.glyphRecords) count = 0 while (offsetOK != 1): count = count + 1 if count > 4: pdb_set_trace() metaData = sstruct.pack(METAHeaderFormat, self) stringRecsOffset = len(metaData) + self.nMetaRecs * (6 + 2 * (self.metaFlags & 1)) stringRecSize = (6 + 2 * (self.metaFlags & 1)) for glyphRec in self.glyphRecords: glyphRec.offset = stringRecsOffset if (glyphRec.offset > 65535) and ((self.metaFlags & 1) == 0): self.metaFlags = self.metaFlags + 1 offsetOK = -1 break metaData = metaData + glyphRec.compile(self) stringRecsOffset = stringRecsOffset + (glyphRec.nMetaEntry * stringRecSize) # this will be the String Record offset for the next GlyphRecord. if offsetOK == -1: offsetOK = 0 continue # metaData now contains the header and all of the GlyphRecords. Its length should bw # the offset to the first StringRecord. stringOffset = stringRecsOffset for glyphRec in self.glyphRecords: assert ( glyphRec.offset == len(metaData)), "Glyph record offset did not compile correctly! for rec:" + str( glyphRec) for stringRec in glyphRec.stringRecs: stringRec.offset = stringOffset if (stringRec.offset > 65535) and ((self.metaFlags & 1) == 0): self.metaFlags = self.metaFlags + 1 offsetOK = -1 break metaData = metaData + stringRec.compile(self) stringOffset = stringOffset + stringRec.stringLen if offsetOK == -1: offsetOK = 0 continue if ((self.metaFlags & 1) == 1) and (stringOffset < 65536): self.metaFlags = self.metaFlags - 1 continue else: offsetOK = 1 # metaData now contains the header and all of the GlyphRecords and all of the String Records. # Its length should be the offset to the first string datum. for glyphRec in self.glyphRecords: for stringRec in glyphRec.stringRecs: assert ( stringRec.offset == len(metaData)), "String offset did not compile correctly! for string:" + str( stringRec.string) metaData = metaData + stringRec.string return metaData
def calcHeadCheckSumAdjustment(flavor, tables): numTables = len(tables) # build the sfnt header searchRange, entrySelector, rangeShift = getSearchRange(numTables) sfntDirectoryData = dict( sfntVersion=flavor, numTables=numTables, searchRange=searchRange, entrySelector=entrySelector, rangeShift=rangeShift ) # build the sfnt directory directory = sstruct.pack(sfntDirectoryFormat, sfntDirectoryData) for tag, entry in sorted(tables.items()): entry = tables[tag] sfntEntry = SFNTDirectoryEntry() sfntEntry.tag = tag sfntEntry.checkSum = entry["checkSum"] sfntEntry.offset = entry["offset"] sfntEntry.length = entry["length"] directory += sfntEntry.toString() # calculate the checkSumAdjustment checkSums = [entry["checkSum"] for entry in tables.values()] checkSums.append(calcChecksum(directory)) checkSumAdjustment = sum(checkSums) checkSumAdjustment = (0xB1B0AFBA - checkSumAdjustment) & 0xffffffff # done return checkSumAdjustment
def _writeTableDirectory(self): if self.verbose: debugmsg("writing table directory") self.file.seek(woffHeaderSize) for tag, (index, entry, data) in sorted(self.tables.items()): entry = sstruct.pack(woffDirectoryEntryFormat, entry) self.file.write(entry)
def close(self): if self.numTables != len(self.tables): raise WOFFLibError("wrong number of tables; expected %d, found %d" % (self.numTables, len(self.tables))) # first, handle the checkSumAdjustment if self.recalculateHeadChecksum and "head" in self.tables: self._handleHeadChecksum() # check the table directory conformance for tag, (index, entry, data) in sorted(self.tables.items()): self._checkTableConformance(entry, data) # write the header header = sstruct.pack(woffHeaderFormat, self) self.file.seek(0) self.file.write(header) # update the directory offsets offset = woffHeaderSize + (woffDirectoryEntrySize * self.numTables) order = self._tableOrder() for tag in order: index, entry, data = self.tables[tag] entry.offset = offset offset += calc4BytePaddedLength(entry.compLength) # ensure byte alignment # write the directory self._writeTableDirectory() # write the table data self._writeTableData() # write the metadata self._writeMetadata() # write the private data self._writePrivateData() # write the header self._writeHeader() # go to the beginning of the file self.file.seek(0)
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.tostring()) 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.tostring()) dataList += self.GMAPs dataList += self.glyphlets data = bytesjoin(dataList) return data
def test_incorrect_compressed_size(self): data = self.file.read(woff2DirectorySize) header = sstruct.unpack(woff2DirectoryFormat, data) header['totalCompressedSize'] = 0 data = sstruct.pack(woff2DirectoryFormat, header) with self.assertRaises((brotli.error, ttLib.TTLibError)): WOFF2Reader(BytesIO(data + self.file.read()))
def compile(self, ttFont): axisTags = [axis.AxisTag for axis in ttFont["fvar"].table.VariationAxis] sharedCoords = self.compileSharedCoords_(ttFont, axisTags) sharedCoordIndices = dict([(sharedCoords[i], i) for i in range(len(sharedCoords))]) sharedCoordSize = sum([len(c) for c in sharedCoords]) compiledGlyphs = self.compileGlyphs_(ttFont, axisTags, sharedCoordIndices) 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["sharedCoordCount"] = len(sharedCoords) header["offsetToCoord"] = GVAR_HEADER_SIZE + len(compiledOffsets) header["glyphCount"] = len(compiledGlyphs) header["flags"] = tableFormat header["offsetToData"] = header["offsetToCoord"] + sharedCoordSize compiledHeader = sstruct.pack(GVAR_HEADER_FORMAT, header) result = [compiledHeader, compiledOffsets] result.extend(sharedCoords) result.extend(compiledGlyphs) return bytesjoin(result)
def test_incorrect_file_size(self): data = self.file.read(woff2DirectorySize) header = sstruct.unpack(woff2DirectoryFormat, data) header["length"] -= 1 data = sstruct.pack(woff2DirectoryFormat, header) with self.assertRaisesRegex(ttLib.TTLibError, "doesn't match the actual file size"): WOFF2Reader(BytesIO(data + self.file.read()))
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 _calcMasterChecksum(self, directory): # calculate checkSumAdjustment tags = self.tables.keys() checksums = [] for i in range(len(tags)): checksums.append(self.tables[tags[i]].checkSum) # TODO(behdad) I'm fairly sure the checksum for woff is not working correctly. # Haven't debugged. if self.DirectoryEntry != SFNTDirectoryEntry: # Create a SFNT directory for checksum calculation purposes self.searchRange, self.entrySelector, self.rangeShift = getSearchRange(self.numTables) directory = sstruct.pack(sfntDirectoryFormat, self) tables = self.tables.items() tables.sort() for tag, entry in tables: sfntEntry = SFNTDirectoryEntry() for item in ['tag', 'checkSum', 'offset', 'length']: setattr(sfntEntry, item, getattr(entry, item)) 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, parentTable): data = sstruct.pack(METAStringRecordFormat, self) if parentTable.metaFlags == 0: datum = struct.pack(">H", self.offset) elif parentTable.metaFlags == 1: datum = struct.pack(">L", self.offset) data = data + datum 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 return ''.join(headers+data)
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, axisTags, includePostScriptName): result = [sstruct.pack(FVAR_INSTANCE_FORMAT, self)] for axis in axisTags: fixedCoord = floatToFixed(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): 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, ttFont): sbixData = "" self.numSets = len(self.bitmapSets) sbixHeader = sstruct.pack(sbixHeaderFormat, self) # calculate offset to start of first bitmap set setOffset = sbixHeaderFormatSize + sbixBitmapSetOffsetFormatSize * self.numSets for si in sorted(self.bitmapSets.keys()): myBitmapSet = self.bitmapSets[si] myBitmapSet.compile(ttFont) # append offset to this bitmap set to table header myBitmapSet.offset = setOffset sbixHeader += sstruct.pack(sbixBitmapSetOffsetFormat, myBitmapSet) setOffset += sbixBitmapSetHeaderFormatSize + len(myBitmapSet.data) sbixData += myBitmapSet.data return sbixHeader + sbixData
def compile(self, ttFont): sbixData = "" 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, 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) for x in self.scriptTags] data += "".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): 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 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(zip(*self.locations)[0]) dataList = [EblcIndexSubTable.compile(self, ttFont)] dataList.append(struct.pack(">L", self.imageSize)) dataList.append(sstruct.pack(bigGlyphMetricsFormat, self.metrics)) return bytesjoin(dataList)
def packSFNT(header, directory, tableData, flavor="cff", calcCheckSum=True, applyPadding=True, sortDirectory=True, searchRange=None, entrySelector=None, rangeShift=None): # update the checkSum if calcCheckSum: if flavor == "cff": f = "OTTO" else: f = "\000\001\000\000" calcHeadCheckSumAdjustmentSFNT(directory, tableData, flavor=f) # update the header cSearchRange, cEntrySelector, cRangeShift = getSearchRange(len(directory), 16) if searchRange is None: searchRange = cSearchRange if entrySelector is None: entrySelector = cEntrySelector if rangeShift is None: rangeShift = cRangeShift if flavor == "cff": header["sfntVersion"] = "OTTO" else: header["sfntVersion"] = "\000\001\000\000" header["searchRange"] = searchRange header["entrySelector"] = entrySelector header["rangeShift"] = rangeShift # version and num tables should already be set sfntData = sstruct.pack(sfntDirectoryFormat, header) # compile the directory sfntDirectoryEntries = {} entryOrder = [] for entry in directory: sfntEntry = SFNTDirectoryEntry() sfntEntry.tag = entry["tag"] sfntEntry.checkSum = entry["checksum"] sfntEntry.offset = entry["offset"] sfntEntry.length = entry["length"] sfntDirectoryEntries[entry["tag"]] = sfntEntry entryOrder.append(entry["tag"]) if sortDirectory: entryOrder = sorted(entryOrder) for tag in entryOrder: entry = sfntDirectoryEntries[tag] sfntData += entry.toString() # compile the data directory = [(entry["offset"], entry["tag"]) for entry in directory] for o, tag in sorted(directory): data = tableData[tag] if applyPadding: data = padData(data) sfntData += data # done return sfntData
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): self.bitmapOffsets = "" self.bitmapData = "" glyphOrder = ttFont.getGlyphOrder() # first bitmap starts right after the header bitmapOffset = sbixBitmapSetHeaderFormatSize + sbixBitmapOffsetEntryFormatSize * ( len(glyphOrder) + 1) for glyphName in glyphOrder: if glyphName in self.bitmaps: # we have a bitmap for this glyph myBitmap = self.bitmaps[glyphName] else: # must add empty bitmap for this glyph myBitmap = Bitmap(glyphName=glyphName) myBitmap.compile(ttFont) myBitmap.ulOffset = bitmapOffset self.bitmapData += myBitmap.rawdata bitmapOffset += len(myBitmap.rawdata) self.bitmapOffsets += sstruct.pack(sbixBitmapOffsetEntryFormat, myBitmap) # add last "offset", really the end address of the last bitmap dummy = Bitmap() dummy.ulOffset = bitmapOffset self.bitmapOffsets += sstruct.pack(sbixBitmapOffsetEntryFormat, dummy) # bitmap sets are padded to 4 byte boundaries dataLength = len(self.bitmapOffsets) + len(self.bitmapData) if dataLength % 4 != 0: padding = 4 - (dataLength % 4) else: padding = 0 # pack header self.data = sstruct.pack(sbixBitmapSetHeaderFormat, self) # add offset, image data and padding after header self.data += self.bitmapOffsets + self.bitmapData + "\0" * padding
def compile(self, ttFont): data = sstruct.pack(postFormat, self) if self.formatType == 1.0: pass # we're done elif self.formatType == 2.0: data = data + self.encode_format_2_0(ttFont) elif self.formatType == 3.0: pass # we're done else: # supported format raise ttLib.TTLibError("'post' table format %f not supported" % self.formatType) return data
def compile(self, ttFont): panose = self.panose 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'] = int( round(self.usLowerOpticalPointSize * 20)) d['usUpperOpticalPointSize'] = int( round(self.usUpperOpticalPointSize * 20)) data = sstruct.pack(OS2_format_5, d) else: from 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): 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): if self.glyphName is None: from 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 = "" else: self.rawdata = sstruct.pack(sbixGlyphHeaderFormat, self) + self.imageData
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 group.items(): vTable = {'yPelHeight': yPelHeight, 'yMax': yMax, 'yMin': yMin} data += sstruct.pack(VDMX_vTableFmt, vTable) return data
def compile(self, glyfTable, recalcBBoxes=True): if hasattr(self, "data"): return self.data if self.numberOfContours == 0: return "" if recalcBBoxes: self.recalcBounds(glyfTable) data = sstruct.pack(glyphHeaderFormat, self) if self.isComposite(): data = data + self.compileComponents(glyfTable) else: data = data + self.compileCoordinates() return data
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): 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={}) 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])
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): 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 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): 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: 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, offset): nTracks = len(self) sizes = self.sizes() nSizes = len(sizes) # offset to the start of the size subtable offset += TRACK_DATA_FORMAT_SIZE + TRACK_TABLE_ENTRY_FORMAT_SIZE * nTracks trackDataHeader = sstruct.pack(TRACK_DATA_FORMAT, { 'nTracks': nTracks, 'nSizes': nSizes, 'sizeTableOffset': offset }) entryDataList = [] perSizeDataList = [] # offset to per-size tracking values offset += SIZE_VALUE_FORMAT_SIZE * nSizes # sort track table entries by track value for track, entry in sorted(self.items()): assert entry.nameIndex is not None entry.track = track entry.offset = offset entryDataList += [sstruct.pack(TRACK_TABLE_ENTRY_FORMAT, entry)] # sort per-size values by size for size, value in sorted(entry.items()): perSizeDataList += [struct.pack(PER_SIZE_VALUE_FORMAT, value)] offset += PER_SIZE_VALUE_FORMAT_SIZE * nSizes # sort size values sizeDataList = [ struct.pack(SIZE_VALUE_FORMAT, fl2fi(sv, 16)) for sv in sorted(sizes) ] data = bytesjoin([trackDataHeader] + entryDataList + sizeDataList + perSizeDataList) return data
def compile(self, ttFont): data = sstruct.pack( Gloc_header, dict(version=1.0, flags=(bool(self.attribIds) << 1) + (self.locations.typecode == 'I'), numAttribs=self.numAttribs)) if sys.byteorder != "big": self.locations.byteswap() data += self.locations.tostring() if sys.byteorder != "big": self.locations.byteswap() if self.attribIds: if sys.byteorder != "big": self.attribIds.byteswap() data += self.attribIds.tostring() if sys.byteorder != "big": self.attribIds.byteswap() return data
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 test_decompile_badOffset(self): # https://github.com/fonttools/fonttools/issues/525 table = table__n_a_m_e() badRecord = { "platformID": 1, "platEncID": 3, "langID": 7, "nameID": 1, "length": 3, "offset": 8765 # out of range } data = bytesjoin([ struct.pack(tostr(">HHH"), 1, 1, 6 + nameRecordSize), sstruct.pack(nameRecordFormat, badRecord)]) table.decompile(data, ttFont=None) self.assertEqual(table.names, [])
def compile(self, ttFont): self.version = 0 numGlyphs = ttFont['maxp'].numGlyphs glyphOrder = ttFont.getGlyphOrder() self.recordSize = 4 * ((2 + numGlyphs + 3) // 4) pad = (self.recordSize - 2 - numGlyphs) * b"\0" self.numRecords = len(self.hdmx) data = sstruct.pack(hdmxHeaderFormat, self) items = sorted(self.hdmx.items()) for ppem, widths in items: data = data + bytechr(ppem) + bytechr(max(widths.values())) for glyphID in range(len(glyphOrder)): width = widths[glyphOrder[glyphID]] data = data + bytechr(width) data = data + pad return data
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 = fl2fi(key, 14) fixedValue = fl2fi(value, 14) result.append(struct.pack(">hh", fixedKey, fixedValue)) return bytesjoin(result)
def calcHeadCheckSumAdjustmentSFNT(directory, tableData, flavor=None): """ Set the checkSumAdjustment in the head table data. Grumble. """ # if the flavor is None, guess. if flavor is None: flavor = "\000\001\000\000" for entry in directory: if entry["tag"] == "CFF ": flavor = "OTTO" break assert flavor in ("OTTO", "\000\001\000\000") # make the sfnt header searchRange, entrySelector, rangeShift = getSearchRange(len(directory), 16) sfntHeaderData = dict( sfntVersion=flavor, numTables=len(directory), searchRange=searchRange, entrySelector=entrySelector, rangeShift=rangeShift ) sfntData = sstruct.pack(sfntDirectoryFormat, sfntHeaderData) # make a SFNT table directory directory = [(entry["tag"], entry) for entry in directory] for tag, entry in sorted(directory): sfntEntry = SFNTDirectoryEntry() sfntEntry.tag = entry["tag"] sfntEntry.checkSum = entry["checksum"] sfntEntry.offset = entry["offset"] sfntEntry.length = entry["length"] sfntData += sfntEntry.toString() # calculate the checksum sfntDataChecksum = calcChecksum(sfntData) # gather all of the checksums checksums = [entry["checksum"] for o, entry in directory] checksums.append(sfntDataChecksum) # calculate the checksum checkSumAdjustment = sum(checksums) checkSumAdjustment = (0xB1B0AFBA - checkSumAdjustment) & 0xffffffff # set the value in the head table headTableData = tableData["head"] newHeadTableData = headTableData[:8] newHeadTableData += struct.pack(">L", checkSumAdjustment) newHeadTableData += headTableData[12:] tableData["head"] = newHeadTableData
def compile(self, glyfTable, recalcBBoxes=True): if hasattr(self, "data"): if recalcBBoxes: # must unpack glyph in order to recalculate bounding box self.expand(glyfTable) else: return self.data if self.numberOfContours == 0: return b'' if recalcBBoxes: self.recalcBounds(glyfTable) data = sstruct.pack(glyphHeaderFormat, self) if self.isComposite(): data = data + self.compileComponents(glyfTable) else: data = data + self.compileCoordinates() return data
def compile(self, glyfTable, recalcBBoxes=True): if hasattr(self, "data"): return self.data if self.numberOfContours == 0: return "" if recalcBBoxes: self.recalcBounds(glyfTable) data = sstruct.pack(glyphHeaderFormat, self) if self.isComposite(): data = data + self.compileComponents(glyfTable) else: data = data + self.compileCoordinates() # From the spec: "Note that the local offsets should be word-aligned" # From a later MS spec: "Note that the local offsets should be long-aligned" # Let's be modern and align on 4-byte boundaries. if len(data) % 4: # add pad bytes nPadBytes = 4 - (len(data) % 4) data = data + b"\0" * nPadBytes return data
def makeGlyfBBox2(bbox): font = getTTFont(sfntTTFSourcePath, recalcBBoxes=False) glyf = font["glyf"] hmtx = font["hmtx"] name = "bbox1" glyph = getTableModule('glyf').Glyph() glyph.numberOfContours = 0 glyph.xMin = glyph.xMax = glyph.yMin = glyph.yMax = bbox glyph.data = sstruct.pack(getTableModule('glyf').glyphHeaderFormat, glyph) glyf.glyphs[name] = glyph hmtx.metrics[name] = (0, 0) glyf.glyphOrder.append(name) tableData = getSFNTData(font)[0] font.close() del font header, directory, tableData = defaultSFNTTestData(tableData=tableData, flavor="TTF") data = packSFNT(header, directory, tableData, flavor="TTF") return data
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)
def compile(self, ttFont, version=2.0): data = b"" oClasses = [] if version >= 4.0: offset = 8 + 4 * (len(self.linear) + len(self.nonLinear)) else: offset = 6 + 2 * (len(self.linear) + len(self.nonLinear)) for l in self.linear: oClasses.append(len(data) + offset) gs = [ttFont.getGlyphID(x) for x in l] data += struct.pack((">%dH" % len(l)), *gs) for l in self.nonLinear: oClasses.append(len(data) + offset) gs = [(ttFont.getGlyphID(x[0]), x[1]) for x in l.items()] data += grUtils.bininfo(len(gs)) data += b"".join([struct.pack(">HH", *x) for x in sorted(gs)]) oClasses.append(len(data) + offset) self.numClass = len(oClasses) - 1 self.numLinear = len(self.linear) return sstruct.pack(Silf_classmap_format, self) + \ struct.pack(((">%dL" if version >= 4.0 else ">%dH") % len(oClasses)), *oClasses) + data
def compile(self, ttFont): dataList = [] offset = TRAK_HEADER_FORMAT_SIZE for direction in ('horiz', 'vert'): trackData = getattr(self, direction + 'Data', TrackData()) offsetName = direction + 'Offset' # set offset to 0 if None or empty if not trackData: setattr(self, offsetName, 0) continue # TrackData table format must be longword aligned alignedOffset = (offset + 3) & ~3 padding, offset = b"\x00"*(alignedOffset - offset), alignedOffset setattr(self, offsetName, offset) data = trackData.compile(offset) offset += len(data) dataList.append(padding + data) self.reserved = 0 tableData = bytesjoin([sstruct.pack(TRAK_HEADER_FORMAT, self)] + dataList) return tableData
def transform(self, ttFont): """ Return transformed 'glyf' data """ self.numGlyphs = len(self.glyphs) assert len(self.glyphOrder) == self.numGlyphs if 'maxp' in ttFont: ttFont['maxp'].numGlyphs = self.numGlyphs self.indexFormat = ttFont['head'].indexToLocFormat for stream in self.subStreams: setattr(self, stream, b"") bboxBitmapSize = ((self.numGlyphs + 31) >> 5) << 2 self.bboxBitmap = array.array('B', [0]*bboxBitmapSize) for glyphID in range(self.numGlyphs): self._encodeGlyph(glyphID) self.bboxStream = self.bboxBitmap.tobytes() + self.bboxStream for stream in self.subStreams: setattr(self, stream + 'Size', len(getattr(self, stream))) self.version = 0 data = sstruct.pack(woff2GlyfTableFormat, self) data += bytesjoin([getattr(self, s) for s in self.subStreams]) return data
def compile(self, ttFont): if not hasattr(self, "names"): # only happens when there are NO name table entries read # from the TTX file self.names = [] self.names.sort( ) # sort according to the spec; see NameRecord.__lt__() stringData = b"" format = 0 n = len(self.names) stringOffset = 6 + n * sstruct.calcsize(nameRecordFormat) data = struct.pack(">HHH", format, n, stringOffset) lastoffset = 0 done = {} # remember the data so we can reuse the "pointers" for name in self.names: if name.string in done: name.offset, name.length = done[name.string] else: name.offset, name.length = done[name.string] = len( stringData), len(name.string) stringData = stringData + name.string data = data + sstruct.pack(nameRecordFormat, name) return data + stringData