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