Browse Source

Multipass rendering. Total pages / location position support. Continuous pages.

Marcin Ziąbek 4 years ago
parent
commit
31191efd88
51 changed files with 778 additions and 308 deletions
  1. 4 2
      QuestPDF.Examples/Engine/RenderingTest.cs
  2. 8 4
      QuestPDF.Examples/Engine/SimpleDocument.cs
  3. 19 6
      QuestPDF.ReportSample/Layouts/StandardReport.cs
  4. 1 0
      QuestPDF.ReportSample/Layouts/TableOfContentsTemplate.cs
  5. 2 1
      QuestPDF.UnitTests/TestEngine/CanvasMock.cs
  6. 0 59
      QuestPDF/Drawing/Canvas.cs
  7. 36 0
      QuestPDF/Drawing/DocumentContainer.cs
  8. 97 80
      QuestPDF/Drawing/DocumentGenerator.cs
  9. 0 1
      QuestPDF/Drawing/DocumentMetadata.cs
  10. 71 0
      QuestPDF/Drawing/FreeCanvas.cs
  11. 65 0
      QuestPDF/Drawing/PdfCanvas.cs
  12. 58 0
      QuestPDF/Drawing/SkiaCanvasBase.cs
  13. 4 4
      QuestPDF/Elements/Alignment.cs
  14. 2 2
      QuestPDF/Elements/AspectRatio.cs
  15. 3 3
      QuestPDF/Elements/Background.cs
  16. 6 6
      QuestPDF/Elements/Border.cs
  17. 2 2
      QuestPDF/Elements/Box.cs
  18. 2 2
      QuestPDF/Elements/Canvas.cs
  19. 2 2
      QuestPDF/Elements/Constrained.cs
  20. 14 6
      QuestPDF/Elements/Decoration.cs
  21. 3 2
      QuestPDF/Elements/DynamicImage.cs
  22. 1 1
      QuestPDF/Elements/Empty.cs
  23. 3 3
      QuestPDF/Elements/ExternalLink.cs
  24. 2 2
      QuestPDF/Elements/Image.cs
  25. 3 3
      QuestPDF/Elements/InternalLink.cs
  26. 17 4
      QuestPDF/Elements/InternalLocation.cs
  27. 10 3
      QuestPDF/Elements/Layers.cs
  28. 4 4
      QuestPDF/Elements/Padding.cs
  29. 28 6
      QuestPDF/Elements/Page.cs
  30. 11 5
      QuestPDF/Elements/PageBreak.cs
  31. 26 17
      QuestPDF/Elements/PageNumber.cs
  32. 3 3
      QuestPDF/Elements/Rotate.cs
  33. 28 11
      QuestPDF/Elements/Row.cs
  34. 3 3
      QuestPDF/Elements/Scale.cs
  35. 9 4
      QuestPDF/Elements/ShowOnce.cs
  36. 19 6
      QuestPDF/Elements/Stack.cs
  37. 4 4
      QuestPDF/Elements/Text.cs
  38. 3 3
      QuestPDF/Elements/Translate.cs
  39. 1 1
      QuestPDF/Fluent/ElementExtensions.cs
  40. 57 2
      QuestPDF/Fluent/PageExtensions.cs
  41. 36 36
      QuestPDF/Helpers/PageSizes.cs
  42. 10 3
      QuestPDF/Infrastructure/ContainerElement.cs
  43. 17 1
      QuestPDF/Infrastructure/Element.cs
  44. 1 0
      QuestPDF/Infrastructure/ICanvas.cs
  45. 1 1
      QuestPDF/Infrastructure/IDocument.cs
  46. 7 0
      QuestPDF/Infrastructure/IDocumentContainer.cs
  47. 11 0
      QuestPDF/Infrastructure/IPageContext.cs
  48. 11 0
      QuestPDF/Infrastructure/IRenderingCanvas.cs
  49. 7 0
      QuestPDF/Infrastructure/IStateResettable.cs
  50. 45 0
      QuestPDF/Infrastructure/PageContext.cs
  51. 1 0
      QuestPDF/Infrastructure/Size.cs

+ 4 - 2
QuestPDF.Examples/Engine/RenderingTest.cs

@@ -1,7 +1,9 @@
 using System;
 using System;
 using System.Diagnostics;
 using System.Diagnostics;
+using QuestPDF.Drawing;
 using QuestPDF.Elements;
 using QuestPDF.Elements;
 using QuestPDF.Fluent;
 using QuestPDF.Fluent;
+using QuestPDF.Helpers;
 using QuestPDF.Infrastructure;
 using QuestPDF.Infrastructure;
 
 
 namespace QuestPDF.Examples.Engine
 namespace QuestPDF.Examples.Engine
@@ -43,8 +45,8 @@ namespace QuestPDF.Examples.Engine
         public void Render(Action<IContainer> content)
         public void Render(Action<IContainer> content)
         {
         {
             var container = new Container();
             var container = new Container();
-            content.Invoke(container);
-
+            content(container);
+            
             Func<int, string> fileNameSchema = i => $"{FileNamePrefix}-${i}.png";
             Func<int, string> fileNameSchema = i => $"{FileNamePrefix}-${i}.png";
 
 
             var document = new SimpleDocument(container, Size);
             var document = new SimpleDocument(container, Size);

+ 8 - 4
QuestPDF.Examples/Engine/SimpleDocument.cs

@@ -1,4 +1,5 @@
 using QuestPDF.Drawing;
 using QuestPDF.Drawing;
+using QuestPDF.Elements;
 using QuestPDF.Fluent;
 using QuestPDF.Fluent;
 using QuestPDF.Helpers;
 using QuestPDF.Helpers;
 using QuestPDF.Infrastructure;
 using QuestPDF.Infrastructure;
@@ -21,14 +22,17 @@ namespace QuestPDF.Examples.Engine
             return new DocumentMetadata()
             return new DocumentMetadata()
             {
             {
                 RasterDpi = PageSizes.PointsPerInch * 2,
                 RasterDpi = PageSizes.PointsPerInch * 2,
-                Size = Size,
                 DocumentLayoutExceptionThreshold = 10
                 DocumentLayoutExceptionThreshold = 10
             };
             };
         }
         }
-
-        public void Compose(IContainer container)
+        
+        public void Compose(IDocumentContainer container)
         {
         {
-            container.Background("#FFF").Element(Container.Child);
+            container.Page(page =>
+            {
+                page.Size(new PageSize(Size.Width, Size.Height));
+                page.Content().Container().Element(Container as Container);
+            });
         }
         }
     }
     }
 }
 }

+ 19 - 6
QuestPDF.ReportSample/Layouts/StandardReport.cs

@@ -18,21 +18,34 @@ namespace QuestPDF.ReportSample.Layouts
         {
         {
             return new DocumentMetadata()
             return new DocumentMetadata()
             {
             {
-                Title = Model.Title,
-                Size = PageSizes.A4
+                Title = Model.Title
             };
             };
         }
         }
 
 
-        public void Compose(IContainer container)
+        public void Compose(IDocumentContainer container)
         {
         {
             container
             container
-                .PaddingVertical(40)
-                .PaddingHorizontal(50)
+                // .Page(page =>
+                // {
+                //     page.MarginVertical(40);
+                //     page.MarginHorizontal(50);
+                //     
+                //     page.ContinuousSize(PageSizes.A4.Width);
+                //         
+                //     page.Header().Element(ComposeHeader);
+                //     page.Content().Element(ComposeContent);
+                //     page.Footer().AlignCenter().PageNumber("Page A {number}");
+                // })
                 .Page(page =>
                 .Page(page =>
                 {
                 {
+                    page.MarginVertical(40);
+                    page.MarginHorizontal(50);
+                    
+                    page.Size(PageSizes.A4);
+                        
                     page.Header().Element(ComposeHeader);
                     page.Header().Element(ComposeHeader);
                     page.Content().Element(ComposeContent);
                     page.Content().Element(ComposeContent);
-                    page.Footer().AlignCenter().PageNumber("Page {number}");
+                    page.Footer().AlignCenter().PageNumber();
                 });
                 });
         }
         }
 
 

+ 1 - 0
QuestPDF.ReportSample/Layouts/TableOfContentsTemplate.cs

@@ -43,6 +43,7 @@ namespace QuestPDF.ReportSample.Layouts
                 {
                 {
                     row.ConstantColumn(25).Text($"{number}.", Typography.Normal);
                     row.ConstantColumn(25).Text($"{number}.", Typography.Normal);
                     row.RelativeColumn().Text(locationName, Typography.Normal);
                     row.RelativeColumn().Text(locationName, Typography.Normal);
+                    row.ConstantColumn(150).AlignRight().PageNumber($"Page {{pdf:{locationName}}}", Typography.Normal.AlignRight());
                 });
                 });
         }
         }
     }
     }

+ 2 - 1
QuestPDF.UnitTests/TestEngine/CanvasMock.cs

@@ -10,11 +10,12 @@ namespace QuestPDF.UnitTests.TestEngine
         public Action<SKImage, Position, Size> DrawImageFunc { get; set; }
         public Action<SKImage, Position, Size> DrawImageFunc { get; set; }
         public Action<string, Position, TextStyle> DrawTextFunc { get; set; }
         public Action<string, Position, TextStyle> DrawTextFunc { get; set; }
         public Action<Position, Size, string> DrawRectFunc { get; set; }
         public Action<Position, Size, string> DrawRectFunc { get; set; }
-        
+
         public void Translate(Position vector) => TranslateFunc(vector);
         public void Translate(Position vector) => TranslateFunc(vector);
         public void DrawRectangle(Position vector, Size size, string color) => DrawRectFunc(vector, size, color);
         public void DrawRectangle(Position vector, Size size, string color) => DrawRectFunc(vector, size, color);
         public void DrawText(string text, Position position, TextStyle style) => DrawTextFunc(text, position, style);
         public void DrawText(string text, Position position, TextStyle style) => DrawTextFunc(text, position, style);
         public void DrawImage(SKImage image, Position position, Size size) => DrawImageFunc(image, position, size);
         public void DrawImage(SKImage image, Position position, Size size) => DrawImageFunc(image, position, size);
+        
         public void DrawExternalLink(string url, Size size)
         public void DrawExternalLink(string url, Size size)
         {
         {
             throw new NotImplementedException();
             throw new NotImplementedException();

+ 0 - 59
QuestPDF/Drawing/Canvas.cs

@@ -1,59 +0,0 @@
-using QuestPDF.Infrastructure;
-using SkiaSharp;
-
-namespace QuestPDF.Drawing
-{
-    internal class Canvas : ICanvas
-    {
-        internal SKCanvas SkiaCanvas { get; }
-        
-        public Canvas(SKCanvas skiaCanvas)
-        {
-            SkiaCanvas = skiaCanvas;
-        }
-        
-        ~Canvas()
-        {
-            SkiaCanvas.Dispose();
-        }
-
-        public void Translate(Position vector)
-        {
-            SkiaCanvas.Translate(vector.X, vector.Y);
-        }
-
-        public void DrawRectangle(Position vector, Size size, string color)
-        {
-            if (size.Width < Size.Epsilon || size.Height < Size.Epsilon)
-                return;
-
-            var paint = color.ColorToPaint();
-            SkiaCanvas.DrawRect(vector.X, vector.Y, size.Width, size.Height, paint);
-        }
-
-        public void DrawText(string text, Position vector, TextStyle style)
-        {
-            SkiaCanvas.DrawText(text, vector.X, vector.Y, style.ToPaint());
-        }
-
-        public void DrawImage(SKImage image, Position vector, Size size)
-        {
-            SkiaCanvas.DrawImage(image, new SKRect(vector.X, vector.Y, size.Width, size.Height));
-        }
-
-        public void DrawExternalLink(string url, Size size)
-        {
-            SkiaCanvas.DrawUrlAnnotation(new SKRect(0, 0, size.Width, size.Height), url);
-        }
-        
-        public void DrawLocationLink(string locationName, Size size)
-        {
-            SkiaCanvas.DrawLinkDestinationAnnotation(new SKRect(0, 0, size.Width, size.Height), locationName);
-        }
-
-        public void DrawLocation(string locationName)
-        {
-            SkiaCanvas.DrawNamedDestinationAnnotation(new SKPoint(0, 0), locationName);
-        }
-    }
-}

+ 36 - 0
QuestPDF/Drawing/DocumentContainer.cs

@@ -0,0 +1,36 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using QuestPDF.Elements;
+using QuestPDF.Fluent;
+using QuestPDF.Helpers;
+using QuestPDF.Infrastructure;
+
+namespace QuestPDF.Drawing
+{
+    internal class DocumentContainer : IDocumentContainer
+    {
+        internal List<IComponent> Pages { get; set; } = new List<IComponent>();
+        
+        internal Container Compose()
+        {
+            var container = new Container();
+            
+            container
+                .Stack(stack =>
+                {
+                    Pages
+                        .SelectMany(x => new List<Action>()
+                        {
+                            () => stack.Item().PageBreak(),
+                            () => stack.Item().Component(x)
+                        })
+                        .Skip(1)
+                        .ToList()
+                        .ForEach(x => x());
+                });
+
+            return container;
+        }
+    }
+}

+ 97 - 80
QuestPDF/Drawing/DocumentGenerator.cs

@@ -1,8 +1,10 @@
 using System;
 using System;
 using System.Collections.Generic;
 using System.Collections.Generic;
+using System.Diagnostics;
 using System.IO;
 using System.IO;
 using QuestPDF.Drawing.Exceptions;
 using QuestPDF.Drawing.Exceptions;
 using QuestPDF.Drawing.SpacePlan;
 using QuestPDF.Drawing.SpacePlan;
+using QuestPDF.Elements;
 using QuestPDF.Fluent;
 using QuestPDF.Fluent;
 using QuestPDF.Helpers;
 using QuestPDF.Helpers;
 using QuestPDF.Infrastructure;
 using QuestPDF.Infrastructure;
@@ -14,115 +16,130 @@ namespace QuestPDF.Drawing
     {
     {
         internal static void GeneratePdf(Stream stream, IDocument document)
         internal static void GeneratePdf(Stream stream, IDocument document)
         {
         {
-            var content = ElementExtensions.Create(document.Compose);
+            var container = new DocumentContainer();
+            document.Compose(container);
+            var content = container.Compose();
+            
             var metadata = document.GetMetadata();
             var metadata = document.GetMetadata();
+            var pageContext = new PageContext();
+
+            var timer = new Stopwatch();
             
             
-            using var pdf = SKDocument.CreatePdf(stream, MapMetadata(metadata));
-            var totalPages = 1;
+            timer.Start();
+            RenderDocument(pageContext, new FreeCanvas(), content, metadata);
             
             
-            while(true)
-            {
-                var spacePlan = content.Measure(metadata.Size);
-
-                using var skiaCanvas = pdf.BeginPage(metadata.Size.Width, metadata.Size.Height);
-                var canvas = new Canvas(skiaCanvas);
-
-                try
-                {
-                    content.Draw(canvas, metadata.Size);
-                }
-                catch (Exception exception)
-                {
-                    pdf.Close();
-                    throw new DocumentDrawingException("An exception occured during document drawing.", exception);
-                }
-
-                pdf.EndPage();
-
-                if (totalPages >= metadata.DocumentLayoutExceptionThreshold)
-                {
-                    pdf.Close();
-                    throw new DocumentLayoutException("Composed layout generates infinite document.");
-                }
-                
-                if (spacePlan is FullRender)
-                    break;
-
-                totalPages++;
-            }
+            Console.WriteLine($"First rendering pass: {timer.Elapsed:g}");
+            timer.Restart();
             
             
-            pdf.Close();
+            var canvas = new PdfCanvas(stream, metadata);
+            RenderDocument(pageContext, canvas, content, metadata);
+            
+            Console.WriteLine($"Second rendering pass: {timer.Elapsed:g}");
         }
         }
 
 
-        internal static IEnumerable<byte[]> GenerateImages(IDocument document)
+        private static void RenderDocument<TCanvas>(PageContext pageContext, TCanvas canvas, Container content, DocumentMetadata documentMetadata)
+            where TCanvas : ICanvas, IRenderingCanvas
         {
         {
-            var content = ElementExtensions.Create(document.Compose);
-            var metadata = document.GetMetadata();
-
-            var totalPages = 1;
+            content.HandleVisitor(x => x.Initialize(pageContext, canvas));
+            content.HandleVisitor(x => (x as IStateResettable)?.ResetState());
+            
+            canvas.BeginDocument();
 
 
-            while (true)
+            var currentPage = 1;
+            
+            while(true)
             {
             {
-                var spacePlan = content.Measure(metadata.Size);
-                byte[] result;
+                pageContext.SetPageNumber(currentPage);
+                var spacePlan = content.Measure(Size.Max) as Size;
+
+                if (spacePlan == null)
+                    break;
 
 
                 try
                 try
                 {
                 {
-                    result = RenderPage(content);
+                    canvas.BeginPage(spacePlan);
+                    content.Draw(spacePlan);
                 }
                 }
                 catch (Exception exception)
                 catch (Exception exception)
                 {
                 {
+                    canvas.EndDocument();
                     throw new DocumentDrawingException("An exception occured during document drawing.", exception);
                     throw new DocumentDrawingException("An exception occured during document drawing.", exception);
                 }
                 }
 
 
-                yield return result;
+                canvas.EndPage();
 
 
-                if (totalPages >= metadata.DocumentLayoutExceptionThreshold)
+                if (currentPage >= documentMetadata.DocumentLayoutExceptionThreshold)
                 {
                 {
+                    canvas.EndDocument();
                     throw new DocumentLayoutException("Composed layout generates infinite document.");
                     throw new DocumentLayoutException("Composed layout generates infinite document.");
                 }
                 }
-
+                
                 if (spacePlan is FullRender)
                 if (spacePlan is FullRender)
                     break;
                     break;
 
 
-                totalPages++;
-            }
-
-            byte[] RenderPage(Element element)
-            {
-                // scale the result so it is more readable
-                var scalingFactor = metadata.RasterDpi / (float) PageSizes.PointsPerInch;
-                
-                var imageInfo = new SKImageInfo((int) (metadata.Size.Width * scalingFactor), (int) (metadata.Size.Height * scalingFactor));
-                using var surface = SKSurface.Create(imageInfo);
-                surface.Canvas.Scale(scalingFactor);
-
-                var canvas = new Canvas(surface.Canvas);
-                element?.Draw(canvas, metadata.Size);
-
-                surface.Canvas.Save();
-                return surface.Snapshot().Encode(SKEncodedImageFormat.Png, 100).ToArray();
+                currentPage++;
             }
             }
+            
+            canvas.EndDocument();
         }
         }
         
         
-        private static SKDocumentPdfMetadata MapMetadata(DocumentMetadata metadata)
+        internal static IEnumerable<byte[]> GenerateImages(IDocument document)
         {
         {
-            return new SKDocumentPdfMetadata
-            {
-                Title = metadata.Title,
-                Author = metadata.Author,
-                Subject = metadata.Subject,
-                Keywords = metadata.Keywords,
-                Creator = metadata.Creator,
-                Producer = metadata.Producer,
-                
-                Creation = metadata.CreationDate,
-                Modified = metadata.ModifiedDate,
-                
-                RasterDpi = metadata.RasterDpi,
-                EncodingQuality = metadata.ImageQuality,
-                PdfA = metadata.PdfA
-            };
+            return null;
+            
+            // var container = new DocumentContainer();
+            // document.Compose(container);
+            // var content = container.Compose();
+            //
+            // var metadata = document.GetMetadata();
+            //
+            // var currentPage = 1;
+            //
+            // while (true)
+            // {
+            //     var spacePlan = content.Measure(null, Size.Max) as Size;
+            //     byte[] result;
+            //
+            //     if (spacePlan == null)
+            //         break;
+            //     
+            //     try
+            //     {
+            //         result = RenderPage(spacePlan, content);
+            //     }
+            //     catch (Exception exception)
+            //     {
+            //         throw new DocumentDrawingException("An exception occured during document drawing.", exception);
+            //     }
+            //
+            //     yield return result;
+            //
+            //     if (currentPage >= metadata.DocumentLayoutExceptionThreshold)
+            //     {
+            //         throw new DocumentLayoutException("Composed layout generates infinite document.");
+            //     }
+            //
+            //     if (spacePlan is FullRender)
+            //         break;
+            //
+            //     currentPage++;
+            // }
+
+            // byte[] RenderPage(Size size, Element element)
+            // {
+            //     // scale the result so it is more readable
+            //     var scalingFactor = metadata.RasterDpi / (float) PageSizes.PointsPerInch;
+            //     
+            //     var imageInfo = new SKImageInfo((int) (size.Width * scalingFactor), (int) (size.Height * scalingFactor));
+            //     using var surface = SKSurface.Create(imageInfo);
+            //     surface.Canvas.Scale(scalingFactor);
+            //
+            //     var canvas = new SkiaCanvasBase(surface.Canvas, new Dictionary<string, int>());
+            //     element?.Draw(canvas, size);
+            //
+            //     surface.Canvas.Save();
+            //     return surface.Snapshot().Encode(SKEncodedImageFormat.Png, 100).ToArray();
+            // }
         }
         }
     }
     }
 }
 }

+ 0 - 1
QuestPDF/Drawing/DocumentMetadata.cs

@@ -6,7 +6,6 @@ namespace QuestPDF.Drawing
 {
 {
     public class DocumentMetadata
     public class DocumentMetadata
     {
     {
-        public Size Size { get; set; } = PageSizes.A4;
         public int ImageQuality { get; set; } = 101;
         public int ImageQuality { get; set; } = 101;
         public int RasterDpi { get; set; } = 72;
         public int RasterDpi { get; set; } = 72;
         public bool PdfA { get; set; }
         public bool PdfA { get; set; }

+ 71 - 0
QuestPDF/Drawing/FreeCanvas.cs

@@ -0,0 +1,71 @@
+using QuestPDF.Infrastructure;
+using SkiaSharp;
+
+namespace QuestPDF.Drawing
+{
+    internal class FreeCanvas : ICanvas, IRenderingCanvas
+    {
+        #region IRenderingCanvas
+
+        public void BeginDocument()
+        {
+            
+        }
+
+        public void EndDocument()
+        {
+            
+        }
+
+        public void BeginPage(Size size)
+        {
+            
+        }
+
+        public void EndPage()
+        {
+            
+        }
+
+        #endregion
+
+        #region ICanvas
+
+        public void Translate(Position vector)
+        {
+            
+        }
+
+        public void DrawRectangle(Position vector, Size size, string color)
+        {
+            
+        }
+
+        public void DrawText(string text, Position position, TextStyle style)
+        {
+            
+        }
+
+        public void DrawImage(SKImage image, Position position, Size size)
+        {
+            
+        }
+
+        public void DrawExternalLink(string url, Size size)
+        {
+           
+        }
+
+        public void DrawLocationLink(string locationName, Size size)
+        {
+            
+        }
+
+        public void DrawLocation(string locationName)
+        {
+            
+        }
+
+        #endregion
+    }
+}

+ 65 - 0
QuestPDF/Drawing/PdfCanvas.cs

@@ -0,0 +1,65 @@
+using System.IO;
+using QuestPDF.Infrastructure;
+using SkiaSharp;
+
+namespace QuestPDF.Drawing
+{
+    internal class PdfCanvas : SkiaCanvasBase
+    {
+        private SKDocument Document { get; }
+
+        public PdfCanvas(Stream stream, DocumentMetadata documentMetadata)
+        {
+            Document = SKDocument.CreatePdf(stream, MapMetadata(documentMetadata));
+        }
+
+        ~PdfCanvas()
+        {
+            Document.Dispose();
+        }
+        
+        public override void BeginDocument()
+        {
+            
+        }
+
+        public override void EndDocument()
+        {
+            Canvas.Dispose();
+            
+            Document.Close();
+            Document.Dispose();
+        }
+
+        public override void BeginPage(Size size)
+        {
+            Canvas = Document.BeginPage(size.Width, size.Height);
+        }
+
+        public override void EndPage()
+        {
+            Document.EndPage();
+            Canvas.Dispose();
+        }
+        
+        private static SKDocumentPdfMetadata MapMetadata(DocumentMetadata metadata)
+        {
+            return new SKDocumentPdfMetadata
+            {
+                Title = metadata.Title,
+                Author = metadata.Author,
+                Subject = metadata.Subject,
+                Keywords = metadata.Keywords,
+                Creator = metadata.Creator,
+                Producer = metadata.Producer,
+                
+                Creation = metadata.CreationDate,
+                Modified = metadata.ModifiedDate,
+                
+                RasterDpi = metadata.RasterDpi,
+                EncodingQuality = metadata.ImageQuality,
+                PdfA = metadata.PdfA
+            };
+        }
+    }
+}

+ 58 - 0
QuestPDF/Drawing/SkiaCanvasBase.cs

@@ -0,0 +1,58 @@
+using System;
+using System.Collections.Generic;
+using QuestPDF.Elements;
+using QuestPDF.Infrastructure;
+using SkiaSharp;
+
+namespace QuestPDF.Drawing
+{
+    internal abstract class SkiaCanvasBase : ICanvas, IRenderingCanvas
+    {
+        internal SKCanvas Canvas { get; set; }
+
+        public abstract void BeginDocument();
+        public abstract void EndDocument();
+        
+        public abstract void BeginPage(Size size);
+        public abstract void EndPage();
+        
+        public void Translate(Position vector)
+        {
+            Canvas.Translate(vector.X, vector.Y);
+        }
+
+        public void DrawRectangle(Position vector, Size size, string color)
+        {
+            if (size.Width < Size.Epsilon || size.Height < Size.Epsilon)
+                return;
+
+            var paint = color.ColorToPaint();
+            Canvas.DrawRect(vector.X, vector.Y, size.Width, size.Height, paint);
+        }
+
+        public void DrawText(string text, Position vector, TextStyle style)
+        {
+            Canvas.DrawText(text, vector.X, vector.Y, style.ToPaint());
+        }
+
+        public void DrawImage(SKImage image, Position vector, Size size)
+        {
+            Canvas.DrawImage(image, new SKRect(vector.X, vector.Y, size.Width, size.Height));
+        }
+
+        public void DrawExternalLink(string url, Size size)
+        {
+            Canvas.DrawUrlAnnotation(new SKRect(0, 0, size.Width, size.Height), url);
+        }
+        
+        public void DrawLocationLink(string locationName, Size size)
+        {
+            Canvas.DrawLinkDestinationAnnotation(new SKRect(0, 0, size.Width, size.Height), locationName);
+        }
+
+        public void DrawLocation(string locationName)
+        {
+            Canvas.DrawNamedDestinationAnnotation(new SKPoint(0, 0), locationName);
+        }
+    }
+}

+ 4 - 4
QuestPDF/Elements/Alignment.cs

@@ -8,7 +8,7 @@ namespace QuestPDF.Elements
         public VerticalAlignment Vertical { get; set; } = VerticalAlignment.Top;
         public VerticalAlignment Vertical { get; set; } = VerticalAlignment.Top;
         public HorizontalAlignment Horizontal { get; set; } = HorizontalAlignment.Left;
         public HorizontalAlignment Horizontal { get; set; } = HorizontalAlignment.Left;
         
         
-        internal override void Draw(ICanvas canvas, Size availableSpace)
+        internal override void Draw(Size availableSpace)
         {
         {
             if (Child == null)
             if (Child == null)
                 return;
                 return;
@@ -21,9 +21,9 @@ namespace QuestPDF.Elements
             var top = GetTopOffset(availableSpace, childSize);
             var top = GetTopOffset(availableSpace, childSize);
             var left = GetLeftOffset(availableSpace, childSize);
             var left = GetLeftOffset(availableSpace, childSize);
             
             
-            canvas.Translate(new Position(left, top));
-            Child.Draw(canvas, childSize);
-            canvas.Translate(new Position(-left, -top));
+            Canvas.Translate(new Position(left, top));
+            Child.Draw(childSize);
+            Canvas.Translate(new Position(-left, -top));
         }
         }
         
         
         private float GetTopOffset(Size availableSpace, Size childSize)
         private float GetTopOffset(Size availableSpace, Size childSize)

+ 2 - 2
QuestPDF/Elements/AspectRatio.cs

@@ -36,13 +36,13 @@ namespace QuestPDF.Elements
             throw new NotSupportedException();
             throw new NotSupportedException();
         }
         }
 
 
-        internal override void Draw(ICanvas canvas, Size availableSpace)
+        internal override void Draw(Size availableSpace)
         {
         {
             if (Child == null)
             if (Child == null)
                 return;
                 return;
             
             
             var size = GetTargetSize(availableSpace);
             var size = GetTargetSize(availableSpace);
-            Child?.Draw(canvas, size);
+            Child?.Draw(size);
         }
         }
         
         
         private Size GetTargetSize(Size availableSpace)
         private Size GetTargetSize(Size availableSpace)

+ 3 - 3
QuestPDF/Elements/Background.cs

@@ -7,10 +7,10 @@ namespace QuestPDF.Elements
     {
     {
         public string Color { get; set; } = Colors.Black;
         public string Color { get; set; } = Colors.Black;
         
         
-        internal override void Draw(ICanvas canvas, Size availableSpace)
+        internal override void Draw(Size availableSpace)
         {
         {
-            canvas.DrawRectangle(Position.Zero, availableSpace, Color);
-            Child?.Draw(canvas, availableSpace);
+            Canvas.DrawRectangle(Position.Zero, availableSpace, Color);
+            Child?.Draw(availableSpace);
         }
         }
     }
     }
 }
 }

+ 6 - 6
QuestPDF/Elements/Border.cs

@@ -12,26 +12,26 @@ namespace QuestPDF.Elements
         public float Bottom { get; set; }
         public float Bottom { get; set; }
         public float Left { get; set; }
         public float Left { get; set; }
 
 
-        internal override void Draw(ICanvas canvas, Size availableSpace)
+        internal override void Draw(Size availableSpace)
         {
         {
-            Child?.Draw(canvas, availableSpace);
+            Child?.Draw(availableSpace);
             
             
-            canvas.DrawRectangle(
+            Canvas.DrawRectangle(
                 new Position(-Left/2, -Top/2), 
                 new Position(-Left/2, -Top/2), 
                 new Size(availableSpace.Width + Left/2 + Right/2, Top), 
                 new Size(availableSpace.Width + Left/2 + Right/2, Top), 
                 Color);
                 Color);
             
             
-            canvas.DrawRectangle(
+            Canvas.DrawRectangle(
                 new Position(-Left/2, -Top/2), 
                 new Position(-Left/2, -Top/2), 
                 new Size(Left, availableSpace.Height + Top/2 + Bottom/2), 
                 new Size(Left, availableSpace.Height + Top/2 + Bottom/2), 
                 Color);
                 Color);
             
             
-            canvas.DrawRectangle(
+            Canvas.DrawRectangle(
                 new Position(-Left/2, availableSpace.Height-Bottom/2), 
                 new Position(-Left/2, availableSpace.Height-Bottom/2), 
                 new Size(availableSpace.Width + Left/2 + Right/2, Bottom), 
                 new Size(availableSpace.Width + Left/2 + Right/2, Bottom), 
                 Color);
                 Color);
             
             
-            canvas.DrawRectangle(
+            Canvas.DrawRectangle(
                 new Position(availableSpace.Width-Right/2, -Top/2), 
                 new Position(availableSpace.Width-Right/2, -Top/2), 
                 new Size(Right, availableSpace.Height + Top/2 + Bottom/2), 
                 new Size(Right, availableSpace.Height + Top/2 + Bottom/2), 
                 Color);
                 Color);

+ 2 - 2
QuestPDF/Elements/Box.cs

@@ -4,14 +4,14 @@ namespace QuestPDF.Elements
 {
 {
     internal class Box : ContainerElement
     internal class Box : ContainerElement
     {
     {
-        internal override void Draw(ICanvas canvas, Size availableSpace)
+        internal override void Draw(Size availableSpace)
         {
         {
             var targetSize = Child?.Measure(availableSpace) as Size;
             var targetSize = Child?.Measure(availableSpace) as Size;
             
             
             if (targetSize == null)
             if (targetSize == null)
                 return;
                 return;
             
             
-            Child?.Draw(canvas, targetSize);
+            Child?.Draw(targetSize);
         }
         }
     }
     }
 }
 }

+ 2 - 2
QuestPDF/Elements/Canvas.cs

@@ -15,9 +15,9 @@ namespace QuestPDF.Elements
             return new FullRender(availableSpace);
             return new FullRender(availableSpace);
         }
         }
 
 
-        internal override void Draw(ICanvas canvas, Size availableSpace)
+        internal override void Draw(Size availableSpace)
         {
         {
-            var skiaCanvas = (canvas as Drawing.Canvas)?.SkiaCanvas;
+            var skiaCanvas = (Canvas as Drawing.SkiaCanvasBase)?.Canvas;
             
             
             if (Handler == null || skiaCanvas == null)
             if (Handler == null || skiaCanvas == null)
                 return;
                 return;

+ 2 - 2
QuestPDF/Elements/Constrained.cs

@@ -44,13 +44,13 @@ namespace QuestPDF.Elements
             throw new NotSupportedException();
             throw new NotSupportedException();
         }
         }
         
         
-        internal override void Draw(ICanvas canvas, Size availableSpace)
+        internal override void Draw(Size availableSpace)
         {
         {
             var available = new Size(
             var available = new Size(
                 MathHelpers.Min(MaxWidth, availableSpace.Width),
                 MathHelpers.Min(MaxWidth, availableSpace.Width),
                 MathHelpers.Min(MaxHeight, availableSpace.Height));
                 MathHelpers.Min(MaxHeight, availableSpace.Height));
             
             
-            Child?.Draw(canvas, available);
+            Child?.Draw(available);
         }
         }
     }
     }
     
     

+ 14 - 6
QuestPDF/Elements/Decoration.cs

@@ -15,7 +15,15 @@ namespace QuestPDF.Elements
     {
     {
         public Element DecorationElement { get; set; } = Empty.Instance;
         public Element DecorationElement { get; set; } = Empty.Instance;
         public Element ContentElement { get; set; } = Empty.Instance;
         public Element ContentElement { get; set; } = Empty.Instance;
-        public DecorationType Type { get; set; } 
+        public DecorationType Type { get; set; }
+
+        internal override void HandleVisitor(Action<Element?> visit)
+        {
+            DecorationElement.HandleVisitor(visit);
+            ContentElement.HandleVisitor(visit);
+            
+            base.HandleVisitor(visit);
+        }
 
 
         internal override ISpacePlan Measure(Size availableSpace)
         internal override ISpacePlan Measure(Size availableSpace)
         {
         {
@@ -42,22 +50,22 @@ namespace QuestPDF.Elements
             throw new NotSupportedException();
             throw new NotSupportedException();
         }
         }
 
 
-        internal override void Draw(ICanvas canvas, Size availableSpace)
+        internal override void Draw(Size availableSpace)
         {
         {
             var decorationSize = DecorationElement?.Measure(availableSpace) as Size ?? Size.Zero;
             var decorationSize = DecorationElement?.Measure(availableSpace) as Size ?? Size.Zero;
             var contentSize = new Size(availableSpace.Width, availableSpace.Height - decorationSize.Height);
             var contentSize = new Size(availableSpace.Width, availableSpace.Height - decorationSize.Height);
 
 
             var translateHeight = Type == DecorationType.Prepend ? decorationSize.Height : contentSize.Height;
             var translateHeight = Type == DecorationType.Prepend ? decorationSize.Height : contentSize.Height;
-            Action drawDecoration = () => DecorationElement?.Draw(canvas, new Size(availableSpace.Width, decorationSize.Height));
-            Action drawContent = () => ContentElement?.Draw(canvas, new Size (availableSpace.Width, contentSize.Height));
+            Action drawDecoration = () => DecorationElement?.Draw(new Size(availableSpace.Width, decorationSize.Height));
+            Action drawContent = () => ContentElement?.Draw(new Size (availableSpace.Width, contentSize.Height));
 
 
             var first = Type == DecorationType.Prepend ? drawDecoration : drawContent;
             var first = Type == DecorationType.Prepend ? drawDecoration : drawContent;
             var second = Type == DecorationType.Prepend ? drawContent : drawDecoration;
             var second = Type == DecorationType.Prepend ? drawContent : drawDecoration;
 
 
             first();
             first();
-            canvas.Translate(new Position(0, translateHeight));
+            Canvas.Translate(new Position(0, translateHeight));
             second();
             second();
-            canvas.Translate(new Position(0, -translateHeight));
+            Canvas.Translate(new Position(0, -translateHeight));
         }
         }
     }
     }
     
     

+ 3 - 2
QuestPDF/Elements/DynamicImage.cs

@@ -14,7 +14,7 @@ namespace QuestPDF.Elements
             return new FullRender(availableSpace.Width, availableSpace.Height);
             return new FullRender(availableSpace.Width, availableSpace.Height);
         }
         }
 
 
-        internal override void Draw(ICanvas canvas, Size availableSpace)
+        internal override void Draw(Size availableSpace)
         {
         {
             var imageData = Source?.Invoke(availableSpace);
             var imageData = Source?.Invoke(availableSpace);
             
             
@@ -26,7 +26,8 @@ namespace QuestPDF.Elements
                 InternalImage = SKImage.FromEncodedData(imageData)
                 InternalImage = SKImage.FromEncodedData(imageData)
             };
             };
             
             
-            imageElement.Draw(canvas, availableSpace);
+            imageElement.Initialize(PageContext, Canvas);
+            imageElement.Draw(availableSpace);
         }
         }
     }
     }
 }
 }

+ 1 - 1
QuestPDF/Elements/Empty.cs

@@ -12,7 +12,7 @@ namespace QuestPDF.Elements
             return new FullRender(Size.Zero);
             return new FullRender(Size.Zero);
         }
         }
 
 
-        internal override void Draw(ICanvas canvas, Size availableSpace)
+        internal override void Draw(Size availableSpace)
         {
         {
             
             
         }
         }

+ 3 - 3
QuestPDF/Elements/ExternalLink.cs

@@ -6,15 +6,15 @@ namespace QuestPDF.Elements
     {
     {
         public string Url { get; set; } = "https://www.questpdf.com";
         public string Url { get; set; } = "https://www.questpdf.com";
         
         
-        internal override void Draw(ICanvas canvas, Size availableSpace)
+        internal override void Draw(Size availableSpace)
         {
         {
             var targetSize = Child?.Measure(availableSpace) as Size;
             var targetSize = Child?.Measure(availableSpace) as Size;
 
 
             if (targetSize == null)
             if (targetSize == null)
                 return;
                 return;
 
 
-            canvas.DrawExternalLink(Url, targetSize);
-            Child?.Draw(canvas, availableSpace);
+            Canvas.DrawExternalLink(Url, targetSize);
+            Child?.Draw(availableSpace);
         }
         }
     }
     }
 }
 }

+ 2 - 2
QuestPDF/Elements/Image.cs

@@ -18,12 +18,12 @@ namespace QuestPDF.Elements
             return new FullRender(availableSpace);
             return new FullRender(availableSpace);
         }
         }
 
 
-        internal override void Draw(ICanvas canvas, Size availableSpace)
+        internal override void Draw(Size availableSpace)
         {
         {
             if (InternalImage == null)
             if (InternalImage == null)
                 return;
                 return;
 
 
-            canvas.DrawImage(InternalImage, Position.Zero, availableSpace);
+            Canvas.DrawImage(InternalImage, Position.Zero, availableSpace);
         }
         }
     }
     }
 }
 }

+ 3 - 3
QuestPDF/Elements/InternalLink.cs

@@ -6,15 +6,15 @@ namespace QuestPDF.Elements
     {
     {
         public string LocationName { get; set; }
         public string LocationName { get; set; }
         
         
-        internal override void Draw(ICanvas canvas, Size availableSpace)
+        internal override void Draw(Size availableSpace)
         {
         {
             var targetSize = Child?.Measure(availableSpace) as Size;
             var targetSize = Child?.Measure(availableSpace) as Size;
 
 
             if (targetSize == null)
             if (targetSize == null)
                 return;
                 return;
 
 
-            canvas.DrawLocationLink(LocationName, targetSize);
-            Child?.Draw(canvas, availableSpace);
+            Canvas.DrawLocationLink(LocationName, targetSize);
+            Child?.Draw(availableSpace);
         }
         }
     }
     }
 }
 }

+ 17 - 4
QuestPDF/Elements/InternalLocation.cs

@@ -2,14 +2,27 @@
 
 
 namespace QuestPDF.Elements
 namespace QuestPDF.Elements
 {
 {
-    internal class InternalLocation : ContainerElement
+    internal class InternalLocation : ContainerElement, IStateResettable
     {
     {
         public string LocationName { get; set; }
         public string LocationName { get; set; }
+        private bool IsRendered { get; set; }
         
         
-        internal override void Draw(ICanvas canvas, Size availableSpace)
+        public void ResetState()
         {
         {
-            canvas.DrawLocation(LocationName);
-            Child?.Draw(canvas, availableSpace);
+            IsRendered = false;
+        }
+        
+        internal override void Draw(Size availableSpace)
+        {
+            if (!IsRendered)
+            {
+                PageContext.SetLocationPage(LocationName);
+                
+                Canvas.DrawLocation(LocationName);
+                IsRendered = true;
+            }
+            
+            Child?.Draw(availableSpace);
         }
         }
     }
     }
 }
 }

+ 10 - 3
QuestPDF/Elements/Layers.cs

@@ -1,4 +1,5 @@
-using System.Collections.Generic;
+using System;
+using System.Collections.Generic;
 using System.Linq;
 using System.Linq;
 using QuestPDF.Drawing.SpacePlan;
 using QuestPDF.Drawing.SpacePlan;
 using QuestPDF.Infrastructure;
 using QuestPDF.Infrastructure;
@@ -14,6 +15,12 @@ namespace QuestPDF.Elements
     {
     {
         public List<Layer> Children { get; set; } = new List<Layer>();
         public List<Layer> Children { get; set; } = new List<Layer>();
         
         
+        internal override void HandleVisitor(Action<Element?> visit)
+        {
+            Children.ForEach(x => x.HandleVisitor(visit));
+            base.HandleVisitor(visit);
+        }
+
         internal override ISpacePlan Measure(Size availableSpace)
         internal override ISpacePlan Measure(Size availableSpace)
         {
         {
             return Children
             return Children
@@ -21,12 +28,12 @@ namespace QuestPDF.Elements
                 .Measure(availableSpace);
                 .Measure(availableSpace);
         }
         }
 
 
-        internal override void Draw(ICanvas canvas, Size availableSpace)
+        internal override void Draw(Size availableSpace)
         {
         {
             Children
             Children
                 .Where(x => x.Measure(availableSpace) is Size)
                 .Where(x => x.Measure(availableSpace) is Size)
                 .ToList()
                 .ToList()
-                .ForEach(x => x.Draw(canvas, availableSpace));
+                .ForEach(x => x.Draw(availableSpace));
         }
         }
     }
     }
 }
 }

+ 4 - 4
QuestPDF/Elements/Padding.cs

@@ -39,16 +39,16 @@ namespace QuestPDF.Elements
             throw new NotSupportedException();
             throw new NotSupportedException();
         }
         }
 
 
-        internal override void Draw(ICanvas canvas, Size availableSpace)
+        internal override void Draw(Size availableSpace)
         {
         {
             if (Child == null)
             if (Child == null)
                 return;
                 return;
 
 
             var internalSpace = InternalSpace(availableSpace);
             var internalSpace = InternalSpace(availableSpace);
             
             
-            canvas.Translate(new Position(Left, Top));
-            Child?.Draw(canvas, internalSpace);
-            canvas.Translate(new Position(-Left, -Top));
+            Canvas.Translate(new Position(Left, Top));
+            Child?.Draw(internalSpace);
+            Canvas.Translate(new Position(-Left, -Top));
         }
         }
 
 
         private Size InternalSpace(Size availableSpace)
         private Size InternalSpace(Size availableSpace)

+ 28 - 6
QuestPDF/Elements/Page.cs

@@ -1,22 +1,44 @@
 using QuestPDF.Fluent;
 using QuestPDF.Fluent;
+using QuestPDF.Helpers;
 using QuestPDF.Infrastructure;
 using QuestPDF.Infrastructure;
 
 
 namespace QuestPDF.Elements
 namespace QuestPDF.Elements
 {
 {
     internal class Page : IComponent
     internal class Page : IComponent
     {
     {
+        public Size MinSize { get; set; } = PageSizes.A4;
+        public Size MaxSize { get; set; } = PageSizes.A4;
+
+        public float MarginLeft { get; set; }
+        public float MarginRight { get; set; }
+        public float MarginTop { get; set; }
+        public float MarginBottom { get; set; }
+
         public Element Header { get; set; } = Empty.Instance;
         public Element Header { get; set; } = Empty.Instance;
         public Element Content { get; set; } = Empty.Instance;
         public Element Content { get; set; } = Empty.Instance;
         public Element Footer { get; set; } = Empty.Instance;
         public Element Footer { get; set; } = Empty.Instance;
 
 
         public void Compose(IContainer container)
         public void Compose(IContainer container)
         {
         {
-            container.Decoration(decoration =>
-            {
-                decoration.Header().Element(Header);
-                decoration.Content().Extend().Element(Content);
-                decoration.Footer().Element(Footer);
-            });
+            container
+
+                .MinWidth(MinSize.Width)
+                .MinHeight(MinSize.Height)
+                
+                .MaxWidth(MaxSize.Width)
+                .MaxHeight(MaxSize.Height)
+     
+                .PaddingLeft(MarginLeft)
+                .PaddingRight(MarginRight)
+                .PaddingTop(MarginTop)
+                .PaddingBottom(MarginBottom)
+                
+                .Decoration(decoration =>
+                {
+                    decoration.Header().Element(Header);
+                    decoration.Content().Extend().Element(Content);
+                    decoration.Footer().Element(Footer);
+                });
         }
         }
     }
     }
 }
 }

+ 11 - 5
QuestPDF/Elements/PageBreak.cs

@@ -1,21 +1,27 @@
-using QuestPDF.Drawing.SpacePlan;
+using System;
+using QuestPDF.Drawing.SpacePlan;
 using QuestPDF.Infrastructure;
 using QuestPDF.Infrastructure;
 
 
 namespace QuestPDF.Elements
 namespace QuestPDF.Elements
 {
 {
-    internal class PageBreak : Element
+    internal class PageBreak : Element, IStateResettable
     {
     {
         private bool IsRendered { get; set; }
         private bool IsRendered { get; set; }
         
         
+        public void ResetState()
+        {
+            IsRendered = false;
+        }
+
         internal override ISpacePlan Measure(Size availableSpace)
         internal override ISpacePlan Measure(Size availableSpace)
         {
         {
             if (IsRendered)
             if (IsRendered)
                 return new FullRender(Size.Zero);
                 return new FullRender(Size.Zero);
-            
-            return new PartialRender(availableSpace.Width, availableSpace.Height);
+
+            return new PartialRender(Size.Zero);
         }
         }
 
 
-        internal override void Draw(ICanvas canvas, Size availableSpace)
+        internal override void Draw(Size availableSpace)
         {
         {
             IsRendered = true;
             IsRendered = true;
         }
         }

+ 26 - 17
QuestPDF/Elements/PageNumber.cs

@@ -1,3 +1,5 @@
+using System;
+using System.Text.RegularExpressions;
 using QuestPDF.Drawing.SpacePlan;
 using QuestPDF.Drawing.SpacePlan;
 using QuestPDF.Infrastructure;
 using QuestPDF.Infrastructure;
 using Size = QuestPDF.Infrastructure.Size;
 using Size = QuestPDF.Infrastructure.Size;
@@ -7,33 +9,40 @@ namespace QuestPDF.Elements
     internal class PageNumber : Element
     internal class PageNumber : Element
     {
     {
         public string TextFormat { get; set; } = "";
         public string TextFormat { get; set; } = "";
-        public TextStyle? TextStyle { get; set; }
-        private Text? TextElement { get; set; }
-        
-        private int Number { get; set; } = 1;
+        private Text TextElement { get; set; } = new Text();
 
 
-        internal override ISpacePlan Measure(Size availableSpace)
+        public TextStyle? TextStyle
         {
         {
-            InitializeTextElement();
+            get => TextElement?.Style;
+            set => TextElement.Style = value;
+        }
+
+        internal override void HandleVisitor(Action<Element?> visit)
+        {
+            TextElement.HandleVisitor(visit);
+            base.HandleVisitor(visit);
+        }
 
 
-            TextElement.Value = TextFormat.Replace("{number}", Number.ToString());
+        internal override ISpacePlan Measure(Size availableSpace)
+        {
+            TextElement.Value = Regex.Replace(TextFormat, @"{pdf:[ \w]+}", "123");
             return TextElement.Measure(availableSpace);
             return TextElement.Measure(availableSpace);
         }
         }
 
 
-        internal override void Draw(ICanvas canvas, Size availableSpace)
+        internal override void Draw(Size availableSpace)
         {
         {
-            InitializeTextElement();
-            
-            TextElement.Draw(canvas, availableSpace);
-            Number++;
+            TextElement.Value = GetText();
+            TextElement.Draw(availableSpace);
         }
         }
 
 
-        private void InitializeTextElement()
+        private string GetText()
         {
         {
-            TextElement ??= new Text()
-            {
-                Style = TextStyle
-            };
+            var result = TextFormat;
+            
+            foreach (var location in PageContext.GetRegisteredLocations())
+                result = result.Replace($"{{pdf:{location}}}", PageContext.GetLocationPage(location).ToString());
+
+            return result;
         }
         }
     }
     }
 }
 }

+ 3 - 3
QuestPDF/Elements/Rotate.cs

@@ -32,9 +32,9 @@ namespace QuestPDF.Elements
             throw new ArgumentException();
             throw new ArgumentException();
         }
         }
         
         
-        internal override void Draw(ICanvas canvas, Size availableSpace)
+        internal override void Draw(Size availableSpace)
         {
         {
-            var skiaCanvas = (canvas as Drawing.Canvas)?.SkiaCanvas;
+            var skiaCanvas = (Canvas as Drawing.SkiaCanvasBase)?.Canvas;
             
             
             if (skiaCanvas == null)
             if (skiaCanvas == null)
                 return;
                 return;
@@ -48,7 +48,7 @@ namespace QuestPDF.Elements
                 skiaCanvas.Translate(0, availableSpace.Height);
                 skiaCanvas.Translate(0, availableSpace.Height);
             
             
             skiaCanvas.RotateRadians(TurnCount * (float) Math.PI / 2f);
             skiaCanvas.RotateRadians(TurnCount * (float) Math.PI / 2f);
-            Child?.Draw(canvas, availableSpace);
+            Child?.Draw(availableSpace);
             skiaCanvas.SetMatrix(currentMatrix);
             skiaCanvas.SetMatrix(currentMatrix);
         }
         }
     }
     }

+ 28 - 11
QuestPDF/Elements/Row.cs

@@ -11,9 +11,9 @@ namespace QuestPDF.Elements
     {
     {
         public float Width { get; set; } = 1;
         public float Width { get; set; } = 1;
         
         
-        internal override void Draw(ICanvas canvas, Size availableSpace)
+        internal override void Draw(Size availableSpace)
         {
         {
-            Child?.Draw(canvas, availableSpace);
+            Child?.Draw(availableSpace);
         }
         }
     }
     }
     
     
@@ -38,6 +38,14 @@ namespace QuestPDF.Elements
         internal Element Left { get; set; }
         internal Element Left { get; set; }
         internal Element Right { get; set; }
         internal Element Right { get; set; }
 
 
+        internal override void HandleVisitor(Action<Element?> visit)
+        {
+            Left.HandleVisitor(visit);
+            Right.HandleVisitor(visit);
+            
+            base.HandleVisitor(visit);
+        }
+
         internal override ISpacePlan Measure(Size availableSpace)
         internal override ISpacePlan Measure(Size availableSpace)
         {
         {
             var leftMeasurement = Left.Measure(new Size(availableSpace.Width, availableSpace.Height)) as Size;
             var leftMeasurement = Left.Measure(new Size(availableSpace.Width, availableSpace.Height)) as Size;
@@ -61,16 +69,16 @@ namespace QuestPDF.Elements
             return new FullRender(targetSize);
             return new FullRender(targetSize);
         }
         }
 
 
-        internal override void Draw(ICanvas canvas, Size availableSpace)
+        internal override void Draw(Size availableSpace)
         {
         {
             var leftMeasurement = Left.Measure(new Size(availableSpace.Width, availableSpace.Height));
             var leftMeasurement = Left.Measure(new Size(availableSpace.Width, availableSpace.Height));
             var leftWidth = (leftMeasurement as Size)?.Width ?? 0;
             var leftWidth = (leftMeasurement as Size)?.Width ?? 0;
             
             
-            Left.Draw(canvas, new Size(leftWidth, availableSpace.Height));
+            Left.Draw(new Size(leftWidth, availableSpace.Height));
             
             
-            canvas.Translate(new Position(leftWidth, 0));
-            Right.Draw(canvas, new Size(availableSpace.Width - leftWidth, availableSpace.Height));
-            canvas.Translate(new Position(-leftWidth, 0));
+            Canvas.Translate(new Position(leftWidth, 0));
+            Right.Draw(new Size(availableSpace.Width - leftWidth, availableSpace.Height));
+            Canvas.Translate(new Position(-leftWidth, 0));
         }
         }
     }
     }
     
     
@@ -78,15 +86,21 @@ namespace QuestPDF.Elements
     {
     {
         public ICollection<RowElement> Children { get; internal set; } = new List<RowElement>();
         public ICollection<RowElement> Children { get; internal set; } = new List<RowElement>();
         public float Spacing { get; set; } = 0;
         public float Spacing { get; set; } = 0;
-        
+
+        internal override void HandleVisitor(Action<Element?> visit)
+        {
+            Children.ToList().ForEach(x => x.HandleVisitor(visit));
+            base.HandleVisitor(visit);
+        }
+
         internal override ISpacePlan Measure(Size availableSpace)
         internal override ISpacePlan Measure(Size availableSpace)
         {
         {
             return Compose(availableSpace.Width).Measure(availableSpace);
             return Compose(availableSpace.Width).Measure(availableSpace);
         }
         }
 
 
-        internal override void Draw(ICanvas canvas, Size availableSpace)
+        internal override void Draw(Size availableSpace)
         {
         {
-            Compose(availableSpace.Width).Draw(canvas, availableSpace);
+            Compose(availableSpace.Width).Draw(availableSpace);
         }
         }
         
         
         #region structure
         #region structure
@@ -95,7 +109,10 @@ namespace QuestPDF.Elements
         {
         {
             var elements = AddSpacing(Children, Spacing);
             var elements = AddSpacing(Children, Spacing);
             var rowElements = ReduceRows(elements, availableWidth);
             var rowElements = ReduceRows(elements, availableWidth);
-            return BuildTree(rowElements.ToArray());
+            var tree = BuildTree(rowElements.ToArray());
+            tree.HandleVisitor(x => x.Initialize(PageContext, Canvas));
+            
+            return tree;
         }
         }
         
         
         private static ICollection<Element> ReduceRows(ICollection<RowElement> elements, float availableWidth)
         private static ICollection<Element> ReduceRows(ICollection<RowElement> elements, float availableWidth)

+ 3 - 3
QuestPDF/Elements/Scale.cs

@@ -33,9 +33,9 @@ namespace QuestPDF.Elements
             throw new ArgumentException();
             throw new ArgumentException();
         }
         }
         
         
-        internal override void Draw(ICanvas canvas, Size availableSpace)
+        internal override void Draw(Size availableSpace)
         {
         {
-            var skiaCanvas = (canvas as Drawing.Canvas)?.SkiaCanvas;
+            var skiaCanvas = (Canvas as Drawing.SkiaCanvasBase)?.Canvas;
             
             
             if (skiaCanvas == null)
             if (skiaCanvas == null)
                 return;
                 return;
@@ -53,7 +53,7 @@ namespace QuestPDF.Elements
                 skiaCanvas.Translate(0, availableSpace.Height);
                 skiaCanvas.Translate(0, availableSpace.Height);
             
             
             skiaCanvas.Scale(ScaleX, ScaleY);
             skiaCanvas.Scale(ScaleX, ScaleY);
-            Child?.Draw(canvas, targetSpace);
+            Child?.Draw(targetSpace);
             skiaCanvas.SetMatrix(currentMatrix);
             skiaCanvas.SetMatrix(currentMatrix);
         }
         }
     }
     }

+ 9 - 4
QuestPDF/Elements/ShowOnce.cs

@@ -3,10 +3,15 @@ using QuestPDF.Infrastructure;
 
 
 namespace QuestPDF.Elements
 namespace QuestPDF.Elements
 {
 {
-    internal class ShowOnce : ContainerElement
+    internal class ShowOnce : ContainerElement, IStateResettable
     {
     {
         private bool IsRendered { get; set; }
         private bool IsRendered { get; set; }
-        
+
+        public void ResetState()
+        {
+            IsRendered = false;
+        }
+
         internal override ISpacePlan Measure(Size availableSpace)
         internal override ISpacePlan Measure(Size availableSpace)
         {
         {
             if (Child == null || IsRendered)
             if (Child == null || IsRendered)
@@ -15,7 +20,7 @@ namespace QuestPDF.Elements
             return Child.Measure(availableSpace);
             return Child.Measure(availableSpace);
         }
         }
 
 
-        internal override void Draw(ICanvas canvas, Size availableSpace)
+        internal override void Draw(Size availableSpace)
         {
         {
             if (Child == null || IsRendered)
             if (Child == null || IsRendered)
                 return;
                 return;
@@ -23,7 +28,7 @@ namespace QuestPDF.Elements
             if (Child.Measure(availableSpace) is FullRender)
             if (Child.Measure(availableSpace) is FullRender)
                 IsRendered = true;
                 IsRendered = true;
             
             
-            Child.Draw(canvas, availableSpace);
+            Child.Draw(availableSpace);
         }
         }
     }
     }
 }
 }

+ 19 - 6
QuestPDF/Elements/Stack.cs

@@ -10,13 +10,26 @@ using IContainer = QuestPDF.Infrastructure.IContainer;
 
 
 namespace QuestPDF.Elements
 namespace QuestPDF.Elements
 {
 {
-    internal class SimpleStack : Element
+    internal class SimpleStack : Element, IStateResettable
     {
     {
         internal Element First { get; set; } = Empty.Instance;
         internal Element First { get; set; } = Empty.Instance;
         internal Element Second { get; set; } = Empty.Instance;
         internal Element Second { get; set; } = Empty.Instance;
 
 
         internal bool IsFirstRendered { get; set; } = false;
         internal bool IsFirstRendered { get; set; } = false;
+
+        internal override void HandleVisitor(Action<Element?> visit)
+        {
+            First.HandleVisitor(visit);
+            Second.HandleVisitor(visit);
+            
+            base.HandleVisitor(visit);
+        }
         
         
+        public void ResetState()
+        {
+            IsFirstRendered = false;
+        }
+
         internal override ISpacePlan Measure(Size availableSpace)
         internal override ISpacePlan Measure(Size availableSpace)
         {
         {
             var firstElement = IsFirstRendered ? Empty.Instance : First;
             var firstElement = IsFirstRendered ? Empty.Instance : First;
@@ -44,7 +57,7 @@ namespace QuestPDF.Elements
             return new FullRender(targetSize);
             return new FullRender(targetSize);
         }
         }
 
 
-        internal override void Draw(ICanvas canvas, Size availableSpace)
+        internal override void Draw(Size availableSpace)
         {
         {
             var firstElement = IsFirstRendered ? Empty.Instance : First;
             var firstElement = IsFirstRendered ? Empty.Instance : First;
 
 
@@ -56,7 +69,7 @@ namespace QuestPDF.Elements
             var firstSize = firstMeasurement as Size;
             var firstSize = firstMeasurement as Size;
 
 
             if (firstSize != null)
             if (firstSize != null)
-                firstElement.Draw(canvas, new Size(availableSpace.Width, firstSize.Height));
+                firstElement.Draw(new Size(availableSpace.Width, firstSize.Height));
 
 
             if (firstMeasurement is Wrap || firstMeasurement is PartialRender)
             if (firstMeasurement is Wrap || firstMeasurement is PartialRender)
                 return;
                 return;
@@ -68,9 +81,9 @@ namespace QuestPDF.Elements
             if (secondMeasurement == null)
             if (secondMeasurement == null)
                 return;
                 return;
 
 
-            canvas.Translate(new Position(0, firstHeight));
-            Second.Draw(canvas, new Size(availableSpace.Width, secondMeasurement.Height));
-            canvas.Translate(new Position(0, -firstHeight));
+            Canvas.Translate(new Position(0, firstHeight));
+            Second.Draw(new Size(availableSpace.Width, secondMeasurement.Height));
+            Canvas.Translate(new Position(0, -firstHeight));
             
             
             if (secondMeasurement is FullRender)
             if (secondMeasurement is FullRender)
                 IsFirstRendered = false;
                 IsFirstRendered = false;

+ 4 - 4
QuestPDF/Elements/Text.cs

@@ -32,22 +32,22 @@ namespace QuestPDF.Elements
             return new FullRender(realWidth, realHeight);
             return new FullRender(realWidth, realHeight);
         }
         }
 
 
-        internal override void Draw(ICanvas canvas, Size availableSpace)
+        internal override void Draw(Size availableSpace)
         {
         {
             var lines = BreakLines(availableSpace.Width);
             var lines = BreakLines(availableSpace.Width);
             
             
             var offsetTop = 0f;
             var offsetTop = 0f;
             var offsetLeft = GetLeftOffset();
             var offsetLeft = GetLeftOffset();
 
 
-            canvas.Translate(new Position(0, Style.Size));
+            Canvas.Translate(new Position(0, Style.Size));
             
             
             foreach (var line in lines)
             foreach (var line in lines)
             {
             {
-                canvas.DrawText(line, new Position(offsetLeft, offsetTop), Style);
+                Canvas.DrawText(line, new Position(offsetLeft, offsetTop), Style);
                 offsetTop += LineHeight;
                 offsetTop += LineHeight;
             }
             }
             
             
-            canvas.Translate(new Position(0, -Style.Size));
+            Canvas.Translate(new Position(0, -Style.Size));
 
 
             float GetLeftOffset()
             float GetLeftOffset()
             {
             {

+ 3 - 3
QuestPDF/Elements/Translate.cs

@@ -8,15 +8,15 @@ namespace QuestPDF.Elements
         public float TranslateX { get; set; } = 1;
         public float TranslateX { get; set; } = 1;
         public float TranslateY { get; set; } = 1;
         public float TranslateY { get; set; } = 1;
 
 
-        internal override void Draw(ICanvas canvas, Size availableSpace)
+        internal override void Draw(Size availableSpace)
         {
         {
-            var skiaCanvas = (canvas as Drawing.Canvas)?.SkiaCanvas;
+            var skiaCanvas = (Canvas as Drawing.SkiaCanvasBase)?.Canvas;
             
             
             if (skiaCanvas == null)
             if (skiaCanvas == null)
                 return;
                 return;
             
             
             skiaCanvas.Translate(TranslateX, TranslateY);
             skiaCanvas.Translate(TranslateX, TranslateY);
-            base.Draw(canvas, availableSpace);
+            base.Draw(availableSpace);
             skiaCanvas.Translate(-TranslateX, -TranslateY);
             skiaCanvas.Translate(-TranslateX, -TranslateY);
         }
         }
     }
     }

+ 1 - 1
QuestPDF/Fluent/ElementExtensions.cs

@@ -41,7 +41,7 @@ namespace QuestPDF.Fluent
             return handler(parent.Container()).Container();
             return handler(parent.Container()).Container();
         }
         }
 
 
-        public static void PageNumber(this IContainer element, string textFormat = "{number}", TextStyle? style = null)
+        public static void PageNumber(this IContainer element, string textFormat = "{pdf:currentPage} / {pdf:totalPages}", TextStyle? style = null)
         {
         {
             element.Element(new PageNumber
             element.Element(new PageNumber
             {
             {

+ 57 - 2
QuestPDF/Fluent/PageExtensions.cs

@@ -1,5 +1,7 @@
 using System;
 using System;
+using QuestPDF.Drawing;
 using QuestPDF.Elements;
 using QuestPDF.Elements;
+using QuestPDF.Helpers;
 using QuestPDF.Infrastructure;
 using QuestPDF.Infrastructure;
 
 
 namespace QuestPDF.Fluent
 namespace QuestPDF.Fluent
@@ -8,6 +10,56 @@ namespace QuestPDF.Fluent
     {
     {
         internal Page Page { get; } = new Page();
         internal Page Page { get; } = new Page();
 
 
+        public void Size(PageSize pageSize)
+        {
+            Page.MinSize = pageSize;
+            Page.MaxSize = pageSize;
+        }
+
+        public void ContinuousSize(float width)
+        {
+            Page.MinSize = new PageSize(width, 0);
+            Page.MaxSize = new PageSize(width, Infrastructure.Size.Max.Height);
+        }
+
+        public void MarginLeft(float value)
+        {
+            Page.MarginLeft = value;
+        }
+        
+        public void MarginRight(float value)
+        {
+            Page.MarginRight = value;
+        }
+        
+        public void MarginTop(float value)
+        {
+            Page.MarginTop = value;
+        }
+        
+        public void MarginBottom(float value)
+        {
+            Page.MarginBottom = value;
+        }
+        
+        public void MarginVertical(float value)
+        {
+            MarginTop(value);
+            MarginBottom(value);
+        }
+        
+        public void MarginHorizontal(float value)
+        {
+            MarginLeft(value);
+            MarginRight(value);
+        }
+        
+        public void Margin(float value)
+        {
+            MarginVertical(value);
+            MarginHorizontal(value);
+        }
+        
         public IContainer Header()
         public IContainer Header()
         {
         {
             var container = new Container();
             var container = new Container();
@@ -32,11 +84,14 @@ namespace QuestPDF.Fluent
     
     
     public static class PageExtensions
     public static class PageExtensions
     {
     {
-        public static void Page(this IContainer document, Action<PageDescriptor> handler)
+        public static IDocumentContainer Page(this IDocumentContainer document, Action<PageDescriptor> handler)
         {
         {
             var descriptor = new PageDescriptor();
             var descriptor = new PageDescriptor();
             handler(descriptor);
             handler(descriptor);
-            document.Component(descriptor.Page);
+            
+            (document as DocumentContainer).Pages.Add(descriptor.Page);
+            
+            return document;
         }
         }
     }
     }
 }
 }

+ 36 - 36
QuestPDF/Helpers/PageSizes.cs

@@ -15,46 +15,46 @@ namespace QuestPDF.Helpers
     {
     {
         public const int PointsPerInch = 72;
         public const int PointsPerInch = 72;
 
 
-        public static Size A0 => new PageSize(2384.2f, 3370.8f);
-        public static Size A1 => new PageSize(1684, 2384.2f);
-        public static Size A2 => new PageSize(1190.7f, 1684);
-        public static Size A3 => new PageSize(842, 1190.7f);
-        public static Size A4 => new PageSize(595.4f, 842);
-        public static Size A5 => new PageSize(419.6f, 595.4f);
-        public static Size A6 => new PageSize(297.7f, 419.6f);
-        public static Size A7 => new PageSize(209.8f, 297.7f);
-        public static Size A8 => new PageSize(147.4f, 209.8f);
-        public static Size A9 => new PageSize(104.9f, 147.4f);
-        public static Size A10 => new PageSize(73.7f, 104.9f);
+        public static PageSize A0 => new PageSize(2384.2f, 3370.8f);
+        public static PageSize A1 => new PageSize(1684, 2384.2f);
+        public static PageSize A2 => new PageSize(1190.7f, 1684);
+        public static PageSize A3 => new PageSize(842, 1190.7f);
+        public static PageSize A4 => new PageSize(595.4f, 842);
+        public static PageSize A5 => new PageSize(419.6f, 595.4f);
+        public static PageSize A6 => new PageSize(297.7f, 419.6f);
+        public static PageSize A7 => new PageSize(209.8f, 297.7f);
+        public static PageSize A8 => new PageSize(147.4f, 209.8f);
+        public static PageSize A9 => new PageSize(104.9f, 147.4f);
+        public static PageSize A10 => new PageSize(73.7f, 104.9f);
 
 
-        public static Size B0 => new PageSize(2835, 4008.7f);
-        public static Size B1 => new PageSize(2004.3f, 2835);
-        public static Size B2 => new PageSize(1417.5f, 2004.3f);
-        public static Size B3 => new PageSize(1000.8f, 1417.5f);
-        public static Size B4 => new PageSize(708.8f, 1000.8f);
-        public static Size B5 => new PageSize(499, 708.8f);
-        public static Size B6 => new PageSize(354.4f, 499);
-        public static Size B7 => new PageSize(249.5f, 354.4f);
-        public static Size B8 => new PageSize(175.8f, 249.5f);
-        public static Size B9 => new PageSize(124.7f, 175.8f);
-        public static Size B10 => new PageSize(87.9f, 124.7f);
+        public static PageSize B0 => new PageSize(2835, 4008.7f);
+        public static PageSize B1 => new PageSize(2004.3f, 2835);
+        public static PageSize B2 => new PageSize(1417.5f, 2004.3f);
+        public static PageSize B3 => new PageSize(1000.8f, 1417.5f);
+        public static PageSize B4 => new PageSize(708.8f, 1000.8f);
+        public static PageSize B5 => new PageSize(499, 708.8f);
+        public static PageSize B6 => new PageSize(354.4f, 499);
+        public static PageSize B7 => new PageSize(249.5f, 354.4f);
+        public static PageSize B8 => new PageSize(175.8f, 249.5f);
+        public static PageSize B9 => new PageSize(124.7f, 175.8f);
+        public static PageSize B10 => new PageSize(87.9f, 124.7f);
 
 
-        public static Size Env10 => new PageSize(683.2f, 294.8f);
-        public static Size EnvC4 => new PageSize(649.2f, 918.5f);
-        public static Size EnvDL => new PageSize(311.9f, 623.7f);
+        public static PageSize Env10 => new PageSize(683.2f, 294.8f);
+        public static PageSize EnvC4 => new PageSize(649.2f, 918.5f);
+        public static PageSize EnvDL => new PageSize(311.9f, 623.7f);
 
 
-        public static Size Executive => new PageSize(522, 756);
-        public static Size Legal => new PageSize(612.4f, 1009.3f);
-        public static Size Letter => new PageSize(612.4f, 791);
+        public static PageSize Executive => new PageSize(522, 756);
+        public static PageSize Legal => new PageSize(612.4f, 1009.3f);
+        public static PageSize Letter => new PageSize(612.4f, 791);
 
 
-        public static Size ARCH_A => new PageSize(649.2f, 864.7f);
-        public static Size ARCH_B => new PageSize(864.7f, 1295.6f);
-        public static Size ARCH_C => new PageSize(1295.6f, 1729.3f);
-        public static Size ARCH_D => new PageSize(1729.3f, 2591.2f);
-        public static Size ARCH_E => new PageSize(2591.2f, 3455.9f);
-        public static Size ARCH_E1 => new PageSize(2160.3f, 3024.9f);
-        public static Size ARCH_E2 => new PageSize(1871.1f, 2735.8f);
-        public static Size ARCH_E3 => new PageSize(1944.8f, 2809.5f);
+        public static PageSize ARCH_A => new PageSize(649.2f, 864.7f);
+        public static PageSize ARCH_B => new PageSize(864.7f, 1295.6f);
+        public static PageSize ARCH_C => new PageSize(1295.6f, 1729.3f);
+        public static PageSize ARCH_D => new PageSize(1729.3f, 2591.2f);
+        public static PageSize ARCH_E => new PageSize(2591.2f, 3455.9f);
+        public static PageSize ARCH_E1 => new PageSize(2160.3f, 3024.9f);
+        public static PageSize ARCH_E2 => new PageSize(1871.1f, 2735.8f);
+        public static PageSize ARCH_E3 => new PageSize(1944.8f, 2809.5f);
     }
     }
 
 
     public static class PageSizeExtensions
     public static class PageSizeExtensions

+ 10 - 3
QuestPDF/Infrastructure/ContainerElement.cs

@@ -1,4 +1,5 @@
-using QuestPDF.Drawing.SpacePlan;
+using System;
+using QuestPDF.Drawing.SpacePlan;
 using QuestPDF.Elements;
 using QuestPDF.Elements;
 
 
 namespace QuestPDF.Infrastructure
 namespace QuestPDF.Infrastructure
@@ -13,14 +14,20 @@ namespace QuestPDF.Infrastructure
             set => Child = value as Element;
             set => Child = value as Element;
         }
         }
 
 
+        internal override void HandleVisitor(Action<Element?> visit)
+        {
+            base.HandleVisitor(visit);
+            Child.HandleVisitor(visit);
+        }
+
         internal override ISpacePlan Measure(Size availableSpace)
         internal override ISpacePlan Measure(Size availableSpace)
         {
         {
             return Child?.Measure(availableSpace) ?? new FullRender(Size.Zero);
             return Child?.Measure(availableSpace) ?? new FullRender(Size.Zero);
         }
         }
         
         
-        internal override void Draw(ICanvas canvas, Size availableSpace)
+        internal override void Draw(Size availableSpace)
         {
         {
-            Child?.Draw(canvas, availableSpace);
+            Child?.Draw(availableSpace);
         }
         }
     }
     }
 }
 }

+ 17 - 1
QuestPDF/Infrastructure/Element.cs

@@ -1,10 +1,26 @@
+using System;
 using QuestPDF.Drawing.SpacePlan;
 using QuestPDF.Drawing.SpacePlan;
+using QuestPDF.Elements;
 
 
 namespace QuestPDF.Infrastructure
 namespace QuestPDF.Infrastructure
 {
 {
     internal abstract class Element : IElement
     internal abstract class Element : IElement
     {
     {
+        internal IPageContext PageContext { get; set; }
+        internal ICanvas Canvas { get; set; }
+        
+        internal virtual void HandleVisitor(Action<Element?> visit)
+        {
+            visit(this);
+        }
+
+        internal void Initialize(IPageContext pageContext, ICanvas canvas)
+        {
+            PageContext = pageContext;
+            Canvas = canvas;
+        }
+        
         internal abstract ISpacePlan Measure(Size availableSpace);
         internal abstract ISpacePlan Measure(Size availableSpace);
-        internal abstract void Draw(ICanvas canvas, Size availableSpace);
+        internal abstract void Draw(Size availableSpace);
     }
     }
 }
 }

+ 1 - 0
QuestPDF/Infrastructure/ICanvas.cs

@@ -1,3 +1,4 @@
+using System.Collections.Generic;
 using SkiaSharp;
 using SkiaSharp;
 
 
 namespace QuestPDF.Infrastructure
 namespace QuestPDF.Infrastructure

+ 1 - 1
QuestPDF/Infrastructure/IDocument.cs

@@ -5,6 +5,6 @@ namespace QuestPDF.Infrastructure
     public interface IDocument
     public interface IDocument
     {
     {
         DocumentMetadata GetMetadata();
         DocumentMetadata GetMetadata();
-        void Compose(IContainer container);
+        void Compose(IDocumentContainer container);
     }
     }
 }
 }

+ 7 - 0
QuestPDF/Infrastructure/IDocumentContainer.cs

@@ -0,0 +1,7 @@
+namespace QuestPDF.Infrastructure
+{
+    public interface IDocumentContainer
+    {
+                
+    }
+}

+ 11 - 0
QuestPDF/Infrastructure/IPageContext.cs

@@ -0,0 +1,11 @@
+using System.Collections.Generic;
+
+namespace QuestPDF.Infrastructure
+{
+    public interface IPageContext
+    {
+        void SetLocationPage(string key);
+        int GetLocationPage(string key);
+        ICollection<string> GetRegisteredLocations();
+    }
+}

+ 11 - 0
QuestPDF/Infrastructure/IRenderingCanvas.cs

@@ -0,0 +1,11 @@
+namespace QuestPDF.Infrastructure
+{
+    public interface IRenderingCanvas
+    {
+        void BeginDocument();
+        void EndDocument();
+        
+        void BeginPage(Size size);
+        void EndPage();
+    }
+}

+ 7 - 0
QuestPDF/Infrastructure/IStateResettable.cs

@@ -0,0 +1,7 @@
+namespace QuestPDF.Infrastructure
+{
+    internal interface IStateResettable
+    {
+        void ResetState();
+    }
+}

+ 45 - 0
QuestPDF/Infrastructure/PageContext.cs

@@ -0,0 +1,45 @@
+using System;
+using System.Collections.Generic;
+using QuestPDF.Elements;
+
+namespace QuestPDF.Infrastructure
+{
+    public class PageContext : IPageContext
+    {
+        public const string CurrentPageSlot = "currentPage";
+        public const string TotalPagesSlot = "totalPages";
+        
+        private Dictionary<string, int> Locations { get; } = new Dictionary<string, int>();
+        private int PageNumber { get; set; }
+
+        internal void SetPageNumber(int number)
+        {
+            PageNumber = number;
+            Locations[CurrentPageSlot] = number;
+
+            if (!Locations.ContainsKey(TotalPagesSlot) || Locations[TotalPagesSlot] < number)
+                Locations[TotalPagesSlot] = number;
+        }
+        
+        public void SetLocationPage(string key)
+        {
+            if (Locations.ContainsKey(key))
+                return;
+            
+            Locations[key] = PageNumber;
+        }
+
+        public int GetLocationPage(string key)
+        {
+            if (!Locations.ContainsKey(key))
+                throw new ArgumentException($"The location '{key}' does not exists.");
+            
+            return Locations[key];
+        }
+
+        public ICollection<string> GetRegisteredLocations()
+        {
+            return Locations.Keys;
+        }
+    }
+}

+ 1 - 0
QuestPDF/Infrastructure/Size.cs

@@ -8,6 +8,7 @@
         public float Height { get; }
         public float Height { get; }
         
         
         public static Size Zero => new Size(0, 0);
         public static Size Zero => new Size(0, 0);
+        public static Size Max => new Size(14_400, 14_400);
 
 
         public Size(float width, float height)
         public Size(float width, float height)
         {
         {