Browse Source

Do not treat combining marks as whitespace (#19498)

* Do not treat spacing combining marks as whitespace

* Do not treat combining marks as whitespace
Try to find a matching typeface on a grapheme boundary

* Add embedded font for testing

* Remove using

* Fix naming and remove redundant code

* Apply default until we reach the next base character
release/11.3.5
Benedikt Stebner 6 months ago
parent
commit
f9373ac34f
  1. 17
      src/Avalonia.Base/Media/TextFormatting/TextCharacters.cs
  2. 4
      src/Avalonia.Base/Media/TextFormatting/Unicode/Codepoint.cs
  3. BIN
      tests/Avalonia.RenderTests/Assets/NotoSansMiao-Regular.ttf
  4. 26
      tests/Avalonia.Skia.UnitTests/Media/TextFormatting/TextFormatterTests.cs

17
src/Avalonia.Base/Media/TextFormatting/TextCharacters.cs

@ -115,21 +115,13 @@ namespace Avalonia.Media.TextFormatting
var codepoint = Codepoint.ReplacementCodepoint;
var codepointEnumerator = new CodepointEnumerator(text.Slice(count).Span);
var graphemeEnumerator = new GraphemeEnumerator(text.Slice(count).Span);
while (codepointEnumerator.MoveNext(out var cp))
if (graphemeEnumerator.MoveNext(out var grapheme))
{
if (cp.IsWhiteSpace)
{
continue;
}
codepoint = cp;
break;
codepoint = grapheme.FirstCodepoint;
}
//ToDo: Fix FontFamily fallback
var matchFound =
fontManager.TryMatchCharacter(codepoint, defaultTypeface.Style, defaultTypeface.Weight,
defaultTypeface.Stretch, defaultTypeface.FontFamily, defaultProperties.CultureInfo,
@ -151,7 +143,8 @@ namespace Avalonia.Media.TextFormatting
// no fallback found
var enumerator = new GraphemeEnumerator(textSpan);
while (enumerator.MoveNext(out var grapheme))
//Move forward until we reach the next base character
while (enumerator.MoveNext(out grapheme))
{
if (!grapheme.FirstCodepoint.IsWhiteSpace && defaultGlyphTypeface.TryGetGlyph(grapheme.FirstCodepoint, out _))
{

4
src/Avalonia.Base/Media/TextFormatting/Unicode/Codepoint.cs

@ -128,10 +128,8 @@ namespace Avalonia.Media.TextFormatting.Unicode
{
const ulong whiteSpaceMask =
(1UL << (int)GeneralCategory.Control) |
(1UL << (int)GeneralCategory.NonspacingMark) |
(1UL << (int)GeneralCategory.Format) |
(1UL << (int)GeneralCategory.SpaceSeparator) |
(1UL << (int)GeneralCategory.SpacingMark);
(1UL << (int)GeneralCategory.SpaceSeparator);
return ((1UL << (int)GeneralCategory) & whiteSpaceMask) != 0UL;
}

BIN
tests/Avalonia.RenderTests/Assets/NotoSansMiao-Regular.ttf

Binary file not shown.

26
tests/Avalonia.Skia.UnitTests/Media/TextFormatting/TextFormatterTests.cs

@ -1088,6 +1088,32 @@ namespace Avalonia.Skia.UnitTests.Media.TextFormatting
}
}
[Fact]
public void Should_MatchCharacter_For_Spacing_CombiningMark()
{
using (Start())
{
var text = "𖾇";
var typeface = new Typeface(new FontFamily(new Uri("resm:Avalonia.Skia.UnitTests.Fonts?assembly=Avalonia.Skia.UnitTests"), "Noto Mono"));
var defaultRunProperties = new GenericTextRunProperties(typeface);
var paragraphProperties = new GenericTextParagraphProperties(defaultRunProperties, textWrapping: TextWrapping.Wrap);
var textLine = TextFormatter.Current.FormatLine(new SimpleTextSource(text, defaultRunProperties), 0, 120, paragraphProperties);
Assert.NotNull(textLine);
var textRuns = textLine.TextRuns;
Assert.NotEmpty(textRuns);
var firstRun = textRuns[0];
Assert.NotNull(firstRun.Properties);
Assert.Equal("Noto Sans Miao", firstRun.Properties.Typeface.GlyphTypeface.FamilyName);
}
}
protected readonly record struct SimpleTextSource : ITextSource
{
private readonly string _text;

Loading…
Cancel
Save