当尝试将headerTemplate
和footerTemplate
选项与page.pdf
一起使用时,我注意到了一些不一致之处:
我怀疑发生这种情况是因为页眉和页脚被视为单独的文档,并分别转换为image / pdf(https://cs.chromium.org/chromium/src/components/printing/resources/print_header_footer_template_page.html也暗示着类似的意思)。熟悉实施的人可以解释其实际工作原理吗?谢谢!
答案 0 :(得分:6)
Puppeteer通过DevTools Protocol控制Chrome或Chromium。
Chromium使用Skia生成PDF。
Skia分别处理页眉,对象集和页脚。
page.pdf(选项)
注意当前仅在Chrome headless中支持生成pdf。
注意
headerTemplate
和footerTemplate
标记具有以下限制:
- 不评估模板中的脚本标签。
- 页面样式在模板中不可见。
我们可以从Puppeteer source code for page.pdf()
中学到:
Page.printToPDF
(以及headerTemplate
和footerTemplate
参数)被发送到page._client
。page._client
是page.target().createCDPSession()
(Chrome DevTools协议会话)的实例。从Chrome DevTools Protocol Viewer中,我们可以看到Page.printToPDF
包含参数headerTemplate
和footerTemplate
:
Page.printToPDF
将页面打印为PDF。
参数
headerTemplate
字符串(可选)
- 用于打印标题的HTML模板。应该是有效的HTML标记,其中包含以下用于向其中注入打印值的类:
date
:格式化的打印日期title
:文档标题url
:文档位置pageNumber
:当前页码totalPages
:文档中的页面总数- 例如,
<span class=title></span>
将生成包含标题的范围。footerTemplate
字符串(可选)
- 打印页脚的HTML模板。应该使用与
headerTemplate
相同的格式。返回对象
data
字符串
- Base64编码的pdf数据。
Chromium source code for Page.printToPDF
向我们显示:
Page.printToPDF
参数被传递到sendDevToolsMessage
函数,该函数发出一个DevTools协议命令并返回对结果的承诺。进一步挖掘后,我们可以看到Chromium具有创建PDF文件的具体implementation of a class called SkDocument
。
SkDocument
来自Skia Graphics Library的Chromium uses for PDF generation。
Skia PDF Theory of Operation部分中的PDF Objects and Document Structure指出:
背景:PDF文件格式具有标头,对象集和页脚,包含文档中所有对象的目录(交叉引用表)。目录列出了每个对象的特定字节位置。这些对象可能具有对其他对象的引用,并且这些引用的ASCII大小取决于分配给所引用对象的对象编号。因此,在知道对象的大小(需要分配对象编号)之前,我们无法计算目录。该文档使用
SkWStream::bytesWritten()
查询每个对象的偏移量并建立交叉引用表。
文档进一步解释如下:
PDF后端要求将PDF中使用的所有间接对象添加到
SkPDFObjNumMap
的{{1}}中。该目录负责分配对象编号并生成PDF文件末尾所需的目录。从某种意义上说,生成PDF是一个三步过程。第一步,创建其中的所有对象和引用(主要由SkPDFDocument
完成)。在第二步中,SkPDFDevice
分配并记住对象编号。最后,在第三步中,打印标头,打印每个对象,然后打印目录和预告片。SkPDFObjNumMap
负责收集来自各个SkPDFDocument
实例的所有对象,将它们添加到SkPDFDevice
中,对这些对象进行一次迭代以设置其文件位置,然后再次进行迭代以生成最终对象PDF。
答案 1 :(得分:4)
多亏了其他答案(https://stackoverflow.com/a/51460641/364131)和代码搜索,我认为我找到了我一直在寻找的大多数答案。
打印实现在PrintPageInternal中。它使用两个单独的WebFrame
-一个用于呈现内容,另一个用于呈现页眉和页脚。通过创建special frame,将print_header_and_footer_template_page.html的内容写入此帧,使用提供的选项调用setup
函数,然后打印到共享画布来完成页眉和页脚的呈现。 。之后,页面的其余内容将在页边距定义的边界内打印在同一画布上。
页眉和页脚由fudge_factor缩放,该比例不适用于其余内容。 DPI可能会发生一些有趣的事情(这可能解释1.33333333f
的fudge_factor等于96/72
)。
我猜想这个特殊的框架会阻止页眉和页脚共享与页面内容相同的资源(样式,字体等)。可能没有设置加载(和等待)页眉和页脚模板请求的任何其他资源,这就是为什么不加载所请求的字体的原因。
答案 2 :(得分:1)
我对此问题进行了大量研究,最后,我实现了一个小型库以通过一个小技巧来解决此问题:
我创建了两个PDF文件。第一个是没有页眉和页脚的HTML内容。第二个是根据原始内容PDF页面的编号重复页眉和页脚,然后将它们合并在一起。