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

112 lines
5.0 KiB
C#

// Copyright (c) Six Labors.
// Licensed under the Six Labors Split License.
using System.Numerics;
namespace SixLabors.Fonts.Tables.General.Kern {
/// <summary>
/// Represents a kerning subtable in the OpenType 'kern' table.
/// Each subtable contains kerning data in a specific format.
/// <see href="https://learn.microsoft.com/en-us/typography/opentype/spec/kern"/>
/// </summary>
internal abstract class KerningSubTable
{
/// <summary>
/// The coverage flags describing the properties of this subtable.
/// </summary>
private readonly KerningCoverage coverage;
/// <summary>
/// Initializes a new instance of the <see cref="KerningSubTable"/> class.
/// </summary>
/// <param name="coverage">The coverage flags for this subtable.</param>
public KerningSubTable(KerningCoverage coverage)
=> this.coverage = coverage;
/// <summary>
/// Loads a <see cref="KerningSubTable"/> from the specified binary reader.
/// Returns <see langword="null"/> if the subtable format is not supported.
/// </summary>
/// <param name="reader">The binary reader positioned at the start of the subtable header.</param>
/// <returns>The loaded <see cref="KerningSubTable"/>, or <see langword="null"/> for unsupported formats.</returns>
public static KerningSubTable? Load(BigEndianBinaryReader reader)
{
// Kerning subtables will share the same header format.
// This header is used to identify the format of the subtable and the kind of information it contains:
// +--------+----------+----------------------------------------------------------+
// | Type | Field | Description |
// +========+==========+==========================================================+
// | uint16 | version | Kern subtable version number |
// +--------+----------+----------------------------------------------------------+
// | uint16 | length | Length of the subtable, in bytes(including this header). |
// +--------+----------+----------------------------------------------------------+
// | uint16 | coverage | What type of information is contained in this table. |
// +--------+----------+----------------------------------------------------------+
ushort subVersion = reader.ReadUInt16();
ushort length = reader.ReadUInt16();
KerningCoverage coverage = KerningCoverage.Read(reader);
if (coverage.Format == 0)
{
return Format0SubTable.Load(reader, coverage);
}
else
{
// we don't support versions other than 'Format 0' same as Windows
return null;
}
}
/// <summary>
/// Attempts to get the kerning offset for the specified pair of glyph indices.
/// </summary>
/// <param name="index1">The glyph index of the first (left) glyph.</param>
/// <param name="index2">The glyph index of the second (right) glyph.</param>
/// <param name="offset">When this method returns, contains the kerning offset if found.</param>
/// <returns><see langword="true"/> if a kerning value was found; otherwise, <see langword="false"/>.</returns>
protected abstract bool TryGetOffset(ushort index1, ushort index2, out short offset);
/// <summary>
/// Attempts to apply the kerning offset for the specified glyph pair to the result vector.
/// The offset is applied to the X component for horizontal kerning or the Y component for vertical kerning.
/// </summary>
/// <param name="index1">The glyph index of the first (left) glyph.</param>
/// <param name="index2">The glyph index of the second (right) glyph.</param>
/// <param name="result">The vector to which the kerning offset is applied.</param>
/// <returns><see langword="true"/> if a kerning offset was applied; otherwise, <see langword="false"/>.</returns>
public bool TryApplyOffset(ushort index1, ushort index2, ref Vector2 result)
{
if (this.TryGetOffset(index1, index2, out short offset))
{
if (this.coverage.Horizontal)
{
// apply to X
if (this.coverage.OverrideAccumulator)
{
result.X = offset;
}
else
{
result.X += offset;
}
}
else
{
// apply to Y
if (this.coverage.OverrideAccumulator)
{
result.Y = offset;
}
else
{
result.Y += offset;
}
}
return true;
}
return false;
}
}
}