private bool SerializeGlyf(ushort indexFormat) { Debug.Assert(_glyphs != null && _glyphs.Length != 0); Debug.Assert(indexFormat == 0 || indexFormat == 1); uint locaOffset = 0; var glyphWriter = new WoffWriter(); uint maxCapacity = 5 * _maxNumPoints; // Size without the application of packing. var pointsWriter = new WoffIndexer((int)maxCapacity); // Created here for reuse int glyphCount = _glyphs.Length; for (ushort i = 0; i < glyphCount; i++) { var glyph = _glyphs[i]; uint glyphSize = 0; pointsWriter.Offset = 0; // Reset the offset, it is non-resizable buffer (size is fixed). if (glyph.Serialize(glyphWriter, pointsWriter, out glyphSize) == false) { Trace.TraceError("Glyph Serialization Error: Glyph Index = {0}.", glyph.GlyphIndex); return(false); } _glyphsLocations.Add(locaOffset); locaOffset += glyphSize; } var glyphBuffer = glyphWriter.GetBuffer(); _woffDir.OrigTable = glyphBuffer; _woffDir.OrigLength = (uint)glyphBuffer.Length; return(true); }
private void Dispose(bool disposing) { if (!_isDisposed) { if (disposing) { if (_writer != null) { _writer.Dispose(); _writer = null; } } _isDisposed = true; } }
private bool SerializeHmtx(IList <ushort> advanceWidths, IList <short> lsbs, int numOfHMetrics, int numOfGlyphs) { var hmtxBufferSize = 2 * numOfGlyphs + 2 * numOfHMetrics; var writer = new WoffWriter(hmtxBufferSize); for (int i = 0; i < numOfGlyphs; i++) { if (i < numOfHMetrics) { writer.WriteUInt16(advanceWidths[i]); } writer.WriteInt16(lsbs[i]); } var tableBuffer = writer.GetBuffer(); var hmtxBuffer = new WoffBuffer(tableBuffer); _woffDir.OrigTable = tableBuffer; _woffDir.OrigLength = (uint)tableBuffer.Length; return(true); }
private bool SerializeLoca() { var glyfTable = _woffFont.Tables[_glyfIndex] as WoffTableGlyf; Debug.Assert(glyfTable != null); if (glyfTable == null) { return(false); } // indexToLocFormat 0 for short offsets (Offset16), 1 for long (Offset32). ushort indexFormat = glyfTable.IndexFormat; if (indexFormat != 0 && indexFormat != 1) { return(false); } var glyphs = glyfTable.Glyphs; var glyphsLocations = glyfTable.GlyphsLocations; var locaWriter = new WoffWriter(); uint locaOffset = 0; int glyphCount = glyphs.Length; for (ushort i = 0; i < glyphCount; i++) { locaOffset = glyphsLocations[i]; // indexToLocFormat 0 for short offsets (Offset16), 1 for long (Offset32). if (indexFormat == 1) { locaWriter.WriteUInt32(locaOffset); } else { var shortOffset = (ushort)(locaOffset >> 1); locaWriter.WriteUInt16(shortOffset); } } //if (locaOffset != glyfDir.OrigLength) //TODO: Needed? locaOffset = glyfTable.Length; if (indexFormat == 1) { locaWriter.WriteUInt32(locaOffset); } else { var shortOffset = (ushort)(locaOffset >> 1); locaWriter.WriteUInt16(shortOffset); } var locaBuffer = locaWriter.GetBuffer(); _woffDir.OrigTable = locaBuffer; _woffDir.OrigLength = (uint)locaBuffer.Length; return(true); }
public bool Serialize(WoffWriter writer, WoffIndexer pointsWriter, out uint glyphSize) { glyphSize = 0; if (writer == null) { return(false); } uint startOffset = writer.Offset; // 1. Write the Glyph Header writer.WriteInt16(_numContours); // int16 numberOfContours for number of contours: >= 0 is simple glyph, < 0 composite glyph. writer.WriteInt16(_xMin); // int16 xMin Minimum x for coordinate data. writer.WriteInt16(_yMin); // int16 yMin Minimum y for coordinate data. writer.WriteInt16(_xMax); // int16 xMax Maximum x for coordinate data. writer.WriteInt16(_yMax); // int16 yMax Maximum y for coordinate data. // 2. Write the Simple Glyph Description, if applicable if (_glyphType == WoffGlyphType.Simple) { // uint16 endPtsOfContours[numberOfContours] // Array of point indices for the last point of each contour, in increasing numeric order. Debug.Assert(_endPtsOfContours != null && _endPtsOfContours.Length == _numContours); if (_endPtsOfContours == null || _endPtsOfContours.Length != _numContours) { Debug.Assert(false, "Invalid condition."); return(false); } for (short i = 0; i < _numContours; i++) { writer.WriteUInt16(_endPtsOfContours[i]); } // uint16 instructionLength Total number of bytes for instructions. // If instructionLength is zero, no instructions are present for this glyph, // and this field is followed directly by the flags field.instructionLength writer.WriteUInt16(_instructionLength); // uint8 instructions[instructionLength] Array of instruction byte code for the glyph. if (_instructionLength != 0) { Debug.Assert(_instructions != null && _instructions.Length == _instructionLength); if (_instructions == null || _instructions.Length != _instructionLength) { Debug.Assert(false, "Invalid condition."); return(false); } writer.Write(_instructions); } // Pack the points Debug.Assert(_numPoints == _contours.Count); int pointsCapacity = 5 * _numPoints; // Size without the application of packing. if (pointsWriter == null) { pointsWriter = new WoffIndexer(pointsCapacity); } else { pointsWriter.Offset = 0; } uint pointsLength = 0; if (PackPoints(pointsWriter, (uint)pointsCapacity, ref pointsLength) == false) { Debug.Assert(false, "Invalid condition."); return(false); } // Serialize the points... writer.Write(pointsWriter.GetBuffer(), 0, (int)pointsLength); } // Write the Simple Glyph Description, if applicable else if (_glyphType == WoffGlyphType.Composite) { Debug.Assert(_componentLength != 0); Debug.Assert(_components != null && _componentLength == _components.Length); if (_componentLength == 0 || _components == null || _componentLength != _components.Length) { Debug.Assert(false, "Invalid condition."); return(false); } // Serialize the Composite Glyph data... writer.Write(_components, 0, _componentLength); } // NOTE: Without the padding the serialization of the glyph fails! var padBytesLength = WoffBuffer.CalcPadBytes((int)writer.Offset, 4); if (padBytesLength > 0) { var paddingBuffer = new byte[4]; writer.Write(paddingBuffer, 0, padBytesLength); } uint endOffset = writer.Offset; if (endOffset <= startOffset) { Debug.Assert(false, "Invalid condition."); return(false); } glyphSize = endOffset - startOffset; return(true); }
/// <summary> /// This writes the transformed WOFF2 font collection data to the TTC OpenType. /// </summary> /// <param name="stream"></param> /// <returns></returns> /// <remarks> /// The strategy is to produce a layout similar to the simple layout used in the WOFF2 data structure and /// still confirm to the OpenType format. /// <code> /// <![CDATA[ /// +---------------------+ /// | TTC Header | /// +---------------------+ /// | All TTC Directories | /// +---------------------+ /// | All the Table Data | /// +---------------------+ /// ]]> /// </code> /// </remarks> private bool WriteWoff2Collection(Stream stream) { if (stream == null || _woffHeader == null || _woffDirs == null || _woffDirs.Count == 0) { return(false); } var numFonts = _collectionHeader.NumFonts; //------------------------------------------------------------------------------------------------- // 1. Compute the checksums, the paddings of the tables will be recalculated after the transformations. for (int i = 0; i < _woffDirs.Count; i++) { _woffDirs[i].RecalculateChecksum(); } // Compute the sizes of the data structures to determine the offsets... // 2. Compute the font collection header size int collectionOffset = 0; collectionOffset += WoffBuffer.SizeOfUInt; // TAG ttcTag collectionOffset += WoffBuffer.SizeOfUShort; // uint16 majorVersion collectionOffset += WoffBuffer.SizeOfUShort; // uint16 minorVersion collectionOffset += WoffBuffer.SizeOfUInt; // uint32 numFonts collectionOffset += WoffBuffer.SizeOfUInt * numFonts; // Offset32 tableDirectoryOffsets[numFonts] if (_collectionHeader.IsVersion(2)) { collectionOffset += WoffBuffer.SizeOfUInt; // uint32 dsigTag collectionOffset += WoffBuffer.SizeOfUInt; // uint32 dsigLength collectionOffset += WoffBuffer.SizeOfUInt; // uint32 dsigOffset } var headerOffset = (uint)collectionOffset; // 3. Compute for each for in the collection the (Header + Directories) size // and sum all to get the size/offset of the fonts var directoryOffsets = new uint[numFonts]; uint directoryOffset = headerOffset; for (ushort i = 0; i < numFonts; i++) { directoryOffsets[i] = directoryOffset; // TableDirectory = HeaderSize + Total Table Records var HeaderDirSize = HeaderSize + _collectionEntries[i].NumTables * TableSize; directoryOffset += HeaderDirSize; } // 4. Compute for each directory in the collection the offsets uint[] tablesOffsets = new uint[_woffDirs.Count]; uint tablesOffset = directoryOffset; for (ushort i = 0; i < _woffDirs.Count; i++) { var woffDir = _woffDirs[i]; tablesOffsets[i] = tablesOffset; tablesOffset += woffDir.OrigLength; if (tablesOffset % 4 != 0) { tablesOffset += 4 - (tablesOffset % 4); } } //------------------------------------------------------------------------------------------------- // Build the smaller headers + directories data in memory... var writer = new WoffWriter(); // 1. The Font Collection File Structure // A font collection file consists of a single TTC Header table, one or more Table Directories // (each corresponding to a different font resource), and a number of OpenType tables. // The TTC Header must be located at the beginning of the TTC file. // TAG ttcTag - Font Collection ID string: 'ttcf' writer.WriteUInt32(_collectionHeader.TtcTag); // uint16 majorVersion - Major version of the TTC Header. writer.WriteUInt16(_collectionHeader.MajorVersion); // uint16 minorVersion - Minor version of the TTC Header. writer.WriteUInt16(_collectionHeader.MinorVersion); // uint32 numFonts - Number of fonts in TTC writer.WriteUInt32(_collectionHeader.NumFonts); // Offset32 tableDirectoryOffsets[numFonts] Array of offsets to the TableDirectory for each font from the beginning of the file for (ushort i = 0; i < numFonts; i++) { writer.WriteUInt32(directoryOffsets[i]); } // For the version 2.0 only add the empty DSIG information. if (_collectionHeader.IsVersion(2)) { // uint32 dsigTag - Tag indicating that a DSIG table exists, 0x44534947 ('DSIG') (null if no signature) writer.WriteUInt32(_collectionHeader.DsigTag); // uint32 dsigLength - The length (in bytes) of the DSIG table (null if no signature) writer.WriteUInt32(_collectionHeader.DsigLength); // uint32 dsigOffset - The offset (in bytes) of the DSIG table from the beginning of the TTC file (null if no signature) writer.WriteUInt32(_collectionHeader.DsigOffset); } // Write out the header for each font uint fontHeaderOffset = writer.Offset; var fontHeaderOffsets = new List <uint>(numFonts + 1); for (ushort j = 0; j < numFonts; j++) { fontHeaderOffsets.Add(fontHeaderOffset); var collectionEntry = _collectionEntries[j]; var numTables = collectionEntry.NumTables; var searchRange = (ushort)(WoffUtils.MaxPower2LE(numTables) * TableSize); var entrySelector = WoffUtils.Log2(WoffUtils.MaxPower2LE(numTables)); var rangeShift = (ushort)(numTables * TableSize - searchRange); // Write the font offset table... // uint32: sfntVersion 0x00010000 or 0x4F54544F ('OTTO') writer.WriteUInt32(collectionEntry.Flavor); // uint16: numTables Number of tables. writer.WriteUInt16(numTables); // uint16 searchRange (Maximum power of 2 <= numTables) x 16. writer.WriteUInt16(searchRange); // uint16: entrySelector Log2(maximum power of 2 <= numTables). writer.WriteUInt16(entrySelector); // uint16: rangeShift NumTables x 16-searchRange. writer.WriteUInt16(rangeShift); var sortedIndices = collectionEntry.SortedIndices; for (ushort i = 0; i < numTables; i++) { var dirIndex = sortedIndices[i]; var woffDir = _woffDirs[dirIndex]; // Tag: tableTag Table identifier. writer.WriteUInt32(woffDir.Tag); // uint32: checkSum CheckSum for this table. writer.WriteUInt32(woffDir.OrigChecksum); // Offset32 offset Offset from beginning of TrueType font file. writer.WriteUInt32(tablesOffsets[dirIndex]); // uint32 length Length of this table. writer.WriteUInt32(woffDir.OrigLength); } fontHeaderOffset += writer.Offset; } fontHeaderOffsets.Add(fontHeaderOffset); // Get the buffer of the collection header and font headers... var fontBuffer = writer.GetBuffer(); // For the font headers, calculate the checksum to create the font checksum for (ushort i = 0; i < numFonts; i += 2) { uint fontOffset = fontHeaderOffsets[i]; uint fontLength = fontHeaderOffsets[i + 1] - fontOffset; _woffFonts[i].ChecksumAdjustment(fontBuffer, fontOffset, fontLength); } //------------------------------------------------------------------------------------------------- // Write the actual tables data to the stream, with the required offsets... stream.Write(fontBuffer, 0, fontBuffer.Length); var paddingBuffer = new byte[4]; for (int i = 0; i < _woffDirs.Count; i++) { var woffDir = _woffDirs[i]; stream.Write(woffDir.OrigTable, 0, (int)woffDir.OrigLength); stream.Write(paddingBuffer, 0, (int)woffDir.Padding); } return(true); }
private bool WriteWoff2(Stream stream) { if (_woffHeader.IsCollection) { return(WriteWoff2Collection(stream)); } if (stream == null || _woffHeader == null || _woffDirs == null || _woffDirs.Count == 0) { return(false); } var numTables = _woffHeader.NumTables; // Re-order tables in output order (by the OpenType Specifications) var sortedDirs = _woffDirs.ToArray(); Array.Sort(sortedDirs, WoffTableDirectory.GetComparer()); //------------------------------------------------------------------------------------------------- // 1. Compute the checksums, the paddings of the tables will be recalculated after the transformations. uint tablesChecksum = 0; // We have only one font here... for (int i = 0; i < numTables; i++) { tablesChecksum += sortedDirs[i].RecalculateChecksum(); } //------------------------------------------------------------------------------------------------- // Build the smaller headers + directories data in memory... var writer = new WoffWriter(); var searchRange = (ushort)(WoffUtils.MaxPower2LE(numTables) * TableSize); var entrySelector = WoffUtils.Log2(WoffUtils.MaxPower2LE(numTables)); var rangeShift = (ushort)(numTables * TableSize - searchRange); // Write the font offset table... // uint32: sfntVersion 0x00010000 or 0x4F54544F ('OTTO') writer.WriteUInt32(_woffHeader.Flavor); // uint16: numTables Number of tables. writer.WriteUInt16(_woffHeader.NumTables); // uint16 searchRange (Maximum power of 2 <= numTables) x 16. writer.WriteUInt16(searchRange); // uint16: entrySelector Log2(maximum power of 2 <= numTables). writer.WriteUInt16(entrySelector); // uint16: rangeShift NumTables x 16-searchRange. writer.WriteUInt16(rangeShift); uint tablesOffset = HeaderSize + numTables * TableSize; for (int i = 0; i < numTables; i++) { var woffDir = sortedDirs[i]; // Tag: tableTag Table identifier. writer.WriteUInt32(woffDir.Tag); // uint32: checkSum CheckSum for this table. writer.WriteUInt32(woffDir.OrigChecksum); // Offset32 offset Offset from beginning of TrueType font file. writer.WriteUInt32(tablesOffset); // uint32 length Length of this table. writer.WriteUInt32(woffDir.OrigLength); tablesOffset += woffDir.OrigLength; if (tablesOffset % 4 != 0) { tablesOffset += 4 - (tablesOffset % 4); } } // Get the buffer of the collection header and font headers... var fontBuffer = writer.GetBuffer(); // For the font headers, calculate the checksum to create the font checksum _woffFonts[0].ChecksumAdjustment(fontBuffer, 0, (uint)fontBuffer.Length, tablesChecksum); //------------------------------------------------------------------------------------------------- // Write the actual tables data to the stream, with the required offsets... stream.Write(fontBuffer, 0, fontBuffer.Length); var paddingBuffer = new byte[4]; for (int i = 0; i < numTables; i++) { var woffDir = sortedDirs[i]; stream.Write(woffDir.OrigTable, 0, (int)woffDir.OrigLength); stream.Write(paddingBuffer, 0, (int)woffDir.Padding); } return(true); }
/// <summary> /// Initializes a new non-resizable instance of the <see cref="WoffIndexer"/> class /// based on the specified region (index) of a byte array. /// </summary> /// <param name="buffer">The array of unsigned bytes from which to create this stream.</param> /// <param name="index">The index into buffer at which the stream begins.</param> /// <param name="count">The length of the stream in bytes.</param> public WoffIndexer(byte[] buffer, int index, int count) { _writer = new WoffWriter(buffer, index, count); }
/// Initializes a new non-resizable instance of the <see cref="WoffIndexer"/> class /// based on the specified byte array. /// </summary> /// <param name="buffer">The array of unsigned bytes from which to create the current stream.</param> public WoffIndexer(byte[] buffer) { _writer = new WoffWriter(buffer); }
/// <summary> /// Initializes a new instance of the <see cref="WoffIndexer"/> class with a non-resizable /// capacity initialized as specified. /// </summary> /// <param name="capacity">The initial size of the internal array in bytes.</param> public WoffIndexer(int capacity) { _writer = new WoffWriter(capacity); }
/// <summary> /// Initializes a new instance of the <see cref="WoffIndexer"/> class with an expandable /// capacity initialized to zero. /// </summary> public WoffIndexer() { _writer = new WoffWriter(); }