using System.Collections.Generic;
using Avalonia.Media.TextFormatting.Unicode;
using Avalonia.Utilities;
namespace Avalonia.Media.TextFormatting
{
///
/// A text run that holds text characters.
///
public class TextCharacters : TextRun
{
public TextCharacters(ReadOnlySlice text, TextRunProperties properties)
{
TextSourceLength = text.Length;
Text = text;
Properties = properties;
}
///
public override int TextSourceLength { get; }
///
public override ReadOnlySlice Text { get; }
///
public override TextRunProperties Properties { get; }
///
/// Gets a list of .
///
/// The shapeable text characters.
internal IList GetShapeableCharacters()
{
var shapeableCharacters = new List(2);
var runText = Text;
while (!runText.IsEmpty)
{
var shapeableRun = CreateShapeableRun(runText, Properties);
shapeableCharacters.Add(shapeableRun);
runText = runText.Skip(shapeableRun.Text.Length);
}
return shapeableCharacters;
}
///
/// Creates a shapeable text run with unique properties.
///
/// The text to create text runs from.
/// The default text run properties.
/// A list of shapeable text runs.
private ShapeableTextCharacters CreateShapeableRun(ReadOnlySlice text, TextRunProperties defaultProperties)
{
var defaultTypeface = defaultProperties.Typeface;
var currentTypeface = defaultTypeface;
if (TryGetRunProperties(text, currentTypeface, defaultTypeface, out var count))
{
return new ShapeableTextCharacters(text.Take(count),
new GenericTextRunProperties(currentTypeface, defaultProperties.FontRenderingEmSize,
defaultProperties.TextDecorations, defaultProperties.ForegroundBrush));
}
var codepoint = Codepoint.ReadAt(text, count, out _);
//ToDo: Fix FontFamily fallback
currentTypeface =
FontManager.Current.MatchCharacter(codepoint, defaultTypeface.Weight, defaultTypeface.Style, defaultTypeface.FontFamily);
if (currentTypeface != null && TryGetRunProperties(text, currentTypeface, defaultTypeface, out count))
{
//Fallback found
return new ShapeableTextCharacters(text.Take(count),
new GenericTextRunProperties(currentTypeface, defaultProperties.FontRenderingEmSize,
defaultProperties.TextDecorations, defaultProperties.ForegroundBrush));
}
// no fallback found
currentTypeface = defaultTypeface;
var glyphTypeface = currentTypeface.GlyphTypeface;
var enumerator = new GraphemeEnumerator(text);
while (enumerator.MoveNext())
{
var grapheme = enumerator.Current;
if (!grapheme.FirstCodepoint.IsWhiteSpace && glyphTypeface.TryGetGlyph(grapheme.FirstCodepoint, out _))
{
break;
}
count += grapheme.Text.Length;
}
return new ShapeableTextCharacters(text.Take(count),
new GenericTextRunProperties(currentTypeface, defaultProperties.FontRenderingEmSize,
defaultProperties.TextDecorations, defaultProperties.ForegroundBrush));
}
///
/// Tries to get run properties.
///
///
///
/// The typeface that is used to find matching characters.
///
///
protected bool TryGetRunProperties(ReadOnlySlice text, Typeface typeface, Typeface defaultTypeface,
out int count)
{
if (text.Length == 0)
{
count = 0;
return false;
}
var isFallback = typeface != defaultTypeface;
count = 0;
var script = Script.Common;
//var direction = BiDiClass.LeftToRight;
var font = typeface.GlyphTypeface;
var defaultFont = defaultTypeface.GlyphTypeface;
var enumerator = new GraphemeEnumerator(text);
while (enumerator.MoveNext())
{
var grapheme = enumerator.Current;
var currentScript = grapheme.FirstCodepoint.Script;
//var currentDirection = grapheme.FirstCodepoint.BiDiClass;
//// ToDo: Implement BiDi algorithm
//if (currentScript.HorizontalDirection != direction)
//{
// if (!UnicodeUtility.IsWhiteSpace(grapheme.FirstCodepoint))
// {
// break;
// }
//}
if (currentScript != script)
{
if (currentScript != Script.Inherited && currentScript != Script.Common)
{
if (script == Script.Inherited || script == Script.Common)
{
script = currentScript;
}
else
{
break;
}
}
}
if (isFallback)
{
if (defaultFont.TryGetGlyph(grapheme.FirstCodepoint, out _))
{
break;
}
}
if (!font.TryGetGlyph(grapheme.FirstCodepoint, out _))
{
if (!grapheme.FirstCodepoint.IsWhiteSpace)
{
break;
}
}
count += grapheme.Text.Length;
}
return count > 0;
}
}
}