diff --git a/src/Avalonia.Base/Media/TextFormatting/TextCharacters.cs b/src/Avalonia.Base/Media/TextFormatting/TextCharacters.cs index d001f8f370..acfffb68fa 100644 --- a/src/Avalonia.Base/Media/TextFormatting/TextCharacters.cs +++ b/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 _)) { diff --git a/src/Avalonia.Base/Media/TextFormatting/Unicode/Codepoint.cs b/src/Avalonia.Base/Media/TextFormatting/Unicode/Codepoint.cs index fec8dc5657..62cba8f5e7 100644 --- a/src/Avalonia.Base/Media/TextFormatting/Unicode/Codepoint.cs +++ b/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; } diff --git a/tests/Avalonia.RenderTests/Assets/NotoSansMiao-Regular.ttf b/tests/Avalonia.RenderTests/Assets/NotoSansMiao-Regular.ttf new file mode 100644 index 0000000000..a08d202e77 Binary files /dev/null and b/tests/Avalonia.RenderTests/Assets/NotoSansMiao-Regular.ttf differ diff --git a/tests/Avalonia.Skia.UnitTests/Media/TextFormatting/TextFormatterTests.cs b/tests/Avalonia.Skia.UnitTests/Media/TextFormatting/TextFormatterTests.cs index be4c35b353..86def1d447 100644 --- a/tests/Avalonia.Skia.UnitTests/Media/TextFormatting/TextFormatterTests.cs +++ b/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;