Skip to content

Table

The Table element is one of the most complex layout-related algorithms available in the QuestPDF library. It can achieve more sophisticated structures than any combination of the Row and the Column elements. It also greatly reduce code complexity. However, it is slightly slower to compute.

Basic usage

Please analyse this simple example showing how to create a simple Table instance:

  1. You start with column definition that describes width of each column.
  2. And then, you can place any number of items inside. Each item has a corresponding position (Row and Column).



 
 
 
 
 
 
 


 
 
 
 













































.Border(1)
.Table(table =>
{
    table.ColumnsDefinition(columns =>
    {
        columns.RelativeColumn();
        columns.RelativeColumn();
        columns.RelativeColumn();
        columns.RelativeColumn();
    });

    // 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");
    
    // for simplicity, you can also use extension method described in the "Extending DSL" section
    static IContainer Block(IContainer container)
    {
        return container
            .Border(1)
            .Background(Colors.Grey.Lighten3)
            .ShowOnce()
            .MinWidth(50)
            .MinHeight(50)
            .AlignCenter()
            .AlignMiddle();
    }
});
.Border(1)
.Table(table =>
{
    table.ColumnsDefinition(columns =>
    {
        columns.RelativeColumn();
        columns.RelativeColumn();
        columns.RelativeColumn();
        columns.RelativeColumn();
    });

    // 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");
    
    // for simplicity, you can also use extension method described in the "Extending DSL" section
    static IContainer Block(IContainer container)
    {
        return container
            .Border(1)
            .Background(Colors.Grey.Lighten3)
            .ShowOnce()
            .MinWidth(50)
            .MinHeight(50)
            .AlignCenter()
            .AlignMiddle();
    }
});

example

Automated cell placement

You don't need to specify position of every cell. When the algorithm detects that the cell does not have assigned location, it places it in the nearest possible location.











 
 
 
 





















.Table(table =>
{
    table.ColumnsDefinition(columns =>
    {
        columns.RelativeColumn();
        columns.RelativeColumn();
        columns.RelativeColumn();
        columns.RelativeColumn();
    });

    table.Cell().Row(1).Column(1).Element(Block).Text("A");
    table.Cell().Row(2).Column(2).Element(Block).Text("B");
    table.Cell().Element(Block).Text("C");
    table.Cell().Element(Block).Text("D");
    
    // the Block() method, that defines default cell style, is omitted
});
.Table(table =>
{
    table.ColumnsDefinition(columns =>
    {
        columns.RelativeColumn();
        columns.RelativeColumn();
        columns.RelativeColumn();
        columns.RelativeColumn();
    });

    table.Cell().Row(1).Column(1).Element(Block).Text("A");
    table.Cell().Row(2).Column(2).Element(Block).Text("B");
    table.Cell().Element(Block).Text("C");
    table.Cell().Element(Block).Text("D");
    
    // the Block() method, that defines default cell style, is omitted
});

example

Column definitions

Similarly to the Row element, you can define columns of constant and relative sizes.

Important: this example uses extensions methods presented in the Extending DSL section.





 
 
 
 
 
 
 


























container
    .Padding(10)
    .Table(table =>
    {
        table.ColumnsDefinition(columns =>
        {
            columns.ConstantColumn(50);
            columns.ConstantColumn(100);
            columns.RelativeColumn(2);
            columns.RelativeColumn(3);
        });

        table.Cell().ColumnSpan(4).LabelCell("Total width: 300px");
        table.Cell().ValueCell("50px");
        table.Cell().ValueCell("100px");
        table.Cell().ValueCell("100px");
        table.Cell().ValueCell("150px");
    });
container
    .Padding(10)
    .Table(table =>
    {
        table.ColumnsDefinition(columns =>
        {
            columns.ConstantColumn(50);
            columns.ConstantColumn(100);
            columns.RelativeColumn(2);
            columns.RelativeColumn(3);
        });

        table.Cell().ColumnSpan(4).LabelCell("Total width: 300px");
        table.Cell().ValueCell("50px");
        table.Cell().ValueCell("100px");
        table.Cell().ValueCell("100px");
        table.Cell().ValueCell("150px");
    });

example

Row spans and column spans

Cells can span over multiple rows and/or multiple columns:











 
 
 
 
 
 
 
 
 


























.Table(table =>
{
    table.ColumnsDefinition(columns =>
    {
        columns.RelativeColumn();
        columns.RelativeColumn();
        columns.RelativeColumn();
        columns.RelativeColumn();
    });

    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");
    
    // the Block() method, that defines default cell style, is omitted
});
.Table(table =>
{
    table.ColumnsDefinition(columns =>
    {
        columns.RelativeColumn();
        columns.RelativeColumn();
        columns.RelativeColumn();
        columns.RelativeColumn();
    });

    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");
    
    // the Block() method, that defines default cell style, is omitted
});

example

Overlapping cells

Cells can overlap each other. This situation is possible when you manually assign cell's location:










 
 
 















.Table(table =>
{
    table.ColumnsDefinition(columns =>
    {
        columns.RelativeColumn();
        columns.RelativeColumn();
        columns.RelativeColumn();
    });

    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);
});
.Table(table =>
{
    table.ColumnsDefinition(columns =>
    {
        columns.RelativeColumn();
        columns.RelativeColumn();
        columns.RelativeColumn();
    });

    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);
});

example

Extend last cells to table bottom

This feature is very useful when creating complex table structures that are likely to page. It applies a special rule to last cells within each column. It extends them in such a way that they end on the table's bottom. This behavior may improve visuals of your table.











 






























.Table(table =>
{
    table.ColumnsDefinition(columns =>
    {
        columns.RelativeColumn();
        columns.RelativeColumn();
        columns.RelativeColumn();
        columns.RelativeColumn();
    });
    
    table.ExtendLastCellsToTableBottom();

    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");
    
    // the Block() method, that defines default cell style, is omitted
});
.Table(table =>
{
    table.ColumnsDefinition(columns =>
    {
        columns.RelativeColumn();
        columns.RelativeColumn();
        columns.RelativeColumn();
        columns.RelativeColumn();
    });
    
    table.ExtendLastCellsToTableBottom();

    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");
    
    // the Block() method, that defines default cell style, is omitted
});

Please notice that the block "C" ends along with the "B" and "D" blocks:

example

Report example

Please analyse this example to understand how to design report-like document structures.

Important: this example uses extensions methods presented in the Extending DSL section.

.MinimalBox()
.Border(1)
.Table(table =>
{
    table.ColumnsDefinition(columns =>
    {
        columns.ConstantColumn(100);
        columns.RelativeColumn();
        columns.ConstantColumn(100);
        columns.RelativeColumn();
    });
    
    table.ExtendLastCellsToTableBottom();

    table.Cell().RowSpan(3).LabelCell("Project");
    table.Cell().RowSpan(3).ShowEntire().ValueCell(Placeholders.Sentence());

    table.Cell().LabelCell("Report number");
    table.Cell().ValueCell(i.ToString());
    
    table.Cell().LabelCell("Date");
    table.Cell().ValueCell(Placeholders.ShortDate());

    table.Cell().LabelCell("Inspector");
    table.Cell().ValueCell("Marcin Ziąbek");

    table.Cell().ColumnSpan(2).LabelCell("Morning weather");
    table.Cell().ColumnSpan(2).LabelCell("Evening weather");

    table.Cell().ValueCell("Time");
    table.Cell().ValueCell("7:13");

    table.Cell().ValueCell("Time");
    table.Cell().ValueCell("18:25");

    table.Cell().ValueCell("Description");
    table.Cell().ValueCell("Sunny");

    table.Cell().ValueCell("Description");
    table.Cell().ValueCell("Windy");

    table.Cell().ValueCell("Wind");
    table.Cell().ValueCell("Mild");

    table.Cell().ValueCell("Wind");
    table.Cell().ValueCell("Strong");

    table.Cell().ValueCell("Temperature");
    table.Cell().ValueCell("17°C");

    table.Cell().ValueCell("Temperature");
    table.Cell().ValueCell("32°C");

    table.Cell().LabelCell("Remarks");
    table.Cell().ColumnSpan(3).ValueCell(Placeholders.Paragraph());
});
.MinimalBox()
.Border(1)
.Table(table =>
{
    table.ColumnsDefinition(columns =>
    {
        columns.ConstantColumn(100);
        columns.RelativeColumn();
        columns.ConstantColumn(100);
        columns.RelativeColumn();
    });
    
    table.ExtendLastCellsToTableBottom();

    table.Cell().RowSpan(3).LabelCell("Project");
    table.Cell().RowSpan(3).ShowEntire().ValueCell(Placeholders.Sentence());

    table.Cell().LabelCell("Report number");
    table.Cell().ValueCell(i.ToString());
    
    table.Cell().LabelCell("Date");
    table.Cell().ValueCell(Placeholders.ShortDate());

    table.Cell().LabelCell("Inspector");
    table.Cell().ValueCell("Marcin Ziąbek");

    table.Cell().ColumnSpan(2).LabelCell("Morning weather");
    table.Cell().ColumnSpan(2).LabelCell("Evening weather");

    table.Cell().ValueCell("Time");
    table.Cell().ValueCell("7:13");

    table.Cell().ValueCell("Time");
    table.Cell().ValueCell("18:25");

    table.Cell().ValueCell("Description");
    table.Cell().ValueCell("Sunny");

    table.Cell().ValueCell("Description");
    table.Cell().ValueCell("Windy");

    table.Cell().ValueCell("Wind");
    table.Cell().ValueCell("Mild");

    table.Cell().ValueCell("Wind");
    table.Cell().ValueCell("Strong");

    table.Cell().ValueCell("Temperature");
    table.Cell().ValueCell("17°C");

    table.Cell().ValueCell("Temperature");
    table.Cell().ValueCell("32°C");

    table.Cell().LabelCell("Remarks");
    table.Cell().ColumnSpan(3).ValueCell(Placeholders.Paragraph());
});

example

It is also possible to define table headers and footers. If your table contains more content and spans multiple pages, header and footer elements are repeated on each page.

Please note that header and footer sections have their own set of rows - they do not count in the content section.











































 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 































































































var pageSizes = new List<(string name, double width, double height)>()
{
    ("Letter (ANSI A)", 8.5f, 11),
    ("Legal", 8.5f, 14),
    ("Ledger (ANSI B)", 11, 17),
    ("Tabloid (ANSI B)", 17, 11),
    ("ANSI C", 22, 17),
    ("ANSI D", 34, 22),
    ("ANSI E", 44, 34)
};

const int inchesToPoints = 72;

container
.Padding(10)
.MinimalBox()
.Border(1)
.Table(table =>
{
    IContainer DefaultCellStyle(IContainer container, string backgroundColor)
    {
        return container
            .Border(1)
            .BorderColor(Colors.Grey.Lighten1)
            .Background(backgroundColor)
            .PaddingVertical(5)
            .PaddingHorizontal(10)
            .AlignCenter()
            .AlignMiddle()
            .ShowOnce();
    }
    
    table.ColumnsDefinition(columns =>
    {
        columns.RelativeColumn();
        
        columns.ConstantColumn(75);
        columns.ConstantColumn(75);
        
        columns.ConstantColumn(75);
        columns.ConstantColumn(75);
    });
    
    table.Header(header =>
    {
        // please be sure to call the 'header' handler!
        
        header.Cell().RowSpan(2).Element(CellStyle).ExtendHorizontal().AlignLeft().Text("Document type");
            
        header.Cell().ColumnSpan(2).Element(CellStyle).Text("Inches");
        header.Cell().ColumnSpan(2).Element(CellStyle).Text("Points");
            
        header.Cell().Element(CellStyle).Text("Width");
        header.Cell().Element(CellStyle).Text("Height");
            
        header.Cell().Element(CellStyle).Text("Width");
        header.Cell().Element(CellStyle).Text("Height");

        // you can extend already existing styles by creating additional methods
        IContainer CellStyle(IContainer container) => DefaultCellStyle(container, Colors.Grey.Lighten3); 
    });

    foreach (var page in pageSizes)
    {
        table.Cell().Element(CellStyle).ExtendHorizontal().AlignLeft().Text(page.name);
                
        // inches
        table.Cell().Element(CellStyle).Text(page.width);
        table.Cell().Element(CellStyle).Text(page.height);
                
        // points
        table.Cell().Element(CellStyle).Text(page.width * inchesToPoints);
        table.Cell().Element(CellStyle).Text(page.height * inchesToPoints);
                
        IContainer CellStyle(IContainer container) => DefaultCellStyle(container, Colors.White); 
    }
});
var pageSizes = new List<(string name, double width, double height)>()
{
    ("Letter (ANSI A)", 8.5f, 11),
    ("Legal", 8.5f, 14),
    ("Ledger (ANSI B)", 11, 17),
    ("Tabloid (ANSI B)", 17, 11),
    ("ANSI C", 22, 17),
    ("ANSI D", 34, 22),
    ("ANSI E", 44, 34)
};

const int inchesToPoints = 72;

container
.Padding(10)
.MinimalBox()
.Border(1)
.Table(table =>
{
    IContainer DefaultCellStyle(IContainer container, string backgroundColor)
    {
        return container
            .Border(1)
            .BorderColor(Colors.Grey.Lighten1)
            .Background(backgroundColor)
            .PaddingVertical(5)
            .PaddingHorizontal(10)
            .AlignCenter()
            .AlignMiddle()
            .ShowOnce();
    }
    
    table.ColumnsDefinition(columns =>
    {
        columns.RelativeColumn();
        
        columns.ConstantColumn(75);
        columns.ConstantColumn(75);
        
        columns.ConstantColumn(75);
        columns.ConstantColumn(75);
    });
    
    table.Header(header =>
    {
        // please be sure to call the 'header' handler!
        
        header.Cell().RowSpan(2).Element(CellStyle).ExtendHorizontal().AlignLeft().Text("Document type");
            
        header.Cell().ColumnSpan(2).Element(CellStyle).Text("Inches");
        header.Cell().ColumnSpan(2).Element(CellStyle).Text("Points");
            
        header.Cell().Element(CellStyle).Text("Width");
        header.Cell().Element(CellStyle).Text("Height");
            
        header.Cell().Element(CellStyle).Text("Width");
        header.Cell().Element(CellStyle).Text("Height");

        // you can extend already existing styles by creating additional methods
        IContainer CellStyle(IContainer container) => DefaultCellStyle(container, Colors.Grey.Lighten3); 
    });

    foreach (var page in pageSizes)
    {
        table.Cell().Element(CellStyle).ExtendHorizontal().AlignLeft().Text(page.name);
                
        // inches
        table.Cell().Element(CellStyle).Text(page.width);
        table.Cell().Element(CellStyle).Text(page.height);
                
        // points
        table.Cell().Element(CellStyle).Text(page.width * inchesToPoints);
        table.Cell().Element(CellStyle).Text(page.height * inchesToPoints);
                
        IContainer CellStyle(IContainer container) => DefaultCellStyle(container, Colors.White); 
    }
});

Page 1:

example

Page 2:

example

Released under the MIT License.