Browse Source

Improved memory management by disposing all elements created in DynamicComponent as early as possible

Marcin Ziąbek 9 months ago
parent
commit
fa56bd1dea
1 changed files with 28 additions and 8 deletions
  1. 28 8
      Source/QuestPDF/Elements/Dynamic.cs

+ 28 - 8
Source/QuestPDF/Elements/Dynamic.cs

@@ -1,4 +1,5 @@
 using System;
 using System;
+using System.Collections.Generic;
 using QuestPDF.Drawing;
 using QuestPDF.Drawing;
 using QuestPDF.Drawing.Exceptions;
 using QuestPDF.Drawing.Exceptions;
 using QuestPDF.Helpers;
 using QuestPDF.Helpers;
@@ -21,7 +22,6 @@ namespace QuestPDF.Elements
         public DynamicHost(DynamicComponentProxy child)
         public DynamicHost(DynamicComponentProxy child)
         {
         {
             Child = child;
             Child = child;
-            
             InitialComponentState = Child.GetState();
             InitialComponentState = Child.GetState();
         }
         }
  
  
@@ -29,10 +29,13 @@ namespace QuestPDF.Elements
         {
         {
             if (IsRendered)
             if (IsRendered)
                 return SpacePlan.Empty();
                 return SpacePlan.Empty();
-            
-            var result = ComposeContent(availableSpace, acceptNewState: false);
+
+            var context = CreateContext(availableSpace);
+            var result = ComposeContent(context, acceptNewState: false);
             var content = result.Content as Element ?? Empty.Instance;
             var content = result.Content as Element ?? Empty.Instance;
             var measurement = content.Measure(availableSpace);
             var measurement = content.Measure(availableSpace);
+            
+            context.DisposeCreatedElements();
             content.ReleaseDisposableChildren();
             content.ReleaseDisposableChildren();
             
             
             if (measurement.Type is SpacePlanType.PartialRender or SpacePlanType.Wrap)
             if (measurement.Type is SpacePlanType.PartialRender or SpacePlanType.Wrap)
@@ -45,20 +48,21 @@ namespace QuestPDF.Elements
 
 
         internal override void Draw(Size availableSpace)
         internal override void Draw(Size availableSpace)
         {
         {
-            var composeResult = ComposeContent(availableSpace, acceptNewState: true);
+            var context = CreateContext(availableSpace);
+            var composeResult = ComposeContent(context, acceptNewState: true);
             var content = composeResult.Content as Element; 
             var content = composeResult.Content as Element; 
             content?.Draw(availableSpace);
             content?.Draw(availableSpace);
+            
+            context.DisposeCreatedElements();
             content.ReleaseDisposableChildren();
             content.ReleaseDisposableChildren();
             
             
             if (!composeResult.HasMoreContent)
             if (!composeResult.HasMoreContent)
                 IsRendered = true;
                 IsRendered = true;
         }
         }
 
 
-        private DynamicComponentComposeResult ComposeContent(Size availableSize, bool acceptNewState)
+        private DynamicContext CreateContext(Size availableSize)
         {
         {
-            var componentState = Child.GetState();
-            
-            var context = new DynamicContext
+            return new DynamicContext
             {
             {
                 PageContext = PageContext,
                 PageContext = PageContext,
                 Canvas = Canvas,
                 Canvas = Canvas,
@@ -74,6 +78,11 @@ namespace QuestPDF.Elements
                 TotalPages = PageContext.IsInitialRenderingPhase ? int.MaxValue : PageContext.DocumentLength,
                 TotalPages = PageContext.IsInitialRenderingPhase ? int.MaxValue : PageContext.DocumentLength,
                 AvailableSize = availableSize
                 AvailableSize = availableSize
             };
             };
+        }
+        
+        private DynamicComponentComposeResult ComposeContent(DynamicContext context, bool acceptNewState)
+        {
+            var componentState = Child.GetState();
             
             
             var result = Child.Compose(context);
             var result = Child.Compose(context);
 
 
@@ -114,6 +123,8 @@ namespace QuestPDF.Elements
         internal ImageCompressionQuality ImageCompressionQuality { get; set; }
         internal ImageCompressionQuality ImageCompressionQuality { get; set; }
         internal bool UseOriginalImage { get; set; }
         internal bool UseOriginalImage { get; set; }
         
         
+        internal List<Element> CreatedElements { get; } = new();
+        
         /// <summary>
         /// <summary>
         /// Returns the number of the page being rendered at the moment.
         /// Returns the number of the page being rendered at the moment.
         /// </summary>
         /// </summary>
@@ -144,6 +155,7 @@ namespace QuestPDF.Elements
         public IDynamicElement CreateElement(Action<IContainer> content)
         public IDynamicElement CreateElement(Action<IContainer> content)
         {
         {
             var container = new DynamicElement();
             var container = new DynamicElement();
+            CreatedElements.Add(container);
             content(container);
             content(container);
             
             
             container.ApplyInheritedAndGlobalTexStyle(TextStyle);
             container.ApplyInheritedAndGlobalTexStyle(TextStyle);
@@ -157,6 +169,14 @@ namespace QuestPDF.Elements
             
             
             return container;
             return container;
         }
         }
+        
+        internal void DisposeCreatedElements()
+        {
+            foreach (var element in CreatedElements)
+                element.ReleaseDisposableChildren();
+            
+            CreatedElements.Clear();
+        }
     }
     }
 
 
     /// <summary>
     /// <summary>