Browse Source

Fixed: section links work correctly in merged documents

MarcinZiabek 2 years ago
parent
commit
9f69f0ab26

+ 30 - 15
Source/QuestPDF.Examples/MergedDocumentExamples.cs

@@ -1,6 +1,7 @@
 using NUnit.Framework;
 using NUnit.Framework;
 using QuestPDF.Examples.Engine;
 using QuestPDF.Examples.Engine;
 using QuestPDF.Fluent;
 using QuestPDF.Fluent;
+using QuestPDF.Helpers;
 
 
 namespace QuestPDF.Examples
 namespace QuestPDF.Examples
 {
 {
@@ -39,25 +40,39 @@ namespace QuestPDF.Examples
                 .Render(mergedDocument);
                 .Render(mergedDocument);
         }
         }
 
 
-
         private static Document CreateDocument(string content)
         private static Document CreateDocument(string content)
         {
         {
-            return Document.Create(d =>
+            return Document.Create(document =>
             {
             {
-                d.Page(p =>
+                document.Page(page =>
                 {
                 {
-                    p.Content().AlignMiddle().AlignCenter().Column(c =>
-                    {
-                        c.Item().Text(content).FontSize(40);
-                        c.Item().PageBreak();
-                        c.Item().Text(content).FontSize(40);
-                    });
-                    p.Footer().AlignCenter().PaddingVertical(20).Text(t =>
-                    {
-                        t.CurrentPageNumber();
-                        t.Span(" / ");
-                        t.TotalPages();
-                    });
+                    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();
+                        });
                 });
                 });
             });
             });
         }
         }

+ 33 - 9
Source/QuestPDF/Drawing/DocumentGenerator.cs

@@ -97,30 +97,35 @@ namespace QuestPDF.Drawing
         internal static void RenderDocument<TCanvas>(TCanvas canvas, IDocument document, DocumentSettings settings, bool useOriginalImages = false)
         internal static void RenderDocument<TCanvas>(TCanvas canvas, IDocument document, DocumentSettings settings, bool useOriginalImages = false)
             where TCanvas : ICanvas, IRenderingCanvas
             where TCanvas : ICanvas, IRenderingCanvas
         {
         {
-            if (document is MergedDocument { PageNumberHandling: MergedDocumentPageNumberHandling.Separate } mergedDocument)
+            if (document is MergedDocument mergedDocument)
             {
             {
-                canvas.BeginDocument();
-                
-                foreach (var singleDocument in mergedDocument.Documents)
-                    RenderDocumentImpl(canvas, singleDocument, settings, useOriginalImages);
+                var pageContext = mergedDocument.PageNumberHandling == MergedDocumentPageNumberHandling.Continuous
+                    ? new PageContext()
+                    : null;
                 
                 
+                canvas.BeginDocument();
+
+                for (var documentId = 0; documentId < mergedDocument.Documents.Count; documentId++) 
+                    RenderDocumentImplementation(canvas, pageContext, mergedDocument.Documents[documentId], settings, useOriginalImages, documentId);
+
                 canvas.EndDocument();
                 canvas.EndDocument();
                 
                 
                 return;
                 return;
             }
             }
 
 
             canvas.BeginDocument();
             canvas.BeginDocument();
-            RenderDocumentImpl(canvas, document, settings, useOriginalImages);
+            RenderDocumentImplementation(canvas, null, document, settings, useOriginalImages);
             canvas.EndDocument();
             canvas.EndDocument();
         }
         }
         
         
-        internal static void RenderDocumentImpl<TCanvas>(TCanvas canvas, IDocument document, DocumentSettings settings, bool useOriginalImages = false)
+        internal static void RenderDocumentImplementation<TCanvas>(TCanvas canvas, PageContext? pageContext, IDocument document, DocumentSettings settings, bool useOriginalImages = false, int documentId = 0)
             where TCanvas : ICanvas, IRenderingCanvas
             where TCanvas : ICanvas, IRenderingCanvas
         {
         {
             var container = new DocumentContainer();
             var container = new DocumentContainer();
             document.Compose(container);
             document.Compose(container);
             var content = container.Compose();
             var content = container.Compose();
-            
+
+            ApplyDocumentId(content, documentId);
             ApplyInheritedAndGlobalTexStyle(content, TextStyle.Default);
             ApplyInheritedAndGlobalTexStyle(content, TextStyle.Default);
             ApplyContentDirection(content, settings.ContentDirection);
             ApplyContentDirection(content, settings.ContentDirection);
             ApplyDefaultImageConfiguration(content, settings.ImageRasterDpi, settings.ImageCompressionQuality, useOriginalImages);
             ApplyDefaultImageConfiguration(content, settings.ImageRasterDpi, settings.ImageCompressionQuality, useOriginalImages);
@@ -130,7 +135,7 @@ namespace QuestPDF.Drawing
             if (Settings.EnableCaching)
             if (Settings.EnableCaching)
                 ApplyCaching(content);
                 ApplyCaching(content);
 
 
-            var pageContext = new PageContext();
+            pageContext ??= new PageContext();
             RenderPass(pageContext, new FreeCanvas(), content, debuggingState);
             RenderPass(pageContext, new FreeCanvas(), content, debuggingState);
             RenderPass(pageContext, canvas, content, debuggingState);
             RenderPass(pageContext, canvas, content, debuggingState);
         }
         }
@@ -309,5 +314,24 @@ namespace QuestPDF.Drawing
             foreach (var child in content.GetChildren())
             foreach (var child in content.GetChildren())
                 ApplyInheritedAndGlobalTexStyle(child, documentDefaultTextStyle);
                 ApplyInheritedAndGlobalTexStyle(child, documentDefaultTextStyle);
         }
         }
+        
+        /// <summary>
+        /// 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)
+        {
+            content.VisitChildren(x =>
+            {
+                if (x is Section section)
+                    section.DocumentId = documentId;
+                
+                else if (x is SectionLink sectionLink)
+                    sectionLink.DocumentId = documentId;
+                
+                else if (x is DynamicHost dynamicHost)
+                    dynamicHost.DocumentId = documentId;
+            });
+        }
     }
     }
 }
 }

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

@@ -14,6 +14,8 @@ namespace QuestPDF.Elements
         internal TextStyle TextStyle { get; set; } = TextStyle.Default;
         internal TextStyle TextStyle { get; set; } = TextStyle.Default;
         public ContentDirection ContentDirection { get; set; }
         public ContentDirection ContentDirection { get; set; }
         
         
+        internal int DocumentId { get; set; }
+        
         internal int? ImageTargetDpi { get; set; }
         internal int? ImageTargetDpi { get; set; }
         internal ImageCompressionQuality? ImageCompressionQuality { get; set; }
         internal ImageCompressionQuality? ImageCompressionQuality { get; set; }
         internal bool UseOriginalImage { get; set; }
         internal bool UseOriginalImage { get; set; }
@@ -58,6 +60,7 @@ namespace QuestPDF.Elements
             {
             {
                 PageContext = PageContext,
                 PageContext = PageContext,
                 Canvas = Canvas,
                 Canvas = Canvas,
+                DocumentId = DocumentId,
                 
                 
                 TextStyle = TextStyle,
                 TextStyle = TextStyle,
                 ContentDirection = ContentDirection,
                 ContentDirection = ContentDirection,
@@ -84,6 +87,7 @@ namespace QuestPDF.Elements
     {
     {
         internal IPageContext PageContext { get; set; }
         internal IPageContext PageContext { get; set; }
         internal ICanvas Canvas { get; set; }
         internal ICanvas Canvas { get; set; }
+        internal int DocumentId { get; set; }
         
         
         internal TextStyle TextStyle { get; set; }
         internal TextStyle TextStyle { get; set; }
         internal ContentDirection ContentDirection { get; set; }
         internal ContentDirection ContentDirection { get; set; }
@@ -101,6 +105,7 @@ namespace QuestPDF.Elements
             var container = new DynamicElement();
             var container = new DynamicElement();
             content(container);
             content(container);
             
             
+            container.ApplyDocumentId(DocumentId);
             container.ApplyInheritedAndGlobalTexStyle(TextStyle);
             container.ApplyInheritedAndGlobalTexStyle(TextStyle);
             container.ApplyContentDirection(ContentDirection);
             container.ApplyContentDirection(ContentDirection);
             container.ApplyDefaultImageConfiguration(ImageTargetDpi, ImageCompressionQuality, UseOriginalImage);
             container.ApplyDefaultImageConfiguration(ImageTargetDpi, ImageCompressionQuality, UseOriginalImage);

+ 11 - 3
Source/QuestPDF/Elements/Section.cs

@@ -4,7 +4,8 @@ namespace QuestPDF.Elements
 {
 {
     internal class Section : ContainerElement, IStateResettable
     internal class Section : ContainerElement, IStateResettable
     {
     {
-        public string LocationName { get; set; }
+        public int DocumentId { get; set; }
+        public string SectionName { get; set; }
         private bool IsRendered { get; set; }
         private bool IsRendered { get; set; }
         
         
         public void ResetState()
         public void ResetState()
@@ -14,14 +15,21 @@ namespace QuestPDF.Elements
         
         
         internal override void Draw(Size availableSpace)
         internal override void Draw(Size availableSpace)
         {
         {
+            var targetName = GetTargetName(DocumentId, SectionName);
+            
             if (!IsRendered)
             if (!IsRendered)
             {
             {
-                Canvas.DrawSection(LocationName);
+                Canvas.DrawSection(targetName);
                 IsRendered = true;
                 IsRendered = true;
             }
             }
             
             
-            PageContext.SetSectionPage(LocationName);
+            PageContext.SetSectionPage(targetName);
             base.Draw(availableSpace);
             base.Draw(availableSpace);
         }
         }
+        
+        internal static string GetTargetName(int documentId, string locationName)
+        {
+            return $"{documentId} | {locationName}";
+        }
     }
     }
 }
 }

+ 3 - 1
Source/QuestPDF/Elements/SectionLink.cs

@@ -5,6 +5,7 @@ namespace QuestPDF.Elements
 {
 {
     internal class SectionLink : ContainerElement
     internal class SectionLink : ContainerElement
     {
     {
+        public int DocumentId { get; set; }
         public string SectionName { get; set; }
         public string SectionName { get; set; }
         
         
         internal override void Draw(Size availableSpace)
         internal override void Draw(Size availableSpace)
@@ -14,7 +15,8 @@ namespace QuestPDF.Elements
             if (targetSize.Type == SpacePlanType.Wrap)
             if (targetSize.Type == SpacePlanType.Wrap)
                 return;
                 return;
 
 
-            Canvas.DrawSectionLink(SectionName, targetSize);
+            var targetName = Section.GetTargetName(DocumentId, SectionName);
+            Canvas.DrawSectionLink(targetName, targetSize);
             base.Draw(availableSpace);
             base.Draw(availableSpace);
         }
         }
     }
     }

+ 1 - 1
Source/QuestPDF/Fluent/ElementExtensions.cs

@@ -126,7 +126,7 @@ namespace QuestPDF.Fluent
         {
         {
             return element.Element(new Section
             return element.Element(new Section
             {
             {
-                LocationName = sectionName
+                SectionName = sectionName
             });
             });
         }
         }