// Copyright (c) Six Labors. // Licensed under the Six Labors Split License. namespace SixLabors.Fonts.Tables.General { /// /// Represents the horizontal metrics table, which contains the horizontal layout metrics /// (advance widths and left side bearings) for each glyph. /// /// internal sealed class HorizontalMetricsTable : Table { /// /// The table name identifier. /// internal const string TableName = "hmtx"; /// /// The left side bearings array for all glyphs. /// private readonly short[] leftSideBearings; /// /// The advance widths array for glyphs with full metric records. /// private readonly ushort[] advancedWidths; /// /// Initializes a new instance of the class. /// /// The advance widths for each glyph. /// The left side bearings for each glyph. public HorizontalMetricsTable(ushort[] advancedWidths, short[] leftSideBearings) { this.advancedWidths = advancedWidths; this.leftSideBearings = leftSideBearings; } /// /// Gets the advance width for the specified glyph. If the glyph index exceeds the /// number of metric records, the last record's advance width is returned. /// /// The glyph index. /// The advance width in font design units. public ushort GetAdvancedWidth(int glyphIndex) { if (glyphIndex >= this.advancedWidths.Length) { // Records are indexed by glyph ID. As an optimization, the number of records can // be less than the number of glyphs, in which case the advance width value of the // last record applies to all remaining glyph IDs. return this.advancedWidths[^1]; } return this.advancedWidths[glyphIndex]; } /// /// Gets the left side bearing for the specified glyph. /// /// The glyph index. /// The left side bearing in font design units. internal short GetLeftSideBearing(int glyphIndex) { if (glyphIndex >= this.leftSideBearings.Length) { return this.leftSideBearings[^1]; } return this.leftSideBearings[glyphIndex]; } /// /// Loads the from the specified font reader. /// /// The font reader. /// The . public static HorizontalMetricsTable Load(FontReader reader) { // you should load all dependent tables prior to manipulating the reader HorizontalHeadTable headTable = reader.GetTable(); MaximumProfileTable profileTable = reader.GetTable(); // Move to start of table using BigEndianBinaryReader binaryReader = reader.GetReaderAtTablePosition(TableName); return Load(binaryReader, headTable.NumberOfHMetrics, profileTable.GlyphCount); } /// /// Loads the from the specified binary reader. /// /// The big-endian binary reader. /// The number of horizontal metric records (from 'hhea'). /// The total number of glyphs in the font (from 'maxp'). /// The . public static HorizontalMetricsTable Load(BigEndianBinaryReader reader, int metricCount, int glyphCount) { // Type | Name | Description // longHorMetric | hMetrics[numberOfHMetrics] | Paired advance width and left side bearing values for each glyph. Records are indexed by glyph ID. // int16 | leftSideBearing[numGlyphs - numberOfHMetrics] | Left side bearings for glyph IDs greater than or equal to numberOfHMetrics. int bearingCount = glyphCount - metricCount; ushort[] advancedWidth = new ushort[metricCount]; short[] leftSideBearings = new short[glyphCount]; for (int i = 0; i < metricCount; i++) { // longHorMetric Record: // Type | Name | Description // uint16 | advanceWidth | Glyph advance width, in font design units. // int16 | lsb | Glyph left side bearing, in font design units. advancedWidth[i] = reader.ReadUInt16(); leftSideBearings[i] = reader.ReadInt16(); } for (int i = 0; i < bearingCount; i++) { leftSideBearings[metricCount + i] = reader.ReadInt16(); } return new HorizontalMetricsTable(advancedWidth, leftSideBearings); } } }