public bool TryUpdatePositions(FontMetrics fontMetrics, GlyphPositioningCollection collection, KerningMode kerningMode, out bool kerned) { kerned = false; bool updated = false; for (ushort i = 0; i < collection.Count; i++) { if (!collection.ShouldProcess(fontMetrics, i)) { continue; } ScriptClass current = CodePoint.GetScriptClass(collection.GetGlyphShapingData(i).CodePoint); BaseShaper shaper = ShaperFactory.Create(current, kerningMode); ushort index = i; ushort count = 1; while (i < collection.Count - 1) { // We want to assign the same feature lookups to individual sections of the text rather // than the text as a whole to ensure that different language shapers do not interfere // with each other when the text contains multiple languages. GlyphShapingData nextData = collection.GetGlyphShapingData(i + 1); ScriptClass next = CodePoint.GetScriptClass(nextData.CodePoint); if (next is not ScriptClass.Common and not ScriptClass.Unknown and not ScriptClass.Inherited && next != current) { break; } i++; count++; } if (shaper.MarkZeroingMode == MarkZeroingMode.PreGPos) { ZeroMarkAdvances(fontMetrics, collection, index, count); } // Assign Substitution features to each glyph. shaper.AssignFeatures(collection, index, count); IEnumerable <Tag> stageFeatures = shaper.GetShapingStageFeatures(); SkippingGlyphIterator iterator = new(fontMetrics, collection, index, default); foreach (Tag stageFeature in stageFeatures) { List <(Tag Feature, ushort Index, LookupTable LookupTable)> lookups = this.GetFeatureLookups(in stageFeature, current); if (lookups.Count == 0) { continue; } // Apply features in order. foreach ((Tag Feature, ushort Index, LookupTable LookupTable)featureLookup in lookups) { Tag feature = featureLookup.Feature; iterator.Reset(index, featureLookup.LookupTable.LookupFlags); while (iterator.Index < index + count) { List <TagEntry> glyphFeatures = collection.GetGlyphShapingData(iterator.Index).Features; if (!HasFeature(glyphFeatures, in feature)) { iterator.Next(); continue; } bool success = featureLookup.LookupTable.TryUpdatePosition(fontMetrics, this, collection, featureLookup.Feature, iterator.Index, count - (iterator.Index - index)); kerned |= success && (feature == KernTag || feature == VKernTag); updated |= success; iterator.Next(); } } } if (shaper.MarkZeroingMode == MarkZeroingMode.PostGpos) { ZeroMarkAdvances(fontMetrics, collection, index, count); } FixCursiveAttachment(collection, index, count); FixMarkAttachment(collection, index, count); if (updated) { UpdatePositions(fontMetrics, collection, index, count); } } return(updated); }
public void ApplySubstitution(FontMetrics fontMetrics, GlyphSubstitutionCollection collection, KerningMode kerningMode) { for (ushort i = 0; i < collection.Count; i++) { // Choose a shaper based on the script. // This determines which features to apply to which glyphs. ScriptClass current = CodePoint.GetScriptClass(collection.GetGlyphShapingData(i).CodePoint); BaseShaper shaper = ShaperFactory.Create(current, kerningMode); ushort index = i; ushort count = 1; while (i < collection.Count - 1) { // We want to assign the same feature lookups to individual sections of the text rather // than the text as a whole to ensure that different language shapers do not interfere // with each other when the text contains multiple languages. GlyphShapingData nextData = collection.GetGlyphShapingData(i + 1); ScriptClass next = CodePoint.GetScriptClass(nextData.CodePoint); if (next is not ScriptClass.Common and not ScriptClass.Unknown and not ScriptClass.Inherited && next != current) { break; } i++; count++; } // Assign Substitution features to each glyph. shaper.AssignFeatures(collection, index, count); IEnumerable <Tag> stageFeatures = shaper.GetShapingStageFeatures(); int currentCount = collection.Count; SkippingGlyphIterator iterator = new(fontMetrics, collection, index, default); foreach (Tag stageFeature in stageFeatures) { List <(Tag Feature, ushort Index, LookupTable LookupTable)> lookups = this.GetFeatureLookups(in stageFeature, current); if (lookups.Count == 0) { continue; } // Apply features in order. foreach ((Tag Feature, ushort Index, LookupTable LookupTable)featureLookup in lookups) { Tag feature = featureLookup.Feature; iterator.Reset(index, featureLookup.LookupTable.LookupFlags); while (iterator.Index < index + count) { List <TagEntry> glyphFeatures = collection.GetGlyphShapingData(iterator.Index).Features; if (!HasFeature(glyphFeatures, in feature)) { iterator.Next(); continue; } featureLookup.LookupTable.TrySubstitution(fontMetrics, this, collection, featureLookup.Feature, iterator.Index, count - (iterator.Index - index)); iterator.Next(); // Account for substitutions changing the length of the collection. if (collection.Count != currentCount) { count = (ushort)(count - (currentCount - collection.Count)); currentCount = collection.Count; } } } } } }