ImageSharp/SixLabors.Fonts/Tables/General/VerticalHeadTable.cs
2026-08-03 22:31:27 +02:00

239 lines
14 KiB
C#

// Copyright (c) Six Labors.
// Licensed under the Six Labors Split License.
namespace SixLabors.Fonts.Tables.General {
/// <summary>
/// Represents the vertical header table, which contains information needed to lay out fonts
/// whose characters are written vertically.
/// <see href="https://learn.microsoft.com/en-us/typography/opentype/spec/vhea"/>
/// </summary>
internal sealed class VerticalHeadTable : Table
{
/// <summary>
/// The table name identifier.
/// </summary>
internal const string TableName = "vhea";
/// <summary>
/// Initializes a new instance of the <see cref="VerticalHeadTable"/> class.
/// </summary>
/// <param name="ascender">The vertical typographic ascender.</param>
/// <param name="descender">The vertical typographic descender.</param>
/// <param name="lineGap">The vertical typographic line gap.</param>
/// <param name="advanceHeightMax">The maximum advance height.</param>
/// <param name="minTopSideBearing">The minimum top side bearing.</param>
/// <param name="minBottomSideBearing">The minimum bottom side bearing.</param>
/// <param name="yMaxExtent">The maximum y extent.</param>
/// <param name="caretSlopeRise">The caret slope rise.</param>
/// <param name="caretSlopeRun">The caret slope run.</param>
/// <param name="caretOffset">The caret offset for slanted fonts.</param>
/// <param name="numberOfVMetrics">The number of vertical metrics in the 'vmtx' table.</param>
public VerticalHeadTable(
short ascender,
short descender,
short lineGap,
short advanceHeightMax,
short minTopSideBearing,
short minBottomSideBearing,
short yMaxExtent,
short caretSlopeRise,
short caretSlopeRun,
short caretOffset,
ushort numberOfVMetrics)
{
this.Ascender = ascender;
this.Descender = descender;
this.LineGap = lineGap;
this.AdvanceHeightMax = advanceHeightMax;
this.MinTopSideBearing = minTopSideBearing;
this.MinBottomSideBearing = minBottomSideBearing;
this.YMaxExtent = yMaxExtent;
this.CaretSlopeRise = caretSlopeRise;
this.CaretSlopeRun = caretSlopeRun;
this.CaretOffset = caretOffset;
this.NumberOfVMetrics = numberOfVMetrics;
}
/// <summary>
/// Gets the vertical typographic ascender.
/// </summary>
public short Ascender { get; }
/// <summary>
/// Gets the vertical typographic descender.
/// </summary>
public short Descender { get; }
/// <summary>
/// Gets the vertical typographic line gap.
/// </summary>
public short LineGap { get; }
/// <summary>
/// Gets the maximum advance height in font design units.
/// </summary>
public short AdvanceHeightMax { get; }
/// <summary>
/// Gets the minimum top side bearing in font design units.
/// </summary>
public short MinTopSideBearing { get; }
/// <summary>
/// Gets the minimum bottom side bearing in font design units.
/// </summary>
public short MinBottomSideBearing { get; }
/// <summary>
/// Gets the maximum y extent: minTopSideBearing + (yMin - yMax).
/// </summary>
public short YMaxExtent { get; }
/// <summary>
/// Gets the caret slope rise. A value of 0 for rise and 1 for run specifies a horizontal caret.
/// </summary>
public short CaretSlopeRise { get; }
/// <summary>
/// Gets the caret slope run. A value of 0 for non-slanted fonts.
/// </summary>
public short CaretSlopeRun { get; }
/// <summary>
/// Gets the caret offset for slanted fonts. Set to 0 for non-slanted fonts.
/// </summary>
public short CaretOffset { get; }
/// <summary>
/// Gets the number of vertical metrics in the 'vmtx' table.
/// </summary>
public ushort NumberOfVMetrics { get; }
/// <summary>
/// Loads the <see cref="VerticalHeadTable"/> from the specified font reader.
/// </summary>
/// <param name="fontReader">The font reader.</param>
/// <returns>The <see cref="VerticalHeadTable"/>, or <see langword="null"/> if the table is not present.</returns>
public static VerticalHeadTable? Load(FontReader fontReader)
{
if (!fontReader.TryGetReaderAtTablePosition(TableName, out BigEndianBinaryReader? binaryReader))
{
return null;
}
using (binaryReader)
{
return Load(binaryReader);
}
}
/// <summary>
/// Loads the <see cref="VerticalHeadTable"/> from the specified binary reader.
/// </summary>
/// <param name="reader">The big-endian binary reader.</param>
/// <returns>The <see cref="VerticalHeadTable"/>.</returns>
public static VerticalHeadTable Load(BigEndianBinaryReader reader)
{
// +---------+----------------------+----------------------------------------------------------------------+
// | Type | Name | Description |
// +=========+======================+======================================================================+
// | fixed32 | version | Version number of the Vertical Header Table (0x00011000 for |
// | | | the current version). |
// +---------+----------------------+----------------------------------------------------------------------+
// | int16 | vertTypoAscender | The vertical typographic ascender for this font. It is the distance |
// | | | in FUnits from the vertical center baseline to the right of the |
// | | | design space. This will usually be set to half the horizontal |
// | | | advance of full-width glyphs. For example, if the full width is |
// | | | 1000 FUnits, this field will be set to 500. |
// +---------+----------------------+----------------------------------------------------------------------+
// | int16 | vertTypoDescender | The vertical typographic descender for this font. It is the |
// | | | distance in FUnits from the vertical center baseline to the left of |
// | | | the design space. This will usually be set to half the horizontal |
// | | | advance of full-width glyphs. For example, if the full width is |
// | | | 1000 FUnits, this field will be set to -500. |
// +---------+----------------------+----------------------------------------------------------------------+
// | int16 | vertTypoLineGap | The vertical typographic line gap for this font. |
// +---------+----------------------+----------------------------------------------------------------------+
// | int16 | advanceHeightMax | The maximum advance height measurement in FUnits found in |
// | | | the font. This value must be consistent with the entries in the |
// | | | vertical metrics table. |
// +---------+----------------------+----------------------------------------------------------------------+
// | int16 | minTopSideBearing | The minimum top side bearing measurement in FUnits found in |
// | | | the font, in FUnits. This value must be consistent with the |
// | | | entries in the vertical metrics table. |
// +---------+----------------------+----------------------------------------------------------------------+
// | int16 | minBottomSideBearing | The minimum bottom side bearing measurement in FUnits |
// | | | found in the font, in FUnits. This value must be consistent with |
// | | | the entries in the vertical metrics table. |
// +---------+----------------------+----------------------------------------------------------------------+
// | int16 | yMaxExtent | This is defined as the value of the minTopSideBearing field |
// | | | added to the result of the value of the yMin field subtracted |
// | | | from the value of the yMax field. |
// +---------+----------------------+----------------------------------------------------------------------+
// | int16 | caretSlopeRise | The value of the caretSlopeRise field divided by the value of the |
// | | | caretSlopeRun field determines the slope of the caret. A value |
// | | | of 0 for the rise and a value of 1 for the run specifies a |
// | | | horizontal caret. A value of 1 for the rise and a value of 0 for the |
// | | | run specifies a vertical caret. A value between 0 for the rise and |
// | | | 1 for the run is desirable for fonts whose glyphs are oblique or |
// | | | italic. For a vertical font, a horizontal caret is best. |
// +---------+----------------------+----------------------------------------------------------------------+
// | int16 | caretSlopeRun | See the caretSlopeRise field. Value = 0 for non-slanted fonts. |
// +---------+----------------------+----------------------------------------------------------------------+
// | int16 | caretOffset | The amount by which the highlight on a slanted glyph needs to |
// | | | be shifted away from the glyph in order to produce the best |
// | | | appearance. Set value equal to 0 for non-slanted fonts. |
// +---------+----------------------+----------------------------------------------------------------------+
// | int16 | reserved | Set to 0. |
// +---------+----------------------+----------------------------------------------------------------------+
// | int16 | reserved | Set to 0. |
// +---------+----------------------+----------------------------------------------------------------------+
// | int16 | reserved | Set to 0. |
// +---------+----------------------+----------------------------------------------------------------------+
// | int16 | reserved | Set to 0. |
// +---------+----------------------+----------------------------------------------------------------------+
// | int16 | metricDataFormat | Set to 0. |
// +---------+----------------------+----------------------------------------------------------------------+
// | uint16 | numOfLongVerMetrics | Number of advance heights in the Vertical Metrics table. |
// +---------+----------------------+----------------------------------------------------------------------+
ushort majorVersion = reader.ReadUInt16();
ushort minorVersion = reader.ReadUInt16();
short vertTypoAscender = reader.ReadInt16();
short vertTypoDescender = reader.ReadInt16();
short vertTypoLineGap = reader.ReadInt16();
short advanceHeightMax = reader.ReadInt16();
short minTopSideBearing = reader.ReadInt16();
short minBottomSideBearing = reader.ReadInt16();
short yMaxExtent = reader.ReadInt16();
short caretSlopeRise = reader.ReadInt16();
short caretSlopeRun = reader.ReadInt16();
short caretOffset = reader.ReadInt16();
reader.ReadInt16(); // reserved
reader.ReadInt16(); // reserved
reader.ReadInt16(); // reserved
reader.ReadInt16(); // reserved
short metricDataFormat = reader.ReadInt16(); // 0
if (metricDataFormat != 0)
{
throw new InvalidFontTableException($"Expected metricDataFormat = 0 found {metricDataFormat}", TableName);
}
ushort numOfLongVerMetrics = reader.ReadUInt16();
return new VerticalHeadTable(
vertTypoAscender,
vertTypoDescender,
vertTypoLineGap,
advanceHeightMax,
minTopSideBearing,
minBottomSideBearing,
yMaxExtent,
caretSlopeRise,
caretSlopeRun,
caretOffset,
numOfLongVerMetrics);
}
}
}