This repository has been archived by the owner. It is now read-only.

PhantomJS 2: PDF rendering too large, page.zoomFactor doesn't work #12685

Closed
thomasbachem opened this Issue Oct 28, 2014 · 197 comments

Comments

Projects
None yet
@thomasbachem

thomasbachem commented Oct 28, 2014

I compiled PhantomJS 2 HEAD on OS X 10.9.5 (MacBook Pro Retina) via brew install phantomjs --HEAD.

When rendering a PDF via rasterize.js, the page contents are rendered much larger than with PhantomJS 1.9, and using the zoom argument doesn't change anything at all.

Experimenting with paperSize, the page contents that do usually fit exactly into 210mm (A4) do now need 303mm, so there's a 144% increase in size.

@elgarfo

This comment has been minimized.

Show comment
Hide comment
@elgarfo

elgarfo Nov 3, 2014

+1 experiencing exactly the same problem.
compiled from source (eddb0db) on debian 7.6

any idea how to work around this?

edit: used css to work around the problem for now
downsides: rendering gets a little ugly and its still a pixel too wide i'd say

html {
    zoom: 0.68; /*workaround for phantomJS2 rendering pages too large*/
}

elgarfo commented Nov 3, 2014

+1 experiencing exactly the same problem.
compiled from source (eddb0db) on debian 7.6

any idea how to work around this?

edit: used css to work around the problem for now
downsides: rendering gets a little ugly and its still a pixel too wide i'd say

html {
    zoom: 0.68; /*workaround for phantomJS2 rendering pages too large*/
}
@mgartner

This comment has been minimized.

Show comment
Hide comment
@mgartner

mgartner Jan 3, 2015

I'm seeing this issue too. It is especially frustrating that I'm trying to use 2.0 to get webfont support, but normal HTML content doesn't fit into PDFs like it did so nicely in 1.9.x.

I'd really appreciate it if anyone could point me in the right direction to what might be causing this in the PhantomJS code base. I'd glady work on submitting a PR to fix this.

mgartner commented Jan 3, 2015

I'm seeing this issue too. It is especially frustrating that I'm trying to use 2.0 to get webfont support, but normal HTML content doesn't fit into PDFs like it did so nicely in 1.9.x.

I'd really appreciate it if anyone could point me in the right direction to what might be causing this in the PhantomJS code base. I'd glady work on submitting a PR to fix this.

@thomasbachem

This comment has been minimized.

Show comment
Hide comment
@thomasbachem

thomasbachem Feb 2, 2015

@ariya I fear this bug will be overlooked quite easily regarding the amount of open tickets. Perhaps you can mark this ticket properly as a regression?

thomasbachem commented Feb 2, 2015

@ariya I fear this bug will be overlooked quite easily regarding the amount of open tickets. Perhaps you can mark this ticket properly as a regression?

@9point6

This comment has been minimized.

Show comment
Hide comment
@9point6

9point6 Feb 19, 2015

+1 still broken in 2.0 brach

9point6 commented Feb 19, 2015

+1 still broken in 2.0 brach

@bompus

This comment has been minimized.

Show comment
Hide comment
@bompus

bompus Feb 20, 2015

Contributor

Still broken for me also. The elgarfo patch works, but it's hacky :)

Contributor

bompus commented Feb 20, 2015

Still broken for me also. The elgarfo patch works, but it's hacky :)

@ariya

This comment has been minimized.

Show comment
Hide comment
@ariya

ariya Feb 22, 2015

Owner

@thomasbachem Good point, labelling it now. Thanks!

Owner

ariya commented Feb 22, 2015

@thomasbachem Good point, labelling it now. Thanks!

@thomasbachem

This comment has been minimized.

Show comment
Hide comment
@thomasbachem

thomasbachem Feb 22, 2015

@ariya Thanks, I just linked two duplicate tickets.

@polarathene mentions some observations in #12936 that may be of help, though I couldn't verify those myself:

I've just done some thorough testing of 2.0.0 PDF conversions and noticed the paperSize 'A4' definition that was 992px wide(120dpi) in 1.9.8 is now 595px wide(72dpi) in 2.0.0. Previously in 1.9.8 your unit values other than % had were scaled down, so that the PDF version required you to zoom to 125% to view units at their original size, also caused the whitespace issue on the right present in your picture due to that scaling. In 2.0.0 units don't appear to be scaled down, they are 100% matching to the browser. paperSize however is now scaling up from it's given px size by 1/3rd, this is matching it to 96dpi/96px per 1 inch which is a CSS inch. Thus 72DPI 595px A4 paperSize is resized to 794px 96DPI A4 when rendered. I've also learnt from OSX(and likely Linux) users that 1.9.8 provided 100% match for their PDF renders but with 2.0.0 they're having to downscale their units from 96dpi to 72dpi, eg 794px wide element needs to be changed to 595px for correct 100% zoom A4 PDF, for comparision on 1.9.8 with windows users that meant changing from 96dpi to 120dpi, 794px wide element needed to be 992px wide. Also worth noting is that between both versions % units would still remain precise, 90% element would always be 90% of the PDF at 100% zoom, for whatever reason they were not scaled like other units.

thomasbachem commented Feb 22, 2015

@ariya Thanks, I just linked two duplicate tickets.

@polarathene mentions some observations in #12936 that may be of help, though I couldn't verify those myself:

I've just done some thorough testing of 2.0.0 PDF conversions and noticed the paperSize 'A4' definition that was 992px wide(120dpi) in 1.9.8 is now 595px wide(72dpi) in 2.0.0. Previously in 1.9.8 your unit values other than % had were scaled down, so that the PDF version required you to zoom to 125% to view units at their original size, also caused the whitespace issue on the right present in your picture due to that scaling. In 2.0.0 units don't appear to be scaled down, they are 100% matching to the browser. paperSize however is now scaling up from it's given px size by 1/3rd, this is matching it to 96dpi/96px per 1 inch which is a CSS inch. Thus 72DPI 595px A4 paperSize is resized to 794px 96DPI A4 when rendered. I've also learnt from OSX(and likely Linux) users that 1.9.8 provided 100% match for their PDF renders but with 2.0.0 they're having to downscale their units from 96dpi to 72dpi, eg 794px wide element needs to be changed to 595px for correct 100% zoom A4 PDF, for comparision on 1.9.8 with windows users that meant changing from 96dpi to 120dpi, 794px wide element needed to be 992px wide. Also worth noting is that between both versions % units would still remain precise, 90% element would always be 90% of the PDF at 100% zoom, for whatever reason they were not scaled like other units.

@mkrn

This comment has been minimized.

Show comment
Hide comment
@mkrn

mkrn Feb 25, 2015

+1 Version 2.0 fixes Function.prototype.bind but breaks paperSize)

mkrn commented Feb 25, 2015

+1 Version 2.0 fixes Function.prototype.bind but breaks paperSize)

@Feendish

This comment has been minimized.

Show comment
Hide comment
@Feendish

Feendish Mar 6, 2015

Also experiencing this issue. I'd love to switch to v2 to fix the page-break-avoid:inside issue but this is a problem now.

elgarfo's tweak seems to work but as noted it causes rendering issues.

Has anyone a better work around? Are there adapted pixel settings for A4&Letter? I'm on Unix.

Feendish commented Mar 6, 2015

Also experiencing this issue. I'd love to switch to v2 to fix the page-break-avoid:inside issue but this is a problem now.

elgarfo's tweak seems to work but as noted it causes rendering issues.

Has anyone a better work around? Are there adapted pixel settings for A4&Letter? I'm on Unix.

@polarathene

This comment has been minimized.

Show comment
Hide comment
@polarathene

polarathene Mar 7, 2015

@Feendish I wrote a lot on the mentioned issue above, but if you read the workaround I gave for mac it should work for unix.

polarathene commented Mar 7, 2015

@Feendish I wrote a lot on the mentioned issue above, but if you read the workaround I gave for mac it should work for unix.

@jimclarkuk

This comment has been minimized.

Show comment
Hide comment
@jimclarkuk

jimclarkuk Mar 7, 2015

@Feendish Following on from the analysis by @polarathene we opted for a transform which seemed to result in fewer rendering issues than using zoom.

.page {
    transform-origin: 0 0; 
    -webkit-transform-origin: 0 0; 
    transform: scale(0.75); 
   -webkit-transform: scale(0.75);
}

jimclarkuk commented Mar 7, 2015

@Feendish Following on from the analysis by @polarathene we opted for a transform which seemed to result in fewer rendering issues than using zoom.

.page {
    transform-origin: 0 0; 
    -webkit-transform-origin: 0 0; 
    transform: scale(0.75); 
   -webkit-transform: scale(0.75);
}
@Feendish

This comment has been minimized.

Show comment
Hide comment
@Feendish

Feendish Mar 9, 2015

@polarathene thanks. You gave a comprehensive break down. I just couldn't follow it exactly.

I tried using the dimensions_width formula you suggested but it was still off.

In the end given your note that the DPI is 72 in Mac/Unix I use a DPI to pixel calculator http://www.hdri.at/dpirechner/dpirechner_en.htm and just hard coded the pageSize to 595x842 for A4 Portrait. 595px = 8.26772 inches x 72dpi where 8.26772=210mm.

@jimclarkuk I tried your solution too with no luck. Page breaking was messed up with overlapping elements.

Feendish commented Mar 9, 2015

@polarathene thanks. You gave a comprehensive break down. I just couldn't follow it exactly.

I tried using the dimensions_width formula you suggested but it was still off.

In the end given your note that the DPI is 72 in Mac/Unix I use a DPI to pixel calculator http://www.hdri.at/dpirechner/dpirechner_en.htm and just hard coded the pageSize to 595x842 for A4 Portrait. 595px = 8.26772 inches x 72dpi where 8.26772=210mm.

@jimclarkuk I tried your solution too with no luck. Page breaking was messed up with overlapping elements.

@thomasbachem

This comment has been minimized.

Show comment
Hide comment
@thomasbachem

thomasbachem Mar 9, 2015

@Feendish What exactly do you mean? Setting

page.paperSize = { width: '595px', height: '842px', margin: '0px' };

Doesn't change or fix anything for me under Mac OS X 10.9. The page is still too small compared to 1.9.8.

thomasbachem commented Mar 9, 2015

@Feendish What exactly do you mean? Setting

page.paperSize = { width: '595px', height: '842px', margin: '0px' };

Doesn't change or fix anything for me under Mac OS X 10.9. The page is still too small compared to 1.9.8.

@Feendish

This comment has been minimized.

Show comment
Hide comment
@Feendish

Feendish Mar 9, 2015

@thomasbachem I'm building the HTML from scratch to test it. Haven't tried running it on established HTML source yet.

I used Bootstrap 3 to make a sample long Invoice.

JS code-> http://pastie.org/10011943
HTML source -> http://s000.tinyupload.com/index.php?file_id=97390552363329839541
Bootstrap override.css -> http://pastie.org/10011951

It now generates a clean A4 sized portait PDF of 44 pages.

I'm on Linux (Centos 6.4).

Feendish commented Mar 9, 2015

@thomasbachem I'm building the HTML from scratch to test it. Haven't tried running it on established HTML source yet.

I used Bootstrap 3 to make a sample long Invoice.

JS code-> http://pastie.org/10011943
HTML source -> http://s000.tinyupload.com/index.php?file_id=97390552363329839541
Bootstrap override.css -> http://pastie.org/10011951

It now generates a clean A4 sized portait PDF of 44 pages.

I'm on Linux (Centos 6.4).

@polarathene

This comment has been minimized.

Show comment
Hide comment
@polarathene

polarathene Mar 9, 2015

@thomasbachem It's been a while but from memory that's the dimensions for A4 at 72dpi(Mac/Linux): http://www.a4papersize.org/a4-paper-size-in-pixels.php

Setting A4 as your papersize would have the same effect. I don't have a mac and haven't tested on linux, what are the px dimensions of an A4 pdf document for you at 100%? On windows they're A4 at 96dpi, I'm guessing you get 72dpi(595x842)? It's been a while but I think you need to upscale your viewport from 72dpi px to 96dpi. On 1.9.8 I used a similar technique that @jimclarkuk provided, though mine scaled up(120dpi to 96dpi). The windows workaround on 1.9.8 wasn't perfect however, if you can get away with it, you should be able to adjust the paperSize to fit your viewports(probably not the same px width/height) and then alter the zoom on the pdf viewer to see the document as intended.

Another alternative could be to run a windows VM or use a web service like Azure to run Phantom on a windows instance.

polarathene commented Mar 9, 2015

@thomasbachem It's been a while but from memory that's the dimensions for A4 at 72dpi(Mac/Linux): http://www.a4papersize.org/a4-paper-size-in-pixels.php

Setting A4 as your papersize would have the same effect. I don't have a mac and haven't tested on linux, what are the px dimensions of an A4 pdf document for you at 100%? On windows they're A4 at 96dpi, I'm guessing you get 72dpi(595x842)? It's been a while but I think you need to upscale your viewport from 72dpi px to 96dpi. On 1.9.8 I used a similar technique that @jimclarkuk provided, though mine scaled up(120dpi to 96dpi). The windows workaround on 1.9.8 wasn't perfect however, if you can get away with it, you should be able to adjust the paperSize to fit your viewports(probably not the same px width/height) and then alter the zoom on the pdf viewer to see the document as intended.

Another alternative could be to run a windows VM or use a web service like Azure to run Phantom on a windows instance.

@Feendish

This comment has been minimized.

Show comment
Hide comment
@Feendish

Feendish Mar 9, 2015

Just to clarify @polarathene on Linux setting "A4" as paperSize doesn't work. The content is sliced off on the right hand side.

I have to explicitly set the pixel width&height to get it to work on Linux with v2.0.0

Feendish commented Mar 9, 2015

Just to clarify @polarathene on Linux setting "A4" as paperSize doesn't work. The content is sliced off on the right hand side.

I have to explicitly set the pixel width&height to get it to work on Linux with v2.0.0

@polarathene

This comment has been minimized.

Show comment
Hide comment
@polarathene

polarathene Mar 9, 2015

@Feendish what viewport size are you using with your papersize?

polarathene commented Mar 9, 2015

@Feendish what viewport size are you using with your papersize?

@Feendish

This comment has been minimized.

Show comment
Hide comment
@Feendish

Feendish Mar 9, 2015

I'm not setting a viewport. I always assumed it was one or the other based on Phantom examples.

Should I be setting one? In 1.9 branch it worked without viewport.

Feendish commented Mar 9, 2015

I'm not setting a viewport. I always assumed it was one or the other based on Phantom examples.

Should I be setting one? In 1.9 branch it worked without viewport.

@polarathene

This comment has been minimized.

Show comment
Hide comment
@polarathene

polarathene Mar 9, 2015

I'd be interested to know if your results are different after setting the viewport. If you get a full A4 pdf filled with the website, also try setting your viewport to half just to confirm that you're getting half once it's rendered to pdf format.

My understanding is that there is a default viewport size, I can't recall what that is though. Plenty of responsive sites will adjust based on the viewport you provide, as well as fixed width sites. Both I imagine can be affected by having a poorly chosen viewport size?

polarathene commented Mar 9, 2015

I'd be interested to know if your results are different after setting the viewport. If you get a full A4 pdf filled with the website, also try setting your viewport to half just to confirm that you're getting half once it's rendered to pdf format.

My understanding is that there is a default viewport size, I can't recall what that is though. Plenty of responsive sites will adjust based on the viewport you provide, as well as fixed width sites. Both I imagine can be affected by having a poorly chosen viewport size?

@thomasbachem

This comment has been minimized.

Show comment
Hide comment
@thomasbachem

thomasbachem Mar 9, 2015

@polarathene @Feendish I tried to play around with setting different viewport sizes, and it changed nothing with PhantomJS 2.

thomasbachem commented Mar 9, 2015

@polarathene @Feendish I tried to play around with setting different viewport sizes, and it changed nothing with PhantomJS 2.

@polarathene

This comment has been minimized.

Show comment
Hide comment
@polarathene

polarathene Mar 9, 2015

@thomasbachem it would depend on the site you're rendering. If you use it on a responsive site that has a mobile layout at a small viewport, changing your viewport to a small size should trigger it just like resizing your chrome window would. I think rendering will still scroll the viewport if needed to fill in the papersize? I'll be using phantom again soon, perhaps can set up an example project for the issue with workaround :)

Honestly though, the issue is with phantom, with 2.0.0 osx/linux got the problems windows had with 1.9.8, while on 2.0.0 windows works like osx/linux used to. Whomever worked on that part of phantom should be able to provide a fix, even if it's a different build which breaks windows, I'd imagine that'd be the quick fix.

polarathene commented Mar 9, 2015

@thomasbachem it would depend on the site you're rendering. If you use it on a responsive site that has a mobile layout at a small viewport, changing your viewport to a small size should trigger it just like resizing your chrome window would. I think rendering will still scroll the viewport if needed to fill in the papersize? I'll be using phantom again soon, perhaps can set up an example project for the issue with workaround :)

Honestly though, the issue is with phantom, with 2.0.0 osx/linux got the problems windows had with 1.9.8, while on 2.0.0 windows works like osx/linux used to. Whomever worked on that part of phantom should be able to provide a fix, even if it's a different build which breaks windows, I'd imagine that'd be the quick fix.

@polarathene

This comment has been minimized.

Show comment
Hide comment
@polarathene

polarathene Mar 9, 2015

If phantomjs itself is the cause, I can only see tinkering with the values here: https://github.com/ariya/phantomjs/blob/2.0/src/webpage.cpp#L1061 that seem like they'd be relevant, but it might actually be handled by QT which was updated with 2.0.0. I see plenty of references for 96dpi, perhaps dpi handling has changed between the QT versions used in 1.9.8 and 2.0.0....which'd mean phantomjs won't ever fix this issue until QT does? I have no QT experience, if someone from the phantomjs team could chime in, is it a QT bug or has phantomjs done something differently with pdf rendering via QT since?

polarathene commented Mar 9, 2015

If phantomjs itself is the cause, I can only see tinkering with the values here: https://github.com/ariya/phantomjs/blob/2.0/src/webpage.cpp#L1061 that seem like they'd be relevant, but it might actually be handled by QT which was updated with 2.0.0. I see plenty of references for 96dpi, perhaps dpi handling has changed between the QT versions used in 1.9.8 and 2.0.0....which'd mean phantomjs won't ever fix this issue until QT does? I have no QT experience, if someone from the phantomjs team could chime in, is it a QT bug or has phantomjs done something differently with pdf rendering via QT since?

@Feendish

This comment has been minimized.

Show comment
Hide comment
@Feendish

Feendish Mar 9, 2015

Setting a viewport of

page.viewportSize = {
width: 595,
height: 400
};

has no effect on the rendered page.

Feendish commented Mar 9, 2015

Setting a viewport of

page.viewportSize = {
width: 595,
height: 400
};

has no effect on the rendered page.

@polarathene

This comment has been minimized.

Show comment
Hide comment
@polarathene

polarathene Mar 10, 2015

@Feendish try 100x100,if you're still getting no difference then tweaking viewport won't help much. Again I did say it completely depends on the website design itself. For the website I was working with, js scripts were generating highchart graphs based on viewport width, they rendered incorrectly without setting the viewport properly.

polarathene commented Mar 10, 2015

@Feendish try 100x100,if you're still getting no difference then tweaking viewport won't help much. Again I did say it completely depends on the website design itself. For the website I was working with, js scripts were generating highchart graphs based on viewport width, they rendered incorrectly without setting the viewport properly.

@jrf0110

This comment has been minimized.

Show comment
Hide comment
@jrf0110

jrf0110 Mar 10, 2015

+1 this sucks really bad :(

jrf0110 commented Mar 10, 2015

+1 this sucks really bad :(

@davidwindell

This comment has been minimized.

Show comment
Hide comment
@davidwindell

davidwindell May 10, 2017

Has anyone tried the new Headless Chrome for PDF generation? Looks really promising https://developers.google.com/web/updates/2017/04/headless-chrome

davidwindell commented May 10, 2017

Has anyone tried the new Headless Chrome for PDF generation? Looks really promising https://developers.google.com/web/updates/2017/04/headless-chrome

@lsapan

This comment has been minimized.

Show comment
Hide comment
@lsapan

lsapan May 10, 2017

@davidwindell judging from https://bugs.chromium.org/p/chromium/issues/detail?id=603559 it doesn't look like it supports headers / footers quite yet, but it definitely looks promising!

lsapan commented May 10, 2017

@davidwindell judging from https://bugs.chromium.org/p/chromium/issues/detail?id=603559 it doesn't look like it supports headers / footers quite yet, but it definitely looks promising!

@melo

This comment has been minimized.

Show comment
Hide comment
@melo

melo May 10, 2017

@davidwindell judging from https://bugs.chromium.org/p/chromium/issues/detail?id=603559 it doesn't look like it supports headers / footers quite yet, but it definitely looks promising!

Actually I don't know about that... According to http://caniuse.com/#feat=css-paged-media, Chrome supports CSS3 Paged Media module, so setting headers and footers via CSS should work. I'm going to try this over the weekend...

melo commented May 10, 2017

@davidwindell judging from https://bugs.chromium.org/p/chromium/issues/detail?id=603559 it doesn't look like it supports headers / footers quite yet, but it definitely looks promising!

Actually I don't know about that... According to http://caniuse.com/#feat=css-paged-media, Chrome supports CSS3 Paged Media module, so setting headers and footers via CSS should work. I'm going to try this over the weekend...

@lsapan

This comment has been minimized.

Show comment
Hide comment
@lsapan

lsapan May 10, 2017

@melo oh interesting! Yes definitely let us know how that goes!

lsapan commented May 10, 2017

@melo oh interesting! Yes definitely let us know how that goes!

@davidwindell

This comment has been minimized.

Show comment
Hide comment
@davidwindell

davidwindell May 10, 2017

Awesome :bated breath:

davidwindell commented May 10, 2017

Awesome :bated breath:

@Fabbok1x

This comment has been minimized.

Show comment
Hide comment
@Fabbok1x

Fabbok1x May 14, 2017

Wow, phantom js dev seems dead, or just noone caring for the issues coming up. How can a zoomFactor issue persist for that long? It is one crucial function. Switching to electron pdf now. Works like a charm.

Fabbok1x commented May 14, 2017

Wow, phantom js dev seems dead, or just noone caring for the issues coming up. How can a zoomFactor issue persist for that long? It is one crucial function. Switching to electron pdf now. Works like a charm.

@miwim

This comment has been minimized.

Show comment
Hide comment
@miwim

miwim May 16, 2017

Hi there,
I work in a project where MacOs and Windows are used with PhantomJs 2.1.1, and we discovered that the measures in cm are working in Windows but not in Mac and the measures in pixels are working in Mac but not in Windows. Therefore we set a different solution depending on the OS. Unfortunatelly I have not tested it in Linux, but I hope this could help.

miwim commented May 16, 2017

Hi there,
I work in a project where MacOs and Windows are used with PhantomJs 2.1.1, and we discovered that the measures in cm are working in Windows but not in Mac and the measures in pixels are working in Mac but not in Windows. Therefore we set a different solution depending on the OS. Unfortunatelly I have not tested it in Linux, but I hope this could help.

@SWGFL

This comment has been minimized.

Show comment
Hide comment
@SWGFL

SWGFL May 23, 2017

From my fiddlings, the solution I have come up with is to set the paper size to what it should be (A4 or whatever), then simply set the width of the document on the body tag in pixels, that seems to zoom it to fit the page to the paper size you have set.

I have rewritten rasterize.js to do this for you with a DPI setting. On the commandline use:

phantomjs rasterize.js https://google.com/ test.pdf --dpi 200[ --optionname value,]

Use the option names defined in args. Also you can use - for either input or output to read from STDIN or STDOUT:

"use strict";
var page = require('webpage').create(),
	system = require('system'),
	render = function (phantom, page, output, config) {
		
		// set DPI
		if (config.dpi !== null) {
			page.evaluate(function (dpi) {
				var div = document.createElement("div"),
					body = document.getElementsByTagName("body")[0],
					dpi;
				div.style.width = "1in";
				body.appendChild(div);
				if (dpi > div.offsetWidth) {
					body.style.width = ((body.offsetWidth / div.offsetWidth) * dpi) + "px";
				}
			}, config.dpi);
		}
		
		// timeout for javascript
		window.setTimeout(function () {
			page.render(output, {format: config.format, quality: config.quality});
			phantom.exit();
		}, config.delay);
	},
	config = {
		width: 800, // image width
		height: 600, // image height
		format: "pdf", // pdf png, gif, jpg
		paper: "A4",
		orientation: "portrait",
		margin: "2.54cm",
		delay: 300, // javascript delay
		href: "/", // the href alias of the page, allows it to retrieve assets relatively
		dpi: 200,
		quality: 80 // jpeg quality
	},
	output, key;

if (system.args.length > 2) {
	
	// process arguments
	system.args.slice(3).forEach(function (arg) {
		if (key) {
			config[key] = arg;
			key = null;
		} else if (arg.indexOf("--") === 0) {
			key = arg.substr(2);
		}
	});
	
	// set paper size
	if (config.format === "pdf") {
		page.paperSize = {
			format: config.paper,
			orientation: config.orientation,
			margin: config.margin
		};
	} else {
	
		// set viewport size
		page.viewportSize = {
			width: config.width,
			height: config.height
		};
		page.clipRect = { top: 0, left: 0, width: config.width, height: config.height };
	}
	
	output = system.args[2] === "-" ? "/dev/stdout" : system.args[2];
	if (phantom.version.major > 1) {
		system.stdout.setEncoding("ISO-8859-1");
	}
	
	// output page from stdin
	if (system.args[1] == "-") {
		if (phantom.version.major > 1) {
			system.stdin.setEncoding("UTF-8");
		}
		page.setContent(system.stdin.read(), config.href);
		render(phantom, page, output, config);
	
	// output page from url
	} else {
		page.open(system.args[1], function (status) {
			if (status !== 'success') {
				console.log('Unable to load the address!');
				phantom.exit(1);
			} else {
				render(phantom, page, output, config);
			}
		});
	}
}

I also had an issue where 2.1.1 rasterized everything on Linux, so I am now using 1.9.8 on there, which this script compensates for, so should work with old and new versions.

Note that this fix relies on your content being responsive (not in a fixed width container), it basically sets the width of the body tag, and the output zooms it to fit on the page.

SWGFL commented May 23, 2017

From my fiddlings, the solution I have come up with is to set the paper size to what it should be (A4 or whatever), then simply set the width of the document on the body tag in pixels, that seems to zoom it to fit the page to the paper size you have set.

I have rewritten rasterize.js to do this for you with a DPI setting. On the commandline use:

phantomjs rasterize.js https://google.com/ test.pdf --dpi 200[ --optionname value,]

Use the option names defined in args. Also you can use - for either input or output to read from STDIN or STDOUT:

"use strict";
var page = require('webpage').create(),
	system = require('system'),
	render = function (phantom, page, output, config) {
		
		// set DPI
		if (config.dpi !== null) {
			page.evaluate(function (dpi) {
				var div = document.createElement("div"),
					body = document.getElementsByTagName("body")[0],
					dpi;
				div.style.width = "1in";
				body.appendChild(div);
				if (dpi > div.offsetWidth) {
					body.style.width = ((body.offsetWidth / div.offsetWidth) * dpi) + "px";
				}
			}, config.dpi);
		}
		
		// timeout for javascript
		window.setTimeout(function () {
			page.render(output, {format: config.format, quality: config.quality});
			phantom.exit();
		}, config.delay);
	},
	config = {
		width: 800, // image width
		height: 600, // image height
		format: "pdf", // pdf png, gif, jpg
		paper: "A4",
		orientation: "portrait",
		margin: "2.54cm",
		delay: 300, // javascript delay
		href: "/", // the href alias of the page, allows it to retrieve assets relatively
		dpi: 200,
		quality: 80 // jpeg quality
	},
	output, key;

if (system.args.length > 2) {
	
	// process arguments
	system.args.slice(3).forEach(function (arg) {
		if (key) {
			config[key] = arg;
			key = null;
		} else if (arg.indexOf("--") === 0) {
			key = arg.substr(2);
		}
	});
	
	// set paper size
	if (config.format === "pdf") {
		page.paperSize = {
			format: config.paper,
			orientation: config.orientation,
			margin: config.margin
		};
	} else {
	
		// set viewport size
		page.viewportSize = {
			width: config.width,
			height: config.height
		};
		page.clipRect = { top: 0, left: 0, width: config.width, height: config.height };
	}
	
	output = system.args[2] === "-" ? "/dev/stdout" : system.args[2];
	if (phantom.version.major > 1) {
		system.stdout.setEncoding("ISO-8859-1");
	}
	
	// output page from stdin
	if (system.args[1] == "-") {
		if (phantom.version.major > 1) {
			system.stdin.setEncoding("UTF-8");
		}
		page.setContent(system.stdin.read(), config.href);
		render(phantom, page, output, config);
	
	// output page from url
	} else {
		page.open(system.args[1], function (status) {
			if (status !== 'success') {
				console.log('Unable to load the address!');
				phantom.exit(1);
			} else {
				render(phantom, page, output, config);
			}
		});
	}
}

I also had an issue where 2.1.1 rasterized everything on Linux, so I am now using 1.9.8 on there, which this script compensates for, so should work with old and new versions.

Note that this fix relies on your content being responsive (not in a fixed width container), it basically sets the width of the body tag, and the output zooms it to fit on the page.

@geoffcallender

This comment has been minimized.

Show comment
Hide comment
@geoffcallender

geoffcallender Jun 5, 2017

This really has to be re-opened. The workarounds above are hacks, should not be necessary, and so far I've found they can't handle my more complex reports, whereas 1.9 was stable (until #14558 which has been closed but not fixed). I'm marooned.

geoffcallender commented Jun 5, 2017

This really has to be re-opened. The workarounds above are hacks, should not be necessary, and so far I've found they can't handle my more complex reports, whereas 1.9 was stable (until #14558 which has been closed but not fixed). I'm marooned.

@apokryfos

This comment has been minimized.

Show comment
Hide comment
@apokryfos

apokryfos Jun 5, 2017

@geoffcallender The issue is closed because there's a fix in master that will be part of the next stable release of PhantomJS. However there hasn't been any activity in this project in quite a while now and I suspect it's dead so chances are there won't be a stable release.

apokryfos commented Jun 5, 2017

@geoffcallender The issue is closed because there's a fix in master that will be part of the next stable release of PhantomJS. However there hasn't been any activity in this project in quite a while now and I suspect it's dead so chances are there won't be a stable release.

@replete

This comment has been minimized.

Show comment
Hide comment
@replete

replete Jun 5, 2017

@geoffcallender You could try electron-pdf. I'm holding out for headless chrome.

For anyone else stuck, this will help your poor souls:

/*------------------------------------------------------------------------------
    PDF
--------------------------------------------------------------------------------
    This file contains styles for the main layout used to generate PDFs.
    Basic styles should be kept separate from renderer-specific fixes.

    PhantomJS-specific styles are at the bottom of this file
------------------------------------------------------------------------------*/

html.pdf-renderer {
  /*----------------------------------------------------------------------------
    Global styles
  ----------------------------------------------------------------------------*/
  body {
    background:#fff;
  }

  .main-container {
    width: 800px;
  }

  /*----------------------------------------------------------------------------
    Global fixes for PDF rendering
  ----------------------------------------------------------------------------*/

  // Remove box shadow:
  *,
  *::after,
  *::before {
    box-shadow: none !important
  }


  // print helpers: ------------------------------------------------------------
  // Add to elements to force page-break behaviour.
  .page-break-before {
    page-break-before: always !important;
    float: none;
  }

  // This is usually the solution you need. Add this to anything, and the page should not break inside it. If this didn't work, you have a child element with a 'float:' set. Remove it.
  .page-break-avoid {
    page-break-inside: avoid !important;
    float: none;
  }

  .page-break-after {
    page-break-after: always !important;
    float: none;
  }


  // PhantomJS-specific fixes: -------------------------------------------------
  &.is-phantomjs-pdf {
    /** 
     * This CSS class is added by phantomJS. Set 'injectJs' option to a new
     * javascript file that contains this one line:
          document.documentElement.classList.add('is-phantomjs-pdf');
     */

    // Fix extra blank pages
    height:0;

    body {

      // Fixes scale issue (renders at 96dpi instead of 72dpi)
      // https://github.com/ariya/phantomjs/issues/12685
      zoom: 0.65;

      // Fixes text color:
      color: inherit !important;
    }

    // Fixes ugly unstyled checkbox inputs:
    input[type=checkbox]:not(.checkbox) {
      -webkit-appearance:none;
      border: 1px solid #ccc;
      border-radius:2px;
      height:1.16em;
      width:1.16em;
      margin:0 0 0 0;
      display:inline-block;
      vertical-align:text-bottom;
      position:relative;

      &:checked::after {
        content:'';
        display:inline-block;
        width:1em;
        height:1em;
        -webkit-transform: translate(0, -1px);
      }
    }

    // Examples of some PhantomJS-specific fixes we found ourselves needing:

    // Chartist SVG fixes:
    .ct-chart svg {
      .ct-grid.ct-vertical {
        stroke-dasharray: none; // should be '0', but we must use 'none' for PhantomJS
        stroke: #eeeeee !important;
      }
    }


    .widget {
      // Border fixes:
     .widget-heading {
        border-bottom: 1px solid  #ddd;
      }

      // Button fixes:
      .btn {
        padding-bottom: 3px;
      }
    }
  }
}

replete commented Jun 5, 2017

@geoffcallender You could try electron-pdf. I'm holding out for headless chrome.

For anyone else stuck, this will help your poor souls:

/*------------------------------------------------------------------------------
    PDF
--------------------------------------------------------------------------------
    This file contains styles for the main layout used to generate PDFs.
    Basic styles should be kept separate from renderer-specific fixes.

    PhantomJS-specific styles are at the bottom of this file
------------------------------------------------------------------------------*/

html.pdf-renderer {
  /*----------------------------------------------------------------------------
    Global styles
  ----------------------------------------------------------------------------*/
  body {
    background:#fff;
  }

  .main-container {
    width: 800px;
  }

  /*----------------------------------------------------------------------------
    Global fixes for PDF rendering
  ----------------------------------------------------------------------------*/

  // Remove box shadow:
  *,
  *::after,
  *::before {
    box-shadow: none !important
  }


  // print helpers: ------------------------------------------------------------
  // Add to elements to force page-break behaviour.
  .page-break-before {
    page-break-before: always !important;
    float: none;
  }

  // This is usually the solution you need. Add this to anything, and the page should not break inside it. If this didn't work, you have a child element with a 'float:' set. Remove it.
  .page-break-avoid {
    page-break-inside: avoid !important;
    float: none;
  }

  .page-break-after {
    page-break-after: always !important;
    float: none;
  }


  // PhantomJS-specific fixes: -------------------------------------------------
  &.is-phantomjs-pdf {
    /** 
     * This CSS class is added by phantomJS. Set 'injectJs' option to a new
     * javascript file that contains this one line:
          document.documentElement.classList.add('is-phantomjs-pdf');
     */

    // Fix extra blank pages
    height:0;

    body {

      // Fixes scale issue (renders at 96dpi instead of 72dpi)
      // https://github.com/ariya/phantomjs/issues/12685
      zoom: 0.65;

      // Fixes text color:
      color: inherit !important;
    }

    // Fixes ugly unstyled checkbox inputs:
    input[type=checkbox]:not(.checkbox) {
      -webkit-appearance:none;
      border: 1px solid #ccc;
      border-radius:2px;
      height:1.16em;
      width:1.16em;
      margin:0 0 0 0;
      display:inline-block;
      vertical-align:text-bottom;
      position:relative;

      &:checked::after {
        content:'';
        display:inline-block;
        width:1em;
        height:1em;
        -webkit-transform: translate(0, -1px);
      }
    }

    // Examples of some PhantomJS-specific fixes we found ourselves needing:

    // Chartist SVG fixes:
    .ct-chart svg {
      .ct-grid.ct-vertical {
        stroke-dasharray: none; // should be '0', but we must use 'none' for PhantomJS
        stroke: #eeeeee !important;
      }
    }


    .widget {
      // Border fixes:
     .widget-heading {
        border-bottom: 1px solid  #ddd;
      }

      // Button fixes:
      .btn {
        padding-bottom: 3px;
      }
    }
  }
}
@duskan

This comment has been minimized.

Show comment
Hide comment
@duskan

duskan Jul 17, 2017

In my case,
First, I used print.css

zoom:0.75

but page layout is broken

so, I use this page_size (a4 / 0.75)

width : 210 * 4/3 // avoid pantomjs bug
height : 297 * 4/3 // avoid pantomjs bug

it realy work good!
but i, as possible, need to change a4 size without manual
(because of customer T.T)

duskan commented Jul 17, 2017

In my case,
First, I used print.css

zoom:0.75

but page layout is broken

so, I use this page_size (a4 / 0.75)

width : 210 * 4/3 // avoid pantomjs bug
height : 297 * 4/3 // avoid pantomjs bug

it realy work good!
but i, as possible, need to change a4 size without manual
(because of customer T.T)

@jazo10

This comment has been minimized.

Show comment
Hide comment
@jazo10

jazo10 Aug 31, 2017

Just adding to this, and hopefully saving a few headaches for people. the devices DPI is taken into effect in this.

We run phantomJS on an AWS machine that we remote into, one of our dev's (who has a 300+dpi laptop) connected and restarted the server, which updated the DPI of the server itself, causing all PDF outputs to become really small. we had to connect with a standard machine (non retina) to reset it back to normal.

jazo10 commented Aug 31, 2017

Just adding to this, and hopefully saving a few headaches for people. the devices DPI is taken into effect in this.

We run phantomJS on an AWS machine that we remote into, one of our dev's (who has a 300+dpi laptop) connected and restarted the server, which updated the DPI of the server itself, causing all PDF outputs to become really small. we had to connect with a standard machine (non retina) to reset it back to normal.

@alvara

This comment has been minimized.

Show comment
Hide comment
@alvara

alvara Sep 11, 2017

Thank you all for posting your solutions. I combined two of the above answers and was eventually able to get it to display properly using the following in my css:

html {
    height: 0;
    transform-origin: 0 0;
    -webkit-transform-origin: 0 0;
    transform: scale(0.53);
    -webkit-transform: scale(0.53);
}

For me, the key was setting the height to 0, without was causing my single page pdf to take up two pages.

alvara commented Sep 11, 2017

Thank you all for posting your solutions. I combined two of the above answers and was eventually able to get it to display properly using the following in my css:

html {
    height: 0;
    transform-origin: 0 0;
    -webkit-transform-origin: 0 0;
    transform: scale(0.53);
    -webkit-transform: scale(0.53);
}

For me, the key was setting the height to 0, without was causing my single page pdf to take up two pages.

@PierBover

This comment has been minimized.

Show comment
Hide comment
@PierBover

PierBover Jan 29, 2018

Hey @jazo10 so how can you set the AWS machine DPI setting?

PierBover commented Jan 29, 2018

Hey @jazo10 so how can you set the AWS machine DPI setting?

@jazo10

This comment has been minimized.

Show comment
Hide comment
@jazo10

jazo10 Jan 29, 2018

jazo10 commented Jan 29, 2018

@SWGFL

This comment has been minimized.

Show comment
Hide comment
@SWGFL

SWGFL Feb 2, 2018

Headless Chrome through Puppeteer is much better than this as it uses the latest Chromium rather than an ancient version of QT. Produces much better results.

SWGFL commented Feb 2, 2018

Headless Chrome through Puppeteer is much better than this as it uses the latest Chromium rather than an ancient version of QT. Produces much better results.

@kromit

This comment has been minimized.

Show comment
Hide comment
@kromit

kromit Feb 2, 2018

@SWGFL did you get header and footer configurable?

kromit commented Feb 2, 2018

@SWGFL did you get header and footer configurable?

@apokryfos

This comment has been minimized.

Show comment
Hide comment
@apokryfos

apokryfos Feb 2, 2018

@kromit since Chrome/Chromium headless supports CSS3 now we can probably configure the header and footer via CSS instead of via custom PDF libraries. Check https://stackoverflow.com/a/46368450/487813 it is useful. It's not native but then again CSS3 page styles are there just for this reason (I think).

apokryfos commented Feb 2, 2018

@kromit since Chrome/Chromium headless supports CSS3 now we can probably configure the header and footer via CSS instead of via custom PDF libraries. Check https://stackoverflow.com/a/46368450/487813 it is useful. It's not native but then again CSS3 page styles are there just for this reason (I think).

@SWGFL

This comment has been minimized.

Show comment
Hide comment
@SWGFL

SWGFL Feb 2, 2018

Version 1.0.0. of Puppeteer has just been released, and this includes now support for headers and footers. See https://github.com/GoogleChrome/puppeteer/blob/master/docs/api.md#pagepdfoptions

SWGFL commented Feb 2, 2018

Version 1.0.0. of Puppeteer has just been released, and this includes now support for headers and footers. See https://github.com/GoogleChrome/puppeteer/blob/master/docs/api.md#pagepdfoptions

@PierBover

This comment has been minimized.

Show comment
Hide comment
@PierBover

PierBover Feb 2, 2018

@SWGFL thanks for the suggestion!

It seems rendering an HTML string is done using page.setContent(htmlString). I'll be doing some tests and report back.

PierBover commented Feb 2, 2018

@SWGFL thanks for the suggestion!

It seems rendering an HTML string is done using page.setContent(htmlString). I'll be doing some tests and report back.

@SWGFL

This comment has been minimized.

Show comment
Hide comment
@SWGFL

SWGFL Feb 5, 2018

FYI: You might have more milage in using:

await page.goto("data:text/html,"+html, {waitUntil: "networkidle0"});

rather than:

const loaded = page.waitForNavigation({waitUntil: "load"});
await page.setContent(html);
await loaded;

SWGFL commented Feb 5, 2018

FYI: You might have more milage in using:

await page.goto("data:text/html,"+html, {waitUntil: "networkidle0"});

rather than:

const loaded = page.waitForNavigation({waitUntil: "load"});
await page.setContent(html);
await loaded;
@PierBover

This comment has been minimized.

Show comment
Hide comment
@PierBover

PierBover Feb 5, 2018

Thanks @SWGFL . We're on national holidays here, but I'll report back once I've made some tests.

PierBover commented Feb 5, 2018

Thanks @SWGFL . We're on national holidays here, but I'll report back once I've made some tests.

@fergardi

This comment has been minimized.

Show comment
Hide comment
@fergardi

fergardi Apr 19, 2018

I'm running with this issue too. PhantomJS 2.1.1 here. In Windows is working flawlessly. In Linux, it appears zoomed and with text centering and other minor CSS issues (strange btw, because the WebKit version should be the same in both binaries).

Any advances?

fergardi commented Apr 19, 2018

I'm running with this issue too. PhantomJS 2.1.1 here. In Windows is working flawlessly. In Linux, it appears zoomed and with text centering and other minor CSS issues (strange btw, because the WebKit version should be the same in both binaries).

Any advances?

@theaspect

This comment has been minimized.

Show comment
Hide comment
@theaspect

theaspect Apr 20, 2018

@fergardi phantomjs was discontinued, try headless chrome

theaspect commented Apr 20, 2018

@fergardi phantomjs was discontinued, try headless chrome

Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.