-
Notifications
You must be signed in to change notification settings - Fork 1.2k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
tables #29
Comments
I work as programmer to do alot of customization for business. It will involve creating report in PDF format.
|
Is there actual PDF markup to render a table, or is it just lots of lines? |
+1000 for this. Anyone know the answer to @jmaurice's question? P.S. A two dimensional array would look like: doc.table([
["cell11","cell21","cell31"],
["cell12","cell22","cell32"],
["cell13","cell23","cell33"]
],{
width:20,
height:40,
x:30,
y:40
}); |
Hi people! doc.table data options Where data =
[
{ code: '0001', name: 'Black table', quantity: '10', price: '$ 19.20' }
{ code: '0005', name: 'White table', quantity: '8', price: '$ 19.20' }
{ code: '0012', name: 'Red chair', quantity: '40', price: '$ 12.00' }
] In my opinion, this strategy is more useful than a two dimensional array when you get the data from a database. The second parameter, options =
columns:
[
{ id: 'code', width: 10, name: 'Code' }
{ id: 'name', width: 40, name: 'Name' }
{ id: 'quantity', width: 25, name: 'Quantity' }
{ id: 'price', width: 25, name: 'Price' }
]
margins:
left: 20
top: 40
right: 20
bottom: 0
padding:
left: 10
top: 10
right: 10
bottom: 10
What do you think about? |
+1 |
Looks great! Can't wait to try it. |
+1 This would be great. |
Hi guys, I'd like to discuss table creation. @giuseppe-santoro made a sample 8 months ago, thanks him a lot. I think, this feature should be able to take parameters which can be set by html at some html table and generate pdf table as close as possible to source html table formatting. Like wkhtmltopdf does. Looking through the specifications, I found in pdf 1.4 mentioning about tables (9.7.4 > Block-Level Structure Elements > Table Elements). What do you think about implementation? What kind of API will be useful to generate tables? |
Hi all, when 9 months ago I have done the sample, I also started to implement it in a fork found here: https://github.com/giuseppe-santoro/pdfkit/blob/master/js/mixins/tables.js. |
+1 |
it works pretty well, but it contains some bugs so it is not suitable for production use |
+1000 |
Please add this to pdfkit! I need to insert a table in the pdf and i have no ideia how |
@giuseppe-santoro have you made any progress with it? could you specify some specifications as in how you thought an example of table. i really need this and i might try to work out the bugs and fix it but i`ll need your base concept for direction. |
Hi @aszharite, sorry for the late replay... |
@giuseppe-santoro how can i use your mixin in a node.js file? can you give a simple example? |
var tableOptions = {
columns:[
{
id: 'description',
width: 45,
name: 'Descrizione'
},
{
id: 'quantity',
width: 10,
name: 'Q.tà' },
{
id: 'unitCost',
width: 15,
name: 'Prezzo',
renderer: currencyRenderer
},
{
id: 'discount',
width: 10,
name: 'Sconto',
renderer: function(item){
return item && item + ' %' || ''
}
},
{
id: 'total',
width: 20,
name: 'Totale',
renderer: currencyRenderer
}
],
y: 250,
noVerticalLines: true,
margins: {
left: 40,
top: 0,
right: 40,
bottom: 20
},
padding: {
left: 10,
top: 10,
right: 10,
bottom: 10
},
font: "liberation-serif",
boldFont: "liberation-serif-bold"
};
doc.table(products, tableOptions); |
thanx a lot! |
I think that to have full control on rendering the table we have to mimic both html and css in javascript and I'm suggesting this format: doc.table(tableOptions, function(){
doc.tableHeaderRow(); // the same as tableRow but may auto repeat header on page break
doc.tableRow(rowOptions); // internal code should allow the page to break
doc.tableRow(rowOptions, function(){
doc.tableCell(cellOptions);
});
}); example for table options var tableOptions = {
width: 100, // other units should be allowed.
// If not specified then (page width - left page margin - right page margin)
columns: [
{width: 20}, // 20 %. other possibilities for widthare '20pt', '14px', '10cm', '1in'
{}, // auto calculated column width if space remained
{}
]
//other options
}; example for table row options: var rowOptions = {
fill: 'red',
margin: {top: 3},
padding: 4, // or event {top: 4, bottom: 2, left: 5, right: 5}
border: {top: 2},
borderWidth: 4, // or object for different sides
height: 20
}; cellOptions accept row options plus: |
Do you have any progress on this feature? |
+1 for a progress report. Also, if more hands are needed, I'm willing to pitch in. |
There is already some progress on this new feature? I can help. I used a powerful module in PHP (FPdf). But I want to bet on node js. |
+1 |
I'm looking into existing solutions to generate PDFs, and PDFkit looks like the best fit. Table support would be great though. I've also found http://ma.rkusa.st/pdfjs/ which seems to have good table support, maybe we can use that code as a starting point? |
doc.table() function not working, so any suggestions ? |
+1 |
@giuseppe-santoro thanks for your work, looks good, only does not work out of the box anymore with the latest version of PDFKIT. Any plans to update? |
@Jaspreet-Sian I just skimmed the discussion, it sounds like the function is still in development and not released in master? |
@omairvaiyani, Some Users are already using it. My opinion it should be released as per now. If not, can you recommend me any another solution? |
I would like to switch from jspdf to pdfkit. pdfkit is so well made but lack of tables is the only issue for me. Is there any way to use jspdf autotable with pdfkit? p.s: I dont want pdfmake |
Has there been any progress? I'd be willing to help. |
RESULT: https://i.stack.imgur.com/GJMNK.png)[https://i.stack.imgur.com/GJMNK.png] |
Here's another way inspired by the above. Call
Method
ResultHope it helps some of you 👍 |
Here is my example of simple table with one row. Use http://pdfkit.org/demo/browser.html to test.
|
I liked @ninasaveljeva 's table format and quickly hacked it to implement multiple rows and added few params by converting it to a function. Someone feel free to optimise it. Here's the code:
usage
|
@s-kris thanks for your functional example. Probably you would create PR with this function? |
Creating a table is possible in many ways. None of them is generic enough, so IMO this should not live in pdfkit but in a separated project. |
@blikblum Okay, and what do you think about all this discussion and developers who happy to see this feature in pdfkit? |
In the discussion above saw different proposed API (i stopped to count at 4) for table creation, if we stick with one, will not satisfy other needs. |
I'll try to create but if you or someone else willing to add this feature ASAP, please go ahead.
|
This 'https://www.andronio.me/2017/09/02/pdfkit-tables/' saved my day.. |
@s-kris is there an easy way to add color to the header cells or some extra styling, if i pass header=true in the data array? I couldn't get it to work |
@s-kris thankyou for the function, but sadly it has a bug when the table is too long, and create new page, the table become 1 cell /page |
Just ran across this myself, here is a typescript version of Andronio's version: Class Extension import PDFDocument from 'pdfkit';
export type PDFTable = {
headers: string[];
rows: string[][];
};
export type PDFTableOptions = {
columnSpacing?: number;
rowSpacing?: number;
width?: number;
prepareHeader?: () => void;
prepareRow?: (row: string[], index: number) => void;
};
export class PDFDocumentWithTables extends PDFDocument {
constructor(options: PDFKit.PDFDocumentOptions) {
super(options);
}
table(
table: PDFTable,
arg0?: number | PDFTableOptions,
arg1?: number | PDFTableOptions,
arg2?: number | PDFTableOptions,
) {
let startX = this.page.margins.left,
startY = this.y;
let options = {} as PDFTableOptions;
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: string[]) => {
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;
}
} Usage // Data table
const table0 = {
headers: ['Word', 'Comment', 'Summary'],
rows: [
[
'Apple',
'Not this one',
'Lorem ipsum dolor sit amet, consectetur adipiscing elit. Nulla viverra at ligula gravida ultrices. Fusce vitae pulvinar magna.',
],
[
'Tire',
'Smells like funny',
'Lorem ipsum dolor sit amet, consectetur adipiscing elit. Nulla viverra at ligula gravida ultrices. Fusce vitae pulvinar magna.',
],
],
};
doc.table(table0, {
prepareHeader: () => doc.font('Helvetica-Bold'),
prepareRow: (row, i) => doc.font('Helvetica').fontSize(12),
}); |
@danielmahon Thanks for saving my day! |
Here is modified Andronio's version with possibility to set custom column widths: 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 columnWidthsDistribution = options.columnWidthsDistribution || null;
const computeRowHeight = (row) => {
let result = 0;
row.forEach((cell, i) => {
const cellHeight = this.heightOfString(cell, {
width: columnWidthsDistribution ? (columnWidthsDistribution[i] * usableWidth) - columnSpacing : 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 + (columnWidthsDistribution ? (columnWidthsDistribution.filter((e, j) => j < i).reduce((acc, v) => acc + v, 0) * usableWidth) : i * columnContainerWidth), startY, {
width: columnWidthsDistribution ? (columnWidthsDistribution[i] * usableWidth) - columnSpacing : 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 + (columnWidthsDistribution ? (columnWidthsDistribution.filter((e, j) => j < i).reduce((acc, v) => acc + v, 0) * usableWidth) : i * columnContainerWidth), startY, {
width: columnWidthsDistribution ? (columnWidthsDistribution[i] * usableWidth) - columnSpacing : 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;
}
} To set column widths you need to pass the 'columnWidthsDistribution' parameter as following: doc.table({
headers: [
"col1",
"col2",
"col3",
"col4",
"col5",
],
rows,
{
columnsWidthDistribution: [0.1, 0.1, 0.5, 0.15, 0.15],
}); The sum of the 'columnWidthsDistribution' has to be 1. Each number means how much of the space the column will contain (0.1 => 10%, 0.5 => 50% etc). |
+1 this is awesome library project. any updates ? i so need table function |
Loving this lib - this is the only feature I can see that it's missing. Keep up the great work @Owners I wrote the HTML table - exported it as an image and then added the image to the pdf -> Hope this helps others |
you might want to consider using jsPDF :) https://github.com/parallax/jsPDF Switching there bacause of this issue 😅 |
I have an idea for it. We can pass a two-dimensional array of texts, to a method with presentation options like the following:
Then in the module we make rectangles and texts for each cell.
The text was updated successfully, but these errors were encountered: