private bool ImportWoff2(Stream stream) { int sizeRead = 0; // 1. WOFF2Header: File header with basic font type and version, along with offsets // to metadata and private data blocks. _woffHeader = new SvgWoffHeader(); _woffTables = null; var buffer = new byte[SvgWoffHeader.Woff2Size]; sizeRead = stream.Read(buffer, 0, (int)SvgWoffHeader.Woff2Size); Debug.Assert(sizeRead == SvgWoffHeader.Woff2Size); if (!_woffHeader.SetHeader(buffer)) { return(false); } // 2. TableDirectory: Directory of font tables, containing size and other info. _woffTables = new List <SvgWoffTableDirectory>(_woffHeader.NumTables); for (int i = 0; i < _woffHeader.NumTables; i++) { var woffTable = new SvgWoffTableDirectory(); if (woffTable.SetHeader(stream, _woffHeader.IsTrueType)) { if (woffTable.IsTransformed) { _isTransformed = true; } _woffTables.Add(woffTable); } } // 3. CollectionDirectory: An optional table containing the font fragment descriptions // of font collection entries. if (_woffHeader.IsCollection) { //TODO: WOFF2 - Font collection not yet supported return(false); } // 4. CompressedFontData: Contents of font tables, compressed for storage in the WOFF2 file. int bytesRead = 0; int bytesCount = (int)_woffHeader.TotalCompressedSize; if (bytesCount != 0) { byte[] compressedBuffer = SvgWoffObject.ReadBytes(stream, bytesCount, out bytesRead); Debug.Assert(bytesRead == bytesCount); if (bytesRead != bytesCount) { return(false); } using (var brotliStream = new BrotliInputStream(new MemoryStream(compressedBuffer))) { for (int i = 0; i < _woffHeader.NumTables; i++) { var woffTable = _woffTables[i]; bytesCount = (int)woffTable.TransformLength; var tableBuffer = SvgWoffObject.ReadBytes(brotliStream, bytesCount, out bytesRead); Debug.Assert(bytesRead == bytesCount); if (bytesRead != bytesCount) { return(false); } woffTable.CompTable = tableBuffer; woffTable.OrigTable = tableBuffer; if (!_isTransformed) { woffTable.OrigChecksum = woffTable.CalculateChecksum(); } } } } // 5. ExtendedMetadata: An optional block of extended metadata, represented in XML format // and compressed for storage in the WOFF2 file. _metadata = new SvgWoffMetadata(_woffHeader.MetaOffset, _woffHeader.MetaLength, _woffHeader.MetaOrigLength); if (_woffHeader.HasMetadata) { stream.Seek(_woffHeader.MetaOffset, SeekOrigin.Begin); bytesRead = 0; bytesCount = (int)_woffHeader.MetaLength; var metaBuffer = SvgWoffObject.ReadBytes(stream, bytesCount, out bytesRead); Debug.Assert(bytesRead == bytesCount); if (bytesRead != bytesCount) { return(false); } _metadata.Data = metaBuffer; _metadata.OrigData = metaBuffer; if (_woffHeader.MetaLength != _woffHeader.MetaOrigLength) { bytesCount = (int)_woffHeader.MetaOrigLength; using (var brotliStream = new BrotliInputStream(new MemoryStream(metaBuffer))) { var origBuffer = SvgWoffObject.ReadBytes(brotliStream, bytesCount, out bytesRead); Debug.Assert(bytesRead == bytesCount); if (bytesRead != bytesCount) { return(false); } _metadata.OrigData = origBuffer; } } } // 6. PrivateData: An optional block of private data for the font designer, foundry, or vendor to use. _privateData = new SvgWoffPrivateData(_woffHeader.PrivateOffset, _woffHeader.PrivateLength); if (_woffHeader.HasPrivateData) { stream.Seek(_woffHeader.PrivateOffset, SeekOrigin.Begin); bytesRead = 0; bytesCount = (int)_woffHeader.PrivateLength; var privateBuffer = SvgWoffObject.ReadBytes(stream, bytesCount, out bytesRead); Debug.Assert(bytesRead == bytesCount); if (bytesRead != bytesCount) { return(false); } _privateData.Data = privateBuffer; } _woffVersion = 2; // ((List<SvgWoffTableDirectory>)(_woffTables)).Sort(new WoffTableComparer()); return(true); }
private bool ExportSingle(Stream stream) { if (stream == null || _woffHeader == null || _woffTables == null || _woffTables.Count == 0) { return(false); } var numTables = _woffHeader.NumTables; var searchRange = (ushort)(SvgWoffObject.MaxPower2LE(numTables) * TableSize); var entrySelector = SvgWoffObject.Log2(SvgWoffObject.MaxPower2LE(numTables)); var rangeShift = (ushort)(numTables * TableSize - searchRange); int offset = 0; // Write the font offset table... var headerBuffer = new byte[HeaderSize]; // uint32: sfntVersion 0x00010000 or 0x4F54544F ('OTTO') offset += SvgWoffObject.WriteUInt32BE(headerBuffer, offset, _woffHeader.Flavor); // uint16: numTables Number of tables. offset += SvgWoffObject.WriteUInt16BE(headerBuffer, offset, _woffHeader.NumTables); // uint16 searchRange (Maximum power of 2 <= numTables) x 16. offset += SvgWoffObject.WriteUInt16BE(headerBuffer, offset, searchRange); // uint16: entrySelector Log2(maximum power of 2 <= numTables). offset += SvgWoffObject.WriteUInt16BE(headerBuffer, offset, entrySelector); // uint16: rangeShift NumTables x 16-searchRange. offset += SvgWoffObject.WriteUInt16BE(headerBuffer, offset, rangeShift); stream.Write(headerBuffer, 0, (int)HeaderSize); uint tablesOffset = HeaderSize + numTables * TableSize; var tableBuffer = new byte[TableSize]; for (int i = 0; i < numTables; i++) { offset = 0; var woffTable = _woffTables[i]; // Tag: tableTag Table identifier. offset += SvgWoffObject.WriteUInt32BE(tableBuffer, offset, woffTable.Tag); // uint32: checkSum CheckSum for this table. offset += SvgWoffObject.WriteUInt32BE(tableBuffer, offset, woffTable.OrigChecksum); // Offset32 offset Offset from beginning of TrueType font file. offset += SvgWoffObject.WriteUInt32BE(tableBuffer, offset, tablesOffset); // uint32 length Length of this table. offset += SvgWoffObject.WriteUInt32BE(tableBuffer, offset, woffTable.OrigLength); stream.Write(tableBuffer, 0, (int)TableSize); tablesOffset += woffTable.OrigLength; if (tablesOffset % 4 != 0) { tablesOffset += 4 - (tablesOffset % 4); } } var paddingBuffer = new byte[4]; for (int i = 0; i < numTables; i++) { var woffTable = _woffTables[i]; stream.Write(woffTable.OrigTable, 0, (int)woffTable.OrigLength); stream.Write(paddingBuffer, 0, (int)woffTable.Padding); } return(true); }
private bool ImportWoff1(Stream stream) { int sizeRead = 0; _woffHeader = new SvgWoffHeader(); _woffTables = null; var buffer = new byte[SvgWoffHeader.Woff1Size]; sizeRead = stream.Read(buffer, 0, (int)SvgWoffHeader.Woff1Size); Debug.Assert(sizeRead == SvgWoffHeader.Woff1Size); if (!_woffHeader.SetHeader(buffer)) { return(false); } _woffTables = new List <SvgWoffTableDirectory>(_woffHeader.NumTables); for (int i = 0; i < _woffHeader.NumTables; i++) { buffer = new byte[SvgWoffTableDirectory.Woff1Size]; sizeRead = stream.Read(buffer, 0, (int)SvgWoffTableDirectory.Woff1Size); Debug.Assert(sizeRead == SvgWoffTableDirectory.Woff1Size); var woffTable = new SvgWoffTableDirectory(); if (woffTable.SetHeader(buffer)) { _woffTables.Add(woffTable); } } for (int i = 0; i < _woffHeader.NumTables; i++) { var woffTable = _woffTables[i]; stream.Seek(woffTable.Offset, SeekOrigin.Begin); int bytesRead = 0; int bytesCount = (int)woffTable.CompLength; if (bytesCount == 0) { continue; } var tableBuffer = SvgWoffObject.ReadBytes(stream, bytesCount, out bytesRead); Debug.Assert(bytesRead == bytesCount); if (bytesRead != bytesCount) { return(false); } woffTable.CompTable = tableBuffer; if (woffTable.CompLength == woffTable.OrigLength) { // table data is not compressed woffTable.OrigTable = tableBuffer; } else { bytesCount = (int)woffTable.OrigLength; var origBuffer = new byte[bytesCount]; using (var zlibStream = new ZLibStream(new MemoryStream(tableBuffer), CompressionMode.Decompress, false)) { int bytesStart = 0; do { bytesRead = zlibStream.Read(origBuffer, bytesStart, bytesCount); if (bytesRead == 0) { break; } bytesStart += bytesRead; bytesCount -= bytesRead; } while (bytesCount > 0); } woffTable.OrigTable = origBuffer; } } _metadata = new SvgWoffMetadata(_woffHeader.MetaOffset, _woffHeader.MetaLength, _woffHeader.MetaOrigLength); if (_woffHeader.HasMetadata) { stream.Seek(_woffHeader.MetaOffset, SeekOrigin.Begin); int bytesRead = 0; int bytesCount = (int)_woffHeader.MetaLength; var metaBuffer = SvgWoffObject.ReadBytes(stream, bytesCount, out bytesRead); Debug.Assert(bytesRead == bytesCount); if (bytesRead != bytesCount) { return(false); } _metadata.Data = metaBuffer; _metadata.OrigData = metaBuffer; if (_woffHeader.MetaLength != _woffHeader.MetaOrigLength) { bytesCount = (int)_woffHeader.MetaOrigLength; var origBuffer = new byte[bytesCount]; using (var zlibStream = new ZLibStream(new MemoryStream(metaBuffer), CompressionMode.Decompress, false)) { int bytesStart = 0; do { bytesRead = zlibStream.Read(origBuffer, bytesStart, bytesCount); if (bytesRead == 0) { break; } bytesStart += bytesRead; bytesCount -= bytesRead; } while (bytesCount > 0); } _metadata.OrigData = origBuffer; } } _privateData = new SvgWoffPrivateData(_woffHeader.PrivateOffset, _woffHeader.PrivateLength); if (_woffHeader.HasPrivateData) { stream.Seek(_woffHeader.PrivateOffset, SeekOrigin.Begin); int bytesRead = 0; int bytesCount = (int)_woffHeader.PrivateLength; var privateBuffer = SvgWoffObject.ReadBytes(stream, bytesCount, out bytesRead); Debug.Assert(bytesRead == bytesCount); if (bytesRead != bytesCount) { return(false); } _privateData.Data = privateBuffer; } _woffVersion = 1; return(true); }