예제 #1
0
        protected int _locaIndex;  // Index = 11

        #region Constructors and Destructor

        public WoffTable(WoffFont woffFont, WoffTableDirectory woffDir)
        {
            if (woffFont == null)
            {
                throw new ArgumentNullException(nameof(woffFont), "The font object is required by the font table object.");
            }
            if (woffDir == null)
            {
                throw new ArgumentNullException(nameof(woffDir), "The font directory object is required by the font table object.");
            }
            _headIndex = -1;
            _hheaIndex = -1;
            _hmtxIndex = -1;
            _maxpIndex = -1;
            _nameIndex = -1;
            _glyfIndex = -1;
            _locaIndex = -1;

            _index    = ushort.MaxValue;
            _length   = woffDir.OrigLength;
            _checksum = woffDir.OrigChecksum;
            _woffDir  = woffDir;
            _woffFont = woffFont;

            if (!woffFont.IsCollection)
            {
                _index = woffDir.WoffIndex;
            }
        }
예제 #2
0
        public WoffTableHead(WoffFont woffFont, WoffTableDirectory woffDir)
            : base(woffFont, woffDir)
        {
            var buffer = woffDir.OrigTable;

            if (buffer != null && buffer.Length >= SizeOfTable)
            {
                var length = buffer.Length;

                _tableBuffer = new WoffBuffer((uint)length);
                _tableBuffer.Copy(buffer);
            }
        }
예제 #3
0
        public WoffTableName(WoffFont woffFont, WoffTableDirectory woffDir)
            : base(woffFont, woffDir)
        {
            var buffer = woffDir.OrigTable;

            if (buffer != null && buffer.Length >= 6)
            {
                var length = buffer.Length;

                _tableBuffer = new WoffBuffer((uint)length);
                _tableBuffer.Copy(buffer);
            }

            _unicodeNoBOM = new UnicodeEncoding(true, false);
        }
예제 #4
0
 public WoffTableHhea(WoffFont woffFont, WoffTableDirectory woffDir)
     : base(woffFont, woffDir)
 {
 }
예제 #5
0
        public void AddDirectory(WoffTableDirectory woffDir)
        {
            if (woffDir == null)
            {
                throw new ArgumentNullException(nameof(woffDir),
                                                "The font directory object is required by the font table object.");
            }

            if (_woffVersion == WoffUtils.Woff1Version)
            {
                _woffDirs.Add(woffDir);

                if (string.Equals(woffDir.Name, "name", StringComparison.OrdinalIgnoreCase))
                {
                    _nameIndex = woffDir.WoffIndex;
                    _woffTables.Add(new WoffTableName(this, woffDir));
                }
                else
                {
                    _woffTables.Add(new WoffTable(this, woffDir));
                }

                // For WOFF1, not further processing or transformation is required.
                return;
            }

            if (woffDir.IsTransformed)
            {
                _isTransformed = true;
            }
            var tableIndex = _woffDirs.Count;

            _woffDirs.Add(woffDir);

            if (!this.IsCollection)
            {
                tableIndex = woffDir.WoffIndex;
            }

            var tagIndex = woffDir.Flags & 0x3F;

            if (_headIndex == -1 || _hheaIndex == -1)
            {
                if (_headIndex == -1 && tagIndex == WoffUtils.HeadIndex) // 1 for head
                {
                    _headIndex = tableIndex;
                }
                if (_hheaIndex == -1 && tagIndex == WoffUtils.HheaIndex) // 2 for hhea
                {
                    _hheaIndex = tableIndex;
                }
            }
            if (_hmtxIndex == -1 || _maxpIndex == -1 || _nameIndex == -1)
            {
                if (_hmtxIndex == -1 && tagIndex == WoffUtils.HmtxIndex) // 3 for hmtx
                {
                    _hmtxIndex = tableIndex;
                }
                if (_maxpIndex == -1 && tagIndex == WoffUtils.MaxpIndex) // 4 for hmtx
                {
                    _maxpIndex = tableIndex;
                }
                if (_nameIndex == -1 && tagIndex == WoffUtils.NameIndex) // 5 for name
                {
                    _nameIndex = tableIndex;
                }
            }
            if (_glyfIndex == -1 || _locaIndex == -1)
            {
                if (_glyfIndex == -1 && tagIndex == WoffUtils.GlyfIndex) // 10 for glyf
                {
                    _glyfIndex = tableIndex;
                }
                if (_locaIndex == -1 && tagIndex == WoffUtils.LocaIndex) // 11 for loca
                {
                    _locaIndex = tableIndex;
                }
            }

            switch (tagIndex)
            {
            case WoffUtils.HeadIndex:     // head = 1
                _woffTables.Add(new WoffTableHead(this, woffDir));
                break;

            case WoffUtils.HheaIndex:     // hhea = 2
                _woffTables.Add(new WoffTableHhea(this, woffDir));
                break;

            case WoffUtils.HmtxIndex:     // hmtx = 3
                _woffTables.Add(new WoffTableHmtx(this, woffDir));
                break;

            case WoffUtils.MaxpIndex:     // maxp = 4
                _woffTables.Add(new WoffTableMaxp(this, woffDir));
                break;

            case WoffUtils.NameIndex:     // name = 5
                _woffTables.Add(new WoffTableName(this, woffDir));
                break;

            case WoffUtils.GlyfIndex:     // glyf = 10
                _woffTables.Add(new WoffTableGlyf(this, woffDir));
                break;

            case WoffUtils.LocaIndex:     // loca = 11
                _woffTables.Add(new WoffTableLoca(this, woffDir));
                break;

            default:                      // All others
                _woffTables.Add(new WoffTable(this, woffDir));
                break;
            }
        }
예제 #6
0
 public WoffTableGlyf(WoffFont woffFont, WoffTableDirectory woffDir)
     : base(woffFont, woffDir)
 {
     _maxNumPoints = 0;
     _indexFormat  = ushort.MaxValue;
 }
예제 #7
0
        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);
        }
예제 #8
0
        private bool ReadWoff2(Stream stream)
        {
            _woffVersion = WoffUtils.Woff2Version;

            uint sizeRead = 0;

            // 1. WOFF2Header: File header with basic font type and version, along with offsets
            //    to metadata and private data blocks.
            _woffHeader = new WoffHeader(WoffUtils.Woff2Version);
            _woffDirs   = null;

            sizeRead = _woffHeader.HeaderSize;

            Debug.Assert(sizeRead == WoffUtils.Woff2HeaderSize);

            if (!_woffHeader.Read(stream))
            {
                return(false);
            }

            // 2. TableDirectory: Directory of font tables, containing size and other info.
            uint offset = 0;

            _woffDirs = new List <WoffTableDirectory>(_woffHeader.NumTables);
            for (ushort i = 0; i < _woffHeader.NumTables; i++)
            {
                var woffDir = new WoffTableDirectory(i, _woffVersion, offset);

                if (woffDir.Read(stream, _woffHeader.IsTrueType))
                {
                    if (woffDir.IsTransformed)
                    {
                        _isTransformed = true;
                    }
                    _woffDirs.Add(woffDir);

                    offset += woffDir.CompLength;
                }
            }

            // 3. CollectionDirectory: An optional table containing the font fragment descriptions
            //    of font collection entries.
            if (_woffHeader.IsCollection)
            {
                var isLoaded = this.ReadWoff2Fonts(stream);
            }
            else
            {
                var isLoaded = this.ReadWoff2Font(stream);
            }

            // 5. ExtendedMetadata: An optional block of extended metadata, represented in XML format
            //    and compressed for storage in the WOFF2 file.
            _metadata = new WoffMetadata(_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 = WoffBuffer.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 = WoffBuffer.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 WoffPrivateData(_woffHeader.PrivateOffset, _woffHeader.PrivateLength);
            if (_woffHeader.HasPrivateData)
            {
                stream.Seek(_woffHeader.PrivateOffset, SeekOrigin.Begin);

                int bytesRead  = 0;
                int bytesCount = (int)_woffHeader.PrivateLength;

                var privateBuffer = WoffBuffer.ReadBytes(stream, bytesCount, out bytesRead);

                Debug.Assert(bytesRead == bytesCount);

                if (bytesRead != bytesCount)
                {
                    return(false);
                }

                _privateData.Data = privateBuffer;
            }

            return(true);
        }
예제 #9
0
        private bool ReadWoff1(Stream stream)
        {
            _woffVersion = WoffUtils.Woff1Version;

            var woffFont = new WoffFont(_woffHeader);

            _woffFonts.Add(woffFont);
            woffFont.BeginDirectory(_woffVersion);

            _woffHeader = new WoffHeader(WoffUtils.Woff1Version);
            _woffDirs   = null;

            int sizeRead = 0;

            sizeRead = _woffHeader.HeaderSize;

            Debug.Assert(sizeRead == WoffUtils.Woff1HeaderSize);

            if (!_woffHeader.Read(stream))
            {
                return(false);
            }

            _woffDirs = new List <WoffTableDirectory>(_woffHeader.NumTables);

            // 2. TableDirectory: Directory of font tables, containing size and other info.
            for (ushort i = 0; i < _woffHeader.NumTables; i++)
            {
                var woffDir = new WoffTableDirectory(i, _woffVersion);
                if (woffDir.Read(stream))
                {
                    _woffDirs.Add(woffDir);

                    woffFont.AddDirectory(woffDir);
                }
            }

            for (int i = 0; i < _woffHeader.NumTables; i++)
            {
                var woffDir = _woffDirs[i];

                stream.Seek(woffDir.Offset, SeekOrigin.Begin);

                int bytesRead  = 0;
                int bytesCount = (int)woffDir.CompLength;
                if (bytesCount == 0)
                {
                    continue;
                }
                var tableBuffer = WoffBuffer.ReadBytes(stream, bytesCount, out bytesRead);

                Debug.Assert(bytesRead == bytesCount);

                if (bytesRead != bytesCount)
                {
                    return(false);
                }

                woffDir.CompTable = tableBuffer;
                if (woffDir.CompLength == woffDir.OrigLength)
                {
                    // table data is not compressed
                    woffDir.OrigTable = tableBuffer;
                }
                else
                {
                    bytesCount = (int)woffDir.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);
                    }

                    woffDir.OrigTable = origBuffer;
                }
            }

            _metadata = new WoffMetadata(_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 = WoffBuffer.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 WoffPrivateData(_woffHeader.PrivateOffset, _woffHeader.PrivateLength);
            if (_woffHeader.HasPrivateData)
            {
                stream.Seek(_woffHeader.PrivateOffset, SeekOrigin.Begin);

                int bytesRead  = 0;
                int bytesCount = (int)_woffHeader.PrivateLength;

                var privateBuffer = WoffBuffer.ReadBytes(stream, bytesCount, out bytesRead);

                Debug.Assert(bytesRead == bytesCount);

                if (bytesRead != bytesCount)
                {
                    return(false);
                }

                _privateData.Data = privateBuffer;
            }

            woffFont.EndDirectory();

            return(true);
        }