public FTVector GetKerning(uint leftGlyph, uint rightGlyph, KerningMode mode) { if (disposed) throw new ObjectDisposedException("face", "Cannot access a disposed object."); FTVector kern; Error err = FT.FT_Get_Kerning(Reference, leftGlyph, rightGlyph, mode, out kern); if (err != Error.Ok) throw new FreeTypeException(err); return kern; }
/// <summary> /// Applies any available substitutions to the collection of glyphs. /// </summary> /// <param name="collection">The glyph substitution collection.</param> /// <param name="kerningMode">The kerning mode.</param> internal abstract void ApplySubstitution(GlyphSubstitutionCollection collection, KerningMode kerningMode);
/// <summary> /// Applies any available positioning updates to the collection of glyphs. /// </summary> /// <param name="collection">The glyph positioning collection.</param> /// <param name="kerningMode">The kerning mode.</param> internal abstract void UpdatePositions(GlyphPositioningCollection collection, KerningMode kerningMode);
internal static extern Error FT_Get_Kerning(IntPtr face, uint left_glyph, uint right_glyph, KerningMode kern_mode, out FTVector akerning);
public static extern Error FT_Get_Kerning(Face *face, uint left_glyph, uint right_glyph, KerningMode kern_mode, out FTVector26Dot6 akerning);
/// <inheritdoc/> internal override void ApplySubstitution(GlyphSubstitutionCollection collection, KerningMode kerningMode) => this.metrics.Value.ApplySubstitution(collection, kerningMode);
/// <inheritdoc/> internal override void UpdatePositions(GlyphPositioningCollection collection, KerningMode kerningMode) => this.metrics.Value.UpdatePositions(collection, kerningMode);
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; } } } } } }
public ArabicShaper(KerningMode kerningMode) : base(MarkZeroingMode.PostGpos, kerningMode) { }
/// <summary> /// Creates a Shaper based on the given script language. /// </summary> /// <param name="script">The script language.</param> /// <param name="kerningMode">The kerning mode.</param> /// <returns>A shaper for the given script.</returns> public static BaseShaper Create(ScriptClass script, KerningMode kerningMode) => script switch {