瀏覽代碼

Layout debugging: significantly improved calculation performance and accuracy of issue detection.

Marcin Ziąbek 1 年之前
父節點
當前提交
7d6de0764c

+ 2 - 1
Source/QuestPDF/Drawing/DocumentGenerator.cs

@@ -230,6 +230,7 @@ namespace QuestPDF.Drawing
                 content.Measure(Size.Max);
 
                 var overflowState = content.ExtractElementsOfType<OverflowDebuggingProxy>().Single();
+                overflowState.StopMeasuring();
                 overflowState.ApplyLayoutOverflowVisualization();
                 
                 content.ApplyContentDirection();
@@ -245,7 +246,7 @@ namespace QuestPDF.Drawing
                 content.Measure(Size.Max);
                 
                 var overflowState = content.ExtractElementsOfType<OverflowDebuggingProxy>().Single();
-                overflowState.CaptureOriginalMeasurementValues();
+                overflowState.StopMeasuring();
                 overflowState.ApplyLayoutOverflowVisualization();
 
                 var rootCause = overflowState.FindLayoutOverflowVisualizationNodes().First();

+ 13 - 26
Source/QuestPDF/Drawing/Proxy/LayoutDebugging.cs

@@ -60,11 +60,6 @@ internal static class LayoutDebugging
             if (element.Value.MeasurementSize is null)
                 return;
             
-            // before assessing the element,
-            // reset layout state by measuring the element with original space
-            // in case when parent has altered the layout state with different overflow test
-            element.Value.Measure(element.Value.MeasurementSize.Value);
-            
             // element was not part of the current layout measurement,
             // it could not impact the process
             if (element.Value.SpacePlan is null)
@@ -75,7 +70,7 @@ internal static class LayoutDebugging
             if (element.Value.SpacePlan?.Type is SpacePlanType.FullRender)
                 return;
 
-            // when element is partially rendering, it likely has no issues,
+            // when the current element is partially rendering, it likely has no issues,
             // however, in certain cases, it may contain a child that is a root cause
             if (element.Value.SpacePlan?.Type is SpacePlanType.PartialRender)
             {
@@ -85,11 +80,11 @@ internal static class LayoutDebugging
                 return;
             }
             
-            // all of the code below relates to element that is wrapping,
-            // it could be root cause, or contain a child (even deeply nested) that is the root cause
+            // all the code below relates to element that is wrapping,
+            // it could be a root cause, or contain a child (even deeply nested) that is the root cause
             
             // strategy
-            // element does not contain any wrapping elements, no obvious root causes,
+            // the current does not contain any wrapping elements, no obvious root causes,
             // if it renders fully with extended space, it is a layout root cause
             if (element.Children.All(x => x.Value.SpacePlan?.Type is not SpacePlanType.Wrap) && MeasureElementWithExtendedSpace() is SpacePlanType.FullRender)
             {
@@ -98,13 +93,8 @@ internal static class LayoutDebugging
                 return;
             }
 
-            // every time a measurement is made, the layout state is mutated
-            // the previous strategy could modify the layout state
-            // reset layout state by measuring the element with original space
-            element.Value.Measure(element.Value.MeasurementSize.Value); 
-            
             // strategy:
-            // element contains wrapping children, they are likely the root cause,
+            // the current contains wrapping children, they are likely the root cause,
             // traverse them and attempt to fix them
             foreach (var child in element.Children.Where(x => x.Value.SpacePlan?.Type is SpacePlanType.Wrap))
                 Traverse(child);
@@ -113,11 +103,8 @@ internal static class LayoutDebugging
             if (MeasureElementWithExtendedSpace() is not SpacePlanType.Wrap)
                 return;
 
-            // reset layout state by measuring the element with original space
-            element.Value.Measure(element.Value.MeasurementSize.Value); // reset state
-            
             // strategy:
-            // element has layout issues but no obvious/trivial root causes
+            // the current has layout issues but no obvious/trivial root causes
             // possibly the problem is in nested children of partial rendering children
             foreach (var child in element.Children.Where(x => x.Value.SpacePlan?.Type is SpacePlanType.PartialRender))
                 Traverse(child);
@@ -145,12 +132,12 @@ internal static class LayoutDebugging
         });
     }
 
-    public static void CaptureOriginalMeasurementValues(this TreeNode<OverflowDebuggingProxy> parent)
+    public static void StopMeasuring(this TreeNode<OverflowDebuggingProxy> parent)
     {
-        parent.Value.CaptureOriginalValues();
+        parent.Value.StopMeasuring();
             
         foreach (var child in parent.Children)
-            CaptureOriginalMeasurementValues(child);
+            StopMeasuring(child);
     }
     
     public static IEnumerable<TreeNode<OverflowDebuggingProxy>> FindLayoutOverflowVisualizationNodes(this TreeNode<OverflowDebuggingProxy> rootNode)
@@ -221,7 +208,7 @@ internal static class LayoutDebugging
                 return;
             }
             
-            if (proxy.OriginalMeasurementSize is null || proxy.OriginalSpacePlan is null)
+            if (proxy.MeasurementSize is null || proxy.SpacePlan is null)
                 return;
             
             var indent = indentationCache[indentationLevel];
@@ -252,8 +239,8 @@ internal static class LayoutDebugging
             
             yield return new string('=', title.Length + 1);
             
-            yield return $"Available Space: {proxy.OriginalMeasurementSize}";
-            yield return $"Space Plan: {proxy.OriginalSpacePlan}";
+            yield return $"Available Space: {proxy.MeasurementSize}";
+            yield return $"Space Plan: {proxy.SpacePlan}";
             
             yield return new string('-', title.Length + 1);
             
@@ -267,7 +254,7 @@ internal static class LayoutDebugging
                 if (proxy.Child is LayoutOverflowVisualization)
                     return $"🚨 {elementName} 🚨";
                 
-                var indicator = proxy.OriginalSpacePlan.Value.Type switch
+                var indicator = proxy.SpacePlan.Value.Type switch
                 {
                     SpacePlanType.Wrap => "🔴",
                     SpacePlanType.PartialRender => "🟡",

+ 10 - 9
Source/QuestPDF/Drawing/Proxy/OverflowDebuggingProxy.cs

@@ -4,10 +4,9 @@ namespace QuestPDF.Drawing.Proxy;
 
 internal class OverflowDebuggingProxy : ElementProxy
 {
-    public Size? OriginalMeasurementSize { get; private set; }
-    public Size? MeasurementSize { get; private set; }
+    public bool IsMeasuring { get; private set; } = true;
     
-    public SpacePlan? OriginalSpacePlan { get; private set; }
+    public Size? MeasurementSize { get; private set; }
     public SpacePlan? SpacePlan { get; private set; }
 
     public OverflowDebuggingProxy(Element child)
@@ -19,15 +18,17 @@ internal class OverflowDebuggingProxy : ElementProxy
     {
         var spacePlan = Child.Measure(availableSpace);
 
-        MeasurementSize = availableSpace;
-        SpacePlan = spacePlan;
+        if (IsMeasuring)
+        {
+            MeasurementSize = availableSpace;
+            SpacePlan = spacePlan;
+        }
         
         return spacePlan;
     }
-
-    internal void CaptureOriginalValues()
+    
+    public void StopMeasuring()
     {
-        OriginalMeasurementSize = MeasurementSize;
-        OriginalSpacePlan = SpacePlan;
+        IsMeasuring = false;
     }
 }

+ 3 - 0
Source/QuestPDF/Elements/LayoutOverflowVisualization.cs

@@ -18,6 +18,9 @@ internal class LayoutOverflowVisualization : ContainerElement, IContentDirection
 
     internal override SpacePlan Measure(Size availableSpace)
     {
+        if (Size.Equal(availableSpace, Size.Zero))
+            return SpacePlan.Wrap();
+        
         var childSize = base.Measure(availableSpace);
         
         if (childSize.Type == SpacePlanType.FullRender)

+ 1 - 0
Source/QuestPDF/Resources/ReleaseNotes.txt

@@ -23,6 +23,7 @@ Bug Fixes:
 
 
 Version 2024.6.1
+- Layout debugging: significantly improved calculation performance and accuracy of issue detection.
 - Text element: improved the default sizing behavior for the Text element. Now, when the text is empty, it works more consistently by taking up zero width while still reserving vertical space based on the font size.
 - Fixed compatibility with .NET Standard 2.0.
 - Fixed an issue causing invisibility of Table elements containing only Header/Footer elements without content.