|
@@ -1,4 +1,5 @@
|
|
-using System.Collections.Generic;
|
|
|
|
|
|
+using System;
|
|
|
|
+using System.Collections.Generic;
|
|
using Avalonia.Media.TextFormatting.Unicode;
|
|
using Avalonia.Media.TextFormatting.Unicode;
|
|
using Avalonia.Utilities;
|
|
using Avalonia.Utilities;
|
|
|
|
|
|
@@ -37,17 +38,20 @@ namespace Avalonia.Media.TextFormatting
|
|
/// Gets a list of <see cref="ShapeableTextCharacters"/>.
|
|
/// Gets a list of <see cref="ShapeableTextCharacters"/>.
|
|
/// </summary>
|
|
/// </summary>
|
|
/// <returns>The shapeable text characters.</returns>
|
|
/// <returns>The shapeable text characters.</returns>
|
|
- internal IList<ShapeableTextCharacters> GetShapeableCharacters(ReadOnlySlice<char> runText, sbyte biDiLevel)
|
|
|
|
|
|
+ internal IList<ShapeableTextCharacters> GetShapeableCharacters(ReadOnlySlice<char> runText, sbyte biDiLevel,
|
|
|
|
+ ref TextRunProperties? previousProperties)
|
|
{
|
|
{
|
|
var shapeableCharacters = new List<ShapeableTextCharacters>(2);
|
|
var shapeableCharacters = new List<ShapeableTextCharacters>(2);
|
|
|
|
|
|
while (!runText.IsEmpty)
|
|
while (!runText.IsEmpty)
|
|
{
|
|
{
|
|
- var shapeableRun = CreateShapeableRun(runText, Properties, biDiLevel);
|
|
|
|
|
|
+ var shapeableRun = CreateShapeableRun(runText, Properties, biDiLevel, ref previousProperties);
|
|
|
|
|
|
shapeableCharacters.Add(shapeableRun);
|
|
shapeableCharacters.Add(shapeableRun);
|
|
|
|
|
|
runText = runText.Skip(shapeableRun.Text.Length);
|
|
runText = runText.Skip(shapeableRun.Text.Length);
|
|
|
|
+
|
|
|
|
+ previousProperties = shapeableRun.Properties;
|
|
}
|
|
}
|
|
|
|
|
|
return shapeableCharacters;
|
|
return shapeableCharacters;
|
|
@@ -59,15 +63,29 @@ namespace Avalonia.Media.TextFormatting
|
|
/// <param name="text">The text to create text runs from.</param>
|
|
/// <param name="text">The text to create text runs from.</param>
|
|
/// <param name="defaultProperties">The default text run properties.</param>
|
|
/// <param name="defaultProperties">The default text run properties.</param>
|
|
/// <param name="biDiLevel">The bidi level of the run.</param>
|
|
/// <param name="biDiLevel">The bidi level of the run.</param>
|
|
|
|
+ /// <param name="previousProperties"></param>
|
|
/// <returns>A list of shapeable text runs.</returns>
|
|
/// <returns>A list of shapeable text runs.</returns>
|
|
- private ShapeableTextCharacters CreateShapeableRun(ReadOnlySlice<char> text, TextRunProperties defaultProperties, sbyte biDiLevel)
|
|
|
|
|
|
+ private static ShapeableTextCharacters CreateShapeableRun(ReadOnlySlice<char> text,
|
|
|
|
+ TextRunProperties defaultProperties, sbyte biDiLevel, ref TextRunProperties? previousProperties)
|
|
{
|
|
{
|
|
var defaultTypeface = defaultProperties.Typeface;
|
|
var defaultTypeface = defaultProperties.Typeface;
|
|
|
|
|
|
var currentTypeface = defaultTypeface;
|
|
var currentTypeface = defaultTypeface;
|
|
|
|
|
|
- if (TryGetShapeableLength(text, currentTypeface, defaultTypeface, out var count))
|
|
|
|
|
|
+ if (TryGetShapeableLength(text, currentTypeface, out var count, out var script))
|
|
{
|
|
{
|
|
|
|
+ var previousTypeface = previousProperties?.Typeface;
|
|
|
|
+
|
|
|
|
+ if (script == Script.Common && previousTypeface is not null)
|
|
|
|
+ {
|
|
|
|
+ if(TryGetShapeableLength(text, previousTypeface.Value, out var fallbackCount, out _))
|
|
|
|
+ {
|
|
|
|
+ return new ShapeableTextCharacters(text.Take(fallbackCount),
|
|
|
|
+ new GenericTextRunProperties(previousTypeface.Value, defaultProperties.FontRenderingEmSize,
|
|
|
|
+ defaultProperties.TextDecorations, defaultProperties.ForegroundBrush), biDiLevel);
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+
|
|
return new ShapeableTextCharacters(text.Take(count),
|
|
return new ShapeableTextCharacters(text.Take(count),
|
|
new GenericTextRunProperties(currentTypeface, defaultProperties.FontRenderingEmSize,
|
|
new GenericTextRunProperties(currentTypeface, defaultProperties.FontRenderingEmSize,
|
|
defaultProperties.TextDecorations, defaultProperties.ForegroundBrush), biDiLevel);
|
|
defaultProperties.TextDecorations, defaultProperties.ForegroundBrush), biDiLevel);
|
|
@@ -94,7 +112,7 @@ namespace Avalonia.Media.TextFormatting
|
|
FontManager.Current.TryMatchCharacter(codepoint, defaultTypeface.Style, defaultTypeface.Weight,
|
|
FontManager.Current.TryMatchCharacter(codepoint, defaultTypeface.Style, defaultTypeface.Weight,
|
|
defaultTypeface.FontFamily, defaultProperties.CultureInfo, out currentTypeface);
|
|
defaultTypeface.FontFamily, defaultProperties.CultureInfo, out currentTypeface);
|
|
|
|
|
|
- if (matchFound && TextCharacters.TryGetShapeableLength(text, currentTypeface, defaultTypeface, out count))
|
|
|
|
|
|
+ if (matchFound && TryGetShapeableLength(text, currentTypeface, out count, out _))
|
|
{
|
|
{
|
|
//Fallback found
|
|
//Fallback found
|
|
return new ShapeableTextCharacters(text.Take(count),
|
|
return new ShapeableTextCharacters(text.Take(count),
|
|
@@ -127,30 +145,26 @@ namespace Avalonia.Media.TextFormatting
|
|
}
|
|
}
|
|
|
|
|
|
/// <summary>
|
|
/// <summary>
|
|
- /// Tries to get run properties.
|
|
|
|
|
|
+ /// Tries to get a shapeable length that is supported by the specified typeface.
|
|
/// </summary>
|
|
/// </summary>
|
|
- /// <param name="defaultTypeface"></param>
|
|
|
|
- /// <param name="text"></param>
|
|
|
|
|
|
+ /// <param name="text">The text.</param>
|
|
/// <param name="typeface">The typeface that is used to find matching characters.</param>
|
|
/// <param name="typeface">The typeface that is used to find matching characters.</param>
|
|
- /// <param name="length"></param>
|
|
|
|
|
|
+ /// <param name="length">The shapeable length.</param>
|
|
|
|
+ /// <param name="script"></param>
|
|
/// <returns></returns>
|
|
/// <returns></returns>
|
|
- protected static bool TryGetShapeableLength(ReadOnlySlice<char> text, Typeface typeface, Typeface defaultTypeface,
|
|
|
|
- out int length)
|
|
|
|
|
|
+ protected static bool TryGetShapeableLength(ReadOnlySlice<char> text, Typeface typeface, out int length,
|
|
|
|
+ out Script script)
|
|
{
|
|
{
|
|
|
|
+ length = 0;
|
|
|
|
+ script = Script.Unknown;
|
|
|
|
+
|
|
if (text.Length == 0)
|
|
if (text.Length == 0)
|
|
{
|
|
{
|
|
- length = 0;
|
|
|
|
return false;
|
|
return false;
|
|
}
|
|
}
|
|
|
|
|
|
- var isFallback = typeface != defaultTypeface;
|
|
|
|
-
|
|
|
|
- length = 0;
|
|
|
|
- var script = Script.Unknown;
|
|
|
|
-
|
|
|
|
var font = typeface.GlyphTypeface;
|
|
var font = typeface.GlyphTypeface;
|
|
- var defaultFont = defaultTypeface.GlyphTypeface;
|
|
|
|
-
|
|
|
|
|
|
+
|
|
var enumerator = new GraphemeEnumerator(text);
|
|
var enumerator = new GraphemeEnumerator(text);
|
|
|
|
|
|
while (enumerator.MoveNext())
|
|
while (enumerator.MoveNext())
|
|
@@ -161,7 +175,8 @@ namespace Avalonia.Media.TextFormatting
|
|
|
|
|
|
if (currentScript != script)
|
|
if (currentScript != script)
|
|
{
|
|
{
|
|
- if (script is Script.Unknown || currentScript != Script.Common && (script is Script.Common || script is Script.Inherited))
|
|
|
|
|
|
+ if (script is Script.Unknown || currentScript != Script.Common &&
|
|
|
|
+ (script is Script.Common || script is Script.Inherited))
|
|
{
|
|
{
|
|
script = currentScript;
|
|
script = currentScript;
|
|
}
|
|
}
|
|
@@ -174,23 +189,8 @@ namespace Avalonia.Media.TextFormatting
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
- //Only handle non whitespace here
|
|
|
|
- if(!currentGrapheme.FirstCodepoint.IsWhiteSpace)
|
|
|
|
- {
|
|
|
|
- //Stop at the first glyph that is present in the default typeface.
|
|
|
|
- if (isFallback && defaultFont.TryGetGlyph(currentGrapheme.FirstCodepoint, out _))
|
|
|
|
- {
|
|
|
|
- break;
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
- //Stop at the first missing glyph
|
|
|
|
- if (!font.TryGetGlyph(currentGrapheme.FirstCodepoint, out _))
|
|
|
|
- {
|
|
|
|
- break;
|
|
|
|
- }
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
- if (!currentGrapheme.FirstCodepoint.IsWhiteSpace && !font.TryGetGlyph(currentGrapheme.FirstCodepoint, out _))
|
|
|
|
|
|
+ //Stop at the first missing glyph
|
|
|
|
+ if (!font.TryGetGlyph(currentGrapheme.FirstCodepoint, out _))
|
|
{
|
|
{
|
|
break;
|
|
break;
|
|
}
|
|
}
|