pdfkit-table
Advanced tools
Comparing version 0.1.67 to 0.1.68
1281
index.js
@@ -60,667 +60,682 @@ // jshint esversion: 6 | ||
*/ | ||
table(table, options) { | ||
typeof table === 'string' && (table = JSON.parse(table)); | ||
table(table, options, callback) { | ||
return new Promise((resolve, reject) => { | ||
try { | ||
table || (table = {}); | ||
options || (options = {}); | ||
typeof table === 'string' && (table = JSON.parse(table)); | ||
table.headers || (table.headers = []); | ||
table.datas || (table.datas = []); | ||
table.rows || (table.rows = []); | ||
table.options && (options = table.options); | ||
options.padding || (options.padding = 0); | ||
options.columnsSize || (options.columnsSize = []); | ||
options.addPage || (options.addPage = false); | ||
const title = table.title ? table.title : ( options.title || '' ) ; | ||
const subtitle = table.subtitle ? table.subtitle : ( options.subtitle || '' ) ; | ||
// const columnIsDefined = options.columnsSize.length ? true : false; | ||
const columnSpacing = options.columnSpacing || 3; // 15 | ||
let columnSizes = []; | ||
let columnPositions = []; // 0, 10, 20, 30, 100 | ||
let columnWidth = 0; | ||
const rowDistance = 0.5; | ||
let cellPadding = {top: 0, right: 0, bottom: 0, left: 0}; // universal | ||
const prepareHeader = options.prepareHeader || (() => this.fillColor('black').font("Helvetica-Bold").fontSize(8).fill()); | ||
const prepareRow = options.prepareRow || ((row, indexColumn, indexRow, rectRow) => this.fillColor('black').font("Helvetica").fontSize(8).fill()); | ||
table || (table = {}); | ||
options || (options = {}); | ||
const maxY = this.page.height - (this.page.margins.top + this.page.margins.bottom); | ||
let startX = options.x || this.x || this.page.margins.left; | ||
let startY = options.y || this.y; | ||
let rowBottomY = 0; | ||
let tableWidth = 0; | ||
// reset position to margins.left | ||
if( options.x === null || options.x === -1 ){ | ||
startX = this.page.margins.left; | ||
} | ||
const createTitle = ( data, size, opacity ) => { | ||
// Title | ||
if(!data) return; | ||
// get height line | ||
// let cellHeight = 0; | ||
// if string | ||
if(typeof data === 'string' ){ | ||
// font size | ||
this.fontSize( size ).opacity( opacity ); | ||
// get height line | ||
// cellHeight = this.heightOfString( data, { | ||
// width: usableWidth, | ||
// align: "left", | ||
// }); | ||
// write | ||
this.text( data, startX, startY ).opacity( 1 ); // moveDown( 0.5 ) | ||
// startY += cellHeight; | ||
startY = this.y + columnSpacing + 2; | ||
// else object | ||
} else if(typeof data === 'object' ){ | ||
// title object | ||
data.label && this.fontSize( data.fontSize || size ).text( data.label, startX, startY ); | ||
} | ||
}; | ||
// add a new page before crate table | ||
options.addPage === true && this.addPage(); | ||
// create title and subtitle | ||
createTitle( title, 12, 1 ); | ||
createTitle( subtitle, 9, 0.7 ); | ||
// add space after title | ||
if( title || subtitle ){ | ||
startY += 3; | ||
} | ||
const onFirePageAdded = () => { | ||
// startX = this.page.margins.left; | ||
startY = this.page.margins.top; | ||
rowBottomY = 0; | ||
addHeader(); | ||
} | ||
// add fire | ||
this.on("pageAdded", onFirePageAdded); | ||
// warning - eval can be harmful | ||
const fEval = (str) => { | ||
let f = null; eval('f = ' + str); return f; | ||
}; | ||
const separationsRow = (x, y, strokeWidth, strokeOpacity) => { | ||
// validate | ||
strokeOpacity || (strokeOpacity = 0.5); | ||
strokeWidth || (strokeWidth = 0.5); | ||
// distance | ||
const d = rowDistance * 1.5; | ||
// margin | ||
const m = options.x || this.page.margins.left; | ||
// draw | ||
this | ||
.moveTo(x, y - d) | ||
.lineTo(x + tableWidth - m, y - d) | ||
.lineWidth(strokeWidth) | ||
.opacity(strokeOpacity) | ||
.stroke() | ||
// Reset opacity after drawing the line | ||
.opacity(1); | ||
}; | ||
// padding: [10, 10, 10, 10] | ||
// padding: [10, 10] | ||
// padding: {top: 10, right: 10, bottom: 10, left: 10} | ||
// padding: 10, | ||
const prepareCellPadding = (p) => { | ||
// array | ||
if(Array.isArray(p)){ | ||
switch(p.length){ | ||
case 3: p = [...p, 0]; break; | ||
case 2: p = [...p, ...p]; break; | ||
case 1: p = Array(4).fill(p[0]); break; | ||
table.headers || (table.headers = []); | ||
table.datas || (table.datas = []); | ||
table.rows || (table.rows = []); | ||
table.options && (options = table.options); | ||
options.padding || (options.padding = 0); | ||
options.columnsSize || (options.columnsSize = []); | ||
options.addPage || (options.addPage = false); | ||
const title = table.title ? table.title : ( options.title || '' ) ; | ||
const subtitle = table.subtitle ? table.subtitle : ( options.subtitle || '' ) ; | ||
// const columnIsDefined = options.columnsSize.length ? true : false; | ||
const columnSpacing = options.columnSpacing || 3; // 15 | ||
let columnSizes = []; | ||
let columnPositions = []; // 0, 10, 20, 30, 100 | ||
let columnWidth = 0; | ||
const rowDistance = 0.5; | ||
let cellPadding = {top: 0, right: 0, bottom: 0, left: 0}; // universal | ||
const prepareHeader = options.prepareHeader || (() => this.fillColor('black').font("Helvetica-Bold").fontSize(8).fill()); | ||
const prepareRow = options.prepareRow || ((row, indexColumn, indexRow, rectRow) => this.fillColor('black').font("Helvetica").fontSize(8).fill()); | ||
const maxY = this.page.height - (this.page.margins.top + this.page.margins.bottom); | ||
let startX = options.x || this.x || this.page.margins.left; | ||
let startY = options.y || this.y; | ||
let rowBottomY = 0; | ||
let tableWidth = 0; | ||
// reset position to margins.left | ||
if( options.x === null || options.x === -1 ){ | ||
startX = this.page.margins.left; | ||
} | ||
} | ||
// number | ||
else if(typeof p === 'number'){ | ||
p = Array(4).fill(p); | ||
} | ||
// object | ||
else if(typeof p === 'object'){ | ||
const {top, right, bottom, left} = p; | ||
p = [top, right, bottom, left]; | ||
} | ||
// null | ||
else { | ||
p = Array(4).fill(0); | ||
} | ||
return { | ||
top: p[0] >> 0, // int | ||
right: p[1] >> 0, | ||
bottom: p[2] >> 0, | ||
left: p[3] >> 0, | ||
}; | ||
}; | ||
const prepareRowOptions = (row) => { | ||
// validate | ||
if( typeof row !== 'object' || !row.hasOwnProperty('options') ) return; | ||
const {fontFamily, fontSize, color} = row.options; | ||
fontFamily && this.font(fontFamily); | ||
fontSize && this.fontSize(fontSize); | ||
color && this.fillColor(color); | ||
// row.options.hasOwnProperty('fontFamily') && this.font(row.options.fontFamily); | ||
// row.options.hasOwnProperty('fontSize') && this.fontSize(row.options.fontSize); | ||
// row.options.hasOwnProperty('color') && this.fillColor(row.options.color); | ||
}; | ||
const prepareRowBackground = (row, rect) => { | ||
// validate | ||
if(typeof row !== 'object') return; | ||
// options | ||
row.options && (row = row.options); | ||
let { fill, opac } = {}; | ||
// add backgroundColor | ||
if(row.hasOwnProperty('columnColor')){ // ^0.1.70 | ||
const { columnColor, columnOpacity } = row; | ||
fill = columnColor; | ||
opac = columnOpacity; | ||
} else if(row.hasOwnProperty('backgroundColor')){ // ~0.1.65 old | ||
const { backgroundColor, backgroundOpacity } = row; | ||
fill = backgroundColor; | ||
opac = backgroundOpacity; | ||
} else if(row.hasOwnProperty('background')){ // dont remove | ||
if(typeof row.background === 'object'){ | ||
let { color, opacity } = row.background; | ||
fill = color; | ||
opac = opacity; | ||
const createTitle = ( data, size, opacity ) => { | ||
// Title | ||
if(!data) return; | ||
// get height line | ||
// let cellHeight = 0; | ||
// if string | ||
if(typeof data === 'string' ){ | ||
// font size | ||
this.fontSize( size ).opacity( opacity ); | ||
// get height line | ||
// cellHeight = this.heightOfString( data, { | ||
// width: usableWidth, | ||
// align: "left", | ||
// }); | ||
// write | ||
this.text( data, startX, startY ).opacity( 1 ); // moveDown( 0.5 ) | ||
// startY += cellHeight; | ||
startY = this.y + columnSpacing + 2; | ||
// else object | ||
} else if(typeof data === 'object' ){ | ||
// title object | ||
data.label && this.fontSize( data.fontSize || size ).text( data.label, startX, startY ); | ||
} | ||
}; | ||
// add a new page before crate table | ||
options.addPage === true && this.addPage(); | ||
// create title and subtitle | ||
createTitle( title, 12, 1 ); | ||
createTitle( subtitle, 9, 0.7 ); | ||
// add space after title | ||
if( title || subtitle ){ | ||
startY += 3; | ||
} | ||
} | ||
fill && this.addBackground(rect, fill, opac); | ||
}; | ||
const computeRowHeight = (row) => { | ||
let result = 0; | ||
let cellp; | ||
// if row is object, content with property and options | ||
if(!Array.isArray(row) && typeof row === 'object' && !row.hasOwnProperty('property')){ | ||
const cells = []; | ||
// get all properties names on header | ||
table.headers.forEach(({property}) => cells.push(row[property]) ); | ||
// define row with properties header | ||
row = cells; | ||
} | ||
row.forEach((cell,i) => { | ||
let text = cell; | ||
// object | ||
// read cell and get label of object | ||
if( typeof cell === 'object' ){ | ||
// define label | ||
text = String(cell.label); | ||
// apply font size on calc about height row | ||
cell.hasOwnProperty('options') && prepareRowOptions(cell); | ||
const onFirePageAdded = () => { | ||
// startX = this.page.margins.left; | ||
startY = this.page.margins.top; | ||
rowBottomY = 0; | ||
addHeader(); | ||
} | ||
text = String(text).replace('bold:','').replace('size',''); | ||
// add fire | ||
this.on("pageAdded", onFirePageAdded); | ||
// warning - eval can be harmful | ||
const fEval = (str) => { | ||
let f = null; eval('f = ' + str); return f; | ||
}; | ||
const separationsRow = (x, y, strokeWidth, strokeOpacity) => { | ||
// validate | ||
strokeOpacity || (strokeOpacity = 0.5); | ||
strokeWidth || (strokeWidth = 0.5); | ||
// distance | ||
const d = rowDistance * 1.5; | ||
// margin | ||
const m = options.x || this.page.margins.left; | ||
// draw | ||
this | ||
.moveTo(x, y - d) | ||
.lineTo(x + tableWidth - m, y - d) | ||
.lineWidth(strokeWidth) | ||
.opacity(strokeOpacity) | ||
.stroke() | ||
// Reset opacity after drawing the line | ||
.opacity(1); | ||
}; | ||
// padding: [10, 10, 10, 10] | ||
// padding: [10, 10] | ||
// padding: {top: 10, right: 10, bottom: 10, left: 10} | ||
// padding: 10, | ||
const prepareCellPadding = (p) => { | ||
// array | ||
if(Array.isArray(p)){ | ||
switch(p.length){ | ||
case 3: p = [...p, 0]; break; | ||
case 2: p = [...p, ...p]; break; | ||
case 1: p = Array(4).fill(p[0]); break; | ||
} | ||
} | ||
// number | ||
else if(typeof p === 'number'){ | ||
p = Array(4).fill(p); | ||
} | ||
// object | ||
else if(typeof p === 'object'){ | ||
const {top, right, bottom, left} = p; | ||
p = [top, right, bottom, left]; | ||
} | ||
// null | ||
else { | ||
p = Array(4).fill(0); | ||
} | ||
return { | ||
top: p[0] >> 0, // int | ||
right: p[1] >> 0, | ||
bottom: p[2] >> 0, | ||
left: p[3] >> 0, | ||
}; | ||
// cell padding | ||
cellp = prepareCellPadding(table.headers[i].padding || options.padding || 0); | ||
// calc height size of string | ||
const cellHeight = this.heightOfString(text, { | ||
width: columnSizes[i] - (cellp.left + cellp.right), | ||
align: 'left', | ||
}); | ||
}; | ||
const prepareRowOptions = (row) => { | ||
// validate | ||
if( typeof row !== 'object' || !row.hasOwnProperty('options') ) return; | ||
const {fontFamily, fontSize, color} = row.options; | ||
fontFamily && this.font(fontFamily); | ||
fontSize && this.fontSize(fontSize); | ||
color && this.fillColor(color); | ||
// row.options.hasOwnProperty('fontFamily') && this.font(row.options.fontFamily); | ||
// row.options.hasOwnProperty('fontSize') && this.fontSize(row.options.fontSize); | ||
// row.options.hasOwnProperty('color') && this.fillColor(row.options.color); | ||
}; | ||
const prepareRowBackground = (row, rect) => { | ||
// validate | ||
if(typeof row !== 'object') return; | ||
// options | ||
row.options && (row = row.options); | ||
let { fill, opac } = {}; | ||
// add backgroundColor | ||
if(row.hasOwnProperty('columnColor')){ // ^0.1.70 | ||
const { columnColor, columnOpacity } = row; | ||
fill = columnColor; | ||
opac = columnOpacity; | ||
} else if(row.hasOwnProperty('backgroundColor')){ // ~0.1.65 old | ||
const { backgroundColor, backgroundOpacity } = row; | ||
fill = backgroundColor; | ||
opac = backgroundOpacity; | ||
} else if(row.hasOwnProperty('background')){ // dont remove | ||
if(typeof row.background === 'object'){ | ||
let { color, opacity } = row.background; | ||
fill = color; | ||
opac = opacity; | ||
} | ||
} | ||
fill && this.addBackground(rect, fill, opac); | ||
}; | ||
result = Math.max(result, cellHeight); | ||
}); | ||
return result + columnSpacing; | ||
}; | ||
// Calc columns size | ||
const computeRowHeight = (row) => { | ||
let result = 0; | ||
let cellp; | ||
const calcColumnSizes = () => { | ||
let h = []; // header width | ||
let p = []; // position | ||
let w = 0; // table width | ||
// (table width) 1o - Max size table | ||
w = this.page.width - this.page.margins.right - ( options.x || this.page.margins.left ); | ||
// (table width) 2o - Size defined | ||
options.width && ( w = String(options.width).replace(/[^0-9]/g,'') >> 0 ); | ||
// (table width) if table is percent of page | ||
// ... | ||
// (size columns) 1o | ||
table.headers.forEach( el => { | ||
el.width && h.push(el.width); // - columnSpacing | ||
}); | ||
// (size columns) 2o | ||
if(h.length === 0) { | ||
h = options.columnsSize; | ||
} | ||
// (size columns) 3o | ||
if(h.length === 0) { | ||
columnWidth = ( w / table.headers.length ); // - columnSpacing // define column width | ||
table.headers.forEach( () => h.push(columnWidth) ); | ||
} | ||
// Set columnPositions | ||
h.reduce((prev, curr, indx) => { | ||
p.push(prev >> 0); | ||
return prev + curr; | ||
},( options.x || this.page.margins.left )); | ||
// !Set columnSizes | ||
h.length && (columnSizes = h); | ||
p.length && (columnPositions = p); | ||
// (table width) 3o - Sum last position + lest header width | ||
w = p[p.length-1] + h[h.length-1]; | ||
// !Set tableWidth | ||
w && ( tableWidth = w ); | ||
// Ajust spacing | ||
// tableWidth = tableWidth - (h.length * columnSpacing); | ||
this.logg('columnSizes', h); | ||
this.logg('columnPositions', p); | ||
}; | ||
calcColumnSizes(); | ||
// Header | ||
const addHeader = () => { | ||
// Allow the user to override style for headers | ||
prepareHeader(); | ||
let rowHeight = computeRowHeight(table.headers); | ||
let lastPositionX = startX; // x position head | ||
// Check to have enough room for header and first rows. default 3 | ||
// if (startY + 2 * rowHeight > maxY) this.addPage(); | ||
if(table.headers.length > 0) { | ||
// simple header | ||
if(typeof table.headers[0] === 'string') { | ||
// // background header | ||
// const rectRow = { | ||
// x: startX, | ||
// y: startY - columnSpacing - (rowDistance * 2), | ||
// width: columnWidth, | ||
// height: rowHeight + columnSpacing, | ||
// }; | ||
// // add background | ||
// this.addBackground(rectRow); | ||
// print headers | ||
table.headers.forEach((header, i) => { | ||
// background header | ||
const rectCell = { | ||
x: lastPositionX, | ||
y: startY - columnSpacing - (rowDistance * 2), | ||
width: columnSizes[i], | ||
height: rowHeight + columnSpacing, | ||
}; | ||
// add background | ||
this.addBackground(rectCell); | ||
// if row is object, content with property and options | ||
if(!Array.isArray(row) && typeof row === 'object' && !row.hasOwnProperty('property')){ | ||
const cells = []; | ||
// get all properties names on header | ||
table.headers.forEach(({property}) => cells.push(row[property]) ); | ||
// define row with properties header | ||
row = cells; | ||
} | ||
row.forEach((cell,i) => { | ||
let text = cell; | ||
// object | ||
// read cell and get label of object | ||
if( typeof cell === 'object' ){ | ||
// define label | ||
text = String(cell.label); | ||
// apply font size on calc about height row | ||
cell.hasOwnProperty('options') && prepareRowOptions(cell); | ||
} | ||
text = String(text).replace('bold:','').replace('size',''); | ||
// cell padding | ||
cellPadding = prepareCellPadding(options.padding || 0); | ||
this.text(header, | ||
lastPositionX + (cellPadding.left), | ||
startY, { | ||
width: Number(columnSizes[i]) - (cellPadding.left + cellPadding.right), | ||
cellp = prepareCellPadding(table.headers[i].padding || options.padding || 0); | ||
// calc height size of string | ||
const cellHeight = this.heightOfString(text, { | ||
width: columnSizes[i] - (cellp.left + cellp.right), | ||
align: 'left', | ||
}); | ||
lastPositionX += columnSizes[i] >> 0; | ||
result = Math.max(result, cellHeight); | ||
}); | ||
return result + columnSpacing; | ||
}; | ||
// Calc columns size | ||
const calcColumnSizes = () => { | ||
let h = []; // header width | ||
let p = []; // position | ||
let w = 0; // table width | ||
// (table width) 1o - Max size table | ||
w = this.page.width - this.page.margins.right - ( options.x || this.page.margins.left ); | ||
// (table width) 2o - Size defined | ||
options.width && ( w = String(options.width).replace(/[^0-9]/g,'') >> 0 ); | ||
// (table width) if table is percent of page | ||
// ... | ||
// (size columns) 1o | ||
table.headers.forEach( el => { | ||
el.width && h.push(el.width); // - columnSpacing | ||
}); | ||
// (size columns) 2o | ||
if(h.length === 0) { | ||
h = options.columnsSize; | ||
} | ||
// (size columns) 3o | ||
if(h.length === 0) { | ||
columnWidth = ( w / table.headers.length ); // - columnSpacing // define column width | ||
table.headers.forEach( () => h.push(columnWidth) ); | ||
} | ||
// Set columnPositions | ||
h.reduce((prev, curr, indx) => { | ||
p.push(prev >> 0); | ||
return prev + curr; | ||
},( options.x || this.page.margins.left )); | ||
// !Set columnSizes | ||
h.length && (columnSizes = h); | ||
p.length && (columnPositions = p); | ||
// (table width) 3o - Sum last position + lest header width | ||
w = p[p.length-1] + h[h.length-1]; | ||
// !Set tableWidth | ||
w && ( tableWidth = w ); | ||
}else{ | ||
// Print all headers | ||
table.headers.forEach( (dataHeader, i) => { | ||
let {label, width, renderer, align, headerColor, headerOpacity, headerAlign, padding} = dataHeader; | ||
// check defination | ||
width = width || columnSizes[i]; | ||
align = headerAlign || align || 'left'; | ||
// force number | ||
width = width >> 0; | ||
// Ajust spacing | ||
// tableWidth = tableWidth - (h.length * columnSpacing); | ||
// register renderer function | ||
if(renderer && typeof renderer === 'string') { | ||
table.headers[i].renderer = fEval(renderer); | ||
this.logg('columnSizes', h); | ||
this.logg('columnPositions', p); | ||
}; | ||
calcColumnSizes(); | ||
// Header | ||
const addHeader = () => { | ||
// Allow the user to override style for headers | ||
prepareHeader(); | ||
let rowHeight = computeRowHeight(table.headers); | ||
let lastPositionX = startX; // x position head | ||
// Check to have enough room for header and first rows. default 3 | ||
// if (startY + 2 * rowHeight > maxY) this.addPage(); | ||
if(table.headers.length > 0) { | ||
// simple header | ||
if(typeof table.headers[0] === 'string') { | ||
// // background header | ||
// const rectRow = { | ||
// x: startX, | ||
// y: startY - columnSpacing - (rowDistance * 2), | ||
// width: columnWidth, | ||
// height: rowHeight + columnSpacing, | ||
// }; | ||
// // add background | ||
// this.addBackground(rectRow); | ||
// print headers | ||
table.headers.forEach((header, i) => { | ||
// background header | ||
const rectCell = { | ||
x: lastPositionX, | ||
y: startY - columnSpacing - (rowDistance * 2), | ||
width: columnSizes[i], | ||
height: rowHeight + columnSpacing, | ||
}; | ||
// add background | ||
this.addBackground(rectCell); | ||
// cell padding | ||
cellPadding = prepareCellPadding(options.padding || 0); | ||
this.text(header, | ||
lastPositionX + (cellPadding.left), | ||
startY, { | ||
width: Number(columnSizes[i]) - (cellPadding.left + cellPadding.right), | ||
align: 'left', | ||
}); | ||
lastPositionX += columnSizes[i] >> 0; | ||
}); | ||
}else{ | ||
// Print all headers | ||
table.headers.forEach( (dataHeader, i) => { | ||
let {label, width, renderer, align, headerColor, headerOpacity, headerAlign, padding} = dataHeader; | ||
// check defination | ||
width = width || columnSizes[i]; | ||
align = headerAlign || align || 'left'; | ||
// force number | ||
width = width >> 0; | ||
// register renderer function | ||
if(renderer && typeof renderer === 'string') { | ||
table.headers[i].renderer = fEval(renderer); | ||
} | ||
// background header | ||
const rectCell = { | ||
x: lastPositionX, | ||
y: startY - columnSpacing - (rowDistance * 2), | ||
width: width, | ||
height: rowHeight + columnSpacing, | ||
}; | ||
// add background | ||
this.addBackground(rectCell, headerColor, headerOpacity); | ||
// cell padding | ||
cellPadding = prepareCellPadding(padding || options.padding || 0); | ||
// write | ||
this.text(label, | ||
lastPositionX + (cellPadding.left), | ||
startY, { | ||
width: width - (cellPadding.left + cellPadding.right), | ||
align: align, | ||
}) | ||
lastPositionX += width; | ||
}); | ||
} | ||
// set style | ||
prepareRowOptions(table.headers); | ||
} | ||
// 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 | ||
separationsRow(startX, rowBottomY); | ||
}; | ||
// End header | ||
addHeader(); | ||
let lastPositionX; | ||
// Datas | ||
table.datas.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 + 2 * rowHeight < maxY) startY = rowBottomY + columnSpacing + rowDistance; // 0.5 is spacing rows | ||
// else this.addPage(); | ||
if(startY + 2 * rowHeight >= maxY) this.addPage(); | ||
startY = rowBottomY + columnSpacing + rowDistance; // 0.5 is spacing rows | ||
const rectRow = { | ||
x: startX, | ||
y: startY - columnSpacing - (rowDistance * 2), | ||
width: tableWidth - startX, | ||
height: rowHeight + columnSpacing, | ||
}; | ||
// add background row | ||
prepareRowBackground(row, rectRow); | ||
lastPositionX = startX; | ||
// Print all cells of the current row | ||
table.headers.forEach(( dataHeader, index) => { | ||
let {property, width, renderer, align, valign, padding} = dataHeader; | ||
// background header | ||
// check defination | ||
width = width || columnWidth; | ||
align = align || 'left'; | ||
// cell padding | ||
cellPadding = prepareCellPadding(padding || options.padding || 0); | ||
const rectCell = { | ||
x: lastPositionX, | ||
y: startY - columnSpacing - (rowDistance * 2), | ||
width: width, | ||
x: lastPositionX, | ||
y: startY - columnSpacing - (rowDistance * 2), | ||
width: width, | ||
height: rowHeight + columnSpacing, | ||
}; | ||
// add background | ||
this.addBackground(rectCell, headerColor, headerOpacity); | ||
// cell padding | ||
cellPadding = prepareCellPadding(padding || options.padding || 0); | ||
// write | ||
this.text(label, | ||
} | ||
// allow the user to override style for rows | ||
prepareRowOptions(row); | ||
prepareRow(row, index, i, rectRow); | ||
let text = row[property]; | ||
// cell object | ||
if(typeof text === 'object' ){ | ||
text = String(text.label); // get label | ||
// row[property].hasOwnProperty('options') && prepareRowOptions(row[property]); // set style | ||
// options if text cell is object | ||
if( row[property].hasOwnProperty('options') ){ | ||
// set font style | ||
prepareRowOptions(row[property]); | ||
prepareRowBackground(row[property], rectCell); | ||
} | ||
} else { | ||
// style column by header | ||
prepareRowBackground(table.headers[index], rectCell); | ||
} | ||
// bold | ||
if( String(text).indexOf('bold:') === 0 ){ | ||
this.font('Helvetica-Bold'); | ||
text = text.replace('bold:',''); | ||
} | ||
// size | ||
if( String(text).indexOf('size') === 0 ){ | ||
let size = String(text).substr(4,2).replace(':','').replace('+','') >> 0; | ||
this.fontSize( size < 7 ? 7 : size ); | ||
text = text.replace(`size${size}:`,''); | ||
} | ||
// renderer column | ||
// renderer && (text = renderer(text, index, i, row, rectRow, rectCell)) // value, index-column, index-row, row nbhmn | ||
if(typeof renderer === 'function'){ | ||
text = renderer(text, index, i, row, rectRow, rectCell); // value, index-column, index-row, row | ||
} | ||
// TODO # Experimental | ||
// ------------------------------------------------------------------------------ | ||
// align vertically | ||
let topTextToAlignVertically = 0; | ||
if(valign && valign !== 'top'){ | ||
const heightText = this.heightOfString(text, { | ||
width: width - (cellPadding.left + cellPadding.right), | ||
align: align, | ||
}); | ||
// line height, spacing hehight, cell and text diference | ||
topTextToAlignVertically = rowDistance - columnSpacing + (rectCell.height - heightText) / 2; | ||
} | ||
// ------------------------------------------------------------------------------ | ||
this.text(text, | ||
lastPositionX + (cellPadding.left), | ||
startY, { | ||
startY + topTextToAlignVertically, { | ||
width: width - (cellPadding.left + cellPadding.right), | ||
align: align, | ||
}) | ||
lastPositionX += width; | ||
}); | ||
lastPositionX += width; | ||
// set style | ||
prepareRowOptions(row); | ||
prepareRow(row, index, i, rectRow); | ||
}); | ||
} | ||
// set style | ||
prepareRowOptions(table.headers); | ||
} | ||
// 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 | ||
separationsRow(startX, rowBottomY); | ||
}; | ||
// End header | ||
addHeader(); | ||
let lastPositionX; | ||
// Datas | ||
table.datas.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 + 2 * rowHeight < maxY) startY = rowBottomY + columnSpacing + rowDistance; // 0.5 is spacing rows | ||
// else this.addPage(); | ||
if(startY + 2 * rowHeight >= maxY) this.addPage(); | ||
startY = rowBottomY + columnSpacing + rowDistance; // 0.5 is spacing rows | ||
const rectRow = { | ||
x: startX, | ||
y: startY - columnSpacing - (rowDistance * 2), | ||
width: tableWidth - startX, | ||
height: rowHeight + columnSpacing, | ||
}; | ||
// add background row | ||
prepareRowBackground(row, rectRow); | ||
lastPositionX = startX; | ||
// Print all cells of the current row | ||
table.headers.forEach(( dataHeader, index) => { | ||
let {property, width, renderer, align, valign, padding} = dataHeader; | ||
// check defination | ||
width = width || columnWidth; | ||
align = align || 'left'; | ||
// cell padding | ||
cellPadding = prepareCellPadding(padding || options.padding || 0); | ||
const rectCell = { | ||
x: lastPositionX, | ||
y: startY - columnSpacing - (rowDistance * 2), | ||
width: width, | ||
height: rowHeight + columnSpacing, | ||
} | ||
// allow the user to override style for rows | ||
prepareRowOptions(row); | ||
prepareRow(row, index, i, rectRow); | ||
let text = row[property]; | ||
// cell object | ||
if(typeof text === 'object' ){ | ||
text = String(text.label); // get label | ||
// row[property].hasOwnProperty('options') && prepareRowOptions(row[property]); // set style | ||
// options if text cell is object | ||
if( row[property].hasOwnProperty('options') ){ | ||
// set font style | ||
prepareRowOptions(row[property]); | ||
prepareRowBackground(row[property], rectCell); | ||
// Refresh the y coordinate of the bottom of this row | ||
rowBottomY = Math.max(startY + rowHeight, rowBottomY); | ||
// Separation line between rows | ||
separationsRow(startX, rowBottomY); | ||
// review this code | ||
if( row.hasOwnProperty('options') ){ | ||
if( row.options.hasOwnProperty('separation') ){ | ||
// Separation line between rows | ||
separationsRow(startX, rowBottomY, 1, 1); | ||
} | ||
} | ||
} else { | ||
// style column by header | ||
prepareRowBackground(table.headers[index], rectCell); | ||
} | ||
// bold | ||
if( String(text).indexOf('bold:') === 0 ){ | ||
this.font('Helvetica-Bold'); | ||
text = text.replace('bold:',''); | ||
} | ||
// size | ||
if( String(text).indexOf('size') === 0 ){ | ||
let size = String(text).substr(4,2).replace(':','').replace('+','') >> 0; | ||
this.fontSize( size < 7 ? 7 : size ); | ||
text = text.replace(`size${size}:`,''); | ||
} | ||
// renderer column | ||
// renderer && (text = renderer(text, index, i, row, rectRow, rectCell)) // value, index-column, index-row, row nbhmn | ||
if(typeof renderer === 'function'){ | ||
text = renderer(text, index, i, row, rectRow, rectCell); // value, index-column, index-row, row | ||
} | ||
// TODO # Experimental | ||
// ------------------------------------------------------------------------------ | ||
// align vertically | ||
let topTextToAlignVertically = 0; | ||
if(valign && valign !== 'top'){ | ||
const heightText = this.heightOfString(text, { | ||
width: width - (cellPadding.left + cellPadding.right), | ||
align: align, | ||
}); | ||
// line height, spacing hehight, cell and text diference | ||
topTextToAlignVertically = rowDistance - columnSpacing + (rectCell.height - heightText) / 2; | ||
} | ||
// ------------------------------------------------------------------------------ | ||
this.text(text, | ||
lastPositionX + (cellPadding.left), | ||
startY + topTextToAlignVertically, { | ||
width: width - (cellPadding.left + cellPadding.right), | ||
align: align, | ||
}); | ||
// End datas | ||
// Rows | ||
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 + columnSpacing + rowDistance; // 0.5 is spacing rows | ||
// else this.addPage(); | ||
if(startY + 2 * rowHeight >= maxY) this.addPage(); | ||
startY = rowBottomY + columnSpacing + rowDistance; // 0.5 is spacing rows | ||
const rectRow = { | ||
x: columnPositions[0], | ||
// x: startX, | ||
y: startY - columnSpacing - (rowDistance * 2), | ||
width: tableWidth - startX, | ||
height: rowHeight + columnSpacing, | ||
} | ||
// add background | ||
// doc.addBackground(rectRow); | ||
lastPositionX = startX; | ||
row.forEach((cell, index) => { | ||
let align = 'left'; | ||
let valign = undefined; | ||
const rectCell = { | ||
// x: columnPositions[index], | ||
x: lastPositionX, | ||
y: startY - columnSpacing - (rowDistance * 2), | ||
width: columnSizes[index], | ||
height: rowHeight + columnSpacing, | ||
} | ||
prepareRowBackground(table.headers[index], rectCell); | ||
// Allow the user to override style for rows | ||
prepareRow(row, index, i, rectRow); | ||
if(typeof table.headers[index] === 'object') { | ||
// renderer column | ||
table.headers[index].renderer && (cell = table.headers[index].renderer(cell, index, i, row, rectRow, rectCell)); // text-cell, index-column, index-line, row | ||
// align | ||
table.headers[index].align && (align = table.headers[index].align); | ||
table.headers[index].valign && (valign = table.headers[index].valign); | ||
} | ||
// cell padding | ||
cellPadding = prepareCellPadding(table.headers[index].padding || options.padding || 0); | ||
// TODO # Experimental | ||
// ------------------------------------------------------------------------------ | ||
// align vertically | ||
let topTextToAlignVertically = 0; | ||
if(valign && valign !== 'top'){ | ||
const heightText = this.heightOfString(cell, { | ||
width: columnSizes[index] - (cellPadding.left + cellPadding.right), | ||
align: align, | ||
}); | ||
// line height, spacing hehight, cell and text diference | ||
topTextToAlignVertically = rowDistance - columnSpacing + (rectCell.height - heightText) / 2; | ||
} | ||
// ------------------------------------------------------------------------------ | ||
this.text(cell, | ||
lastPositionX + (cellPadding.left), | ||
startY + topTextToAlignVertically, { | ||
width: columnSizes[index] - (cellPadding.left + cellPadding.right), | ||
align: align, | ||
}); | ||
lastPositionX += columnSizes[index]; | ||
}); | ||
// Refresh the y coordinate of the bottom of this row | ||
rowBottomY = Math.max(startY + rowHeight, rowBottomY); | ||
// Separation line between rows | ||
separationsRow(startX, rowBottomY); | ||
}); | ||
// End rows | ||
lastPositionX += width; | ||
// update position | ||
this.x = startX; | ||
this.y = rowBottomY; // position y final; | ||
this.moveDown(); // break | ||
// add fire | ||
this.off("pageAdded", onFirePageAdded); | ||
// callback | ||
typeof callback === 'function' && callback(this); | ||
// nice :) | ||
resolve(); | ||
} catch (error) { | ||
// set style | ||
prepareRowOptions(row); | ||
prepareRow(row, index, i, rectRow); | ||
}); | ||
// Refresh the y coordinate of the bottom of this row | ||
rowBottomY = Math.max(startY + rowHeight, rowBottomY); | ||
// Separation line between rows | ||
separationsRow(startX, rowBottomY); | ||
// review this code | ||
if( row.hasOwnProperty('options') ){ | ||
if( row.options.hasOwnProperty('separation') ){ | ||
// Separation line between rows | ||
separationsRow(startX, rowBottomY, 1, 1); | ||
} | ||
// error | ||
reject(error); | ||
} | ||
}); | ||
// End datas | ||
// Rows | ||
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 + columnSpacing + rowDistance; // 0.5 is spacing rows | ||
// else this.addPage(); | ||
if(startY + 2 * rowHeight >= maxY) this.addPage(); | ||
startY = rowBottomY + columnSpacing + rowDistance; // 0.5 is spacing rows | ||
const rectRow = { | ||
x: columnPositions[0], | ||
// x: startX, | ||
y: startY - columnSpacing - (rowDistance * 2), | ||
width: tableWidth - startX, | ||
height: rowHeight + columnSpacing, | ||
} | ||
// add background | ||
// doc.addBackground(rectRow); | ||
lastPositionX = startX; | ||
row.forEach((cell, index) => { | ||
let align = 'left'; | ||
let valign = undefined; | ||
const rectCell = { | ||
// x: columnPositions[index], | ||
x: lastPositionX, | ||
y: startY - columnSpacing - (rowDistance * 2), | ||
width: columnSizes[index], | ||
height: rowHeight + columnSpacing, | ||
} | ||
prepareRowBackground(table.headers[index], rectCell); | ||
// Allow the user to override style for rows | ||
prepareRow(row, index, i, rectRow); | ||
if(typeof table.headers[index] === 'object') { | ||
// renderer column | ||
table.headers[index].renderer && (cell = table.headers[index].renderer(cell, index, i, row, rectRow, rectCell)); // text-cell, index-column, index-line, row | ||
// align | ||
table.headers[index].align && (align = table.headers[index].align); | ||
table.headers[index].valign && (valign = table.headers[index].valign); | ||
} | ||
// cell padding | ||
cellPadding = prepareCellPadding(table.headers[index].padding || options.padding || 0); | ||
// TODO # Experimental | ||
// ------------------------------------------------------------------------------ | ||
// align vertically | ||
let topTextToAlignVertically = 0; | ||
if(valign && valign !== 'top'){ | ||
const heightText = this.heightOfString(cell, { | ||
width: columnSizes[index] - (cellPadding.left + cellPadding.right), | ||
align: align, | ||
}); | ||
// line height, spacing hehight, cell and text diference | ||
topTextToAlignVertically = rowDistance - columnSpacing + (rectCell.height - heightText) / 2; | ||
} | ||
// ------------------------------------------------------------------------------ | ||
this.text(cell, | ||
lastPositionX + (cellPadding.left), | ||
startY + topTextToAlignVertically, { | ||
width: columnSizes[index] - (cellPadding.left + cellPadding.right), | ||
align: align, | ||
}); | ||
lastPositionX += columnSizes[index]; | ||
}); | ||
// Refresh the y coordinate of the bottom of this row | ||
rowBottomY = Math.max(startY + rowHeight, rowBottomY); | ||
// Separation line between rows | ||
separationsRow(startX, rowBottomY); | ||
}); | ||
// End rows | ||
// update position | ||
this.x = startX; | ||
this.y = rowBottomY; // position y final; | ||
this.moveDown(); // break | ||
// add fire | ||
this.off("pageAdded", onFirePageAdded); | ||
return this; | ||
} | ||
@@ -733,3 +748,3 @@ | ||
*/ | ||
tables(tables) { | ||
tables(tables, callback) { | ||
// if tables is Array | ||
@@ -741,2 +756,4 @@ Array.isArray(tables) ? | ||
( typeof tables === 'object' ? this.table( tables, tables.options || {} ) : null ) ; | ||
// callback | ||
typeof callback === 'function' && callback(this); | ||
} | ||
@@ -743,0 +760,0 @@ |
{ | ||
"name": "pdfkit-table", | ||
"version": "0.1.67", | ||
"version": "0.1.68", | ||
"description": "PdfKit Table. Helps to draw informations in simple tables using pdfkit. #server-side. Generate pdf tables with javascript (PDFKIT plugin) ", | ||
@@ -5,0 +5,0 @@ "main": "index.js", |
@@ -50,5 +50,7 @@ <p align="center"> | ||
// options | ||
const options = {} | ||
const options = {}; | ||
// callback | ||
const callback = () => {}; | ||
// the magic | ||
doc.table( table, options ); | ||
doc.table( table, options, callback ); | ||
@@ -393,2 +395,27 @@ // done! | ||
### 0.1.68 | ||
+ added ***Promise***. table is a Promise(); | ||
- Async/Await function | ||
```js | ||
;(async function(){ | ||
// create document | ||
const doc = new PDFDocument({ margin: 30, }); | ||
// to save on server | ||
doc.pipe(fs.createWriteStream("./document-2.pdf")); | ||
// tables | ||
await doc.table(table, options); | ||
await doc.table(table, options); | ||
await doc.table(table, options); | ||
// done | ||
doc.end(); | ||
})(); | ||
``` | ||
+ added ***callback***. | ||
```js | ||
doc.table(table, options, callback); | ||
``` | ||
### 0.1.63 | ||
@@ -395,0 +422,0 @@ |
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
Filesystem access
Supply chain riskAccesses the file system, and could potentially read sensitive data.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
47465
6
721
590
2