Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

pdfMake - Wide Table Page Break

Tags:

pdfmake

I am using pdfMake to generate table reports. Some of the reports are very wide and dont fit on a standard page width, even in landscape mode. Currently, pdfMake is cutting off the table content when it overflows past the page margin.

I would like to page break the table when it is too wide, much like when the rows overflow to the next page.

Is this possible using pdfMake?

Can using pageBreakBefore callback function help for this?

Thank you

like image 751
Jeffrey Avatar asked May 25 '17 14:05

Jeffrey


2 Answers

Yes, this is possible with pdfMake, even though not currently a feature.

To achieve this, you can just break overflowing columns into another table. We can put all the tables in an array, then just set them in the docDefinition as follows. Any common attributes you want in the tables can be defined in the Template constructor.

for (var i = 0; i < tables.length;i++) {
    docDefinition.content[i] = new Template();  
    docDefinition.content[i].table.body  = tables[i];
    docDefinition.content[i].table.widths = widths[i];
    if (i > 0) {
        docDefinition.content[i].pageBreak = 'before';
    }
}

function Template(){
    this.table = {
            dontBreakRows: true
    };
    //zebra stripes layout
    this.layout = {
            fillColor: function (row, node, col) {
                return (row % 2 === 0) ? '#CCCCCC' : null;
            }
    }
}

How do we determine if a column will overflow? We have two options:

  1. If you are using bootstrap datatables, you can use the "width" attribute in the html.
  2. pdfmake calculates the actual width, but you may have to dig around in pdfmake.js.

Then, just loop through, adding widths until you exceed your limit (my limit was for 8pt font). You can do this for THs then save those column splits and use those for the TDs.

If the final page is just barely overflowing, we don't want the final page to have just one column, we want each page to have roughly the same width. We calculate the number of pages needed, then find the desired break point from there. To link the pages together more easily, you can add a row number column at the beginning of each table.

var colSplits = [];
var tables = new Array();

function parseTHs(colSplits, tables) {
    var colSum = 0;
    var pageSize = 1120-7*rows.toString().length;
    var paddingDiff = 11.9;
    var columns = 0;
    var prevSum;
    var i = 0;
    var width = $(".dataTables_scrollHeadInner > table").width();
    var pages = Math.ceil(width/pageSize);
    console.log("pages: "+pages);
    var desiredBreakPoint = width/pages;
    console.log("spread: "+desiredBreakPoint);
    var limit = pageSize;
    var row = ['#'];
    var percent = '%';
    widths.push(percent);
    $(".dataTables_scrollHeadInner > table > thead > tr:first > th").each(function() {          
        prevSum = colSum;
        colSum += $(this).outerWidth()-paddingDiff;
        //if adding column brings us farther away from the desiredBreakPoint than before, kick it to next table
        if (colSum > limit || desiredBreakPoint-colSum > desiredBreakPoint-prevSum) {
            tables[i] = [row];
            row = ['#'];
            widths.push(percent);
            colSplits.push(columns);
            i++;
            desiredBreakPoint += width/pages;
            limit = prevSum+pageSize;
        }
        row.push({text: $(this).text(), style:'header'});
        widths.push(percent);
        columns++;
    });
    //add the final set of columns
    tables[i] = [row];
}

function parseTDs(colSplits, tables) {
    var currentRow = 0;
    $("#"+tableId+" > tbody > tr").each(function() {
        var i = 0;
        var row = [currentRow+1];
        var currentColumn = 0;
        var split = colSplits[i];
        $(this).find("td").each(function() {
            if (currentColumn === split) {
                tables[i].push(row);
                row = [currentRow+1];
                i++;
                split = colSplits[i];
            }
            row.push({text: $(this).text()});
            currentColumn++;
        });
        //add the final set of columns
        tables[i].push(row);
        currentRow++;
    });
}

parseTHs(colSplits, tables);
parseTDs(colSplits, tables);

Note: If you want the columns to fill all the available page, there's a good implementation for that at this link.

I just added '%' for the widths and added that code to pdfmake.js.

Hope this helps!

like image 174
mjlitz Avatar answered Oct 02 '22 15:10

mjlitz


Just add dontBreakRows property in your table object like this

       table: {
                dontBreakRows: true,
                widths: [30,75,48,48,48,48,48,115],
                body: []
       }
like image 37
Sanoodia Avatar answered Oct 02 '22 14:10

Sanoodia