36

I am using headless Chrome to export html documents to pdf

google-chrome --headless --disable-gpu --print-to-pdf='output_path' 'url'

How can I change paper size in generated pdf?

I have control on both Chrome parameters and html.

I always get US Letter.

There are no documented command-line options for this.

I've tried setting CSS: @page {size: A4;}. No effect in headless mode, but works when I hit Ctrl+P in normal mode (option to choose paper size for Save as pdf disappears, exported pdf has A4 page size).

I've tried this on Chrome versions 59, 60 and 61 on Ubuntu 16.04.

Crazy Yoghurt
  • 2,365
  • 2
  • 26
  • 37
  • I have checked Chromium source code. As far as I understand there are no undocumented command-line options also. So probably some CSS (or other) hacks are only way at the moment. – Crazy Yoghurt Jul 07 '17 at 13:32

6 Answers6

12

The page size could be set in inches/mm. I haven't tested with a size in pixels. Here is a set of CSS rules which did the trick for me:

@page {
  margin: 0;
  padding: 0;
  size: 5in 6.5in;
}

My exact case is rendering svg-to-pdf, not html; For svg, you may also need to add width and height attributes to <svg> tag:

<svg width="5in" height="6.5in" ...>

That's all! Output PDF won't have margins, and will preserve the desired size - 5"x6.5" in my case.

Avael Kross
  • 428
  • 1
  • 5
  • 17
  • I just put the style tag into head of my HTML (without CData), set size in mm, and it works. – omegastripes Jul 05 '19 at 18:17
  • Yes, for HTML it's true - just put a style tag into . My case is for svg rendering, so I need CDATA. I should state it more clearly in my answer. – Avael Kross Jul 10 '19 at 15:34
7

You can run headless chrome from Node environment.

Then you would be able to pass additional parameters to printToPdf function including pageWidth and pageHeight.

Dimitry Leonov
  • 319
  • 2
  • 4
  • 3
    Thank you Dimitry. I am aware of this option, but it causes additional problems: 1. Chrome instance accepts only one debugging connection. I'd have to spawn multiple instances on multiple ports for each instance of my service. Not so cool. 2. Synchronisation with events (onLoad etc.) is additional layer of complexity. Basically I would need to write a wrapping program running one Chrome instance, which would abstract page-loading, printing etc. Also not so cool. That's why I wanted to use single Chrome instance per single job, automatically closing after it's done, no remote debugging. Simpler – Crazy Yoghurt Jul 11 '17 at 11:00
6

Page size apparently can now be "almost" accurately controlled without using the debug interface.

The following is a method of creating a PDF with nearly the exact dimensions of its contents using headless chrome.

<head>
    <style>
      html, body {
        width:  fit-content;
        height: fit-content;
        margin:  0px;
        padding: 0px;
      }
     </style>

     <style id=page_style>
      @page { size: 100px 100px ; margin : 0px }
     </style>

</head>

This prepares for making the pdf to fit the page, but will not be right, since the page size has been set to an arbitrary value of 100x100.

After the document has been rendered, the following is used to set the page size correctly at the bottom of the page:

<script>
window.onload(fixpage);

function fixpage() {

     renderBlock = document.getElementsByTagName("html")[0];
     renderBlockInfo = window.getComputedStyle(renderBlock)

     // fix chrome page bug
     fixHeight = parseInt(renderBlockInfo.height) + 1 + "px"   

     pageCss = `@page { size: \${renderBlockInfo.width} \${fixHeight} ; margin:0;}`
     document.getElementById("page_style").innerHTML = pageCss
}
</script>

This approach eliminates the header/footer and deals with a numerical issue with pixel conversion to pdf.

One more thing

Chrome currently has a bug with the calculation of absolute height of a div when you use the CSS

line-height: normal; 

This will make the page calculation too short and lead to an extra pdf page being generated. You can fix this using:

line-height: unset; 

Throughout your CSS. You won't get an accurate height without it!

sdw
  • 623
  • 9
  • 10
3

There was a patch created sometime ago that enables page sizing configuration https://codereview.chromium.org/2829973002/

It is now closed and available in the unstable version of chrome, so you can use it as you suggested @page { size: A4 }.

I tested, it works on the unstable build I have installed (Google Chrome 61.0.3141.7 dev). I am not sure, though, how to check when it will be available in the stable build ...

atomrc
  • 2,543
  • 1
  • 16
  • 20
  • 1
    That doesn't work. I've installed Chrome 61.0.3153.4 and set `@page { size: A4 }` in css. That makes no difference. Have you been able to obtain different sizes of paper this way? Anyhow, change that you've linked is not really connected with this problem. Look here: https://codereview.chromium.org/2829973002/patch/1/10010, params are brutally hard-coded when using shell `--print-to-pdf` option. It's more connected with @Dimitry Leonov's answer, as it gives you controll while using debugging api. – Crazy Yoghurt Jul 14 '17 at 09:36
  • Oh weird, it seems to work on my machine. Here are 2 different generation I just made with headless chrome. The first is with `@page { size: A4; }` and the second with `A3`. The output is not the same as you can see http://imgur.com/QaQQQ8R http://imgur.com/CYpMopU. That said, the red part is supposed to have a size of `210mm / 297mm` so I am not sure why there is a extra margin on the side ... – atomrc Jul 14 '17 at 11:03
  • About the patch link I sent you, someone from the Chromium team sent me this link when I asked about page size configuration for headless chrome https://bugs.chromium.org/p/chromium/issues/detail?id=603559#c30. So I though this would be relevant to you too, but unfortunately I have to admit I am not really able to understand what is going on in this patch :/ – atomrc Jul 14 '17 at 11:08
  • You're right, output may not be the same. The case is: there are 3 different sizes: `paper`, `page` and `body`. You have changed `page` size, leaving `paper` and `body` untouched. Chrome squeezes larger `page` to the same `paper` size, so your `body` gets visually smaller. It looks similar to printing on larger paper size, but you may check pdf properties and you'll see that it stayed the same (at least in my case) – Crazy Yoghurt Jul 14 '17 at 11:33
  • 1
    I am not really aware of those three different sizes. You seem to know a lot more than I do about PDF, so I guess I cannot help you, sorry :/ BTW How are the results I showed you different from what you expect? Does that mean I need to look, somehow, in the metadata of the generated PDF? – atomrc Jul 14 '17 at 17:41
  • To check pdf paper size, you should open it in Acrobat Reader and right click on any page. In description tab there's page size in inches (Acrobat Reader DC 2017.009 on macOS). Or use any other pdf viewer you have :) – Crazy Yoghurt Jul 16 '17 at 08:19
3

Note: after going through the comments in atomrc's answer, I thought about adding this as an answer to be more clear.

You can't change the page size right now unless using the devtools protocol.

This is a bug in headless Chrome. The @page size CSS rule is not understood correctly in headless mode, as this user describes it well on the chromium bug tracker:

Desktop Chrome does support @page at-rules for size and margins, and will set the sheet dimensions according to the size property.

It appears that Headless Chrome does parse @page as well to some extent, but behaves differently than the desktop version: If you specify @page {size}, headless seems to change the dimensions of the page box (essentially, the print area), and not the sheet, which always remains US Letter sized. However, it does rotate the sheet if you specify {size: landscape}.

Community
  • 1
  • 1
Leimi
  • 257
  • 2
  • 6
0

I managed to get it correct using org.openqa.selenium.print.PageSize

PrintsPage printer = (PrintsPage) browser;
PrintOptions printOptions = new PrintOptions();
printOptions.setPageSize(new PageSize(21,29.7)); //A4
printOptions.setOrientation(PrintOptions.Orientation.PORTRAIT);
printOptions.setPageRanges("All");
Pdf pdf =  printer.print(printOptions);
String content = pdf.getContent();
FileOutputStream fos = new FileOutputStream("C:\\workspace\\tmp\\test.pdf");
byte[] decoder = Base64.getDecoder().decode(content);
fos.write(decoder);
fos.close();
sarath
  • 103
  • 3
  • 8