Browse Source

Documentation Examples: Code Patterns

Marcin Ziąbek 9 months ago
parent
commit
75c60a4124

+ 81 - 0
Source/QuestPDF.DocumentationExamples/CodePatterns/CodePatternAddressComponentExample.cs

@@ -0,0 +1,81 @@
+using QuestPDF.Fluent;
+using QuestPDF.Helpers;
+using QuestPDF.Infrastructure;
+
+namespace QuestPDF.DocumentationExamples.CodePatterns;
+
+public class CodePatternAddressComponentExample
+{
+    [Test]
+    public void Example()
+    {
+        var address = new Address
+        {
+            CompanyName = "Apple",
+            PostalCode = "95014",
+            Country = "United States",
+            City = "Cupertino",
+            Street = "One Apple Park Way"
+        };
+        
+        Document
+            .Create(document =>
+            {
+                document.Page(page =>
+                {
+                    page.MinSize(new PageSize(0, 0));
+                    page.MaxSize(new PageSize(600, 1200));
+                    page.DefaultTextStyle(x => x.FontSize(20));
+                    page.Margin(25);
+
+                    page.Content()
+                        .Component(new AddressComponent(address));
+                });
+            })
+            .GenerateImages(x => $"code-pattern-component-address.webp", new ImageGenerationSettings() { ImageFormat = ImageFormat.Webp, ImageCompressionQuality = ImageCompressionQuality.Best, RasterDpi = 144 });
+    }
+
+    public class Address
+    {
+        public string CompanyName { get; set; }
+        
+        public string PostalCode { get; set; }
+        public string Country { get; set; }
+        public string City { get; set; }
+        public string Street { get; set; }
+    }
+    
+    public class AddressComponent : IComponent
+    {
+        private Address Address { get; }
+
+        public AddressComponent(Address address)
+        {
+            Address = address;
+        }
+        
+        public void Compose(IContainer container)
+        {
+            container
+                .Column(column =>
+                {
+                    column.Spacing(10);
+                    
+                    AddItem("Company name", Address.CompanyName);
+                    AddItem("Postal code", Address.PostalCode);
+                    AddItem("Country", Address.Country);
+                    AddItem("City", Address.City);
+                    AddItem("Street", Address.Street);
+                    
+                    void AddItem(string label, string value)
+                    {
+                        column.Item().Text(text =>
+                        {
+                            text.Span($"{label}: ").Bold();
+                            text.Span(value);
+                        });
+                    }
+                });
+        }
+    }
+}

+ 110 - 0
Source/QuestPDF.DocumentationExamples/CodePatterns/CodePatternComponentProgressbarComponentExample.cs

@@ -0,0 +1,110 @@
+
+using QuestPDF.Elements;
+using QuestPDF.Fluent;
+using QuestPDF.Helpers;
+using QuestPDF.Infrastructure;
+
+namespace QuestPDF.DocumentationExamples.CodePatterns;
+
+public class CodePatternComponentProgressbarComponentExample
+{
+    [Test]
+    public void Example()
+    {
+        var content = GenerateReport();
+        File.WriteAllBytes("code-pattern-dynamic-component-progressbar.pdf", content);
+    }
+    
+    public byte[] GenerateReport()
+    {
+        return Document
+            .Create(document =>
+            {
+                document.Page(page =>
+                {
+                    page.Size(PageSizes.A4);
+                    page.Margin(50);
+                    page.DefaultTextStyle(x => x.FontSize(20));
+
+                    page.Header().Column(column =>
+                    {
+                        column.Item()
+                            .Text("MyBrick Set")
+                            .FontSize(48).FontColor(Colors.Blue.Darken2).Bold();
+                          
+                        column.Item()
+                            .Text("Building Instruction")
+                            .FontSize(24);
+                        
+                        column.Item().Height(15);
+                        
+                        column.Item().Dynamic(new PageProgressbarComponent());
+                    });
+                        
+                    page.Content().PaddingVertical(25).Column(column =>
+                    {
+                        column.Spacing(25);
+                        
+                        foreach (var i in Enumerable.Range(1, 30))
+                        {
+                            column.Item()
+                                .Background(Colors.Grey.Lighten3)
+                                .Height(Random.Shared.Next(4, 8) * 25)
+                                .AlignCenter()
+                                .AlignMiddle()
+                                .Text($"Step {i}");
+                        }
+                    });
+
+                    page.Footer().Dynamic(new PageNumberSideComponent());
+                });
+            })
+            .GeneratePdf();
+    }
+}
+
+public class PageProgressbarComponent : IDynamicComponent
+{
+    public DynamicComponentComposeResult Compose(DynamicContext context)
+    {
+        var content = context.CreateElement(element =>
+        {
+            var width = context.AvailableSize.Width * context.PageNumber / context.TotalPages;
+                
+            element
+                .Background(Colors.Blue.Lighten3)
+                .Height(5)
+                .Width(width)
+                .Background(Colors.Blue.Darken2);
+        });
+
+        return new DynamicComponentComposeResult
+        {
+            Content = content,
+            HasMoreContent = false
+        };
+    }
+}
+
+public class PageNumberSideComponent : IDynamicComponent
+{
+    public DynamicComponentComposeResult Compose(DynamicContext context)
+    {
+        var content = context.CreateElement(element =>
+        {
+            element
+                .Element(x => context.PageNumber % 2 == 0 ? x.AlignRight() : x.AlignLeft())
+                .Text(text =>
+                {
+                    text.Span("Page ");
+                    text.CurrentPageNumber();
+                });
+        });
+
+        return new DynamicComponentComposeResult
+        {
+            Content = content,
+            HasMoreContent = false
+        };
+    }
+}

+ 96 - 0
Source/QuestPDF.DocumentationExamples/CodePatterns/CodePatternConfigurableComponentExample.cs

@@ -0,0 +1,96 @@
+using QuestPDF.Fluent;
+using QuestPDF.Helpers;
+using QuestPDF.Infrastructure;
+
+namespace QuestPDF.DocumentationExamples.CodePatterns;
+
+public class CodePatternConfigurableComponentExample
+{
+    [Test]
+    public void Example()
+    {
+        Document
+            .Create(document =>
+            {
+                document.Page(page =>
+                {
+                    page.MinSize(new PageSize(0, 0));
+                    page.MaxSize(new PageSize(600, 1200));
+                    page.DefaultTextStyle(x => x.FontSize(20));
+                    page.Margin(25);
+
+                    page.Content()
+                        .Component(BuildSampleSection());
+                });
+            })
+            .GenerateImages(x => $"code-pattern-component-configurable.webp", new ImageGenerationSettings() { ImageFormat = ImageFormat.Webp, ImageCompressionQuality = ImageCompressionQuality.Best, RasterDpi = 144 });
+
+        IComponent BuildSampleSection()
+        {
+            var section = new SectionComponent();
+
+            section.Text("Product name", Placeholders.Label());
+            section.Text("Description", Placeholders.Sentence());
+            section.Text("Price", Placeholders.Price());
+            section.Text("Date of production", Placeholders.ShortDate());
+            section.Image("Photo of the product", "Resources/product.jpg");
+            section.Custom("Status").Text("Accepted").FontColor(Colors.Green.Darken2).Bold();
+            
+            return section;
+        }
+    }
+
+    public class SectionComponent : IComponent
+    {
+        private List<(string Label, IContainer Content)> Fields { get; set; } = [];
+
+        public SectionComponent()
+        {
+            
+        }
+        
+        public void Compose(IContainer container)
+        {
+            container
+                .Border(1)
+                .Column(column =>
+                {
+                    foreach (var field in Fields)
+                    {
+                        column.Item().Row(row =>
+                        {
+                            row.RelativeItem()
+                                .Border(1)
+                                .BorderColor(Colors.Grey.Medium)
+                                .Background(Colors.Grey.Lighten3)
+                                .Padding(10)
+                                .Text(field.Label);
+
+                            row.RelativeItem(2)
+                                .Border(1)
+                                .BorderColor(Colors.Grey.Medium)
+                                .Padding(10)
+                                .Element(field.Content);
+                        });
+                    }
+                });
+        }
+
+        public void Text(string label, string text)
+        {
+            Custom(label).Text(text);
+        }
+        
+        public void Image(string label, string imagePath)
+        {
+            Custom(label).Image(imagePath);
+        }
+        
+        public IContainer Custom(string label)
+        {
+            var content = IContainer.Empty;
+            Fields.Add((label, content));
+            return content;
+        }
+    }
+}

+ 66 - 0
Source/QuestPDF.DocumentationExamples/CodePatterns/CodePatternContentStylingExample.cs

@@ -0,0 +1,66 @@
+using QuestPDF.Fluent;
+using QuestPDF.Helpers;
+using QuestPDF.Infrastructure;
+
+namespace QuestPDF.DocumentationExamples.CodePatterns;
+
+public class CodePatternContentStylingExample
+{
+    [Test]
+    public void Example()
+    {
+        Document
+            .Create(document =>
+            {
+                document.Page(page =>
+                {
+                    page.MinSize(new PageSize(650, 0));
+                    page.MaxSize(new PageSize(650, 1000));
+                    page.DefaultTextStyle(x => x.FontSize(20));
+                    page.Margin(25);
+
+                    page.Content()
+                        .Table(table =>
+                        {
+                            table.ColumnsDefinition(columns =>
+                            {
+                                columns.ConstantColumn(50);
+                                columns.RelativeColumn(1);
+                                columns.RelativeColumn(2);
+                            });
+                            
+                            table.Header(header =>
+                            {
+                                header.Cell().Element(Style).Text("#");
+                                header.Cell().Element(Style).Text("Product Name");
+                                header.Cell().Element(Style).Text("Description");
+
+                                IContainer Style(IContainer container)
+                                {
+                                    return container
+                                        .Background(Colors.Blue.Lighten5)
+                                        .Padding(10)
+                                        .DefaultTextStyle(TextStyle.Default.FontColor(Colors.Blue.Darken4).Bold());
+                                }
+                            });
+
+                            foreach (var i in Enumerable.Range(1, 5))
+                            {
+                                table.Cell().Element(Style).Text(i.ToString());
+                                table.Cell().Element(Style).Text(Placeholders.Label());
+                                table.Cell().Element(Style).Text(Placeholders.Sentence());
+                            }
+
+                            IContainer Style(IContainer container)
+                            { 
+                                return container
+                                    .BorderTop(2)
+                                    .BorderColor(Colors.Blue.Lighten3)
+                                    .Padding(10);
+                            }
+                        });
+                });
+            })
+            .GenerateImages(x => $"code-pattern-content-styling.webp", new ImageGenerationSettings() { ImageFormat = ImageFormat.Webp, ImageCompressionQuality = ImageCompressionQuality.Best, RasterDpi = 144 });
+    }
+}

+ 106 - 0
Source/QuestPDF.DocumentationExamples/CodePatterns/CodePatternDocumentStructureExample.cs

@@ -0,0 +1,106 @@
+using QuestPDF.Fluent;
+using QuestPDF.Helpers;
+using QuestPDF.Infrastructure;
+
+namespace QuestPDF.DocumentationExamples.CodePatterns;
+
+public class CodePatternDocumentStructureExample
+{
+    [Test]
+    public void Example()
+    {
+        var content = GenerateReport();
+        File.WriteAllBytes("code-pattern-document-structure.pdf", content);
+    }
+
+    public byte[] GenerateReport()
+    {
+        return Document
+            .Create(document =>
+            {
+                document.Page(page =>
+                {
+                    page.Size(PageSizes.A5);
+                    page.DefaultTextStyle(x => x.FontSize(20));
+                    page.Margin(25);
+
+                    page.Content()
+                        .PaddingBottom(15)
+                        .Column(column =>
+                        {
+                            column.Item().Element(ReportTitle);
+                            column.Item().PageBreak();
+                            column.Item().Element(RedSection);
+                            column.Item().PageBreak();
+                            column.Item().Element(GreenSection);
+                            column.Item().PageBreak();
+                            column.Item().Element(BlueSection);
+                        });
+
+                    page.Footer().AlignCenter().Text(text => text.CurrentPageNumber());
+                });
+            })
+            .GeneratePdf();
+    }
+
+    private void ReportTitle(IContainer container)
+    {
+        container.Extend()
+            .AlignCenter()
+            .AlignMiddle()
+            .Text("Multi-section report")
+            .FontSize(48)
+            .Bold();
+    }
+    
+    private void RedSection(IContainer container)
+    {
+        container.Grid(grid =>
+        {
+            grid.Columns(3);
+            grid.Spacing(15);
+            
+            grid.Item(3 ).Text("Red section")
+                .FontColor(Colors.Red.Darken2).FontSize(32).Bold();
+
+            grid.Item(3).Text(Placeholders.Paragraph()).Light();
+
+            foreach (var i in Enumerable.Range(0, 6))
+                grid.Item().AspectRatio(4 / 3f).Background(Colors.Red.Lighten4);
+        });
+    }
+    
+    private void GreenSection(IContainer container)
+    {
+        container.Grid(grid =>
+        {
+            grid.Columns(3);
+            grid.Spacing(15);
+            
+            grid.Item(3).Text("Green section")
+                .FontColor(Colors.Green.Darken2).FontSize(32).Bold();
+
+            grid.Item(3).Text(Placeholders.Paragraph()).Light();
+
+            foreach (var i in Enumerable.Range(0, 12))
+                grid.Item().AspectRatio(4 / 3f).Background(Colors.Green.Lighten4);
+        });
+    }
+    
+    private void BlueSection(IContainer container)
+    {
+        container.Grid(grid =>
+        {
+            grid.Columns(3);
+            grid.Spacing(15);
+            
+            grid.Item(3).Text("Blue section")
+                .FontColor(Colors.Blue.Darken2).FontSize(32).Bold();
+
+            grid.Item(3).Text(Placeholders.Paragraph()).Light();
+
+            foreach (var i in Enumerable.Range(0, 18))
+                grid.Item().AspectRatio(4 / 3f).Background(Colors.Blue.Lighten4);
+        });
+    }
+}

+ 185 - 0
Source/QuestPDF.DocumentationExamples/CodePatterns/CodePatternDynamicComponentExample.cs

@@ -0,0 +1,185 @@
+using System.Globalization;
+using QuestPDF.Elements;
+using QuestPDF.Fluent;
+using QuestPDF.Helpers;
+using QuestPDF.Infrastructure;
+
+namespace QuestPDF.DocumentationExamples.CodePatterns;
+
+public class CodePatternDynamicComponentExample
+{ 
+    [Test]
+    public static void Dynamic()
+    {
+        var items = Enumerable.Range(0, 25).Select(x => new OrderItem()).ToList();
+        
+        Document
+            .Create(document =>
+            {
+                document.Page(page =>
+                {
+                    page.Size(PageSizes.A4);
+                    page.DefaultTextStyle(x => x.FontSize(20));
+                    page.Margin(50);
+
+                    page.Content()
+                        .Decoration(decoration =>
+                        {
+                            decoration
+                                .Before()
+                                .PaddingBottom(10)
+                                .Text(text =>
+                                {
+                                    text.DefaultTextStyle(TextStyle.Default.Bold().FontColor(Colors.Blue.Darken2));
+                                    text.Span("Page ");
+                                    text.CurrentPageNumber();
+                                    text.Span(" of ");
+                                    text.TotalPages();
+                                });
+                            
+                            decoration
+                                .Content()
+                                .Dynamic(new OrdersTableWithPageSubtotalsComponent(items));
+                        });
+                });
+            })
+            .GeneratePdf("code-pattern-dynamic-component-table-with-per-page-subtotals.pdf");
+    }
+    
+    public class OrderItem
+    {
+        public string ItemName { get; set; } = Placeholders.Label();
+        public int Price { get; set; } = Placeholders.Random.Next(1, 11) * 10;
+        public int Count { get; set; } = Placeholders.Random.Next(1, 11);
+    }
+
+    public struct OrdersTableWithPageSubtotalsComponentState
+    {
+        public int ShownItemsCount { get; set; }
+    }
+    
+    public class OrdersTableWithPageSubtotalsComponent : IDynamicComponent<OrdersTableWithPageSubtotalsComponentState>
+    {
+        private ICollection<OrderItem> Items { get; }
+        public OrdersTableWithPageSubtotalsComponentState State { get; set; }
+
+        public OrdersTableWithPageSubtotalsComponent(ICollection<OrderItem> items)
+        {
+            Items = items;
+
+            State = new OrdersTableWithPageSubtotalsComponentState
+            {
+                ShownItemsCount = 0
+            };
+        }
+        
+        public DynamicComponentComposeResult Compose(DynamicContext context)
+        {
+            var header = ComposeHeader(context);
+            var sampleFooter = ComposeFooter(context, []);
+            var decorationHeight = header.Size.Height + sampleFooter.Size.Height;
+            
+            var rows = GetItemsForPage(context, decorationHeight).ToList();
+            var footer = ComposeFooter(context, rows.Select(x => x.Item));
+
+            var content = context.CreateElement(container =>
+            {
+                container.Shrink().Decoration(decoration =>
+                {
+                    decoration.Before().Element(header);
+
+                    decoration.Content().Column(column =>
+                    {
+                        foreach (var row in rows)
+                            column.Item().Element(row.Element);
+                    });
+
+                    decoration.After().Element(footer);
+                });
+            });
+
+            State = new OrdersTableWithPageSubtotalsComponentState
+            {
+                ShownItemsCount = State.ShownItemsCount + rows.Count
+            };
+
+            return new DynamicComponentComposeResult
+            {
+                Content = content,
+                HasMoreContent = State.ShownItemsCount < Items.Count
+            };
+        }
+
+        private static IDynamicElement ComposeHeader(DynamicContext context)
+        {
+            return context.CreateElement(element =>
+            {
+                element
+                    .Width(context.AvailableSize.Width)
+                    .BorderBottom(1)
+                    .BorderColor(Colors.Grey.Darken2)
+                    .Padding(10)
+                    .DefaultTextStyle(TextStyle.Default.SemiBold())
+                    .Row(row =>
+                    {
+                        row.ConstantItem(50).Text("#");
+                        row.RelativeItem().Text("Item name");
+                        row.ConstantItem(75).AlignRight().Text("Count");
+                        row.ConstantItem(75).AlignRight().Text("Price");
+                        row.ConstantItem(75).AlignRight().Text("Total");
+                    });
+            });
+        }
+        
+        private static IDynamicElement ComposeFooter(DynamicContext context, IEnumerable<OrderItem> items)
+        {
+            var total = items.Sum(x => x.Count * x.Price);
+
+            return context.CreateElement(element =>
+            {
+                element
+                    .Width(context.AvailableSize.Width)
+                    .Padding(10)
+                    .AlignRight()
+                    .Text($"Subtotal: {total}$")
+                    .Bold();
+            });
+        }
+        
+        private IEnumerable<(OrderItem Item, IDynamicElement Element)> GetItemsForPage(DynamicContext context, float decorationHeight)
+        {
+            var totalHeight = decorationHeight;
+
+            foreach (var index in Enumerable.Range(State.ShownItemsCount, Items.Count - State.ShownItemsCount))
+            {
+                var item = Items.ElementAt(index);
+                
+                var element = context.CreateElement(content =>
+                {
+                    content
+                        .Width(context.AvailableSize.Width)
+                        .BorderBottom(1)
+                        .BorderColor(Colors.Grey.Lighten2)
+                        .Padding(10)
+                        .Row(row =>
+                        {
+                            row.ConstantItem(50).Text((index + 1).ToString(CultureInfo.InvariantCulture));
+                            row.RelativeItem().Text(item.ItemName);
+                            row.ConstantItem(75).AlignRight().Text(item.Count.ToString(CultureInfo.InvariantCulture));
+                            row.ConstantItem(75).AlignRight().Text($"{item.Price}$");
+                            row.ConstantItem(75).AlignRight().Text($"{item.Count*item.Price}$");
+                        });
+                });
+
+                var elementHeight = element.Size.Height;
+
+                // it is important to use the Size.Epsilon constant to avoid floating point comparison issues
+                if (totalHeight + elementHeight > context.AvailableSize.Height + Size.Epsilon)
+                    break;
+                    
+                totalHeight += elementHeight;
+                yield return (item, element);
+            }
+        }
+    }
+}

+ 55 - 0
Source/QuestPDF.DocumentationExamples/CodePatterns/CodePatternExecutionOrderExample.cs

@@ -0,0 +1,55 @@
+using QuestPDF.Fluent;
+using QuestPDF.Helpers;
+using QuestPDF.Infrastructure;
+
+namespace QuestPDF.DocumentationExamples.CodePatterns;
+
+public class CodePatternExecutionOrderExample
+{
+    [Test]
+    public void Example()
+    {
+        Document
+            .Create(document =>
+            {
+                document.Page(page =>
+                {
+                    page.MinSize(new PageSize(400, 0));
+                    page.MaxSize(new PageSize(400, 1000));
+                    page.DefaultTextStyle(x => x.FontSize(20));
+                    page.Margin(25);
+
+                    page.Content()
+                        .Column(column =>
+                        {
+                            column.Spacing(25);
+
+                            column.Item()
+                                .Border(1)
+                                .Background(Colors.Blue.Lighten4)
+                                .Padding(15)
+                                .Text("border → background → padding");
+                            
+                            column.Item()
+                                .Border(1)
+                                .Padding(15)
+                                .Background(Colors.Blue.Lighten4)
+                                .Text("border → padding → background");
+    
+                            column.Item()
+                                .Background(Colors.Blue.Lighten4)
+                                .Padding(15)
+                                .Border(1)
+                                .Text("background → padding → border");
+                            
+                            column.Item()
+                                .Padding(15)
+                                .Border(1)
+                                .Background(Colors.Blue.Lighten4)
+                                .Text("padding → border → background");
+                        });
+                });
+            })
+            .GenerateImages(x => "code-pattern-execution-order.webp", new ImageGenerationSettings() { ImageFormat = ImageFormat.Webp, ImageCompressionQuality = ImageCompressionQuality.VeryHigh, RasterDpi = 144 });
+    }
+}

+ 78 - 0
Source/QuestPDF.DocumentationExamples/CodePatterns/CodePatternExtesionMethodExample.cs

@@ -0,0 +1,78 @@
+using QuestPDF.Fluent;
+using QuestPDF.Helpers;
+using QuestPDF.Infrastructure;
+
+namespace QuestPDF.DocumentationExamples.CodePatterns;
+
+public class CodePatternExtensionMethodExample
+{
+    [Test]
+    public void Example()
+    {
+        Document
+            .Create(document =>
+            {
+                document.Page(page =>
+                {
+                    page.MinSize(new PageSize(600, 0));
+                    page.MaxSize(new PageSize(600, 1000));
+                    page.DefaultTextStyle(x => x.FontSize(14));
+                    page.Margin(25);
+
+                    page.Content()
+                        .Border(1)
+                        .Table(table =>
+                        {
+                            table.ColumnsDefinition(columns =>
+                            {
+                                columns.RelativeColumn(2);
+                                columns.RelativeColumn(3);
+                                columns.RelativeColumn(2);
+                                columns.RelativeColumn(3);
+                            });
+                            
+                            table.Cell().TableLabelCell("Product name");
+                            table.Cell().TableValueCell().Text(Placeholders.Label());
+                            
+                            table.Cell().TableLabelCell("Description");
+                            table.Cell().TableValueCell().Text(Placeholders.Sentence());
+                            
+                            table.Cell().TableLabelCell("Price");
+                            table.Cell().TableValueCell().Text(Placeholders.Price());
+                            
+                            table.Cell().TableLabelCell("Date of production");
+                            table.Cell().TableValueCell().Text(Placeholders.ShortDate());
+                            
+                            table.Cell().ColumnSpan(2).TableLabelCell("Photo of the product");
+                            table.Cell().ColumnSpan(2).TableValueCell().AspectRatio(16 / 9f).Image(Placeholders.Image);
+                        });
+                });
+            })
+            .GenerateImages(x => "code-pattern-extension-methods.webp", new ImageGenerationSettings() { ImageFormat = ImageFormat.Webp, ImageCompressionQuality = ImageCompressionQuality.VeryHigh, RasterDpi = 144 });
+    }
+}
+
+public static class TableExtensions
+{
+    private static IContainer TableCellStyle(this IContainer container, string backgroundColor)
+    {
+        return container
+            .Border(1)
+            .BorderColor(Colors.Black)
+            .Background(backgroundColor)
+            .Padding(10);
+    }
+    
+    public static void TableLabelCell(this IContainer container, string text)
+    {
+        container
+            .TableCellStyle(Colors.Grey.Lighten3)
+            .Text(text)
+            .Bold();
+    }
+    
+    public static IContainer TableValueCell(this IContainer container)
+    {
+        return container.TableCellStyle(Colors.Transparent);
+    }
+}

+ 4 - 4
Source/QuestPDF.DocumentationExamples/SvgExamples.cs → Source/QuestPDF.DocumentationExamples/CodePatterns/CodePatternLocalHelpersExample.cs

@@ -2,9 +2,9 @@ using QuestPDF.Fluent;
 using QuestPDF.Helpers;
 using QuestPDF.Infrastructure;
 
-namespace QuestPDF.DocumentationExamples;
+namespace QuestPDF.DocumentationExamples.CodePatterns;
 
-public class SvgExamples
+public class CodePatternLocalHelpersExample
 {
     [Test]
     public void Example()
@@ -24,7 +24,7 @@ public class SvgExamples
                         {
                             column.Spacing(15);
 
-                            column.Item().Text("Business details:").Bold().FontColor(Colors.Blue.Darken2);
+                            column.Item().Text("Business details:").FontSize(24).Bold().FontColor(Colors.Blue.Darken2);
                             
                             AddContactItem("Resources/Icons/phone.svg", Placeholders.PhoneNumber());
                             AddContactItem("Resources/Icons/email.svg", Placeholders.Email());
@@ -42,6 +42,6 @@ public class SvgExamples
                         });
                 });
             })
-            .GenerateImages(x => $"svg.webp", new ImageGenerationSettings() { ImageFormat = ImageFormat.Webp, ImageCompressionQuality = ImageCompressionQuality.Best, RasterDpi = 144 });
+            .GenerateImages(x => $"code-pattern-local-helpers.webp", new ImageGenerationSettings() { ImageFormat = ImageFormat.Webp, ImageCompressionQuality = ImageCompressionQuality.Best, RasterDpi = 144 });
     }
 }

BIN
Source/QuestPDF.DocumentationExamples/Resources/product-accepted.png


BIN
Source/QuestPDF.DocumentationExamples/Resources/product-rejected.png


BIN
Source/QuestPDF.DocumentationExamples/Resources/product.jpg


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

@@ -105,6 +105,11 @@ namespace QuestPDF.Fluent
             return handler(handlerContainer);
         }
         
+        public static void Element(this IContainer parent, IContainer child)
+        {
+            parent.Child = child as IElement;
+        }
+        
         internal static IContainer NonTrackingElement(this IContainer parent, Func<IContainer, IContainer> handler)
         {
             return handler(parent.Container());

+ 7 - 1
Source/QuestPDF/Infrastructure/IContainer.cs

@@ -1,4 +1,6 @@
-namespace QuestPDF.Infrastructure
+using QuestPDF.Elements;
+
+namespace QuestPDF.Infrastructure
 {
     /// <summary>
     /// Represents a layout structure with exactly one child element.
@@ -10,5 +12,9 @@
     public interface IContainer
     {
         IElement? Child { get; set; }
+        
+        #if NETCOREAPP3_0_OR_GREATER
+        public static IContainer Empty => new Container();
+        #endif
     }
 }