pdfmake icon indicating copy to clipboard operation
pdfmake copied to clipboard

How to handle tables being wider than the page width?

Open Bazze opened this issue 10 years ago • 19 comments

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.

Bazze avatar Nov 18 '15 21:11 Bazze

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.

AaronBuxbaum avatar Dec 02 '15 15:12 AaronBuxbaum

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.

Bazze avatar Dec 02 '15 21:12 Bazze

Any progress on this old issue? We have the same problem.

DamienCassou avatar Aug 24 '17 14:08 DamienCassou

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

9vimu9 avatar Sep 14 '17 03:09 9vimu9

any progress yet ?

akhiljain1611 avatar Mar 13 '18 16:03 akhiljain1611

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!

jasonvasquez avatar Jun 23 '18 02:06 jasonvasquez

    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.

ValeSauer avatar Mar 06 '19 13:03 ValeSauer

any work around?

ShubhamOjha avatar Jun 19 '19 08:06 ShubhamOjha

any updates on this issue?

srallapalli avatar Jul 11 '19 11:07 srallapalli

Much needed by me too!

GiordanoArman avatar Sep 17 '19 19:09 GiordanoArman

Any updates on this? At least any way of knowing when table will overflow?

axelisr avatar Oct 17 '19 18:10 axelisr

You could use DocMeasure.measureTable to get the actual size and compare it to the page width and height.

nmenke avatar Oct 17 '19 18:10 nmenke

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?

ajperez81 avatar Dec 10 '19 16:12 ajperez81

If you don't use the bundled version, you can access everything needed. Just bundle it yourself.

nmenke avatar Dec 10 '19 18:12 nmenke

@Bazze Did you find any workaround on this issue?

MussaCharles avatar Feb 17 '20 14:02 MussaCharles

@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 avatar Feb 17 '20 17:02 Bazze

@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 :)

MussaCharles avatar Feb 17 '20 17:02 MussaCharles

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';
},

temo-o avatar Feb 16 '22 19:02 temo-o

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 }; 

danielrose7 avatar May 31 '24 16:05 danielrose7