Insert folding marks on every page (wkhtmltopdf)
Asked Answered
E

3

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

Expel answered 8/2, 2016 at 12:31 Comment(10)
instead of down- and closevote, tell me why so that I'll be able to improve my question.Expel
I hate it when people do that.Robertroberta
I don't know why you got downvoted, but can you demonstrate where javascript comes into play here ? Im not familiar with wkhtmltopdf.Tiber
you are able to execute javascript while generating a pdf with wkhtmltopdf. My thoughts where calculation the height or the number of pages and then duplicate the marks.Expel
Do you need horizontal or vertical folding lines?Confraternity
@PXgamer should be horizontal, they are located on the left and right side of each page here's an image s12.postimg.org/439e0diil/pdf.jpgExpel
paint the folding marks in photoshop, then export as PNG then set the PNG as vertically repeating page background?Malcah
@Expel I can assume it must be fixed on every page if there are multiple pages? If so i probably have a solution for you.Boelter
@YoramdeLangen yes I need the marks on every page. Not only on the first one (that's what I already have)Expel
@JohannesJander I've already thought about doing it like that, but I'd prefer to be able to change the appearance with css instead of switching an image.Expel
B
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.

Boelter answered 17/2, 2016 at 20:2 Comment(11)
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.Expel
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).Boelter
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%Boelter
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.Expel
ok with a lot of playing around i think i'll achieve what i need. your answer is a good start! Thanks.Expel
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..Boelter
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 :DExpel
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? sspaste.com/paste/show/56c59fbc19101Expel
while(i <= pageNum) { ?Boelter
sry i think i didn't explain that well. on each page are 3 marks. but on the last page only 2 are displayed.Expel
Is the page the full height (wrapper element)? check it by adding an border on the wrapper element.Boelter
D
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.

Damaris answered 2/8, 2018 at 14:14 Comment(0)
A
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.

Amphioxus answered 20/4, 2021 at 9:4 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.