Skip to content

Commit b859d24

Browse files
committed
Ignore null terminator and replace them with a zero space before shaping
1 parent d7040e2 commit b859d24

File tree

3 files changed

+35
-13
lines changed

3 files changed

+35
-13
lines changed

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

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,8 @@ namespace Avalonia.Media.TextFormatting
99
/// </summary>
1010
public class TextCharacters : TextRun
1111
{
12+
private static char[] ZeroWidthSpace = ['\u200b'];
13+
1214
/// <summary>
1315
/// Constructs a run for text content from a string.
1416
/// </summary>
@@ -82,6 +84,15 @@ private static UnshapedTextRun CreateShapeableRun(ReadOnlyMemory<char> text,
8284
var previousGlyphTypeface = previousProperties?.CachedGlyphTypeface;
8385
var textSpan = text.Span;
8486

87+
//Read first codepoint
88+
var firstCodepoint = Codepoint.ReadAt(textSpan, 0, out _);
89+
90+
//Detect null terminator
91+
if (firstCodepoint.Value == 0)
92+
{
93+
return new UnshapedTextRun(ZeroWidthSpace.AsMemory(), defaultProperties, biDiLevel);
94+
}
95+
8596
if (TryGetShapeableLength(textSpan, defaultGlyphTypeface, null, out var count))
8697
{
8798
return new UnshapedTextRun(text.Slice(0, count), defaultProperties.WithTypeface(defaultTypeface),

src/Skia/Avalonia.Skia/TextShaperImpl.cs

Lines changed: 1 addition & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -14,8 +14,6 @@ namespace Avalonia.Skia
1414
{
1515
internal class TextShaperImpl : ITextShaperImpl
1616
{
17-
private const uint ZeroWidthSpace = '\u200b';
18-
1917
private static readonly ConcurrentDictionary<int, Language> s_cachedLanguage = new();
2018

2119
public ShapedBuffer ShapeText(ReadOnlyMemory<char> text, TextShaperOptions options)
@@ -69,17 +67,7 @@ public ShapedBuffer ShapeText(ReadOnlyMemory<char> text, TextShaperOptions optio
6967

7068
var glyphIndex = (ushort)sourceInfo.Codepoint;
7169

72-
var glyphCluster = (int)(sourceInfo.Cluster);
73-
74-
if (glyphIndex == 0)
75-
{
76-
var codepoint = Codepoint.ReadAt(textSpan, glyphCluster, out _);
77-
78-
if (codepoint.GeneralCategory == GeneralCategory.Control)
79-
{
80-
glyphIndex = options.Typeface.GetGlyph(ZeroWidthSpace);
81-
}
82-
}
70+
var glyphCluster = (int)sourceInfo.Cluster;
8371

8472
var glyphAdvance = GetGlyphAdvance(glyphPositions, i, textScale) + options.LetterSpacing;
8573

tests/Avalonia.Skia.UnitTests/Media/TextFormatting/TextLineTests.cs

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1374,6 +1374,29 @@ public void Should_GetPreviousCharacterHit_Non_Trailing()
13741374
}
13751375
}
13761376

1377+
[Fact]
1378+
public void Should_Ignore_Null_Terminator()
1379+
{
1380+
var text = "\x0";
1381+
1382+
using (Start())
1383+
{
1384+
var defaultProperties = new GenericTextRunProperties(Typeface.Default);
1385+
var textSource = new SingleBufferTextSource(text, defaultProperties, true);
1386+
1387+
var formatter = new TextFormatterImpl();
1388+
1389+
var textLine =
1390+
formatter.FormatLine(textSource, 0, double.PositiveInfinity,
1391+
new GenericTextParagraphProperties(FlowDirection.LeftToRight, TextAlignment.Left,
1392+
true, true, defaultProperties, TextWrapping.NoWrap, 0, 0, 0));
1393+
1394+
Assert.NotNull(textLine);
1395+
1396+
Assert.Equal(0, textLine.Width);
1397+
}
1398+
}
1399+
13771400
private class FixedRunsTextSource : ITextSource
13781401
{
13791402
private readonly IReadOnlyList<TextRun> _textRuns;

0 commit comments

Comments
 (0)