Example #1
0
        private static TrueTypeFontProgram ParseTables(decimal version, IReadOnlyDictionary <string, TrueTypeHeaderTable> tables, TrueTypeDataBytes data)
        {
            var isPostScript = tables.ContainsKey(TrueTypeHeaderTable.Cff);

            var builder = new TableRegister.Builder();

            if (!tables.TryGetValue(TrueTypeHeaderTable.Head, out var table))
            {
                throw new InvalidOperationException($"The {TrueTypeHeaderTable.Head} table is required.");
            }

            // head
            builder.HeaderTable = HeaderTable.Load(data, table);

            if (!tables.TryGetValue(TrueTypeHeaderTable.Hhea, out var hHead))
            {
                throw new InvalidOperationException("The horizontal header table is required.");
            }

            // hhea
            builder.HorizontalHeaderTable = HorizontalHeaderTable.Load(data, hHead);

            if (!tables.TryGetValue(TrueTypeHeaderTable.Maxp, out var maxHeaderTable))
            {
                throw new InvalidOperationException("The maximum profile table is required.");
            }

            // maxp
            builder.MaximumProfileTable = BasicMaximumProfileTable.Load(data, maxHeaderTable);

            // post
            if (tables.TryGetValue(TrueTypeHeaderTable.Post, out var postscriptHeaderTable))
            {
                builder.PostScriptTable = PostScriptTable.Load(data, postscriptHeaderTable, builder.MaximumProfileTable);
            }

            if (!isPostScript)
            {
                if (!tables.TryGetValue(TrueTypeHeaderTable.Loca, out var indexToLocationHeaderTable))
                {
                    throw new InvalidOperationException("The location to index table is required for non-PostScript fonts.");
                }

                // loca
                builder.IndexToLocationTable =
                    IndexToLocationTable.Load(data, indexToLocationHeaderTable, builder);

                if (!tables.TryGetValue(TrueTypeHeaderTable.Glyf, out var glyphHeaderTable))
                {
                    throw new InvalidOperationException("The glyph table is required for non-PostScript fonts.");
                }

                // glyf
                builder.GlyphDataTable = GlyphDataTable.Load(data, glyphHeaderTable, builder);

                OptionallyParseTables(tables, data, builder);
            }

            return(new TrueTypeFontProgram(version, tables, builder.Build()));
        }
Example #2
0
        public static T Parse <T>(TrueTypeHeaderTable table, TrueTypeDataBytes data, TableRegister.Builder register) where T : ITrueTypeTable
        {
            if (typeof(T) == typeof(CMapTable))
            {
                return((T)(object)CMapTableParser.Parse(table, data, register));
            }

            if (typeof(T) == typeof(HorizontalMetricsTable))
            {
                return((T)(object)HorizontalMetricsTableParser.Parse(table, data, register));
            }

            if (typeof(T) == typeof(NameTable))
            {
                return((T)(object)NameTableParser.Parse(table, data, register));
            }

            if (typeof(T) == typeof(Os2Table))
            {
                return((T)(object)Os2TableParser.Parse(table, data, register));
            }

            if (typeof(T) == typeof(HorizontalHeaderTable))
            {
                return((T)(object)HorizontalHeaderTableParser.Parse(table, data, register));
            }

            throw new NotImplementedException();
        }
Example #3
0
        public NameTable Parse(TrueTypeHeaderTable header, TrueTypeDataBytes data, TableRegister.Builder register)
        {
            data.Seek(header.Offset);
            // ReSharper disable once UnusedVariable
            var format       = data.ReadUnsignedShort();
            var count        = data.ReadUnsignedShort();
            var stringOffset = data.ReadUnsignedShort();

            var names = new NameRecordBuilder[count];

            for (var i = 0; i < count; i++)
            {
                names[i] = NameRecordBuilder.Read(data);
            }

            var strings = new TrueTypeNameRecord[count];
            var offset  = header.Offset + stringOffset;

            for (var i = 0; i < count; i++)
            {
                strings[i] = GetTrueTypeNameRecord(names[i], data, offset);
            }

            return(new NameTable(header, GetName(4, strings), GetName(1, strings), GetName(2, strings), strings));
        }
Example #4
0
        public static T Parse <T>(TrueTypeHeaderTable table, TrueTypeDataBytes data, TableRegister.Builder register) where T : ITrueTypeTable
        {
            //checksum https://developer.apple.com/fonts/TrueType-Reference-Manual/RM06/Chap6.html
            uint sum    = 0;
            var  nLongs = (table.Length + 3) / 4;

            data.Seek(table.Offset);
            while (nLongs-- > 0)
            {
                sum += (uint)data.ReadSignedInt();
            }

            if (sum != table.CheckSum)
            {
                Trace.TraceWarning("Table with invalid checksum found in TrueType font file.");
            }

            if (typeof(T) == typeof(CMapTable))
            {
                return((T)(object)CMapTableParser.Parse(table, data, register));
            }

            if (typeof(T) == typeof(HorizontalMetricsTable))
            {
                return((T)(object)HorizontalMetricsTableParser.Parse(table, data, register));
            }

            if (typeof(T) == typeof(NameTable))
            {
                return((T)(object)NameTableParser.Parse(table, data, register));
            }

            if (typeof(T) == typeof(Os2Table))
            {
                return((T)(object)Os2TableParser.Parse(table, data, register));
            }

            if (typeof(T) == typeof(HorizontalHeaderTable))
            {
                return((T)(object)HorizontalHeaderTableParser.Parse(table, data, register));
            }

            throw new NotImplementedException();
        }
Example #5
0
        public NameTable Parse(TrueTypeHeaderTable header, TrueTypeDataBytes data, TableRegister.Builder register)
        {
            data.Seek(header.Offset);
            // ReSharper disable once UnusedVariable
            var format       = data.ReadUnsignedShort();
            var count        = data.ReadUnsignedShort();
            var stringOffset = data.ReadUnsignedShort();

            var names = new NameRecordBuilder[count];

            for (var i = 0; i < count; i++)
            {
                names[i] = NameRecordBuilder.Read(data);
            }

            var strings = new TrueTypeNameRecord[count];

            for (var i = 0; i < count; i++)
            {
                var nameRecord = names[i];

                var encoding = OtherEncodings.Iso88591;

                switch (nameRecord.PlatformId)
                {
                case TrueTypePlatformIdentifier.Windows:
                {
                    var platformEncoding = (TrueTypeWindowsEncodingIdentifier)nameRecord.PlatformEncodingId;

                    if (platformEncoding == TrueTypeWindowsEncodingIdentifier.Symbol ||
                        platformEncoding == TrueTypeWindowsEncodingIdentifier.UnicodeBmp)
                    {
                        encoding = Encoding.BigEndianUnicode;
                    }
                    break;
                }

                case TrueTypePlatformIdentifier.Unicode:
                {
                    encoding = Encoding.BigEndianUnicode;
                    break;
                }

                case TrueTypePlatformIdentifier.Iso:
                {
                    switch (nameRecord.PlatformEncodingId)
                    {
                    case 0:
                        encoding = Encoding.GetEncoding("US-ASCII");
                        break;

                    case 1:
                        encoding = Encoding.GetEncoding("ISO-10646-UCS-2");
                        break;
                    }

                    break;
                }
                }

                var position = header.Offset + stringOffset + nameRecord.Offset;

                data.Seek(position);

                var str = data.ReadString(nameRecord.Length, encoding);

                strings[i] = nameRecord.ToNameRecord(str);
            }

            return(new NameTable(header, GetName(4, strings), GetName(1, strings), GetName(2, strings), strings));
        }
        public HorizontalMetricsTable Parse(TrueTypeHeaderTable header, TrueTypeDataBytes data, TableRegister.Builder register)
        {
            var glyphCount  = register.MaximumProfileTable.NumberOfGlyphs;
            var metricCount = register.HorizontalHeaderTable.NumberOfHeaderMetrics;

            data.Seek(header.Offset);

            // The number of entries in the left side bearing field per entry is number of glyphs - number of metrics
            var additionalLeftSideBearingLength = glyphCount - metricCount;

            var advancedWidths = new int[metricCount];

            // For bearings over the metric count, the width is the same as the last width in advanced widths.
            var leftSideBearings = new short[glyphCount];

            for (var i = 0; i < metricCount; i++)
            {
                advancedWidths[i]   = data.ReadUnsignedShort();
                leftSideBearings[i] = data.ReadSignedShort();
            }

            for (var i = 0; i < additionalLeftSideBearingLength; i++)
            {
                leftSideBearings[metricCount + i] = data.ReadSignedShort();
            }

            return(new HorizontalMetricsTable(header, advancedWidths, leftSideBearings, metricCount));
        }
Example #7
0
        private static void OptionallyParseTables(IReadOnlyDictionary <string, TrueTypeHeaderTable> tables, TrueTypeDataBytes data, TableRegister.Builder tableRegister)
        {
            // cmap
            if (tables.TryGetValue(TrueTypeHeaderTable.Cmap, out var cmap))
            {
                tableRegister.CMapTable = CMapTable.Load(data, cmap, tableRegister);
            }

            // hmtx
            if (tables.TryGetValue(TrueTypeHeaderTable.Hmtx, out var hmtxHeaderTable))
            {
                tableRegister.HorizontalMetricsTable = HorizontalMetricsTable.Load(data, hmtxHeaderTable, tableRegister);
            }

            // name
            if (tables.TryGetValue(TrueTypeHeaderTable.Name, out var nameHeaderTable))
            {
                // TODO: Not important
            }

            // os2

            // kern
            if (tables.TryGetValue(TrueTypeHeaderTable.Kern, out var kernHeaderTable))
            {
                tableRegister.KerningTable = KerningTable.Load(data, kernHeaderTable);
            }
        }
        public HorizontalHeaderTable Parse(TrueTypeHeaderTable header, TrueTypeDataBytes data, TableRegister.Builder register)
        {
            data.Seek(header.Offset);
            var majorVersion = data.ReadUnsignedShort();
            var minorVersion = data.ReadUnsignedShort();

            var ascender  = data.ReadSignedShort();
            var descender = data.ReadSignedShort();
            var lineGap   = data.ReadSignedShort();

            var advancedWidthMax = data.ReadUnsignedShort();

            var minLeftSideBearing  = data.ReadSignedShort();
            var minRightSideBearing = data.ReadSignedShort();
            var xMaxExtent          = data.ReadSignedShort();

            var caretSlopeRise = data.ReadSignedShort();
            var caretSlopeRun  = data.ReadSignedShort();
            var caretOffset    = data.ReadSignedShort();

            // Reserved section
            data.ReadSignedShort();
            data.ReadSignedShort();
            data.ReadSignedShort();
            data.ReadSignedShort();

            var metricDataFormat = data.ReadSignedShort();

            if (metricDataFormat != 0)
            {
                throw new NotSupportedException("The metric data format for a horizontal header table should be 0.");
            }

            var numberOfHeaderMetrics = data.ReadUnsignedShort();

            return(new HorizontalHeaderTable(header, majorVersion, minorVersion, ascender,
                                             descender, lineGap, advancedWidthMax,
                                             minLeftSideBearing,
                                             minRightSideBearing,
                                             xMaxExtent,
                                             caretSlopeRise,
                                             caretSlopeRun,
                                             caretOffset,
                                             metricDataFormat,
                                             numberOfHeaderMetrics));
        }
Example #9
0
        public CMapTable Parse(TrueTypeHeaderTable header, TrueTypeDataBytes data, TableRegister.Builder register)
        {
            data.Seek(header.Offset);

            var tableVersionNumber = data.ReadUnsignedShort();

            var numberOfEncodingTables = data.ReadUnsignedShort();

            var subTableHeaders = new SubTableHeaderEntry[numberOfEncodingTables];

            for (int i = 0; i < numberOfEncodingTables; i++)
            {
                var platformId = (TrueTypeCMapPlatform)data.ReadUnsignedShort();
                var encodingId = data.ReadUnsignedShort();
                var offset     = data.ReadUnsignedInt();

                subTableHeaders[i] = new SubTableHeaderEntry(platformId, encodingId, offset);
            }

            var tables = new List <ICMapSubTable>(numberOfEncodingTables);

            var numberofGlyphs = register.MaximumProfileTable.NumberOfGlyphs;

            for (var i = 0; i < subTableHeaders.Length; i++)
            {
                var subTableHeader = subTableHeaders[i];

                data.Seek(header.Offset + subTableHeader.Offset);

                var format = data.ReadUnsignedShort();

                /*
                 * There are 9 currently available formats:
                 * 0: Character code and glyph indices are restricted to a single byte. Rare.
                 * 2: Suitable for CJK characters. Contain mixed 8/16 byte encoding.
                 * 4: 2 byte encoding format. Used when character codes fall into (gappy) contiguous ranges.
                 * 6: 'Trimmed table mapping', used when character codes fall into a single contiguous range. This is dense mapping.
                 * 8: 16/32 bit coverage. Uses mixed length character codes.
                 * 10: Similar to format 6, trimmed table/array for 32 bits.
                 * 12: Segmented coverage, similar to format 4 but for 32 bit/4 byte.
                 * 13: Many to one mappings. Used by Apple for the LastResort font.
                 * 14: Unicode variation sequences.
                 *
                 * Many of the formats are obsolete or not really used. Modern fonts will tend to use formats 4, 6 and 12.
                 * For PDF we will support 0, 2 and 4 since these are in the original TrueType spec.
                 */
                switch (format)
                {
                case 0:
                {
                    // Simple 1 to 1 mapping of character codes to glyph codes.
                    var item = ByteEncodingCMapTable.Load(data, subTableHeader.PlatformId, subTableHeader.EncodingId);
                    tables.Add(item);
                    break;
                }

                case 2:
                {
                    // Useful for CJK characters. Use mixed 8/16 bit encoding.
                    var item = HighByteMappingCMapTable.Load(data, numberofGlyphs, subTableHeader.PlatformId, subTableHeader.EncodingId);
                    tables.Add(item);
                    break;
                }

                case 4:
                {
                    // Microsoft's standard mapping table.
                    var item = Format4CMapTable.Load(data, subTableHeader.PlatformId, subTableHeader.EncodingId);
                    tables.Add(item);
                    break;
                }

                case 6:
                {
                    var item = TrimmedTableMappingCMapTable.Load(data, subTableHeader.PlatformId, subTableHeader.EncodingId);
                    tables.Add(item);
                    break;
                }
                }
            }

            return(new CMapTable(tableVersionNumber, header, tables));
        }
Example #10
0
        public HorizontalMetricsTable Parse(TrueTypeHeaderTable header, TrueTypeDataBytes data, TableRegister.Builder register)
        {
            var glyphCount  = register.MaximumProfileTable.NumberOfGlyphs;
            var metricCount = register.HorizontalHeaderTable.NumberOfHeaderMetrics;

            data.Seek(header.Offset);
            var bytesRead = 0;


            var horizontalMetrics = new HorizontalMetric[metricCount];


            for (var i = 0; i < metricCount; i++)
            {
                var width = data.ReadUnsignedShort();
                var lsb   = data.ReadSignedShort();

                horizontalMetrics[i] = new HorizontalMetric(width, lsb);

                bytesRead += 4;
            }

            // The number of entries in the left side bearing field per entry is number of glyphs - number of metrics
            // For bearings over the metric count, the width is the same as the last width in advanced widths.
            var additionalLeftSideBearings = new short[glyphCount - metricCount];

            for (var i = 0; i < additionalLeftSideBearings.Length; i++)
            {
                if (bytesRead >= header.Length)
                {
                    break;
                }

                additionalLeftSideBearings[i] = data.ReadSignedShort();
                bytesRead += 2;
            }

            return(new HorizontalMetricsTable(header, horizontalMetrics, additionalLeftSideBearings));
        }
Example #11
0
        public Os2Table Parse(TrueTypeHeaderTable header, TrueTypeDataBytes data, TableRegister.Builder register)
        {
            data.Seek(header.Offset);

            var version = data.ReadUnsignedShort();

            var xAvgCharWidth       = data.ReadSignedShort();
            var weightClass         = data.ReadUnsignedShort();
            var widthClass          = data.ReadUnsignedShort();
            var typeFlags           = data.ReadUnsignedShort();
            var ySubscriptXSize     = data.ReadSignedShort();
            var ySubscriptYSize     = data.ReadSignedShort();
            var ySubscriptXOffset   = data.ReadSignedShort();
            var ySubscriptYOffset   = data.ReadSignedShort();
            var ySuperscriptXSize   = data.ReadSignedShort();
            var ySuperscriptYSize   = data.ReadSignedShort();
            var ySuperscriptXOffset = data.ReadSignedShort();
            var ySuperscriptYOffset = data.ReadSignedShort();
            var yStrikeoutSize      = data.ReadSignedShort();
            var yStrikeoutPosition  = data.ReadSignedShort();
            var familyClass         = data.ReadSignedShort();
            var panose              = data.ReadByteArray(10);
            var ulCharRange1        = data.ReadUnsignedInt();
            var ulCharRange2        = data.ReadUnsignedInt();
            var ulCharRange3        = data.ReadUnsignedInt();
            var ulCharRange4        = data.ReadUnsignedInt();
            var vendorIdBytes       = data.ReadByteArray(4);
            var selectionFlags      = data.ReadUnsignedShort();
            var firstCharacterIndex = data.ReadUnsignedShort();
            var lastCharacterIndex  = data.ReadUnsignedShort();
            var unicodeCharRange    = new[] { ulCharRange1, ulCharRange2, ulCharRange3, ulCharRange4 };

            var vendorId = Encoding.ASCII.GetString(vendorIdBytes);

            /*
             * Documentation for OS/2 version 0 in Appleā€™s TrueType Reference Manual stops at the usLastCharIndex field
             * and does not include the last five fields of the table as it was defined by Microsoft.
             * Some legacy TrueType fonts may have been built with a shortened version 0 OS/2 table.
             * Applications should check the table length for a version 0 OS/2 table before reading these fields.
             */
            if (version == 0 && header.Length == 68)
            {
                return(new Os2Table(header, version, xAvgCharWidth,
                                    weightClass, widthClass, typeFlags, ySubscriptXSize,
                                    ySubscriptYSize,
                                    ySubscriptXOffset,
                                    ySubscriptYOffset,
                                    ySuperscriptXSize,
                                    ySuperscriptYSize,
                                    ySuperscriptXOffset,
                                    ySuperscriptYOffset,
                                    yStrikeoutSize,
                                    yStrikeoutPosition,
                                    familyClass,
                                    panose,
                                    unicodeCharRange,
                                    vendorId,
                                    selectionFlags,
                                    firstCharacterIndex,
                                    lastCharacterIndex));
            }

            short  sTypoAscender;
            short  sTypoDescender;
            short  sTypoLineGap;
            ushort usWinAscent;
            ushort usWinDescent;

            try
            {
                sTypoAscender  = data.ReadSignedShort();
                sTypoDescender = data.ReadSignedShort();
                sTypoLineGap   = data.ReadSignedShort();
                usWinAscent    = data.ReadUnsignedShort();
                usWinDescent   = data.ReadUnsignedShort();
            }
            catch
            {
                // Font may be invalid. Try falling back to shorter version...
                return(new Os2Table(header, version, xAvgCharWidth,
                                    weightClass, widthClass, typeFlags, ySubscriptXSize,
                                    ySubscriptYSize,
                                    ySubscriptXOffset,
                                    ySubscriptYOffset,
                                    ySuperscriptXSize,
                                    ySuperscriptYSize,
                                    ySuperscriptXOffset,
                                    ySuperscriptYOffset,
                                    yStrikeoutSize,
                                    yStrikeoutPosition,
                                    familyClass,
                                    panose,
                                    unicodeCharRange,
                                    vendorId,
                                    selectionFlags,
                                    firstCharacterIndex,
                                    lastCharacterIndex));
            }

            if (version == 0)
            {
                return(new Os2RevisedVersion0Table(header, version, xAvgCharWidth,
                                                   weightClass, widthClass, typeFlags, ySubscriptXSize,
                                                   ySubscriptYSize,
                                                   ySubscriptXOffset,
                                                   ySubscriptYOffset,
                                                   ySuperscriptXSize,
                                                   ySuperscriptYSize,
                                                   ySuperscriptXOffset,
                                                   ySuperscriptYOffset,
                                                   yStrikeoutSize,
                                                   yStrikeoutPosition,
                                                   familyClass,
                                                   panose,
                                                   unicodeCharRange,
                                                   vendorId,
                                                   selectionFlags,
                                                   firstCharacterIndex,
                                                   lastCharacterIndex,
                                                   sTypoAscender,
                                                   sTypoDescender,
                                                   sTypoLineGap,
                                                   usWinAscent,
                                                   usWinDescent));
            }

            var ulCodePageRange1 = data.ReadUnsignedInt();
            var ulCodePageRange2 = data.ReadUnsignedInt();

            if (version == 1)
            {
                return(new Os2Version1Table(header, version, xAvgCharWidth,
                                            weightClass, widthClass, typeFlags, ySubscriptXSize,
                                            ySubscriptYSize,
                                            ySubscriptXOffset,
                                            ySubscriptYOffset,
                                            ySuperscriptXSize,
                                            ySuperscriptYSize,
                                            ySuperscriptXOffset,
                                            ySuperscriptYOffset,
                                            yStrikeoutSize,
                                            yStrikeoutPosition,
                                            familyClass,
                                            panose,
                                            unicodeCharRange,
                                            vendorId,
                                            selectionFlags,
                                            firstCharacterIndex,
                                            lastCharacterIndex,
                                            sTypoAscender,
                                            sTypoDescender,
                                            sTypoLineGap,
                                            usWinAscent,
                                            usWinDescent,
                                            ulCodePageRange1,
                                            ulCodePageRange2));
            }

            var sxHeight      = data.ReadSignedShort();
            var sCapHeight    = data.ReadSignedShort();
            var usDefaultChar = data.ReadUnsignedShort();
            var usBreakChar   = data.ReadUnsignedShort();
            var usMaxContext  = data.ReadUnsignedShort();

            if (version < 5)
            {
                return(new Os2Version2To4OpenTypeTable(header, version, xAvgCharWidth,
                                                       weightClass, widthClass, typeFlags, ySubscriptXSize,
                                                       ySubscriptYSize,
                                                       ySubscriptXOffset,
                                                       ySubscriptYOffset,
                                                       ySuperscriptXSize,
                                                       ySuperscriptYSize,
                                                       ySuperscriptXOffset,
                                                       ySuperscriptYOffset,
                                                       yStrikeoutSize,
                                                       yStrikeoutPosition,
                                                       familyClass,
                                                       panose,
                                                       unicodeCharRange,
                                                       vendorId,
                                                       selectionFlags,
                                                       firstCharacterIndex,
                                                       lastCharacterIndex,
                                                       sTypoAscender,
                                                       sTypoDescender,
                                                       sTypoLineGap,
                                                       usWinAscent,
                                                       usWinDescent,
                                                       ulCodePageRange1,
                                                       ulCodePageRange2,
                                                       sxHeight,
                                                       sCapHeight,
                                                       usDefaultChar,
                                                       usBreakChar,
                                                       usMaxContext));
            }

            var usLowerOpticalPointSize = data.ReadUnsignedShort();
            var usUpperOpticalPointSize = data.ReadUnsignedShort();

            return(new Os2Version5OpenTypeTable(header, version, xAvgCharWidth,
                                                weightClass, widthClass, typeFlags, ySubscriptXSize,
                                                ySubscriptYSize,
                                                ySubscriptXOffset,
                                                ySubscriptYOffset,
                                                ySuperscriptXSize,
                                                ySuperscriptYSize,
                                                ySuperscriptXOffset,
                                                ySuperscriptYOffset,
                                                yStrikeoutSize,
                                                yStrikeoutPosition,
                                                familyClass,
                                                panose,
                                                unicodeCharRange,
                                                vendorId,
                                                selectionFlags,
                                                firstCharacterIndex,
                                                lastCharacterIndex,
                                                sTypoAscender,
                                                sTypoDescender,
                                                sTypoLineGap,
                                                usWinAscent,
                                                usWinDescent,
                                                ulCodePageRange1,
                                                ulCodePageRange2,
                                                sxHeight,
                                                sCapHeight,
                                                usDefaultChar,
                                                usBreakChar,
                                                usMaxContext,
                                                usLowerOpticalPointSize,
                                                usUpperOpticalPointSize));
        }
Example #12
0
        private static void OptionallyParseTables(IReadOnlyDictionary <string, TrueTypeHeaderTable> tables, TrueTypeDataBytes data, TableRegister.Builder tableRegister)
        {
            // cmap
            if (tables.TryGetValue(TrueTypeHeaderTable.Cmap, out var cmap))
            {
                tableRegister.CMapTable = TableParser.Parse <CMapTable>(cmap, data, tableRegister);
            }

            // hmtx
            if (tables.TryGetValue(TrueTypeHeaderTable.Hmtx, out var hmtxHeaderTable))
            {
                tableRegister.HorizontalMetricsTable = TableParser.Parse <HorizontalMetricsTable>(hmtxHeaderTable, data, tableRegister);
            }

            // kern
            if (tables.TryGetValue(TrueTypeHeaderTable.Kern, out var kernHeaderTable))
            {
                tableRegister.KerningTable = KerningTable.Load(data, kernHeaderTable);
            }
        }