Skip to content

One Year With Wkhtmltopdf: One Thousand One Problems, One Thousand One Solutions

Vincent Langlet3 min read

In the previous article, we saw how to use wkhtmltopdf. But, when I did it, I encountered problems that I really want to share with you.

Each problem has a solution

First, you have to understand what wkhtmltopdf does: rendering the html with ‘its own browser’. So, when something does not seem to work, try:

Now, we can improve the rendering of our pdf!

How to handle the dimensions of the pdf

wkhtmltopdf3

First, we need to define these two new functions:

// controller.js

// Return the width and the height of the document
// In the way the user see it
getSize = function(html) {
  return {
    width: html.offsetWidth,
    height: html.offsetHeight,
  }
}

// Return the real full height of the document
// With no scroll
getRealHeight = function(html) {
  clone = angular.copy(html)
  clone.style.height = 'auto'
  realHeight = clone.offsetHeight

  return realHeight
}

And give these two new values to our back-end

// controller.js

$scope.print = function() {
  var html = document.getElementsByTagName('html')[0];
  var body = {
    html: html,
    size: getSize(html),
    realHeight: getRealHeight(html),
  };

  $http.post('api/pdf/print', body, {responseType: 'arraybuffer'})
  .success(function(response) {
    var file = new Blob([ response ], {type: 'application/pdf'});
    FileSaver.saveAs(file, 'print.pdf');
  })
}

In the back-end you just have to set the following options

// pdf.js
  var size = req.body.size
  var realHeight = req.body.realHeight

  var options = {
    'viewport-size': size.width + 'x' + size.height,
    // I found a 0.271 ratio
    'page-width': (size.width * 0.271),
    'page-height': (realHeight * 0.271),
    'user-style-sheet': CSSLocation,
  }

This way you have exactly what you see on your navigator!

Nota Bene: the page-width and page-height values were given in mm, so I thought I should have a 0.264583333 ratio from pixel to mm. But when I tried, I found 0.271 as a better approximation. (This was useful on my project because of SVG with inline dimensions)

PDF

 How to display the images correctly

You need to know one thing: wkhtmltopdf needs absolute paths for images. In my project, I used this fix:

// pdf.js

// Replace relativ path of img by absolute path
html = html.replace(/static\/images\//g, projectLocation + 'client/www/static/images/')

But you have to change the regex /static\/images\//g and the path client/www/static/images/ according to where the images are stored.

How to modify the pdf before printing it

If you understand how wkhtmltopdf works, this hint won’t surprise you: modify the html you send!

// controller.js
  var body = {
    html: getModifiedHtml(html),
    size: getSize(html),
    realHeight: getRealHeight(html),
  };

Now you can do what you want with your pdf:

// controller.js
getModifiedHtml = function(html) {
  newHtml = angular.copy(html)
  newHtml = removeHeader(newHtml)
  newHtml = removeFooter(newHtml)
  newHtml = addNewHeader(newHtml)
  newHtml = addNewFooter(newHtml)
  newHtml = doStuff(newHtml)
  // ...
  return newHtml
}

But the first line newHtml = angular.copy(html) is really important.
Do not forget to start by copying the html you got before modifying it.
Otherwise, your user will be surprised…

wkhtmltopdf4