// 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); } } }