8

Is there a way to dynamically check remaining page height in PDFMake? When dynamically creating pages, I want to be able to check the remaining available page height to compare it to the element height, so that the last on page element (e.g. image or long textarea content) could not be cut but be transfered to another page instead. Do not know how to do it dynamically.

Peter Babukh
  • 352
  • 5
  • 18

4 Answers4

6

Thanks all of you. I finally used pageBreakBefore function, and headlineLevel which I use as a marker, and found a version of pdfmake which allows we to see if the node is an image, and thus I calculate the height of the element. Here is how it looks in my code. There I also have a footer and have to consider it in my calculations so that the content should not go on it:

pageBreakBefore: function(currentNode, followingNodesOnPage, nodesOnNextPage, previousNodesOnPage) {
          var pageInnerHeight = currentNode.startPosition.pageInnerHeight;
          var top = (currentNode.startPosition.top) ? currentNode.startPosition.top : 0;
          var footerHeight = 30;
          var nodeHeight = 0;
          if (followingNodesOnPage && followingNodesOnPage.length) {
            nodeHeight = followingNodesOnPage[0].startPosition.top - top;
          }

          if (currentNode.headlineLevel === 'footer') return false;

          return (currentNode.image && (top + nodeHeight + footerHeight > pageInnerHeight))
              || (currentNode.headlineLevel === 'longField' && (top + nodeHeight + footerHeight > pageInnerHeight))
              || currentNode.startPosition.verticalRatio >= 0.95;
        }
Peter Babukh
  • 352
  • 5
  • 18
  • I have dynamic content, dont know how to use `pageBreakBefore` Can you help me there? here is the link of my question. https://stackoverflow.com/questions/45096504/add-new-page-while-generating-pdf-from-html-with-dynamic-content?noredirect=1#comment77165384_45096504 – Nikhil Radadiya Jul 14 '17 at 13:47
  • Checkout my answer at your link. – Peter Babukh Jul 15 '17 at 18:40
  • var pageInnerHeight = currentNode.startPosition.pageInnerHeight; var top = (currentNode.startPosition.top) ? currentNode.startPosition.top : 0; Thank you so much. – Dipak Jun 03 '20 at 05:57
5

Well, I might be a bit late. But in Version 0.1.17, they introduced the pageBreakBefore function.

Release Notes on Github

You can now specify a pageBreakBefore function, which can determine if a page break should be inserted before the page break. To implement a 'no orphan child' rule, this could like like this:

var dd = {
  content: [
    {text: '1 Headline', headlineLevel: 1},
    'Some long text of variable length ...',
    {text: '2 Headline', headlineLevel: 1},
    'Some long text of variable length ...',
    {text: '3 Headline', headlineLevel: 1},
    'Some long text of variable length ...',
  ],
  pageBreakBefore: function(currentNode, followingNodesOnPage, nodesOnNextPage, previousNodesOnPage) {
    return currentNode.headlineLevel === 1 && followingNodesOnPage.length === 0;
  }
}

If pageBreakBefore returns true, a page break will be added before the currentNode. Current node has the following information attached:

{
 id: '<as specified in doc definition>', 
 headlineLevel: '<as specified in doc definition>',
 text: '<as specified in doc definition>', 
 ul: '<as specified in doc definition>', 
 ol: '<as specified in doc definition>', 
 table: '<as specified in doc definition>', 
 image: '<as specified in doc definition>', 
 qr: '<as specified in doc definition>', 
 canvas: '<as specified in doc definition>', 
 columns: '<as specified in doc definition>', 
 style: '<as specified in doc definition>', 
 pageOrientation '<as specified in doc definition>',
 pageNumbers: [2, 3], // The pages this element is visible on (e.g. multi-line text could be on more than one page)
 pages: 6, // the total number of pages of this document
 stack: false, // if this is an element which encapsulates multiple sub-objects
 startPosition: {
   pageNumber: 2, // the page this node starts on
   pageOrientation: 'landscape', // the orientation of this page
   left: 60, // the left position
   right: 60, // the right position
   verticalRatio: 0.2, // the ratio of space used vertically in this document (excluding margins)
   horizontalRatio: 0.0  // the ratio of space used horizontally in this document (excluding margins)
 }
}
Founded1898
  • 977
  • 5
  • 13
0

I recently talked to a colleague on a dev congress. They faced the same problem. If you really need to know there are 2 possibilities as far as I know:

1) Test render the page data and check if output is more than one page. This is derpy but you do not know internals.

2) Do the calculations which are done within pdfmake on your own before generating the pdf. On how to you need to look into the pdfmake generation code itself unfortunately.

If there is a more elegant solution I'd very much like to know myself!

Flowkap
  • 729
  • 6
  • 16
0

I am doing it manually. All you need is to know what is the max size of the page, before text starts getting out. In my case, Legal paper size has max width of 700px before text gets truncated.

So what I do is to reduce the column widths in a loop until totalPageWidth is less than acceptableWidth. It may not be very readable, but here is the code.

// For a legal size page, total width is 700. So try and push all columns within 700
        // Following lines are there to reduce the width of columns so as to adjust the total width.
        // Width is deducted for every column so as not to affect any individual column.
        totalOutOfPageWidth = totalWidth - 700;
        var totalWidthDeducted = 0;
        while (totalOutOfPageWidth > 0) {
            for (var c = 0; c < colWidthArray.length; c++) {
                if (totalOutOfPageWidth > 0) {
                    if (colWidthArray[c] == width70 - totalWidthDeducted) {
                        colWidthArray[c] = colWidthArray[c] - 5;
                        totalOutOfPageWidth -= 5;
                    }
                }
            }
            if (totalOutOfPageWidth > 0) {
                for (var c = 0; c < colWidthArray.length; c++) {
                    if (colWidthArray[c] == width50 - totalWidthDeducted) {
                        colWidthArray[c] = colWidthArray[c] - 5;
                        totalOutOfPageWidth -= 5;
                    }
                }
            }
            if (totalOutOfPageWidth > 0) {
                for (var c = 0; c < colWidthArray.length; c++) {
                    if (colWidthArray[c] == width35 - totalWidthDeducted) {
                        colWidthArray[c] = colWidthArray[c] - 5;
                        totalOutOfPageWidth -= 5;
                    }
                }
            }
            if (totalOutOfPageWidth > 0) {
                for (var c = 0; c < colWidthArray.length; c++) {
                    if (colWidthArray[c] == width25 - totalWidthDeducted) {
                        colWidthArray[c] = colWidthArray[c] - 5;
                        totalOutOfPageWidth -= 5;
                    }
                }
            }
            totalWidthDeducted += 5;
aMazing
  • 1,430
  • 4
  • 20
  • 43