Example #1
0
        public static void ReadMaxp(DataReader reader, TableRecord[] tables, ref FaceHeader header)
        {
            SeekToTable(reader, tables, FourCC.Maxp, required: true);

            if (reader.ReadInt32BE() != 0x00010000)
                throw new InvalidFontException("Font contains an old style maxp table.");

            header.GlyphCount = reader.ReadUInt16BE();
            if (header.GlyphCount > MaxGlyphs)
                throw new InvalidFontException("Font contains too many glyphs.");

            // skip maxPoints, maxContours, maxCompositePoints, maxCompositeContours, maxZones
            reader.Skip(sizeof(short) * 5);

            header.MaxTwilightPoints = reader.ReadUInt16BE();
            header.MaxStorageLocations = reader.ReadUInt16BE();
            header.MaxFunctionDefs = reader.ReadUInt16BE();
            header.MaxInstructionDefs = reader.ReadUInt16BE();
            header.MaxStackSize = reader.ReadUInt16BE();

            // sanity checking
            if (header.MaxTwilightPoints > MaxTwilightPoints || header.MaxStorageLocations > MaxStorageLocations ||
                header.MaxFunctionDefs > MaxFunctionDefs || header.MaxInstructionDefs > MaxFunctionDefs ||
                header.MaxStackSize > MaxStackSize)
                throw new InvalidFontException("Font programs have limits that are larger than built-in sanity checks.");
        }
Example #2
0
        public static TableRecord[] ReadFaceHeader(DataReader reader)
        {
            var tag = reader.ReadUInt32BE();
            //if (tag != TTFv1 && tag != TTFv2 && tag != FourCC.True)
            //    throw new InvalidFontException("Unknown or unsupported sfnt version.");

            var tableCount = reader.ReadUInt16BE();

            reader.Skip(6); // skip the rest of the header

            // read each font table descriptor
            var tables = new TableRecord[tableCount];

            for (int i = 0; i < tableCount; i++)
            {
                tables[i] = new TableRecord {
                    Tag      = reader.ReadUInt32(),
                    CheckSum = reader.ReadUInt32BE(),
                    Offset   = reader.ReadUInt32BE(),
                    Length   = reader.ReadUInt32BE(),
                };
            }

            return(tables);
        }
Example #3
0
        public unsafe static SbitTable Read(DataReader reader, TableRecord[] tables)
        {
            if (!SfntTables.SeekToTable(reader, tables, FourCC.Eblc))
                return null;

            // skip version
            var baseOffset = reader.Position;
            reader.Skip(sizeof(int));

            // load each strike table
            var count = reader.ReadInt32BE();
            if (count > MaxBitmapStrikes)
                throw new InvalidFontException("Too many bitmap strikes in font.");

            var sizeTableHeaders = stackalloc BitmapSizeTable[count];
            for (int i = 0; i < count; i++)
            {
                sizeTableHeaders[i].SubTableOffset = reader.ReadUInt32BE();
                sizeTableHeaders[i].SubTableSize = reader.ReadUInt32BE();
                sizeTableHeaders[i].SubTableCount = reader.ReadUInt32BE();

                // skip colorRef, metrics entries, start and end glyph indices
                reader.Skip(sizeof(uint) + sizeof(ushort) * 2 + 12 * 2);

                sizeTableHeaders[i].PpemX = reader.ReadByte();
                sizeTableHeaders[i].PpemY = reader.ReadByte();
                sizeTableHeaders[i].BitDepth = reader.ReadByte();
                sizeTableHeaders[i].Flags = (BitmapSizeFlags)reader.ReadByte();
            }

            // read index subtables
            var indexSubTables = stackalloc IndexSubTable[count];
            for (int i = 0; i < count; i++)
            {
                reader.Seek(baseOffset + sizeTableHeaders[i].SubTableOffset);
                indexSubTables[i] = new IndexSubTable
                {
                    FirstGlyph = reader.ReadUInt16BE(),
                    LastGlyph = reader.ReadUInt16BE(),
                    Offset = reader.ReadUInt32BE()
                };
            }

            // read the actual data for each strike table
            for (int i = 0; i < count; i++)
            {
                // read the subtable header
                reader.Seek(baseOffset + sizeTableHeaders[i].SubTableOffset + indexSubTables[i].Offset);
                var indexFormat = reader.ReadUInt16BE();
                var imageFormat = reader.ReadUInt16BE();
                var imageDataOffset = reader.ReadUInt32BE();


            }

            return null;
        }
Example #4
0
        public static void ReadHead(DataReader reader, TableRecord[] tables, out FaceHeader header)
        {
            SeekToTable(reader, tables, FourCC.Head, required: true);

            // 'head' table contains global information for the font face
            // we only care about a few fields in it
            reader.Skip(sizeof(int) * 4);   // version, revision, checksum, magic number

            header = new FaceHeader
            {
                Flags = (HeadFlags)reader.ReadUInt16BE(),
                UnitsPerEm = reader.ReadUInt16BE()
            };
            if (header.UnitsPerEm == 0)
                throw new InvalidFontException("Invalid 'head' table.");

            // skip over created and modified times, bounding box,
            // deprecated style bits, direction hints, and size hints
            reader.Skip(sizeof(long) * 2 + sizeof(short) * 7);

            header.IndexFormat = (IndexFormat)reader.ReadInt16BE();
        }
Example #5
0
        public static TableRecord[] ReadFaceHeader(DataReader reader)
        {
            var tag = reader.ReadUInt32BE();
            if (tag != TTFv1 && tag != TTFv2 && tag != FourCC.True)
                throw new InvalidFontException("Unknown or unsupported sfnt version.");

            var tableCount = reader.ReadUInt16BE();
            reader.Skip(6); // skip the rest of the header

            // read each font table descriptor
            var tables = new TableRecord[tableCount];
            for (int i = 0; i < tableCount; i++)
            {
                tables[i] = new TableRecord
                {
                    Tag = reader.ReadUInt32(),
                    CheckSum = reader.ReadUInt32BE(),
                    Offset = reader.ReadUInt32BE(),
                    Length = reader.ReadUInt32BE(),
                };
            }

            return tables;
        }
Example #6
0
        public static KerningTable ReadKern(DataReader reader, TableRecord[] tables)
        {
            // kern table is optional
            if (!SfntTables.SeekToTable(reader, tables, FourCC.Kern))
                return null;

            // skip version
            reader.Skip(sizeof(short));

            // read each subtable and accumulate kerning values
            var tableData = new Dictionary<uint, int>();
            var subtableCount = reader.ReadUInt16BE();
            for (int i = 0; i < subtableCount; i++)
            {
                // skip version
                var currentOffset = reader.Position;
                reader.Skip(sizeof(short));

                var length = reader.ReadUInt16BE();
                var coverage = reader.ReadUInt16BE();

                // we (and Windows) only support Format 0 tables
                // only care about tables with horizontal kerning data
                var kc = (KernCoverage)coverage;
                if ((coverage & FormatMask) == 0 && (kc & KernCoverage.Horizontal) != 0 && (kc & KernCoverage.CrossStream) == 0)
                {
                    // read the number of entries; skip over the rest of the header
                    var entryCount = reader.ReadUInt16BE();
                    reader.Skip(sizeof(short) * 3);

                    var isMin = (kc & KernCoverage.Minimum) != 0;
                    var isOverride = (kc & KernCoverage.Override) != 0;

                    // read in each entry and accumulate its kerning data
                    for (int j = 0; j < entryCount; j++)
                    {
                        var left = reader.ReadUInt16BE();
                        var right = reader.ReadUInt16BE();
                        var value = reader.ReadInt16BE();

                        // look up the current value, if we have one; if not, start at zero
                        int current = 0;
                        var key = ((uint)left << 16) | right;
                        tableData.TryGetValue(key, out current);

                        if (isMin)
                        {
                            if (current < value)
                                tableData[key] = value;
                        }
                        else if (isOverride)
                            tableData[key] = value;
                        else
                            tableData[key] = current + value;
                    }
                }

                // jump to the next subtable
                reader.Seek(currentOffset + length);
            }

            return new KerningTable(tableData);
        }
Example #7
0
        public static CharacterMap ReadCmap(DataReader reader, TableRecord[] tables)
        {
            SfntTables.SeekToTable(reader, tables, FourCC.Cmap, required: true);

            // skip version
            var cmapOffset = reader.Position;
            reader.Skip(sizeof(short));

            // read all of the subtable headers
            var subtableCount = reader.ReadUInt16BE();
            var subtableHeaders = new CmapSubtableHeader[subtableCount];
            for (int i = 0; i < subtableHeaders.Length; i++)
            {
                subtableHeaders[i] = new CmapSubtableHeader
                {
                    PlatformID = reader.ReadUInt16BE(),
                    EncodingID = reader.ReadUInt16BE(),
                    Offset = reader.ReadUInt32BE()
                };
            }

            // search for a "full" Unicode table first
            var chosenSubtableOffset = 0u;
            for (int i = 0; i < subtableHeaders.Length; i++)
            {
                var platform = subtableHeaders[i].PlatformID;
                var encoding = subtableHeaders[i].EncodingID;
                if ((platform == PlatformID.Microsoft && encoding == WindowsEncoding.UnicodeFull) ||
                    (platform == PlatformID.Unicode && encoding == UnicodeEncoding.Unicode32))
                {

                    chosenSubtableOffset = subtableHeaders[i].Offset;
                    break;
                }
            }

            // if no full unicode table, just grab the first
            // one that supports any flavor of Unicode
            if (chosenSubtableOffset == 0)
            {
                for (int i = 0; i < subtableHeaders.Length; i++)
                {
                    var platform = subtableHeaders[i].PlatformID;
                    var encoding = subtableHeaders[i].EncodingID;
                    if ((platform == PlatformID.Microsoft && encoding == WindowsEncoding.UnicodeBmp) ||
                         platform == PlatformID.Unicode)
                    {

                        chosenSubtableOffset = subtableHeaders[i].Offset;
                        break;
                    }
                }
            }

            // no unicode support at all is an error
            if (chosenSubtableOffset == 0)
                throw new Exception("Font does not support Unicode.");

            // jump to our chosen table and find out what format it's in
            reader.Seek(cmapOffset + chosenSubtableOffset);
            var format = reader.ReadUInt16BE();
            switch (format)
            {
                case 4: return ReadCmapFormat4(reader);
                default: throw new Exception("Unsupported cmap format.");
            }
        }
Example #8
0
        public static bool SeekToTable(DataReader reader, TableRecord[] tables, FourCC tag, bool required = false)
        {
            // check if we have the desired table and that it's not empty
            var index = FindTable(tables, tag);
            if (index == -1 || tables[index].Length == 0)
            {
                if (required)
                    //throw new InvalidFontException($"Missing or empty '{tag}' table.");
                    throw new InvalidFontException(string.Format(
                        "Missing or empty '{0}' table.", tag));
                return false;
            }

            // seek to the appropriate offset
            reader.Seek(tables[index].Offset);
            return true;
        }
Example #9
0
        public static byte[] ReadProgram(DataReader reader, TableRecord[] tables, FourCC tag)
        {
            var index = FindTable(tables, tag);
            if (index == -1)
                return null;

            reader.Seek(tables[index].Offset);
            return reader.ReadBytes((int)tables[index].Length);
        }
Example #10
0
        public static int FindTable(TableRecord[] tables, FourCC tag)
        {
            var index = -1;
            for (int i = 0; i < tables.Length; i++)
            {
                if (tables[i].Tag == tag)
                {
                    index = i;
                    break;
                }
            }

            return index;
        }
Example #11
0
        public static FUnit[] ReadCvt(DataReader reader, TableRecord[] tables)
        {
            var index = FindTable(tables, FourCC.Cvt);
            if (index == -1)
                return null;

            reader.Seek(tables[index].Offset);

            var results = new FUnit[tables[index].Length / sizeof(short)];
            for (int i = 0; i < results.Length; i++)
                results[i] = (FUnit)reader.ReadInt16BE();

            return results;
        }
Example #12
0
        public unsafe static NameData ReadNames(DataReader reader, TableRecord[] tables)
        {
            if (!SeekToTable(reader, tables, FourCC.Name))
                return default(NameData);

            // read header
            var currentOffset = reader.Position;
            var format = reader.ReadUInt16BE();
            var count = reader.ReadUInt16BE();
            var dataOffset = currentOffset + reader.ReadUInt16BE();

            // read name records, filtering out non-Unicode and platforms we don't know about
            var stringData = stackalloc StringData[count];
            var stringDataCount = 0;
            for (int i = 0; i < count; i++)
            {
                var platform = reader.ReadUInt16BE();
                var encoding = reader.ReadUInt16BE();
                var language = reader.ReadUInt16BE();
                var name = reader.ReadUInt16BE();
                var length = reader.ReadUInt16BE();
                var offset = reader.ReadUInt16BE();

                // we only support Unicode strings
                if (platform == PlatformID.Microsoft)
                {
                    if (encoding != WindowsEncoding.UnicodeBmp && encoding != WindowsEncoding.UnicodeFull)
                        continue;

                    if (language != CultureInfo.CurrentCulture.LCID)
                        continue;
                }
                else if (platform != PlatformID.Unicode)
                    continue;

                stringData[stringDataCount++] = new StringData
                {
                    Name = name,
                    Offset = offset,
                    Length = length
                };
            }

            // find strings we care about and extract them from the blob
            var nameData = new NameData();
            for (int i = 0; i < stringDataCount; i++)
            {
                var data = stringData[i];
                switch (data.Name)
                {
                    case NameID.FamilyName: nameData.FamilyName = ExtractString(reader, dataOffset, data); break;
                    case NameID.SubfamilyName: nameData.SubfamilyName = ExtractString(reader, dataOffset, data); break;
                    case NameID.UniqueID: nameData.UniqueID = ExtractString(reader, dataOffset, data); break;
                    case NameID.FullName: nameData.FullName = ExtractString(reader, dataOffset, data); break;
                    case NameID.Version: nameData.Version = ExtractString(reader, dataOffset, data); break;
                    case NameID.Description: nameData.Description = ExtractString(reader, dataOffset, data); break;
                    case NameID.TypographicFamilyName: nameData.TypographicFamilyName = ExtractString(reader, dataOffset, data); break;
                    case NameID.TypographicSubfamilyName: nameData.TypographicSubfamilyName = ExtractString(reader, dataOffset, data); break;
                }
            }

            return nameData;
        }
Example #13
0
        public static void ReadLoca(DataReader reader, TableRecord[] tables, IndexFormat format, uint* table, int count)
        {
            SeekToTable(reader, tables, FourCC.Loca, required: true);

            if (format == IndexFormat.Short)
            {
                // values are ushort, divided by 2, so we need to shift back
                for (int i = 0; i < count; i++)
                    *table++ = (uint)(reader.ReadUInt16BE() << 1);
            }
            else
            {
                for (int i = 0; i < count; i++)
                    *table++ = reader.ReadUInt32BE();
            }
        }
Example #14
0
        public static void ReadPost(DataReader reader, TableRecord[] tables, ref FaceHeader header)
        {
            if (!SeekToTable(reader, tables, FourCC.Post))
                return;

            // skip over version and italicAngle
            reader.Skip(sizeof(int) * 2);

            header.UnderlinePosition = reader.ReadInt16BE();
            header.UnderlineThickness = reader.ReadInt16BE();
            header.IsFixedPitch = reader.ReadUInt32BE() != 0;
        }
Example #15
0
        public static OS2Data ReadOS2(DataReader reader, TableRecord[] tables)
        {
            SeekToTable(reader, tables, FourCC.OS_2, required: true);

            // skip over version, xAvgCharWidth
            reader.Skip(sizeof(short) * 2);

            var result = new OS2Data
            {
                Weight = (FontWeight)reader.ReadUInt16BE(),
                Stretch = (FontStretch)reader.ReadUInt16BE()
            };

            // skip over fsType, ySubscriptXSize, ySubscriptYSize, ySubscriptXOffset, ySubscriptYOffset,
            // ySuperscriptXSize, ySuperscriptYSize, ySuperscriptXOffset, ySuperscriptXOffset
            reader.Skip(sizeof(short) * 9);

            result.StrikeoutSize = reader.ReadInt16BE();
            result.StrikeoutPosition = reader.ReadInt16BE();

            // skip over sFamilyClass, panose[10], ulUnicodeRange1-4, achVendID[4]
            reader.Skip(sizeof(short) + sizeof(int) * 4 + 14);

            // check various style flags
            var fsSelection = (FsSelectionFlags)reader.ReadUInt16BE();
            result.Style = (fsSelection & FsSelectionFlags.Italic) != 0 ? FontStyle.Italic :
                            (fsSelection & FsSelectionFlags.Bold) != 0 ? FontStyle.Bold :
                            (fsSelection & FsSelectionFlags.Oblique) != 0 ? FontStyle.Oblique :
                            FontStyle.Regular;
            result.IsWWSFont = (fsSelection & FsSelectionFlags.WWS) != 0;
            result.UseTypographicMetrics = (fsSelection & FsSelectionFlags.UseTypoMetrics) != 0;

            // skip over usFirstCharIndex, usLastCharIndex
            reader.Skip(sizeof(short) * 2);

            result.TypographicAscender = reader.ReadInt16BE();
            result.TypographicDescender = reader.ReadInt16BE();
            result.TypographicLineGap = reader.ReadInt16BE();
            result.WinAscent = reader.ReadUInt16BE();
            result.WinDescent = reader.ReadUInt16BE();

            // skip over ulCodePageRange1-2
            reader.Skip(sizeof(int) * 2);

            result.XHeight = reader.ReadInt16BE();
            result.CapHeight = reader.ReadInt16BE();

            return result;
        }