Пример #1
0
        static byte[] TryFixNameTable(ref byte[] data)
        {
            /*
             * For a font to render properly in the XAML engine it requires
             * a FamilyName to be properly set in the font's 'name' table.
             * It is valid to not have this value set and web fonts may
             * intentionally leave this blank as most web-font renderers will
             * work fine without it. However as we need it to get XAML to render
             * the font we need to manually pull the font name out from somewhere
             * else and stick it in the expected place.
             */

            using var ms = new MemoryStream(data);
            using var t  = new BEBinaryReader(ms);

            var version = t.ReadUInt16();

            if (version == 0)
            {
                var count  = t.ReadUInt16();
                var offset = t.ReadUInt16();
                var recs   = Enumerable.Range(0, count).Select(_ =>
                {
                    return(new NameRecord
                    {
                        PlatformId = t.ReadUInt16(),
                        EncodingId = t.ReadUInt16(),
                        LangId = t.ReadUInt16(),
                        NameId = t.ReadUInt16(),
                        Length = t.ReadUInt16(),
                        Offset = t.ReadUInt16()
                    });
                }).ToList();



                // Debug code to view table data.
                //var options = recs.Where(r => r.PlatformId == 3).ToList();
                //var ns = recs.Where(r => r.NameId == 1).ToList();
                //foreach (var rec in recs)
                //{
                //    ms.Seek(rec.Offset + offset, SeekOrigin.Begin);
                //    byte[] buf = t.ReadBytes(rec.Length);
                //    Encoding enc2;
                //    if (rec.EncodingId == 3 || rec.EncodingId == 1)
                //    {
                //        enc2 = Encoding.BigEndianUnicode;
                //    }
                //    else
                //    {
                //        enc2 = Encoding.UTF8;
                //    }
                //    string strRet = enc2.GetString(buf, 0, buf.Length);
                //    System.Diagnostics.Debug.WriteLine(strRet);
                //}

                var fam  = recs.FirstOrDefault(r => r.PlatformId == 3 && r.NameId == 1);
                var post = recs.FirstOrDefault(r => r.NameId == 6);
                if (fam is not null && post is not null &&
                    fam.Length == 0 && post.Length != 0)
                {
                    fam.Length = post.Length;
                    fam.Offset = post.Offset;
                }
                else
                {
                    return(null);
                }


                using var mso = new MemoryStream();
                using var w   = new BEBinaryWriter(mso);
                w.Write(version);
                w.Write(count);
                w.Write(offset);
                foreach (var r in recs)
                {
                    w.Write(r.PlatformId);
                    w.Write(r.EncodingId);
                    w.Write(r.LangId);
                    w.Write(r.NameId);
                    w.Write(r.Length);
                    w.Write(r.Offset);
                }
                w.Flush();

                ms.Seek(offset, SeekOrigin.Begin);
                ms.CopyTo(mso);
                mso.Flush();
                return(mso.ToArray());
            }
Пример #2
0
        public static ConversionStatus Convert(
            Stream input,
            Stream output)
        {
            using var reader = new BEBinaryReader(input);

            var header = new WoffHeader
            {
                Signature         = reader.ReadUInt32(),
                Flavor            = reader.ReadUInt32(),
                Length            = reader.ReadUInt32(),
                TableCount        = reader.ReadUInt16(),
                Reserved          = reader.ReadUInt16(),
                TotalSfntSize     = reader.ReadUInt32(),
                MajorVersion      = reader.ReadUInt16(),
                MinorVersion      = reader.ReadUInt16(),
                MetaOffset        = reader.ReadUInt32(),
                MetaLength        = reader.ReadUInt32(),
                MetaOrignalLength = reader.ReadUInt32(),
                PrivOffset        = reader.ReadUInt32(),
                PrivLength        = reader.ReadUInt32()
            };

            if (header.Signature is not 0x774F4646)
            {
                if (header.Signature is 0x774F4632)
                {
                    return(ConversionStatus.UnsupportedWOFF2);
                }
                else
                {
                    return(ConversionStatus.UnrecognisedFile);
                }
            }


            UInt32 offset = 12;

            // Read Table Headers
            List <TableDirectory> entries = new List <TableDirectory>();

            for (var i = 0; i < header.TableCount; i++)
            {
                var entry = new TableDirectory
                {
                    Tag              = reader.ReadUInt32(),
                    Offset           = reader.ReadUInt32(),
                    CompressedLength = reader.ReadUInt32(),
                    OriginalLength   = reader.ReadUInt32(),
                    OriginalChecksum = reader.ReadUInt32()
                };

                if (Name(entry.Tag) == "DSIG") // Conversion invalidates
                {
                    continue;
                }

                entries.Add(entry);
                offset += (4 * 4);
            }

            // Amend table count after removing DSIG
            header.TableCount = (ushort)entries.Count;

            // Calculate header values
            UInt16 entrySelector = 0;

            while (Math.Pow(2, entrySelector) <= header.TableCount)
            {
                entrySelector++;
            }
            entrySelector--;

            UInt16 searchRange = (UInt16)(Math.Pow(2, entrySelector) * 16);
            UInt16 rangeShift  = (UInt16)(header.TableCount * 16 - searchRange);

            // Create writer
            using var writer = new BEBinaryWriter(output);

            // Write Font Header
            writer.Write(header.Flavor);
            writer.Write(header.TableCount);
            writer.Write(searchRange);
            writer.Write(entrySelector);
            writer.Write(rangeShift);

            // Write Table Headers
            foreach (var entry in entries)
            {
                writer.Write(entry.Tag);
                writer.Write(entry.OriginalChecksum);
                writer.Write(offset);
                writer.Write(entry.OriginalLength);
                entry.OutputOffset = offset;

                offset += entry.OriginalLength;
                if ((offset % 4) != 0)
                {
                    offset += 4 - (offset % 4);
                }
            }

            // Write Table contents
            foreach (var entry in entries)
            {
                input.Seek(entry.Offset, SeekOrigin.Begin);
                byte[] compressed = reader.ReadBytes((int)entry.CompressedLength);
                byte[] uncompressed;
                if (entry.CompressedLength != entry.OriginalLength)
                {
                    // Decompress table
                    using var comp = new MemoryStream(compressed.AsSpan().Slice(2).ToArray()); // Ignore the ZLib header (2 bytes long)
                    using var outs = new MemoryStream();
                    using var def  = new DeflateStream(comp, CompressionMode.Decompress);
                    def.CopyTo(outs);
                    uncompressed = outs.ToArray();
                }
                else
                {
                    uncompressed = compressed;
                }

                if (uncompressed.Length != entry.OriginalLength)
                {
                    return(ConversionStatus.TableLengthMismatch);
                }

                if (entry.ToString() == "name")
                {
                    if (TryFixNameTable(ref uncompressed) is byte[] ammended)
                    {
                        uncompressed = ammended;
                    }
                }

                output.Seek(entry.OutputOffset, SeekOrigin.Begin);
                writer.Write(uncompressed);
                offset = (uint)output.Position;
                if (offset % 4 != 0)
                {
                    uint padding = 4 - (offset % 4);
                    writer.Write(new byte[padding]);
                }
            }

            writer.Flush();
            output.Flush();

            return(ConversionStatus.OK);
        }