Skip to content

Commit

Permalink
chore: add table docs
Browse files Browse the repository at this point in the history
  • Loading branch information
hollandjake committed Jan 8, 2025
1 parent 2269cd0 commit 9257859
Show file tree
Hide file tree
Showing 4 changed files with 375 additions and 0 deletions.
1 change: 1 addition & 0 deletions docs/generate.js
Original file line number Diff line number Diff line change
Expand Up @@ -365,5 +365,6 @@ render(doc, 'forms.md');
render(doc, 'destinations.md');
render(doc, 'attachments.md');
render(doc, 'accessibility.md');
render(doc, 'table.md');
render(doc, 'you_made_it.md');
doc.end();
1 change: 1 addition & 0 deletions docs/generate_website.js
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ const files = [
'destinations.md',
'attachments.md',
'accessibility.md',
'table.md',
'you_made_it.md'
];

Expand Down
Binary file modified docs/guide.pdf
Binary file not shown.
373 changes: 373 additions & 0 deletions docs/table.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,373 @@
# 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?'])

![]()

---

### 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",
{ content: "nothing interesting here", textColor: "grey" },
{ content: "nothing interesting here", textColor: "grey" },
{ content: "nothing interesting here", textColor: "grey" }
],
],
});

![]()

---

### 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"],
],
});

![]()

With same height:

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

![]()

With height from function:

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

![]()

---

### Column/row spans

Each cell can set a rowSpan or colSpan

doc.table({
columnStyles: [200, "*", "*"],
data: [
[{ colSpan: 2, content: "Header with Colspan = 2" }, "Header 3"],
["Header 1", "Header 2", "Header 3"],
["Sample value 1", "Sample value 2", "Sample value 3"],
[
{
rowSpan: 3,
content: "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,
content: "Both:\nrowspan and colspan\ncan be defined at the same time",
},
],
["Sample value 1"],
],
})

![]()

---

### 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"],
],
})

![]()

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"],
],
})

![]()

---

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"],
],
})

![]()

---

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"],
],
})

![]()

---

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"],
],
})

![]()

---

### Optional border

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

![]()

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

![]()

---

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')

![]()

## 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

- `content` - The value, will be cast to a string (boolean is converted to `Y/N`, and `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 content (default `{x: 'left', y: 'top'}`)
- `textStroke` - The text stroke (default `0`)
- `textStrokeColor` - Sets the text stroke color of the cells content (default `black`)
- `textColor` - Sets the text color of the cells content (default `black`)
- `type` - Sets the cell type (for accessibility) (default `TD`)
- `debug` - Whether to show the debug lines for the cell (default `false`)

## Column options

Extends the [cell options](#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](#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`)

0 comments on commit 9257859

Please sign in to comment.