作者:亚璨的秘密 | 来源:互联网 | 2022-12-09 22:25
在尝试使用headerTemplate
和footerTemplate
选项时,我注意到了一些不一致page.pdf
:
页眉和页脚的DPI似乎更低(我认为主体为72比96).因此,如果我想要匹配边距,我必须按比例缩放.
样式不与主体共享,因此我必须将它们包含在模板中.
如果我尝试使用本地存储的字体,它可以在主体上工作,但不在页眉/页脚中,即使我在页眉/页脚模板中包含相同的CSS.
我怀疑这是因为页眉和页脚被视为单独的文档并分别转换为image/pdf(https://cs.chromium.org/chromium/src/components/printing/resources/print_header_footer_template_page.html也暗示类似的东西).熟悉该实现的人是否可以解释它实际上是如何工作的?谢谢!
1> Grant Miller..:
简答:
Puppeteer通过DevTools协议控制Chrome或Chromium .
Chromium使用Skia生成PDF.
Skia分别处理标题,一组对象和页脚.
详细解答:
从Puppeteer文档:
page.pdf(选项)
options
可能具有以下属性的Options对象:
headerTemplate
打印标题的HTML模板.应该是有效的HTML标记,其中包含用于将打印值注入其中的以下类:
date
格式化打印日期
title
文件名
url
文件位置
pageNumber
当前页码
totalPages
文档中的总页数
footerTemplate
打印页脚的HTML模板.应该使用相同的格式headerTemplate
.
返回:>使用PDF缓冲区解析的Promise.
注意目前仅在Chrome无头中支持生成pdf.
注意 headerTemplate
和footerTemplate
标记具有以下限制:
不评估模板内的脚本标记.
页面样式在模板中不可见.
我们可以从Puppeteer源代码page.pdf()
中学到:
在Chrome DevTools协议方法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.
footerTemplate
字符串(可选)
打印页脚的HTML模板.应该使用相同的格式headerTemplate
.
返回对象
data
串
Base64编码的pdf数据.
该铬源代码Page.printToPDF
向我们表明:
该Page.printToPDF
参数被传递给sendDevToolsMessage
函数,它发出一个DevTools协议命令并返回结果的承诺.
在进一步挖掘之后,我们可以看到Chromium具有一个称为SkDocument
创建PDF文件的类的具体实现.
SkDocument
来自Skia图形库,Chromium用于生成PDF.
该操作的Skia的PDF理论,在PDF对象和文档结构部分,指出:
背景:PDF文件格式有一个标题,一组对象,然后是一个页脚,其中包含文档中所有对象(交叉引用表)的目录.目录列出了每个对象的特定字节位置.对象可能具有对其他对象的引用,并且这些引用的ASCII大小取决于分配给引用对象的对象编号; 因此,在知道对象的大小之前,我们无法计算目录,这需要分配对象编号.该文档用于SkWStream::bytesWritten()
查询每个对象的偏移量并构建交叉引用表.
该文件进一步解释:
该PDF后端需要在PDF中使用的所有间接对象被添加到SkPDFObjNumMap
的SkPDFDocument
.目录负责分配对象编号并生成PDF文件末尾所需的目录.从某种意义上说,生成PDF是一个三步过程.在第一步中,创建它们中的所有对象和引用(主要由它完成SkPDFDevice
).在第二步中,SkPDFObjNumMap
分配并记住对象编号.最后,在第三步骤中,报头被印刷,每一个对象被印刷,然后将印刷的内容和拖车表.SkPDFDocument
负责收集各种SkPDFDevice
实例中的所有对象,将它们添加到一个SkPDFObjNumMap
,迭代对象一次以设置其文件位置,然后再次迭代以生成最终的PDF.
2> Shrey..:
感谢其他答案(/sf/ask/17360801/)和代码搜索,我想我找到了我一直在寻找的大多数答案。
打印实现在PrintPageInternal中。它使用两个单独的WebFrame
s-一个用于呈现内容,一个用于呈现页眉和页脚。通过创建一个特殊的框架,将print_header_and_footer_template_page.html的内容写入此框架,setup
使用提供的选项调用函数,然后打印到共享画布,来完成页眉和页脚的呈现。此后,页面的其余内容将在页边距定义的边界内打印在同一画布上。
页眉和页脚由fudge_factor缩放,而fudge_factor不适用于其余内容。DPI可能会发生一些有趣的事情(这可能解释了fudge_factor 1.33333333f
等于96/72
)。
我猜想这个特殊的框架会阻止页眉和页脚共享与页面内容相同的资源(样式,字体等)。可能没有设置加载(和等待)页眉和页脚模板请求的任何其他资源,这就是为什么不加载所请求的字体的原因。