Jelajahi Sumber

Merge remote-tracking branch 'origin/2022.02'

# Conflicts:
#	QuestPDF.Examples/RowExamples.cs
#	QuestPDF.UnitTests/RowTests.cs
#	QuestPDF/Elements/Row.cs
#	QuestPDF/Fluent/RowExtensions.cs
#	QuestPDF/QuestPDF.csproj
#	QuestPDF/Resources/ReleaseNotes.txt
Marcin Ziąbek 3 tahun lalu
induk
melakukan
3649607bd9
71 mengubah file dengan 1501 tambahan dan 1343 penghapusan
  1. 3 3
      QuestPDF.Examples/ChartExamples.cs
  2. 37 0
      QuestPDF.Examples/ColumnExamples.cs
  3. 5 5
      QuestPDF.Examples/ComplexLayoutBenchmark.cs
  4. 2 2
      QuestPDF.Examples/ContinousPage.cs
  5. 2 2
      QuestPDF.Examples/DebuggingTesting.cs
  6. 4 4
      QuestPDF.Examples/DefaultTextStyleExample.cs
  7. 4 4
      QuestPDF.Examples/DefaultTextStyleExamples.cs
  8. 7 7
      QuestPDF.Examples/DifferentHeaderOnFirstPageExample.cs
  9. 46 46
      QuestPDF.Examples/ElementExamples.cs
  10. 3 3
      QuestPDF.Examples/EnsureSpaceExample.cs
  11. 4 4
      QuestPDF.Examples/FrameExample.cs
  12. 5 5
      QuestPDF.Examples/ImageExamples.cs
  13. 1 1
      QuestPDF.Examples/InlinedExamples.cs
  14. 57 0
      QuestPDF.Examples/LineExamples.cs
  15. 1 1
      QuestPDF.Examples/LoremPicsumExample.cs
  16. 6 6
      QuestPDF.Examples/Padding.cs
  17. 20 21
      QuestPDF.Examples/RowExamples.cs
  18. 42 0
      QuestPDF.Examples/ScaleToFitExamples.cs
  19. 2 2
      QuestPDF.Examples/ShowOnceExample.cs
  20. 3 3
      QuestPDF.Examples/SkipOnceExample.cs
  21. 48 0
      QuestPDF.Examples/StopPaging.cs
  22. 28 28
      QuestPDF.Examples/TextBenchmark.cs
  23. 1 1
      QuestPDF.Examples/TextExamples.cs
  24. 21 21
      QuestPDF.ReportSample/Layouts/DifferentHeadersTemplate.cs
  25. 9 9
      QuestPDF.ReportSample/Layouts/PhotoTemplate.cs
  26. 11 11
      QuestPDF.ReportSample/Layouts/SectionTemplate.cs
  27. 16 16
      QuestPDF.ReportSample/Layouts/StandardReport.cs
  28. 8 8
      QuestPDF.ReportSample/Layouts/TableOfContentsTemplate.cs
  29. 1 1
      QuestPDF.ReportSample/Tests.cs
  30. 54 165
      QuestPDF.UnitTests/ColumnTests.cs
  31. 76 73
      QuestPDF.UnitTests/DecorationTests.cs
  32. 54 54
      QuestPDF.UnitTests/GridTests.cs
  33. 0 268
      QuestPDF.UnitTests/RowTests.cs
  34. 3 3
      QuestPDF/Drawing/DocumentContainer.cs
  35. 4 2
      QuestPDF/Drawing/DocumentGenerator.cs
  36. 1 1
      QuestPDF/Drawing/PdfCanvas.cs
  37. 1 1
      QuestPDF/Drawing/XpsCanvas.cs
  38. 120 0
      QuestPDF/Elements/Column.cs
  39. 75 61
      QuestPDF/Elements/Decoration.cs
  40. 6 6
      QuestPDF/Elements/Grid.cs
  41. 30 43
      QuestPDF/Elements/Inlined.cs
  42. 46 0
      QuestPDF/Elements/Line.cs
  43. 2 2
      QuestPDF/Elements/Page.cs
  44. 98 119
      QuestPDF/Elements/Row.cs
  45. 78 0
      QuestPDF/Elements/ScaleToFit.cs
  46. 0 143
      QuestPDF/Elements/Stack.cs
  47. 25 0
      QuestPDF/Elements/StopPaging.cs
  48. 20 14
      QuestPDF/Fluent/BorderExtensions.cs
  49. 44 0
      QuestPDF/Fluent/ColumnExtensions.cs
  50. 16 12
      QuestPDF/Fluent/ConstrainedExtensions.cs
  51. 38 6
      QuestPDF/Fluent/DecorationExtensions.cs
  52. 11 2
      QuestPDF/Fluent/ElementExtensions.cs
  53. 7 7
      QuestPDF/Fluent/GridExtensions.cs
  54. 12 5
      QuestPDF/Fluent/InlinedExtensions.cs
  55. 36 0
      QuestPDF/Fluent/LineExtensions.cs
  56. 20 14
      QuestPDF/Fluent/PaddingExtensions.cs
  57. 28 20
      QuestPDF/Fluent/PageExtensions.cs
  58. 31 10
      QuestPDF/Fluent/RowExtensions.cs
  59. 2 2
      QuestPDF/Fluent/ScaleExtensions.cs
  60. 0 33
      QuestPDF/Fluent/StackExtensions.cs
  61. 5 5
      QuestPDF/Fluent/TableExtensions.cs
  62. 5 5
      QuestPDF/Fluent/TextExtensions.cs
  63. 4 4
      QuestPDF/Fluent/TranslateExtensions.cs
  64. 3 3
      QuestPDF/Helpers/PageSizes.cs
  65. 56 0
      QuestPDF/Helpers/WriteOnlyStream.cs
  66. 2 2
      QuestPDF/Infrastructure/Size.cs
  67. 44 0
      QuestPDF/Infrastructure/Unit.cs
  68. 1 1
      QuestPDF/QuestPDF.csproj
  69. 7 7
      QuestPDF/Resources/Description.md
  70. 8 4
      QuestPDF/Resources/ReleaseNotes.txt
  71. 31 32
      readme.md

+ 3 - 3
QuestPDF.Examples/ChartExamples.cs

@@ -52,14 +52,14 @@ namespace QuestPDF.Examples
                     container
                         .Background(Colors.White)
                         .Padding(25)
-                        .Stack(stack =>
+                        .Column(column =>
                         {
-                            stack
+                            column
                                 .Item()
                                 .PaddingBottom(10)
                                 .Text("Chart example", TextStyle.Default.Size(20).SemiBold().Color(Colors.Blue.Medium));
                             
-                            stack
+                            column
                                 .Item()
                                 .Border(1)
                                 .ExtendHorizontal()

+ 37 - 0
QuestPDF.Examples/ColumnExamples.cs

@@ -0,0 +1,37 @@
+using NUnit.Framework;
+using QuestPDF.Examples.Engine;
+using QuestPDF.Fluent;
+using QuestPDF.Helpers;
+using QuestPDF.Infrastructure;
+
+namespace QuestPDF.Examples
+{
+    public class ColumnExamples
+    {
+        [Test]
+        public void Column()
+        {
+            RenderingTest
+                .Create()
+                .PageSize(PageSizes.A4)
+                .ShowResults()
+                .ProducePdf()
+                .Render(container =>
+                {
+                    container.Column(column =>
+                    {
+                        column.Item().Element(Block);
+
+                        static void Block(IContainer container)
+                        {
+                            container
+                                .Width(72)
+                                .Height(3.5f)
+                                .Height(1.5f)
+                                .Background(Placeholders.BackgroundColor());
+                        }
+                    });
+                });
+        }
+    }
+}

+ 5 - 5
QuestPDF.Examples/ComplexLayoutBenchmark.cs

@@ -36,8 +36,8 @@ namespace QuestPDF.Examples
                     .BorderColor(Colors.Black)
                     .Row(row =>
                     {
-                        row.RelativeColumn().Element(x => GenerateStructure(x, level));
-                        row.RelativeColumn().Element(x => GenerateStructure(x, level));
+                        row.RelativeItem().Element(x => GenerateStructure(x, level));
+                        row.RelativeItem().Element(x => GenerateStructure(x, level));
                     });
             }
             else
@@ -45,10 +45,10 @@ namespace QuestPDF.Examples
                 container
                     .Border(level / 4f)
                     .BorderColor(Colors.Black)
-                    .Stack(stack =>
+                    .Column(column =>
                     {
-                        stack.Item().Element(x => GenerateStructure(x, level));
-                        stack.Item().Element(x => GenerateStructure(x, level));
+                        column.Item().Element(x => GenerateStructure(x, level));
+                        column.Item().Element(x => GenerateStructure(x, level));
                     });
             }
         }

+ 2 - 2
QuestPDF.Examples/ContinousPage.cs

@@ -22,10 +22,10 @@ namespace QuestPDF.Examples
                 
                 page.Header().Text("Header");
                 
-                page.Content().PaddingVertical(10).Border(1).Padding(10).Stack(stack =>
+                page.Content().PaddingVertical(10).Border(1).Padding(10).Column(column =>
                 {
                     foreach (var index in Enumerable.Range(1, 100))
-                        stack.Item().Text($"Line {index}", TextStyle.Default.Color(Placeholders.Color()));
+                        column.Item().Text($"Line {index}", TextStyle.Default.Color(Placeholders.Color()));
                 });
                 
                 page.Footer().Text("Footer");

+ 2 - 2
QuestPDF.Examples/DebuggingTesting.cs

@@ -9,7 +9,7 @@ namespace QuestPDF.Examples
     public class DebuggingTesting
     {
         [Test]
-        public void Stack()
+        public void Column()
         {
             Assert.Throws<DocumentLayoutException>(() =>
             {
@@ -23,7 +23,7 @@ namespace QuestPDF.Examples
                             .Width(100)
                             .Background(Colors.Grey.Lighten3)
                             .DebugPointer("Example debug pointer")
-                            .Stack(x =>
+                            .Column(x =>
                             {
                                 x.Item().Text("Test");
                                 x.Item().Width(150);

+ 4 - 4
QuestPDF.Examples/DefaultTextStyleExample.cs

@@ -13,7 +13,7 @@ namespace QuestPDF.Examples
         {
             RenderingTest
                 .Create()
-                .ProduceImages()
+                .ProducePdf()
                 .ShowResults()
                 .RenderDocument(container =>
                 {
@@ -26,11 +26,11 @@ namespace QuestPDF.Examples
                         page.Size(PageSizes.A4);
                         page.Background(Colors.White);
         
-                        page.Content().Stack(stack =>
+                        page.Content().Column(column =>
                         {
-                            stack.Item().Text(Placeholders.Sentence());
+                            column.Item().Text(Placeholders.Sentence());
                         
-                            stack.Item().Text(text =>
+                            column.Item().Text(text =>
                             {
                                 // text in this block is additionally semibold
                                 text.DefaultTextStyle(TextStyle.Default.SemiBold());

+ 4 - 4
QuestPDF.Examples/DefaultTextStyleExamples.cs

@@ -24,12 +24,12 @@ namespace QuestPDF.Examples
                     container
                         .Padding(10)
                         .DefaultTextStyle(TextStyle.Default.Bold().Underline())
-                        .Stack(stack =>
+                        .Column(column =>
                         { 
-                            stack.Item().Text("Default style applies to all children", TextStyle.Default);
-                            stack.Item().Text("You can override certain styles", TextStyle.Default.Underline(false).Color(Colors.Green.Darken2));
+                            column.Item().Text("Default style applies to all children", TextStyle.Default);
+                            column.Item().Text("You can override certain styles", TextStyle.Default.Underline(false).Color(Colors.Green.Darken2));
                             
-                            stack.Item().PaddingTop(10).Border(1).Grid(grid =>
+                            column.Item().PaddingTop(10).Border(1).Grid(grid =>
                             {
                                 grid.Columns(4);
 

+ 7 - 7
QuestPDF.Examples/DifferentHeaderOnFirstPageExample.cs

@@ -23,21 +23,21 @@ namespace QuestPDF.Examples
                     container.Page(page =>
                     {
                         page.Size(PageSizes.A6);
-                        page.Margin(30);
+                        page.Margin(5);
                         page.Background(Colors.White);
                         
-                        page.Header().Stack(stack =>
+                        page.Header().Column(column =>
                         {
-                            stack.Item().ShowOnce().Background(Colors.Blue.Lighten2).Height(60);
-                            stack.Item().SkipOnce().Background(Colors.Green.Lighten2).Height(40);
+                            column.Item().ShowOnce().Background(Colors.Blue.Lighten2).Height(60);
+                            column.Item().SkipOnce().Background(Colors.Green.Lighten2).Height(40);
                         });
                         
-                        page.Content().PaddingVertical(10).Stack(stack =>
+                        page.Content().PaddingVertical(10).Column(column =>
                         {
-                            stack.Spacing(10);
+                            column.Spacing(10);
 
                             foreach (var _ in Enumerable.Range(0, 13))
-                                stack.Item().Background(Colors.Grey.Lighten2).Height(40);
+                                column.Item().Background(Colors.Grey.Lighten2).Height(40);
                         });
                         
                         page.Footer().AlignCenter().Text(text =>

+ 46 - 46
QuestPDF.Examples/ElementExamples.cs

@@ -40,7 +40,7 @@ namespace QuestPDF.Examples
                         .Decoration(decoration =>
                         {
                             decoration
-                                .Header()
+                                .Before()
                                 .Background(Colors.Grey.Medium)
                                 .Padding(10)
                                 .Text("Notes", TextStyle.Default.Size(16).Color("#FFF"));
@@ -66,27 +66,27 @@ namespace QuestPDF.Examples
                     container
                         .Background("#FFF")
                         .Padding(20)
-                        .Stack(stack =>
+                        .Column(column =>
                         {
-                            stack.Item()
+                            column.Item()
                                 .PaddingBottom(10)
                                 .AlignCenter()
                                 .Text("This Row element is 700pt wide");
 
-                            stack.Item().Row(row =>
+                            column.Item().Row(row =>
                             {
-                                row.ConstantColumn(100)
+                                row.ConstantItem(100)
                                     .Background(Colors.Grey.Lighten1)
                                     .Padding(10)
                                     .ExtendVertical()
                                     .Text("This column is 100 pt wide");
 
-                                row.RelativeColumn()
+                                row.RelativeItem()
                                     .Background(Colors.Grey.Lighten2)
                                     .Padding(10)
                                     .Text("This column takes 1/3 of the available space (200pt)");
 
-                                row.RelativeColumn(2)
+                                row.RelativeItem(2)
                                     .Background(Colors.Grey.Lighten3)
                                     .Padding(10)
                                     .Text("This column takes 2/3 of the available space (400pt)");
@@ -109,15 +109,15 @@ namespace QuestPDF.Examples
                         .Row(row =>
                         {
                             row.Spacing(20);
-                            row.RelativeColumn(2).Border(1).Background(Colors.Grey.Lighten1);
-                            row.RelativeColumn(3).Border(1).Background(Colors.Grey.Lighten2);
-                            row.RelativeColumn(4).Border(1).Background(Colors.Grey.Lighten3);
+                            row.RelativeItem(2).Border(1).Background(Colors.Grey.Lighten1);
+                            row.RelativeItem(3).Border(1).Background(Colors.Grey.Lighten2);
+                            row.RelativeItem(4).Border(1).Background(Colors.Grey.Lighten3);
                         });
                 });
         }
     
         [Test]
-        public void Stack()
+        public void column()
         {
             RenderingTest
                 .Create()
@@ -127,13 +127,13 @@ namespace QuestPDF.Examples
                     container
                         .Background("#FFF")
                         .Padding(15)
-                        .Stack(stack =>
+                        .Column(column =>
                         {
-                            stack.Spacing(15);
+                            column.Spacing(15);
                     
-                            stack.Item().Background(Colors.Grey.Medium).Height(50);
-                            stack.Item().Background(Colors.Grey.Lighten1).Height(100);
-                            stack.Item().Background(Colors.Grey.Lighten2).Height(150);
+                            column.Item().Background(Colors.Grey.Medium).Height(50);
+                            column.Item().Background(Colors.Grey.Lighten1).Height(100);
+                            column.Item().Background(Colors.Grey.Lighten2).Height(150);
                         });
                 });
         }
@@ -267,12 +267,12 @@ namespace QuestPDF.Examples
                             layers
                                 .PrimaryLayer()
                                 .Padding(25)
-                                .Stack(stack =>
+                                .Column(column =>
                                 {
-                                    stack.Spacing(5);
+                                    column.Spacing(5);
                             
                                     foreach (var _ in Enumerable.Range(0, 7))
-                                        stack.Item().Text(Placeholders.Sentence());
+                                        column.Item().Text(Placeholders.Sentence());
                                 });
                         
                             // layer above the main content    
@@ -304,16 +304,16 @@ namespace QuestPDF.Examples
         //                 {
         //                     page.Header().PageNumber("Page {pdf:currentPage}");
         //             
-        //                     page.Content().Height(300).Stack(content =>
+        //                     page.Content().Height(300).column(content =>
         //                     {
         //                         content.Item().Height(200).Background(Colors.Grey.Lighten2);
         //                 
-        //                         content.Item().EnsureSpace(100).Stack(stack =>
+        //                         content.Item().EnsureSpace(100).column(column =>
         //                         {
-        //                             stack.Spacing(10);
+        //                             column.Spacing(10);
         //                     
         //                             foreach (var _ in Enumerable.Range(0, 4))
-        //                                 stack.Item().Height(50).Background(Colors.Green.Lighten1);
+        //                                 column.Item().Height(50).Background(Colors.Green.Lighten1);
         //                         }); 
         //                     });
         //                 });
@@ -378,7 +378,7 @@ namespace QuestPDF.Examples
                         .Row(row =>
                         {
                             foreach (var color in colors)
-                                row.RelativeColumn().Background(color);
+                                row.RelativeItem().Background(color);
                         });
                 });
         }
@@ -437,10 +437,10 @@ namespace QuestPDF.Examples
                         {
                             layers.Layer().Text("Something else");
                     
-                            layers.PrimaryLayer().Stack(stack =>
+                            layers.PrimaryLayer().Column(column =>
                             {
-                                stack.Item().PaddingTop(20).Text("Text 1");
-                                stack.Item().PaddingTop(40).Text("Text 2");
+                                column.Item().PaddingTop(20).Text("Text 1");
+                                column.Item().PaddingTop(40).Text("Text 2");
                             });
                     
                             layers.Layer().Canvas((canvas, size) =>
@@ -501,13 +501,13 @@ namespace QuestPDF.Examples
                                 .SemiBold();
     
                             decoration
-                                .Header()
+                                .Before()
                                 .PaddingBottom(10)
                                 .Text("Example: scale component", headerFontStyle);
     
                             decoration
                                 .Content()
-                                .Stack(stack =>
+                                .Column(column =>
                                 {
                                     var scales = new[] { 0.8f, 0.9f, 1.1f, 1.2f };
 
@@ -519,7 +519,7 @@ namespace QuestPDF.Examples
 
                                         var fontStyle = TextStyle.Default.Size(16);
                 
-                                        stack
+                                        column
                                             .Item()
                                             .Border(1)
                                             .Background(fontColor)
@@ -708,14 +708,14 @@ namespace QuestPDF.Examples
                         .Border(2)
                         .Row(row =>
                         {
-                            row.ConstantColumn(25)
+                            row.ConstantItem(25)
                                 .Border(1)
                                 .RotateLeft()
                                 .AlignCenter()
                                 .AlignMiddle()
                                 .Text("Sample text");
                             
-                            row.RelativeColumn().Border(1).Padding(5).Text(Placeholders.Paragraph());
+                            row.RelativeItem().Border(1).Padding(5).Text(Placeholders.Paragraph());
                         });
                 });
         }
@@ -731,11 +731,11 @@ namespace QuestPDF.Examples
                     container
                         .Padding(25)
                         .PaddingLeft(75)
-                        .Stack(stack =>
+                        .Column(column =>
                         {
-                            stack.Item().Width(300).Height(150).Background(Colors.Blue.Lighten4);
+                            column.Item().Width(300).Height(150).Background(Colors.Blue.Lighten4);
                             
-                            stack
+                            column
                                 .Item()
                                 
                                 // creates an infinite space for its child
@@ -751,7 +751,7 @@ namespace QuestPDF.Examples
                                 
                                 .Background(Colors.Blue.Darken1);
                             
-                            stack.Item().Width(300).Height(150).Background(Colors.Blue.Lighten3);
+                            column.Item().Width(300).Height(150).Background(Colors.Blue.Lighten3);
                         });
                 });
         }
@@ -766,13 +766,13 @@ namespace QuestPDF.Examples
                 {
                     container
                         .Padding(25)
-                        .Stack(stack =>
+                        .Column(column =>
                         {
-                            stack.Item().Row(row =>
+                            column.Item().Row(row =>
                             {
-                                row.RelativeColumn().LabelCell("Label 1");
+                                row.RelativeItem().LabelCell("Label 1");
                                 
-                                row.RelativeColumn(3).Grid(grid =>
+                                row.RelativeItem(3).Grid(grid =>
                                 {
                                     grid.Columns(3);
                                     
@@ -784,11 +784,11 @@ namespace QuestPDF.Examples
                                 });
                             });
                             
-                            stack.Item().Row(row =>
+                            column.Item().Row(row =>
                             {
-                                row.RelativeColumn().ValueCell().Text("Value 1");
+                                row.RelativeItem().ValueCell().Text("Value 1");
                                 
-                                row.RelativeColumn(3).Grid(grid =>
+                                row.RelativeItem(3).Grid(grid =>
                                 {
                                     grid.Columns(3);
                                     
@@ -800,10 +800,10 @@ namespace QuestPDF.Examples
                                 });
                             });
                             
-                            stack.Item().Row(row =>
+                            column.Item().Row(row =>
                             {
-                                row.RelativeColumn().LabelCell("Label 6");
-                                row.RelativeColumn().ValueCell().Text("Value 6");
+                                row.RelativeItem().LabelCell("Label 6");
+                                row.RelativeItem().ValueCell().Text("Value 6");
                             });
                         });
                 });

+ 3 - 3
QuestPDF.Examples/EnsureSpaceExample.cs

@@ -25,15 +25,15 @@ namespace QuestPDF.Examples
                         
                         page.Header().Text("With ensure space", TextStyle.Default.SemiBold());
                         
-                        page.Content().Stack(stack =>
+                        page.Content().Column(column =>
                         {
-                            stack
+                            column
                                 .Item()
                                 .ExtendHorizontal()
                                 .Height(75)
                                 .Background(Colors.Grey.Lighten2);
                             
-                            stack
+                            column
                                 .Item()
                                 .EnsureSpace(100)
                                 .Text(Placeholders.LoremIpsum());

+ 4 - 4
QuestPDF.Examples/FrameExample.cs

@@ -36,14 +36,14 @@ namespace QuestPDF.Examples
                     container
                         .Background("#FFF")
                         .Padding(25)
-                        .Stack(stack =>
+                        .Column(column =>
                         {
                             for(var i = 1; i <= 4; i++)
                             {
-                                stack.Item().Row(row =>
+                                column.Item().Row(row =>
                                 {
-                                    row.RelativeColumn(2).LabelCell(Placeholders.Label());
-                                    row.RelativeColumn(3).ValueCell().Text(Placeholders.Paragraph());
+                                    row.RelativeItem(2).LabelCell(Placeholders.Label());
+                                    row.RelativeItem(3).ValueCell().Text(Placeholders.Paragraph());
                                 });
                             }
                         });

+ 5 - 5
QuestPDF.Examples/ImageExamples.cs

@@ -19,17 +19,17 @@ namespace QuestPDF.Examples
                 .ShowResults()
                 .Render(page =>
                 {
-                    page.Padding(25).Stack(stack =>
+                    page.Padding(25).Column(column =>
                     {
-                        stack.Spacing(25);
+                        column.Spacing(25);
                         
-                        stack.Item().Image("logo.png");
+                        column.Item().Image("logo.png");
 
                         var binaryData = File.ReadAllBytes("logo.png");
-                        stack.Item().Image(binaryData);
+                        column.Item().Image(binaryData);
                         
                         using var stream = new FileStream("logo.png", FileMode.Open);
-                        stack.Item().Image(stream);
+                        column.Item().Image(stream);
                     });
                 });
         }

+ 1 - 1
QuestPDF.Examples/InlinedExamples.cs

@@ -25,7 +25,7 @@ namespace QuestPDF.Examples
                         .Padding(25)
                         .Decoration(decoration =>
                         {
-                            decoration.Header().Text(text =>
+                            decoration.Before().Text(text =>
                             {
                                 text.DefaultTextStyle(TextStyle.Default.Size(20));
                                 

+ 57 - 0
QuestPDF.Examples/LineExamples.cs

@@ -0,0 +1,57 @@
+using System.IO;
+using NUnit.Framework;
+using QuestPDF.Examples.Engine;
+using QuestPDF.Fluent;
+using QuestPDF.Helpers;
+using QuestPDF.Infrastructure;
+
+namespace QuestPDF.Examples
+{
+    public class LineExamples
+    {
+        [Test]
+        public void LineHorizontal()
+        {
+            RenderingTest
+                .Create()
+                .PageSize(PageSizes.A5)
+                .ProduceImages()
+                .ShowResults()
+                .Render(container => 
+                {
+                    container
+                        .Padding(15)
+                        .MinimalBox()
+                        .DefaultTextStyle(TextStyle.Default.Size(16))
+                        .Column(column =>
+                        {
+                            column.Item().Text("Above text");
+                            column.Item().PaddingVertical(5).LineHorizontal(1).LineColor(Colors.Grey.Medium);
+                            column.Item().Text("Below text");
+                        });
+                });
+        }
+        
+        [Test]
+        public void LineVertical()
+        {
+            RenderingTest
+                .Create()
+                .PageSize(PageSizes.A5)
+                .ProduceImages()
+                .ShowResults()
+                .Render(container => 
+                {
+                    container
+                        .Padding(15)
+                        .DefaultTextStyle(TextStyle.Default.Size(16))
+                        .Row(row =>
+                        {
+                            row.AutoItem().Text("Left text");
+                            row.AutoItem().PaddingHorizontal(10).LineVertical(1).LineColor(Colors.Grey.Medium);
+                            row.AutoItem().Text("Right text");
+                        });
+                });
+        }
+    }
+}

+ 1 - 1
QuestPDF.Examples/LoremPicsumExample.cs

@@ -43,7 +43,7 @@ namespace QuestPDF.Examples
                     container
                         .Background("#FFF")
                         .Padding(25)
-                        .Stack(column =>
+                        .Column(column =>
                         {
                             column.Spacing(10);
 

+ 6 - 6
QuestPDF.Examples/Padding.cs

@@ -63,7 +63,7 @@ namespace QuestPDF.Examples
                 .Render(container =>
                 {
                     container
-                        .Stack(column =>
+                        .Column(column =>
                         {
                             column
                                 .Item()
@@ -113,14 +113,14 @@ namespace QuestPDF.Examples
                 .Render(container =>
                 {
                     container
-                        .Stack(column =>
+                        .Column(column =>
                         {
                             column
                                 .Item()
                                 .Height(150)
                                 .Row(row =>
                                 {
-                                    row.RelativeColumn()
+                                    row.RelativeItem()
                                         .Extend()
                                         .Background("FFF")
 
@@ -128,7 +128,7 @@ namespace QuestPDF.Examples
                                         .Width(50)
                                         .Background("444");
                             
-                                    row.RelativeColumn()
+                                    row.RelativeItem()
                                         .Extend()
                                         .Background("BBB")
 
@@ -142,7 +142,7 @@ namespace QuestPDF.Examples
                                 .Height(150)
                                 .Row(row =>
                                 {
-                                    row.RelativeColumn()
+                                    row.RelativeItem()
                                         .Extend()
                                         .Background("BBB")
 
@@ -150,7 +150,7 @@ namespace QuestPDF.Examples
                                         .Width(50)
                                         .Background("444");
                             
-                                    row.RelativeColumn()
+                                    row.RelativeItem()
                                         .Extend()
                                         .Background("BBB")
 

+ 20 - 21
QuestPDF.Examples/RowExamples.cs

@@ -9,7 +9,7 @@ namespace QuestPDF.Examples
     public class RowExamples
     {
         [Test]
-        public void ColumnTypes()
+        public void ItemTypes()
         {
             RenderingTest
                 .Create()
@@ -22,25 +22,24 @@ namespace QuestPDF.Examples
                         .Padding(25)
                         .MinimalBox()
                         .Border(1)
-                        .Stack(stack =>
+                        .Column(column =>
                         {
-                            stack.Item().LabelCell("Total width: 600px");
+                            column.Item().LabelCell("Total width: 600px");
                             
-                            stack.Item().Row(row =>
+                            column.Item().Row(row =>
                             {
-                                row.ConstantColumn(150).ValueCell("150px");
-                                row.ConstantColumn(100).ValueCell("100px");
-                                row.RelativeColumn(4).ValueCell("200px");
-                                row.RelativeColumn(3).ValueCell("150px");
+                                row.ConstantItem(150).ValueCell("150px");
+                                row.ConstantItem(100).ValueCell("100px");
+                                row.RelativeItem(4).ValueCell("200px");
+                                row.RelativeItem(3).ValueCell("150px");
                             });
                             
-                            stack.Item().Row(row =>
+                            column.Item().Row(row =>
                             {
-                                row.ConstantColumn(100).ValueCell("100px");
-                                row.ConstantColumn(50).ValueCell("50px");
-                                row.RelativeColumn(3).ValueCell("300px");
-                                row.RelativeColumn(1).ValueCell("100px");
-                                row.ConstantColumn(50).ValueCell("50px");
+                                row.ConstantItem(100).ValueCell("100px");
+                                row.ConstantItem(50).ValueCell("50px");
+                                row.RelativeItem(2).ValueCell("100px");
+                                row.RelativeItem(1).ValueCell("50px");
                             });
                         });
                 });
@@ -63,17 +62,17 @@ namespace QuestPDF.Examples
                         .Padding(25)
                         .Row(row =>
                         {
-                            row.RelativeColumn().Stack(stack =>
+                            row.RelativeItem().Column(column =>
                             {
-                                stack.Item().ShowOnce().Element(CreateBox).Text("X");
-                                stack.Item().Element(CreateBox).Text("1");
-                                stack.Item().Element(CreateBox).Text("2");
+                                column.Item().ShowOnce().Element(CreateBox).Text("X");
+                                column.Item().Element(CreateBox).Text("1");
+                                column.Item().Element(CreateBox).Text("2");
                             });
                                 
-                            row.RelativeColumn().Stack(stack =>
+                            row.RelativeItem().Column(column =>
                             {
-                                stack.Item().Element(CreateBox).Text("1");
-                                stack.Item().Element(CreateBox).Text("2");
+                                column.Item().Element(CreateBox).Text("1");
+                                column.Item().Element(CreateBox).Text("2");
                             });
                         });
                 });

+ 42 - 0
QuestPDF.Examples/ScaleToFitExamples.cs

@@ -0,0 +1,42 @@
+using System.Linq;
+using NUnit.Framework;
+using QuestPDF.Examples.Engine;
+using QuestPDF.Fluent;
+using QuestPDF.Helpers;
+using QuestPDF.Infrastructure;
+
+namespace QuestPDF.Examples
+{
+    public class ScaleToFitExamples
+    {
+        [Test]
+        public void ScaleToFit()
+        {
+            RenderingTest
+                .Create()
+                .PageSize(PageSizes.A4)
+                .ProduceImages()
+                .ShowResults()
+                .Render(container =>
+                {
+                    container.Padding(25).Column(column =>
+                    {
+                        var text = Placeholders.Paragraph();
+
+                        foreach (var i in Enumerable.Range(2, 5))
+                        {
+                            column
+                                .Item()
+                                .MinimalBox()
+                                .Border(1)
+                                .Padding(5)
+                                .Width(i * 40)
+                                .Height(i * 20)
+                                .ScaleToFit()
+                                .Text(text);
+                        }
+                    });
+                });
+        }
+    }
+}

+ 2 - 2
QuestPDF.Examples/ShowOnceExample.cs

@@ -27,14 +27,14 @@ namespace QuestPDF.Examples
                         
                         page.Content().PaddingVertical(5).Row(row =>
                         {
-                            row.RelativeColumn()
+                            row.RelativeItem()
                                 .Background(Colors.Grey.Lighten2)
                                 .Border(1)
                                 .Padding(5)
                                 .ShowOnce()
                                 .Text(Placeholders.Label());
                             
-                            row.RelativeColumn(2)
+                            row.RelativeItem(2)
                                 .Border(1)
                                 .Padding(5)
                                 .Text(Placeholders.Paragraph());

+ 3 - 3
QuestPDF.Examples/SkipOnceExample.cs

@@ -23,10 +23,10 @@ namespace QuestPDF.Examples
                         page.Size(PageSizes.A7.Landscape());
                         page.Background(Colors.White);
         
-                        page.Header().Stack(stack =>
+                        page.Header().Column(column =>
                         {
-                            stack.Item().ShowOnce().Text("This header is visible on the first page.");
-                            stack.Item().SkipOnce().Text("This header is visible on the second page and all following.");
+                            column.Item().ShowOnce().Text("This header is visible on the first page.");
+                            column.Item().SkipOnce().Text("This header is visible on the second page and all following.");
                         });
                         
                         page.Content()

+ 48 - 0
QuestPDF.Examples/StopPaging.cs

@@ -0,0 +1,48 @@
+using NUnit.Framework;
+using QuestPDF.Examples.Engine;
+using QuestPDF.Fluent;
+using QuestPDF.Helpers;
+using QuestPDF.Infrastructure;
+
+namespace QuestPDF.Examples
+{
+    public class StopPaging
+    {
+        [Test]
+        public void Example()
+        {
+            RenderingTest
+                .Create()
+                .PageSize(300, 250)
+                .ProduceImages()
+                .ShowResults()
+                .Render(container => 
+                {
+                    container
+                        .Padding(25)
+                        .DefaultTextStyle(TextStyle.Default.Size(14))
+                        .Decoration(decoration =>
+                        {
+                            decoration
+                                .Before()
+                                .Text(text =>
+                                {
+                                    text.DefaultTextStyle(TextStyle.Default.SemiBold().Color(Colors.Blue.Medium));
+                                    
+                                    text.Span("Page ");
+                                    text.CurrentPageNumber();
+                                });
+                            
+                            decoration
+                                .Content()
+                                .Column(column =>
+                                {
+                                    column.Spacing(25);
+                                    column.Item().StopPaging().Text(Placeholders.LoremIpsum());
+                                    column.Item().ExtendHorizontal().Height(75).Background(Colors.Grey.Lighten2);
+                                });
+                        });
+                });
+        }
+    }
+}

+ 28 - 28
QuestPDF.Examples/TextBenchmark.cs

@@ -117,19 +117,19 @@ namespace QuestPDF.Examples
                     {
                         decoration
                             .Content()
-                            .Stack(stack =>
+                            .Column(column =>
                             {
-                                stack.Item().Element(Title);
-                                stack.Item().PageBreak();
-                                stack.Item().Element(TableOfContents);
-                                stack.Item().PageBreak();
+                                column.Item().Element(Title);
+                                column.Item().PageBreak();
+                                column.Item().Element(TableOfContents);
+                                column.Item().PageBreak();
 
-                                Chapters(stack);
+                                Chapters(column);
 
-                                stack.Item().Element(Acknowledgements);
+                                column.Item().Element(Acknowledgements);
                             });
 
-                        decoration.Footer().Element(Footer);
+                        decoration.After().Element(Footer);
                     });
             }
             
@@ -139,61 +139,61 @@ namespace QuestPDF.Examples
                     .Extend()
                     .PaddingBottom(200)
                     .AlignBottom()
-                    .Stack(stack =>
+                    .Column(column =>
                     {
-                        stack.Item().Text("Quo Vadis", TextStyle.Default.Size(72).Bold().Color(Colors.Blue.Darken2));
-                        stack.Item().Text("Henryk Sienkiewicz", TextStyle.Default.Size(24).Color(Colors.Grey.Darken2));
+                        column.Item().Text("Quo Vadis", TextStyle.Default.Size(72).Bold().Color(Colors.Blue.Darken2));
+                        column.Item().Text("Henryk Sienkiewicz", TextStyle.Default.Size(24).Color(Colors.Grey.Darken2));
                     });
             }
 
             void TableOfContents(IContainer container)
             {
-                container.Stack(stack =>
+                container.Column(column =>
                 {
-                    SectionTitle(stack, "Spis treści");
+                    SectionTitle(column, "Spis treści");
                     
                     foreach (var chapter in chapters)
                     {
-                        stack.Item().InternalLink(chapter.Title).Row(row =>
+                        column.Item().InternalLink(chapter.Title).Row(row =>
                         {
-                            row.RelativeColumn().Text(chapter.Title, normalStyle);
-                            row.ConstantColumn(100).AlignRight().Text(text => text.PageNumberOfLocation(chapter.Title, normalStyle));
+                            row.RelativeItem().Text(chapter.Title, normalStyle);
+                            row.ConstantItem(100).AlignRight().Text(text => text.PageNumberOfLocation(chapter.Title, normalStyle));
                         });
                     }
                 });
             }
 
-            void Chapters(StackDescriptor stack)
+            void Chapters(ColumnDescriptor column)
             {
                 foreach (var chapter in chapters)
                 {
-                    stack.Item().Element(container => Chapter(container, chapter.Title, chapter.Content));
+                    column.Item().Element(container => Chapter(container, chapter.Title, chapter.Content));
                 }
             }
             
             void Chapter(IContainer container, string title, string content)
             {
-                container.Stack(stack =>
+                container.Column(column =>
                 {
-                    SectionTitle(stack, title);
+                    SectionTitle(column, title);
   
-                    stack.Item().Text(text =>
+                    column.Item().Text(text =>
                     {
                         text.ParagraphSpacing(5);
                         text.Span(content, normalStyle);
                     });
                     
-                    stack.Item().PageBreak();
+                    column.Item().PageBreak();
                 });
             }
 
             void Acknowledgements(IContainer container)
             {
-                container.Stack(stack =>
+                container.Column(column =>
                 {
-                    SectionTitle(stack, "Podziękowania");
+                    SectionTitle(column, "Podziękowania");
                     
-                    stack.Item().Text(text =>
+                    column.Item().Text(text =>
                     {
                         text.DefaultTextStyle(normalStyle);
                         
@@ -204,10 +204,10 @@ namespace QuestPDF.Examples
                 });
             }
 
-            void SectionTitle(StackDescriptor stack, string text)
+            void SectionTitle(ColumnDescriptor column, string text)
             {
-                stack.Item().Location(text).Text(text, subtitleStyle);
-                stack.Item().PaddingTop(10).PaddingBottom(50).BorderBottom(1).BorderColor(Colors.Grey.Lighten2).ExtendHorizontal();
+                column.Item().Location(text).Text(text, subtitleStyle);
+                column.Item().PaddingTop(10).PaddingBottom(50).BorderBottom(1).BorderColor(Colors.Grey.Lighten2).ExtendHorizontal();
             }
             
             void Footer(IContainer container)

+ 1 - 1
QuestPDF.Examples/TextExamples.cs

@@ -126,7 +126,7 @@ namespace QuestPDF.Examples
         }
         
         [Test]
-        public void TextStack()
+        public void Textcolumn()
         {
             RenderingTest
                 .Create()

+ 21 - 21
QuestPDF.ReportSample/Layouts/DifferentHeadersTemplate.cs

@@ -26,58 +26,58 @@ namespace QuestPDF.ReportSample.Layouts
 
         private void ComposeHeader(IContainer container)
         {
-            container.Background(Colors.Grey.Lighten3).Border(1).Stack(stack =>
+            container.Background(Colors.Grey.Lighten3).Border(1).Column(column =>
             {
-                stack.Item().ShowOnce().Padding(5).AlignMiddle().Row(row =>
+                column.Item().ShowOnce().Padding(5).AlignMiddle().Row(row =>
                 {
-                    row.RelativeColumn(2).AlignMiddle().Text("PRIMARY HEADER", TextStyle.Default.Color(Colors.Grey.Darken3).Size(30).Bold());
-                    row.RelativeColumn(1).AlignRight().MinimalBox().AlignMiddle().Background(Colors.Blue.Darken2).Padding(30);
+                    row.RelativeItem(2).AlignMiddle().Text("PRIMARY HEADER", TextStyle.Default.Color(Colors.Grey.Darken3).Size(30).Bold());
+                    row.RelativeItem(1).AlignRight().MinimalBox().AlignMiddle().Background(Colors.Blue.Darken2).Padding(30);
                 });
-                stack.Item().SkipOnce().Padding(5).Row(row =>
+                column.Item().SkipOnce().Padding(5).Row(row =>
                 {
-                    row.RelativeColumn(2).Text("SECONDARY HEADER", TextStyle.Default.Color(Colors.Grey.Darken3).Size(30).Bold());
-                    row.RelativeColumn(1).AlignRight().MinimalBox().Background(Colors.Blue.Lighten4).Padding(15);
+                    row.RelativeItem(2).Text("SECONDARY HEADER", TextStyle.Default.Color(Colors.Grey.Darken3).Size(30).Bold());
+                    row.RelativeItem(1).AlignRight().MinimalBox().Background(Colors.Blue.Lighten4).Padding(15);
                 });
             });
         }
 
         private void ComposeContent(IContainer container)
         {
-            container.Stack(stack =>
+            container.Column(column =>
             {
-                stack.Item().PaddingVertical(80).Text("First");
-                stack.Item().PageBreak();
-                stack.Item().PaddingVertical(80).Text("Second");
-                stack.Item().PageBreak();
-                stack.Item().PaddingVertical(80).Text("Third");
-                stack.Item().PageBreak();
+                column.Item().PaddingVertical(80).Text("First");
+                column.Item().PageBreak();
+                column.Item().PaddingVertical(80).Text("Second");
+                column.Item().PageBreak();
+                column.Item().PaddingVertical(80).Text("Third");
+                column.Item().PageBreak();
             });
         }
 
         private void ComposeFooter(IContainer container)
         {
-            container.Background(Colors.Grey.Lighten3).Stack(stack =>
+            container.Background(Colors.Grey.Lighten3).Column(column =>
             {
-                stack.Item().ShowOnce().Background(Colors.Grey.Lighten3).Row(row =>
+                column.Item().ShowOnce().Background(Colors.Grey.Lighten3).Row(row =>
                 {
-                    row.RelativeColumn().Text(x =>
+                    row.RelativeItem().Text(x =>
                     {
                         x.CurrentPageNumber();
                         x.Span(" / ");
                         x.TotalPages();
                     });
-                    row.RelativeColumn().AlignRight().Text("Footer for header");
+                    row.RelativeItem().AlignRight().Text("Footer for header");
                 });
 
-                stack.Item().SkipOnce().Background(Colors.Grey.Lighten3).Row(row =>
+                column.Item().SkipOnce().Background(Colors.Grey.Lighten3).Row(row =>
                 {
-                    row.RelativeColumn().Text(x =>
+                    row.RelativeItem().Text(x =>
                     {
                         x.CurrentPageNumber();
                         x.Span(" / ");
                         x.TotalPages();
                     });
-                    row.RelativeColumn().AlignRight().Text("Footer for every page except header");
+                    row.RelativeItem().AlignRight().Text("Footer for every page except header");
                 });
             });
         }

+ 9 - 9
QuestPDF.ReportSample/Layouts/PhotoTemplate.cs

@@ -17,11 +17,11 @@ namespace QuestPDF.ReportSample.Layouts
         {
             container
                 .ShowEntire()
-                .Stack(stack =>
+                .Column(column =>
                 {
-                    stack.Spacing(5);
-                    stack.Item().Element(PhotoWithMaps);
-                    stack.Item().Element(PhotoDetails);
+                    column.Spacing(5);
+                    column.Item().Element(PhotoWithMaps);
+                    column.Item().Element(PhotoDetails);
                 });
         }
         
@@ -30,14 +30,14 @@ namespace QuestPDF.ReportSample.Layouts
             container
                 .Row(row =>
                 {
-                    row.RelativeColumn(2).AspectRatio(4 / 3f).Component<ImagePlaceholder>();
+                    row.RelativeItem(2).AspectRatio(4 / 3f).Component<ImagePlaceholder>();
 
-                    row.RelativeColumn().PaddingLeft(5).Stack(stack =>
+                    row.RelativeItem().PaddingLeft(5).Column(column =>
                     {
-                        stack.Spacing(7f);
+                        column.Spacing(7f);
                         
-                        stack.Item().AspectRatio(4 / 3f).Component<ImagePlaceholder>();
-                        stack.Item().AspectRatio(4 / 3f).Component<ImagePlaceholder>();
+                        column.Item().AspectRatio(4 / 3f).Component<ImagePlaceholder>();
+                        column.Item().AspectRatio(4 / 3f).Component<ImagePlaceholder>();
                     });
                 });
         }

+ 11 - 11
QuestPDF.ReportSample/Layouts/SectionTemplate.cs

@@ -21,25 +21,25 @@ namespace QuestPDF.ReportSample.Layouts
                 .Decoration(decoration =>
                 {
                     decoration
-                        .Header()
+                        .Before()
                         .PaddingBottom(5)
                         .Text(Model.Title, Typography.Headline);
 
-                    decoration.Content().Border(0.75f).BorderColor(Colors.Grey.Medium).Stack(stack =>
+                    decoration.Content().Border(0.75f).BorderColor(Colors.Grey.Medium).Column(column =>
                     {
                         foreach (var part in Model.Parts)
                         {
-                            stack.Item().EnsureSpace(25).Row(row =>
+                            column.Item().EnsureSpace(25).Row(row =>
                             {
-                                row.ConstantColumn(150).LabelCell().Text(part.Label);
-                                var frame = row.RelativeColumn().ValueCell();
+                                row.ConstantItem(150).LabelCell().Text(part.Label);
+                                var frame = row.RelativeItem().ValueCell();
                             
                                 if (part is ReportSectionText text)
                                     frame.ShowEntire().Text(text.Text);
-                        
+                                
                                 if (part is ReportSectionMap map)
                                     frame.Element(x => MapElement(x, map));
-                        
+                                
                                 if (part is ReportSectionPhotos photos)
                                     frame.Element(x => PhotosElement(x, photos));
                             });
@@ -56,12 +56,12 @@ namespace QuestPDF.ReportSample.Layouts
                 return;
             }
 
-            container.ShowEntire().Stack(stack =>
+            container.ShowEntire().Column(column =>
             {
-                stack.Spacing(5);
+                column.Spacing(5);
                 
-                stack.Item().MaxWidth(250).AspectRatio(4 / 3f).Component<ImagePlaceholder>();
-                stack.Item().Text(model.Location.Format());
+                column.Item().MaxWidth(250).AspectRatio(4 / 3f).Component<ImagePlaceholder>();
+                column.Item().Text(model.Location.Format());
             });
         }
         

+ 16 - 16
QuestPDF.ReportSample/Layouts/StandardReport.cs

@@ -48,19 +48,19 @@ namespace QuestPDF.ReportSample.Layouts
 
         private void ComposeHeader(IContainer container)
         {
-            container.Stack(stack =>
+            container.Column(column =>
             {
-                stack.Item().Row(row =>
+                column.Item().Row(row =>
                 {
                     row.Spacing(50);
                     
-                    row.RelativeColumn().PaddingTop(-10).Text(Model.Title, Typography.Title);
-                    row.ConstantColumn(90).ExternalLink("https://www.questpdf.com").MaxHeight(30).Component<ImagePlaceholder>();
+                    row.RelativeItem().PaddingTop(-10).Text(Model.Title, Typography.Title);
+                    row.ConstantItem(90).ExternalLink("https://www.questpdf.com").MaxHeight(30).Component<ImagePlaceholder>();
                 });
 
-                stack.Item().ShowOnce().PaddingVertical(15).Border(1f).BorderColor(Colors.Grey.Lighten1).ExtendHorizontal();
+                column.Item().ShowOnce().PaddingVertical(15).Border(1f).BorderColor(Colors.Grey.Lighten1).ExtendHorizontal();
                 
-                stack.Item().ShowOnce().Grid(grid =>
+                column.Item().ShowOnce().Grid(grid =>
                 {
                     grid.Columns(2);
                     grid.Spacing(5);
@@ -79,22 +79,22 @@ namespace QuestPDF.ReportSample.Layouts
 
         void ComposeContent(IContainer container)
         {
-            container.PaddingVertical(20).Stack(stack =>
+            container.PaddingVertical(20).Column(column =>
             {
-                stack.Spacing(20);
-
-                stack.Item().Component(new TableOfContentsTemplate(Model.Sections));
+                column.Spacing(20);
+                
+                column.Item().Component(new TableOfContentsTemplate(Model.Sections));
                 
-                stack.Item().PageBreak();
+                column.Item().PageBreak();
                 
                 foreach (var section in Model.Sections)
-                    stack.Item().Location(section.Title).Component(new SectionTemplate(section));
-
-                stack.Item().PageBreak();
-                stack.Item().Location("Photos");
+                    column.Item().Location(section.Title).Component(new SectionTemplate(section));
+                
+                column.Item().PageBreak();
+                column.Item().Location("Photos");
                 
                 foreach (var photo in Model.Photos)
-                    stack.Item().Component(new PhotoTemplate(photo));
+                    column.Item().Component(new PhotoTemplate(photo));
             });
         }
     }

+ 8 - 8
QuestPDF.ReportSample/Layouts/TableOfContentsTemplate.cs

@@ -19,18 +19,18 @@ namespace QuestPDF.ReportSample.Layouts
                 .Decoration(decoration =>
                 {
                     decoration
-                        .Header()
+                        .Before()
                         .PaddingBottom(5)
                         .Text("Table of contents", Typography.Headline);
 
-                    decoration.Content().Stack(stack =>
+                    decoration.Content().Column(column =>
                     {
-                        stack.Spacing(5);
+                        column.Spacing(5);
                         
                         for (var i = 0; i < Sections.Count; i++)
-                            stack.Item().Element(c => DrawLink(c, i+1, Sections[i].Title));
+                            column.Item().Element(c => DrawLink(c, i+1, Sections[i].Title));
 
-                        stack.Item().Element(c => DrawLink(c, Sections.Count+1, "Photos"));
+                        column.Item().Element(c => DrawLink(c, Sections.Count+1, "Photos"));
                     });
                 });
         }
@@ -41,9 +41,9 @@ namespace QuestPDF.ReportSample.Layouts
                 .InternalLink(locationName)
                 .Row(row =>
                 {
-                    row.ConstantColumn(25).Text($"{number}.");
-                    row.RelativeColumn().Text(locationName);
-                    row.ConstantColumn(150).AlignRight().Text(text => text.PageNumberOfLocation(locationName));
+                    row.ConstantItem(25).Text($"{number}.");
+                    row.RelativeItem().Text(locationName);
+                    row.ConstantItem(150).AlignRight().Text(text => text.PageNumberOfLocation(locationName));
                 });
         }
     }

+ 1 - 1
QuestPDF.ReportSample/Tests.cs

@@ -24,7 +24,7 @@ namespace QuestPDF.ReportSample
         [Test] 
         public void GenerateAndShowPdf()
         {
-            ImagePlaceholder.Solid = true;
+            //ImagePlaceholder.Solid = true;
         
             var path = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, $"test_result.pdf");
             Report.GeneratePdf(path);

+ 54 - 165
QuestPDF.UnitTests/StackTests.cs → QuestPDF.UnitTests/ColumnTests.cs

@@ -1,4 +1,5 @@
-using NUnit.Framework;
+using System.Linq;
+using NUnit.Framework;
 using QuestPDF.Drawing;
 using QuestPDF.Elements;
 using QuestPDF.Fluent;
@@ -8,19 +9,40 @@ using QuestPDF.UnitTests.TestEngine;
 namespace QuestPDF.UnitTests
 {
     [TestFixture]
-    public class StackTests
+    public class ColumnTests
     {
+        private Column CreateColumnWithTwoItems(TestPlan testPlan)
+        {
+            return new Column
+            {
+                Items =
+                {
+                    new ColumnItem
+                    {
+                        Child = testPlan.CreateChild("first")
+                    },
+                    new ColumnItem
+                    {
+                        Child = testPlan.CreateChild("second")
+                    }
+                }
+            };
+        }
+        
+        private Column CreateColumnWithTwoItemsWhereFirstIsFullyRendered(TestPlan testPlan)
+        {
+            var column = CreateColumnWithTwoItems(testPlan);
+            column.Items.First().IsRendered = true;
+            return column;
+        }
+        
         #region Measure
 
         [Test]
         public void Measure_ReturnsWrap_WhenFirstChildWraps()
         {
             TestPlan
-                .For(x => new BinaryStack
-                {
-                    First = x.CreateChild("first"),
-                    Second = x.CreateChild("second")
-                })
+                .For(CreateColumnWithTwoItems)
                 .MeasureElement(new Size(400, 300))
                 .ExpectChildMeasure("first", new Size(400, 300), SpacePlan.Wrap())
                 .CheckMeasureResult(SpacePlan.Wrap());
@@ -30,11 +52,7 @@ namespace QuestPDF.UnitTests
         public void Measure_ReturnsPartialRender_WhenFirstChildReturnsPartialRender()
         {
             TestPlan
-                .For(x => new BinaryStack
-                {
-                    First = x.CreateChild("first"),
-                    Second = x.CreateChild("second")
-                })
+                .For(CreateColumnWithTwoItems)
                 .MeasureElement(new Size(400, 300))
                 .ExpectChildMeasure("first", new Size(400, 300), SpacePlan.PartialRender(300, 200))
                 .CheckMeasureResult(SpacePlan.PartialRender(300, 200));
@@ -44,11 +62,7 @@ namespace QuestPDF.UnitTests
         public void Measure_ReturnsPartialRender_WhenSecondChildWraps()
         {
             TestPlan
-                .For(x => new BinaryStack
-                {
-                    First = x.CreateChild("first"),
-                    Second = x.CreateChild("second")
-                })
+                .For(CreateColumnWithTwoItems)
                 .MeasureElement(new Size(400, 300))
                 .ExpectChildMeasure("first", new Size(400, 300), SpacePlan.FullRender(200, 100))
                 .ExpectChildMeasure("second", new Size(400, 200), SpacePlan.Wrap())
@@ -59,11 +73,7 @@ namespace QuestPDF.UnitTests
         public void Measure_ReturnsPartialRender_WhenSecondChildReturnsPartialRender()
         {
             TestPlan
-                .For(x => new BinaryStack
-                {
-                    First = x.CreateChild("first"),
-                    Second = x.CreateChild("second")
-                })
+                .For(CreateColumnWithTwoItems)
                 .MeasureElement(new Size(400, 300))
                 .ExpectChildMeasure("first", new Size(400, 300), SpacePlan.FullRender(200, 100))
                 .ExpectChildMeasure("second", new Size(400, 200), SpacePlan.PartialRender(300, 150))
@@ -74,33 +84,13 @@ namespace QuestPDF.UnitTests
         public void Measure_ReturnsFullRender_WhenSecondChildReturnsFullRender()
         {
             TestPlan
-                .For(x => new BinaryStack
-                {
-                    First = x.CreateChild("first"),
-                    Second = x.CreateChild("second")
-                })
+                .For(CreateColumnWithTwoItems)
                 .MeasureElement(new Size(400, 300))
                 .ExpectChildMeasure("first", new Size(400, 300), SpacePlan.FullRender(200, 100))
                 .ExpectChildMeasure("second", new Size(400, 200), SpacePlan.FullRender(100, 50))
                 .CheckMeasureResult(SpacePlan.FullRender(200, 150));
         }
-        
-        [Test]
-        public void Measure_UsesEmpty_WhenFirstChildIsRendered()
-        {
-            TestPlan
-                .For(x => new BinaryStack
-                {
-                    First = x.CreateChild("first"),
-                    Second = x.CreateChild("second"),
-                    
-                    IsFirstRendered = true
-                })
-                .MeasureElement(new Size(400, 300))
-                .ExpectChildMeasure("second", new Size(400, 300), SpacePlan.FullRender(200, 300))
-                .CheckMeasureResult(SpacePlan.FullRender(200, 300));
-        }
-        
+
         #endregion
         
         #region Draw
@@ -109,11 +99,7 @@ namespace QuestPDF.UnitTests
         public void Draw_WhenFirstChildWraps()
         {
             TestPlan
-                .For(x => new BinaryStack
-                {
-                    First = x.CreateChild("first"),
-                    Second = x.CreateChild("second")
-                })
+                .For(CreateColumnWithTwoItems)
                 .DrawElement(new Size(400, 300))
                 .ExpectChildMeasure("first", new Size(400, 300), SpacePlan.Wrap())
                 .CheckDrawResult();
@@ -123,14 +109,12 @@ namespace QuestPDF.UnitTests
         public void Draw_WhenFirstChildPartiallyRenders()
         {
             TestPlan
-                .For(x => new BinaryStack
-                {
-                    First = x.CreateChild("first"),
-                    Second = x.CreateChild("second")
-                })
+                .For(CreateColumnWithTwoItems)
                 .DrawElement(new Size(400, 300))
                 .ExpectChildMeasure("first", new Size(400, 300), SpacePlan.PartialRender(200, 100))
+                .ExpectCanvasTranslate(0, 0)
                 .ExpectChildDraw("first", new Size(400, 100))
+                .ExpectCanvasTranslate(0, 0)
                 .CheckDrawResult();
         }
         
@@ -138,15 +122,13 @@ namespace QuestPDF.UnitTests
         public void Draw_WhenFirstChildFullyRenders_AndSecondChildWraps()
         {
             TestPlan
-                .For(x => new BinaryStack
-                {
-                    First = x.CreateChild("first"),
-                    Second = x.CreateChild("second")
-                })
+                .For(CreateColumnWithTwoItems)
                 .DrawElement(new Size(400, 300))
                 .ExpectChildMeasure("first", new Size(400, 300), SpacePlan.FullRender(200, 100))
-                .ExpectChildDraw("first", new Size(400, 100))
                 .ExpectChildMeasure("second", new Size(400, 200), SpacePlan.Wrap())
+                .ExpectCanvasTranslate(0, 0)
+                .ExpectChildDraw("first", new Size(400, 100))
+                .ExpectCanvasTranslate(0, 0)
                 .CheckDrawResult();
         }
         
@@ -154,15 +136,13 @@ namespace QuestPDF.UnitTests
         public void Draw_WhenFirstChildFullyRenders_AndSecondChildPartiallyRenders()
         {
             TestPlan
-                .For(x => new BinaryStack
-                {
-                    First = x.CreateChild("first"),
-                    Second = x.CreateChild("second")
-                })
+                .For(CreateColumnWithTwoItems)
                 .DrawElement(new Size(400, 300))
                 .ExpectChildMeasure("first", new Size(400, 300), SpacePlan.FullRender(200, 100))
-                .ExpectChildDraw("first", new Size(400, 100))
                 .ExpectChildMeasure("second", new Size(400, 200), SpacePlan.PartialRender(250, 150))
+                .ExpectCanvasTranslate(0, 0)
+                .ExpectChildDraw("first", new Size(400, 100))
+                .ExpectCanvasTranslate(0, 0)
                 .ExpectCanvasTranslate(0, 100)
                 .ExpectChildDraw("second", new Size(400, 150))
                 .ExpectCanvasTranslate(0, -100)
@@ -173,15 +153,13 @@ namespace QuestPDF.UnitTests
         public void Draw_WhenFirstChildFullyRenders_AndSecondChildFullyRenders()
         {
             TestPlan
-                .For(x => new BinaryStack
-                {
-                    First = x.CreateChild("first"),
-                    Second = x.CreateChild("second")
-                })
+                .For(CreateColumnWithTwoItems)
                 .DrawElement(new Size(400, 300))
                 .ExpectChildMeasure("first", new Size(400, 300), SpacePlan.FullRender(200, 100))
-                .ExpectChildDraw("first", new Size(400, 100))
                 .ExpectChildMeasure("second", new Size(400, 200), SpacePlan.FullRender(250, 150))
+                .ExpectCanvasTranslate(0, 0)
+                .ExpectChildDraw("first", new Size(400, 100))
+                .ExpectCanvasTranslate(0, 0)
                 .ExpectCanvasTranslate(0, 100)
                 .ExpectChildDraw("second", new Size(400, 150))
                 .ExpectCanvasTranslate(0, -100)
@@ -192,19 +170,13 @@ namespace QuestPDF.UnitTests
         public void Draw_UsesEmpty_WhenFirstChildIsRendered()
         {
             TestPlan
-                .For(x => new BinaryStack
-                {
-                    First = x.CreateChild("first"),
-                    Second = x.CreateChild("second"),
-                    
-                    IsFirstRendered = true
-                })
+                .For(CreateColumnWithTwoItemsWhereFirstIsFullyRendered)
                 .DrawElement(new Size(400, 300))
                 .ExpectChildMeasure("second", new Size(400, 300), SpacePlan.PartialRender(200, 300))
                 .ExpectCanvasTranslate(0, 0)
                 .ExpectChildDraw("second", new Size(400, 300))
                 .ExpectCanvasTranslate(0, 0)
-                .CheckState<BinaryStack>(x => x.IsFirstRendered)
+                .CheckState<Column>(x => x.Items.First().IsRendered)
                 .CheckDrawResult();
         }
         
@@ -212,97 +184,14 @@ namespace QuestPDF.UnitTests
         public void Draw_TogglesFirstRenderedFlag_WhenSecondFullyRenders()
         {
             TestPlan
-                .For(x => new BinaryStack
-                {
-                    First = x.CreateChild("first"),
-                    Second = x.CreateChild("second"),
-                    
-                    IsFirstRendered = true
-                })
+                .For(CreateColumnWithTwoItemsWhereFirstIsFullyRendered)
                 .DrawElement(new Size(400, 300))
                 .ExpectChildMeasure("second", new Size(400, 300), SpacePlan.FullRender(200, 300))
                 .ExpectCanvasTranslate(0, 0)
                 .ExpectChildDraw("second", new Size(400, 300))
                 .ExpectCanvasTranslate(0, 0)
                 .CheckDrawResult()
-                .CheckState<BinaryStack>(x => !x.IsFirstRendered);
-        }
-        
-        #endregion
-        
-        #region Structure
-        
-        [Test]
-        public void Structure_Simple()
-        { 
-            // arrange
-            var childA = TestPlan.CreateUniqueElement();
-            var childB = TestPlan.CreateUniqueElement();
-            var childC = TestPlan.CreateUniqueElement();
-            var childD = TestPlan.CreateUniqueElement();
-            var childE = TestPlan.CreateUniqueElement();
-
-            const int spacing = 20;
-            
-            // act
-            var structure = new Container();
-            
-            structure.Stack(stack =>
-            {
-                stack.Spacing(spacing);
-                
-                stack.Item().Element(childA);
-                stack.Item().Element(childB);
-                stack.Item().Element(childC);
-                stack.Item().Element(childD);
-                stack.Item().Element(childE);
-            });
-            
-            // assert
-            var expected = new Padding
-            {
-                Bottom = -spacing,
-
-                Child = new BinaryStack
-                {
-                    First = new BinaryStack
-                    {
-                        First = new Padding
-                        {
-                            Bottom = spacing,
-                            Child = childA
-                        },
-                        Second = new Padding
-                        {
-                            Bottom = spacing,
-                            Child = childB
-                        }
-                    },
-                    Second = new BinaryStack
-                    {
-                        First = new Padding
-                        {
-                            Bottom = spacing,
-                            Child = childC
-                        },
-                        Second = new BinaryStack
-                        {
-                            First = new Padding
-                            {
-                                Bottom = spacing,
-                                Child = childD
-                            },
-                            Second = new Padding
-                            {
-                                Bottom = spacing,
-                                Child = childE
-                            }
-                        }
-                    }
-                }
-            };
-
-            TestPlan.CompareOperations(structure, expected);
+                .CheckState<Column>(x => !x.Items.First().IsRendered);
         }
         
         #endregion

+ 76 - 73
QuestPDF.UnitTests/DecorationTests.cs

@@ -9,125 +9,128 @@ namespace QuestPDF.UnitTests
     [TestFixture]
     public class DecorationTests
     {
+        private Decoration CreateDecoration(TestPlan testPlan)
+        {
+            return new Decoration
+            {
+                Before = testPlan.CreateChild("before"),
+                Content = testPlan.CreateChild("content"),
+                After = testPlan.CreateChild("after"),
+            };
+        }
+        
         #region Measure
 
         [Test]
-        public void Measure_ReturnsWrap_WhenDecorationReturnsWrap()
+        public void Measure_ReturnsWrap_WhenBeforeReturnsWrap()
         {
             TestPlan
-                .For(x => new BinaryDecoration
-                {
-                    Type = DecorationType.Append,
-                    DecorationElement = x.CreateChild("decoration"),
-                    ContentElement = x.CreateChild("content")
-                })
+                .For(CreateDecoration)
                 .MeasureElement(new Size(400, 300))
-                .ExpectChildMeasure("decoration", new Size(400, 300), SpacePlan.Wrap())
+                .ExpectChildMeasure("before", new Size(400, 300), SpacePlan.Wrap())
+                .ExpectChildMeasure("after", new Size(400, 300), SpacePlan.FullRender(100, 50))
+                .ExpectChildMeasure("content", new Size(400, 250), SpacePlan.FullRender(100, 100))
                 .CheckMeasureResult(SpacePlan.Wrap());
         }
         
         [Test]
-        public void Measure_ReturnsWrap_WhenDecorationReturnsPartialRender()
+        public void Measure_ReturnsWrap_WhenContentReturnsWrap()
         {
             TestPlan
-                .For(x => new BinaryDecoration
-                {
-                    Type = DecorationType.Append,
-                    DecorationElement = x.CreateChild("decoration"),
-                    ContentElement = x.CreateChild("content")
-                })
+                .For(CreateDecoration)
                 .MeasureElement(new Size(400, 300))
-                .ExpectChildMeasure("decoration", new Size(400, 300), SpacePlan.PartialRender(300, 200))
+                .ExpectChildMeasure("before", new Size(400, 300), SpacePlan.FullRender(100, 50))
+                .ExpectChildMeasure("after", new Size(400, 300), SpacePlan.FullRender(100, 50))
+                .ExpectChildMeasure("content", new Size(400, 200), SpacePlan.Wrap())
                 .CheckMeasureResult(SpacePlan.Wrap());
         }
         
         [Test]
-        public void Measure_ReturnsWrap_WhenContentReturnsWrap()
+        public void Measure_ReturnsWrap_WhenAfterReturnsWrap()
         {
             TestPlan
-                .For(x => new BinaryDecoration
-                {
-                    Type = DecorationType.Append,
-                    DecorationElement = x.CreateChild("decoration"),
-                    ContentElement = x.CreateChild("content")
-                })
+                .For(CreateDecoration)
                 .MeasureElement(new Size(400, 300))
-                .ExpectChildMeasure("decoration", new Size(400, 300), SpacePlan.FullRender(300, 100))
-                .ExpectChildMeasure("content", new Size(400, 200), SpacePlan.Wrap())
+                .ExpectChildMeasure("before", new Size(400, 300), SpacePlan.FullRender(100, 50))
+                .ExpectChildMeasure("after", new Size(400, 300), SpacePlan.Wrap())
+                .ExpectChildMeasure("content", new Size(400, 250), SpacePlan.FullRender(100, 100))
                 .CheckMeasureResult(SpacePlan.Wrap());
         }
         
         [Test]
-        public void Measure_ReturnsPartialRender_WhenContentReturnsPartialRender()
+        public void Measure_ReturnsWrap_WhenBeforeReturnsPartialRender()
         {
             TestPlan
-                .For(x => new BinaryDecoration
-                {
-                    Type = DecorationType.Append,
-                    DecorationElement = x.CreateChild("decoration"),
-                    ContentElement = x.CreateChild("content")
-                })
+                .For(CreateDecoration)
                 .MeasureElement(new Size(400, 300))
-                .ExpectChildMeasure("decoration", new Size(400, 300), SpacePlan.FullRender(300, 100))
-                .ExpectChildMeasure("content", new Size(400, 200), SpacePlan.PartialRender(200, 150))
-                .CheckMeasureResult(SpacePlan.PartialRender(400, 250));
+                .ExpectChildMeasure("before", new Size(400, 300), SpacePlan.PartialRender(100, 50))
+                .ExpectChildMeasure("after", new Size(400, 300), SpacePlan.FullRender(100, 50))
+                .ExpectChildMeasure("content", new Size(400, 250), SpacePlan.FullRender(100, 100))
+                .CheckMeasureResult(SpacePlan.Wrap());
         }
         
         [Test]
-        public void Measure_ReturnsFullRender_WhenContentReturnsFullRender()
+        public void Measure_ReturnsWrap_WhenAfterReturnsPartialRender()
         {
             TestPlan
-                .For(x => new BinaryDecoration
-                {
-                    Type = DecorationType.Append,
-                    DecorationElement = x.CreateChild("decoration"),
-                    ContentElement = x.CreateChild("content")
-                })
+                .For(CreateDecoration)
                 .MeasureElement(new Size(400, 300))
-                .ExpectChildMeasure("decoration", new Size(400, 300), SpacePlan.FullRender(300, 100))
-                .ExpectChildMeasure("content", new Size(400, 200), SpacePlan.FullRender(200, 150))
-                .CheckMeasureResult(SpacePlan.FullRender(400, 250));
+                .ExpectChildMeasure("before", new Size(400, 300), SpacePlan.FullRender(100, 50))
+                .ExpectChildMeasure("after", new Size(400, 300), SpacePlan.PartialRender(100, 50))
+                .ExpectChildMeasure("content", new Size(400, 250), SpacePlan.FullRender(100, 100))
+                .CheckMeasureResult(SpacePlan.Wrap());
         }
         
-        #endregion
-        
-        #region Draw
+        [Test]
+        public void Measure_ReturnsWrap_WhenContentReturnsPartialRender()
+        {
+            TestPlan
+                .For(CreateDecoration)
+                .MeasureElement(new Size(400, 300))
+                .ExpectChildMeasure("before", new Size(400, 300), SpacePlan.FullRender(100, 50))
+                .ExpectChildMeasure("after", new Size(400, 300), SpacePlan.FullRender(100, 50))
+                .ExpectChildMeasure("content", new Size(400, 200), SpacePlan.PartialRender(150, 100))
+                .CheckMeasureResult(SpacePlan.PartialRender(150, 200));
+        }
         
         [Test]
-        public void Draw_Prepend()
+        public void Measure_ReturnsWrap_WhenContentReturnsFullRender()
         {
             TestPlan
-                .For(x => new BinaryDecoration
-                {
-                    Type = DecorationType.Prepend,
-                    DecorationElement = x.CreateChild("decoration"),
-                    ContentElement = x.CreateChild("content")
-                })
-                .DrawElement(new Size(400, 300))
-                .ExpectChildMeasure("decoration", new Size(400, 300), SpacePlan.FullRender(300, 100))
-                .ExpectChildDraw("decoration", new Size(400, 100))
-                .ExpectCanvasTranslate(0, 100)
-                .ExpectChildDraw("content", new Size(400, 200))
-                .ExpectCanvasTranslate(0, -100)
-                .CheckDrawResult();
+                .For(CreateDecoration)
+                .MeasureElement(new Size(400, 300))
+                .ExpectChildMeasure("before", new Size(400, 300), SpacePlan.FullRender(100, 50))
+                .ExpectChildMeasure("after", new Size(400, 300), SpacePlan.FullRender(100, 50))
+                .ExpectChildMeasure("content", new Size(400, 200), SpacePlan.FullRender(150, 100))
+                .CheckMeasureResult(SpacePlan.FullRender(150, 200));
         }
+
+        #endregion
+        
+        #region Draw
         
         [Test]
         public void Draw_Append()
         {
             TestPlan
-                .For(x => new BinaryDecoration
-                {
-                    Type = DecorationType.Append,
-                    DecorationElement = x.CreateChild("decoration"),
-                    ContentElement = x.CreateChild("content")
-                })
+                .For(CreateDecoration)
                 .DrawElement(new Size(400, 300))
-                .ExpectChildMeasure("decoration", new Size(400, 300), SpacePlan.FullRender(300, 100))
-                .ExpectChildDraw("content", new Size(400, 200))
-                .ExpectCanvasTranslate(0, 200)
-                .ExpectChildDraw("decoration", new Size(400, 100))
-                .ExpectCanvasTranslate(0, -200)
+                .ExpectChildMeasure("before", new Size(400, 300), SpacePlan.FullRender(200, 40))
+                .ExpectChildMeasure("after", new Size(400, 300), SpacePlan.FullRender(200, 60))
+                .ExpectChildMeasure("content", new Size(400, 200), SpacePlan.FullRender(300, 100))
+                
+                .ExpectCanvasTranslate(0, 0)
+                .ExpectChildDraw("before", new Size(300, 40))
+                .ExpectCanvasTranslate(0, 0)
+                
+                .ExpectCanvasTranslate(0, 40)
+                .ExpectChildDraw("content", new Size(300, 100))
+                .ExpectCanvasTranslate(0, -40)
+                
+                .ExpectCanvasTranslate(0, 140)
+                .ExpectChildDraw("after", new Size(300, 60))
+                .ExpectCanvasTranslate(0, -140)
+
                 .CheckDrawResult();
         }
 

+ 54 - 54
QuestPDF.UnitTests/GridTests.cs

@@ -39,26 +39,26 @@ namespace QuestPDF.UnitTests
             // assert
             var expected = new Container();
             
-            expected.Stack(stack =>
+            expected.Column(column =>
             {
-                stack.Item().Row(row =>
+                column.Item().Row(row =>
                 {
-                    row.RelativeColumn(6).Element(childA);
-                    row.RelativeColumn(4).Element(childB);
-                    row.RelativeColumn(2);
+                    row.RelativeItem(6).Element(childA);
+                    row.RelativeItem(4).Element(childB);
+                    row.RelativeItem(2);
                 });
                 
-                stack.Item().Row(row =>
+                column.Item().Row(row =>
                 {
-                    row.RelativeColumn(4).Element(childC);
-                    row.RelativeColumn(2).Element(childD);
-                    row.RelativeColumn(6);
+                    row.RelativeItem(4).Element(childC);
+                    row.RelativeItem(2).Element(childD);
+                    row.RelativeItem(6);
                 });
                 
-                stack.Item().Row(row =>
+                column.Item().Row(row =>
                 {
-                    row.RelativeColumn(8).Element(childE);
-                    row.RelativeColumn(4);
+                    row.RelativeItem(8).Element(childE);
+                    row.RelativeItem(4);
                 });
             });
             
@@ -93,29 +93,29 @@ namespace QuestPDF.UnitTests
             // assert
             var expected = new Container();
             
-            expected.Stack(stack =>
+            expected.Column(column =>
             {
-                stack.Item().Row(row =>
+                column.Item().Row(row =>
                 {
-                    row.RelativeColumn(1);
-                    row.RelativeColumn(6).Element(childA);
-                    row.RelativeColumn(4).Element(childB);
-                    row.RelativeColumn(1);
+                    row.RelativeItem(1);
+                    row.RelativeItem(6).Element(childA);
+                    row.RelativeItem(4).Element(childB);
+                    row.RelativeItem(1);
                 });
                 
-                stack.Item().Row(row =>
+                column.Item().Row(row =>
                 {
-                    row.RelativeColumn(3);
-                    row.RelativeColumn(4).Element(childC);
-                    row.RelativeColumn(2).Element(childD);
-                    row.RelativeColumn(3);
+                    row.RelativeItem(3);
+                    row.RelativeItem(4).Element(childC);
+                    row.RelativeItem(2).Element(childD);
+                    row.RelativeItem(3);
                 });
                 
-                stack.Item().Row(row =>
+                column.Item().Row(row =>
                 {
-                    row.RelativeColumn(2);
-                    row.RelativeColumn(8).Element(childE);
-                    row.RelativeColumn(2);
+                    row.RelativeItem(2);
+                    row.RelativeItem(8).Element(childE);
+                    row.RelativeItem(2);
                 });
             });
 
@@ -150,26 +150,26 @@ namespace QuestPDF.UnitTests
             // assert
             var expected = new Container();
             
-            expected.Stack(stack =>
+            expected.Column(column =>
             {
-                stack.Item().Row(row =>
+                column.Item().Row(row =>
                 {
-                    row.RelativeColumn(2);
-                    row.RelativeColumn(6).Element(childA);
-                    row.RelativeColumn(4).Element(childB);
+                    row.RelativeItem(2);
+                    row.RelativeItem(6).Element(childA);
+                    row.RelativeItem(4).Element(childB);
                 });
                 
-                stack.Item().Row(row =>
+                column.Item().Row(row =>
                 {
-                    row.RelativeColumn(6);
-                    row.RelativeColumn(4).Element(childC);
-                    row.RelativeColumn(2).Element(childD);
+                    row.RelativeItem(6);
+                    row.RelativeItem(4).Element(childC);
+                    row.RelativeItem(2).Element(childD);
                 });
                 
-                stack.Item().Row(row =>
+                column.Item().Row(row =>
                 {
-                    row.RelativeColumn(4);
-                    row.RelativeColumn(8).Element(childE);
+                    row.RelativeItem(4);
+                    row.RelativeItem(8).Element(childE);
                 });
             });
             
@@ -210,36 +210,36 @@ namespace QuestPDF.UnitTests
             // assert
             var expected = new Container();
             
-            expected.Stack(stack =>
+            expected.Column(column =>
             {
-                stack.Spacing(20);
+                column.Spacing(20);
                 
-                stack.Item().Row(row =>
+                column.Item().Row(row =>
                 {
                     row.Spacing(30);
                     
-                    row.RelativeColumn(3);
-                    row.RelativeColumn(5).Element(childA);
-                    row.RelativeColumn(5).Element(childB);
-                    row.RelativeColumn(3);
+                    row.RelativeItem(3);
+                    row.RelativeItem(5).Element(childA);
+                    row.RelativeItem(5).Element(childB);
+                    row.RelativeItem(3);
                 });
                 
-                stack.Item().Row(row =>
+                column.Item().Row(row =>
                 {
                     row.Spacing(30);
                     
-                    row.RelativeColumn(3);
-                    row.RelativeColumn(10).Element(childC);
-                    row.RelativeColumn(3);
+                    row.RelativeItem(3);
+                    row.RelativeItem(10).Element(childC);
+                    row.RelativeItem(3);
                 });
                 
-                stack.Item().Row(row =>
+                column.Item().Row(row =>
                 {
                     row.Spacing(30);
                     
-                    row.RelativeColumn(2);
-                    row.RelativeColumn(12).Element(childD);
-                    row.RelativeColumn(2);
+                    row.RelativeItem(2);
+                    row.RelativeItem(12).Element(childD);
+                    row.RelativeItem(2);
                 });
             });
             

+ 0 - 268
QuestPDF.UnitTests/RowTests.cs

@@ -1,268 +0,0 @@
-using NUnit.Framework;
-using QuestPDF.Drawing;
-using QuestPDF.Elements;
-using QuestPDF.Fluent;
-using QuestPDF.Infrastructure;
-using QuestPDF.UnitTests.TestEngine;
-
-namespace QuestPDF.UnitTests
-{
-    [TestFixture]
-    public class RowTests
-    {
-        #region Measure
-        
-        [Test]
-        public void Measure_ReturnsWrap_WhenLeftChildReturnsWrap()
-        {
-            TestPlan
-                .For(x => new BinaryRow
-                {
-                    Left = x.CreateChild("left"),
-                    Right = x.CreateChild("right")
-                })
-                .MeasureElement(new Size(400, 300))
-                .ExpectChildMeasure("left", new Size(400, 300), SpacePlan.Wrap())
-                .CheckMeasureResult(SpacePlan.Wrap());
-        }
-        
-        [Test]
-        public void Measure_ReturnsWrap_WhenRightChildReturnsWrap()
-        {
-            TestPlan
-                .For(x => new BinaryRow
-                {
-                    Left = x.CreateChild("left"),
-                    Right = x.CreateChild("right")
-                })
-                .MeasureElement(new Size(400, 300))
-                .ExpectChildMeasure("left", new Size(400, 300), SpacePlan.FullRender(250, 150))
-                .ExpectChildMeasure("right", new Size(150, 300), SpacePlan.Wrap())
-                .CheckMeasureResult(SpacePlan.Wrap());
-        }
-        
-        [Test]
-        public void Measure_ReturnsPartialRender_WhenLeftChildReturnsPartialRender()
-        {
-            TestPlan
-                .For(x => new BinaryRow
-                {
-                    Left = x.CreateChild("left"),
-                    Right = x.CreateChild("right")
-                })
-                .MeasureElement(new Size(400, 300))
-                .ExpectChildMeasure("left", new Size(400, 300), SpacePlan.PartialRender(250, 150))
-                .ExpectChildMeasure("right", new Size(150, 300), SpacePlan.FullRender(100, 100))
-                .CheckMeasureResult(SpacePlan.PartialRender(350, 150));
-        }
-        
-        [Test]
-        public void Measure_ReturnsPartialRender_WhenRightChildReturnsPartialRender()
-        {
-            TestPlan
-                .For(x => new BinaryRow
-                {
-                    Left = x.CreateChild("left"),
-                    Right = x.CreateChild("right")
-                })
-                .MeasureElement(new Size(400, 300))
-                .ExpectChildMeasure("left", new Size(400, 300), SpacePlan.FullRender(250, 150))
-                .ExpectChildMeasure("right", new Size(150, 300), SpacePlan.PartialRender(100, 100))
-                .CheckMeasureResult(SpacePlan.PartialRender(350, 150));
-        }
-        
-        [Test]
-        public void Measure_ReturnsFullRender_WhenBothChildrenReturnFullRender()
-        {
-            TestPlan
-                .For(x => new BinaryRow
-                {
-                    Left = x.CreateChild("left"),
-                    Right = x.CreateChild("right")
-                })
-                .MeasureElement(new Size(400, 300))
-                .ExpectChildMeasure("left", new Size(400, 300), SpacePlan.FullRender(200, 150))
-                .ExpectChildMeasure("right", new Size(200, 300), SpacePlan.FullRender(100, 100))
-                .CheckMeasureResult(SpacePlan.FullRender(300, 150));
-        }
-        
-        #endregion
-
-        #region Draw
-
-        [Test]
-        public void Draw()
-        {
-            TestPlan
-                .For(x => new BinaryRow
-                {
-                    Left = x.CreateChild("left"),
-                    Right = x.CreateChild("right")
-                })
-                .DrawElement(new Size(400, 300))
-                .ExpectChildMeasure("left", new Size(400, 300), SpacePlan.FullRender(250, 150))
-                .ExpectChildDraw("left", new Size(250, 300))
-                .ExpectChildMeasure("right", new Size(150, 300), SpacePlan.FullRender(150, 200))
-                .ExpectCanvasTranslate(250, 0)
-                .ExpectChildDraw("right", new Size(150, 300))
-                .ExpectCanvasTranslate(-250, 0)
-                .CheckDrawResult();
-        }
-
-        #endregion
-        
-        #region Structure
-        
-        [Test]
-        public void Structure_RelativeColumnsHandling()
-        { 
-            // arrange
-            var childA = TestPlan.CreateUniqueElement();
-            var childB = TestPlan.CreateUniqueElement();
-            var childC = TestPlan.CreateUniqueElement();
-            var childD = TestPlan.CreateUniqueElement();
-            var childE = TestPlan.CreateUniqueElement();
-
-            const int spacing = 25;
-            var availableSpace = new Size(1100, 400);
-            
-            // act
-            var value = new Container();
-
-            value.Row(row =>
-            {
-                row.Spacing(spacing);
-                
-                row.ConstantColumn(150).Element(childA);
-                row.ConstantColumn(250).Element(childB);
-                row.RelativeColumn(1).Element(childC);
-                row.RelativeColumn(2).Element(childD);
-                row.RelativeColumn(3).Element(childE);
-            });
-            
-            // assert
-            var expected = new Container();
-
-            expected.Row(row =>
-            {
-                row.Spacing(spacing);
-                
-                row.ConstantColumn(150).Element(childA);
-                row.ConstantColumn(250).Element(childB);
-                row.ConstantColumn(100).Element(childC);
-                row.ConstantColumn(200).Element(childD);
-                row.ConstantColumn(300).Element(childE);
-            });
-            
-            TestPlan.CompareOperations(value, expected, availableSpace);
-        }
-        
-        [Test]
-        public void Structure_Tree()
-        { 
-            // arrange
-            var childA = TestPlan.CreateUniqueElement();
-            var childB = TestPlan.CreateUniqueElement();
-            var childC = TestPlan.CreateUniqueElement();
-            var childD = TestPlan.CreateUniqueElement();
-            var childE = TestPlan.CreateUniqueElement();
-
-            const int spacing = 25;
-            var availableSpace = new Size(1200, 400);
-            
-            // act
-            var value = new Container();
-
-            value.Row(row =>
-            {
-                row.Spacing(spacing);
-                
-                row.ConstantColumn(150).Element(childA);
-                row.ConstantColumn(200).Element(childB);
-                row.ConstantColumn(250).Element(childC);
-                row.RelativeColumn(2).Element(childD);
-                row.RelativeColumn(3).Element(childE);
-            });
-            
-            // assert
-            var expected = new BinaryRow
-            {
-                Left = new BinaryRow
-                {
-                    Left = new BinaryRow
-                    {
-                        Left = new Constrained
-                        {
-                            MinWidth = 150,
-                            MaxWidth = 150,
-                            Child = childA
-                        },
-                        Right = new Constrained
-                        {
-                            MinWidth = 25,
-                            MaxWidth = 25
-                        }
-                    },
-                    Right = new BinaryRow
-                    {
-                        Left = new Constrained
-                        {
-                            MinWidth = 200,
-                            MaxWidth = 200,
-                            Child = childB
-                        },
-                        Right = new Constrained
-                        {
-                            MinWidth = 25,
-                            MaxWidth = 25
-                        }
-                    }
-                },
-                Right = new BinaryRow
-                {
-                    Left = new BinaryRow
-                    {
-                        Left = new Constrained
-                        {
-                            MinWidth = 250,
-                            MaxWidth = 250,
-                            Child = childC
-                        },
-                        Right = new Constrained
-                        {
-                            MinWidth = 25,
-                            MaxWidth = 25
-                        }
-                    },
-                    Right = new BinaryRow
-                    {
-                        Left = new Constrained
-                        {
-                            MinWidth = 200,
-                            MaxWidth = 200,
-                            Child = childD
-                        },
-                        Right = new BinaryRow
-                        {
-                            Left = new Constrained
-                            {
-                                MinWidth = 25,
-                                MaxWidth = 25
-                            },
-                            Right = new Constrained
-                            {
-                                MinWidth = 300,
-                                MaxWidth = 300,
-                                Child = childE
-                            }
-                        }
-                    }
-                }
-            };
-            
-            TestPlan.CompareOperations(value, expected, availableSpace);
-        }
-        
-        #endregion
-    }
-}

+ 3 - 3
QuestPDF/Drawing/DocumentContainer.cs

@@ -16,13 +16,13 @@ namespace QuestPDF.Drawing
             var container = new Container();
             
             container
-                .Stack(stack =>
+                .Column(column =>
                 {
                     Pages
                         .SelectMany(x => new List<Action>()
                         {
-                            () => stack.Item().PageBreak(),
-                            () => stack.Item().Component(x)
+                            () => column.Item().PageBreak(),
+                            () => column.Item().Component(x)
                         })
                         .Skip(1)
                         .ToList()

+ 4 - 2
QuestPDF/Drawing/DocumentGenerator.cs

@@ -18,14 +18,16 @@ namespace QuestPDF.Drawing
         internal static void GeneratePdf(Stream stream, IDocument document)
         {
             var metadata = document.GetMetadata();
-            var canvas = new PdfCanvas(stream, metadata);
+            var writeOnlyStream = new WriteOnlyStream(stream);
+            var canvas = new PdfCanvas(writeOnlyStream, metadata);
             RenderDocument(canvas, document);
         }
         
         internal static void GenerateXps(Stream stream, IDocument document)
         {
             var metadata = document.GetMetadata();
-            var canvas = new XpsCanvas(stream, metadata);
+            var writeOnlyStream = new WriteOnlyStream(stream);
+            var canvas = new XpsCanvas(writeOnlyStream, metadata);
             RenderDocument(canvas, document);
         }
         

+ 1 - 1
QuestPDF/Drawing/PdfCanvas.cs

@@ -1,5 +1,5 @@
 using System.IO;
-using QuestPDF.Infrastructure;
+using QuestPDF.Helpers;
 using SkiaSharp;
 
 namespace QuestPDF.Drawing

+ 1 - 1
QuestPDF/Drawing/XpsCanvas.cs

@@ -1,5 +1,5 @@
 using System.IO;
-using QuestPDF.Infrastructure;
+using QuestPDF.Helpers;
 using SkiaSharp;
 
 namespace QuestPDF.Drawing

+ 120 - 0
QuestPDF/Elements/Column.cs

@@ -0,0 +1,120 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using QuestPDF.Drawing;
+using QuestPDF.Infrastructure;
+
+namespace QuestPDF.Elements
+{
+    internal class ColumnItem : Container
+    {
+        public bool IsRendered { get; set; }
+    }
+    
+    internal class ColumnItemRenderingCommand
+    {
+        public ColumnItem ColumnItem { get; set; }
+        public SpacePlan Measurement { get; set; }
+        public Size Size { get; set; }
+        public Position Offset { get; set; }
+    }
+    
+    internal class Column : Element, ICacheable, IStateResettable
+    {
+        internal List<ColumnItem> Items { get; } = new();
+        internal float Spacing { get; set; }
+
+        public void ResetState()
+        {
+            Items.ForEach(x => x.IsRendered = false);
+        }
+        
+        internal override IEnumerable<Element?> GetChildren()
+        {
+            return Items;
+        }
+        
+        internal override void CreateProxy(Func<Element?, Element?> create)
+        {
+            Items.ForEach(x => x.Child = create(x.Child));
+        }
+
+        internal override SpacePlan Measure(Size availableSpace)
+        {
+            var renderingCommands = PlanLayout(availableSpace);
+
+            if (!renderingCommands.Any())
+                return SpacePlan.Wrap();
+
+            var width = renderingCommands.Max(x => x.Size.Width);
+            var height = renderingCommands.Last().Offset.Y + renderingCommands.Last().Size.Height;
+            var size = new Size(width, height);
+            
+            if (width > availableSpace.Width + Size.Epsilon || height > availableSpace.Height + Size.Epsilon)
+                return SpacePlan.Wrap();
+            
+            var totalRenderedItems = Items.Count(x => x.IsRendered) + renderingCommands.Count(x => x.Measurement.Type == SpacePlanType.FullRender);
+            var willBeFullyRendered = totalRenderedItems == Items.Count;
+
+            return willBeFullyRendered
+                ? SpacePlan.FullRender(size)
+                : SpacePlan.PartialRender(size);
+        }
+
+        internal override void Draw(Size availableSpace)
+        {
+            var renderingCommands = PlanLayout(availableSpace);
+
+            foreach (var command in renderingCommands)
+            {
+                if (command.Measurement.Type == SpacePlanType.FullRender)
+                    command.ColumnItem.IsRendered = true;
+
+                var targetSize = new Size(availableSpace.Width, command.Size.Height);
+                
+                Canvas.Translate(command.Offset);
+                command.ColumnItem.Draw(targetSize);
+                Canvas.Translate(command.Offset.Reverse());
+            }
+            
+            if (Items.All(x => x.IsRendered))
+                ResetState();
+        }
+
+        private ICollection<ColumnItemRenderingCommand> PlanLayout(Size availableSpace)
+        {
+            var topOffset = 0f;
+            var commands = new List<ColumnItemRenderingCommand>();
+
+            foreach (var item in Items)
+            {
+                if (item.IsRendered)
+                    continue;
+
+                var itemSpace = new Size(availableSpace.Width, availableSpace.Height - topOffset);
+                var measurement = item.Measure(itemSpace);
+                
+                if (measurement.Type == SpacePlanType.Wrap)
+                    break;
+
+                commands.Add(new ColumnItemRenderingCommand
+                {
+                    ColumnItem = item,
+                    Size = measurement,
+                    Measurement = measurement,
+                    Offset = new Position(0, topOffset)
+                });
+                
+                if (measurement.Type == SpacePlanType.PartialRender)
+                    break;
+                
+                topOffset += measurement.Height + Spacing;
+            }
+
+            var targetWidth = commands.Select(x => x.Size.Width).DefaultIfEmpty(0).Max();
+            commands.ForEach(x => x.Size = new Size(targetWidth, x.Size.Height));
+            
+            return commands;
+        }
+    }
+}

+ 75 - 61
QuestPDF/Elements/Decoration.cs

@@ -1,98 +1,112 @@
 using System;
 using System.Collections.Generic;
+using System.Linq;
 using QuestPDF.Drawing;
 using QuestPDF.Fluent;
 using QuestPDF.Infrastructure;
 
 namespace QuestPDF.Elements
 {
-    internal enum DecorationType
+    internal class DecorationItemRenderingCommand
     {
-        Prepend,
-        Append
+        public Element Element { get; set; }
+        public SpacePlan Measurement { get; set; }
+        public Position Offset { get; set; }
     }
     
-    internal class BinaryDecoration : Element, ICacheable
+    internal class Decoration : Element, ICacheable
     {
-        public Element DecorationElement { get; set; } = Empty.Instance;
-        public Element ContentElement { get; set; } = Empty.Instance;
-        public DecorationType Type { get; set; }
+        internal Element Before { get; set; } = new Empty();
+        internal Element Content { get; set; } = new Empty();
+        internal Element After { get; set; } = new Empty();
 
         internal override IEnumerable<Element?> GetChildren()
         {
-            yield return DecorationElement;
-            yield return ContentElement;
+            yield return Before;
+            yield return Content;
+            yield return After;
         }
-
-        internal override void CreateProxy(Func<Element, Element> create)
+        
+        internal override void CreateProxy(Func<Element?, Element?> create)
         {
-            DecorationElement = create(DecorationElement);
-            ContentElement = create(ContentElement);
+            Before = create(Before);
+            Content = create(Content);
+            After = create(After);
         }
 
         internal override SpacePlan Measure(Size availableSpace)
         {
-            var decorationMeasure = DecorationElement.Measure(availableSpace);
-            
-            if (decorationMeasure.Type == SpacePlanType.Wrap || decorationMeasure.Type == SpacePlanType.PartialRender)
-                return SpacePlan.Wrap();
+            var renderingCommands = PlanLayout(availableSpace).ToList();
 
-            var decorationSize = decorationMeasure;
-            var contentMeasure = ContentElement.Measure(new Size(availableSpace.Width, availableSpace.Height - decorationSize.Height));
-            
-            if (contentMeasure.Type == SpacePlanType.Wrap)
+            if (renderingCommands.Any(x => x.Measurement.Type == SpacePlanType.Wrap))
                 return SpacePlan.Wrap();
 
-            var contentSize = contentMeasure;
-            var resultSize = new Size(availableSpace.Width, decorationSize.Height + contentSize.Height);
-            
-            if (contentSize.Type == SpacePlanType.PartialRender)
-                return SpacePlan.PartialRender(resultSize);
+            var width = renderingCommands.Max(x => x.Measurement.Width);
+            var height = renderingCommands.Sum(x => x.Measurement.Height);
+            var size = new Size(width, height);
             
-            if (contentSize.Type == SpacePlanType.FullRender)
-                return SpacePlan.FullRender(resultSize);
+            if (width > availableSpace.Width + Size.Epsilon || height > availableSpace.Height + Size.Epsilon)
+                return SpacePlan.Wrap();
             
-            throw new NotSupportedException();
+            var willBeFullyRendered = renderingCommands.All(x => x.Measurement.Type == SpacePlanType.FullRender);
+
+            return willBeFullyRendered
+                ? SpacePlan.FullRender(size)
+                : SpacePlan.PartialRender(size);
         }
 
         internal override void Draw(Size availableSpace)
         {
-            var decorationSize = DecorationElement.Measure(availableSpace);
-            var contentSize = new Size(availableSpace.Width, availableSpace.Height - decorationSize.Height);
-
-            var translateHeight = Type == DecorationType.Prepend ? decorationSize.Height : 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 second = Type == DecorationType.Prepend ? drawContent : drawDecoration;
-
-            first();
-            Canvas.Translate(new Position(0, translateHeight));
-            second();
-            Canvas.Translate(new Position(0, -translateHeight));
+            var renderingCommands = PlanLayout(availableSpace).ToList();
+            var width = renderingCommands.Max(x => x.Measurement.Width);
+            
+            foreach (var command in renderingCommands)
+            {
+                var elementSize = new Size(width, command.Measurement.Height);
+                
+                Canvas.Translate(command.Offset);
+                command.Element.Draw(elementSize);
+                Canvas.Translate(command.Offset.Reverse());
+            }
         }
-    }
-    
-    internal class Decoration : IComponent
-    {
-        public Element Header { get; set; } = Empty.Instance;
-        public Element Content { get; set; } = Empty.Instance;
-        public Element Footer { get; set; } = Empty.Instance;
 
-        public void Compose(IContainer container)
+        private IEnumerable<DecorationItemRenderingCommand> PlanLayout(Size availableSpace)
         {
-            container.Element(new BinaryDecoration
+            SpacePlan GetDecorationMeasurement(Element element)
+            {
+                var measurement = element.Measure(availableSpace);
+                
+                return measurement.Type == SpacePlanType.FullRender 
+                    ? measurement 
+                    : SpacePlan.Wrap();
+            }
+            
+            var beforeMeasurement = GetDecorationMeasurement(Before);
+            var afterMeasurement = GetDecorationMeasurement(After);
+            
+            var contentSpace = new Size(availableSpace.Width, availableSpace.Height - beforeMeasurement.Height - afterMeasurement.Height);
+            var contentMeasurement = Content.Measure(contentSpace);
+
+            yield return new DecorationItemRenderingCommand
+            {
+                Element = Before,
+                Measurement = beforeMeasurement,
+                Offset = Position.Zero
+            };
+            
+            yield return new DecorationItemRenderingCommand
+            {
+                Element = Content,
+                Measurement = contentMeasurement,
+                Offset = new Position(0, beforeMeasurement.Height)
+            };
+
+            yield return new DecorationItemRenderingCommand
             {
-                Type = DecorationType.Prepend,
-                DecorationElement = Header,
-                ContentElement = new BinaryDecoration
-                {
-                    Type = DecorationType.Append,
-                    ContentElement = Content,
-                    DecorationElement = Footer
-                }
-            });
+                Element = After,
+                Measurement = afterMeasurement,
+                Offset = new Position(0, beforeMeasurement.Height + contentMeasurement.Height)
+            };
         }
     }
 }

+ 6 - 6
QuestPDF/Elements/Grid.cs

@@ -27,12 +27,12 @@ namespace QuestPDF.Elements
         {
             ChildrenQueue = new Queue<GridElement>(Children);
             
-            container.Stack(stack =>
+            container.Column(column =>
             {
-                stack.Spacing(VerticalSpacing);
+                column.Spacing(VerticalSpacing);
                 
                 while (ChildrenQueue.Any())
-                    stack.Item().Row(BuildRow);
+                    column.Item().Row(BuildRow);
             });
         }
         
@@ -65,12 +65,12 @@ namespace QuestPDF.Elements
                 emptySpace /= 2;
             
             if (hasEmptySpace && Alignment != HorizontalAlignment.Left)
-                row.RelativeColumn(emptySpace);
+                row.RelativeItem(emptySpace);
                 
-            elements.ForEach(x => row.RelativeColumn(x.Columns).Element(x.Child));
+            elements.ForEach(x => row.RelativeItem(x.Columns).Element(x.Child));
 
             if (hasEmptySpace && Alignment != HorizontalAlignment.Right)
-                row.RelativeColumn(emptySpace);
+                row.RelativeItem(emptySpace);
         }
     }
 }

+ 30 - 43
QuestPDF/Elements/Inlined.cs

@@ -8,15 +8,7 @@ namespace QuestPDF.Elements
 {
     internal class InlinedElement : Container
     {
-        public SpacePlan? MeasureCache { get; set; }
 
-        internal override SpacePlan Measure(Size availableSpace)
-        {
-            // TODO: once element caching proxy is introduces, this can be removed
-            
-            MeasureCache ??= Child.Measure(Size.Max);
-            return MeasureCache.Value;
-        }
     }
 
     internal enum InlinedAlignment
@@ -112,8 +104,11 @@ namespace QuestPDF.Elements
                 
                 foreach (var element in elements)
                 {
-                    var size = element.Measure(Size.Max);
+                    var size = (Size)element.Measure(Size.Max);
                     var baselineOffset = BaselineOffset(size, lineSize.Height);
+
+                    if (size.Height == 0)
+                        size = new Size(size.Width, lineSize.Height);
                     
                     Canvas.Translate(new Position(0, baselineOffset));
                     element.Draw(size);
@@ -132,48 +127,39 @@ namespace QuestPDF.Elements
                     if (elements.Count == 1)
                         return 0;
 
-                    if (ElementsAlignment == InlinedAlignment.Justify)
-                        return difference / (elements.Count - 1);
-                    
-                    if (ElementsAlignment == InlinedAlignment.SpaceAround)
-                        return difference / (elements.Count + 1);
-                    
-                    return HorizontalSpacing;
+                    return ElementsAlignment switch
+                    {
+                        InlinedAlignment.Justify => difference / (elements.Count - 1),
+                        InlinedAlignment.SpaceAround => difference / (elements.Count + 1),
+                        _ => HorizontalSpacing
+                    };
                 }
 
                 float AlignOffset()
                 {
-                    if (ElementsAlignment == InlinedAlignment.Left)
-                        return 0;
-                    
-                    if (ElementsAlignment == InlinedAlignment.Justify)
-                        return 0;
-                    
-                    if (ElementsAlignment == InlinedAlignment.SpaceAround)
-                        return elementOffset;
-
                     var difference = availableSpace.Width - lineSize.Width - (elements.Count - 1) * HorizontalSpacing;
-                    
-                    if (ElementsAlignment == InlinedAlignment.Center)
-                        return difference / 2;
-
-                    if (ElementsAlignment == InlinedAlignment.Right)
-                        return difference;
 
-                    return 0;
+                    return ElementsAlignment switch
+                    {
+                        InlinedAlignment.Left => 0,
+                        InlinedAlignment.Justify => 0,
+                        InlinedAlignment.SpaceAround => elementOffset,
+                        InlinedAlignment.Center => difference / 2,
+                        InlinedAlignment.Right => difference,
+                        _ => 0
+                    };
                 }
                 
                 float BaselineOffset(Size elementSize, float lineHeight)
                 {
-                    if (BaselineAlignment == VerticalAlignment.Top)
-                        return 0;
-
                     var difference = lineHeight - elementSize.Height;
-                    
-                    if (BaselineAlignment == VerticalAlignment.Middle)
-                        return difference / 2;
 
-                    return difference;
+                    return BaselineAlignment switch
+                    {
+                        VerticalAlignment.Top => 0,
+                        VerticalAlignment.Middle => difference / 2,
+                        _ => difference
+                    };
                 }
             }
         }
@@ -250,11 +236,12 @@ namespace QuestPDF.Elements
             float GetInitialAlignmentOffset()
             {
                 // this method makes sure that the spacing between elements is no lesser than configured
-                
-                if (ElementsAlignment == InlinedAlignment.SpaceAround)
-                    return HorizontalSpacing * 2;
 
-                return 0;
+                return ElementsAlignment switch
+                {
+                    InlinedAlignment.SpaceAround => HorizontalSpacing * 2,
+                    _ => 0
+                };
             }
         }
     }

+ 46 - 0
QuestPDF/Elements/Line.cs

@@ -0,0 +1,46 @@
+using QuestPDF.Drawing;
+using QuestPDF.Helpers;
+using QuestPDF.Infrastructure;
+
+namespace QuestPDF.Elements
+{
+    public interface ILine
+    {
+        
+    }
+    
+    internal enum LineType
+    {
+        Vertical,
+        Horizontal
+    }
+    
+    internal class Line : Element, ILine, ICacheable
+    {
+        public LineType Type { get; set; } = LineType.Vertical;
+        public string Color { get; set; } = Colors.Black;
+        public float Size { get; set; } = 1;
+        
+        internal override SpacePlan Measure(Size availableSpace)
+        {
+            return Type switch
+            {
+                LineType.Vertical when availableSpace.Width >= Size => SpacePlan.FullRender(Size, 0),
+                LineType.Horizontal when availableSpace.Height >= Size => SpacePlan.FullRender(0, Size),
+                _ => SpacePlan.Wrap()
+            };
+        }
+
+        internal override void Draw(Size availableSpace)
+        {
+            if (Type == LineType.Vertical)
+            {
+                Canvas.DrawRectangle(new Position(-Size/2, 0), new Size(Size, availableSpace.Height), Color);
+            }
+            else if (Type == LineType.Horizontal)
+            {
+                Canvas.DrawRectangle(new Position(0, -Size/2), new Size(availableSpace.Width, Size), Color);
+            }
+        }
+    }
+}

+ 2 - 2
QuestPDF/Elements/Page.cs

@@ -45,7 +45,7 @@ namespace QuestPDF.Elements
                 .Decoration(decoration =>
                 {
                     decoration
-                        .Header()
+                        .Before()
                         .DebugPointer("Page header")
                         .Element(Header);
                     
@@ -57,7 +57,7 @@ namespace QuestPDF.Elements
                         .Element(Content);
                     
                     decoration
-                        .Footer()
+                        .After()
                         .DebugPointer("Page footer")
                         .Element(Footer);
                 });

+ 98 - 119
QuestPDF/Elements/Row.cs

@@ -1,4 +1,5 @@
 using System;
+using System.Collections;
 using System.Collections.Generic;
 using System.Linq;
 using QuestPDF.Drawing;
@@ -6,174 +7,152 @@ using QuestPDF.Infrastructure;
 
 namespace QuestPDF.Elements
 {
-    internal class RowElement : Constrained
+    internal enum RowItemType
     {
-        public float ConstantSize { get; }
-        public float RelativeSize { get; }
-
-        public RowElement(float constantSize, float relativeSize)
-        {
-            ConstantSize = constantSize;
-            RelativeSize = relativeSize;
-        }
+        Auto,
+        Constant,
+        Relative
+    }
+    
+    internal class RowItem : Container
+    {
+        public bool IsRendered { get; set; }
+        public float Width { get; set; }
         
-        public void SetWidth(float width)
-        {
-            MinWidth = width;
-            MaxWidth = width;
-        }
+        public RowItemType Type { get; set; }
+        public float Size { get; set; }
+    }
+
+    internal class RowItemRenderingCommand
+    {
+        public RowItem RowItem { get; set; }
+        public SpacePlan Measurement { get; set; }
+        public Size Size { get; set; }
+        public Position Offset { get; set; }
     }
     
-    internal class BinaryRow : Element, ICacheable, IStateResettable
+    internal class Row : Element, ICacheable, IStateResettable
     {
-        internal Element Left { get; set; }
-        internal Element Right { get; set; }
+        internal List<RowItem> Items { get; } = new();
+        internal float Spacing { get; set; }
 
-        private bool IsLeftRendered { get; set; } 
-        private bool IsRightRendered { get; set; } 
-        
         public void ResetState()
         {
-            IsLeftRendered = false;
-            IsRightRendered = false;
+            Items.ForEach(x => x.IsRendered = false);
         }
         
         internal override IEnumerable<Element?> GetChildren()
         {
-            yield return Left;
-            yield return Right;
+            return Items;
         }
         
         internal override void CreateProxy(Func<Element?, Element?> create)
         {
-            Left = create(Left);
-            Right = create(Right);
+            Items.ForEach(x => x.Child = create(x.Child));
         }
 
         internal override SpacePlan Measure(Size availableSpace)
         {
-            var leftMeasurement = Left.Measure(new Size(availableSpace.Width, availableSpace.Height));
-            
-            if (leftMeasurement.Type == SpacePlanType.Wrap)
-                return SpacePlan.Wrap();
-
-            var rightMeasurement = Right.Measure(new Size(availableSpace.Width - leftMeasurement.Width, availableSpace.Height));
+            UpdateItemsWidth(availableSpace.Width);
+            var renderingCommands = PlanLayout(availableSpace);
 
-            if (rightMeasurement.Type == SpacePlanType.Wrap)
+            if (renderingCommands.Any(x => !x.RowItem.IsRendered && x.Measurement.Type == SpacePlanType.Wrap))
                 return SpacePlan.Wrap();
 
-            var totalWidth = leftMeasurement.Width + rightMeasurement.Width;
-            var totalHeight = Math.Max(leftMeasurement.Height, rightMeasurement.Height);
+            var width = renderingCommands.Last().Offset.X + renderingCommands.Last().Size.Width;
+            var height = renderingCommands.Max(x => x.Size.Height);
+            var size = new Size(width, height);
 
-            var targetSize = new Size(totalWidth, totalHeight);
-
-            if ((!IsLeftRendered && leftMeasurement.Type == SpacePlanType.PartialRender) || 
-                (!IsRightRendered && rightMeasurement.Type == SpacePlanType.PartialRender))
-                return SpacePlan.PartialRender(targetSize);
+            if (width > availableSpace.Width + Size.Epsilon || height > availableSpace.Height + Size.Epsilon)
+                return SpacePlan.Wrap();
+            
+            if (renderingCommands.Any(x => !x.RowItem.IsRendered && x.Measurement.Type == SpacePlanType.PartialRender))
+                return SpacePlan.PartialRender(size);
 
-            return SpacePlan.FullRender(targetSize);
+            return SpacePlan.FullRender(size);
         }
 
         internal override void Draw(Size availableSpace)
         {
-            var leftSpace = new Size(availableSpace.Width, availableSpace.Height);
-            var leftMeasurement = Left.Measure(leftSpace);
-
-            if (leftMeasurement.Type == SpacePlanType.FullRender)
-                IsLeftRendered = true;
-            
-            Left.Draw(new Size(leftMeasurement.Width, availableSpace.Height));
+            UpdateItemsWidth(availableSpace.Width);
+            var renderingCommands = PlanLayout(availableSpace);
 
-            var rightSpace = new Size(availableSpace.Width - leftMeasurement.Width, availableSpace.Height);
-            var rightMeasurement = Right.Measure(rightSpace);
-            
-            if (rightMeasurement.Type == SpacePlanType.FullRender)
-                IsRightRendered = true;
+            foreach (var command in renderingCommands)
+            {
+                if (command.Measurement.Type == SpacePlanType.FullRender)
+                    command.RowItem.IsRendered = true;
+                
+                if (command.Measurement.Type == SpacePlanType.Wrap)
+                    continue;
+
+                Canvas.Translate(command.Offset);
+                command.RowItem.Draw(command.Size);
+                Canvas.Translate(command.Offset.Reverse());
+            }
             
-            Canvas.Translate(new Position(leftMeasurement.Width, 0));
-            Right.Draw(rightSpace);
-            Canvas.Translate(new Position(-leftMeasurement.Width, 0));
+            if (Items.All(x => x.IsRendered))
+                ResetState();
         }
-    }
-    
-    internal class Row : Element
-    {
-        public float Spacing { get; set; } = 0;
-        
-        public ICollection<RowElement> Items { get; internal set; } = new List<RowElement>();
-        private Element? RootElement { get; set; }
 
-        internal override IEnumerable<Element?> GetChildren()
+        private void UpdateItemsWidth(float availableWidth)
         {
-            if (RootElement == null)
-                ComposeTree();
-
-            yield return RootElement;
-        }
+            HandleItemsWithAutoWidth();
+            
+            var constantWidth = Items.Where(x => x.Type == RowItemType.Constant).Sum(x => x.Size);
+            var relativeWidth = Items.Where(x => x.Type == RowItemType.Relative).Sum(x => x.Size);
+            var spacingWidth = (Items.Count - 1) * Spacing;
 
-        internal override SpacePlan Measure(Size availableSpace)
-        {
-            UpdateElementsWidth(availableSpace.Width);
-            return RootElement.Measure(availableSpace);
-        }
+            foreach (var item in Items.Where(x => x.Type == RowItemType.Constant))
+                item.Width = item.Size;
+            
+            if (relativeWidth <= 0)
+                return;
 
-        internal override void Draw(Size availableSpace)
-        {
-            UpdateElementsWidth(availableSpace.Width);
-            RootElement.Draw(availableSpace);
-        }
-        
-        #region structure
-        
-        private void ComposeTree()
-        {
-            Items = AddSpacing(Items, Spacing);
+            var widthPerRelativeUnit = (availableWidth - constantWidth - spacingWidth) / relativeWidth;
             
-            var elements = Items.Cast<Element>().ToArray();
-            RootElement = BuildTree(elements);
+            foreach (var item in Items.Where(x => x.Type == RowItemType.Relative))
+                item.Width = item.Size * widthPerRelativeUnit;
         }
 
-        private void UpdateElementsWidth(float availableWidth)
+        private void HandleItemsWithAutoWidth()
         {
-            var constantWidth = Items.Sum(x => x.ConstantSize);
-            var relativeWidth = Items.Sum(x => x.RelativeSize);
-
-            var widthPerRelativeUnit = (relativeWidth > 0) ? (availableWidth - constantWidth) / relativeWidth : 0;
-            
-            foreach (var row in Items)
+            foreach (var rowItem in Items.Where(x => x.Type == RowItemType.Auto))
             {
-                row.SetWidth(row.ConstantSize + row.RelativeSize * widthPerRelativeUnit);
+                rowItem.Size = rowItem.Measure(Size.Max).Width;
+                rowItem.Type = RowItemType.Constant;
             }
         }
-        
-        private static ICollection<RowElement> AddSpacing(ICollection<RowElement> elements, float spacing)
-        {
-            if (spacing < Size.Epsilon)
-                return elements;
-            
-            return elements
-                .SelectMany(x => new[] { new RowElement(spacing, 0), x })
-                .Skip(1)
-                .ToList();
-        }
 
-        private static Element BuildTree(Span<Element> elements)
+        private ICollection<RowItemRenderingCommand> PlanLayout(Size availableSpace)
         {
-            if (elements.IsEmpty)
-                return Empty.Instance;
+            var leftOffset = 0f;
+            var renderingCommands = new List<RowItemRenderingCommand>();
 
-            if (elements.Length == 1)
-                return elements[0];
+            foreach (var item in Items)
+            {
+                var itemSpace = new Size(item.Width, availableSpace.Height);
+                
+                var command = new RowItemRenderingCommand
+                {
+                    RowItem = item,
+                    Size = itemSpace,
+                    Measurement = item.Measure(itemSpace),
+                    Offset = new Position(leftOffset, 0)
+                };
+                
+                renderingCommands.Add(command);
+                leftOffset += item.Width + Spacing;
+            }
 
-            var half = elements.Length / 2;
+            var rowHeight = renderingCommands.Where(x => !x.RowItem.IsRendered).Max(x => x.Measurement.Height);
             
-            return new BinaryRow
+            foreach (var command in renderingCommands)
             {
-                Left = BuildTree(elements.Slice(0, half)),
-                Right = BuildTree(elements.Slice(half))
-            };
+                command.Size = new Size(command.Size.Width, rowHeight);
+                command.Measurement = command.RowItem.Measure(command.Size);
+            }
+            
+            return renderingCommands;
         }
-        
-        #endregion
     }
 }

+ 78 - 0
QuestPDF/Elements/ScaleToFit.cs

@@ -0,0 +1,78 @@
+using System;
+using System.Linq;
+using QuestPDF.Drawing;
+using QuestPDF.Infrastructure;
+
+namespace QuestPDF.Elements
+{
+    internal class ScaleToFit : ContainerElement
+    {
+        internal override SpacePlan Measure(Size availableSpace)
+        {
+            if (Child == null)
+                return SpacePlan.FullRender(Size.Zero);
+
+            var perfectScale = FindPerfectScale(Child, availableSpace);
+
+            if (perfectScale == null)
+                return SpacePlan.Wrap();
+
+            var targetSpace = ScaleSize(availableSpace, perfectScale.Value);
+            return SpacePlan.FullRender(targetSpace);
+        }
+        
+        internal override void Draw(Size availableSpace)
+        {
+            var perfectScale = FindPerfectScale(Child, availableSpace);
+            
+            if (!perfectScale.HasValue)
+                return;
+
+            var targetScale = perfectScale.Value;
+            var targetSpace = ScaleSize(availableSpace, 1 / targetScale);
+            
+            Canvas.Scale(targetScale, targetScale);
+            Child?.Draw(targetSpace);
+            Canvas.Scale(1 / targetScale, 1 / targetScale);
+        }
+
+        private static Size ScaleSize(Size size, float factor)
+        {
+            return new Size(size.Width * factor, size.Height * factor);
+        }
+        
+        private static float? FindPerfectScale(Element child, Size availableSpace)
+        {
+            if (ChildFits(1))
+                return 1;
+            
+            var maxScale = 1f;
+            var minScale = Size.Epsilon;
+
+            var lastWorkingScale = (float?)null;
+            
+            foreach (var _ in Enumerable.Range(0, 8))
+            {
+                var halfScale = (maxScale + minScale) / 2;
+
+                if (ChildFits(halfScale))
+                {
+                    minScale = halfScale;
+                    lastWorkingScale = halfScale;
+                }
+                else
+                {
+                    maxScale = halfScale;
+                }
+            }
+            
+            return lastWorkingScale;
+            
+            bool ChildFits(float scale)
+            {
+                var scaledSpace = ScaleSize(availableSpace, 1 / scale);
+                return child.Measure(scaledSpace).Type == SpacePlanType.FullRender;
+            }
+        }
+    }
+}

+ 0 - 143
QuestPDF/Elements/Stack.cs

@@ -1,143 +0,0 @@
-using System;
-using System.Collections.Generic;
-using System.Linq;
-using QuestPDF.Drawing;
-using QuestPDF.Fluent;
-using QuestPDF.Infrastructure;
-using IComponent = QuestPDF.Infrastructure.IComponent;
-using IContainer = QuestPDF.Infrastructure.IContainer;
-
-namespace QuestPDF.Elements
-{
-    internal class BinaryStack : Element, IStateResettable, ICacheable
-    {
-        internal Element First { get; set; } = Empty.Instance;
-        internal Element Second { get; set; } = Empty.Instance;
-
-        internal bool IsFirstRendered { get; set; } = false;
-
-        internal override IEnumerable<Element?> GetChildren()
-        {
-            yield return First;
-            yield return Second;
-        }
-
-        public void ResetState()
-        {
-            IsFirstRendered = false;
-        }
-
-        internal override void CreateProxy(Func<Element?, Element?> create)
-        {
-            First = create(First);
-            Second = create(Second);
-        }
-        
-        internal override SpacePlan Measure(Size availableSpace)
-        {
-            var firstElement = IsFirstRendered ? Empty.Instance : First;
-            var firstSize = firstElement.Measure(availableSpace);
-
-            if (firstSize.Type == SpacePlanType.Wrap)
-                return SpacePlan.Wrap();
-            
-            if (firstSize.Type == SpacePlanType.PartialRender)
-                return firstSize;
-                
-            var spaceForSecond = new Size(availableSpace.Width, availableSpace.Height - firstSize.Height);
-            var secondSize = Second.Measure(spaceForSecond);
-
-            if (secondSize.Type == SpacePlanType.Wrap)
-                return SpacePlan.PartialRender(firstSize);
-
-            var totalWidth = Math.Max(firstSize.Width, secondSize.Width);
-            var totalHeight = firstSize.Height + secondSize.Height;
-            var targetSize = new Size(totalWidth, totalHeight);
-
-            if (secondSize.Type == SpacePlanType.PartialRender)
-                return SpacePlan.PartialRender(targetSize);
-                
-            return SpacePlan.FullRender(targetSize);
-        }
-
-        internal override void Draw(Size availableSpace)
-        {
-            var firstElement = IsFirstRendered ? Empty.Instance : First;
-
-            var firstMeasurement = firstElement.Measure(availableSpace);
-
-            if (firstMeasurement.Type == SpacePlanType.FullRender)
-                IsFirstRendered = true;
-
-            var firstSize = firstMeasurement;
-
-            if (firstSize.Type != SpacePlanType.Wrap)
-                firstElement.Draw(new Size(availableSpace.Width, firstSize.Height));
-
-            if (firstMeasurement.Type == SpacePlanType.Wrap || firstMeasurement.Type == SpacePlanType.PartialRender)
-                return;
-
-            var firstHeight = firstSize.Height;
-            var spaceForSecond = new Size(availableSpace.Width, availableSpace.Height - firstHeight);
-            var secondMeasurement = Second.Measure(spaceForSecond);
-
-            if (secondMeasurement.Type == SpacePlanType.Wrap)
-                return;
-
-            Canvas.Translate(new Position(0, firstHeight));
-            Second.Draw(new Size(availableSpace.Width, secondMeasurement.Height));
-            Canvas.Translate(new Position(0, -firstHeight));
-            
-            if (secondMeasurement.Type == SpacePlanType.FullRender)
-                IsFirstRendered = false;
-        }
-    }
-    
-    internal class Stack : IComponent
-    {
-        public ICollection<Element> Items { get; } = new List<Element>();
-        public float Spacing { get; set; } = 0;
-        
-        public void Compose(IContainer container)
-        {
-            var elements = AddSpacing(Spacing, Items);
-
-            container
-                .PaddingBottom(-Spacing)    
-                .Element(BuildTree(elements.ToArray()));
-        }
-        
-        static ICollection<Element> AddSpacing(float spacing, ICollection<Element> elements)
-        {
-            if (spacing < Size.Epsilon)
-                return elements;
-                
-            return elements
-                .Where(x => !(x is Empty))
-                .Select(x => new Padding
-                {
-                    Bottom = spacing,
-                    Child = x
-                })
-                .Cast<Element>()
-                .ToList();
-        }
-
-        static Element BuildTree(Span<Element> elements)
-        {
-            if (elements.IsEmpty)
-                return Empty.Instance;
-
-            if (elements.Length == 1)
-                return elements[0];
-
-            var half = elements.Length / 2;
-                
-            return new BinaryStack
-            {
-                First = BuildTree(elements.Slice(0, half)),
-                Second = BuildTree(elements.Slice(half))
-            };
-        }
-    }
-}

+ 25 - 0
QuestPDF/Elements/StopPaging.cs

@@ -0,0 +1,25 @@
+using System;
+using QuestPDF.Drawing;
+using QuestPDF.Infrastructure;
+
+namespace QuestPDF.Elements
+{
+    internal class StopPaging : ContainerElement
+    {
+        internal override SpacePlan Measure(Size availableSpace)
+        {
+            if (Child == null)
+                return SpacePlan.FullRender(Size.Zero);
+
+            var measurement = Child.Measure(availableSpace);
+
+            return measurement.Type switch
+            {
+                SpacePlanType.Wrap => SpacePlan.FullRender(Size.Zero),
+                SpacePlanType.PartialRender => SpacePlan.FullRender(measurement),
+                SpacePlanType.FullRender => measurement,
+                _ => throw new ArgumentOutOfRangeException()
+            };
+        }
+    }
+}

+ 20 - 14
QuestPDF/Fluent/BorderExtensions.cs

@@ -14,39 +14,45 @@ namespace QuestPDF.Fluent
             return element.Element(border);
         }
         
-        public static IContainer Border(this IContainer element, float value)
+        public static IContainer Border(this IContainer element, float value, Unit unit = Unit.Point)
         {
-            return element.BorderHorizontal(value).BorderVertical(value);
+            return element
+                .BorderHorizontal(value, unit)
+                .BorderVertical(value, unit);
         }
         
-        public static IContainer BorderVertical(this IContainer element, float value)
+        public static IContainer BorderVertical(this IContainer element, float value, Unit unit = Unit.Point)
         {
-            return element.BorderLeft(value).BorderRight(value);
+            return element
+                .BorderLeft(value, unit)
+                .BorderRight(value, unit);
         }
         
-        public static IContainer BorderHorizontal(this IContainer element, float value)
+        public static IContainer BorderHorizontal(this IContainer element, float value, Unit unit = Unit.Point)
         {
-            return element.BorderTop(value).BorderBottom(value);
+            return element
+                .BorderTop(value, unit)
+                .BorderBottom(value, unit);
         }
         
-        public static IContainer BorderLeft(this IContainer element, float value)
+        public static IContainer BorderLeft(this IContainer element, float value, Unit unit = Unit.Point)
         {
-            return element.Border(x => x.Left = value);
+            return element.Border(x => x.Left = value.ToPoints(unit));
         }
         
-        public static IContainer BorderRight(this IContainer element, float value)
+        public static IContainer BorderRight(this IContainer element, float value, Unit unit = Unit.Point)
         {
-            return element.Border(x => x.Right = value);
+            return element.Border(x => x.Right = value.ToPoints(unit));
         }
         
-        public static IContainer BorderTop(this IContainer element, float value)
+        public static IContainer BorderTop(this IContainer element, float value, Unit unit = Unit.Point)
         {
-            return element.Border(x => x.Top = value);
+            return element.Border(x => x.Top = value.ToPoints(unit));
         }
         
-        public static IContainer BorderBottom(this IContainer element, float value)
+        public static IContainer BorderBottom(this IContainer element, float value, Unit unit = Unit.Point)
         {
-            return element.Border(x => x.Bottom = value);
+            return element.Border(x => x.Bottom = value.ToPoints(unit));
         }
         
         public static IContainer BorderColor(this IContainer element, string color)

+ 44 - 0
QuestPDF/Fluent/ColumnExtensions.cs

@@ -0,0 +1,44 @@
+using System;
+using QuestPDF.Elements;
+using QuestPDF.Infrastructure;
+
+namespace QuestPDF.Fluent
+{
+    public class ColumnDescriptor
+    {
+        internal Column Column { get; } = new();
+
+        public void Spacing(float value, Unit unit = Unit.Point)
+        {
+            Column.Spacing = value.ToPoints(unit);
+        }
+        
+        public IContainer Item()
+        {
+            var container = new Container();
+            
+            Column.Items.Add(new ColumnItem
+            {
+                Child = container
+            });
+            
+            return container;
+        }
+    }
+    
+    public static class ColumnExtensions
+    {
+        [Obsolete("This element has been renamed since version 2022.2. Please use the 'Column' method.")]
+        public static void Stack(this IContainer element, Action<ColumnDescriptor> handler)
+        {
+            element.Column(handler);
+        }
+        
+        public static void Column(this IContainer element, Action<ColumnDescriptor> handler)
+        {
+            var descriptor = new ColumnDescriptor();
+            handler(descriptor);
+            element.Element(descriptor.Column);
+        }
+    }
+}

+ 16 - 12
QuestPDF/Fluent/ConstrainedExtensions.cs

@@ -14,34 +14,38 @@ namespace QuestPDF.Fluent
             return element.Element(constrained);
         }
         
-        public static IContainer Width(this IContainer element, float value)
+        public static IContainer Width(this IContainer element, float value, Unit unit = Unit.Point)
         {
-            return element.MinWidth(value).MaxWidth(value);
+            return element
+                .MinWidth(value, unit)
+                .MaxWidth(value, unit);
         }
         
-        public static IContainer MinWidth(this IContainer element, float value)
+        public static IContainer MinWidth(this IContainer element, float value, Unit unit = Unit.Point)
         {
-            return element.Constrained(x => x.MinWidth = value);
+            return element.Constrained(x => x.MinWidth = value.ToPoints(unit));
         }
         
-        public static IContainer MaxWidth(this IContainer element, float value)
+        public static IContainer MaxWidth(this IContainer element, float value, Unit unit = Unit.Point)
         {
-            return element.Constrained(x => x.MaxWidth = value);
+            return element.Constrained(x => x.MaxWidth = value.ToPoints(unit));
         }
         
-        public static IContainer Height(this IContainer element, float value)
+        public static IContainer Height(this IContainer element, float value, Unit unit = Unit.Point)
         {
-            return element.MinHeight(value).MaxHeight(value);
+            return element
+                .MinHeight(value, unit)
+                .MaxHeight(value, unit);
         }
         
-        public static IContainer MinHeight(this IContainer element, float value)
+        public static IContainer MinHeight(this IContainer element, float value, Unit unit = Unit.Point)
         {
-            return element.Constrained(x => x.MinHeight = value);
+            return element.Constrained(x => x.MinHeight = value.ToPoints(unit));
         }
         
-        public static IContainer MaxHeight(this IContainer element, float value)
+        public static IContainer MaxHeight(this IContainer element, float value, Unit unit = Unit.Point)
         {
-            return element.Constrained(x => x.MaxHeight = value);
+            return element.Constrained(x => x.MaxHeight = value.ToPoints(unit));
         }
     }
 }

+ 38 - 6
QuestPDF/Fluent/DecorationExtensions.cs

@@ -8,16 +8,16 @@ namespace QuestPDF.Fluent
     {
         internal Decoration Decoration { get; } = new Decoration();
         
-        public IContainer Header()
+        public IContainer Before()
         {
             var container = new Container();
-            Decoration.Header = container;
+            Decoration.Before = container;
             return container;
         }
         
-        public void Header(Action<IContainer> handler)
+        public void Before(Action<IContainer> handler)
         {
-            handler?.Invoke(Header());
+            handler?.Invoke(Before());
         }
         
         public IContainer Content()
@@ -32,17 +32,49 @@ namespace QuestPDF.Fluent
             handler?.Invoke(Content());
         }
         
+        public IContainer After()
+        {
+            var container = new Container();
+            Decoration.After = container;
+            return container;
+        }
+        
+        public void After(Action<IContainer> handler)
+        {
+            handler?.Invoke(After());
+        }
+
+        #region Obsolete
+
+        [Obsolete("This element has been renamed since version 2022.2. Please use the 'Before' method.")]
+        public IContainer Header()
+        {
+            var container = new Container();
+            Decoration.Before = container;
+            return container;
+        }
+        
+        [Obsolete("This element has been renamed since version 2022.2. Please use the 'Before' method.")]
+        public void Header(Action<IContainer> handler)
+        {
+            handler?.Invoke(Header());
+        }
+        
+        [Obsolete("This element has been renamed since version 2022.2. Please use the 'After' method.")]
         public IContainer Footer()
         {
             var container = new Container();
-            Decoration.Footer = container;
+            Decoration.After = container;
             return container;
         }
         
+        [Obsolete("This element has been renamed since version 2022.2. Please use the 'After' method.")]
         public void Footer(Action<IContainer> handler)
         {
             handler?.Invoke(Footer());
         }
+
+        #endregion
     }
     
     public static class DecorationExtensions
@@ -52,7 +84,7 @@ namespace QuestPDF.Fluent
             var descriptor = new DecorationDescriptor();
             handler(descriptor);
             
-            element.Component(descriptor.Decoration);
+            element.Element(descriptor.Decoration);
         }
     }
 }

+ 11 - 2
QuestPDF/Fluent/ElementExtensions.cs

@@ -136,8 +136,7 @@ namespace QuestPDF.Fluent
             });
         }
         
-        // TODO: deprecated Box method in QuestPDF 2022.1
-        [Obsolete("This element has been renamed. Please use the MinimalBox method.")]
+        [Obsolete("This element has been renamed since version 2022.1. Please use the MinimalBox method.")]
         public static IContainer Box(this IContainer element)
         {
             return element.Element(new MinimalBox());
@@ -160,5 +159,15 @@ namespace QuestPDF.Fluent
                 TextStyle = textStyle
             });
         }
+
+        public static IContainer StopPaging(this IContainer element)
+        {
+            return element.Element(new StopPaging());
+        }
+        
+        public static IContainer ScaleToFit(this IContainer element)
+        {
+            return element.Element(new ScaleToFit());
+        }
     }
 }

+ 7 - 7
QuestPDF/Fluent/GridExtensions.cs

@@ -8,20 +8,20 @@ namespace QuestPDF.Fluent
     {
         internal Grid Grid { get; } = new Grid();
         
-        public void Spacing(float value)
+        public void Spacing(float value, Unit unit = Unit.Point)
         {
-            VerticalSpacing(value);
-            HorizontalSpacing(value);
+            VerticalSpacing(value, unit);
+            HorizontalSpacing(value, unit);
         }
         
-        public void VerticalSpacing(float value)
+        public void VerticalSpacing(float value, Unit unit = Unit.Point)
         {
-            Grid.VerticalSpacing = value;
+            Grid.VerticalSpacing = value.ToPoints(unit);
         }
          
-        public void HorizontalSpacing(float value)
+        public void HorizontalSpacing(float value, Unit unit = Unit.Point)
         {
-            Grid.HorizontalSpacing = value;
+            Grid.HorizontalSpacing = value.ToPoints(unit);
         }
         
         public void Columns(int value = Grid.DefaultColumnsCount)

+ 12 - 5
QuestPDF/Fluent/InlinedExtensions.cs

@@ -10,14 +10,21 @@ namespace QuestPDF.Fluent
     {
         internal Inlined Inlined { get; } = new Inlined();
         
-        public void Spacing(float value)
+        public void Spacing(float value, Unit unit = Unit.Point)
         {
-            VerticalSpacing(value);
-            HorizontalSpacing(value);
+            VerticalSpacing(value, unit);
+            HorizontalSpacing(value, unit);
         }
         
-        public void VerticalSpacing(float value) => Inlined.VerticalSpacing = value;
-        public void HorizontalSpacing(float value) => Inlined.HorizontalSpacing = value;
+        public void VerticalSpacing(float value, Unit unit = Unit.Point)
+        {
+            Inlined.VerticalSpacing = value.ToPoints(unit);
+        }
+
+        public void HorizontalSpacing(float value, Unit unit = Unit.Point)
+        {
+            Inlined.HorizontalSpacing = value.ToPoints(unit);
+        }
 
         public void BaselineTop() => Inlined.BaselineAlignment = VerticalAlignment.Top;
         public void BaselineMiddle() => Inlined.BaselineAlignment = VerticalAlignment.Middle;

+ 36 - 0
QuestPDF/Fluent/LineExtensions.cs

@@ -0,0 +1,36 @@
+using System;
+using QuestPDF.Elements;
+using QuestPDF.Infrastructure;
+
+namespace QuestPDF.Fluent
+{
+    public static class LineExtensions
+    {
+        private static ILine Line(this IContainer element, LineType type, float size)
+        {
+            var line = new Line
+            {
+                Size = size,
+                Type = type
+            };
+
+            element.Element(line);
+            return line;
+        }
+        
+        public static ILine LineVertical(this IContainer element, float size, Unit unit = Unit.Point)
+        {
+            return element.Line(LineType.Vertical, size.ToPoints(unit));
+        }
+        
+        public static ILine LineHorizontal(this IContainer element, float size, Unit unit = Unit.Point)
+        {
+            return element.Line(LineType.Horizontal, size.ToPoints(unit));
+        }
+        
+        public static void LineColor(this ILine descriptor, string value)
+        {
+            (descriptor as Line).Color = value;
+        }
+    }
+}

+ 20 - 14
QuestPDF/Fluent/PaddingExtensions.cs

@@ -14,39 +14,45 @@ namespace QuestPDF.Fluent
             return element.Element(padding);
         }
         
-        public static IContainer Padding(this IContainer element, float value)
+        public static IContainer Padding(this IContainer element, float value, Unit unit = Unit.Point)
         {
-            return element.PaddingVertical(value).PaddingHorizontal(value);
+            return element
+                .PaddingVertical(value, unit)
+                .PaddingHorizontal(value, unit);
         }
         
-        public static IContainer PaddingHorizontal(this IContainer element, float value)
+        public static IContainer PaddingHorizontal(this IContainer element, float value, Unit unit = Unit.Point)
         {
-            return element.PaddingLeft(value).PaddingRight(value);
+            return element
+                .PaddingLeft(value, unit)
+                .PaddingRight(value, unit);
         }
         
-        public static IContainer PaddingVertical(this IContainer element, float value)
+        public static IContainer PaddingVertical(this IContainer element, float value, Unit unit = Unit.Point)
         {
-            return element.PaddingTop(value).PaddingBottom(value);
+            return element
+                .PaddingTop(value, unit)
+                .PaddingBottom(value, unit);
         }
         
-        public static IContainer PaddingTop(this IContainer element, float value)
+        public static IContainer PaddingTop(this IContainer element, float value, Unit unit = Unit.Point)
         {
-            return element.Padding(x => x.Top = value);
+            return element.Padding(x => x.Top += value.ToPoints(unit));
         }
         
-        public static IContainer PaddingBottom(this IContainer element, float value)
+        public static IContainer PaddingBottom(this IContainer element, float value, Unit unit = Unit.Point)
         {
-            return element.Padding(x => x.Bottom = value);
+            return element.Padding(x => x.Bottom += value.ToPoints(unit));
         }
         
-        public static IContainer PaddingLeft(this IContainer element, float value)
+        public static IContainer PaddingLeft(this IContainer element, float value, Unit unit = Unit.Point)
         {
-            return element.Padding(x => x.Left = value);
+            return element.Padding(x => x.Left += value.ToPoints(unit));
         }
         
-        public static IContainer PaddingRight(this IContainer element, float value)
+        public static IContainer PaddingRight(this IContainer element, float value, Unit unit = Unit.Point)
         {
-            return element.Padding(x => x.Right = value);
+            return element.Padding(x => x.Right += value.ToPoints(unit));
         }
     }
 }

+ 28 - 20
QuestPDF/Fluent/PageExtensions.cs

@@ -10,16 +10,24 @@ namespace QuestPDF.Fluent
     {
         internal Page Page { get; } = new Page();
 
+        public void Size(float width, float height, Unit unit = Unit.Inch)
+        {
+            var pageSize = new PageSize(width, height, unit);
+            
+            MinSize(pageSize);
+            MaxSize(pageSize);
+        }
+        
         public void Size(PageSize pageSize)
         {
             MinSize(pageSize);
             MaxSize(pageSize);
         }
         
-        public void ContinuousSize(float width)
+        public void ContinuousSize(float width, Unit unit = Unit.Point)
         {
-            MinSize(new PageSize(width, 0));
-            MaxSize(new PageSize(width, Infrastructure.Size.Max.Height));
+            MinSize(new PageSize(width.ToPoints(unit), 0));
+            MaxSize(new PageSize(width.ToPoints(unit), Infrastructure.Size.Max.Height));
         }
 
         public void MinSize(PageSize pageSize)
@@ -32,42 +40,42 @@ namespace QuestPDF.Fluent
             Page.MaxSize = pageSize;
         }
 
-        public void MarginLeft(float value)
+        public void MarginLeft(float value, Unit unit = Unit.Point)
         {
-            Page.MarginLeft = value;
+            Page.MarginLeft = value.ToPoints(unit);
         }
         
-        public void MarginRight(float value)
+        public void MarginRight(float value, Unit unit = Unit.Point)
         {
-            Page.MarginRight = value;
+            Page.MarginRight = value.ToPoints(unit);
         }
         
-        public void MarginTop(float value)
+        public void MarginTop(float value, Unit unit = Unit.Point)
         {
-            Page.MarginTop = value;
+            Page.MarginTop = value.ToPoints(unit);
         }
         
-        public void MarginBottom(float value)
+        public void MarginBottom(float value, Unit unit = Unit.Point)
         {
-            Page.MarginBottom = value;
+            Page.MarginBottom = value.ToPoints(unit);
         }
         
-        public void MarginVertical(float value)
+        public void MarginVertical(float value, Unit unit = Unit.Point)
         {
-            MarginTop(value);
-            MarginBottom(value);
+            MarginTop(value, unit);
+            MarginBottom(value, unit);
         }
         
-        public void MarginHorizontal(float value)
+        public void MarginHorizontal(float value, Unit unit = Unit.Point)
         {
-            MarginLeft(value);
-            MarginRight(value);
+            MarginLeft(value, unit);
+            MarginRight(value, unit);
         }
         
-        public void Margin(float value)
+        public void Margin(float value, Unit unit = Unit.Point)
         {
-            MarginVertical(value);
-            MarginHorizontal(value);
+            MarginVertical(value, unit);
+            MarginHorizontal(value, unit);
         }
         
         public void DefaultTextStyle(TextStyle textStyle)

+ 31 - 10
QuestPDF/Fluent/RowExtensions.cs

@@ -6,29 +6,50 @@ namespace QuestPDF.Fluent
 {
     public class RowDescriptor
     {
-        internal Row Row { get; } = new Row();
+        internal Row Row { get; } = new();
 
         public void Spacing(float value)
         {
             Row.Spacing = value;
         }
+
+        private IContainer Item(RowItemType type, float size = 0)
+        {
+            var element = new RowItem
+            {
+                Type = type,
+                Size = size
+            };
+            
+            Row.Items.Add(element);
+            return element;
+        }
         
-        public IContainer ConstantColumn(float width)
+        [Obsolete("This element has been renamed since version 2022.2. Please use the RelativeItem method.")]
+        public IContainer RelativeColumn(float size = 1)
         {
-            return Column(constantWidth: width);
+            return Item(RowItemType.Relative, size);
         }
         
-        public IContainer RelativeColumn(float width = 1)
+        [Obsolete("This element has been renamed since version 2022.2. Please use the ConstantItem method.")]
+        public IContainer ConstantColumn(float size)
         {
-            return Column(relativeWidth: width);
+            return Item(RowItemType.Constant, size);
+        }
+
+        public IContainer RelativeItem(float size = 1)
+        {
+            return Item(RowItemType.Relative, size);
         }
         
-        private IContainer Column(float constantWidth = 0, float relativeWidth = 0)
+        public IContainer ConstantItem(float size, Unit unit = Unit.Point)
         {
-            var element = new RowElement(constantWidth, relativeWidth);
-            
-            Row.Items.Add(element);
-            return element;
+            return Item(RowItemType.Constant, size.ToPoints(unit));
+        }
+
+        public IContainer AutoItem()
+        {
+            return Item(RowItemType.Auto);
         }
     }
     

+ 2 - 2
QuestPDF/Fluent/ScaleExtensions.cs

@@ -21,7 +21,7 @@ namespace QuestPDF.Fluent
         
         public static IContainer ScaleHorizontal(this IContainer element, float value)
         {
-            return element.Scale(x => x.ScaleX = value);
+            return element.Scale(x => x.ScaleX *= value);
         }
         
         public static IContainer FlipHorizontal(this IContainer element)
@@ -31,7 +31,7 @@ namespace QuestPDF.Fluent
         
         public static IContainer ScaleVertical(this IContainer element, float value)
         {
-            return element.Scale(x => x.ScaleY = value);
+            return element.Scale(x => x.ScaleY *= value);
         }
         
         public static IContainer FlipVertical(this IContainer element)

+ 0 - 33
QuestPDF/Fluent/StackExtensions.cs

@@ -1,33 +0,0 @@
-using System;
-using QuestPDF.Elements;
-using QuestPDF.Infrastructure;
-
-namespace QuestPDF.Fluent
-{
-    public class StackDescriptor
-    {
-        internal Stack Stack { get; } = new Stack();
-
-        public void Spacing(float value)
-        {
-            Stack.Spacing = value;
-        }
-        
-        public IContainer Item()
-        {
-            var container = new Container();
-            Stack.Items.Add(container);
-            return container;
-        }
-    }
-    
-    public static class StackExtensions
-    {
-        public static void Stack(this IContainer element, Action<StackDescriptor> handler)
-        {
-            var descriptor = new StackDescriptor();
-            handler(descriptor);
-            element.Component(descriptor.Stack);
-        }
-    }
-}

+ 5 - 5
QuestPDF/Fluent/TableExtensions.cs

@@ -14,9 +14,9 @@ namespace QuestPDF.Fluent
     {
         internal List<TableColumnDefinition> Columns { get; } = new();
         
-        public void ConstantColumn(float width)
+        public void ConstantColumn(float width, Unit unit = Unit.Point)
         {
-            ComplexColumn(constantWidth: width);
+            ComplexColumn(constantWidth: width.ToPoints(unit));
         }
         
         public void RelativeColumn(float width = 1)
@@ -24,7 +24,7 @@ namespace QuestPDF.Fluent
             ComplexColumn(relativeWidth: width);
         }
         
-        public void ComplexColumn(float constantWidth = 0, float relativeWidth = 0)
+        private void ComplexColumn(float constantWidth = 0, float relativeWidth = 0)
         {
             var columnDefinition = new TableColumnDefinition(constantWidth, relativeWidth);
             Columns.Add(columnDefinition);
@@ -101,9 +101,9 @@ namespace QuestPDF.Fluent
             container
                 .Decoration(decoration =>
                 {
-                    decoration.Header().Element(HeaderTable);
+                    decoration.Before().Element(HeaderTable);
                     decoration.Content().Element(ContentTable);
-                    decoration.Footer().Element(FooterTable);
+                    decoration.After().Element(FooterTable);
                 });
 
             return container;

+ 5 - 5
QuestPDF/Fluent/TextExtensions.cs

@@ -37,9 +37,9 @@ namespace QuestPDF.Fluent
             Alignment = HorizontalAlignment.Right;
         }
 
-        public void ParagraphSpacing(float value)
+        public void ParagraphSpacing(float value, Unit unit = Unit.Point)
         {
-            Spacing = value;
+            Spacing = value.ToPoints(unit);
         }
 
         private void AddItemToLastTextBlock(ITextBlockItem item)
@@ -174,12 +174,12 @@ namespace QuestPDF.Fluent
         {
             TextBlocks.ToList().ForEach(x => x.Alignment = Alignment);
 
-            container.DefaultTextStyle(DefaultStyle).Stack(stack =>
+            container.DefaultTextStyle(DefaultStyle).Column(column =>
             {
-                stack.Spacing(Spacing);
+                column.Spacing(Spacing);
 
                 foreach (var textBlock in TextBlocks)
-                    stack.Item().Element(textBlock);
+                    column.Item().Element(textBlock);
             });
         }
     }

+ 4 - 4
QuestPDF/Fluent/TranslateExtensions.cs

@@ -14,14 +14,14 @@ namespace QuestPDF.Fluent
             return element.Element(translate);
         }
 
-        public static IContainer TranslateX(this IContainer element, float value)
+        public static IContainer TranslateX(this IContainer element, float value, Unit unit = Unit.Point)
         {
-            return element.Translate(x => x.TranslateX = value);
+            return element.Translate(x => x.TranslateX += value.ToPoints(unit));
         }
         
-        public static IContainer TranslateY(this IContainer element, float value)
+        public static IContainer TranslateY(this IContainer element, float value, Unit unit = Unit.Point)
         {
-            return element.Translate(x => x.TranslateY = value);
+            return element.Translate(x => x.TranslateY += value.ToPoints(unit));
         }
     }
 }

+ 3 - 3
QuestPDF/Helpers/PageSizes.cs

@@ -8,10 +8,10 @@ namespace QuestPDF.Helpers
         public readonly float Width;
         public readonly float Height;
         
-        public PageSize(float width, float height)
+        public PageSize(float width, float height, Unit unit = Unit.Point)
         {
-            Width = width;
-            Height = height;
+            Width = width.ToPoints(unit);
+            Height = height.ToPoints(unit);
         }
 
         public static implicit operator Size(PageSize pageSize) => new Size(pageSize.Width, pageSize.Height);

+ 56 - 0
QuestPDF/Helpers/WriteOnlyStream.cs

@@ -0,0 +1,56 @@
+using System;
+using System.IO;
+
+namespace QuestPDF.Helpers
+{
+    /// <summary>
+    /// SkiaSharp calls the Position property when generating target document file.
+    /// If the output stream does not support the Position property, the NullReferenceException is thrown.
+    /// This wrapper fixes this issue by providing cached Position value (always the end of the stream / current length).
+    /// Example stream affected: HttpContext.Response.Body
+    /// </summary>
+    internal class WriteOnlyStream : Stream
+    {
+        private readonly Stream InnerStream;
+        private long StreamLength { get; set; }
+
+        public WriteOnlyStream(Stream stream)
+        {            
+            if (!stream.CanWrite)
+                throw new NotSupportedException("Stream cannot be written");
+
+            InnerStream = stream;
+        }
+
+        public override bool CanRead => false;
+
+        public override bool CanSeek => false;
+
+        public override bool CanWrite => true;
+
+        public override long Length => StreamLength;
+
+        public override long Position
+        { 
+            get => StreamLength; 
+            set => throw new NotImplementedException(); 
+        }
+
+        public override void Flush() => InnerStream.Flush();
+
+        public override int Read(byte[] buffer, int offset, int count) 
+            => throw new NotImplementedException();
+
+        public override long Seek(long offset, SeekOrigin origin) 
+            => throw new NotImplementedException();
+
+        public override void SetLength(long value) 
+            => throw new NotImplementedException();
+
+        public override void Write(byte[] buffer, int offset, int count)
+        {
+            InnerStream.Write(buffer, offset, count);
+            StreamLength += count;
+        }
+    }
+}

+ 2 - 2
QuestPDF/Infrastructure/Size.cs

@@ -3,13 +3,13 @@
     public readonly struct Size
     {
         public const float Epsilon = 0.001f;
-        public const float Infinity = float.PositiveInfinity;
+        public const float Infinity = 14_400;
 
         public readonly float Width;
         public readonly float Height;
         
         public static Size Zero { get; } = new Size(0, 0);
-        public static Size Max { get; } = new Size(14_400, 14_400);
+        public static Size Max { get; } = new Size(Infinity, Infinity);
 
         public Size(float width, float height)
         {

+ 44 - 0
QuestPDF/Infrastructure/Unit.cs

@@ -0,0 +1,44 @@
+using System;
+using static QuestPDF.Infrastructure.Unit;
+
+namespace QuestPDF.Infrastructure
+{
+    public enum Unit
+    {
+        Point,
+        
+        Meter,
+        Centimetre,
+        Millimetre,
+        
+        Feet,
+        Inch,
+        Mill
+    }
+
+    internal static class UnitExtensions
+    {
+        private const float InchToCentimetre = 2.54f;
+        private const float InchToPoints = 72;
+        
+        public static float ToPoints(this float value, Unit unit)
+        {
+            return value * GetConversionFactor();
+            
+            float GetConversionFactor()
+            {
+                return unit switch
+                {
+                    Point => 1,
+                    Meter => 100 / InchToCentimetre * InchToPoints,
+                    Centimetre => 1 / InchToCentimetre * InchToPoints,
+                    Millimetre => 10 / InchToCentimetre * InchToPoints,
+                    Feet => 12 * InchToPoints,
+                    Inch => InchToPoints,
+                    Mill => InchToPoints / 1000f,
+                    _ => throw new ArgumentOutOfRangeException(nameof(unit), unit, null)
+                };
+            }
+        }
+    }
+}

+ 1 - 1
QuestPDF/QuestPDF.csproj

@@ -4,7 +4,7 @@
         <Authors>MarcinZiabek</Authors>
         <Company>CodeFlint</Company>
         <PackageId>QuestPDF</PackageId>
-        <Version>2022.1.0</Version>
+        <Version>2022.2.0</Version>
         <PackageDescription>QuestPDF is an open-source, modern and battle-tested library that can help you with generating PDF documents by offering friendly, discoverable and predictable C# fluent API.</PackageDescription>
         <PackageReleaseNotes>$([System.IO.File]::ReadAllText("$(MSBuildProjectDirectory)/Resources/ReleaseNotes.txt"))</PackageReleaseNotes>
         <LangVersion>9</LangVersion>

+ 7 - 7
QuestPDF/Resources/Description.md

@@ -71,15 +71,15 @@ void ComposeHeader(IContainer container)
     container.Row(row =>
     {
         {
-            stack.Item().Text($"Invoice #{Model.InvoiceNumber}", titleStyle);
+            column.Item().Text($"Invoice #{Model.InvoiceNumber}", titleStyle);
 
-            stack.Item().Text(text =>
+            column.Item().Text(text =>
             {
                 text.Span("Issue date: ", TextStyle.Default.SemiBold());
                 text.Span($"{Model.IssueDate:d}");
             });
 
-            stack.Item().Text(text =>
+            column.Item().Text(text =>
             {
                 text.Span("Due date: ", TextStyle.Default.SemiBold());
                 text.Span($"{Model.DueDate:d}");
@@ -97,7 +97,7 @@ Implementation of **the content area** that contains seller and customer details
 ```csharp
 void ComposeContent(IContainer container)
 {
-    container.PaddingVertical(40).Stack(column => 
+    container.PaddingVertical(40).column(column => 
     {
         column.Spacing(20);
         
@@ -147,7 +147,7 @@ void ComposeTable(IContainer container)
         // content
         decoration
             .Content()
-            .Stack(column =>
+            .column(column =>
             {
                 foreach (var item in Model.Items)
                 {
@@ -174,7 +174,7 @@ void ComposeTable(IContainer container)
 ```csharp
 void ComposeComments(IContainer container)
 {
-    container.ShowEntire().Background(Colors.Grey.Lighten3).Padding(10).Stack(message => 
+    container.ShowEntire().Background(Colors.Grey.Lighten3).Padding(10).column(message => 
     {
         message.Spacing(5);
         message.Item().Text("Comments", TextStyle.Default.Size(14).SemiBold());
@@ -200,7 +200,7 @@ public class AddressComponent : IComponent
     
     public void Compose(IContainer container)
     {
-        container.ShowEntire().Stack(column =>
+        container.ShowEntire().column(column =>
         {
             column.Spacing(5);
 

+ 8 - 4
QuestPDF/Resources/ReleaseNotes.txt

@@ -1,4 +1,8 @@
-- Introduced new element: `Table` - a great way to construct complex document structures, e.g. reports. This element covers all cases offered by combination of the `Stack` and the `Row` elements. Additionally, it provides support for more complex layouts and corner cases. Updating to the `Table` element can greatly simplify your code 😁
-- Added new element `DefaultTextStyle` - it allows set new text style to all its children,
-- Improved the default paging behavior for the `Row` element. In some minor corner cases it might cause infinite layout exceptions and confuse developers.
-- Fixed default page sizes for: Letter and Legal.
+- Added a `ScaleToFit` element - scales its child down so it fits in the provided space,
+- Added a `StopPaging` element - when its child requires more than one page to fully render, only the first page is shown,
+- Added a 'LineVertical' and a 'LineHorizontal' elements - those will simplify your code a lot, there is no need to use the `Border` element anymore!
+- Renaming: the `Stack` element was renamed to the `Column` element,
+- Renaming: children of the `Row` element are now called `items` instead of `columns`, e.g. `RelativeItem` instead of `RelativeColumn`,
+- Added support of the `AutoItem` to the `Row` element - those items take as little width as possible,
+- Improved default Fluent configuration behavior for elements: Scale, Padding, Translate,
+- Improved integration support with the HttpContext.Response.Body. This improvement was introduced by schulz3000, thank you!

+ 31 - 32
readme.md

@@ -149,41 +149,40 @@ void ComposeTable(IContainer container)
 {
     var headerStyle = TextStyle.Default.SemiBold();
     
-    container.Decoration(decoration =>
+    container.Table(table =>
     {
-        // header
-        decoration.Header().BorderBottom(1).Padding(5).Row(row => 
+        table.ColumnsDefinition(columns =>
         {
-            row.ConstantColumn(25).Text("#", headerStyle);
-            row.RelativeColumn(3).Text("Product", headerStyle);
-            row.RelativeColumn().AlignRight().Text("Unit price", headerStyle);
-            row.RelativeColumn().AlignRight().Text("Quantity", headerStyle);
-            row.RelativeColumn().AlignRight().Text("Total", headerStyle);
+            columns.ConstantColumn(25);
+            columns.RelativeColumn(3);
+            columns.RelativeColumn();
+            columns.RelativeColumn();
+            columns.RelativeColumn();
         });
-
-        // content
-        decoration
-            .Content()
-            .Stack(column =>
-            {
-                foreach (var item in Model.Items)
-                {
-                    column
-                    .Item()
-                    .ShowEntire()
-                    .BorderBottom(1)
-                    .BorderColor(Colors.Grey.Lighten2)
-                    .Padding(5)
-                    .Row(row => 
-                    {
-                        row.ConstantColumn(25).Text(Model.Items.IndexOf(item) + 1);
-                        row.RelativeColumn(3).Text(item.Name);
-                        row.RelativeColumn().AlignRight().Text($"{item.Price}$");
-                        row.RelativeColumn().AlignRight().Text(item.Quantity);
-                        row.RelativeColumn().AlignRight().Text($"{item.Price * item.Quantity}$");
-                    });
-                }
-            });
+        
+        table.Header(header =>
+        {
+            header.Cell().Text("#", headerStyle);
+            header.Cell().Text("Product", headerStyle);
+            header.Cell().AlignRight().Text("Unit price", headerStyle);
+            header.Cell().AlignRight().Text("Quantity", headerStyle);
+            header.Cell().AlignRight().Text("Total", headerStyle);
+            
+            header.Cell().ColumnSpan(5)
+                  .PaddingVertical(5).BorderBottom(1).BorderColor(Colors.Black);
+        });
+        
+        foreach (var item in Model.Items)
+        {
+            table.Cell().Text(Model.Items.IndexOf(item) + 1);
+            table.Cell().Text(item.Name);
+            table.Cell().AlignRight().Text($"{item.Price}$");
+            table.Cell().AlignRight().Text(item.Quantity);
+            table.Cell().AlignRight().Text($"{item.Price * item.Quantity}$");
+            
+            table.Cell().ColumnSpan(5)
+                 .PaddingVertical(5).LineHorizontal(1).LineColor(Colors.Grey.Lighten2);
+        }
     });
 }
 ```