Browse Source

Table: row height fixes for overlapping cells. Added code examples

Marcin Ziąbek 4 years ago
parent
commit
4152b67679

+ 21 - 10
QuestPDF.Examples/CanvasExamples.cs

@@ -15,30 +15,41 @@ namespace QuestPDF.Examples
         {
             RenderingTest
                 .Create()
+                .PageSize(175, 100)
                 .ProduceImages()
                 .ShowResults()
                 .Render(container =>
                 {
                     container
-                        .Background(Colors.White)
+                        .Background(Colors.Grey.Lighten2)
                         .Padding(25)
                         .Box()
                         .Layers(layers =>
                         {
-                            layers.PrimaryLayer().Padding(10).Text("Sample text");
-                            
                             layers.Layer().Canvas((canvas, size) =>
                             {
-                                using var paint = new SKPaint
+                                DrawRoundedRectangle(Colors.White, false);
+                                DrawRoundedRectangle(Colors.Blue.Darken2, true);
+
+                                void DrawRoundedRectangle(string color, bool isStroke)
                                 {
-                                    Color = SKColor.Parse(Colors.Black),
-                                    IsStroke = true,
-                                    StrokeWidth = 1,
-                                    IsAntialias = true
-                                };
+                                    using var paint = new SKPaint
+                                    {
+                                        Color = SKColor.Parse(color),
+                                        IsStroke = isStroke,
+                                        StrokeWidth = 2,
+                                        IsAntialias = true
+                                    };
                                 
-                                canvas.DrawRoundRect(0, 0, size.Width, size.Height, 20, 20, paint);
+                                    canvas.DrawRoundRect(0, 0, size.Width, size.Height, 20, 20, paint);
+                                }
                             });
+                            
+                            layers
+                                .PrimaryLayer()
+                                .PaddingVertical(10)
+                                .PaddingHorizontal(20)
+                                .Text("Sample text", TextStyle.Default.Size(16).Color(Colors.Blue.Darken2).SemiBold());
                         });
                 });
         }

+ 55 - 0
QuestPDF.Examples/DifferentHeaderOnFirstPageExample.cs

@@ -0,0 +1,55 @@
+using System.Linq;
+using NUnit.Framework;
+using QuestPDF.Examples.Engine;
+using QuestPDF.Fluent;
+using QuestPDF.Helpers;
+using QuestPDF.Infrastructure;
+
+namespace QuestPDF.Examples
+{
+    public class DifferentHeaderOnFirstPageExample
+    {
+        [Test]
+        public void Placeholder()
+        {
+            RenderingTest
+                .Create()
+                .PageSize(PageSizes.A6)
+                .ProduceImages()
+                .ShowResults()
+                .EnableDebugging()
+                .RenderDocument(container =>
+                {
+                    container.Page(page =>
+                    {
+                        page.Size(PageSizes.A6);
+                        page.Margin(30);
+                        page.Background(Colors.White);
+                        
+                        page.Header().Stack(stack =>
+                        {
+                            stack.Item().ShowOnce().Background(Colors.Blue.Lighten2).Height(60);
+                            stack.Item().SkipOnce().Background(Colors.Green.Lighten2).Height(40);
+                        });
+                        
+                        page.Content().PaddingVertical(10).Stack(stack =>
+                        {
+                            stack.Spacing(10);
+
+                            foreach (var _ in Enumerable.Range(0, 13))
+                                stack.Item().Background(Colors.Grey.Lighten2).Height(40);
+                        });
+                        
+                        page.Footer().AlignCenter().Text(text =>
+                        {
+                            text.DefaultTextStyle(TextStyle.Default.Size(16));
+                            
+                            text.CurrentPageNumber();
+                            text.Span(" / ");
+                            text.TotalPages();
+                        });
+                    });
+                });
+        }
+    }
+}

+ 77 - 98
QuestPDF.Examples/TableExamples.cs

@@ -40,23 +40,11 @@ namespace QuestPDF.Examples
                                 columns.RelativeColumn();
                             });
 
-                            // by using the custom element, we can reuse the same style
-                            table.Cell().Row(1).Column(4).Element(x => TextContent(x, "A"));
-                            table.Cell().Row(2).Column(2).Element(x => TextContent(x, "A"));
-                            table.Cell().Row(3).Column(3).Element(x => TextContent(x, "A"));
-                            table.Cell().Row(4).Column(1).Element(x => TextContent(x, "A"));
-
-                            static void TextContent(IContainer container, string text)
-                            {
-                                container
-                                    .Border(1)
-                                    .Background(Colors.Grey.Lighten3)
-                                    .MinWidth(50)
-                                    .MinHeight(50)
-                                    .AlignCenter()
-                                    .AlignMiddle()
-                                    .Text(text);
-                            }
+                            // by using custom 'Element' method, we can reuse visual configuration
+                            table.Cell().Row(1).Column(4).Element(Block).Text("A");
+                            table.Cell().Row(2).Column(2).Element(Block).Text("B");
+                            table.Cell().Row(3).Column(3).Element(Block).Text("C");
+                            table.Cell().Row(4).Column(1).Element(Block).Text("D");
                         });
                 });
         }
@@ -86,21 +74,10 @@ namespace QuestPDF.Examples
                                 columns.RelativeColumn();
                             });
 
-                            table.DefaultCellStyle(cell =>
-                            {
-                                return cell
-                                    .Border(1)
-                                    .Background(Colors.Grey.Lighten3)
-                                    .MinWidth(50)
-                                    .MinHeight(50)
-                                    .AlignCenter()
-                                    .AlignMiddle();
-                            });
-                            
-                            table.Cell().Row(1).Column(1).Text("A");
-                            table.Cell().Row(2).Column(2).Text("B");
-                            table.Cell().Row(1).Column(3).Text("C");
-                            table.Cell().Row(2).Column(4).Text("D");
+                            table.Cell().Row(1).Column(1).Element(Block).Text("A");
+                            table.Cell().Row(2).Column(2).Element(Block).Text("B");
+                            table.Cell().Row(1).Column(3).Element(Block).Text("C");
+                            table.Cell().Row(2).Column(4).Element(Block).Text("D");
                         });
                 });
         }
@@ -160,11 +137,11 @@ namespace QuestPDF.Examples
                                 columns.RelativeColumn();
                             });
 
-                            table.Cell().TextBox("A");
-                            table.Cell().Row(2).Column(2).TextBox("B");
-                            table.Cell().TextBox("C");
-                            table.Cell().Row(3).Column(3).TextBox("D");
-                            table.Cell().ColumnSpan(2).TextBox("E");
+                            table.Cell().Element(Block).Text("A");
+                            table.Cell().Row(2).Column(2).Element(Block).Text("B");
+                            table.Cell().Element(Block).Text("C");
+                            table.Cell().Row(3).Column(3).Element(Block).Text("D");
+                            table.Cell().ColumnSpan(2).Element(Block).Text("E");
                         });
                 });
         }
@@ -175,7 +152,7 @@ namespace QuestPDF.Examples
             RenderingTest
                 .Create()
                 .ProduceImages()
-                .PageSize(170, 170)
+                .PageSize(220, 170)
                 .ShowResults()
                 .Render(container =>
                 {
@@ -190,14 +167,16 @@ namespace QuestPDF.Examples
                                 columns.RelativeColumn();
                                 columns.RelativeColumn();
                                 columns.RelativeColumn();
+                                columns.RelativeColumn();
                             });
                             
                             table.ExtendLastCellsToTableBottom();
 
-                            table.Cell().Row(1).Column(1).TextBox("A");
-                            table.Cell().Row(3).Column(1).TextBox("B");
-                            table.Cell().Row(2).Column(2).TextBox("C");
-                            table.Cell().Row(3).Column(3).TextBox("D");
+                            table.Cell().Row(1).Column(1).Element(Block).Text("A");
+                            table.Cell().Row(3).Column(1).Element(Block).Text("B");
+                            table.Cell().Row(2).Column(2).Element(Block).Text("C");
+                            table.Cell().Row(3).Column(3).Element(Block).Text("D");
+                            table.Cell().Row(2).RowSpan(2).Column(4).Element(Block).Text("E");
                         });
                 });
         }
@@ -208,7 +187,7 @@ namespace QuestPDF.Examples
             RenderingTest
                 .Create()
                 .ProduceImages()
-                .PageSize(170, 120)
+                .PageSize(170, 170)
                 .ShowResults()
                 .Render(container =>
                 {
@@ -225,9 +204,9 @@ namespace QuestPDF.Examples
                                 columns.RelativeColumn();
                             });
 
-                            table.Cell().RowSpan(2).ColumnSpan(2).Background(Colors.Green.Lighten3);
-                            table.Cell().Background(Colors.Blue.Lighten3).MinHeight(50);
-                            table.Cell().Row(2).Column(2).ColumnSpan(2).Background(Colors.Red.Lighten3).MinHeight(50);
+                            table.Cell().Row(1).RowSpan(3).Column(1).ColumnSpan(3).Background(Colors.Grey.Lighten3).MinHeight(150);
+                            table.Cell().Row(1).RowSpan(2).Column(1).ColumnSpan(2).Background(Colors.Grey.Lighten1).MinHeight(100);
+                            table.Cell().Row(3).Column(3).Background(Colors.Grey.Darken1).MinHeight(50);
                         });
                 });
         }
@@ -254,15 +233,15 @@ namespace QuestPDF.Examples
                                 columns.RelativeColumn();
                             });
 
-                            table.Cell().RowSpan(2).ColumnSpan(2).TextBox("1");
-                            table.Cell().ColumnSpan(2).TextBox("2");
-                            table.Cell().TextBox("3");
-                            table.Cell().TextBox("4");
-                            table.Cell().RowSpan(2).TextBox("5");
-                            table.Cell().ColumnSpan(2).TextBox("6");
-                            table.Cell().RowSpan(2).TextBox("7");
-                            table.Cell().TextBox("8");
-                            table.Cell().TextBox("9");
+                            table.Cell().RowSpan(2).ColumnSpan(2).Element(Block).Text("1");
+                            table.Cell().ColumnSpan(2).Element(Block).Text("2");
+                            table.Cell().Element(Block).Text("3");
+                            table.Cell().Element(Block).Text("4");
+                            table.Cell().RowSpan(2).Element(Block).Text("5");
+                            table.Cell().ColumnSpan(2).Element(Block).Text("6");
+                            table.Cell().RowSpan(2).Element(Block).Text("7");
+                            table.Cell().Element(Block).Text("8");
+                            table.Cell().Element(Block).Text("9");
                         });
                 });
         }
@@ -289,15 +268,15 @@ namespace QuestPDF.Examples
                                 columns.RelativeColumn();
                             });
 
-                            table.Cell().RowSpan(4).TextBox("1");
+                            table.Cell().RowSpan(4).Element(Block).Text("1");
                             
-                            table.Cell().RowSpan(2).TextBox("2");
-                            table.Cell().RowSpan(1).TextBox("3");
-                            table.Cell().RowSpan(1).TextBox("4");
+                            table.Cell().RowSpan(2).Element(Block).Text("2");
+                            table.Cell().RowSpan(1).Element(Block).Text("3");
+                            table.Cell().RowSpan(1).Element(Block).Text("4");
                             
-                            table.Cell().RowSpan(2).TextBox("5");
-                            table.Cell().RowSpan(1).TextBox("6");
-                            table.Cell().RowSpan(1).TextBox("7");
+                            table.Cell().RowSpan(2).Element(Block).Text("5");
+                            table.Cell().RowSpan(1).Element(Block).Text("6");
+                            table.Cell().RowSpan(1).Element(Block).Text("7");
                         });
                 });
         }
@@ -332,24 +311,38 @@ namespace QuestPDF.Examples
                         .Border(1)
                         .Decoration(decoration =>
                         {
+                            IContainer DefaultCellStyle(IContainer container, string backgroundColor)
+                            {
+                                return container
+                                    .Border(1)
+                                    .BorderColor(Colors.Grey.Lighten1)
+                                    .Background(backgroundColor)
+                                    .PaddingVertical(5)
+                                    .PaddingHorizontal(10)
+                                    .AlignCenter()
+                                    .AlignMiddle();
+                            }
+                            
                             decoration
                                 .Header()
                                 .DefaultTextStyle(TextStyle.Default.SemiBold())
                                 .Table(table =>
                                 {
                                     table.ColumnsDefinition(DefineTableColumns);
-                                    table.DefaultCellStyle(cell => DefineDefaultCellStyle(cell, Colors.Grey.Lighten3));
                                     
-                                    table.Cell().RowSpan(2).ExtendHorizontal().AlignLeft().Text("Document type");
+                                    table.Cell().RowSpan(2).Element(CellStyle).ExtendHorizontal().AlignLeft().Text("Document type");
                                     
-                                    table.Cell().ColumnSpan(2).Text("Inches");
-                                    table.Cell().ColumnSpan(2).Text("Points");
+                                    table.Cell().ColumnSpan(2).Element(CellStyle).Text("Inches");
+                                    table.Cell().ColumnSpan(2).Element(CellStyle).Text("Points");
                                     
-                                    table.Cell().Text("Width");
-                                    table.Cell().Text("Height");
+                                    table.Cell().Element(CellStyle).Text("Width");
+                                    table.Cell().Element(CellStyle).Text("Height");
                                     
-                                    table.Cell().Text("Width");
-                                    table.Cell().Text("Height");
+                                    table.Cell().Element(CellStyle).Text("Width");
+                                    table.Cell().Element(CellStyle).Text("Height");
+
+                                    // you can extend already existing styles by creating additional methods
+                                    IContainer CellStyle(IContainer container) => DefaultCellStyle(container, Colors.Grey.Lighten3); 
                                 });
                             
                             decoration
@@ -357,19 +350,20 @@ namespace QuestPDF.Examples
                                 .Table(table =>
                                 {
                                     table.ColumnsDefinition(DefineTableColumns);
-                                    table.DefaultCellStyle(cell => DefineDefaultCellStyle(cell, Colors.White));
                                     
                                     foreach (var page in pageSizes)
                                     {
-                                        table.Cell().ExtendHorizontal().AlignLeft().Text(page.name);
+                                        table.Cell().Element(CellStyle).ExtendHorizontal().AlignLeft().Text(page.name);
                                         
                                         // inches
-                                        table.Cell().Text(page.width);
-                                        table.Cell().Text(page.height);
+                                        table.Cell().Element(CellStyle).Text(page.width);
+                                        table.Cell().Element(CellStyle).Text(page.height);
                                         
                                         // points
-                                        table.Cell().Text(page.width * inchesToPoints);
-                                        table.Cell().Text(page.height * inchesToPoints);
+                                        table.Cell().Element(CellStyle).Text(page.width * inchesToPoints);
+                                        table.Cell().Element(CellStyle).Text(page.height * inchesToPoints);
+                                        
+                                        IContainer CellStyle(IContainer container) => DefaultCellStyle(container, Colors.White); 
                                     }
                                 });
    
@@ -383,18 +377,6 @@ namespace QuestPDF.Examples
                                 columns.ConstantColumn(75);
                                 columns.ConstantColumn(75);
                             }
-
-                            IContainer DefineDefaultCellStyle(IContainer container, string backgroundColor)
-                            {
-                                return container
-                                    .Border(1)
-                                    .BorderColor(Colors.Grey.Lighten1)
-                                    .Background(backgroundColor)
-                                    .PaddingVertical(5)
-                                    .PaddingHorizontal(10)
-                                    .AlignCenter()
-                                    .AlignMiddle();
-                            }
                         });
                 });
         }
@@ -404,13 +386,13 @@ namespace QuestPDF.Examples
         {
             RenderingTest
                 .Create()
-                .ProduceImages()
+                .ProducePdf()
                 .PageSize(PageSizes.A4)
                 .MaxPages(10_000)
                 .EnableCaching()
                 .EnableDebugging(false)
                 .ShowResults()
-                .Render(container => GeneratePerformanceStructure(container, 1));
+                .Render(container => GeneratePerformanceStructure(container, 1000));
         }
         
         public static void GeneratePerformanceStructure(IContainer container, int repeats)
@@ -479,20 +461,17 @@ namespace QuestPDF.Examples
                     }
                 });
         }
-    }
-    
-    public static class TableTestsExtensions
-    {
-        public static void TextBox(this IContainer container, string text)
+        
+        // this method uses a higher order function to define a custom and dynamic style
+        static IContainer Block(IContainer container)
         {
-            container
+            return container
                 .Border(1)
                 .Background(Colors.Grey.Lighten3)
                 .MinWidth(50)
                 .MinHeight(50)
                 .AlignCenter()
-                .AlignMiddle()
-                .Text(text, TextStyle.Default.Size(16));
+                .AlignMiddle();
         }
     }
 }

+ 5 - 6
QuestPDF/Elements/Table/Table.cs

@@ -173,7 +173,7 @@ namespace QuestPDF.Elements.Table
                             break;
 
                         foreach (var row in Enumerable.Range(cell.Row, cell.Row - currentRow))
-                            rowBottomOffsets[row] = rowBottomOffsets[row - 1];
+                            rowBottomOffsets[row] = Math.Max(rowBottomOffsets[row - 1], rowBottomOffsets[row]);
                         
                         currentRow = cell.Row;
                     }
@@ -226,11 +226,8 @@ namespace QuestPDF.Elements.Table
 
                 var maxRow = commands.Select(x => x.Cell).Max(x => x.Row + x.RowSpan);
 
-                if (maxRow > currentRow)
-                {
-                    foreach (var row in Enumerable.Range(currentRow + 1, maxRow - currentRow))
-                        rowBottomOffsets[row] = rowBottomOffsets[row - 1];   
-                }
+                foreach (var row in Enumerable.Range(CurrentRow, maxRow - CurrentRow))
+                    rowBottomOffsets[row] = Math.Max(rowBottomOffsets[row - 1], rowBottomOffsets[row]);   
 
                 AdjustCellSizes(commands, rowBottomOffsets);
                 
@@ -246,7 +243,9 @@ namespace QuestPDF.Elements.Table
                 {
                     var lastRow = command.Cell.Row + command.Cell.RowSpan - 1;
                     var height = rowBottomOffsets[lastRow] - command.Offset.Y;
+                    
                     command.Size = new Size(command.Size.Width, height);
+                    command.Offset = new Position(command.Offset.X, rowBottomOffsets[command.Cell.Row - 1]);
                 }
             }
             

+ 2 - 2
QuestPDF/Elements/Table/TableCell.cs

@@ -2,10 +2,10 @@ namespace QuestPDF.Elements.Table
 {
     internal class TableCell : Container, ITableCellContainer
     {
-        public int Row { get; set; } = 1;
+        public int Row { get; set; } = 0;
         public int RowSpan { get; set; } = 1;
 
-        public int Column { get; set; } = 1;
+        public int Column { get; set; } = 0;
         public int ColumnSpan { get; set; } = 1;
         
         public bool IsRendered { get; set; }

+ 15 - 1
QuestPDF/Elements/Table/TableLayoutPlanner.cs

@@ -25,6 +25,8 @@ namespace QuestPDF.Elements.Table
                         .Where(x => x.Row + x.RowSpan > currentLocation.y)
                         .ToList();
                 }
+
+                SetPartialLocation(cell);
                 
                 if (cell.HasLocation())
                 {
@@ -77,9 +79,21 @@ namespace QuestPDF.Elements.Table
             return neighbours.Any(cell.CollidesWith);
         }
 
+        private static void SetPartialLocation(this TableCell cell)
+        {
+            if (cell.Row == default && cell.Column == default)
+                return;
+
+            if (cell.Row == default)
+                cell.Row = 1;
+            
+            if (cell.Column == default)
+                cell.Column = 1;
+        }
+        
         private static bool HasLocation(this TableCell cell)
         {
-            return cell.Row != 1 || cell.Column != 1;
+            return cell.Row != 0 && cell.Column != 0;
         }
     }
 }

+ 10 - 2
QuestPDF/Elements/Table/TableLayoutValidator.cs

@@ -15,13 +15,21 @@ namespace QuestPDF.Elements.Table
         
         private static void ValidateCellPositions(int columnsCount, ICollection<TableCell> cells)
         {
+            const string prefix = "Detected issue in table cells configuration.";
+            
             foreach (var cell in cells)
             {
+                if (cell.Column < 1)
+                    throw new DocumentComposeException($"{prefix} A cell column position should be greater or equal to 1. Got {cell.Column}.");
+                
+                if (cell.Row < 1)
+                    throw new DocumentComposeException($"{prefix} A cell row position should be greater or equal to 1. Got {cell.Row}.");
+                
                 if (cell.Column > columnsCount)
-                    throw new DocumentLayoutException($"Cell location is incorrect. Cell starts at column that does not exist. Cell details: {GetCellDetails(cell)}.");
+                    throw new DocumentComposeException($"{prefix} Cell starts at column that does not exist. Cell details: {GetCellDetails(cell)}.");
                 
                 if (cell.Column + cell.ColumnSpan - 1 > columnsCount)
-                    throw new DocumentLayoutException($"Cell location is incorrect. Cell spans over columns that do not exist. Cell details: {GetCellDetails(cell)}.");
+                    throw new DocumentComposeException($"{prefix} Table cell location is incorrect. Cell spans over columns that do not exist. Cell details: {GetCellDetails(cell)}.");
             }
 
             string GetCellDetails(TableCell cell)

+ 2 - 19
QuestPDF/Fluent/TableExtensions.cs

@@ -1,5 +1,6 @@
 using System;
 using System.Diagnostics;
+using QuestPDF.Drawing.Exceptions;
 using QuestPDF.Elements;
 using QuestPDF.Elements.Table;
 using QuestPDF.Infrastructure;
@@ -35,7 +36,6 @@ namespace QuestPDF.Fluent
     public class TableDescriptor
     {
         private Table Table { get; }
-        private Func<IContainer, IContainer> DefaultCellStyleFunc { get; set; } = x => x;
 
         internal TableDescriptor(Table table)
         {
@@ -52,29 +52,13 @@ namespace QuestPDF.Fluent
         {
             Table.ExtendLastCellsToTableBottom = true;
         }
-
-        public void DefaultCellStyle(Func<IContainer, IContainer> mapper)
-        {
-            DefaultCellStyleFunc = mapper;
-        }
-
+        
         public ITableCellContainer Cell()
         {
             var cell = new TableCell();
             Table.Cells.Add(cell);
             return cell;
         }
-
-        internal void ApplyDefaultCellStyle()
-        {
-            foreach (var cell in Table.Cells)
-            {
-                var container = new Container();
-                DefaultCellStyleFunc(container).Element(cell.Child);
-                
-                cell.Child = container;
-            }
-        }
     }
     
     public static class TableExtensions
@@ -85,7 +69,6 @@ namespace QuestPDF.Fluent
             
             var descriptor = new TableDescriptor(table);
             handler(descriptor);
-            descriptor.ApplyDefaultCellStyle();
 
             table.PlanCellPositions();
             table.ValidateCellPositions();