Browse Source

Fix TextLineIImpl.GetTextBounds for clustered trailing zero width characters (#19208) (#19251)

* Make sure we only apply the cluster offset if we are inside the current cluster

* Better naming

* Guard coveredLength against invalid values

---------

Co-authored-by: Max Katz <maxkatz6@outlook.com>
release/11.3.3
Benedikt Stebner 7 months ago
committed by GitHub
parent
commit
4f3af7e0fb
No known key found for this signature in database GPG Key ID: B5690EEEBB952194
  1. 11
      src/Avalonia.Base/Media/TextFormatting/TextLineImpl.cs
  2. 2
      tests/Avalonia.Skia.UnitTests/Media/TextFormatting/TextFormatterTests.cs
  3. 36
      tests/Avalonia.Skia.UnitTests/Media/TextFormatting/TextLineTests.cs

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

@ -706,6 +706,11 @@ namespace Avalonia.Media.TextFormatting
lastBounds = currentBounds;
if(coveredLength <= 0)
{
throw new InvalidOperationException("Covered length must be greater than zero.");
}
remainingLength -= coveredLength;
}
@ -1090,7 +1095,8 @@ namespace Avalonia.Media.TextFormatting
var endHit = currentRun.GlyphRun.GetCharacterHitFromDistance(endOffset, out _);
//Adjust characterLength by the cluster offset to only cover the remaining length of the cluster.
var characterLength = Math.Abs(startHit.FirstCharacterIndex + startHit.TrailingLength - endHit.FirstCharacterIndex - endHit.TrailingLength) - clusterOffset;
var characterLength = Math.Max(0, Math.Abs(startHit.FirstCharacterIndex + startHit.TrailingLength -
endHit.FirstCharacterIndex - endHit.TrailingLength) - clusterOffset);
if (characterLength == 0 && currentRun.Text.Length > 0 && startIndex < currentRun.Text.Length)
{
@ -1172,7 +1178,8 @@ namespace Avalonia.Media.TextFormatting
startIndex -= clusterOffset;
}
var characterLength = Math.Abs(startHit.FirstCharacterIndex + startHit.TrailingLength - endHit.FirstCharacterIndex - endHit.TrailingLength) - clusterOffset;
var characterLength = Math.Max(0, Math.Abs(startHit.FirstCharacterIndex + startHit.TrailingLength -
endHit.FirstCharacterIndex - endHit.TrailingLength) - clusterOffset);
if (characterLength == 0 && currentRun.Text.Length > 0 && startIndex < currentRun.Text.Length)
{

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

@ -1158,7 +1158,7 @@ namespace Avalonia.Skia.UnitTests.Media.TextFormatting
}
}
private class ListTextSource : ITextSource
internal class ListTextSource : ITextSource
{
private readonly List<TextRun> _runs;

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

@ -1715,6 +1715,42 @@ namespace Avalonia.Skia.UnitTests.Media.TextFormatting
}
}
[Fact]
public void Should_GetTextBounds_For_Clustered_Zero_Width_Characters()
{
const string text = "\r\n";
using (Start())
{
var defaultProperties = new GenericTextRunProperties(Typeface.Default);
var textSource = new TextFormatterTests.ListTextSource(new TextHidden(1) ,new TextCharacters(text, defaultProperties));
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);
var textBounds = textLine.GetTextBounds(2, 1);
Assert.NotEmpty(textBounds);
var firstBounds = textBounds[0];
Assert.NotEmpty(firstBounds.TextRunBounds);
var firstRunBounds = firstBounds.TextRunBounds[0];
Assert.Equal(2, firstRunBounds.TextSourceCharacterIndex);
Assert.Equal(1, firstRunBounds.Length);
}
}
private class FixedRunsTextSource : ITextSource
{
private readonly IReadOnlyList<TextRun> _textRuns;

Loading…
Cancel
Save