public void LoadHead()
        {
            var writer = new BigEndianBinaryWriter();

            writer.WriteHeadTable(new HeadTable(
                                      HeadTable.HeadFlags.None,
                                      HeadTable.HeadMacStyle.Italic | HeadTable.HeadMacStyle.Bold,
                                      1024,
                                      new DateTime(2017, 02, 06, 07, 47, 00),
                                      new DateTime(2017, 02, 07, 07, 47, 00),
                                      new Bounds(0, 0, 1024, 1022),
                                      0,
                                      HeadTable.IndexLocationFormats.Offset16));

            var head = HeadTable.Load(writer.GetReader());

            Assert.Equal(HeadTable.HeadFlags.None, head.Flags);
            Assert.Equal(HeadTable.HeadMacStyle.Italic | HeadTable.HeadMacStyle.Bold, head.MacStyle);
            Assert.Equal(1024, head.UnitsPerEm);
            Assert.Equal(new DateTime(2017, 02, 06, 07, 47, 00), head.Created);
            Assert.Equal(new DateTime(2017, 02, 07, 07, 47, 00), head.Modified);
            Assert.Equal(0, head.Bounds.Min.X);
            Assert.Equal(0, head.Bounds.Min.Y);
            Assert.Equal(1024, head.Bounds.Max.X);
            Assert.Equal(1022, head.Bounds.Max.Y);
            Assert.Equal(0, head.LowestRecPPEM);
            Assert.Equal(HeadTable.IndexLocationFormats.Offset16, head.IndexLocationFormat);
        }
Beispiel #2
0
        internal static FontInstance LoadFont(FontReader reader)
        {
            // https://www.microsoft.com/typography/otspec/recom.htm#TableOrdering
            // recomended order
            HeadTable head = reader.GetTable <HeadTable>();                                        // head - not saving but loading in suggested order

            reader.GetTable <HoizontalHeadTable>();                                                // hhea
            reader.GetTable <MaximumProfileTable>();                                               // maxp
            OS2Table os2 = reader.GetTable <OS2Table>();                                           // OS/2
            HorizontalMetricsTable horizontalMetrics = reader.GetTable <HorizontalMetricsTable>(); // hmtx
            // LTSH - Linear threshold data
            // VDMX - Vertical device metrics
            // hdmx - Horizontal device metrics
            CMapTable cmap = reader.GetTable <CMapTable>(); // cmap

            // fpgm - Font Program
            // prep - Control Value Program
            // cvt  - Control Value Table
            reader.GetTable <IndexLocationTable>();                    // loca
            GlyphTable   glyphs    = reader.GetTable <GlyphTable>();   // glyf
            KerningTable kern      = reader.GetTable <KerningTable>(); // kern - Kerning
            NameTable    nameTable = reader.GetTable <NameTable>();    // name

            // post - PostScript information
            // gasp - Grid-fitting/Scan-conversion (optional table)
            // PCLT - PCL 5 data
            // DSIG - Digital signature
            return(new FontInstance(nameTable, cmap, glyphs, os2, horizontalMetrics, head, kern));
        }
        private static FontStyle ConvertStyle(OS2Table os2, HeadTable head)
        {
            FontStyle style = FontStyle.Regular;

            if (os2 != null)
            {
                if (os2.FontStyle.HasFlag(OS2Table.FontStyleSelection.BOLD))
                {
                    style |= FontStyle.Bold;
                }

                if (os2.FontStyle.HasFlag(OS2Table.FontStyleSelection.ITALIC))
                {
                    style |= FontStyle.Italic;
                }
            }
            else if (head != null)
            {
                if (head.MacStyle.HasFlag(HeadTable.HeadMacStyle.Bold))
                {
                    style |= FontStyle.Bold;
                }

                if (head.MacStyle.HasFlag(HeadTable.HeadMacStyle.Italic))
                {
                    style |= FontStyle.Italic;
                }
            }

            return(style);
        }
 internal FakeFontInstance(
     NameTable nameTable,
     MaximumProfileTable maxpTable,
     CMapTable cmap,
     GlyphTable glyphs,
     OS2Table os2,
     HorizontalHeadTable horizontalHeadTable,
     HorizontalMetricsTable horizontalMetrics,
     VerticalHeadTable verticalHeadTable,
     VerticalMetricsTable verticalMetrics,
     HeadTable head,
     KerningTable kern)
     : base(
         nameTable,
         maxpTable,
         cmap,
         glyphs,
         os2,
         horizontalHeadTable,
         horizontalMetrics,
         verticalHeadTable,
         verticalMetrics,
         head,
         kern,
         null,
         null,
         null,
         null,
         null,
         null,
         null,
         null)
 {
 }
Beispiel #5
0
        public ActionResult DeleteConfirmed(int id)
        {
            HeadTable headTable = db.DvHeadTables.Find(id);

            db.DvHeadTables.Remove(headTable);
            db.SaveChanges();
            return(RedirectToAction("Index"));
        }
Beispiel #6
0
        /// <summary>
        /// Reads a <see cref="FontDescription" /> from the specified stream.
        /// </summary>
        /// <param name="reader">The reader.</param>
        /// <returns>
        /// a <see cref="FontDescription" />.
        /// </returns>
        internal static FontDescription LoadDescription(FontReader reader)
        {
            HeadTable head      = reader.GetTable <HeadTable>();
            OS2Table  os2       = reader.GetTable <OS2Table>();
            NameTable nameTable = reader.GetTable <NameTable>();

            return(new FontDescription(nameTable, os2, head));
        }
Beispiel #7
0
        public HeadTable Head(TableHeader header)
        {
            var table = new HeadTable(header);

            if (TablesByName.ContainsKey(table.Name))
            {
                table.ReadFrom(reader);
            }
            return(table);
        }
        /// <summary>
        /// Reads a <see cref="FontDescription" /> from the specified stream.
        /// </summary>
        /// <param name="reader">The reader.</param>
        /// <returns>
        /// a <see cref="FontDescription" />.
        /// </returns>
        internal static FontDescription LoadDescription(FontReader reader)
        {
            // NOTE: These fields are read in their optimized order
            // https://docs.microsoft.com/en-gb/typography/opentype/spec/recom#optimized-table-ordering
            HeadTable head      = reader.GetTable <HeadTable>();
            OS2Table  os2       = reader.GetTable <OS2Table>();
            NameTable nameTable = reader.GetTable <NameTable>();

            return(new FontDescription(nameTable, os2, head));
        }
Beispiel #9
0
 public ActionResult Edit([Bind(Include = "Id,Idhead,Time,ConfigurationTable,ConnectedTable")] HeadTable headTable)
 {
     if (ModelState.IsValid)
     {
         db.Entry(headTable).State = EntityState.Modified;
         db.SaveChanges();
         return(RedirectToAction("Index"));
     }
     ViewBag.Idhead = new SelectList(db.DvHeads, "Id", "Ipaddress", headTable.Idhead);
     return(View(headTable));
 }
Beispiel #10
0
 internal FakeFontInstance(
     NameTable nameTable,
     CMapTable cmap,
     GlyphTable glyphs,
     OS2Table os2,
     HorizontalHeadTable horizontalHeadTable,
     HorizontalMetricsTable horizontalMetrics,
     HeadTable head,
     KerningTable kern)
     : base(nameTable, cmap, glyphs, os2, horizontalHeadTable, horizontalMetrics, head, kern, null, null)
 {
 }
Beispiel #11
0
        // GET: HeadTables/Details/5
        public ActionResult Details(int?id)
        {
            if (id == null)
            {
                return(new HttpStatusCodeResult(HttpStatusCode.BadRequest));
            }
            HeadTable headTable = db.DvHeadTables.Find(id);

            if (headTable == null)
            {
                return(HttpNotFound());
            }
            return(View(headTable));
        }
Beispiel #12
0
        /// <summary>
        /// Initializes a new instance of the <see cref="FontDescription" /> class.
        /// </summary>
        /// <param name="nameTable">The name table.</param>
        /// <param name="cmap">The cmap.</param>
        /// <param name="glyphs">The glyphs.</param>
        /// <param name="os2">The os2.</param>
        /// <param name="horizontalMetrics">The horizontal metrics.</param>
        /// <param name="head">The head.</param>
        /// <param name="kern">The kern.</param>
        internal FontInstance(NameTable nameTable, CMapTable cmap, GlyphTable glyphs, OS2Table os2, HorizontalMetricsTable horizontalMetrics, HeadTable head, KerningTable kern)
        {
            this.cmap              = cmap;
            this.os2               = os2;
            this.glyphs            = glyphs;
            this.horizontalMetrics = horizontalMetrics;
            this.head              = head;
            this.glyphCache        = new GlyphInstance[this.glyphs.GlyphCount];

            // https://www.microsoft.com/typography/otspec/recom.htm#tad
            this.LineHeight  = os2.TypoAscender - os2.TypoDescender + os2.TypoLineGap;
            this.EmSize      = this.head.UnitsPerEm;
            this.kerning     = kern;
            this.Description = new FontDescription(nameTable, os2, head);
        }
Beispiel #13
0
        // GET: HeadTables/Edit/5
        public ActionResult Edit(int?id)
        {
            if (id == null)
            {
                return(new HttpStatusCodeResult(HttpStatusCode.BadRequest));
            }
            HeadTable headTable = db.DvHeadTables.Find(id);

            if (headTable == null)
            {
                return(HttpNotFound());
            }
            ViewBag.Idhead = new SelectList(db.DvHeads, "Id", "Ipaddress", headTable.Idhead);
            return(View(headTable));
        }
Beispiel #14
0
        internal Font(FontStreamReader reader, OffsetTable offsetTable)
        {
            this.reader      = reader;
            this.offsetTable = offsetTable;

            HeadTable = HeadTable.Read(reader, offsetTable.Entries["head"]);
            if (offsetTable.Entries.ContainsKey("fpgm"))
            {
                fpgmTable = FpgmTable.Read(reader, offsetTable.Entries["fpgm"]);
            }
            cmapTable = CmapTable.Read(reader, offsetTable.Entries["cmap"]);
            locaTable = LocaTable.Read(this, reader, offsetTable.Entries["loca"]);
            glyfTable = GlyfTable.Read(reader, offsetTable.Entries["glyf"]);

            //TODO run fpgm
        }
        /// <summary>
        /// Запросить таблицы устройств DV-HEAD OMEGA
        /// </summary>
        /// <param name="id">Идентификатор DV-HEAD</param>
        /// <returns></returns>
        public TablesDto RequestDeviceTables(int id)
        {
            TablesDto tablesDto = new TablesDto();

            try {
                using (MainDbContext dbContext = new MainDbContext()) {
                    HeadTable hd = dbContext.DvHeadTables.Where(t => t.Idhead == id).OrderByDescending(t => t.DgsTime).First();
                    tablesDto.DvHead             = hd.DvHead;
                    tablesDto.ConfigurationTable = ParseConfigTable(hd.ConfigurationTable).OrderBy(s => s); // the original was not sorted
                    tablesDto.ExternalTable      = ParseExternalTable(hd.ConnectedTable, tablesDto.ConfigurationTable);
                    tablesDto.Address            = hd.DvHead.Address;
                    tablesDto.Failure            = false;
                }
            } catch (Exception)
            {
                tablesDto.Failure = true;
            }
            return(tablesDto);
        }
Beispiel #16
0
        /// <summary>
        /// Initializes a new instance of the <see cref="FontInstance"/> class.
        /// </summary>
        /// <param name="nameTable">The name table.</param>
        /// <param name="cmap">The cmap.</param>
        /// <param name="glyphs">The glyphs.</param>
        /// <param name="os2">The os2.</param>
        /// <param name="horizontalHeadTable">The horizontal head table.</param>
        /// <param name="horizontalMetrics">The horizontal metrics.</param>
        /// <param name="head">The head.</param>
        /// <param name="kern">The kern.</param>
        /// <param name="colrTable">The COLR table</param>
        /// <param name="cpalTable">The CPAL table</param>
        internal FontInstance(
            NameTable nameTable,
            CMapTable cmap,
            GlyphTable glyphs,
            OS2Table os2,
            HorizontalHeadTable horizontalHeadTable,
            HorizontalMetricsTable horizontalMetrics,
            HeadTable head,
            KerningTable kern,
            ColrTable?colrTable,
            CpalTable?cpalTable)
        {
            this.cmap              = cmap;
            this.os2               = os2;
            this.glyphs            = glyphs;
            this.horizontalMetrics = horizontalMetrics;
            this.head              = head;
            this.glyphCache        = new GlyphInstance[this.glyphs.GlyphCount];
            if (!(colrTable is null))
            {
                this.colorGlyphCache = new GlyphInstance[this.glyphs.GlyphCount][];
            }

            bool useTypoMetrics = os2.FontStyle.HasFlag(OS2Table.FontStyleSelection.USE_TYPO_METRICS);

            // https://www.microsoft.com/typography/otspec/recom.htm#tad
            this.Ascender    = useTypoMetrics ? os2.TypoAscender : horizontalHeadTable.Ascender;
            this.Descender   = useTypoMetrics ? os2.TypoDescender : horizontalHeadTable.Descender;
            this.LineGap     = useTypoMetrics ? os2.TypoLineGap : horizontalHeadTable.LineGap;
            this.LineHeight  = this.Ascender - this.Descender + this.LineGap;
            this.EmSize      = this.head.UnitsPerEm;
            this.kerning     = kern;
            this.colrTable   = colrTable;
            this.cpalTable   = cpalTable;
            this.Description = new FontDescription(nameTable, os2, head);
        }
Beispiel #17
0
 /// <summary>
 /// Initializes a new instance of the <see cref="FontDescription" /> class.
 /// </summary>
 /// <param name="nameTable">The name table.</param>
 /// <param name="os2">The os2.</param>
 /// <param name="head">The head.</param>
 internal FontDescription(NameTable nameTable, OS2Table os2, HeadTable head)
     : this(nameTable.FontName, nameTable.FontFamilyName, nameTable.FontSubFamilyName, ConvertStyle(os2, head))
 {
 }
Beispiel #18
0
        public static Font ReadFromStream(Stream stream)
        {
            var font   = new Font();
            var reader = new BinaryReader(stream);

            // Offset Table
            font.OffsetTable = OffsetTable.Read(reader);

            // Head
            var headTableRecord = font.OffsetTable.TableRecords.Single(x => x.TableTag == HeadTable.Tag);

            stream.Seek(headTableRecord.Offset, SeekOrigin.Begin);
            font.HeadTable = HeadTable.Read(reader);

            // Horizontal Header
            var horizontalHeaderTableRecord = font.OffsetTable.TableRecords.Single(x => x.TableTag == HorizontalHeaderTable.Tag);

            stream.Seek(horizontalHeaderTableRecord.Offset, SeekOrigin.Begin);
            font.HorizontalHeaderTable = HorizontalHeaderTable.Read(reader);

            // Maximum Profile
            var maximumProfileTableRecord = font.OffsetTable.TableRecords.Single(x => x.TableTag == MaximumProfileTableBase.Tag);

            stream.Seek(maximumProfileTableRecord.Offset, SeekOrigin.Begin);
            var maximumProfileTableVersion = Math.Round(reader.ReadFixedPointNumber(), 2);

            if (maximumProfileTableVersion == 0.5m)
            {
                font.MaximumProfileTable = MaximumProfileTableV0_5.Read(reader, maximumProfileTableVersion);
            }
            else if (maximumProfileTableVersion == 1.0m)
            {
                font.MaximumProfileTable = MaximumProfileTableV1.Read(reader, maximumProfileTableVersion);
            }

            // Horizontal Metrics
            var horizontalMetricsTableRecord = font.OffsetTable.TableRecords.Single(x => x.TableTag == HorizontalMetricsTable.Tag);

            stream.Seek(horizontalMetricsTableRecord.Offset, SeekOrigin.Begin);
            font.HorizontalMetricsTable = HorizontalMetricsTable.Read(reader,
                                                                      font.HorizontalHeaderTable.NumberOfHMetrics,
                                                                      font.MaximumProfileTable.NumGlyphs);

            // Glyph
            var glyphTableRecord = font.OffsetTable.TableRecords.Single(x => x.TableTag == GlyphTable.Tag);

            font.GlyphTable = new GlyphTable(reader, glyphTableRecord.Offset);

            // IndexLocation
            var indexLocationTableRecord = font.OffsetTable.TableRecords.Single(x => x.TableTag == IndexLocationTableBase.Tag);

            stream.Seek(indexLocationTableRecord.Offset, SeekOrigin.Begin);
            if (font.HeadTable.IndexToLocFormat == 0)
            {
                font.IndexLocationTable = IndexLocationShortFormatTable.Read(reader, font.MaximumProfileTable.NumGlyphs);
            }
            else
            {
                font.IndexLocationTable = IndexLocationLongFormatTable.Read(reader, font.MaximumProfileTable.NumGlyphs);
            }

            // PostScript
            var postScriptTableRecord = font.OffsetTable.TableRecords.Single(x => x.TableTag == PostScriptTable.Tag);

            stream.Seek(postScriptTableRecord.Offset, SeekOrigin.Begin);
            font.PostScriptTable = PostScriptTable.Read(reader);

            // OS/2 and Windows
            var os2TableRecord = font.OffsetTable.TableRecords.Single(x => x.TableTag == Os2Table.Tag);

            stream.Seek(os2TableRecord.Offset, SeekOrigin.Begin);
            font.Os2Table = Os2Table.Read(reader);

            // CharacterMap
            var characterMapTableRecord = font.OffsetTable.TableRecords.Single(x => x.TableTag == CharacterMapTable.Tag);

            stream.Seek(characterMapTableRecord.Offset, SeekOrigin.Begin);
            font.CharacterMapTable = CharacterMapTable.Read(reader);

            return(font);
        }
        public OpenTypeFont Deserialize(BinaryReader reader)
        {
            var font = new OpenTypeFont {
                SfntVersion = DataTypeConverter.ReadFixed(reader)
            };

            if (!_supportedSfntVersions.Contains(font.SfntVersion))
            {
                throw new NotSupportedException("Bad sfnt version.");
            }

            // Table directory
            var numberOfTables = DataTypeConverter.ReadUShort(reader);

            reader.BaseStream.Position += 3 * DataTypeLength.UShort; // searchRange, entrySelector, rangeShift
            var entryList = Enumerable.Range(0, numberOfTables).Select(i =>
            {
                var entry = new TableDirectoryEntry {
                    Tag = DataTypeConverter.ReadTag(reader)
                };
                reader.BaseStream.Position += DataTypeLength.ULong; // checksum
                entry.Offset = DataTypeConverter.ReadULong(reader);
                entry.Length = DataTypeConverter.ReadULong(reader);
                return(entry);
            }).ToList();

            // Tables
            font.Tables.AddRange(
                entryList.OrderBy(entry => entry.Priority).Select <TableDirectoryEntry, IOpenTypeFontTable>(entry =>
            {
                switch (entry.Tag)
                {
                case "cmap":
                    return(CmapTable.Deserialize(reader, entry.Offset));

                case "head":
                    return(HeadTable.Deserialize(reader, entry.Offset));

                case "maxp":
                    return(MaxpTable.Deserialize(reader, entry.Offset));

                case "loca":
                    return(LocaTable.Deserialize(reader, entry.Offset,
                                                 font.Get <MaxpTable>().NumberOfGlyphs,
                                                 font.Get <HeadTable>().LocaTableVersion));

                case "glyf":
                    return(GlyfTable.Deserialize(reader, entry.Offset, entry.Length,
                                                 font.Get <LocaTable>()));

                case "hhea":
                    return(HheaTable.Deserialize(reader, entry.Offset));

                case "hmtx":
                    return(HmtxTable.Deserialize(reader, entry.Offset, font.Get <HheaTable>().NumberOfHMetrics,
                                                 font.Get <MaxpTable>().NumberOfGlyphs));

                case "post":
                    return(PostTable.Deserialize(reader, entry.Offset));

                default:
                    return(BinaryDataTable.Deserialize(reader, entry.Offset, entry.Length, entry.Tag));
                }
            }));

            return(font);
        }
Beispiel #20
0
        public static void WriteHeadTable(this BinaryWriter writer, HeadTable table)
        {
            // Type         | Name               | Description
            // -------------|--------------------|----------------------------------------------------------------------------------------------------
            // uint16       | majorVersion       | Major version number of the font header table — set to 1.
            // uint16       | minorVersion       | Minor version number of the font header table — set to 0.
            // Fixed        | fontRevision       | Set by font manufacturer.
            // uint32       | checkSumAdjustment | To compute: set it to 0, sum the entire font as uint32, then store 0xB1B0AFBA - sum.If the font is used as a component in a font collection file, the value of this field will be invalidated by changes to the file structure and font table directory, and must be ignored.
            // uint32       | magicNumber        | Set to 0x5F0F3CF5.
            // uint16       | flags              |    Bit 0: Baseline for font at y = 0;
            //                                            Bit 1: Left sidebearing point at x = 0(relevant only for TrueType rasterizers) — see the note below regarding variable fonts;
            //                                            Bit 2: Instructions may depend on point size;
            //                                            Bit 3: Force ppem to integer values for all internal scaler math; may use fractional ppem sizes if this bit is clear;
            //                                            Bit 4: Instructions may alter advance width(the advance widths might not scale linearly);
            //                                            Bit 5: This bit is not used in OpenType, and should not be set in order to ensure compatible behavior on all platforms.If set, it may result in different behavior for vertical layout in some platforms. (See Apple's specification for details regarding behavior in Apple platforms.)
            //                                            Bits 6–10: These bits are not used in Opentype and should always be cleared. (See Apple's specification for details regarding legacy used in Apple platforms.)
            //                                            Bit 11: Font data is ‘lossless’ as a results of having been subjected to optimizing transformation and/or compression (such as e.g.compression mechanisms defined by ISO/IEC 14496-18, MicroType Express, WOFF 2.0 or similar) where the original font functionality and features are retained but the binary compatibility between input and output font files is not guaranteed.As a result of the applied transform, the ‘DSIG’ Table may also be invalidated.
            //                                            Bit 12: Font converted (produce compatible metrics)
            //                                            Bit 13: Font optimized for ClearType™. Note, fonts that rely on embedded bitmaps (EBDT) for rendering should not be considered optimized for ClearType, and therefore should keep this bit cleared.
            //                                            Bit 14: Last Resort font.If set, indicates that the glyphs encoded in the cmap subtables are simply generic symbolic representations of code point ranges and don’t truly represent support for those code points.If unset, indicates that the glyphs encoded in the cmap subtables represent proper support for those code points.
            //                                            Bit 15: Reserved, set to 0
            // uint16       | unitsPerEm         | Valid range is from 16 to 16384. This value should be a power of 2 for fonts that have TrueType outlines.
            // LONGDATETIME | created            | Number of seconds since 12:00 midnight that started January 1st 1904 in GMT/UTC time zone. 64-bit integer
            // LONGDATETIME | modified           | Number of seconds since 12:00 midnight that started January 1st 1904 in GMT/UTC time zone. 64-bit integer
            // int16        | xMin               | For all glyph bounding boxes.
            // int16        | yMin               | For all glyph bounding boxes.
            // int16        | xMax               | For all glyph bounding boxes.
            // int16        | yMax               | For all glyph bounding boxes.
            // uint16       | macStyle           |   Bit 0: Bold (if set to 1);
            //                                       Bit 1: Italic(if set to 1)
            //                                       Bit 2: Underline(if set to 1)
            //                                       Bit 3: Outline(if set to 1)
            //                                       Bit 4: Shadow(if set to 1)
            //                                       Bit 5: Condensed(if set to 1)
            //                                       Bit 6: Extended(if set to 1)
            //                                       Bits 7–15: Reserved(set to 0).
            // uint16       |lowestRecPPEM       |  Smallest readable size in pixels.
            // int16        | fontDirectionHint  |  Deprecated(Set to 2).
            //                                          0: Fully mixed directional glyphs;
            //                                          1: Only strongly left to right;
            //                                          2: Like 1 but also contains neutrals;
            //                                          -1: Only strongly right to left;
            //                                          -2: Like -1 but also contains neutrals. 1
            // int16        | indexToLocFormat   | 0 for short offsets (Offset16), 1 for long (Offset32).
            // int16        | glyphDataFormat    | 0 for current format.

            writer.WriteUInt16(1);
            writer.WriteUInt16(0);
            writer.WriteUInt32(0);
            writer.WriteUInt32(0);
            writer.WriteUInt32(0x5F0F3CF5);

            writer.WriteUInt16((ushort)table.Flags);
            writer.WriteUInt16(table.UnitsPerEm);

            DateTime startDate = new DateTime(1904, 01, 01, 0, 0, 0, DateTimeKind.Utc);

            writer.WriteInt64((long)table.Created.Subtract(startDate).TotalSeconds);
            writer.WriteInt64((long)table.Modified.Subtract(startDate).TotalSeconds);
            writer.WriteInt16((short)table.Bounds.Min.X);
            writer.WriteInt16((short)table.Bounds.Min.Y);
            writer.WriteInt16((short)table.Bounds.Max.X);
            writer.WriteInt16((short)table.Bounds.Max.Y);
            writer.WriteUInt16((ushort)table.MacStyle);
            writer.WriteUInt16(table.LowestRecPPEM);
            writer.WriteInt16(2);
            writer.WriteInt16((short)table.IndexLocationFormat);
            writer.WriteInt16(0);
        }
        /// <summary>
        /// Initializes a new instance of the <see cref="StreamFontMetrics"/> class.
        /// </summary>
        /// <param name="nameTable">The name table.</param>
        /// <param name="maximumProfileTable">The maximum profile table.</param>
        /// <param name="cmap">The cmap table.</param>
        /// <param name="glyphs">The glyph table.</param>
        /// <param name="os2">The os2 table.</param>
        /// <param name="horizontalHeadTable">The horizontal head table.</param>
        /// <param name="horizontalMetrics">The horizontal metrics table.</param>
        /// <param name="verticalHeadTable">The vertical head table.</param>
        /// <param name="verticalMetrics">The vertical metrics table.</param>
        /// <param name="head">The head table.</param>
        /// <param name="kern">The kerning table.</param>
        /// <param name="gSubTable">The glyph substitution table.</param>
        /// <param name="gPosTable">The glyph positioning table.</param>
        /// <param name="colrTable">The COLR table</param>
        /// <param name="cpalTable">The CPAL table</param>
        /// <param name="fpgm">The font program table.</param>
        /// <param name="cvt">The control value table.</param>
        /// <param name="prep">The control value program table.</param>
        /// <param name="glyphDefinitionTable">The glyph definition table.</param>
        internal StreamFontMetrics(
            NameTable nameTable,
            MaximumProfileTable maximumProfileTable,
            CMapTable cmap,
            GlyphTable glyphs,
            OS2Table os2,
            HorizontalHeadTable horizontalHeadTable,
            HorizontalMetricsTable horizontalMetrics,
            VerticalHeadTable?verticalHeadTable,
            VerticalMetricsTable?verticalMetrics,
            HeadTable head,
            KerningTable kern,
            GSubTable?gSubTable,
            GPosTable?gPosTable,
            ColrTable?colrTable,
            CpalTable?cpalTable,
            FpgmTable?fpgm,
            CvtTable?cvt,
            PrepTable?prep,
            GlyphDefinitionTable?glyphDefinitionTable)
        {
            this.maximumProfileTable = maximumProfileTable;
            this.cmap                 = cmap;
            this.os2                  = os2;
            this.glyphs               = glyphs;
            this.horizontalMetrics    = horizontalMetrics;
            this.verticalMetricsTable = verticalMetrics;
            this.head                 = head;
            this.glyphCache           = new GlyphMetrics[this.glyphs.GlyphCount][];
            if (colrTable is not null)
            {
                this.colorGlyphCache = new GlyphMetrics[this.glyphs.GlyphCount][];
            }

            // https://www.microsoft.com/typography/otspec/recom.htm#tad
            // We use the same approach as FreeType for calculating the the global  ascender, descender,  and
            // height of  OpenType fonts for consistency.
            //
            // 1.If the OS/ 2 table exists and the fsSelection bit 7 is set (USE_TYPO_METRICS), trust the font
            //   and use the Typo* metrics.
            // 2.Otherwise, use the HorizontalHeadTable "hhea" table's metrics.
            // 3.If they are zero and the OS/ 2 table exists,
            //    - Use the OS/ 2 table's sTypo* metrics if they are non-zero.
            //    - Otherwise, use the OS / 2 table's usWin* metrics.
            bool useTypoMetrics = os2.FontStyle.HasFlag(OS2Table.FontStyleSelection.USE_TYPO_METRICS);

            if (useTypoMetrics)
            {
                this.Ascender   = os2.TypoAscender;
                this.Descender  = os2.TypoDescender;
                this.LineGap    = os2.TypoLineGap;
                this.LineHeight = (short)(this.Ascender - this.Descender + this.LineGap);
            }
            else
            {
                this.Ascender   = horizontalHeadTable.Ascender;
                this.Descender  = horizontalHeadTable.Descender;
                this.LineGap    = horizontalHeadTable.LineGap;
                this.LineHeight = (short)(this.Ascender - this.Descender + this.LineGap);
            }

            if (this.Ascender == 0 || this.Descender == 0)
            {
                if (os2.TypoAscender != 0 || os2.TypoDescender != 0)
                {
                    this.Ascender   = os2.TypoAscender;
                    this.Descender  = os2.TypoDescender;
                    this.LineGap    = os2.TypoLineGap;
                    this.LineHeight = (short)(this.Ascender - this.Descender + this.LineGap);
                }
                else
                {
                    this.Ascender   = (short)os2.WinAscent;
                    this.Descender  = (short)-os2.WinDescent;
                    this.LineHeight = (short)(this.Ascender - this.Descender);
                }
            }

            this.UnitsPerEm = this.head.UnitsPerEm;

            // 72 * UnitsPerEm means 1pt = 1px
            this.ScaleFactor      = this.UnitsPerEm * 72F;
            this.AdvanceWidthMax  = (short)horizontalHeadTable.AdvanceWidthMax;
            this.AdvanceHeightMax = verticalHeadTable == null ? this.LineHeight : verticalHeadTable.AdvanceHeightMax;

            this.kerningTable         = kern;
            this.gSubTable            = gSubTable;
            this.gPosTable            = gPosTable;
            this.colrTable            = colrTable;
            this.cpalTable            = cpalTable;
            this.fpgm                 = fpgm;
            this.cvt                  = cvt;
            this.prep                 = prep;
            this.glyphDefinitionTable = glyphDefinitionTable;
            this.Description          = new FontDescription(nameTable, os2, head);
        }