Browse Source

[Text] Fix infinite loop hang on selection of bidi text (#16306)

* [Text] fix infinite loop hang on selection of bidi text

* Deal with zero width whitespaces during run bounds calculation

* Do the same thing for RTL

---------

Co-authored-by: Benedikt Stebner <Gillibald@users.noreply.github.com>
pull/16470/head
Sergey Mikolaytis 2 years ago
committed by GitHub
parent
commit
20b5c2b880
No known key found for this signature in database GPG Key ID: B5690EEEBB952194
  1. 69
      src/Avalonia.Base/Media/TextFormatting/TextLineImpl.cs
  2. 34
      tests/Avalonia.Skia.UnitTests/Media/TextFormatting/TextLineTests.cs

69
src/Avalonia.Base/Media/TextFormatting/TextLineImpl.cs

@ -732,23 +732,26 @@ namespace Avalonia.Media.TextFormatting
}
}
if (coveredLength > 0)
if (coveredLength == 0)
{
if (lastBounds != null && TryMergeWithLastBounds(currentBounds, lastBounds))
{
currentBounds = lastBounds;
result[result.Count - 1] = currentBounds;
}
else
{
result.Add(currentBounds);
}
//This should never happen
break;
}
lastBounds = currentBounds;
if (lastBounds != null && TryMergeWithLastBounds(currentBounds, lastBounds))
{
currentBounds = lastBounds;
remainingLength -= coveredLength;
result[result.Count - 1] = currentBounds;
}
else
{
result.Add(currentBounds);
}
lastBounds = currentBounds;
remainingLength -= coveredLength;
}
result.Sort(TextBoundsComparer);
@ -1018,10 +1021,23 @@ namespace Avalonia.Media.TextFormatting
var characterLength = Math.Abs(startHit.FirstCharacterIndex + startHit.TrailingLength - endHit.FirstCharacterIndex - endHit.TrailingLength);
//Make sure we properly deal with zero width space runs
if (characterLength == 0 && currentRun.Length > 0 && currentRun.GlyphRun.Metrics.WidthIncludingTrailingWhitespace == 0)
if (characterLength == 0 && currentRun.Text.Length > 0 && startIndex < currentRun.Text.Length)
{
characterLength = currentRun.Length;
//Make sure we are properly dealing with zero width space runs
var codepointEnumerator = new CodepointEnumerator(currentRun.Text.Span.Slice(startIndex));
while (remainingLength > 0 && codepointEnumerator.MoveNext(out var codepoint))
{
if (codepoint.IsWhiteSpace)
{
characterLength++;
remainingLength--;
}
else
{
break;
}
}
}
if (endX < startX)
@ -1074,13 +1090,26 @@ namespace Avalonia.Media.TextFormatting
var characterLength = Math.Abs(startHit.FirstCharacterIndex + startHit.TrailingLength - endHit.FirstCharacterIndex - endHit.TrailingLength);
//Make sure we properly deal with zero width space runs
if (characterLength == 0 && currentRun.Length > 0 && currentRun.GlyphRun.Metrics.WidthIncludingTrailingWhitespace == 0)
if (characterLength == 0 && currentRun.Text.Length > 0 && startIndex < currentRun.Text.Length)
{
characterLength = currentRun.Length;
//Make sure we are properly dealing with zero width space runs
var codepointEnumerator = new CodepointEnumerator(currentRun.Text.Span.Slice(startIndex));
while (remainingLength > 0 && codepointEnumerator.MoveNext(out var codepoint))
{
if (codepoint.IsWhiteSpace)
{
characterLength++;
remainingLength--;
}
else
{
break;
}
}
}
if(startHit.FirstCharacterIndex > endHit.FirstCharacterIndex)
if (startHit.FirstCharacterIndex > endHit.FirstCharacterIndex)
{
startHit = endHit;
}

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

@ -1236,6 +1236,40 @@ namespace Avalonia.Skia.UnitTests.Media.TextFormatting
Assert.True(firstBounds.TextRunBounds.Count > 0);
}
}
[Fact]
public void Should_GetTextBounds_NotInfiniteLoop()
{
using (Start())
{
var defaultProperties = new GenericTextRunProperties(Typeface.Default);
var shaperOption = new TextShaperOptions(Typeface.Default.GlyphTypeface, 10, 0, CultureInfo.CurrentCulture);
var shaperOption2 = new TextShaperOptions(Typeface.Default.GlyphTypeface, 11, 0, CultureInfo.CurrentCulture);
var textRuns = new List<TextRun>
{
new ShapedTextRun(TextShaper.Current.ShapeText("قرأ ", shaperOption), defaultProperties),
new ShapedTextRun(TextShaper.Current.ShapeText("Wikipedia\u2122", shaperOption), defaultProperties),
new ShapedTextRun(TextShaper.Current.ShapeText("\u200e ", shaperOption2), defaultProperties),
new ShapedTextRun(TextShaper.Current.ShapeText("طوال اليوم", shaperOption), defaultProperties),
new ShapedTextRun(TextShaper.Current.ShapeText(".", shaperOption), defaultProperties)
};
var textSource = new FixedRunsTextSource(textRuns);
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);
textLine.GetTextBounds(4, 11);
}
}
[Fact]
public void Should_GetTextBounds_Bidi()

Loading…
Cancel
Save