Пример #1
0
        public byte[] Rebuild(bool checksumAdjustment)
        {
            // create a Motorola Byte Order buffer for the new table
            WoffBuffer headBuffer = new WoffBuffer(SizeOfTable);

            // populate the buffer
            headBuffer.SetFixed(this.TableVersionNumber, (uint)FieldOffsets.TableVersionNumber);
            headBuffer.SetFixed(this.FontRevision, (uint)FieldOffsets.FontRevision);
            headBuffer.SetUInt(this.CheckSumAdjustment, (uint)FieldOffsets.CheckSumAdjustment);
            headBuffer.SetUInt(this.MagicNumber, (uint)FieldOffsets.MagicNumber);
            headBuffer.SetUShort(this.Flags, (uint)FieldOffsets.Flags);
            headBuffer.SetUShort(this.UnitsPerEm, (uint)FieldOffsets.UnitsPerEm);
            headBuffer.SetLong(this.Created, (uint)FieldOffsets.Created);
            headBuffer.SetLong(this.Modified, (uint)FieldOffsets.Modified);
            headBuffer.SetShort(this.XMin, (uint)FieldOffsets.XMin);
            headBuffer.SetShort(this.YMin, (uint)FieldOffsets.YMin);
            headBuffer.SetShort(this.XMax, (uint)FieldOffsets.XMax);
            headBuffer.SetShort(this.YMax, (uint)FieldOffsets.YMax);
            headBuffer.SetUShort(this.MacStyle, (uint)FieldOffsets.MacStyle);
            headBuffer.SetUShort(this.LowestRecPPEM, (uint)FieldOffsets.LowestRecPPEM);
            headBuffer.SetShort(this.FontDirectionHint, (uint)FieldOffsets.FontDirectionHint);
            headBuffer.SetShort(this.IndexToLocFormat, (uint)FieldOffsets.IndexToLocFormat);
            headBuffer.SetShort(this.GlyphDataFormat, (uint)FieldOffsets.GlyphDataFormat);

            if (checksumAdjustment)
            {
                // For checksum adjustment, we set this value to 0
                headBuffer.SetUInt(0, (uint)FieldOffsets.CheckSumAdjustment);
            }

            return(headBuffer.GetBuffer());
        }
Пример #2
0
            public bool Read(Stream stream, int directoryCount)
            {
                int bytesRead = 0;

                this.NumTables = WoffBuffer.Read255UInt16(stream);
                if (this.NumTables == 0)
                {
                    return(false);
                }

                this.Flavor = WoffBuffer.ReadUInt32BE(WoffBuffer.ReadBytes(stream, WoffBuffer.SizeOfUInt, out bytesRead), 0);

                this.TableIndices = new ushort[this.NumTables];

                for (ushort i = 0; i < this.NumTables; i++)
                {
                    this.TableIndices[i] = WoffBuffer.Read255UInt16(stream);
                    if (this.TableIndices[i] >= directoryCount)
                    {
                        Trace.TraceError("Invalid collection font entry: " + this.TableIndices[i]);
                        return(false);
                    }
                }
                return(true);
            }
Пример #3
0
            public NameRecord Clone()
            {
                ushort     offset   = 0;
                WoffBuffer bufTable = new WoffBuffer(SizeOf);

                NameRecord cloned = new NameRecord(offset, bufTable);

                cloned.PlatformID   = this.PlatformID;
                cloned.EncodingID   = this.EncodingID;
                cloned.LanguageID   = this.LanguageID;
                cloned.NameID       = this.NameID;
                cloned.StringLength = this.StringLength;
                cloned.StringOffset = this.StringOffset;

                if (_nameBytes != null)
                {
                    byte[] nameBytes = new byte[_nameBytes.Length];
                    Buffer.BlockCopy(_nameBytes, 0, nameBytes, 0, _nameBytes.Length);

                    cloned.NameBytes = nameBytes;
                }
                if (_nameString != null)
                {
                    cloned.NameString = new string(_nameString.ToCharArray());
                }
                return(cloned);
            }
Пример #4
0
        /// <summary>
        /// Initializes a new non-resizable instance of the <see cref="WoffWriter"/> 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 WoffWriter(byte[] buffer, int index, int count)
        {
            if (buffer == null)
            {
                throw new ArgumentNullException(nameof(buffer), "ArgumentNull_Buffer");
            }
            if (index < 0)
            {
                throw new ArgumentOutOfRangeException(nameof(index), "ArgumentOutOfRange_NeedNonNegNum");
            }
            if (count < 0)
            {
                throw new ArgumentOutOfRangeException(nameof(count), "ArgumentOutOfRange_NeedNonNegNum");
            }
            if (buffer.Length - index < count)
            {
                throw new ArgumentException("Argument_InvalidOffLen");
            }

            _converter = new WoffBuffer(WoffBuffer.SizeOfLong * 2); // 16-bytes

            if (buffer.Length == 0)
            {
                _buffer       = new MemoryStream();
                _isExpandable = true;
            }
            else
            {
                _buffer = new MemoryStream(buffer, index, count, true, true);
            }
        }
Пример #5
0
 /// <summary>
 /// Initializes a new instance of the <see cref="WoffReader"/> class with the specified bytes stream.
 /// </summary>
 /// <param name="buffer">A object containing the bytes stream.</param>
 public WoffReader(byte[] buffer)
 {
     if (buffer == null)
     {
         throw new ArgumentNullException(nameof(buffer), "The buffer parameter is required and cannot be null.");
     }
     _buffer = new WoffBuffer(buffer);
 }
Пример #6
0
        public void ChecksumAdjustment(byte[] headersBuffer, uint headerOffset, uint headerLength, uint tablesChecksum = 0)
        {
            Debug.Assert(_headIndex != -1);
            if (_headIndex == -1)
            {
                return;
            }
            var headTable = _woffTables[_headIndex] as WoffTableHead;

            Debug.Assert(headTable != null);
            if (headTable == null)
            {
                return;
            }

            // Compute the checksum of the font's header
            Debug.Assert(headerLength != 0);
            Debug.Assert(headersBuffer != null && headersBuffer.Length != 0);

            var headerPadding = (uint)WoffBuffer.CalcPadBytes((int)headerLength, 4);

            var buffer = headersBuffer;

            if (headerPadding > 0)
            {
                var paddedLength = headerLength + headerPadding;
                buffer = new byte[paddedLength];
                Buffer.BlockCopy(headersBuffer, 0, buffer, 0, headersBuffer.Length);
            }

            uint nLongs         = (headerLength + 3) / 4;
            uint headerChecksum = 0;

            for (uint i = 0; i < nLongs; i += 4)
            {
                headerChecksum += WoffBuffer.GetUIntBE(buffer, i);
            }

            // Compute the checksum of the font by summing the checksum of the header and tables
            var fontChecksum = headerChecksum;

            if (tablesChecksum == 0)
            {
                tablesChecksum = headerChecksum;
                for (ushort i = 0; i < _woffDirs.Count; i++)
                {
                    tablesChecksum += _woffDirs[i].OrigChecksum;
                }
            }
            fontChecksum += headerChecksum;

            // Finally, update the 'head' table's checkSumAdjustment.
            headTable.CheckSumAdjustment = 0xB1B0AFBA - fontChecksum;
        }
Пример #7
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);
            }
        }
Пример #8
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);
        }
Пример #9
0
            public bool Read(Stream stream)
            {
                int bytesRead = 0;

                this.Version = WoffBuffer.ReadUInt32BE(WoffBuffer.ReadBytes(stream,
                                                                            WoffBuffer.SizeOfUInt, out bytesRead), 0);
                this.NumFonts = WoffBuffer.Read255UInt16(stream);

                if (this.Version == 0x00010000 || this.Version == 0x00020000)
                {
                    TtcTag            = WoffUtils.TtcSignature;
                    this.MajorVersion = (ushort)(this.Version == 0x00010000 ? 1 : 2);
                    return(true);
                }

                return(false);
            }
Пример #10
0
        ///  Initializes a new non-resizable instance of the <see cref="WoffWriter"/> 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 WoffWriter(byte[] buffer)
        {
            if (buffer == null)
            {
                throw new ArgumentNullException(nameof(buffer), "ArgumentNull_Buffer");
            }
            _converter = new WoffBuffer(WoffBuffer.SizeOfLong * 2); // 16-bytes

            if (buffer.Length == 0)
            {
                _buffer       = new MemoryStream();
                _isExpandable = true;
            }
            else
            {
                _buffer = new MemoryStream(buffer, 0, buffer.Length, true, true);
            }
        }
Пример #11
0
        /// <summary>
        /// Initializes a new instance of the <see cref="WoffWriter"/> class with a non-resizable
        /// capacity initialized as specified.
        /// </summary>
        /// <param name="capacity">The initial size of the internal array in bytes.</param>
        public WoffWriter(uint capacity)
        {
            if (capacity < 0)
            {
                throw new ArgumentOutOfRangeException(nameof(capacity));
            }
            _converter = new WoffBuffer(WoffBuffer.SizeOfLong * 2); // 16-bytes

            if (capacity == 0)
            {
                _buffer       = new MemoryStream();
                _isExpandable = true;
            }
            else
            {
                _buffer = new MemoryStream(new byte[capacity], 0, (int)capacity, true, true);
            }
        }
Пример #12
0
        protected override bool ReconstructTable()
        {
            var headBuffer = _woffDir.OrigTable;

            if (headBuffer == null || headBuffer.Length < SizeOfTable)
            {
                return(false);
            }
            var length = headBuffer.Length;

            _tableBuffer = new WoffBuffer((uint)length);
            _tableBuffer.Copy(headBuffer);
            // For checksum adjustment, we set this value to 0
            _tableBuffer.SetUInt(0, (uint)FieldOffsets.CheckSumAdjustment);

            _woffDir.OrigTable  = headBuffer;
            _woffDir.OrigLength = (uint)headBuffer.Length;

            return(true);
        }
Пример #13
0
        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);
        }
Пример #14
0
        private bool ReadData(Stream stream)
        {
            if (stream == null)
            {
                return(false);
            }

            int bytesRead = 0;

            byte[] header = WoffBuffer.ReadBytes(stream, WoffUtils.Woff1DirSize, out bytesRead);

            _tag          = WoffBuffer.ReadUInt32BE(header, 0);
            _offset       = WoffBuffer.ReadUInt32BE(header, 4);
            _compLength   = WoffBuffer.ReadUInt32BE(header, 8);
            _origLength   = WoffBuffer.ReadUInt32BE(header, 12);
            _origChecksum = WoffBuffer.ReadUInt32BE(header, 16);

            if (_origLength > 0)
            {
                _padding = WoffUtils.CalculatePadding(_origLength);
            }

            return(true);
        }
Пример #15
0
        public uint CalculateChecksum()
        {
            Debug.Assert(_origLength != 0);
            Debug.Assert(_origTable != null && _origTable.Length != 0);

            var padBytesLength = (uint)WoffBuffer.CalcPadBytes((int)_origLength, 4);

            if (padBytesLength != 0)
            {
                _padding = padBytesLength;
            }

            var origLength = _origLength;
            var buffer     = _origTable;

            if (_padding > 0)
            {
                origLength = _origLength + _padding;
                buffer     = new byte[origLength];
                Buffer.BlockCopy(_origTable, 0, buffer, 0, _origTable.Length);
            }

            uint nLongs = (_origLength + 3) / 4;
            uint sum    = 0;

            //for (uint i = 0; i < nLongs; i++)
            //{
            //    sum += WoffBuffer.GetUIntBE(buffer, i * 4);
            //}
            for (uint i = 0; i < nLongs; i += 4)
            {
                sum += WoffBuffer.GetUIntBE(buffer, i);
            }

            return(sum);
        }
Пример #16
0
        public static bool BinaryEqual(WoffBuffer buf1, WoffBuffer buf2)
        {
            bool bEqual = true;

            if (buf1.GetLength() != buf2.GetLength())
            {
                bEqual = false;
            }
            else
            {
                byte[] b1 = buf1.GetBuffer();
                byte[] b2 = buf2.GetBuffer();
                for (int i = 0; i < b1.Length; i++)
                {
                    if (b1[i] != b2[i])
                    {
                        bEqual = false;
                        break;
                    }
                }
            }

            return(bEqual);
        }
Пример #17
0
 /// <summary>
 /// Initializes a new instance of the <see cref="WoffWriter"/> class with an expandable
 /// capacity initialized to zero.
 /// </summary>
 public WoffWriter()
 {
     _buffer       = new MemoryStream();
     _converter    = new WoffBuffer(WoffBuffer.SizeOfLong * 2); // 16-bytes
     _isExpandable = true;
 }
Пример #18
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);
        }
Пример #19
0
 public NameRecord(ushort offset, WoffBuffer bufTable)
 {
     _offsetNameRecord = offset;
     _tableBuffer      = bufTable;
 }
Пример #20
0
        protected override bool ReconstructTable()
        {
            var headBuffer = _woffDir.OrigTable;

            if (headBuffer == null || headBuffer.Length < 6)
            {
                return(false);
            }
            var length = headBuffer.Length;

            _tableBuffer = new WoffBuffer((uint)length);
            _tableBuffer.Copy(headBuffer);

            var fullFontName   = this.GetNameString();
            var fontFamilyName = this.GetFamilyString();
            var postScriptName = this.GetPostScriptString();

            // Check if the FullFontName and FontFamilyName are stripped from the compressed table data
            // Some tools (such as the dvisvgm – A fast DVI to SVG converter) strip both leaving only the PostScriptName
            if (!string.IsNullOrWhiteSpace(fullFontName) && !string.IsNullOrWhiteSpace(fontFamilyName))
            {
                return(true);
            }

            var fixFullFontName   = string.IsNullOrWhiteSpace(fullFontName);
            var fixFontFamilyName = string.IsNullOrWhiteSpace(fontFamilyName);

            // For the fix, we will just duplicate an existing name record, find out which one.
            ushort searchRecord = 0;

            if (!fixFullFontName)
            {
                searchRecord = (ushort)NameIdentifiers.FullFontName;
            }
            else if (!fixFontFamilyName)
            {
                searchRecord = (ushort)NameIdentifiers.FontFamilyName;
            }
            else if (!string.IsNullOrWhiteSpace(postScriptName))
            {
                searchRecord = (ushort)NameIdentifiers.PostScriptName;
            }
            else
            {
                // We cannot fix it...
                return(true);
            }

            int numRecords = this.NumberNameRecords;
            List <NameRecord> nameRecords = new List <NameRecord>(numRecords);

            for (uint i = 0; i < numRecords; i++)
            {
                NameRecord nr = GetNameRecord(i);
                if (nr != null)
                {
                    byte[] nameBytes = GetEncodedString(nr);
                    if (nameBytes != null && nameBytes.Length != 0)
                    {
                        nr.NameBytes  = nameBytes;
                        nr.NameString = DecodeString(nr.PlatformID, nr.EncodingID, nameBytes);

                        nameRecords.Add(nr);

                        if (searchRecord == nr.NameID)
                        {
                            if (fixFontFamilyName)
                            {
                                var nrFontFamily = nr.Clone();
                                nrFontFamily.NameID = (ushort)NameIdentifiers.FontFamilyName;

                                nameRecords.Add(nrFontFamily);
                            }
                            if (fixFullFontName)
                            {
                                var nrFullFont = nr.Clone();
                                nrFullFont.NameID = (ushort)NameIdentifiers.FullFontName;

                                nameRecords.Add(nrFullFont);
                            }
                        }
                    }
                }
            }

            numRecords = nameRecords.Count;

            List <byte[]> bytesNameString = new List <byte[]>();
            uint          lengthOfStrings = 0;
            ushort        offsetToStrings = (ushort)(6 + (numRecords * 12));

            for (int i = 0; i < numRecords; i++)
            {
                var    nrc        = nameRecords[i];
                byte[] byteString = nrc.NameBytes;
                bytesNameString.Add(byteString);
                lengthOfStrings += (ushort)byteString.Length;
            }

            // create a Motorola Byte Order buffer for the new table
            var tableBuffer = new WoffBuffer((uint)((ushort)FieldOffsets.NameRecords + numRecords * 12 + lengthOfStrings));

            // populate the buffer
            tableBuffer.SetUShort(this.FormatSelector, (uint)FieldOffsets.FormatSelector);
            tableBuffer.SetUShort((ushort)numRecords, (uint)FieldOffsets.NumberNameRecords);
            tableBuffer.SetUShort(offsetToStrings, (uint)FieldOffsets.OffsetToStrings);

            ushort nOffset = 0;

            // Write the NameRecords and Strings
            for (int i = 0; i < numRecords; i++)
            {
                byte[] namBytes = bytesNameString[i];

                uint startOffset = (uint)((ushort)(FieldOffsets.NameRecords) + i * NameRecord.SizeOf);

                tableBuffer.SetUShort((nameRecords[i]).PlatformID, startOffset + (ushort)NameRecord.FieldOffsets.PlatformID);
                tableBuffer.SetUShort((nameRecords[i]).EncodingID, startOffset + (ushort)NameRecord.FieldOffsets.EncodingID);
                tableBuffer.SetUShort((nameRecords[i]).LanguageID, startOffset + (ushort)NameRecord.FieldOffsets.LanguageID);
                tableBuffer.SetUShort((nameRecords[i]).NameID, startOffset + (ushort)NameRecord.FieldOffsets.NameID);
                tableBuffer.SetUShort((ushort)namBytes.Length, startOffset + (ushort)NameRecord.FieldOffsets.StringLength);
                tableBuffer.SetUShort(nOffset, startOffset + (ushort)NameRecord.FieldOffsets.StringOffset);

                //Write the string to the buffer
                for (int j = 0; j < namBytes.Length; j++)
                {
                    tableBuffer.SetByte(namBytes[j], (uint)(offsetToStrings + nOffset + j));
                }

                nOffset += (ushort)namBytes.Length;
            }


            _tableBuffer = tableBuffer;

            var nameBuffer = _tableBuffer.GetBuffer();

            _woffDir.OrigTable  = nameBuffer;
            _woffDir.OrigLength = (uint)nameBuffer.Length;
            _woffDir.RecalculateChecksum();

            return(true);
        }
Пример #21
0
        private bool WriteWoff1(Stream stream)
        {
            if (stream == null || _woffHeader == null || _woffDirs == null || _woffDirs.Count == 0)
            {
                return(false);
            }

            var numTables     = _woffHeader.NumTables;
            var searchRange   = (ushort)(WoffUtils.MaxPower2LE(numTables) * TableSize);
            var entrySelector = WoffUtils.Log2(WoffUtils.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 += WoffBuffer.WriteUInt32BE(headerBuffer, offset, _woffHeader.Flavor);
            // uint16:	numTables	Number of tables.
            offset += WoffBuffer.WriteUInt16BE(headerBuffer, offset, _woffHeader.NumTables);
            // uint16	searchRange	(Maximum power of 2 <= numTables) x 16.
            offset += WoffBuffer.WriteUInt16BE(headerBuffer, offset, searchRange);
            // uint16:	entrySelector	Log2(maximum power of 2 <= numTables).
            offset += WoffBuffer.WriteUInt16BE(headerBuffer, offset, entrySelector);
            // uint16:	rangeShift	NumTables x 16-searchRange.
            offset += WoffBuffer.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 woffDir = _woffDirs[i];
                // Tag:	tableTag	Table identifier.
                offset += WoffBuffer.WriteUInt32BE(tableBuffer, offset, woffDir.Tag);
                // uint32:	checkSum	CheckSum for this table.
                offset += WoffBuffer.WriteUInt32BE(tableBuffer, offset, woffDir.OrigChecksum);
                // Offset32	offset	Offset from beginning of TrueType font file.
                offset += WoffBuffer.WriteUInt32BE(tableBuffer, offset, tablesOffset);
                // uint32	length	Length of this table.
                offset += WoffBuffer.WriteUInt32BE(tableBuffer, offset, woffDir.OrigLength);

                stream.Write(tableBuffer, 0, (int)TableSize);

                tablesOffset += woffDir.OrigLength;
                if (tablesOffset % 4 != 0)
                {
                    tablesOffset += 4 - (tablesOffset % 4);
                }
            }

            var paddingBuffer = new byte[4];

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

                stream.Write(woffDir.OrigTable, 0, (int)woffDir.OrigLength);
                stream.Write(paddingBuffer, 0, (int)woffDir.Padding);
            }

            return(true);
        }
Пример #22
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);
        }
Пример #23
0
        private bool ReadWoff2Font(Stream stream)
        {
            var woffFont = new WoffFont(_woffHeader);

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

            // 2. TableDirectory: Directory of font tables, containing size and other info.
            for (ushort i = 0; i < _woffHeader.NumTables; i++)
            {
                var woffDir = _woffDirs[i];

                woffFont.AddDirectory(woffDir);
            }

            // 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 = WoffBuffer.ReadBytes(stream, bytesCount, out bytesRead);
                Debug.Assert(bytesRead == bytesCount);
                if (bytesRead != bytesCount)
                {
                    return(false);
                }

                bool errorOccurred = false;

                var memoryStream = new MemoryStream(compressedBuffer);

                using (var brotliStream = new BrotliInputStream(memoryStream))
                {
                    var decompressedStream = new MemoryStream();
                    brotliStream.CopyTo(decompressedStream);
                    decompressedStream.Seek(0, SeekOrigin.Begin);

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

                        try
                        {
                            // bytesCount = (int)woffDir.TransformLength;
                            bytesCount = (int)woffDir.CompLength;

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

                            var tableBuffer = WoffBuffer.ReadBytes(decompressedStream, bytesCount, out bytesRead);

                            Debug.Assert(bytesRead == bytesCount);

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

                            woffDir.CompTable = tableBuffer;
                            woffDir.OrigTable = tableBuffer;
                        }
                        catch (Exception ex)
                        {
                            errorOccurred = true;
                            Trace.TraceError(ex.Message);
                        }
                    }
                }

                if (errorOccurred)
                {
                    return(false);
                }
            }

            woffFont.EndDirectory();
            return(true);
        }
Пример #24
0
        private bool ReadWoff2Fonts(Stream stream)
        {
            // 1. Read the collection font header
            _collectionHeader = new CollectionHeader();
            if (_collectionHeader.Read(stream) == false)
            {
                Trace.TraceError("Collection Font Error: Reading of collection header failed.");
                return(false);
            }

            // 2. Read this collection font entries
            var numFonts = _collectionHeader.NumFonts;

            _collectionEntries = new List <CollectionFontEntry>(numFonts);
            for (ushort i = 0; i < numFonts; i++)
            {
                var collectionEntry = new CollectionFontEntry(i);
                if (collectionEntry.Read(stream, _woffDirs.Count) == false)
                {
                    return(false);
                }
                _collectionEntries.Add(collectionEntry);
            }

            // 3. TableDirectory: Directory of font tables, containing size and other info.
            for (ushort i = 0; i < numFonts; i++)
            {
                var collectionEntry = _collectionEntries[i];

                var woffFont = new WoffFont(_woffHeader, _collectionHeader, collectionEntry);
                _woffFonts.Add(woffFont);
                woffFont.BeginDirectory(_woffVersion);

                for (ushort j = 0; j < collectionEntry.NumTables; j++)
                {
                    var index = collectionEntry.TableIndices[j];

                    var woffDir = _woffDirs[index];

                    woffFont.AddDirectory(woffDir);
                }
            }

            // 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 = WoffBuffer.ReadBytes(stream, bytesCount, out bytesRead);
                Debug.Assert(bytesRead == bytesCount);
                if (bytesRead != bytesCount)
                {
                    return(false);
                }

                bool errorOccurred = false;

                var memoryStream = new MemoryStream(compressedBuffer);

                using (var brotliStream = new BrotliInputStream(memoryStream))
                {
                    var decompressedStream = new MemoryStream();
                    brotliStream.CopyTo(decompressedStream);
                    decompressedStream.Seek(0, SeekOrigin.Begin);

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

                        try
                        {
                            // bytesCount = (int)woffDir.TransformLength;
                            bytesCount = (int)woffDir.CompLength;

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

                            var tableBuffer = WoffBuffer.ReadBytes(decompressedStream, bytesCount, out bytesRead);

                            Debug.Assert(bytesRead == bytesCount);

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

                            woffDir.CompTable = tableBuffer;
                            woffDir.OrigTable = tableBuffer;
                        }
                        catch (Exception ex)
                        {
                            errorOccurred = true;
                            Trace.TraceError(ex.Message);
                        }
                    }
                }

                if (errorOccurred)
                {
                    return(false);
                }
            }

            for (ushort i = 0; i < numFonts; i++)
            {
                var collectionEntry = _collectionEntries[i];

                var woffFont = _woffFonts[i];
                _woffFonts.Add(woffFont);
                woffFont.EndDirectory();
            }

            return(true);
        }
Пример #25
0
        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);
        }
Пример #26
0
        private bool ReadData(Stream stream, bool isTrueType)
        {
            if (stream == null)
            {
                return(false);
            }
            var tableFlags = WoffTable.TableFlags;

            int bytesRead = 0;

            // UInt8: flags	table type and flags
            _flags = (byte)stream.ReadByte();
            // The interpretation of the flags field is as follows:
            // 1. Bits [0..5] contain an index to the "known tag" table, which represents tags likely to appear in fonts
            int tagIndex = _flags & 0x3F;

            // If the tag is not present in this table, then the value of this bit field is 63.
            if (tagIndex >= 63)
            {
                // UInt32: tag	4-byte tag (optional)
                _tag = WoffBuffer.ReadUInt32BE(WoffBuffer.ReadBytes(stream, 4, out bytesRead), 0);
                var name = WoffBuffer.TagString(_tag);

                for (int i = 0; i < tableFlags.Count; i++)
                {
                    if (string.Equals(name, tableFlags[i], StringComparison.Ordinal))
                    {
                        tagIndex = i;
                        break;
                    }
                }
            }
            else
            {
                _tag = WoffBuffer.TagInt(tableFlags[tagIndex]);
            }

            // 2. Bits 6 and 7 indicate the preprocessing transformation version number (0-3) that was applied to each table.
            int transformVersion = (_flags >> 5) & 0x3;

            // UIntBase128:	origLength	length of original table
            if (!WoffBuffer.ReadUIntBase128(stream, out _origLength))
            {
                return(false);
            }

            // UIntBase128	transformLength	transformed length (if applicable)
            //            _transformLength = _origLength;
            _compLength      = _origLength;
            _transformLength = 0;

            // For all tables in a font, except for 'glyf' and 'loca' tables, transformation version 0
            // indicates the null transform where the original table data is passed directly to the
            // Brotli compressor for inclusion in the compressed data stream.
            if (transformVersion == 0)
            {
                // "glyf" : 10,  "loca" : 11
                if (tagIndex == 10 || tagIndex == 11)
                {
                    if (!WoffBuffer.ReadUIntBase128(stream, out _transformLength))
                    {
                        return(false);
                    }
                    _compLength = _transformLength;
                }
            }
            // The transformation version "1", specified below, is optional and can be applied to
            // eliminate certain redundancies in the hmtx table data.
            if (transformVersion == 1)
            {
                // The transformation version 1 described in this subclause is optional and can only
                // be used when an input font is TrueType-flavored (i.e. has a glyf table)
                // "hmtx" : 3
                if (tagIndex == 3 && isTrueType)
                {
                    if (!WoffBuffer.ReadUIntBase128(stream, out _transformLength))
                    {
                        return(false);
                    }
                    _compLength = _transformLength;
                }
            }

            if (_origLength > 0)
            {
                _padding = WoffUtils.CalculatePadding(_origLength);
            }

            return(true);
        }
Пример #27
0
        public bool Read(Stream stream)
        {
            if (stream == null)
            {
                return(false);
            }
            Debug.Assert(_woffVersion == WoffUtils.Woff1Version || _woffVersion == WoffUtils.Woff2Version);

            var headerSize = this.HeaderSize;

            var header = new byte[headerSize];

            var sizeRead = stream.Read(header, 0, headerSize);

            Debug.Assert(sizeRead == headerSize);

            var bufferSize = header.Length;

            _signature = WoffBuffer.ReadUInt32BE(header, 0);
            _flavor    = WoffBuffer.ReadUInt32BE(header, 4);
            _length    = WoffBuffer.ReadUInt32BE(header, 8);

            _numTables = WoffBuffer.ReadUInt16BE(header, 12);
            _reserved  = WoffBuffer.ReadUInt16BE(header, 14);

            _totalSfntSize = WoffBuffer.ReadUInt32BE(header, 16);
            if (_woffVersion == WoffUtils.Woff1Version)
            {
                _majorVersion = WoffBuffer.ReadUInt16BE(header, 20);
                _minorVersion = WoffBuffer.ReadUInt16BE(header, 22);

                _metaOffset     = WoffBuffer.ReadUInt32BE(header, 24);
                _metaLength     = WoffBuffer.ReadUInt32BE(header, 28);
                _metaOrigLength = WoffBuffer.ReadUInt32BE(header, 32);
                _privateOffset  = WoffBuffer.ReadUInt32BE(header, 36);
                _privateLength  = WoffBuffer.ReadUInt32BE(header, 40);
            }
            else
            {
                _totalCompressedSize = WoffBuffer.ReadUInt32BE(header, 20);

                _majorVersion = WoffBuffer.ReadUInt16BE(header, 24);
                _minorVersion = WoffBuffer.ReadUInt16BE(header, 26);

                _metaOffset     = WoffBuffer.ReadUInt32BE(header, 28);
                _metaLength     = WoffBuffer.ReadUInt32BE(header, 32);
                _metaOrigLength = WoffBuffer.ReadUInt32BE(header, 36);
                _privateOffset  = WoffBuffer.ReadUInt32BE(header, 40);
                _privateLength  = WoffBuffer.ReadUInt32BE(header, 44);
            }

            // The signature field in the WOFF-1 header must contain the "magic number" 0x774F4646.
            // If the field does not contain this value, user agents must reject the file as invalid.
            Debug.Assert((bufferSize == WoffUtils.Woff1HeaderSize)
                ? _signature == WoffUtils.Woff1Signature : _signature == WoffUtils.Woff2Signature);

            // The header includes a reserved field; this must be set to zero.
            // If this field is non-zero, a conforming user agent must reject the file as invalid.
            Debug.Assert(_reserved == 0);

            return(true);
        }