diff --git a/src/Avalonia.Base/Media/TextFormatting/TextLayout.cs b/src/Avalonia.Base/Media/TextFormatting/TextLayout.cs index 39a8ff870e..590d2f2133 100644 --- a/src/Avalonia.Base/Media/TextFormatting/TextLayout.cs +++ b/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; diff --git a/tests/Avalonia.Skia.UnitTests/Avalonia.Skia.UnitTests.csproj b/tests/Avalonia.Skia.UnitTests/Avalonia.Skia.UnitTests.csproj index d006c8c189..d092e3b0b9 100644 --- a/tests/Avalonia.Skia.UnitTests/Avalonia.Skia.UnitTests.csproj +++ b/tests/Avalonia.Skia.UnitTests/Avalonia.Skia.UnitTests.csproj @@ -12,8 +12,10 @@ + + diff --git a/tests/Avalonia.Skia.UnitTests/Fonts/WinSymbols3.ttf b/tests/Avalonia.Skia.UnitTests/Fonts/WinSymbols3.ttf new file mode 100644 index 0000000000..4cba3d13a1 Binary files /dev/null and b/tests/Avalonia.Skia.UnitTests/Fonts/WinSymbols3.ttf differ diff --git a/tests/Avalonia.Skia.UnitTests/Media/TextFormatting/TextLayoutTests.cs b/tests/Avalonia.Skia.UnitTests/Media/TextFormatting/TextLayoutTests.cs index 3d19efeae9..0113973ab6 100644 --- a/tests/Avalonia.Skia.UnitTests/Media/TextFormatting/TextLayoutTests.cs +++ b/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