如何在无界面Chrome中更改纸张大小--打印为PDF

36

我正在使用无界面 Chrome 将 HTML 文档导出为 PDF

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

如何在生成的PDF中更改纸张大小?

我可以控制Chrome参数和HTML。

我总是得到US Letter的大小。

没有记录的命令行选项可以实现此操作。

我尝试设置CSS:@page {size: A4;}。在无头模式下没有效果,但当我在正常模式下按下Ctrl+P时有效(选择“另存为PDF”的纸张大小选项消失,导出的PDF具有A4纸张大小)。

我在Ubuntu 16.04上尝试了Chrome版本59、60和61。


我已经检查了Chromium源代码。据我所知,目前没有未记录的命令行选项。因此,目前可能只有一些CSS(或其他)黑客是唯一的方法。 - Crazy Yoghurt
6个回答

12

页面大小可以设置为英寸/毫米。我没有尝试使用像素大小。以下是一组CSS规则,对我很有用:

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

我的具体情况是将svg转换为pdf,而不是html;对于svg,您可能还需要在<svg>标签中添加widthheight属性:

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

就是这样!输出的PDF将没有边距,将保留所需的尺寸 - 在我的情况下是5英寸x6.5英寸。


我刚将样式标签放到了 HTML 的头部(没有 CData),用毫米设置大小,它就可以工作了。 - omegastripes
是的,对于 HTML 来说是这样的 - 只需将样式标签放入 <head> 中即可。我的情况是针对 SVG 渲染,所以我需要 CDATA。我应该在我的回答中更清楚地说明这一点。 - Avael Kross

7

您可以在Node环境中运行无头Chrome。

这样,您就可以向printToPdf函数传递附加参数,包括pageWidthpageHeight。请参考此链接该文档了解更多信息。


3
谢谢Dimitry。我知道这个选项,但它会带来一些额外的问题:1. Chrome实例只接受一个调试连接。我必须为我的每个服务实例生成多个实例并在多个端口上进行端口转发,这不是很好。2. 与事件(如onLoad等)的同步是额外的复杂性层面。基本上,我需要编写一个包装程序来运行一个Chrome实例,它将抽象页面加载、打印等操作。这也不是很好。这就是为什么我想要使用每个任务使用单个Chrome实例,完成后自动关闭,不进行远程调试。更简单。 - Crazy Yoghurt

6

页面大小现在可以“几乎”精确控制,无需使用调试接口。

以下是使用Headless Chrome创建几乎与其内容完全相同大小的PDF的方法。

<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>

这将为制作pdf以适应页面做好准备,但不正确,因为页面大小已设置为任意值100x100。

文档渲染完成后,以下内容用于在页面底部正确设置页面大小:

<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>

这种方法消除了页眉/页脚,并解决了像素转换为pdf的数字问题。
还有一件事
Chrome目前存在一个bug,当您使用CSS时,计算div的绝对高度时会出现问题。
line-height: normal; 

这会导致页面计算过短,从而生成额外的PDF页面。你可以使用以下方法解决:
line-height: unset; 

在整个CSS中,没有使用它你将无法得到准确的高度!

3

之前有一次补丁,让页面大小可以配置。详见此地址

现在已经关闭,可用于Chrome的不稳定版本,所以你可以使用@page { size: A4 }命令。

我测试过,在我已安装的不稳定构建版(Google Chrome 61.0.3141.7 dev)中可以使用。但我不确定它何时会出现在稳定版中...


1
那不起作用。我已经安装了Chrome 61.0.3153.4并在CSS中设置了@page { size: A4 }。这没有任何区别。你能通过这种方式获得不同大小的纸张吗?无论如何,你链接的更改与这个问题没有真正的联系。看这里:https://codereview.chromium.org/2829973002/patch/1/10010,当使用shell --print-to-pdf选项时,参数被粗暴地硬编码。它更多地与@Dimitry Leonov的答案有关,因为它在使用调试API时给你控制。 - Crazy Yoghurt
哦,奇怪了,似乎在我的电脑上可以工作。这是我用无头Chrome生成的两个不同版本。第一个是用@page { size: A4; }生成的,第二个是A3大小。输出并不相同,如您所见http://imgur.com/QaQQQ8R http://imgur.com/CYpMopU. 话虽如此,红色部分应该具有210mm / 297mm的大小,因此我不确定为什么侧面会有额外的边距... - atomrc
关于我发给你的补丁链接,当我询问无头Chrome页面大小配置时,Chromium团队中的某个人向我发送了这个链接https://bugs.chromium.org/p/chromium/issues/detail?id=603559#c30。所以我认为这对你也很相关,但不幸的是,我必须承认我真的无法理解这个补丁中正在发生什么:/ - atomrc
你说得对,输出可能不同。情况是这样的:有三种不同的尺寸:paperpagebody。你改变了page的大小,而paperbody保持不变。Chrome将较大的page压缩到相同的paper大小,因此你的body在视觉上变小了。它看起来类似于在较大的纸张上打印,但你可以检查PDF属性,你会发现它保持不变(至少在我的情况下)。 - Crazy Yoghurt
1
我并不真正了解这三种不同的尺寸。你似乎比我更了解PDF,所以我猜我不能帮助你,抱歉 :/ 顺便问一下,我给你展示的结果与你期望的有什么不同吗?这是否意味着我需要在生成的PDF元数据中查找某些信息? - atomrc
要检查PDF纸张大小,您应该在Acrobat Reader中打开它并右键单击任何页面。在描述选项卡中,有以英寸为单位的页面大小(在macOS上的Acrobat Reader DC 2017.009)。或者使用您拥有的任何其他PDF查看器 :) - Crazy Yoghurt

3

注意:在阅读atomrc的回答评论后,我考虑添加这个回答以便更加清晰。

除非使用devtools协议,否则无法更改页面大小。

这是headless Chrome中的一个bug。在无头模式下,@page size CSS规则没有被正确理解,正如该用户在Chromium bug跟踪器上描述得很好那样

桌面版Chrome支持@page规则的大小和边距,并将根据size属性设置表格尺寸。

看起来Headless Chrome也在某种程度上解析了@page,但与桌面版本不同:如果您指定了@page{size},headless似乎会更改页面框(本质上是打印区域)的尺寸,而不是纸张,后者始终保持US Letter大小。但是,如果您指定{size:landscape},它确实会旋转纸张。


0

我使用 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();

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接