// Copyright (c) Six Labors.
// Licensed under the Six Labors Split License.
using System.Numerics;
namespace SixLabors.Fonts.Tables.General.Kern {
///
/// Represents a kerning subtable in the OpenType 'kern' table.
/// Each subtable contains kerning data in a specific format.
///
///
internal abstract class KerningSubTable
{
///
/// The coverage flags describing the properties of this subtable.
///
private readonly KerningCoverage coverage;
///
/// Initializes a new instance of the class.
///
/// The coverage flags for this subtable.
public KerningSubTable(KerningCoverage coverage)
=> this.coverage = coverage;
///
/// Loads a from the specified binary reader.
/// Returns if the subtable format is not supported.
///
/// The binary reader positioned at the start of the subtable header.
/// The loaded , or for unsupported formats.
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;
}
}
///
/// Attempts to get the kerning offset for the specified pair of glyph indices.
///
/// The glyph index of the first (left) glyph.
/// The glyph index of the second (right) glyph.
/// When this method returns, contains the kerning offset if found.
/// if a kerning value was found; otherwise, .
protected abstract bool TryGetOffset(ushort index1, ushort index2, out short offset);
///
/// 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.
///
/// The glyph index of the first (left) glyph.
/// The glyph index of the second (right) glyph.
/// The vector to which the kerning offset is applied.
/// if a kerning offset was applied; otherwise, .
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;
}
}
}