Browse Source

Fix TextLayout.Width calculation (#18305) (#18310)

* Fix TextLayout.Width calculation (#18305)

* Use WinSymbols3.ttf
release/11.2.5
Alexandre Mutel 2 years ago
committed by Julien Lebosquain
parent
commit
e5aff32225
  1. 49
      src/Avalonia.Base/Media/TextFormatting/TextLayout.cs
  2. 2
      tests/Avalonia.Skia.UnitTests/Avalonia.Skia.UnitTests.csproj
  3. BIN
      tests/Avalonia.Skia.UnitTests/Fonts/WinSymbols3.ttf
  4. 13
      tests/Avalonia.Skia.UnitTests/Media/TextFormatting/TextLayoutTests.cs

49
src/Avalonia.Base/Media/TextFormatting/TextLayout.cs

@ -679,20 +679,47 @@ namespace Avalonia.Media.TextFormatting
private void UpdateMetrics(TextLineImpl currentLine, ref bool first)
{
_metrics.InkBounds = _metrics.InkBounds.Union(new Rect(new Point(0, _metrics.Bounds.Bottom) + currentLine.InkBounds.Position, currentLine.InkBounds.Size));
_metrics.Bounds = _metrics.Bounds.Union(new Rect(new Point(0, _metrics.Bounds.Bottom) + currentLine.Bounds.Position, currentLine.Bounds.Size));
// 1) Offset each line’s bounding rectangles by the total height so far,
// so we keep an overall bounding box for the entire text block.
var lineTop = _metrics.Height;
_metrics.MinTextWidth = Math.Max(_metrics.MinTextWidth, currentLine.Bounds.Width);
_metrics.MinTextWidth = Math.Max(_metrics.MinTextWidth, currentLine.InkBounds.Width);
// Offset the line's Bounds
var lineBoundsRect = new Rect(
currentLine.Bounds.X,
lineTop + currentLine.Bounds.Y,
currentLine.Bounds.Width,
currentLine.Bounds.Height);
_metrics.Height = _metrics.Bounds.Height;
_metrics.Width = _metrics.InkBounds.Width;
_metrics.WidthIncludingTrailingWhitespace = _metrics.Bounds.Width;
_metrics.Extent = _metrics.InkBounds.Height;
_metrics.OverhangLeading = Math.Max(0, _metrics.Bounds.Left - _metrics.InkBounds.Left);
_metrics.OverhangTrailing = Math.Max(0, _metrics.InkBounds.Right - _metrics.Bounds.Right);
_metrics.OverhangAfter = Math.Max(0, _metrics.InkBounds.Bottom - _metrics.Bounds.Bottom);
_metrics.Bounds = _metrics.Bounds.Union(lineBoundsRect);
// Offset the line's InkBounds
var lineInkRect = new Rect(
currentLine.InkBounds.X,
lineTop + currentLine.InkBounds.Y,
currentLine.InkBounds.Width,
currentLine.InkBounds.Height);
_metrics.InkBounds = _metrics.InkBounds.Union(lineInkRect);
// 2) Accumulate total layout height by adding the line’s Height.
_metrics.Height += currentLine.Height;
// 3) For the layout’s Width and WidthIncludingTrailingWhitespace,
// use the maximum of the line widths rather than the bounding box.
_metrics.Width = Math.Max(_metrics.Width, currentLine.Width);
_metrics.WidthIncludingTrailingWhitespace = Math.Max(_metrics.WidthIncludingTrailingWhitespace, currentLine.WidthIncludingTrailingWhitespace);
// 4) Extent is the max black-pixel extent among lines.
_metrics.Extent = Math.Max(_metrics.Extent, currentLine.Extent);
// 5) We can track min-text-width or overhangs similarly if needed.
_metrics.MinTextWidth = Math.Max(_metrics.MinTextWidth, currentLine.Width);
_metrics.OverhangLeading = Math.Max(_metrics.OverhangLeading, currentLine.OverhangLeading);
_metrics.OverhangTrailing = Math.Max(_metrics.OverhangTrailing, currentLine.OverhangTrailing);
_metrics.OverhangAfter = Math.Max(_metrics.OverhangAfter, currentLine.OverhangAfter);
// 6) Capture the baseline from the first line.
if (first)
{
_metrics.Baseline = currentLine.Baseline;

2
tests/Avalonia.Skia.UnitTests/Avalonia.Skia.UnitTests.csproj

@ -12,8 +12,10 @@
<EmbeddedResource Include="..\Avalonia.RenderTests\*\*.ttf" />
<None Remove="Fonts\DejaVuSans.ttf" />
<None Remove="Fonts\Manrope-Light.ttf" />
<None Remove="Fonts\WinSymbols3.ttf" />
<EmbeddedResource Include="Fonts\DejaVuSans.ttf" />
<EmbeddedResource Include="Fonts\Manrope-Light.ttf" />
<EmbeddedResource Include="Fonts\WinSymbols3.ttf" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\..\src\Avalonia.Base\Avalonia.Base.csproj" />

BIN
tests/Avalonia.Skia.UnitTests/Fonts/WinSymbols3.ttf

Binary file not shown.

13
tests/Avalonia.Skia.UnitTests/Media/TextFormatting/TextLayoutTests.cs

@ -1147,6 +1147,19 @@ namespace Avalonia.Skia.UnitTests.Media.TextFormatting
}
}
[Fact]
public void Should_Measure_TextLayoutSymbolWithAndWidthIncludingTrailingWhitespace()
{
const string symbolsFont = "resm:Avalonia.Skia.UnitTests.Fonts?assembly=Avalonia.Skia.UnitTests#Symbols";
using (Start())
{
var textLayout = new TextLayout("\ue971", new Typeface(symbolsFont), 12.0, Brushes.White);
Assert.Equal(new Size(12.0, 12.0), new Size(textLayout.Width, textLayout.Height));
Assert.Equal(12.0, textLayout.WidthIncludingTrailingWhitespace);
}
}
private static IDisposable Start()
{
var disposable = UnitTestApplication.Start(TestServices.MockPlatformRenderInterface

Loading…
Cancel
Save