// Copyright (c) Six Labors. // Licensed under the Six Labors Split License. using System; using System.Collections.Generic; using System.Diagnostics; using SixLabors.Fonts.Tables.AdvancedTypographic; using SixLabors.Fonts.Unicode; using static SixLabors.Fonts.Unicode.Resources.IndicShapingData; namespace SixLabors.Fonts { /// /// Contains supplementary data that allows the shaping of glyphs. /// [DebuggerDisplay("{DebuggerDisplay,nq}")] internal class GlyphShapingData { private ushort glyphId; /// /// Initializes a new instance of the class. /// /// The text run. public GlyphShapingData(TextRun textRun) => this.TextRun = textRun; /// /// Initializes a new instance of the class. /// /// The data to copy properties from. /// Whether to clear features. public GlyphShapingData(GlyphShapingData data, bool clearFeatures = false) { this.GlyphId = data.GlyphId; this.CodePoint = data.CodePoint; this.CodePointCount = data.CodePointCount; this.Direction = data.Direction; this.TextRun = data.TextRun; this.LigatureId = data.LigatureId; this.IsLigated = data.IsLigated; this.LigatureComponent = data.LigatureComponent; this.MarkAttachment = data.MarkAttachment; this.CursiveAttachment = data.CursiveAttachment; this.IsSubstituted = data.IsSubstituted; this.IsDecomposed = data.IsDecomposed; this.IsPlaceholder = data.IsPlaceholder; this.BidiRun = data.BidiRun; this.IsPositioned = data.IsPositioned; this.IsKerned = data.IsKerned; if (data.UniversalShapingEngineInfo != null) { this.UniversalShapingEngineInfo = new( data.UniversalShapingEngineInfo.Category, data.UniversalShapingEngineInfo.SyllableType, data.UniversalShapingEngineInfo.Syllable); } if (data.IndicShapingEngineInfo != null) { this.IndicShapingEngineInfo = new( data.IndicShapingEngineInfo.Category, data.IndicShapingEngineInfo.Position, data.IndicShapingEngineInfo.SyllableType, data.IndicShapingEngineInfo.Syllable); } if (!clearFeatures) { this.Features.AddRange(data.Features); foreach (Tag tag in data.EnabledFeatureTags) { this.EnabledFeatureTags.Add(tag); } } foreach (Tag feature in data.AppliedFeatures) { this.AppliedFeatures.Add(feature); } this.Bounds = data.Bounds; this.CachedShapingClass = data.CachedShapingClass; this.ShapingClassCacheKey = data.ShapingClassCacheKey; } /// /// Gets or sets the glyph id. Setting this value invalidates the cached shaping class. /// public ushort GlyphId { get => this.glyphId; set { if (this.glyphId != value) { this.glyphId = value; this.ShapingClassCacheKey = -1; } } } /// /// Gets or sets the cached glyph shaping class, avoiding repeated GDEF lookups. /// internal GlyphShapingClass CachedShapingClass { get; set; } /// /// Gets or sets the cache key for . /// A value of -1 indicates the cache is invalid. Valid entries store the glyph id. /// internal int ShapingClassCacheKey { get; set; } = -1; /// /// Gets or sets the leading codepoint. /// public CodePoint CodePoint { get; set; } /// /// Gets or sets the codepoint count represented by this glyph. /// public int CodePointCount { get; set; } = 1; /// /// Gets or sets the text direction. /// public TextDirection Direction { get; set; } /// /// Gets or sets the text run this glyph belongs to. /// public TextRun TextRun { get; set; } /// /// Gets or sets the id of any ligature this glyph is a member of. /// public int LigatureId { get; set; } /// /// Gets or sets a value indicating whether the glyph is ligated. /// public bool IsLigated { get; set; } /// /// Gets or sets the ligature component index of the glyph. /// public int LigatureComponent { get; set; } = -1; /// /// Gets or sets the index of any mark attachment. /// public int MarkAttachment { get; set; } = -1; /// /// Gets or sets the index of any cursive attachment. /// public int CursiveAttachment { get; set; } = -1; /// /// Gets or sets the collection of features. /// public List Features { get; set; } = []; /// /// Gets the set of feature tags that are currently enabled, maintained /// in sync with for O(1) lookup. /// internal HashSet EnabledFeatureTags { get; } = []; /// /// Gets or sets the collection of applied features. /// public HashSet AppliedFeatures { get; set; } = []; /// /// Gets or sets the shaping bounds. /// public GlyphShapingBounds Bounds { get; set; } = new(0, 0, 0, 0); /// /// Gets or sets a value indicating whether this glyph is the result of a substitution. /// public bool IsSubstituted { get; set; } /// /// Gets or sets a value indicating whether this glyph is the result of a decomposition substitution /// public bool IsDecomposed { get; set; } /// /// Gets or sets a value indicating whether this glyph represents an inline placeholder. /// public bool IsPlaceholder { get; set; } /// /// Gets or sets the bidi run assigned to an inline placeholder. /// public BidiRun BidiRun { get; set; } /// /// Gets or sets a value indicating whether this glyph has been positioned. /// public bool IsPositioned { get; set; } /// /// Gets or sets a value indicating whether this glyph has been kerned. /// public bool IsKerned { get; set; } /// /// Gets or sets the universal shaping information. /// public UniversalShapingEngineInfo? UniversalShapingEngineInfo { get; set; } /// /// Gets or sets the Indic shaping information. /// public IndicShapingEngineInfo? IndicShapingEngineInfo { get; set; } private string DebuggerDisplay => FormattableString .Invariant($" {this.GlyphId} : {this.CodePoint.ToDebuggerDisplay()} : {CodePoint.GetScriptClass(this.CodePoint)} : {this.Direction} : {this.TextRun.TextAttributes} : {this.LigatureId} : {this.LigatureComponent} : {this.IsDecomposed}"); internal string ToDebuggerDisplay() => this.DebuggerDisplay; } /// /// Represents information required for universal shaping. /// internal class UniversalShapingEngineInfo { public UniversalShapingEngineInfo(string category, string syllableType, int syllable) { this.Category = category; this.SyllableType = syllableType; this.Syllable = syllable; } public string Category { get; set; } public string SyllableType { get; set; } public int Syllable { get; set; } } internal class IndicShapingEngineInfo { public IndicShapingEngineInfo( Categories category, Positions position, string syllableType, int syllable) { this.Category = category; this.Position = position; this.SyllableType = syllableType; this.Syllable = syllable; } public Categories Category { get; set; } public MyanmarCategories MyanmarCategory => (MyanmarCategories)this.Category; public Positions Position { get; set; } public string SyllableType { get; set; } public int Syllable { get; set; } } }