Skip to content
Advertisement

Why PDFKit doesn’t work well with margins?

although I just don’t have the need to ask something normally (internet is just a gold mine, especially this site), this is getting me off, as I had been two days trying to get this working and I don’t find anything useful. Link for similar post would be thanked if founded, as I didn’t found one. Let’s get started.


I’m using PDFKit with Python 2.7 and Django 1.9 to generate some HTML to PDF. Easy work. The HTML just looks like this:
<!DOCTYPE html>
<html>
<head>
  <meta charset="utf-8">
  <style type="text/css">
    * {
      /* Box-model */
      margin: 0;
      padding: 0;
    }

    .wrapper {
      /* Box-model */
      width: 229mm;
      height: 161mm;
    }
  </style>
</head>
<body>
  {% for someone in people %}
  <div class="wrapper">
    <p><strong>{{ someone.complete_name }}</strong></p>
    <p>{{ someone.bio }}</p>
  </div>
  {% endfor %}
</body>
</html>

My PDFKit options looks like this:

{
    "page-width": "229mm",
    "page-height": "162mm",
    "paper-size": "CE5",
    "encoding": "utf-8"
}

In both cases I just don’t add any margins or paddings. I generate the PDF, and looks great (no surprises, all goes where it belongs). But, if I add any margin/padding in the HTML and/or options, I just get a continued repetitive incremental margin when the PDF is generated (to give an example, in 50 pages, the first one has no margin, and the 50th has a margin of about two fingers more or less). I tried different combinations of margins, paddings and heights and nothing: when I just add margins or paddings, the PDF gets crazy.

(Note: yeah, page-height is 1mm larger than height in HTML, but that is needed so the wrapper fits in the PDF page).

Anyone had meet this problem before? Thanks in advance.

Advertisement

Answer

Ok, I finally managed to understand what was happening:

Let’s say I want to print a paper size A4 (you have a list here). So, translating it into px (because its easier to set cm or mm instead of calculating manually pixels), we have that 1 mm = 3.779528 px. We set our CSS height: Xmm. Easy.

The problem gets when PDFKit, that it uses WebKit from what I know, renders HTML: WebKit only has a 3 decimal precision point value, so our 3.779528 pass to be 3.779 (it rounds towards down if I remember), which is not the real size. So that means that adding any type of margin/padding/positioning will break things and create magic spacing, as the lengths aren’t correct (even if you use the same dimension unit for those).

The good news are that not setting any margin/padding/positioning will not break things, so in some stuff it’ll be good enough.

But what if you need to add margins or something like that? CSS’s page-break properties (the linked one is page-break-after) comes to the rescue. It secures that any magic spacing isn’t setted when printing the HTML (via render, via web browser).

Taking that into account, an example would be this:

<!DOCTYPE html>
<html>
<head>
  <style type="text/css">
    * {
      /* Box-model */
      /* To ensure no unexpected magic spacing */
      margin: 0;
      padding: 0;
    }

    .wrapper {
      /* Box-model */
      width: 197mm; /* + 100 from horizontal padding = 297 */ 
      height: 160mm; /* + 50 from vertical padding = 210 */
      padding: 25mm 50mm;
    }

    /* The selector applies the page break after, so we have to exclude the last wrapper */
    .wrapper:not(:last-of-type) {
      /* Box-model */
      page-break-after: always;
    }
  </style>
</head>
<body>
  {% for someone in people %}
  <div class="wrapper">
    <p><strong>{{ someone.complete_name }}</strong></p>
    <p>{{ someone.bio }}</p>
  </div>
  {% endfor %}
</body>
</html>

And voila, you have a perfectly fitted template for rendering PDF data, with no magic spacing. You only have to ensure that the element you use for “representing” a page (wrapper in this example) has the same dimensions as the page’s.

User contributions licensed under: CC BY-SA
9 People found this is helpful
Advertisement