bpampuch / pdfmake

Client/server side PDF printing in pure JavaScript
http://pdfmake.org
Other
11.7k stars 2.04k forks source link

pageBreakBefore wreaking havoc with tables #1956

Open Dev63 opened 4 years ago

Dev63 commented 4 years ago

If you try to request a page break while in a table, the pageBreakBefore function keeps asking you for every subsequent cell in the current row, even after you've indicated a page break for the first cell. OK.

You can avoid this by setting dontBreakRows, but then the pageBreakBefore function receives incorrect information indicating that an extra row will be included on the current page #1955

But regardless of how you handle that situation, if you break the table across a page, the document's right margin is modified in varied ways for any subsequent tables in the document. In one example it reset the right margin to 0, so I could compensate by never using that, and instead setting right margins within contained elements, but in this example, the margin is changed to a negative number: https://jsfiddle.net/h30enu5o/

function test() {
  doc_def = {}
  doc_def.styles = {table: {margin: [20, 20, 10, 10]}};
  doc_def.pageMargins = [40, 55, 40, 40];

  // Avoid leaving single last row (subtotal) at top of next page
  doc_def.pageBreakBefore = function(cur, next, next_page, prev) {
    let pb = false;
    if ((cur.headlineLevel === 1) || (cur.headlineLevel === 0)) {       // Each table row has headlineLevel 1, last row has headlineLevel 2
        if (!next.find(node => node.headlineLevel) && next_page.length) {   // If last row, test further
        let next_row = next_page.find(node => node.headlineLevel);
        pb = (next_row && (next_row.headlineLevel === 2));  // Page break if 1st row on next page is last row of table
      }
    }
    return pb;
  }

  // Create several tables.  In call to pagebreakBefore, it appears that 2nd page starts with a new table.  But in the
  // generated document, the last row of current table appears at the top of 2nd page
  doc_def.content = [];
  [10, 2, 3, 0, 0, 3, 4, 60].forEach(num_rows => doc_def.content.push(add_table(num_rows)));
  let doc = pdfMake.createPdf(doc_def);
  doc.download('Test.pdf');
}

// Generate a simple table with requested number rows
function add_table(num_rows) {
  let table = {
    layout: 'standard',
    style: 'table',
    table: {
      headerRows: 1,
      //dontBreakRows: true,
      widths: [30, '*', 66],
      body: [['Item', '', 'Value']]     // Header row
    }
  }
  for (let i = 0; i <= num_rows; i++) {
    let row_str = i.toString();
    table.table.body.push([
      {text: row_str, headlineLevel: (i === num_rows) ? 2 : 1}, // Give last row headlineLevel 2
      {text:'Item name ' + row_str, headlineLevel:0},
      {text: row_str, headlineLevel:0, alignment: 'right'}
    ]);
  }
  return(table);
}
zburkhardt-zz commented 4 years ago

I have run in to this same issue as well. As a temporary workaround I now create a brand new table each time I want to force a page break and just put an empty element in the content array (between tables) with the pageBreak specified. However, this has added a lot of unwanted complexity, having a better way to handle this would be great!

Dev63 commented 4 years ago

@zburkhardt-zz, that's a very interesting idea. Are you somehow doing all page break calculations outside of pdfMake and passing pdfMake the perfect structure so it will output as desired, or are you somehow able to modify the content as you describe from within a pdfMake pageBreak callback? In either case, how are you doing this?