20

I'm using wkhtmltopdf 0.12.2.1 to create invoices and so on.

I need to display folding marks on every page in a pdf. How can I repeat them with javascript on every page, if the content is larger than one?

That's my basic markup

<!DOCTYPE html>
<html>
  <head>
    <title>PDF Title</title>
    <meta http-equiv="Content-Type" content="text/html; charset=utf-8">
  </head>
  <body id="pdf-page">
    <div class="marks">
      <div class="mark mark-top mark-left"></div>
      <div class="mark mark-top mark-right"></div>
      <div class="mark mark-middle mark-left"></div>
      <div class="mark mark-bottom mark-left"></div>
      <div class="mark mark-bottom mark-right"></div>
    </div>
    <div id="print-area">
      <div id="letter-head"></div>
      <div id="subject-line">Subject</div>
      <div id="document-content">
        ....
        ....
        ....
      </div>
    </div>
  </body>
</html>

It looks basically like that Image

Cœur
  • 37,241
  • 25
  • 195
  • 267
csy_dot_io
  • 1,199
  • 6
  • 19

3 Answers3

5

Okay, this took me days to figuring this out.... because of the lack of examples on the internet (for PHP/HTML/Javascript). We got the following steps:

  1. get the DPI of your document
  2. set element to the real page size (out of sight)
  3. create wrapper/page element (your case .marks)
  4. determine if content of the page needs multiple pages to fit on

Note: I'm doing this without testing etc. and you need to play around of course.

...

<div class="marks">
    <div class="mark mark-top mark-left"></div>
    <div class="mark mark-top mark-right"></div>
    <div class="mark mark-middle mark-left"></div>
    <div class="mark mark-bottom mark-left"></div>
    <div class="mark mark-bottom mark-right"></div>
</div>

...

<script>
    // some static stuff found it somewhere on the internet
    var PDF = {
        width: 8.27, // inches, 210mm
        height: 11.65, // inches, 296mm
        margins: {
            top: 1.97, left: 0.34,
            right: 0.393700787, bottom: 0.393700787
        }
    };

    $(document).ready(function() {
        // get page sizes by creating 'shadow' element
        var pageSize = $('<div></div>')
            .css({
                height: PDF.height +'in', width: PDF.width +'in',
                top: '-100%', left: '-100%',
                position: 'absolute',
            })
            .appendTo('body');

        // set debug element
        $('.debug').html(pageSize.height());

        // set every page elements .marks to proper height
        // dont forget the substract the header and footer height
        $('.marks').height(pageSize.height() - footerHeight - headerHeight);

        // @TODO: duplicate .marks to times a pages is created, and !!maybe!! set the top of .marks with pageSize.height()
    });
</script>

I found my inspiration for the code here.

This worked out for me because I had fixed height elements and needed to repeat it on the page (in my .wrapper elm same as your .marks and they were 'relative' elements). In this way I could determine when to 'open' an new page by closing .marks in your case.

Yoram de Langen
  • 5,391
  • 3
  • 24
  • 31
  • how did you find out when a new page starts? I didn't understand that. also my marks are positioned absolute and only on the first page. – csy_dot_io Feb 18 '16 at 08:36
  • Because the dynamic elements had an height of 150px and in development I check how much I could place on the page before I needed to close my `.wrapper` element and started a new `.wrapper`. Then I continued with placing the data into a page. Example: page had height of 550px each dynamic element has an height of 150px. 550 / 150 = 3,6 times rounded down to 3 and I know much element I got to place in ONE single page/.wrapper element. So after 3 elements I ended the .wrapper and started a new .wrapper element (`$index % 3 === 0 ? close + open : nothing`). – Yoram de Langen Feb 18 '16 at 08:46
  • Because the `.wrapper` element has an height of: `page_height - footer_height - header_height` it would technically fit perfectly between the header and foorter on each page that's got generated. Though don't forget to set an `page-break-after` on the `.wrapper` element. Within the wrapper element place your marks as an 'absolute' element and place them something like: 35% - 50% - 65% – Yoram de Langen Feb 18 '16 at 08:48
  • so the key to get this working is to get the height of every element on the page between header and footer and divide it by page size. Cause in practice the elements wont have the same height. Ok that's clear. I'll check if this is working. – csy_dot_io Feb 18 '16 at 09:01
  • 2
    ok with a lot of playing around i think i'll achieve what i need. your answer is a good start! Thanks. – csy_dot_io Feb 18 '16 at 09:44
  • Nice to hear it helpt out! Its indeed a bit hacking around.. but unfortunately wkhtmltopdf is the best HTML to PDF generator i've been found so far.. – Yoram de Langen Feb 18 '16 at 09:54
  • that's true. But at this specific case, creating folding marks with for example dompdf would be much easier. But that's the only benefit :D – csy_dot_io Feb 18 '16 at 09:57
  • nearly there. Just a small bug is remaining, which causes that the last mark on the last page to disappear (so there are only the top and middle mark on the last page) any suggestions? http://www.sspaste.com/paste/show/56c59fbc19101 – csy_dot_io Feb 18 '16 at 10:42
  • `while(i <= pageNum) {` ? – Yoram de Langen Feb 18 '16 at 11:39
  • sry i think i didn't explain that well. on each page are 3 marks. but on the last page only 2 are displayed. – csy_dot_io Feb 18 '16 at 11:41
  • 1
    Is the page the full height (wrapper element)? check it by adding an border on the wrapper element. – Yoram de Langen Feb 18 '16 at 13:01
2

I know this thread is old, but I just had the same problem and also spent numerous hours on it. The basic problem is that wkhtml simply does not know the number of pages which will result, before it has rendered. That's why only the header/footer get that information.

Calculating something with DPI does not make any sense because $(document).height() will always return the same value regardless of DPI setting. Also, this height is not given back correctly anyway; I tried it with "textblock, textblock, textblock" = 4500 px, "textblock, image, textblock, image, textblock, image" = 4560 px, despite the images having > 500px height each. So, this road leads nowhere.

My working solution:

  1. Generate the HTML and generate the PDF (without a TOC).
  2. Use the commandline tool "pdfinfo" to get the actual number of pages.
  3. Generate the HTML again, but this time, pass along the number of pages.
  4. In your HTML, do something like this (adapt this to your template language):

$factor = 100 / $totalPages; // = 25 with 4 pages

for ($pageNum = 1; $pageNum <= $totalPages; $pageNum++) { $html .= '<div style="position: absolute; top: ' . ($factor*$pageNum)-($factor/2) . '%"></div> }

  1. You will get 4 DIVs with a "top" attributes of 25-12.5, 50-12.5, 75 - 12.5, 100-12.5 percent.
  2. Render the PDF
  3. Be happy, finally

I am using wkhtml 0.12.3 with patched qt.

user2390733
  • 501
  • 1
  • 5
  • 7
0

Old thread, but I have a full JS response. It take place at the end of the html file to render. Inspired by PhP solution below.

<script>
// get total Height
var height = document.body.clientHeight;
// in my exemple, I use twig to generate HTML
// pageHeightInCm is the wanted page Height
// 125 is the value I found to transform cm in px
var pageHeight = {{ pageHeightInCm }} * 125;
var pageQtt = Math.floor((height)/pageHeight) ;
var factor = 100 / pageQtt;
for (var pageNum = 0; pageNum < pageQtt; pageNum++) {
    var element = document.createElement("div");
    element.classList.add('borderer');
    element.style.position = "absolute";
    element.style.top = ((factor*pageNum)) + '%';
    element.style.height = factor + '%';
    element.style.width = '100%';
    var parentDiv = document.getElementById("wrapper").parentNode;
    var ref = document.getElementById("wrapper");
    parentDiv.insertBefore(element, ref);
}

This way, I obtain one div per page. Each div have page heigth and width. Other css stuff is add in a separated css file on .borderer selector.