Skip to content

Commit

Permalink
Ignore null terminator and replace them with a zero space before shaping
Browse files Browse the repository at this point in the history
  • Loading branch information
Gillibald committed Sep 25, 2024
1 parent d7040e2 commit b859d24
Show file tree
Hide file tree
Showing 3 changed files with 35 additions and 13 deletions.
11 changes: 11 additions & 0 deletions src/Avalonia.Base/Media/TextFormatting/TextCharacters.cs
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,8 @@ namespace Avalonia.Media.TextFormatting
/// </summary>
public class TextCharacters : TextRun
{
private static char[] ZeroWidthSpace = ['\u200b'];

/// <summary>
/// Constructs a run for text content from a string.
/// </summary>
Expand Down Expand Up @@ -82,6 +84,15 @@ private static UnshapedTextRun CreateShapeableRun(ReadOnlyMemory<char> text,
var previousGlyphTypeface = previousProperties?.CachedGlyphTypeface;
var textSpan = text.Span;

//Read first codepoint
var firstCodepoint = Codepoint.ReadAt(textSpan, 0, out _);

//Detect null terminator
if (firstCodepoint.Value == 0)
{
return new UnshapedTextRun(ZeroWidthSpace.AsMemory(), defaultProperties, biDiLevel);
}

if (TryGetShapeableLength(textSpan, defaultGlyphTypeface, null, out var count))
{
return new UnshapedTextRun(text.Slice(0, count), defaultProperties.WithTypeface(defaultTypeface),
Expand Down
14 changes: 1 addition & 13 deletions src/Skia/Avalonia.Skia/TextShaperImpl.cs
Original file line number Diff line number Diff line change
Expand Up @@ -14,8 +14,6 @@ namespace Avalonia.Skia
{
internal class TextShaperImpl : ITextShaperImpl
{
private const uint ZeroWidthSpace = '\u200b';

private static readonly ConcurrentDictionary<int, Language> s_cachedLanguage = new();

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

var glyphIndex = (ushort)sourceInfo.Codepoint;

var glyphCluster = (int)(sourceInfo.Cluster);

if (glyphIndex == 0)
{
var codepoint = Codepoint.ReadAt(textSpan, glyphCluster, out _);

if (codepoint.GeneralCategory == GeneralCategory.Control)
{
glyphIndex = options.Typeface.GetGlyph(ZeroWidthSpace);
}
}
var glyphCluster = (int)sourceInfo.Cluster;

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

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1374,6 +1374,29 @@ public void Should_GetPreviousCharacterHit_Non_Trailing()
}
}

[Fact]
public void Should_Ignore_Null_Terminator()
{
var text = "\x0";

using (Start())
{
var defaultProperties = new GenericTextRunProperties(Typeface.Default);
var textSource = new SingleBufferTextSource(text, defaultProperties, true);

var formatter = new TextFormatterImpl();

var textLine =
formatter.FormatLine(textSource, 0, double.PositiveInfinity,
new GenericTextParagraphProperties(FlowDirection.LeftToRight, TextAlignment.Left,
true, true, defaultProperties, TextWrapping.NoWrap, 0, 0, 0));

Assert.NotNull(textLine);

Assert.Equal(0, textLine.Width);
}
}

private class FixedRunsTextSource : ITextSource
{
private readonly IReadOnlyList<TextRun> _textRuns;
Expand Down

0 comments on commit b859d24

Please sign in to comment.