Browse Source

Initial implementation: capture element Location for usage in dynamic component

Marcin Ziąbek 1 year ago
parent
commit
3d159ade04

+ 72 - 0
Source/QuestPDF.Examples/DynamicPositionCapture.cs

@@ -0,0 +1,72 @@
+using System;
+using System.Collections.Generic;
+using System.Drawing;
+using System.Globalization;
+using System.Linq;
+using NUnit.Framework;
+using QuestPDF.Elements;
+using QuestPDF.Examples.Engine;
+using QuestPDF.Fluent;
+using QuestPDF.Helpers;
+using QuestPDF.Infrastructure;
+
+namespace QuestPDF.Examples
+{
+    public class DynamicPositionCaptureExample : IDynamicComponent
+    {
+        public DynamicComponentComposeResult Compose(DynamicContext context)
+        {
+            var positions = Enumerable
+                .Range(0, 20)
+                .SelectMany(x => context.GetElementCapturedLocations($"capture_{x}"))
+                .ToList();
+            
+            var visibleCount = positions.Count(x => x.PageNumber == context.PageNumber);
+
+            return new DynamicComponentComposeResult
+            {
+                Content = context.CreateElement(container => container.Text(visibleCount.ToString())),
+                HasMoreContent = positions.Any(x => x.PageNumber > context.PageNumber + 1)
+            };
+        }
+    }
+    
+    public static class DynamicPositionCapture
+    {
+        [Test]
+        public static void Dynamic()
+        {
+            RenderingTest
+                .Create()
+                .PageSize(PageSizes.A4)
+                .ShowResults()
+                .ProducePdf()
+                .Render(container =>
+                {
+                    container
+                        .Background(Colors.White)
+                        .Padding(25)
+                        .Row(row =>
+                        {
+                            row.Spacing(25);
+                            
+                            row.RelativeItem().Border(1).Column(column =>
+                            {
+                                column.Spacing(25);
+                                
+                                foreach (var i in Enumerable.Range(0, 20))
+                                {
+                                    column.Item()
+                                        .CaptureLocation($"capture_{i}")
+                                        .Width(Random.Shared.Next(25, 125))
+                                        .Height(Random.Shared.Next(25, 125))
+                                        .Background(Placeholders.BackgroundColor());
+                                }
+                            });
+                            
+                            row.RelativeItem().Dynamic(new DynamicPositionCaptureExample());
+                        });
+                });
+        }
+    }
+}

+ 9 - 0
Source/QuestPDF/Elements/Dynamic.cs

@@ -1,4 +1,5 @@
 using System;
+using System.Collections.Generic;
 using QuestPDF.Drawing;
 using QuestPDF.Drawing.Exceptions;
 using QuestPDF.Helpers;
@@ -134,6 +135,14 @@ namespace QuestPDF.Elements
         /// </summary>
         public Size AvailableSize { get; internal set; }
 
+        /// <summary>
+        /// Returns all page locations of the captured element.
+        /// </summary>
+        public ICollection<PageElementLocation> GetElementCapturedLocations(string id)
+        {
+            return PageContext.GetContentPosition(id);
+        }
+        
         /// <summary>
         /// Enables the creation of unattached layout structures and provides their size measurements.
         /// </summary>

+ 39 - 0
Source/QuestPDF/Elements/ElementLocationCapturer.cs

@@ -0,0 +1,39 @@
+using QuestPDF.Drawing;
+using QuestPDF.Infrastructure;
+
+namespace QuestPDF.Elements;
+
+internal class ElementLocationCapturer : ContainerElement, IContentDirectionAware
+{
+    public ContentDirection ContentDirection { get; set; }
+    
+    public string Id { get; set; }
+    
+    internal override void Draw(Size availableSpace)
+    {
+        base.Draw(availableSpace);
+        
+        var canvas = Canvas as SkiaCanvasBase;
+        
+        if (canvas == null)
+            return;
+        
+        var matrix = canvas.Canvas.GetCurrentTotalMatrix();
+        var size = Child?.Measure(availableSpace) ?? SpacePlan.Empty();
+
+        var position = new PageElementLocation
+        {
+            Id = Id,
+            
+            PageNumber = PageContext.CurrentPage,
+            
+            Width = size.Width,
+            Height = size.Height,
+            
+            X = ContentDirection == ContentDirection.LeftToRight ? matrix.TranslateX : matrix.TranslateX - size.Width,
+            Y = matrix.TranslateY,
+        };
+        
+        PageContext.CaptureContentPosition(position);
+    }
+}

+ 13 - 0
Source/QuestPDF/Fluent/ElementExtensions.cs

@@ -408,6 +408,19 @@ namespace QuestPDF.Fluent
         {
             return element.Element(new RepeatContent());
         }
+        
+        /// <summary>
+        /// Captures the size and location of its content.
+        /// The captured data can be then used in the Dynamic component to build and position other elements.
+        /// </summary>
+        public static IContainer CaptureLocation(this IContainer element, string id)
+        {
+            return element.Element(new ElementLocationCapturer
+            {
+                Id = id
+            });
+        }
+
 
         #region Canvas [Obsolete]
 

+ 19 - 1
Source/QuestPDF/Infrastructure/IPageContext.cs

@@ -1,4 +1,6 @@
-namespace QuestPDF.Infrastructure
+using System.Collections.Generic;
+
+namespace QuestPDF.Infrastructure
 {
     internal sealed class DocumentLocation
     {
@@ -17,5 +19,21 @@
         void SetSectionPage(string name);
         DocumentLocation? GetLocation(string name);
         string GetDocumentLocationName(string locationName);
+
+        void CaptureContentPosition(PageElementLocation location);
+        ICollection<PageElementLocation> GetContentPosition(string id);
+    }
+
+    public class PageElementLocation
+    {
+        public string Id { get; set; }
+        
+        public int PageNumber { get; set; }
+        
+        public float Width { get; set; }
+        public float Height { get; set; }
+        
+        public float X { get; set; }
+        public float Y { get; set; }
     }
 }

+ 13 - 0
Source/QuestPDF/Infrastructure/PageContext.cs

@@ -1,5 +1,6 @@
 using System;
 using System.Collections.Generic;
+using System.Linq;
 
 namespace QuestPDF.Infrastructure
 {
@@ -64,5 +65,17 @@ namespace QuestPDF.Infrastructure
         {
             return $"{CurrentDocumentId} | {locationName}";
         }
+        
+        private List<PageElementLocation> PageElementLocations { get; } = new();
+
+        public void CaptureContentPosition(PageElementLocation location)
+        {
+            PageElementLocations.Add(location);
+        }
+
+        public ICollection<PageElementLocation> GetContentPosition(string id)
+        {
+            return PageElementLocations.Where(x => x.Id == id).ToList();
+        }
     }
 }