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:
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
Bootstrap Container with Position:Absolute Loses Layout Inside
Webpack - Require('Node_Modules/Leaflet/Leaflet.CSS')
Chrome Devtools Converts All Hex Colors to Rgb
Creating 3 Perfectly Equal Columns That Take Up 100% on The Browser Window Width
Full Viewport Height Scaling Div Just CSS No Js... Possible
Body { Font-Size: 100.01%; } Vs Body { Font-Size: 100%; }
Gooey CSS Effects with Contrast Parent
How to Order My CSS Columns Horizontally Instead of Vertically
Overriding Styles in Semantic UI React
Gulp-Sass Fails to Compile SCSS File
Does The Shadow Dom Replace: :Before and: :After
How to Install Bootstrap in Laravel 8