// Copyright (c) Six Labors.
// Licensed under the Six Labors Split License.
using System;
namespace SixLabors.Fonts.Tables.AdvancedTypographic {
///
/// An iterator over a glyph shaping collection that respects OpenType lookup flags,
/// skipping glyphs that should be ignored (marks, base glyphs, ligatures) based on the flags.
///
internal struct SkippingGlyphIterator
{
private readonly FontMetrics fontMetrics;
private bool ignoreMarks;
private bool ignoreBaseGlyphs;
private bool ignoreLigatures;
private ushort markAttachmentType;
private bool useMarkFilteringSet;
private ushort markFilteringSet;
///
/// Initializes a new instance of the struct.
///
/// The font metrics for glyph class lookups.
/// The glyph shaping collection to iterate over.
/// The starting index in the collection.
/// The lookup flags that control which glyphs to skip.
/// The mark filtering set index, used when is set.
public SkippingGlyphIterator(
FontMetrics fontMetrics,
IGlyphShapingCollection collection,
int index,
LookupFlags lookupFlags,
ushort markFilteringSet)
{
this.fontMetrics = fontMetrics;
this.Collection = collection;
this.Index = index;
this.ignoreMarks = (lookupFlags & LookupFlags.IgnoreMarks) != 0;
this.ignoreBaseGlyphs = (lookupFlags & LookupFlags.IgnoreBaseGlyphs) != 0;
this.ignoreLigatures = (lookupFlags & LookupFlags.IgnoreLigatures) != 0;
this.markAttachmentType = (ushort)((int)(lookupFlags & LookupFlags.MarkAttachmentTypeMask) >> 8);
this.useMarkFilteringSet = (lookupFlags & LookupFlags.UseMarkFilteringSet) != 0;
this.markFilteringSet = markFilteringSet;
}
///
/// Gets the glyph shaping collection being iterated.
///
public IGlyphShapingCollection Collection { get; }
///
/// Gets or sets the current index in the collection.
///
public int Index { get; set; }
///
/// Advances to the next non-skipped glyph in the forward direction.
///
/// The new index after advancing.
public int Next()
{
this.Move(1);
return this.Index;
}
///
/// Advances to the next non-skipped glyph in the backward direction.
///
/// The new index after moving backward.
public int Prev()
{
this.Move(-1);
return this.Index;
}
///
/// Moves the iterator by the specified number of non-skipped glyphs. A negative count moves backward.
///
/// The number of positions to move. Negative values move backward.
/// The new index after incrementing.
public int Increment(int count = 1)
{
int direction = count < 0 ? -1 : 1;
count = Math.Abs(count);
while (count-- > 0)
{
this.Move(direction);
}
return this.Index;
}
///
/// Resets the iterator to a new index and lookup flags.
///
/// The new starting index.
/// The new lookup flags.
/// The new mark filtering set index.
public void Reset(int index, LookupFlags lookupFlags, ushort markFilteringSet)
{
this.Index = index;
this.ignoreMarks = (lookupFlags & LookupFlags.IgnoreMarks) != 0;
this.ignoreBaseGlyphs = (lookupFlags & LookupFlags.IgnoreBaseGlyphs) != 0;
this.ignoreLigatures = (lookupFlags & LookupFlags.IgnoreLigatures) != 0;
this.markAttachmentType = (ushort)((int)(lookupFlags & LookupFlags.MarkAttachmentTypeMask) >> 8);
this.useMarkFilteringSet = (lookupFlags & LookupFlags.UseMarkFilteringSet) != 0;
this.markFilteringSet = markFilteringSet;
}
///
/// Moves the iterator one step in the given direction, skipping glyphs that should be ignored.
///
/// The direction to move: 1 for forward, -1 for backward.
private void Move(int direction)
{
this.Index += direction;
while (this.Index >= 0 && this.Index < this.Collection.Count)
{
if (!this.ShouldIgnore(this.Index))
{
break;
}
this.Index += direction;
}
}
///
/// Determines whether the glyph at the given index should be ignored based on the current lookup flags.
///
/// The index of the glyph to check.
/// if the glyph should be skipped; otherwise, .
private readonly bool ShouldIgnore(int index)
{
GlyphShapingData data = this.Collection[index];
GlyphShapingClass shapingClass = AdvancedTypographicUtils.GetGlyphShapingClass(this.fontMetrics, data.GlyphId, data);
if (this.useMarkFilteringSet && shapingClass.IsMark)
{
// Skip marks not in the lookup's MarkFilteringSet.
// This requires GDEF MarkGlyphSetsDef support.
if (!AdvancedTypographicUtils.IsInMarkFilteringSet(this.fontMetrics, this.markFilteringSet, data.GlyphId))
{
return true;
}
}
return (this.ignoreMarks && shapingClass.IsMark) ||
(this.ignoreBaseGlyphs && shapingClass.IsBase) ||
(this.ignoreLigatures && shapingClass.IsLigature) ||
(this.markAttachmentType > 0 && shapingClass.IsMark && shapingClass.MarkAttachmentType != this.markAttachmentType);
}
}
}