From a316b718e15f21e5186fec74523a58c8f7132fc3 Mon Sep 17 00:00:00 2001 From: Alfie Brimblecombe Date: Tue, 20 Aug 2024 11:51:54 +1000 Subject: [PATCH 1/2] Calculate page breaks after child elements have been rendered --- .../HtmlRenderer.Demo.Console/Program.cs | 2 +- Source/HtmlRenderer/Core/Dom/CssBox.cs | 22 +++++++++++-------- 2 files changed, 14 insertions(+), 10 deletions(-) diff --git a/Source/Demos/HtmlRenderer.Demo.Console/Program.cs b/Source/Demos/HtmlRenderer.Demo.Console/Program.cs index 80e8c60b2..b24057843 100644 --- a/Source/Demos/HtmlRenderer.Demo.Console/Program.cs +++ b/Source/Demos/HtmlRenderer.Demo.Console/Program.cs @@ -24,7 +24,7 @@ foreach (var htmlSample in samples) { ////Just doing one test here. Comment this for all of them. - if (!htmlSample.FullName.Contains("37")) continue; + if (!htmlSample.FullName.Contains("34")) continue; //await skia.GenerateSampleAsync(htmlSample); //await svgSkia.GenerateSampleAsync(htmlSample); diff --git a/Source/HtmlRenderer/Core/Dom/CssBox.cs b/Source/HtmlRenderer/Core/Dom/CssBox.cs index a940aa713..2a59c54a0 100644 --- a/Source/HtmlRenderer/Core/Dom/CssBox.cs +++ b/Source/HtmlRenderer/Core/Dom/CssBox.cs @@ -700,15 +700,6 @@ protected virtual async Task PerformLayoutImpAsync(RGraphics g) { this.BreakPage(true); } - else if (this.PageBreakInside == CssConstants.Avoid && prevSibling != null) - { - // handle page break avoiding. - var pageLocationY = Location.Y % HtmlContainer.PageSize.Height; - if (ActualHeight + pageLocationY > HtmlContainer.PageSize.Height) - { - this.BreakPage(true); - } - } //Start with the assumption this is zero height. ActualBottom = Location.Y; @@ -756,6 +747,19 @@ protected virtual async Task PerformLayoutImpAsync(RGraphics g) { var actualWidth = Math.Max(GetMinimumWidth() + GetWidthMarginDeep(this), Size.Width < 90999 ? ActualRight - HtmlContainer.Root.Location.X : 0); HtmlContainer.ActualSize = CommonUtils.Max(HtmlContainer.ActualSize, new RSize(actualWidth, ActualBottom - HtmlContainer.Root.Location.Y)); + + if (this.PageBreakInside == CssConstants.Avoid && prevSibling != null + && Display != CssConstants.TableCell) + { + // handle page break avoiding. + var pageLocationY = Location.Y % HtmlContainer.PageSize.Height; + if (Size.Height + pageLocationY > HtmlContainer.PageSize.Height) + { + // offset the current box and all of it's children to the next page. + var offset = HtmlContainer.PageSize.Height - pageLocationY; + OffsetTop(offset + 1); + } + } } } From 179da6a154508feb37ab7c5edcd584bead93a38b Mon Sep 17 00:00:00 2001 From: Alfie Brimblecombe Date: Tue, 20 Aug 2024 14:13:11 +1000 Subject: [PATCH 2/2] Improve page break avoidance by re-performing box layouts --- .../37. Breaking Pages 4 - Divs.htm | 10 +++--- .../HtmlRenderer.Demo.Console/Program.cs | 2 +- Source/HtmlRenderer/Core/Dom/CssBox.cs | 34 ++++++++++++++----- 3 files changed, 31 insertions(+), 15 deletions(-) diff --git a/Source/Demos/HtmlRenderer.Demo.Common/TestSamples/37. Breaking Pages 4 - Divs.htm b/Source/Demos/HtmlRenderer.Demo.Common/TestSamples/37. Breaking Pages 4 - Divs.htm index 8af74d192..b0c4ebeae 100644 --- a/Source/Demos/HtmlRenderer.Demo.Common/TestSamples/37. Breaking Pages 4 - Divs.htm +++ b/Source/Demos/HtmlRenderer.Demo.Common/TestSamples/37. Breaking Pages 4 - Divs.htm @@ -15,14 +15,14 @@
This is on page 4. -
+
This is also on page 4
-
- This is also on page 4. -
-
+
+ This is on page 4 +
+
This is on page 5
diff --git a/Source/Demos/HtmlRenderer.Demo.Console/Program.cs b/Source/Demos/HtmlRenderer.Demo.Console/Program.cs index b24057843..3b9f51e90 100644 --- a/Source/Demos/HtmlRenderer.Demo.Console/Program.cs +++ b/Source/Demos/HtmlRenderer.Demo.Console/Program.cs @@ -24,7 +24,7 @@ foreach (var htmlSample in samples) { ////Just doing one test here. Comment this for all of them. - if (!htmlSample.FullName.Contains("34")) continue; + if (!htmlSample.FullName.Contains("37") && !htmlSample.FullName.Contains("34")) continue; //await skia.GenerateSampleAsync(htmlSample); //await svgSkia.GenerateSampleAsync(htmlSample); diff --git a/Source/HtmlRenderer/Core/Dom/CssBox.cs b/Source/HtmlRenderer/Core/Dom/CssBox.cs index 2a59c54a0..59579aef4 100644 --- a/Source/HtmlRenderer/Core/Dom/CssBox.cs +++ b/Source/HtmlRenderer/Core/Dom/CssBox.cs @@ -70,6 +70,12 @@ internal class CssBox : CssBoxProperties, IDisposable /// Flag that indicates that CssTable algorithm already made fixes on it. /// internal bool _tableFixed; + + /// + /// Flag indicating that we're on a second pass for laying out the element after + /// avoiding a page break. + /// + internal bool _pageBreakAvoided; protected bool _wordsSizeMeasured; private CssBox _listItemBox; @@ -651,7 +657,9 @@ protected virtual async Task PerformLayoutImpAsync(RGraphics g) Size = new RSize(width - ActualMarginLeft - ActualMarginRight, Size.Height); } - if (Display != CssConstants.TableCell) + // Don't recalculate the element's position if we're + // on our second pass after avoiding a page break. + if (Display != CssConstants.TableCell && !_pageBreakAvoided) { double left; double top; @@ -745,21 +753,29 @@ protected virtual async Task PerformLayoutImpAsync(RGraphics g) if (!IsFixed && !IsAbsolute) { - var actualWidth = Math.Max(GetMinimumWidth() + GetWidthMarginDeep(this), Size.Width < 90999 ? ActualRight - HtmlContainer.Root.Location.X : 0); - HtmlContainer.ActualSize = CommonUtils.Max(HtmlContainer.ActualSize, new RSize(actualWidth, ActualBottom - HtmlContainer.Root.Location.Y)); - - if (this.PageBreakInside == CssConstants.Avoid && prevSibling != null - && Display != CssConstants.TableCell) + if (PageBreakInside == CssConstants.Avoid + && prevSibling != null + && Display != CssConstants.TableCell + && !_pageBreakAvoided) { // handle page break avoiding. var pageLocationY = Location.Y % HtmlContainer.PageSize.Height; if (Size.Height + pageLocationY > HtmlContainer.PageSize.Height) { - // offset the current box and all of it's children to the next page. - var offset = HtmlContainer.PageSize.Height - pageLocationY; - OffsetTop(offset + 1); + // if we break page, we'll do another pass at PerformLayoutAsync + // so that child elements are re-positioned correctly. + BreakPage(true); + _pageBreakAvoided = true; + await this.PerformLayoutAsync(g); + + // We'll set this flag back to false as this element + // might need re-positioning by its parent. + _pageBreakAvoided = false; } } + + var actualWidth = Math.Max(GetMinimumWidth() + GetWidthMarginDeep(this), Size.Width < 90999 ? ActualRight - HtmlContainer.Root.Location.X : 0); + HtmlContainer.ActualSize = CommonUtils.Max(HtmlContainer.ActualSize, new RSize(actualWidth, ActualBottom - HtmlContainer.Root.Location.Y)); } }