Phantomjs Doesn't Render Footers with a Custom Styles

PhantomJS: place div element on bottom part of a page

The javascript based solution:

You can find out the full height of the single page. You can also find out the real height of the document. These two values helps you to calculate the bottom of the last page. Then you can absolute position the cut area to the last page using js.

<script>
// magical page size number was only estimated based on very long pdf
// it differs based on the recipe and platform used to render
// windows phantomjs 1.9.8 = 1274
// linux/osx phantomjs 1.9.8 = 989
var pageSize = 1274
// the size of the area you want to cut
var cutDivHeight = 200
var numberOfPages = Math.ceil(document.height / pageSize)

// run debug to see the values
console.log(numberOfPages * pageSize - document.height)

// find out if the extra div fits to the last page space
if (numberOfPages * pageSize - document.height < cutDivHeight) {
numberOfPages++
}

// add the cut area
var watermark = document.createElement('div');
watermark.innerHTML = "CUT ME"
watermark.style.top = (numberOfPages * pageSize) - cutDivHeight
watermark.style.height = cutDivHeight + 'px'
watermark.style.width = '100%'
watermark.style['background-color'] = 'red'
watermark.style.position = 'absolute'
document.body.appendChild(watermark)
</script>

Demo here
https://playground.jsreport.net/studio/workspace/BJEMYu3bb/36

How can I number pages in a PDF with PhantomJS?

Yes, it's possible.

Look at the printheaderfooter.js example on GitHub.

Basically, two callbacks are invoked when printing : one for header and another for footer. It's not in the doc, but the example is easy to understand.

The only problem you may have with this feature is if you want to apply custom styling or include images. Sometimes, it's a bit tricky...

Alter the default header/footer when printing to PDF

This is an update/answer to the question. As of Chromium 64 it is possible using the headerTemplate and footerTemplate parameters to printToPDF

Using chrome remote interface here's example code that should work:

return new Promise(async function (resolve, reject) {
const url = "<MyURL here>";
const [tab] = await Cdp.List()
const client = await Cdp({ host: '127.0.0.1', target: tab });
await Promise.all([
Network.enable(),
Page.enable()
]);

Page.loadEventFired(function () {
setTimeout(function () {
//https://chromedevtools.github.io/devtools-protocol/tot/Page/#method-printToPDF
resolve(Page.printToPDF({
displayHeaderFooter:true,
footerTemplate: "<span class='pageNumber'> of <span class='totalPages'>"
})));
}, 3000);
});
await Page.navigate({ url });
};

Running web-site's JavaScript functions inside phantomjs

Yes, of course it is possible. You just call the site function inside of page.evaluate. Consider the example:

example.com html

<html>
<head>
</head>
<body style="background-color: white">
<p>A page</p>
<script>
function makeRed() {
document.body.style.backgroundColor = "red";
}
</script>
</body>
</html>

PhantomJS script

var page = require('webpage').create();
page.viewportSize = { width: 600, height: 300 };

page.open('http://example.com', function() {

page.evaluate(function(){
makeRed();
});

setTimeout(function(){
page.render('red.png');
phantom.exit();
}, 1000);

});

Result:

Result

Render HTML to an image

There is a lot of options and they all have their pro and cons.

Option 1: Use an API

  • ApiFlash (based on chrome)
  • EvoPDF (has an option for html)
  • Grabzit
  • HTML/CSS to Image API
  • ...

Pros

  • Execute Javascript
  • Near perfect rendering
  • Fast when caching options are correctly used
  • Scale is handled by the APIs
  • Precise timing, viewport, ...
  • Most of the time they offer a free plan

Cons

  • Not free if you plan to use them a lot

Option 2: Use one of the many available libraries

  • dom-to-image
  • wkhtmltoimage (included in the wkhtmltopdf tool)
  • IMGKit (for ruby and based on wkhtmltoimage)
  • imgkit (for python and based on wkhtmltoimage)
  • python-webkit2png
  • ...

Pros

  • Conversion is quite fast most of the time

Cons

  • Bad rendering
  • Does not execute javascript
  • No support for recent web features (FlexBox, Advanced Selectors, Webfonts, Box Sizing, Media Queries, ...)
  • Sometimes not so easy to install
  • Complicated to scale

Option 3: Use PhantomJs and maybe a wrapper library

  • PhantomJs
  • node-webshot (javascript wrapper library for PhantomJs)
  • ...

Pros

  • Execute Javascript
  • Quite fast

Cons

  • Bad rendering
  • No support for recent web features (FlexBox, Advanced Selectors, Webfonts, Box Sizing, Media Queries, ...)
  • Complicated to scale
  • Not so easy to make it work if there is images to be loaded ...

Option 4: Use Chrome Headless and maybe a wrapper library

  • Chrome Headless
  • chrome-devtools-protocol
  • Puppeteer (javascript wrapper library for Chrome headless)
  • ...

Pros

  • Execute Javascript
  • Near perfect rendering

Cons

  • Not so easy to have exactly the wanted result regarding:
    • page load timing
    • viewport dimensions
  • Complicated to scale
  • Quite slow and even slower if the html contains external links

Disclosure: I'm the founder of ApiFlash. I did my best to provide an honest and useful answer.



Related Topics



Leave a reply



Submit