pdfkit/docs/table.md
Jake Holland 033ba3426b
Add support for tables (#1577)
* Add page size utilities

- Added page.contentWidth
- Added page.contentHeight

* Add table support

- Tables support cell customization (including colors)
- Tables also support rotatable text (with alignment support)
- Tables have accessibility support

* chore: fix code generation context

- code generation now respects the current document positioning to allow use of page dependent operations

* chore: remove comments from build

* removed unnecessary config optimisations

* Optimize table minification

* Performance improvements to tables

* Improve font handling in tables
2025-02-24 07:49:25 -03:00

10 KiB

Tables in PDFKit

The basics

PDFKit makes adding tables to documents quite simple, and includes many options to customize the display of the output.

A simple table

Basic tables can be defined without configuration:

doc.table({
    data: [
        ['Column 1', 'Column 2', 'Column 3'],
        ['One value goes here', 'Another one here', 'OK?']
    ]
})

or the more verbose way

doc.table()
   .row(['Column 1', 'Column 2', 'Column 3'])
   .row(['One value goes here', 'Another one here', 'OK?'])

1


Defining column widths

Tables allow you to define the widths of columns:

  • * - distributes equally, filling the whole available space (default)
  • fixed value - a fixed width based on the document content

Example:

doc.table({
   columnStyles: [100, "*", 200, "*"],
   data: [
        ["width=100", "star-sized", "width=200", "star-sized"],
        [
            "fixed-width cells have exactly the specified width", 
            { text: "nothing interesting here", textColor: "grey" }, 
            { text: "nothing interesting here", textColor: "grey" }, 
            { text: "nothing interesting here", textColor: "grey" }
        ],
   ],
});

2


Defining row heights

doc.table({
    rowStyles: [20, 50, 70],
    data: [
        ["row 1 with height 20", "column B"],
        ["row 2 with height 50", "column B"],
        ["row 3 with height 70", "column B"],
    ],
});

3

With same height:

doc.table({
    rowStyles: 40,
    data: [
        ["row 1", "column B"],
        ["row 2", "column B"],
        ["row 3", "column B"],
    ],
});

4


With height from function:

doc.table({
    rowStyles: (row) => (row + 1) * 25,
    data: [
        ["row 1", "column B"],
        ["row 2", "column B"],
        ["row 3", "column B"],
    ],
  });

5


Column/row spans

Each cell can set a rowSpan or colSpan

doc.table({
  columnStyles: [200, "*", "*"],
  data: [
    [{ colSpan: 2, text: "Header with Colspan = 2" }, "Header 3"],
    ["Header 1", "Header 2", "Header 3"],
    ["Sample value 1", "Sample value 2", "Sample value 3"],
    [
      {
        rowSpan: 3,
        text: "rowspan set to 3\nLorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor",
      },
      "Sample value 2",
      "Sample value 3",
    ],
    ["Sample value 2", "Sample value 3"],
    ["Sample value 2", "Sample value 3"],
    [
      "Sample value 1",
      {
        colSpan: 2,
        rowSpan: 2,
        text: "Both:\nrowspan and colspan\ncan be defined at the same time",
      },
    ],
    ["Sample value 1"],
  ],
})

6


Styling

No borders:

doc.table({
  rowStyles: { border: false },
  data: [
    ["Header 1", "Header 2", "Header 3"],
    ["Sample value 1", "Sample value 2", "Sample value 3"],
    ["Sample value 1", "Sample value 2", "Sample value 3"],
    ["Sample value 1", "Sample value 2", "Sample value 3"],
    ["Sample value 1", "Sample value 2", "Sample value 3"],
    ["Sample value 1", "Sample value 2", "Sample value 3"],
  ],
})

7

Header line only:

doc.table({
  rowStyles: (i) => {
    return i < 1 ? { border: [0, 0, 1, 0] } : { border: false };
  },
  data: [
    ["Header 1", "Header 2", "Header 3"],
    ["Sample value 1", "Sample value 2", "Sample value 3"],
    ["Sample value 1", "Sample value 2", "Sample value 3"],
    ["Sample value 1", "Sample value 2", "Sample value 3"],
    ["Sample value 1", "Sample value 2", "Sample value 3"],
    ["Sample value 1", "Sample value 2", "Sample value 3"],
  ],
})

8


Light Horizontal lines:

doc.table({
  rowStyles: (i) => {
    return i < 1
      ? { border: [0, 0, 2, 0], borderColor: "black" }
      : { border: [0, 0, 1, 0], borderColor: "#aaa" };
  },
  data: [
    ["Header 1", "Header 2", "Header 3"],
    ["Sample value 1", "Sample value 2", "Sample value 3"],
    ["Sample value 1", "Sample value 2", "Sample value 3"],
    ["Sample value 1", "Sample value 2", "Sample value 3"],
    ["Sample value 1", "Sample value 2", "Sample value 3"],
    ["Sample value 1", "Sample value 2", "Sample value 3"],
  ],
})

9


But you can provide a custom styler as well

doc.table({
  // Set the style for all cells
  defaultStyle: { border: 1, borderColor: "gray" },
  // Set the style for cells based on their column
  columnStyles: (i) => {
    if (i === 0) return { border: { left: 2 }, borderColor: { left: "black" } };
    if (i === 2) return { border: { right: 2 }, borderColor: { right: "black" } };
  },
  // Set the style for cells based on their row
  rowStyles: (i) => {
    if (i === 0) return { border: { top: 2 }, borderColor: { top: "black" } };
    if (i === 3) return { border: { bottom: 2 }, borderColor: { bottom: "black" } };
  },
  data: [
    ["Header 1", "Header 2", "Header 3"],
    ["Sample value 1", "Sample value 2", "Sample value 3"],
    ["Sample value 1", "Sample value 2", "Sample value 3"],
    ["Sample value 1", "Sample value 2", "Sample value 3"],
  ],
})

10


Zebra style

doc.table({
  rowStyles: (i) => {
    if (i % 2 === 0) return { backgroundColor: "#ccc" };
  },
  data: [
    ["Sample value 1", "Sample value 2", "Sample value 3"],
    ["Sample value 1", "Sample value 2", "Sample value 3"],
    ["Sample value 1", "Sample value 2", "Sample value 3"],
    ["Sample value 1", "Sample value 2", "Sample value 3"],
    ["Sample value 1", "Sample value 2", "Sample value 3"],
  ],
})

11


Optional border

doc.table({
  data: [
    [
      { border: [true, false, false, false], backgroundColor: "#eee", text: "border:\n[true, false, false, false]" },
      { border: false, backgroundColor: "#ddd", text: "border:\nfalse" },
      { border: true, backgroundColor: "#eee", text: "border:\ntrue" },
    ],
    [
      { rowSpan: 3, border: true, backgroundColor: "#eef", text: "rowSpan: 3\n\nborder:\ntrue" },
      { border: undefined, backgroundColor: "#eee", text: "border:\nundefined (default)" },
      { border: [false, false, false, true], backgroundColor: "#ddd", text: "border:\n[false, false, false, true]" },
    ],
    [
      { colSpan: 2, border: true, backgroundColor: "#efe", text: "colSpan: 2\n\nborder:\ntrue" },
    ],
    [
      { border: 0, backgroundColor: "#eee", text: "border:\n0 (same as false)" },
      { border: [false, true, true, false], backgroundColor: "#ddd", text: "border:\n[false, true, true, false]" },
    ],
  ],
})

12


doc.table({
  defaultStyle: { border: false, width: 60 },
  data: [
    ["", "column 1", "column 2", "column 3"],
    [
      "row 1",
      {
        rowSpan: 3,
        colSpan: 3,
        border: true,
        backgroundColor: "#ccc",
        text: "rowSpan: 3\ncolSpan: 3\n\nborder:\n[true, true, true, true]",
      },
    ],
    ["row 2"],
    ["row 3"],
  ],
})

13


When defining multiple styles, the cells follow the precedence:

  1. defaultStyle
  2. columnStyles
  3. rowStyles
  4. cellStyle

so if a table was:

doc.table({
  defaultStyle: { border: 1 },
  columnStyles: { border: { right: 2 } },
  rowStyles: { border: { bottom: 3 } },
  data: [
    [{ border: { left: 4 } }]
  ]
})

The resulting cell would have a style of:

{ 
    border: { 
        top: 1, // From the default
        right: 2, // From the column
        bottom: 3, // From the row
        left: 4  // From the cell
    }
}

Internally, PDFKit keeps track of the current X and Y position of table as it is added to the document. This way, any calls to text or table will be placed below the table row.

doc
    .text('before')
    .table({
        data: [
            ['Column 1', 'Column 2', 'Column 3'],
            ['One value goes here', 'Another one here', 'OK?']
        ]
    })
    .text('after')

16

Table options

  • position - The position of the table (default {x: doc.x, y: doc.y})
  • maxWidth - The maximum width the table can expand to (defaults to the remaining content width (offset from the tables position))
  • columnStyles - Column definitions of the table. (default auto)
  • rowStyles - Row definitions of the table. (default *)
  • defaultStyle - Defaults to apply to every cell
  • data - The data to render (not required, you can call .row()). This can be an iterable (async or sync)
  • debug - Whether to show the debug lines for all the cells (default false)

Cell options

  • text - The value, will be cast to a string (null and undefined are not rendered but the cell is still outlined)
  • rowSpan - How many rows this cell covers, follows the same logic as HTML rowspan
  • colSpan - How many columns this cell covers, follows the same logic as HTML colspan
  • padding - The padding for the cell (default 0.25em)
  • border - The border for the cell (default 1pt)
  • borderColor - The border colors for the cell (default black)
  • font - Font options for the cell
  • backgroundColor - Set the background color of the cell
  • align - The alignment of the cell text (default {x: 'left', y: 'top'})
  • textStroke - The text stroke (default 0)
  • textStrokeColor - Sets the text stroke color of the cells text (default black)
  • textColor - Sets the text color of the cells text (default black)
  • type - Sets the cell type (for accessibility) (default TD)
  • textOptions - Sets any text options you wish to provide (such as rotation)
  • debug - Whether to show the debug lines for the cell (default false)

Column options

Extends the cell options above with:

  • width - The width of the column (default *)
  • minWidth - The minimum width of the column (default 0)
  • maxWidth - The maximum width of the column (default Infinity)

Row options

Extends the cell options above with:

  • height - The height of the row (default auto)
  • minHeight - The minimum height of the row (default 0)
  • maxHeight - The maximum height of the row (default Infinity)