diff --git a/src/SixLabors.Fonts/GlyphShapingData.cs b/src/SixLabors.Fonts/GlyphShapingData.cs
index a69ae3f1..8b7b2aab 100644
--- a/src/SixLabors.Fonts/GlyphShapingData.cs
+++ b/src/SixLabors.Fonts/GlyphShapingData.cs
@@ -61,7 +61,10 @@ public GlyphShapingData(GlyphShapingData data, bool clearFeatures = false)
this.Features.AddRange(data.Features);
}
- this.AppliedFeatures.AddRange(data.AppliedFeatures);
+ foreach (Tag feature in data.AppliedFeatures)
+ {
+ this.AppliedFeatures.Add(feature);
+ }
this.Bounds = data.Bounds;
}
@@ -124,7 +127,7 @@ public GlyphShapingData(GlyphShapingData data, bool clearFeatures = false)
///
/// Gets or sets the collection of applied features.
///
- public List AppliedFeatures { get; set; } = new();
+ public HashSet AppliedFeatures { get; set; } = new();
///
/// Gets or sets the shaping bounds.
diff --git a/src/SixLabors.Fonts/Tables/AdvancedTypographic/AdvancedTypographicUtils.cs b/src/SixLabors.Fonts/Tables/AdvancedTypographic/AdvancedTypographicUtils.cs
index 157a56a5..d6c5b636 100644
--- a/src/SixLabors.Fonts/Tables/AdvancedTypographic/AdvancedTypographicUtils.cs
+++ b/src/SixLabors.Fonts/Tables/AdvancedTypographic/AdvancedTypographicUtils.cs
@@ -51,7 +51,6 @@ public static bool ApplyLookupList(
int index,
int count)
{
- bool hasChanged = false;
SkippingGlyphIterator iterator = new(fontMetrics, collection, index, lookupFlags);
int currentCount = collection.Count;
@@ -62,7 +61,7 @@ public static bool ApplyLookupList(
iterator.Index = index;
iterator.Increment(sequenceIndex);
GSub.LookupTable lookup = table.LookupList.LookupTables[lookupIndex];
- hasChanged |= lookup.TrySubstitution(fontMetrics, table, collection, feature, iterator.Index, count - (iterator.Index - index));
+ _ = lookup.TrySubstitution(fontMetrics, table, collection, feature, iterator.Index, count - (iterator.Index - index));
// Account for substitutions changing the length of the collection.
if (collection.Count != currentCount)
@@ -72,7 +71,7 @@ public static bool ApplyLookupList(
}
}
- return hasChanged;
+ return true;
}
public static bool ApplyLookupList(
@@ -85,7 +84,6 @@ public static bool ApplyLookupList(
int index,
int count)
{
- bool hasChanged = false;
SkippingGlyphIterator iterator = new(fontMetrics, collection, index, lookupFlags);
foreach (SequenceLookupRecord lookupRecord in records)
{
@@ -94,10 +92,10 @@ public static bool ApplyLookupList(
iterator.Index = index;
iterator.Increment(sequenceIndex);
LookupTable lookup = table.LookupList.LookupTables[lookupIndex];
- hasChanged |= lookup.TryUpdatePosition(fontMetrics, table, collection, feature, iterator.Index, count - (iterator.Index - index));
+ _ = lookup.TryUpdatePosition(fontMetrics, table, collection, feature, iterator.Index, count - (iterator.Index - index));
}
- return hasChanged;
+ return true;
}
public static bool MatchInputSequence(SkippingGlyphIterator iterator, Tag feature, ushort increment, ushort[] sequence, Span matches)
@@ -222,7 +220,7 @@ public static bool CheckAllCoverages(
CoverageTable[] lookahead)
{
// Check that there are enough context glyphs.
- if (index - backtrack.Length < 0 || input.Length + lookahead.Length > count)
+ if (index < backtrack.Length || input.Length + lookahead.Length > count)
{
return false;
}
@@ -253,7 +251,8 @@ public static void ApplyAnchor(
int index,
AnchorTable baseAnchor,
MarkRecord markRecord,
- int baseGlyphIndex)
+ int baseGlyphIndex,
+ Tag feature)
{
GlyphShapingData baseData = collection[baseGlyphIndex];
AnchorXY baseXY = baseAnchor.GetAnchor(fontMetrics, baseData, collection);
@@ -264,18 +263,21 @@ public static void ApplyAnchor(
markData.Bounds.X = baseXY.XCoordinate - markXY.XCoordinate;
markData.Bounds.Y = baseXY.YCoordinate - markXY.YCoordinate;
markData.MarkAttachment = baseGlyphIndex;
+ markData.AppliedFeatures.Add(feature);
}
public static void ApplyPosition(
GlyphPositioningCollection collection,
int index,
- ValueRecord record)
+ ValueRecord record,
+ Tag feature)
{
GlyphShapingData current = collection[index];
current.Bounds.Width += record.XAdvance;
current.Bounds.Height += record.YAdvance;
current.Bounds.X += record.XPlacement;
current.Bounds.Y += record.YPlacement;
+ current.AppliedFeatures.Add(feature);
}
public static bool IsMarkGlyph(FontMetrics fontMetrics, ushort glyphId, GlyphShapingData shapingData)
diff --git a/src/SixLabors.Fonts/Tables/AdvancedTypographic/GPos/LookupType1SubTable.cs b/src/SixLabors.Fonts/Tables/AdvancedTypographic/GPos/LookupType1SubTable.cs
index 09d791dc..e5307d75 100644
--- a/src/SixLabors.Fonts/Tables/AdvancedTypographic/GPos/LookupType1SubTable.cs
+++ b/src/SixLabors.Fonts/Tables/AdvancedTypographic/GPos/LookupType1SubTable.cs
@@ -81,7 +81,7 @@ public override bool TryUpdatePosition(
if (coverage > -1)
{
ValueRecord record = this.valueRecord;
- AdvancedTypographicUtils.ApplyPosition(collection, index, record);
+ AdvancedTypographicUtils.ApplyPosition(collection, index, record, feature);
return true;
}
@@ -152,7 +152,7 @@ public override bool TryUpdatePosition(
if (coverage > -1)
{
ValueRecord record = this.valueRecords[coverage];
- AdvancedTypographicUtils.ApplyPosition(collection, index, record);
+ AdvancedTypographicUtils.ApplyPosition(collection, index, record, feature);
return true;
}
diff --git a/src/SixLabors.Fonts/Tables/AdvancedTypographic/GPos/LookupType2SubTable.cs b/src/SixLabors.Fonts/Tables/AdvancedTypographic/GPos/LookupType2SubTable.cs
index ed1f56aa..ecf296a7 100644
--- a/src/SixLabors.Fonts/Tables/AdvancedTypographic/GPos/LookupType2SubTable.cs
+++ b/src/SixLabors.Fonts/Tables/AdvancedTypographic/GPos/LookupType2SubTable.cs
@@ -117,10 +117,10 @@ public override bool TryUpdatePosition(
if (pairSet.TryGetPairValueRecord(glyphId2, out PairValueRecord pairValueRecord))
{
ValueRecord record1 = pairValueRecord.ValueRecord1;
- AdvancedTypographicUtils.ApplyPosition(collection, index, record1);
+ AdvancedTypographicUtils.ApplyPosition(collection, index, record1, feature);
ValueRecord record2 = pairValueRecord.ValueRecord2;
- AdvancedTypographicUtils.ApplyPosition(collection, index + 1, record2);
+ AdvancedTypographicUtils.ApplyPosition(collection, index + 1, record2, feature);
return true;
}
@@ -285,10 +285,10 @@ public override bool TryUpdatePosition(
Class2Record class2Record = class1Record.Class2Records[classDef2];
ValueRecord record1 = class2Record.ValueRecord1;
- AdvancedTypographicUtils.ApplyPosition(collection, index, record1);
+ AdvancedTypographicUtils.ApplyPosition(collection, index, record1, feature);
ValueRecord record2 = class2Record.ValueRecord2;
- AdvancedTypographicUtils.ApplyPosition(collection, index + 1, record2);
+ AdvancedTypographicUtils.ApplyPosition(collection, index + 1, record2, feature);
return true;
}
diff --git a/src/SixLabors.Fonts/Tables/AdvancedTypographic/GPos/LookupType4SubTable.cs b/src/SixLabors.Fonts/Tables/AdvancedTypographic/GPos/LookupType4SubTable.cs
index b4551407..24a05cb3 100644
--- a/src/SixLabors.Fonts/Tables/AdvancedTypographic/GPos/LookupType4SubTable.cs
+++ b/src/SixLabors.Fonts/Tables/AdvancedTypographic/GPos/LookupType4SubTable.cs
@@ -126,7 +126,7 @@ public override bool TryUpdatePosition(
MarkRecord markRecord = this.markArrayTable.MarkRecords[markIndex];
AnchorTable baseAnchor = this.baseArrayTable.BaseRecords[baseIndex].BaseAnchorTables[markRecord.MarkClass];
- AdvancedTypographicUtils.ApplyAnchor(fontMetrics, collection, index, baseAnchor, markRecord, baseGlyphIndex);
+ AdvancedTypographicUtils.ApplyAnchor(fontMetrics, collection, index, baseAnchor, markRecord, baseGlyphIndex, feature);
return true;
}
diff --git a/src/SixLabors.Fonts/Tables/AdvancedTypographic/GPos/LookupType5SubTable.cs b/src/SixLabors.Fonts/Tables/AdvancedTypographic/GPos/LookupType5SubTable.cs
index 10631ece..4db3872e 100644
--- a/src/SixLabors.Fonts/Tables/AdvancedTypographic/GPos/LookupType5SubTable.cs
+++ b/src/SixLabors.Fonts/Tables/AdvancedTypographic/GPos/LookupType5SubTable.cs
@@ -140,7 +140,7 @@ public override bool TryUpdatePosition(
MarkRecord markRecord = this.markArrayTable.MarkRecords[markIndex];
AnchorTable baseAnchor = ligatureAttach.ComponentRecords[compIndex].LigatureAnchorTables[markRecord.MarkClass];
- AdvancedTypographicUtils.ApplyAnchor(fontMetrics, collection, index, baseAnchor, markRecord, baseGlyphIndex);
+ AdvancedTypographicUtils.ApplyAnchor(fontMetrics, collection, index, baseAnchor, markRecord, baseGlyphIndex, feature);
return true;
}
diff --git a/src/SixLabors.Fonts/Tables/AdvancedTypographic/GPos/LookupType6SubTable.cs b/src/SixLabors.Fonts/Tables/AdvancedTypographic/GPos/LookupType6SubTable.cs
index 8799de54..2bc1f6d6 100644
--- a/src/SixLabors.Fonts/Tables/AdvancedTypographic/GPos/LookupType6SubTable.cs
+++ b/src/SixLabors.Fonts/Tables/AdvancedTypographic/GPos/LookupType6SubTable.cs
@@ -158,7 +158,7 @@ public override bool TryUpdatePosition(
MarkRecord markRecord = this.mark1ArrayTable.MarkRecords[mark1Index];
AnchorTable baseAnchor = this.mark2ArrayTable.Mark2Records[mark2Index].MarkAnchorTable[markRecord.MarkClass];
- AdvancedTypographicUtils.ApplyAnchor(fontMetrics, collection, index, baseAnchor, markRecord, prevIdx);
+ AdvancedTypographicUtils.ApplyAnchor(fontMetrics, collection, index, baseAnchor, markRecord, prevIdx, feature);
return true;
}
diff --git a/tests/Images/ReferenceOutput/Issue_451_A-.png b/tests/Images/ReferenceOutput/Issue_451_A-.png
new file mode 100644
index 00000000..ab1a3ce6
--- /dev/null
+++ b/tests/Images/ReferenceOutput/Issue_451_A-.png
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:17b4d0c60a62c2a7d0dae921906c803f67d6885ea2b53cb58c7c62c25a1654dc
+size 2213
diff --git a/tests/Images/ReferenceOutput/Issue_451_B-.png b/tests/Images/ReferenceOutput/Issue_451_B-.png
new file mode 100644
index 00000000..28eedbd7
--- /dev/null
+++ b/tests/Images/ReferenceOutput/Issue_451_B-.png
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:8662ca4a1d837e5c0714e4deb36a679edccf3aeaab245d70494f5643c04a5475
+size 1843
diff --git a/tests/Images/ReferenceOutput/Issue_451_C-.png b/tests/Images/ReferenceOutput/Issue_451_C-.png
new file mode 100644
index 00000000..6e754ab3
--- /dev/null
+++ b/tests/Images/ReferenceOutput/Issue_451_C-.png
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:cd9256bf5cd2dcd4a48a8ba6f6a9f4d10cfd5ee858dc99f3122cf340b14140ad
+size 23102
diff --git a/tests/SixLabors.Fonts.Tests/Fonts/VeryBerryProRegular.ttf b/tests/SixLabors.Fonts.Tests/Fonts/VeryBerryProRegular.ttf
new file mode 100644
index 00000000..2c10e070
Binary files /dev/null and b/tests/SixLabors.Fonts.Tests/Fonts/VeryBerryProRegular.ttf differ
diff --git a/tests/SixLabors.Fonts.Tests/Issues/Issues_451.cs b/tests/SixLabors.Fonts.Tests/Issues/Issues_451.cs
new file mode 100644
index 00000000..4fe1a766
--- /dev/null
+++ b/tests/SixLabors.Fonts.Tests/Issues/Issues_451.cs
@@ -0,0 +1,47 @@
+// Copyright (c) Six Labors.
+// Licensed under the Six Labors Split License.
+
+using System.Numerics;
+
+namespace SixLabors.Fonts.Tests.Issues;
+
+public class Issues_451
+{
+ private readonly FontFamily berry = new FontCollection().Add(TestFonts.VeryBerryProRegular);
+
+ [Fact]
+ public void Issue_451_A()
+ {
+ Font font = this.berry.CreateFont(85);
+ TextLayoutTestUtilities.TestLayout(
+ "The",
+ new TextOptions(font)
+ {
+ Origin = new Vector2(0, 50),
+ });
+ }
+
+ [Fact]
+ public void Issue_451_B()
+ {
+ Font font = this.berry.CreateFont(85);
+ TextLayoutTestUtilities.TestLayout(
+ "Th",
+ new TextOptions(font)
+ {
+ Origin = new Vector2(0, 50),
+ });
+ }
+
+ [Fact]
+ public void Issue_451_C()
+ {
+ Font font = this.berry.CreateFont(85);
+ TextLayoutTestUtilities.TestLayout(
+ "The quick brown fox jumps over the lazy dog",
+ new TextOptions(font)
+ {
+ Origin = new Vector2(0, 50),
+ });
+ }
+}
diff --git a/tests/SixLabors.Fonts.Tests/TestFonts.cs b/tests/SixLabors.Fonts.Tests/TestFonts.cs
index 54187cb1..b7a5e233 100644
--- a/tests/SixLabors.Fonts.Tests/TestFonts.cs
+++ b/tests/SixLabors.Fonts.Tests/TestFonts.cs
@@ -261,6 +261,8 @@ public static class TestFonts
public static string CharisSILRegular => GetFullPath("CharisSIL-Regular.ttf");
+ public static string VeryBerryProRegular => GetFullPath("VeryBerryProRegular.ttf");
+
public static Stream TwemojiMozillaData() => OpenStream(TwemojiMozillaFile);
public static Stream SegoeuiEmojiData() => OpenStream(SegoeuiEmojiFile);