From b64822e91b5795dc70a2a2bb9b6df49575e80186 Mon Sep 17 00:00:00 2001 From: James Jackson-South Date: Mon, 16 Dec 2024 16:28:14 +1000 Subject: [PATCH] Do not insert mandatory break when line is broken by wrapping. --- .../DrawWithImageSharp.csproj | 4 +-- src/SixLabors.Fonts/TextLayout.cs | 29 +++++++++++++---- .../Issues/Issues_431.cs | 31 +++++++++++++++++++ 3 files changed, 56 insertions(+), 8 deletions(-) create mode 100644 tests/SixLabors.Fonts.Tests/Issues/Issues_431.cs diff --git a/samples/DrawWithImageSharp/DrawWithImageSharp.csproj b/samples/DrawWithImageSharp/DrawWithImageSharp.csproj index 94e5f44e..58f71516 100644 --- a/samples/DrawWithImageSharp/DrawWithImageSharp.csproj +++ b/samples/DrawWithImageSharp/DrawWithImageSharp.csproj @@ -1,4 +1,4 @@ - + portable @@ -46,7 +46,7 @@ - + diff --git a/src/SixLabors.Fonts/TextLayout.cs b/src/SixLabors.Fonts/TextLayout.cs index 0f521d1c..9c9ee09c 100644 --- a/src/SixLabors.Fonts/TextLayout.cs +++ b/src/SixLabors.Fonts/TextLayout.cs @@ -1055,11 +1055,14 @@ private static TextBox BreakLines( // Mandatory wrap at index. if (currentLineBreak.PositionWrap == codePointIndex && currentLineBreak.Required) { - textLines.Add(textLine.Finalize()); - glyphCount += textLine.Count; - textLine = new(); - lineAdvance = 0; - requiredBreak = true; + if (textLine.Count > 0) + { + textLines.Add(textLine.Finalize()); + glyphCount += textLine.Count; + textLine = new(); + lineAdvance = 0; + requiredBreak = true; + } } else if (shouldWrap && lineAdvance + glyphAdvance >= wrappingLength) { @@ -1140,6 +1143,7 @@ private static TextBox BreakLines( } // Find the next line break. + bool lastMandatory = lastLineBreak.Required; if (currentLineBreak.PositionWrap == codePointIndex) { lastLineBreak = currentLineBreak; @@ -1161,9 +1165,22 @@ private static TextBox BreakLines( continue; } + // The previous line ended with a non-mandatory break at the wrapping length but the new line starts + // with a mandatory line break. We should not add a new line in this case as the line break has + // already been synthesized. + if (textLine.Count == 0 + && textLines.Count > 0 + && !lastMandatory + && CodePoint.IsNewLine(codePoint)) + { + codePointIndex++; + graphemeCodePointIndex++; + continue; + } + + // Do not add new lines unless at position zero. if (textLine.Count > 0 && CodePoint.IsNewLine(codePoint)) { - // Do not add new lines unless at position zero. codePointIndex++; graphemeCodePointIndex++; continue; diff --git a/tests/SixLabors.Fonts.Tests/Issues/Issues_431.cs b/tests/SixLabors.Fonts.Tests/Issues/Issues_431.cs new file mode 100644 index 00000000..ec47bbd4 --- /dev/null +++ b/tests/SixLabors.Fonts.Tests/Issues/Issues_431.cs @@ -0,0 +1,31 @@ +// Copyright (c) Six Labors. +// Licensed under the Six Labors Split License. + +using System.Numerics; + +namespace SixLabors.Fonts.Tests.Issues; + +public class Issues_431 +{ + [Fact] + public void ShouldNotInsertExtraLineBreaks() + { + if (SystemFonts.TryGet("Arial", out FontFamily family)) + { + Font font = family.CreateFont(60); + const string text = "- Lorem ipsullll\ndolor sit amet\n-consectetur elit"; + + TextOptions options = new(font) + { + Origin = new Vector2(50, 20), + WrappingLength = 400, + }; + + int lineCount = TextMeasurer.CountLines(text, options); + Assert.Equal(4, lineCount); + + IReadOnlyList layout = TextLayout.GenerateLayout(text, options); + Assert.Equal(46, layout.Count); + } + } +}