@@ -1474,9 +1474,10 @@ internal void GetMinMaxWidth(out double minWidth, out double maxWidth)
double maxSum = 0f;
double paddingSum = 0f;
double marginSum = 0f;
- GetMinMaxSumWords(this, ref min, ref maxSum, ref paddingSum, ref marginSum);
+ double brokenLinesMax = 0f;
+ GetMinMaxSumWords(this, ref min, ref maxSum, ref paddingSum, ref marginSum, ref brokenLinesMax);
- maxWidth = paddingSum + maxSum;
+ maxWidth = paddingSum + Math.Max(maxSum, brokenLinesMax);
minWidth = paddingSum + (min < 90999 ? min : 0);
}
@@ -1488,10 +1489,12 @@ internal void GetMinMaxWidth(out double minWidth, out double maxWidth)
/// the max width a single line of words can take without wrapping
/// the total amount of padding the content has
///
+ /// the widest line already ended by a line break in the current block
///
- private static void GetMinMaxSumWords(CssBox box, ref double min, ref double maxSum, ref double paddingSum, ref double marginSum)
+ private static void GetMinMaxSumWords(CssBox box, ref double min, ref double maxSum, ref double paddingSum, ref double marginSum, ref double brokenLinesMax)
{
double? oldSum = null;
+ double oldBrokenLinesMax = 0;
// paddingSum must be scoped per "line" the same way maxSum is (see the oldSum save/restore
// below) - it represents the border/padding belonging to the WIDEST line found so far, not a
@@ -1509,6 +1512,8 @@ private static void GetMinMaxSumWords(CssBox box, ref double min, ref double max
maxSum = marginSum;
oldPaddingSum = paddingSum;
paddingSum = 0;
+ oldBrokenLinesMax = brokenLinesMax;
+ brokenLinesMax = 0;
}
// add the padding
@@ -1524,6 +1529,14 @@ private static void GetMinMaxSumWords(CssBox box, ref double min, ref double max
// calculate the min and max sum for all the words in the box
foreach (CssRect word in box.Words)
{
+ // A line break ends the line, so the next word starts a new one instead of adding to it.
+ if (word.IsLineBreak)
+ {
+ brokenLinesMax = Math.Max(brokenLinesMax, maxSum);
+ maxSum = marginSum;
+ continue;
+ }
+
maxSum += word.FullWidth + (word.HasSpaceBefore ? word.OwnerBox.ActualWordSpacing : 0);
min = Math.Max(min, word.Width);
}
@@ -1542,7 +1555,7 @@ private static void GetMinMaxSumWords(CssBox box, ref double min, ref double max
//maxSum += childBox.ActualMarginLeft + childBox.ActualMarginRight;
var maxSumBeforeChild = maxSum;
- GetMinMaxSumWords(childBox, ref min, ref maxSum, ref paddingSum, ref marginSum);
+ GetMinMaxSumWords(childBox, ref min, ref maxSum, ref paddingSum, ref marginSum, ref brokenLinesMax);
// This walk otherwise never consults a box's own explicit CSS `width` at all - only
// literal word/text content. That's usually fine (explicit width constrains layout
@@ -1575,7 +1588,8 @@ private static void GetMinMaxSumWords(CssBox box, ref double min, ref double max
// max sum (and its matching padding contribution) is the max of all the lines in the box
if (oldSum.HasValue)
{
- maxSum = Math.Max(maxSum, oldSum.Value);
+ maxSum = Math.Max(Math.Max(maxSum, brokenLinesMax), oldSum.Value);
+ brokenLinesMax = oldBrokenLinesMax;
paddingSum = Math.Max(paddingSum, oldPaddingSum!.Value);
}
}
diff --git a/Source/HtmlRenderer/Core/Dom/CssLayoutEngine.cs b/Source/HtmlRenderer/Core/Dom/CssLayoutEngine.cs
index c1ebb75d9..6acf2a0be 100644
--- a/Source/HtmlRenderer/Core/Dom/CssLayoutEngine.cs
+++ b/Source/HtmlRenderer/Core/Dom/CssLayoutEngine.cs
@@ -467,9 +467,14 @@ private static void FlowBox(RGraphics g, CssBox blockbox, CssBox box, double lim
actualLimitRight = rightObstructingFloat.Location.X - rightObstructingFloat.ActualMarginLeft - rightspacing;
}
+ // A line break ends the current line, so the word after it starts the next one. Wrapping on
+ // the break itself would add an empty line after a trailing
and drop the one a
+ // leading
creates.
+ var afterLineBreak = line.Words.Count > 0 && line.Words[line.Words.Count - 1].IsLineBreak;
+
if ((b.WhiteSpace != CssConstants.NoWrap && b.WhiteSpace != CssConstants.Pre && curx + word.Width + rightspacing > actualLimitRight
&& (b.WhiteSpace != CssConstants.PreWrap || !word.IsSpaces))
- || word.IsLineBreak || wrapNoWrapBox)
+ || afterLineBreak || wrapNoWrapBox)
{
wrapNoWrapBox = false;
curx = startx;
diff --git a/Source/HtmlRenderer/Core/Parse/HtmlParser.cs b/Source/HtmlRenderer/Core/Parse/HtmlParser.cs
index 3e85e1997..534ace396 100644
--- a/Source/HtmlRenderer/Core/Parse/HtmlParser.cs
+++ b/Source/HtmlRenderer/Core/Parse/HtmlParser.cs
@@ -98,11 +98,29 @@ private static void AddTextBox(HtmlDataToken token, ref CssBox curBox)
if (text == null) return;
+ // HTML drops a single newline directly after the , or