/// <summary> /// Convert sfnt file to woff file. /// </summary> /// <param name="reader">BinaryReader to read the sfnt data stream.</param> /// <param name="writer">BinaryWriter to write the woff data.</param> /// <param name="compression">Set to true to compress data.</param> public static void SfntToWoff(BinaryReader reader, BinaryWriter writer, bool compression = false) { reader.BaseStream.Position = 0; writer.BaseStream.Position = 0; // Header DataTypeConverter.WriteULong(writer, 0x774F4646); // signature writer.Write(reader.ReadBytes(DataTypeLength.Fixed)); // flavor writer.BaseStream.Position += DataTypeLength.ULong; // length var numberOfTables = DataTypeConverter.ReadUShort(reader); DataTypeConverter.WriteUShort(writer, numberOfTables); // numTables DataTypeConverter.WriteUShort(writer, 0); // reserved DataTypeConverter.WriteULong(writer, (uint)reader.BaseStream.Length); // totalSfntSize DataTypeConverter.WriteUShort(writer, 1); // majorVision DataTypeConverter.WriteUShort(writer, 0); // minorVision DataTypeConverter.WriteULong(writer, 0); // metaOffset DataTypeConverter.WriteULong(writer, 0); // metaLength DataTypeConverter.WriteULong(writer, 0); // metaOriLength DataTypeConverter.WriteULong(writer, 0); // privOffset DataTypeConverter.WriteULong(writer, 0); // privLength // Read original table directory reader.BaseStream.Position += 3 * DataTypeLength.UShort; var tags = new uint[numberOfTables]; var offsets = new uint[numberOfTables]; var compLengths = new uint[numberOfTables]; var origLengths = new uint[numberOfTables]; var origChecksum = new uint[numberOfTables]; var origOffsets = new uint[numberOfTables]; var startOffsetOfTableDirectory = writer.BaseStream.Position; for (var i = 0; i < numberOfTables; i++) { tags[i] = DataTypeConverter.ReadULong(reader); origChecksum[i] = DataTypeConverter.ReadULong(reader); origOffsets[i] = DataTypeConverter.ReadULong(reader); origLengths[i] = DataTypeConverter.ReadULong(reader); } // Table data writer.BaseStream.Position += numberOfTables * 5 * DataTypeLength.ULong; for (var i = 0; i < numberOfTables; i++) { offsets[i] = (uint)writer.BaseStream.Position; reader.BaseStream.Position = origOffsets[i]; var tableData = reader.ReadBytes((int)origLengths[i]); if (compression) { using (var compressedStream = new MemoryStream()) { var zOutputStream = new ZOutputStream(compressedStream, zlibConst.Z_DEFAULT_COMPRESSION); zOutputStream.Write(tableData, 0, (int)origLengths[i]); zOutputStream.finish(); if (compressedStream.Length >= origLengths[i]) { writer.Write(tableData); } else { compressedStream.WriteTo(writer.BaseStream); } } } else { writer.Write(tableData); } compLengths[i] = (uint)(writer.BaseStream.Position - offsets[i]); // 4byte padding if (writer.BaseStream.Position % 4 != 0) { var zeroCount = 4 - writer.BaseStream.Position % 4; for (var j = 0; j < zeroCount; j++) { writer.Write((byte)0); } } } // Write table directory writer.BaseStream.Position = startOffsetOfTableDirectory; for (var i = 0; i < numberOfTables; i++) { DataTypeConverter.WriteULong(writer, tags[i]); DataTypeConverter.WriteULong(writer, offsets[i]); DataTypeConverter.WriteULong(writer, compLengths[i]); DataTypeConverter.WriteULong(writer, origLengths[i]); DataTypeConverter.WriteULong(writer, origChecksum[i]); } // Write length in header writer.BaseStream.Position = 2 * DataTypeLength.ULong; DataTypeConverter.WriteULong(writer, (uint)writer.BaseStream.Length); }