How to handle tables being wider than the page width?
Hello!
I haven't been able to find any documentation or issues about how to handle tables with many columns, causing the table to be wider than the page width. How would one handle such a case? I know there is built-in support for automatically breaking the page if the table is getting close to the end of the page but is there similar features for breaking when a table gets too wide?
I'm currently in a situation where I have tables with a lot of columns causing it to get too wide. Would be nice if it just breaks and continues below just as for the page break feature.
Thanks.
I have the same bug; I think a good enhancement would be to add a flag that will throw an error if it extends off that page. That way, the consumer can handle the case on their end.
Breaking page horizontally would be a good feature as well.
Yes, it needs to be handled one way or the other. I actually did an implementation that works for my specific use-case. When a table gets to wide it puts exceeding columns into a new table just below where the headerRows and headerColumns (new setting introduced by me) are repeated. I created a branch in my fork, https://github.com/Bazze/pdfmake/tree/feature/wide-tables-without-filesaver-2093de79, where maybe you can find something of use.
My fix for this is a school-book example of quick and dirty so you should NOT use it. I just had to get something to work quick, so I copy and pasted and scribbled some code down. I'm not proud of it.
Any progress on this old issue? We have the same problem.
change page size
extend: 'pdfHtml5',
orientation: 'landscape',//landscape give you more space
pageSize: 'A0',//A0 is the largest A5 smallest(A0,A1,A2,A3,legal,A4,A5,letter))
title:title
datatatables link https://datatables.net/reference/button/pdf
more about standard paper sizes http://www.papersizes.org/a-series-full-sized-diagram.htm
any progress yet ?
We have the same issue. Is there anyway to programatically determine that the table has been truncated, or to calculate it's final width to see if it's too wide? I'm fine for now if we could know it was going to happen, and then reshape the content accordingly.
Thanks!
calculateTableFontSize = function (colCount) {
var defaultSize = docDefinition.defaultStyle.fontSize; // default = 10
var size = defaultSize
size = 18 - colCount
if (size > defaultSize)
size = defaultSize
return size
}
if (table.table.body[0] && table.table.body[0].length) {
docDefinition.styles.tableCell.fontSize = calculateTableFontSize(table.table.body[0].length) //add style: 'tableCell' to every table cell
}
docDefinition.content.push(table)
As my tables are usually only slightly too wide (max. 200% of page width) I can scale fontSize in order to make tables with too many columns fit to the page. Perhaps this workaround helps anyone. However this does only take into account number of columns, not the actual width of the column.
any work around?
any updates on this issue?
Much needed by me too!
Any updates on this? At least any way of knowing when table will overflow?
You could use DocMeasure.measureTable to get the actual size and compare it to the page width and height.
You could use DocMeasure.measureTable to get the actual size and compare it to the page width and height.
That seems to be the most reasonable way to workaround this issue. Is there any way to access DocMeasure.measureTable from outside pdfmake? Or would I need to modify pdfmake itself? I don't see any way directly from the pdfmake class, but maybe there are other objects exported aswell?
If you don't use the bundled version, you can access everything needed. Just bundle it yourself.
@Bazze Did you find any workaround on this issue?
@Bazze Did you find any workaround on this issue?
I did my own workaround, as I mentioned here: https://github.com/bpampuch/pdfmake/issues/441#issuecomment-161433196
Basically what I did back then was to rewrite the document measurement function to split the table when it got wider than the page width. However, it's not a solution that I've maintained or even using anymore. We've now redesigned the report we used it for so we no longer get very long tables.
@Bazze Did you find any workaround on this issue?
I did my own workaround, as I mentioned here: #441 (comment)
Basically what I did back then was to rewrite the document measurement function to split the table when it got wider than the page width. However, it's not a solution that I've maintained or even using anymore. We've now redesigned the report we used it for so we no longer get very long tables.
Thanks, Let me try digging a little bit more :)
You can find answer in this thread.
It's basically this:
pageSize: 'A0',//A0 is the largest A5 smallest(A0,A1,A2,A3,legal,A4,A5,letter))
And if after this columns/data will be missaligned you can use this inside customize : (src)
doc.defaultStyle.alignment = 'center';
doc.styles.tableHeader.alignment = 'center';
So basically I ended up with smth like this:
extend: 'pdfHtml5',
text: '<img style="width=100%; display:none;" src="/assets/custom-images/pdf_icon.png" title="pdf" alt="pdf" />',
footer: false,
title: pdf_title,
orientation: 'landscape',
pageSize: 'A3',
customize: function (doc) {
doc.content[2].table.widths =
Array(doc.content[2].table.body[0].length + 1).join('*').split(''); //You probably don't need this part
doc.defaultStyle.alignment = 'center';
doc.styles.tableHeader.alignment = 'center';
},
It's a bit hacky of a solution but I found that if you first call pdfMake.createPdf(...) on the content you want, the library injects some real calculations into 'widths' rather than just relying on '*' and 'auto'
From here we then loop and downsize the font to fit the content the the page per something like
// #region helpers
const MIN_FONT_SIZE = 4; // really small!
const buildPaddingLeft = (padding = 8) => (index: number) => index === 0 ? 0 : padding;
const buildPaddingRight = (padding = 8) => (index: number, node: ContentTable) => index + 1 === node.table.widths?.length ? 0 : padding;
const inchToPts = (inch: number) => inch * 72; // 1 inch = 72 points
// #endregion
// #region base case setup
const orientation = foo ? "portrait" : "landscape";
const pageWidth = orientation === "landscape" ? inchToPts(11) : inchToPts(8.5); // based on US Letter (8.5" x 11")
const pageSideMargin = orientation ? 40 : 80; // 20pts | 40pts each side
const maxPageWidth = pageWidth - pageSideMargin;
const tableLayout: TableLayout = { ...pdfMake.tableLayouts.default };
const initialFontSize = orientation ? 20 : 10; // or something different for you!
let lineItemsFontSize = initialFontSize;
const fontSizeDelta = Math.max(Math.round(initialFontSize / 10), 1); // the downsize step per run
// #endregion
// #region loop to fit table
let doesTableFitOnPage = false;
while (!doesTableFitOnPage) {
doesTableFitOnPage = await new Promise<boolean>((resolve) =>
pdfMake
.createPdf({
content: {
...lineItemContent,
fontSize: lineItemsFontSize,
layout: tableLayout,
},
})
.getDataUrl(() => {
if (lineItemsFontSize <= MIN_FONT_SIZE) return resolve(true); // too small... overflow
const paddingRight = lineItemsFontSize - 2;
const paddingLeft = lineItemsFontSize - 2;
const paddingPerColumn = paddingRight + paddingLeft + 1;
const edgeSkips = paddingLeft + paddingRight; // left and right most cells skip padding
const paddingTotal =
paddingPerColumn * lineItemContent.table.widths.length - edgeSkips;
// @ts-expect-error _calcWidth private + injected by pdfmake.createPdf
const colWidths = lineItemContent.table.widths.map((w) => w._calcWidth);
const colTotal = colWidths.reduce((total, w) => (total += w), 0);
const tableWidth = paddingTotal + colTotal;
const tableFits = tableWidth <= maxPageWidth;
if (tableFits) return resolve(true);
// table does not fit, reduce font size and try again
lineItemsFontSize -= fontSizeDelta;
tableLayout.paddingLeft = buildPaddingLeft(
lineItemsFontSize - fontSizeDelta,
);
tableLayout.paddingRight = buildPaddingRight(
lineItemsFontSize - fontSizeDelta,
);
return resolve(false);
}),
);
}
// #endregion
// return content to where injected into document def
return { ...lineItemContent, fontSize: lineItemsFontSize, layout: tableLayout };