328 lines
26 KiB
C#
328 lines
26 KiB
C#
// Copyright (c) Six Labors.
|
|
// Licensed under the Six Labors Split License.
|
|
|
|
using System;
|
|
|
|
namespace SixLabors.Fonts.Tables.AdvancedTypographic {
|
|
/// <summary>
|
|
/// Provides shared utility methods for loading GPOS and GSUB lookup subtables.
|
|
/// </summary>
|
|
internal static class TableLoadingUtils
|
|
{
|
|
/// <summary>
|
|
/// Loads Sequence Context Format 1 (simple glyph contexts) data.
|
|
/// </summary>
|
|
/// <param name="reader">The big endian binary reader.</param>
|
|
/// <param name="offset">Offset from the beginning of the subtable.</param>
|
|
/// <param name="coverageTable">When this method returns, contains the loaded coverage table.</param>
|
|
/// <returns>The array of sequence rule set tables.</returns>
|
|
internal static SequenceRuleSetTable[] LoadSequenceContextFormat1(BigEndianBinaryReader reader, long offset, out CoverageTable coverageTable)
|
|
{
|
|
// https://docs.microsoft.com/en-us/typography/opentype/spec/chapter2#seqctxt1
|
|
// SequenceContextFormat1
|
|
// +----------+------------------------------------+---------------------------------------------------------------+
|
|
// | Type | Name | Description |
|
|
// +==========+====================================+===============================================================+
|
|
// | uint16 | format | Format identifier: format = 1 |
|
|
// +----------+------------------------------------+---------------------------------------------------------------+
|
|
// | Offset16 | coverageOffset | Offset to Coverage table, from beginning of |
|
|
// | | | SequenceContextFormat1 table. |
|
|
// +----------+------------------------------------+---------------------------------------------------------------+
|
|
// | uint16 | seqRuleSetCount | Number of SequenceRuleSet tables. |
|
|
// +----------+------------------------------------+---------------------------------------------------------------+
|
|
// | Offset16 | seqRuleSetOffsets[seqRuleSetCount] | Array of offsets to SequenceRuleSet tables, from beginning of |
|
|
// | | | SequenceContextFormat1 table (offsets may be NULL). |
|
|
// +----------+------------------------------------+---------------------------------------------------------------+
|
|
ushort coverageOffset = reader.ReadOffset16();
|
|
ushort seqRuleSetCount = reader.ReadUInt16();
|
|
|
|
using Buffer<ushort> seqRuleSetOffsetsBuffer = new(seqRuleSetCount);
|
|
Span<ushort> seqRuleSetOffsets = seqRuleSetOffsetsBuffer.GetSpan();
|
|
reader.ReadUInt16Array(seqRuleSetOffsets);
|
|
|
|
var seqRuleSets = new SequenceRuleSetTable[seqRuleSetCount];
|
|
|
|
for (int i = 0; i < seqRuleSets.Length; i++)
|
|
{
|
|
seqRuleSets[i] = SequenceRuleSetTable.Load(reader, offset + seqRuleSetOffsets[i]);
|
|
}
|
|
|
|
coverageTable = CoverageTable.Load(reader, offset + coverageOffset);
|
|
return seqRuleSets;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Loads Sequence Context Format 2 (class-based glyph contexts) data.
|
|
/// </summary>
|
|
/// <param name="reader">The big endian binary reader.</param>
|
|
/// <param name="offset">Offset from the beginning of the subtable.</param>
|
|
/// <param name="classDefTable">When this method returns, contains the loaded class definition table.</param>
|
|
/// <param name="classSeqRuleSets">When this method returns, contains the array of class sequence rule set tables.</param>
|
|
/// <returns>The coverage table.</returns>
|
|
internal static CoverageTable LoadSequenceContextFormat2(BigEndianBinaryReader reader, long offset, out ClassDefinitionTable classDefTable, out ClassSequenceRuleSetTable[] classSeqRuleSets)
|
|
{
|
|
// https://docs.microsoft.com/en-us/typography/opentype/spec/chapter2#sequence-context-format-2-class-based-glyph-contexts
|
|
// Context Positioning Subtable Format 2: Class-based Glyph Contexts.
|
|
// +----------+----------------------------------------------+--------------------------------------------------------------------+
|
|
// | Type | Name | Description |
|
|
// +==========+==============================================+====================================================================+
|
|
// | uint16 | format | Format identifier: format = 2 |
|
|
// +----------+----------------------------------------------+--------------------------------------------------------------------+
|
|
// | Offset16 | coverageOffset | Offset to Coverage table, from beginning of |
|
|
// | | | SequenceContextFormat2 table. |
|
|
// +----------+----------------------------------------------+--------------------------------------------------------------------+
|
|
// | Offset16 | classDefOffset | Offset to ClassDef table, from beginning of |
|
|
// | | | SequenceContextFormat2 table. |
|
|
// +----------+----------------------------------------------+--------------------------------------------------------------------+
|
|
// | uint16 | classSeqRuleSetCount | Number of ClassSequenceRuleSet tables. |
|
|
// +----------+----------------------------------------------+--------------------------------------------------------------------+
|
|
// | Offset16 | classSeqRuleSetOffsets[classSeqRuleSetCount] | Array of offsets to ClassSequenceRuleSet tables, from beginning of |
|
|
// | | | SequenceContextFormat2 table (may be NULL). |
|
|
// +----------+----------------------------------------------+--------------------------------------------------------------------+
|
|
ushort coverageOffset = reader.ReadOffset16();
|
|
ushort classDefOffset = reader.ReadOffset16();
|
|
ushort classSeqRuleSetCount = reader.ReadUInt16();
|
|
|
|
using Buffer<ushort> classSeqRuleSetOffsetsBuffer = new(classSeqRuleSetCount);
|
|
Span<ushort> classSeqRuleSetOffsets = classSeqRuleSetOffsetsBuffer.GetSpan();
|
|
reader.ReadUInt16Array(classSeqRuleSetOffsets);
|
|
|
|
var coverageTable = CoverageTable.Load(reader, offset + coverageOffset);
|
|
classDefTable = ClassDefinitionTable.Load(reader, offset + classDefOffset);
|
|
|
|
classSeqRuleSets = new ClassSequenceRuleSetTable[classSeqRuleSetCount];
|
|
for (int i = 0; i < classSeqRuleSets.Length; i++)
|
|
{
|
|
ushort ruleSetOffset = classSeqRuleSetOffsets[i];
|
|
if (ruleSetOffset > 0)
|
|
{
|
|
classSeqRuleSets[i] = ClassSequenceRuleSetTable.Load(reader, offset + classSeqRuleSetOffsets[i]);
|
|
}
|
|
}
|
|
|
|
return coverageTable;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Loads Sequence Context Format 3 (coverage-based glyph contexts) data.
|
|
/// </summary>
|
|
/// <param name="reader">The big endian binary reader.</param>
|
|
/// <param name="offset">Offset from the beginning of the subtable.</param>
|
|
/// <param name="coverageTables">When this method returns, contains the array of coverage tables for the input sequence.</param>
|
|
/// <returns>The array of sequence lookup records.</returns>
|
|
internal static SequenceLookupRecord[] LoadSequenceContextFormat3(BigEndianBinaryReader reader, long offset, out CoverageTable[] coverageTables)
|
|
{
|
|
// https://docs.microsoft.com/en-us/typography/opentype/spec/chapter2#sequence-context-format-3-coverage-based-glyph-contexts
|
|
// SequenceContextFormat3
|
|
// +----------------------+----------------------------------+-------------------------------------------+
|
|
// | Type | Name | Description |
|
|
// +======================+==================================+===========================================+
|
|
// | uint16 | format | Format identifier: format = 3 |
|
|
// +----------------------+----------------------------------+-------------------------------------------+
|
|
// | uint16 | glyphCount | Number of glyphs in the input sequence |
|
|
// +----------------------+----------------------------------+-------------------------------------------+
|
|
// | uint16 | seqLookupCount | Number of SequenceLookupRecords |
|
|
// +----------------------+----------------------------------+-------------------------------------------+
|
|
// | Offset16 | coverageOffsets[glyphCount] | Array of offsets to Coverage tables, from |
|
|
// | | | beginning of SequenceContextFormat3 |
|
|
// | | | subtable |
|
|
// +----------------------+----------------------------------+-------------------------------------------+
|
|
// | SequenceLookupRecord | seqLookupRecords[seqLookupCount] | Array of SequenceLookupRecords |
|
|
// +----------------------+----------------------------------+-------------------------------------------+
|
|
ushort glyphCount = reader.ReadUInt16();
|
|
ushort seqLookupCount = reader.ReadUInt16();
|
|
ushort[] coverageOffsets = reader.ReadUInt16Array(glyphCount);
|
|
SequenceLookupRecord[] seqLookupRecords = SequenceLookupRecord.LoadArray(reader, seqLookupCount);
|
|
|
|
coverageTables = new CoverageTable[glyphCount];
|
|
for (int i = 0; i < coverageTables.Length; i++)
|
|
{
|
|
coverageTables[i] = CoverageTable.Load(reader, offset + coverageOffsets[i]);
|
|
}
|
|
|
|
return seqLookupRecords;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Loads Chained Sequence Context Format 1 (simple glyph contexts) data.
|
|
/// </summary>
|
|
/// <param name="reader">The big endian binary reader.</param>
|
|
/// <param name="offset">Offset from the beginning of the subtable.</param>
|
|
/// <param name="coverageTable">When this method returns, contains the loaded coverage table.</param>
|
|
/// <returns>The array of chained sequence rule set tables.</returns>
|
|
internal static ChainedSequenceRuleSetTable[] LoadChainedSequenceContextFormat1(BigEndianBinaryReader reader, long offset, out CoverageTable coverageTable)
|
|
{
|
|
// https://docs.microsoft.com/en-us/typography/opentype/spec/chapter2#chained-sequence-context-format-1-simple-glyph-contexts
|
|
// ChainedSequenceContextFormat1
|
|
// +----------+--------------------------------------------------+------------------------------------------+
|
|
// | Type | Name | Description |
|
|
// +==========+==================================================+==========================================+
|
|
// | uint16 | format | Format identifier: format = 1 |
|
|
// +----------+--------------------------------------------------+------------------------------------------+
|
|
// | Offset16 | coverageOffset | Offset to Coverage table, from beginning |
|
|
// | | | of ChainSequenceContextFormat1 table |
|
|
// +----------+--------------------------------------------------+------------------------------------------+
|
|
// | uint16 | chainedSeqRuleSetCount | Number of ChainedSequenceRuleSet tables |
|
|
// +----------+--------------------------------------------------+------------------------------------------+
|
|
// | Offset16 | chainedSeqRuleSetOffsets[chainedSeqRuleSetCount] | Array of offsets to ChainedSeqRuleSet |
|
|
// | | | tables, from beginning of |
|
|
// | | | ChainedSequenceContextFormat1 table |
|
|
// | | | (may be NULL) |
|
|
// +----------+--------------------------------------------------+------------------------------------------+
|
|
ushort coverageOffset = reader.ReadOffset16();
|
|
ushort chainedSeqRuleSetCount = reader.ReadUInt16();
|
|
|
|
using Buffer<ushort> chainedSeqRuleSetOffsetsBuffer = new(chainedSeqRuleSetCount);
|
|
Span<ushort> chainedSeqRuleSetOffsets = chainedSeqRuleSetOffsetsBuffer.GetSpan();
|
|
reader.ReadUInt16Array(chainedSeqRuleSetOffsets);
|
|
|
|
var seqRuleSets = new ChainedSequenceRuleSetTable[chainedSeqRuleSetCount];
|
|
|
|
for (int i = 0; i < seqRuleSets.Length; i++)
|
|
{
|
|
if (chainedSeqRuleSetOffsets[i] > 0)
|
|
{
|
|
seqRuleSets[i] = ChainedSequenceRuleSetTable.Load(reader, offset + chainedSeqRuleSetOffsets[i]);
|
|
}
|
|
}
|
|
|
|
coverageTable = CoverageTable.Load(reader, offset + coverageOffset);
|
|
return seqRuleSets;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Loads Chained Sequence Context Format 2 (class-based glyph contexts) data.
|
|
/// </summary>
|
|
/// <param name="reader">The big endian binary reader.</param>
|
|
/// <param name="offset">Offset from the beginning of the subtable.</param>
|
|
/// <param name="coverageTable">When this method returns, contains the loaded coverage table.</param>
|
|
/// <param name="backtrackClassDefTable">When this method returns, contains the backtrack class definition table.</param>
|
|
/// <param name="inputClassDefTable">When this method returns, contains the input class definition table.</param>
|
|
/// <param name="lookaheadClassDefTable">When this method returns, contains the lookahead class definition table.</param>
|
|
/// <returns>The array of chained class sequence rule set tables.</returns>
|
|
internal static ChainedClassSequenceRuleSetTable[] LoadChainedSequenceContextFormat2(
|
|
BigEndianBinaryReader reader,
|
|
long offset,
|
|
out CoverageTable coverageTable,
|
|
out ClassDefinitionTable backtrackClassDefTable,
|
|
out ClassDefinitionTable inputClassDefTable,
|
|
out ClassDefinitionTable lookaheadClassDefTable)
|
|
{
|
|
// https://docs.microsoft.com/en-us/typography/opentype/spec/chapter2#chained-sequence-context-format-2-class-based-glyph-contexts
|
|
// ChainedSequenceContextFormat2
|
|
// +----------+------------------------------------------------------------+---------------------------------------------------------------------+
|
|
// | Type | Name | Description |
|
|
// +==========+============================================================+=====================================================================+
|
|
// | uint16 | format | Format identifier: format = 2 |
|
|
// +----------+------------------------------------------------------------+---------------------------------------------------------------------+
|
|
// | Offset16 | coverageOffset | Offset to Coverage table, from beginning |
|
|
// | | | of ChainedSequenceContextFormat2 table |
|
|
// +----------+------------------------------------------------------------+---------------------------------------------------------------------+
|
|
// | Offset16 | backtrackClassDefOffset | Offset to ClassDef table containing |
|
|
// | | | backtrack sequence context, from |
|
|
// | | | beginning of ChainedSequenceContextFormat2 table |
|
|
// +----------+------------------------------------------------------------+---------------------------------------------------------------------+
|
|
// | Offset16 | inputClassDefOffset | Offset to ClassDef table containing input |
|
|
// | | | sequence context, from beginning of |
|
|
// | | | ChainedSequenceContextFormat2 table |
|
|
// +----------+------------------------------------------------------------+---------------------------------------------------------------------+
|
|
// | Offset16 | lookaheadClassDefOffset | Offset to ClassDef table containing |
|
|
// | | | lookahead sequence context, from |
|
|
// | | | beginning of ChainedSequenceContextFormat2 table |
|
|
// +----------+------------------------------------------------------------+---------------------------------------------------------------------+
|
|
// | uint16 | chainedClassSeqRuleSetCount | Number of ChainedClassSequenceRuleSet tables |
|
|
// +----------+------------------------------------------------------------+---------------------------------------------------------------------+
|
|
// | Offset16 | chainedClassSeqRuleSetOffsets[chainedClassSeqRuleSetCount] | Array of offsets to ChainedClassSequenceRuleSet tables, |
|
|
// | | | from beginning of ChainedSequenceContextFormat2 table (may be NULL) |
|
|
// +----------+------------------------------------------------------------+---------------------------------------------------------------------+
|
|
ushort coverageOffset = reader.ReadOffset16();
|
|
ushort backtrackClassDefOffset = reader.ReadOffset16();
|
|
ushort inputClassDefOffset = reader.ReadOffset16();
|
|
ushort lookaheadClassDefOffset = reader.ReadOffset16();
|
|
ushort chainedClassSeqRuleSetCount = reader.ReadUInt16();
|
|
ChainedClassSequenceRuleSetTable[] seqRuleSets = Array.Empty<ChainedClassSequenceRuleSetTable>();
|
|
if (chainedClassSeqRuleSetCount != 0)
|
|
{
|
|
ushort[] chainedClassSeqRuleSetOffsets = new ushort[chainedClassSeqRuleSetCount];
|
|
for (int i = 0; i < chainedClassSeqRuleSetCount; i++)
|
|
{
|
|
chainedClassSeqRuleSetOffsets[i] = reader.ReadOffset16();
|
|
}
|
|
|
|
seqRuleSets = new ChainedClassSequenceRuleSetTable[chainedClassSeqRuleSetCount];
|
|
for (int i = 0; i < seqRuleSets.Length; i++)
|
|
{
|
|
if (chainedClassSeqRuleSetOffsets[i] > 0)
|
|
{
|
|
seqRuleSets[i] =
|
|
ChainedClassSequenceRuleSetTable.Load(reader, offset + chainedClassSeqRuleSetOffsets[i]);
|
|
}
|
|
}
|
|
}
|
|
|
|
coverageTable = CoverageTable.Load(reader, offset + coverageOffset);
|
|
backtrackClassDefTable = ClassDefinitionTable.Load(reader, offset + backtrackClassDefOffset);
|
|
inputClassDefTable = ClassDefinitionTable.Load(reader, offset + inputClassDefOffset);
|
|
lookaheadClassDefTable = ClassDefinitionTable.Load(reader, offset + lookaheadClassDefOffset);
|
|
return seqRuleSets;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Loads Chained Sequence Context Format 3 (coverage-based glyph contexts) data.
|
|
/// </summary>
|
|
/// <param name="reader">The big endian binary reader.</param>
|
|
/// <param name="offset">Offset from the beginning of the subtable.</param>
|
|
/// <param name="backtrackCoverageTables">When this method returns, contains the array of backtrack coverage tables.</param>
|
|
/// <param name="inputCoverageTables">When this method returns, contains the array of input coverage tables.</param>
|
|
/// <param name="lookaheadCoverageTables">When this method returns, contains the array of lookahead coverage tables.</param>
|
|
/// <returns>The array of sequence lookup records.</returns>
|
|
internal static SequenceLookupRecord[] LoadChainedSequenceContextFormat3(
|
|
BigEndianBinaryReader reader,
|
|
long offset,
|
|
out CoverageTable[] backtrackCoverageTables,
|
|
out CoverageTable[] inputCoverageTables,
|
|
out CoverageTable[] lookaheadCoverageTables)
|
|
{
|
|
// https://docs.microsoft.com/en-us/typography/opentype/spec/chapter2#chseqctxt3
|
|
// ChainedSequenceContextFormat3 1
|
|
// +----------------------+-----------------------------------------------+----------------------------------------------------------------+
|
|
// | Type | Name | Description |
|
|
// +======================+===============================================+================================================================+
|
|
// | uint16 | format | Format identifier: format = 3 |
|
|
// +----------------------+-----------------------------------------------+----------------------------------------------------------------+
|
|
// | uint16 | backtrackGlyphCount | Number of glyphs in the backtrack sequence |
|
|
// +----------------------+-----------------------------------------------+----------------------------------------------------------------+
|
|
// | Offset16 | backtrackCoverageOffsets[backtrackGlyphCount] | Array of offsets to coverage tables for the backtrack sequence |
|
|
// +----------------------+-----------------------------------------------+----------------------------------------------------------------+
|
|
// | uint16 | inputGlyphCount | Number of glyphs in the input sequence |
|
|
// +----------------------+-----------------------------------------------+----------------------------------------------------------------+
|
|
// | Offset16 | inputCoverageOffsets[inputGlyphCount] | Array of offsets to coverage tables for the input sequence |
|
|
// +----------------------+-----------------------------------------------+----------------------------------------------------------------+
|
|
// | uint16 | lookaheadGlyphCount | Number of glyphs in the lookahead sequence |
|
|
// +----------------------+-----------------------------------------------+----------------------------------------------------------------+
|
|
// | Offset16 | lookaheadCoverageOffsets[lookaheadGlyphCount] | Array of offsets to coverage tables for the lookahead sequence |
|
|
// +----------------------+-----------------------------------------------+----------------------------------------------------------------+
|
|
// | uint16 | seqLookupCount | Number of SequenceLookupRecords |
|
|
// +----------------------+-----------------------------------------------+----------------------------------------------------------------+
|
|
// | SequenceLookupRecord | seqLookupRecords[seqLookupCount] | Array of SequenceLookupRecords |
|
|
// +----------------------+-----------------------------------------------+----------------------------------------------------------------+
|
|
ushort backtrackGlyphCount = reader.ReadUInt16();
|
|
ushort[] backtrackCoverageOffsets = reader.ReadUInt16Array(backtrackGlyphCount);
|
|
|
|
ushort inputGlyphCount = reader.ReadUInt16();
|
|
ushort[] inputCoverageOffsets = reader.ReadUInt16Array(inputGlyphCount);
|
|
|
|
ushort lookaheadGlyphCount = reader.ReadUInt16();
|
|
ushort[] lookaheadCoverageOffsets = reader.ReadUInt16Array(lookaheadGlyphCount);
|
|
|
|
ushort seqLookupCount = reader.ReadUInt16();
|
|
SequenceLookupRecord[] seqLookupRecords = SequenceLookupRecord.LoadArray(reader, seqLookupCount);
|
|
|
|
backtrackCoverageTables = CoverageTable.LoadArray(reader, offset, backtrackCoverageOffsets);
|
|
inputCoverageTables = CoverageTable.LoadArray(reader, offset, inputCoverageOffsets);
|
|
lookaheadCoverageTables = CoverageTable.LoadArray(reader, offset, lookaheadCoverageOffsets);
|
|
return seqLookupRecords;
|
|
}
|
|
}
|
|
}
|