فهرست منبع

Fixed rendering merged documents

MarcinZiabek 2 سال پیش
والد
کامیت
12e21f24a3

+ 39 - 23
Source/QuestPDF.Examples/MergedDocumentExamples.cs

@@ -1,7 +1,9 @@
-using NUnit.Framework;
+using System.Linq;
+using NUnit.Framework;
 using QuestPDF.Examples.Engine;
 using QuestPDF.Fluent;
 using QuestPDF.Helpers;
+using QuestPDF.Infrastructure;
 
 namespace QuestPDF.Examples
 {
@@ -11,10 +13,11 @@ namespace QuestPDF.Examples
         [Test]
         public void Merge_ContinuousPageNumbers()
         {
-            var mergedDocument = Document.Merge(
-                    CreateDocument("Document 1"),
-                    CreateDocument("Document 2"),
-                    CreateDocument("Document 3"))
+            var mergedDocument = Document
+                .Merge(
+                    GenerateReport("Short Document 1", 8),
+                    GenerateReport("Medium Document 2", 16),
+                    GenerateReport("Long Document 3", 24))
                 .UseContinuousPageNumbers();
 
             RenderingTest
@@ -27,10 +30,11 @@ namespace QuestPDF.Examples
         [Test]
         public void Merge_SeparatePageNumbers()
         {
-            var mergedDocument = Document.Merge(
-                    CreateDocument("Document 1"),
-                    CreateDocument("Document 2"),
-                    CreateDocument("Document 3"))
+            var mergedDocument = Document
+                .Merge(
+                    GenerateReport("Short Document 1", 8),
+                    GenerateReport("Medium Document 2", 16),
+                    GenerateReport("Long Document 3", 24))
                 .UseOriginalPageNumbers();
 
             RenderingTest
@@ -40,28 +44,38 @@ namespace QuestPDF.Examples
                 .Render(mergedDocument);
         }
 
-        private static Document CreateDocument(string content)
+        private static Document GenerateReport(string title, int itemsCount)
         {
             return Document.Create(document =>
             {
                 document.Page(page =>
                 {
+                    page.Margin(1, Unit.Inch);
+                    
+                    page.Header()
+                        .Text(title)
+                        .Bold()
+                        .FontSize(24)
+                        .FontColor(Colors.Blue.Accent1);
+                    
                     page.Content()
-                        .AlignMiddle()
-                        .AlignCenter()
+                        .PaddingVertical(20)
                         .Column(column =>
                         {
-                            column.Item().Text(content).FontSize(40);
-                            
-                            column.Item().PageBreak();
-                            
-                            column.Item().Text(content).FontSize(40);
-                            column.Item().AlignCenter().SectionLink("next").Text("Next page").FontSize(16).Underline().FontColor(Colors.Blue.Medium);
-                            
-                            column.Item().PageBreak();
-                            
-                            column.Item().Text(content).FontSize(40);
-                            column.Item().AlignCenter().Section("next").Text("Next page").FontSize(16).FontColor(Colors.Green.Medium);
+                            column.Spacing(10);
+
+                            foreach (var i in Enumerable.Range(0, itemsCount))
+                            {
+                                column
+                                    .Item()
+                                    .Width(200)
+                                    .Height(50)
+                                    .Background(Colors.Grey.Lighten3)
+                                    .AlignMiddle()
+                                    .AlignCenter()
+                                    .Text($"Item {i}")
+                                    .FontSize(16);
+                            }
                         });
                     
                     page.Footer()
@@ -69,6 +83,8 @@ namespace QuestPDF.Examples
                         .PaddingVertical(20)
                         .Text(text =>
                         {
+                            text.DefaultTextStyle(TextStyle.Default.FontSize(16));
+                            
                             text.CurrentPageNumber();
                             text.Span(" / ");
                             text.TotalPages();

+ 80 - 0
Source/QuestPDF.Examples/MergedDocumentSectionLinksTests.cs

@@ -0,0 +1,80 @@
+using NUnit.Framework;
+using QuestPDF.Examples.Engine;
+using QuestPDF.Fluent;
+using QuestPDF.Helpers;
+
+namespace QuestPDF.Examples
+{
+    [TestFixture]
+    public class MergedDocumentSectionLinksTests
+    {
+        [Test]
+        public void Merge_ContinuousPageNumbers()
+        {
+            var mergedDocument = Document.Merge(
+                    CreateDocument("Document 1"),
+                    CreateDocument("Document 2"),
+                    CreateDocument("Document 3"))
+                .UseContinuousPageNumbers();
+
+            RenderingTest
+                .Create()
+                .ProducePdf()
+                .ShowResults()
+                .Render(mergedDocument);
+        }
+
+        [Test]
+        public void Merge_SeparatePageNumbers()
+        {
+            var mergedDocument = Document.Merge(
+                    CreateDocument("Document 1"),
+                    CreateDocument("Document 2"),
+                    CreateDocument("Document 3"))
+                .UseOriginalPageNumbers();
+
+            RenderingTest
+                .Create()
+                .ProducePdf()
+                .ShowResults()
+                .Render(mergedDocument);
+        }
+
+        private static Document CreateDocument(string content)
+        {
+            return Document.Create(document =>
+            {
+                document.Page(page =>
+                {
+                    page.Content()
+                        .AlignMiddle()
+                        .AlignCenter()
+                        .Column(column =>
+                        {
+                            column.Item().Text(content).FontSize(40);
+                            
+                            column.Item().PageBreak();
+                            
+                            column.Item().Text(content).FontSize(40);
+                            column.Item().AlignCenter().SectionLink("next").Text("Next page").FontSize(16).Underline().FontColor(Colors.Blue.Medium);
+                            
+                            column.Item().PageBreak();
+                            
+                            column.Item().Text(content).FontSize(40);
+                            column.Item().AlignCenter().Section("next").Text("Next page").FontSize(16).FontColor(Colors.Green.Medium);
+                        });
+                    
+                    page.Footer()
+                        .AlignCenter()
+                        .PaddingVertical(20)
+                        .Text(text =>
+                        {
+                            text.CurrentPageNumber();
+                            text.Span(" / ");
+                            text.TotalPages();
+                        });
+                });
+            });
+        }
+    }
+}

+ 72 - 37
Source/QuestPDF/Drawing/DocumentGenerator.cs

@@ -94,60 +94,98 @@ namespace QuestPDF.Drawing
             return canvas.Pictures;
         }
         
-        internal static void RenderDocument<TCanvas>(TCanvas canvas, IDocument document, DocumentSettings settings) where TCanvas : ICanvas, IRenderingCanvas
+        private static void RenderDocument<TCanvas>(TCanvas canvas, IDocument document, DocumentSettings settings) where TCanvas : ICanvas, IRenderingCanvas
         {
             canvas.BeginDocument();
             
             if (document is MergedDocument mergedDocument)
+                RenderMergedDocument(canvas, mergedDocument, settings);
+            
+            else
+                RenderSingleDocument(canvas, document, settings);
+            
+            canvas.EndDocument();
+        }
+
+        private static void RenderSingleDocument<TCanvas>(TCanvas canvas, IDocument document, DocumentSettings settings)
+            where TCanvas : ICanvas, IRenderingCanvas
+        {
+            var debuggingState = new DebuggingState();
+            var useOriginalImages = canvas is ImageCanvas;
+
+            var content = ConfigureContent(document, settings, debuggingState, 0, useOriginalImages);
+
+            var pageContext = new PageContext();
+            RenderPass(pageContext, new FreeCanvas(), content, debuggingState);
+            pageContext.ResetPageNumber();
+            RenderPass(pageContext, canvas, content, debuggingState);
+        }
+        
+        private static void RenderMergedDocument<TCanvas>(TCanvas canvas, MergedDocument document, DocumentSettings settings)
+            where TCanvas : ICanvas, IRenderingCanvas
+        {
+            var debuggingState = new DebuggingState();
+            var useOriginalImages = canvas is ImageCanvas;
+            
+            var documentContents = Enumerable
+                .Range(0, document.Documents.Count)
+                .Select(index => ConfigureContent(document.Documents[index], settings, debuggingState, index, useOriginalImages))
+                .ToList();
+
+            if (document.PageNumberStrategy == MergedDocumentPageNumberStrategy.Continuous)
             {
-                var pageContext = mergedDocument.PageNumberStrategy == MergedDocumentPageNumberStrategy.Continuous ? new PageContext() : null;
+                var documentPageContext = new PageContext();
+                
+                foreach (var content in documentContents)
+                    RenderPass(documentPageContext, new FreeCanvas(), content, debuggingState);
+                
+                documentPageContext.ResetPageNumber();
                 
-                for (var documentId = 0; documentId < mergedDocument.Documents.Count; documentId++) 
-                    RenderDocumentFragment(canvas, pageContext, mergedDocument.Documents[documentId], settings, documentId);
+                foreach (var content in documentContents)
+                    RenderPass(documentPageContext, canvas, content, debuggingState);
             }
             else
             {
-                RenderDocumentFragment(canvas, null, document, settings);
+                foreach (var content in documentContents)
+                {
+                    var pageContext = new PageContext();
+                    RenderPass(pageContext, new FreeCanvas(), content, debuggingState);
+                    pageContext.ResetPageNumber();
+                    RenderPass(pageContext, canvas, content, debuggingState);
+                }
             }
-            
-            canvas.EndDocument();
         }
-        
-        internal static void RenderDocumentFragment<TCanvas>(TCanvas canvas, PageContext? pageContext, IDocument document, DocumentSettings settings, int documentId = 0)
-            where TCanvas : ICanvas, IRenderingCanvas
+
+        private static Container ConfigureContent(IDocument document, DocumentSettings settings, DebuggingState debuggingState, int documentIndex, bool useOriginalImages)
         {
             var container = new DocumentContainer();
             document.Compose(container);
             
             var content = container.Compose();
-            var useOriginalImages = canvas is ImageCanvas;
-
-            ApplyDocumentId(content, documentId);
-            ApplyInheritedAndGlobalTexStyle(content, TextStyle.Default);
-            ApplyContentDirection(content, settings.ContentDirection);
-            ApplyDefaultImageConfiguration(content, settings.ImageRasterDpi, settings.ImageCompressionQuality, useOriginalImages);
-            
-            var debuggingState = Settings.EnableDebugging ? ApplyDebugging(content) : null;
             
+            content.ApplyDocumentId(documentIndex);
+            content.ApplyInheritedAndGlobalTexStyle(TextStyle.Default);
+            content.ApplyContentDirection(settings.ContentDirection);
+            content.ApplyDefaultImageConfiguration(settings.ImageRasterDpi, settings.ImageCompressionQuality, useOriginalImages);
+                    
             if (Settings.EnableCaching)
-                ApplyCaching(content);
+                content.ApplyCaching();
 
-            pageContext ??= new PageContext();
-            RenderPass(pageContext, new FreeCanvas(), content, debuggingState);
-            RenderPass(pageContext, canvas, content, debuggingState);
+            if (Settings.EnableDebugging)
+                content.ApplyDebugging(debuggingState);
+
+            return content;
         }
-        
-        internal static void RenderPass<TCanvas>(PageContext pageContext, TCanvas canvas, Container content, DebuggingState? debuggingState)
+
+        private static void RenderPass<TCanvas>(PageContext pageContext, TCanvas canvas, Container content, DebuggingState? debuggingState)
             where TCanvas : ICanvas, IRenderingCanvas
         {
             InjectDependencies(content, pageContext, canvas);
             content.VisitChildren(x => (x as IStateResettable)?.ResetState());
-
-            var currentPage = 1;
             
             while(true)
             {
-                pageContext.SetPageNumber(currentPage);
+                pageContext.IncrementPageNumber();
                 debuggingState?.Reset();
                 
                 var spacePlan = content.Measure(Size.Max);
@@ -171,7 +209,7 @@ namespace QuestPDF.Drawing
 
                 canvas.EndPage();
 
-                if (currentPage >= Settings.DocumentLayoutExceptionThreshold)
+                if (pageContext.CurrentPage >= Settings.DocumentLayoutExceptionThreshold)
                 {
                     canvas.EndDocument();
                     ThrowLayoutException();
@@ -179,8 +217,6 @@ namespace QuestPDF.Drawing
                 
                 if (spacePlan.Type == SpacePlanType.FullRender)
                     break;
-
-                currentPage++;
             }
 
             void ThrowLayoutException()
@@ -209,7 +245,7 @@ namespace QuestPDF.Drawing
             });
         }
 
-        private static void ApplyCaching(Container content)
+        private static void ApplyCaching(this Container content)
         {
             content.VisitChildren(x =>
             {
@@ -218,16 +254,15 @@ namespace QuestPDF.Drawing
             });
         }
 
-        private static DebuggingState ApplyDebugging(Container content)
+        private static void ApplyDebugging(this Container content, DebuggingState? debuggingState)
         {
-            var debuggingState = new DebuggingState();
-
+            if (debuggingState == null)
+                return;
+            
             content.VisitChildren(x =>
             {
                 x.CreateProxy(y => new DebuggingProxy(debuggingState, y));
             });
-
-            return debuggingState;
         }
         
         internal static void ApplyContentDirection(this Element? content, ContentDirection direction)
@@ -316,7 +351,7 @@ namespace QuestPDF.Drawing
         /// This method is important when merging multiple documents together.
         /// Applying unique document Id to all section names and associated links, prevents from name collisions.
         /// </summary>
-        internal static void ApplyDocumentId(this Element? content, int documentId)
+        internal static void ApplyDocumentId(this Element? content, int documentId = 0)
         {
             content.VisitChildren(x =>
             {

+ 8 - 3
Source/QuestPDF/Infrastructure/PageContext.cs

@@ -11,12 +11,17 @@ namespace QuestPDF.Infrastructure
         private List<DocumentLocation> Locations { get; } = new();
         public int CurrentPage { get; private set; }
 
-        internal void SetPageNumber(int number)
+        internal void ResetPageNumber()
         {
-            CurrentPage = number;
-            SetSectionPage(DocumentLocation);
+            CurrentPage = 0;
         }
         
+        internal void IncrementPageNumber()
+        {
+            CurrentPage++;
+            SetSectionPage(DocumentLocation);
+        }
+
         public void SetSectionPage(string name)
         {
             var location = GetLocation(name);