Benedikt Stebner 6 days ago
committed by GitHub
parent
commit
79d3400c8a
No known key found for this signature in database GPG Key ID: B5690EEEBB952194
  1. 57
      src/Avalonia.Base/Media/TextFormatting/InterWordJustification.cs
  2. 25
      tests/Avalonia.RenderTests/Controls/TextBlockTests.cs
  3. BIN
      tests/TestFiles/Skia/Controls/TextBlock/Should_Justify_With_Spaces.expected.png

57
src/Avalonia.Base/Media/TextFormatting/InterWordJustification.cs

@ -31,6 +31,8 @@ namespace Avalonia.Media.TextFormatting
var currentPosition = textLine.FirstTextSourceIndex; var currentPosition = textLine.FirstTextSourceIndex;
var whiteSpaceWidth = 0.0;
for (var i = 0; i < lineImpl.TextRuns.Count; ++i) for (var i = 0; i < lineImpl.TextRuns.Count; ++i)
{ {
var textRun = lineImpl.TextRuns[i]; var textRun = lineImpl.TextRuns[i];
@ -41,15 +43,38 @@ namespace Avalonia.Media.TextFormatting
continue; continue;
} }
var lineBreakEnumerator = new LineBreakEnumerator(text.Span); if (textRun is ShapedTextRun shapedText)
while (lineBreakEnumerator.MoveNext(out var currentBreak))
{ {
if (!currentBreak.Required && currentBreak.PositionWrap != textRun.Length) var glyphRun = shapedText.GlyphRun;
var shapedBuffer = shapedText.ShapedBuffer;
var lineBreakEnumerator = new LineBreakEnumerator(text.Span);
while (lineBreakEnumerator.MoveNext(out var currentBreak))
{ {
breakOportunities.Enqueue(currentPosition + currentBreak.PositionMeasure); //Ignore the break at the end
if(currentPosition + currentBreak.PositionWrap == textLine.Length - TextRun.DefaultTextSourceLength)
{
break;
}
if (!currentBreak.Required)
{
breakOportunities.Enqueue(currentPosition + currentBreak.PositionWrap);
var offset = Math.Max(0, currentPosition - glyphRun.Metrics.FirstCluster);
var characterIndex = currentPosition - offset + currentBreak.PositionWrap - 1;
var glyphIndex = glyphRun.FindGlyphIndex(characterIndex);
var glyphInfo = shapedBuffer[glyphIndex];
if (Codepoint.ReadAt(text.Span, currentBreak.PositionWrap - 1, out _).IsWhiteSpace)
{
whiteSpaceWidth += glyphInfo.GlyphAdvance;
}
}
} }
} }
currentPosition += textRun.Length; currentPosition += textRun.Length;
} }
@ -59,7 +84,9 @@ namespace Avalonia.Media.TextFormatting
return; return;
} }
var remainingSpace = Math.Max(0, paragraphWidth - lineImpl.WidthIncludingTrailingWhitespace); //Adjust remaining space by whiteSpace width
var remainingSpace = Math.Max(0, paragraphWidth - lineImpl.Width) + whiteSpaceWidth;
var spacing = remainingSpace / breakOportunities.Count; var spacing = remainingSpace / breakOportunities.Count;
currentPosition = textLine.FirstTextSourceIndex; currentPosition = textLine.FirstTextSourceIndex;
@ -82,17 +109,25 @@ namespace Avalonia.Media.TextFormatting
{ {
var characterIndex = breakOportunities.Dequeue(); var characterIndex = breakOportunities.Dequeue();
if (characterIndex < currentPosition) var offset = Math.Max(0, currentPosition - glyphRun.Metrics.FirstCluster);
if (characterIndex + offset < currentPosition)
{ {
continue; continue;
} }
var offset = Math.Max(0, currentPosition - glyphRun.Metrics.FirstCluster); var glyphIndex = glyphRun.FindGlyphIndex(characterIndex - offset - 1);
var glyphIndex = glyphRun.FindGlyphIndex(characterIndex - offset);
var glyphInfo = shapedBuffer[glyphIndex]; var glyphInfo = shapedBuffer[glyphIndex];
var isWhitespace = Codepoint.ReadAt(text.Span, characterIndex - 1 - currentPosition, out _).IsWhiteSpace;
shapedBuffer[glyphIndex] = new GlyphInfo(glyphInfo.GlyphIndex, shapedBuffer[glyphIndex] = new GlyphInfo(glyphInfo.GlyphIndex,
glyphInfo.GlyphCluster, glyphInfo.GlyphAdvance + spacing); glyphInfo.GlyphCluster, isWhitespace ? spacing : glyphInfo.GlyphAdvance + spacing);
if (glyphIndex == shapedBuffer.Length - 1)
{
break;
}
} }
glyphRun.GlyphInfos = shapedBuffer; glyphRun.GlyphInfos = shapedBuffer;

25
tests/Avalonia.RenderTests/Controls/TextBlockTests.cs

@ -7,6 +7,7 @@ using Avalonia.Controls.Documents;
using Avalonia.Layout; using Avalonia.Layout;
using Avalonia.Media; using Avalonia.Media;
using Xunit; using Xunit;
using static System.Net.Mime.MediaTypeNames;
namespace Avalonia.Skia.RenderTests namespace Avalonia.Skia.RenderTests
{ {
@ -424,5 +425,29 @@ namespace Avalonia.Skia.RenderTests
CompareImages(testName); CompareImages(testName);
} }
[Win32Fact("Has text")]
public async Task Should_Justify_With_Spaces()
{
var target = new StackPanel
{
Width = 300,
Height = 400,
Background = new SolidColorBrush(Colors.White), // Required antialiasing to work for Overhang
};
target.Children.Add(CreateText("今天的晚饭很好"));
target.Children.Add(CreateText("今 天 的 晚 饭 很 好"));
await RenderToFile(target);
CompareImages();
static TextBlock CreateText(string text) => new TextBlock
{
TextAlignment = TextAlignment.Justify,
FontSize = 28,
Text = text
};
}
} }
} }

BIN
tests/TestFiles/Skia/Controls/TextBlock/Should_Justify_With_Spaces.expected.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 10 KiB

Loading…
Cancel
Save