121 lines
3.9 KiB
JavaScript
121 lines
3.9 KiB
JavaScript
'use strict ';
|
|
|
|
const PDFDocument = require('pdfkit');
|
|
|
|
class PDFDocumentWithTables extends PDFDocument {
|
|
constructor(options) {
|
|
super(options);
|
|
}
|
|
|
|
table(table, arg0, arg1, arg2) {
|
|
let startX = this.page.margins.left,
|
|
startY = this.y;
|
|
let options = {};
|
|
|
|
if ((typeof arg0 === 'number') && (typeof arg1 === 'number')) {
|
|
startX = arg0;
|
|
startY = arg1;
|
|
|
|
if (typeof arg2 === 'object')
|
|
options = arg2;
|
|
} else if (typeof arg0 === 'object') {
|
|
options = arg0;
|
|
}
|
|
|
|
const columnCount = table.headers.length;
|
|
const columnSpacing = options.columnSpacing || 15;
|
|
const rowSpacing = options.rowSpacing || 5;
|
|
const usableWidth = options.width || (this.page.width - this.page.margins.left - this.page.margins.right);
|
|
|
|
const prepareHeader = options.prepareHeader || (() => {});
|
|
const prepareRow = options.prepareRow || (() => {});
|
|
const computeRowHeight = (row) => {
|
|
let result = 0;
|
|
|
|
row.forEach((cell) => {
|
|
const cellHeight = this.heightOfString(cell, {
|
|
width: columnWidth,
|
|
align: 'left'
|
|
});
|
|
result = Math.max(result, cellHeight);
|
|
});
|
|
|
|
return result + rowSpacing;
|
|
};
|
|
|
|
const columnContainerWidth = usableWidth / columnCount;
|
|
const columnWidth = columnContainerWidth - columnSpacing;
|
|
const maxY = this.page.height - this.page.margins.bottom;
|
|
|
|
let rowBottomY = 0;
|
|
|
|
this.on('pageAdded', () => {
|
|
startY = this.page.margins.top;
|
|
rowBottomY = 0;
|
|
});
|
|
|
|
// Allow the user to override style for headers
|
|
prepareHeader();
|
|
|
|
// Check to have enough room for header and first rows
|
|
if (startY + 3 * computeRowHeight(table.headers) > maxY)
|
|
this.addPage();
|
|
|
|
// Print all headers
|
|
table.headers.forEach((header, i) => {
|
|
this.text(header, startX + i * columnContainerWidth, startY, {
|
|
width: columnWidth,
|
|
align: 'left'
|
|
});
|
|
});
|
|
|
|
// Refresh the y coordinate of the bottom of the headers row
|
|
rowBottomY = Math.max(startY + computeRowHeight(table.headers), rowBottomY);
|
|
|
|
// Separation line between headers and rows
|
|
this.moveTo(startX, rowBottomY - rowSpacing * 0.5)
|
|
.lineTo(startX + usableWidth, rowBottomY - rowSpacing * 0.5)
|
|
.lineWidth(2)
|
|
.stroke();
|
|
|
|
table.rows.forEach((row, i) => {
|
|
const rowHeight = computeRowHeight(row);
|
|
|
|
// Switch to next page if we cannot go any further because the space is over.
|
|
// For safety, consider 3 rows margin instead of just one
|
|
if (startY + 3 * rowHeight < maxY)
|
|
startY = rowBottomY + rowSpacing;
|
|
else
|
|
this.addPage();
|
|
|
|
// Allow the user to override style for rows
|
|
prepareRow(row, i);
|
|
|
|
// Print all cells of the current row
|
|
row.forEach((cell, i) => {
|
|
this.text(cell, startX + i * columnContainerWidth, startY, {
|
|
width: columnWidth,
|
|
align: 'left'
|
|
});
|
|
});
|
|
|
|
// Refresh the y coordinate of the bottom of this row
|
|
rowBottomY = Math.max(startY + rowHeight, rowBottomY);
|
|
|
|
// Separation line between rows
|
|
this.moveTo(startX, rowBottomY - rowSpacing * 0.5)
|
|
.lineTo(startX + usableWidth, rowBottomY - rowSpacing * 0.5)
|
|
.lineWidth(1)
|
|
.opacity(0.7)
|
|
.stroke()
|
|
.opacity(1); // Reset opacity after drawing the line
|
|
});
|
|
|
|
this.x = startX;
|
|
this.moveDown();
|
|
|
|
return this;
|
|
}
|
|
}
|
|
|
|
module.exports = PDFDocumentWithTables; |