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:
- To add an option to wkhtmltopdf’s browser configuration
- Modify the html you gave to wkhtmltopdf’s browser
Now, we can improve the rendering of our pdf!
How to handle the dimensions of the pdf
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
viewport-size
is used to emulate the window sizepage-width
andpage-height
are used to set the pdf size
// 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)
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…