如何从您的 Web 应用程序创建 PDF
已发表: 2022-03-10许多 Web 应用程序要求用户能够下载 PDF 格式的内容。 对于应用程序(例如电子商务商店),这些 PDF 必须使用动态数据创建,并且用户可以立即使用。
在本文中,我将探讨直接从 Web 应用程序动态生成 PDF 的方法。 它不是一个完整的工具列表,但我的目标是展示不同的方法。 如果您有最喜欢的工具或您自己的任何经验要分享,请将它们添加到下面的评论中。
从 HTML 和 CSS 开始
我们的 Web 应用程序可能已经在使用将添加到 PDF 中的信息创建 HTML 文档。 如果是发票,用户可以在线查看信息,然后单击下载 PDF 以供记录。 您可能正在创建装箱单; 再一次,信息已经保存在系统中。 您想以一种很好的方式对其进行格式化以供下载和打印。 因此,一个好的起点是考虑是否可以使用 HTML 和 CSS 来生成 PDF 版本。
CSS 确实有处理打印 CSS 的规范,这就是 Paged Media 模块。 我在我的文章“使用 CSS 进行印刷设计”中对此规范进行了概述,许多图书出版商都使用 CSS 来进行所有的印刷输出。 所以,既然 CSS 本身就有印刷材料的规范,那我们应该可以用吗?
用户生成 PDF 的最简单方法是通过他们的浏览器。 通过选择打印到 PDF 而不是打印机,将生成 PDF。 可悲的是,这个 PDF 通常并不完全令人满意! 首先,当您从网页打印某些内容时,它会自动添加页眉和页脚。 它还将根据您的打印样式表进行格式化——假设您有一个。
我们在这里遇到的问题是浏览器对分片规范的支持很差; 这可能意味着您的网页内容以不寻常的方式中断。 正如我在研究我的文章“Breaking Boxes With CSS Fragmentation”时发现的那样,对碎片的支持是不完整的。 这意味着您可能无法防止内容的次优中断,将标题作为页面上的最后一项,等等。
此外,我们无法控制页边距框中的内容,例如在每页添加我们选择的页眉或页码以显示复杂发票有多少页。 这些东西是 Paged Media 规范的一部分,但尚未在任何浏览器中实现。
我的文章“2018 年打印样式表状态指南”在浏览器对使用打印样式表直接从浏览器打印的支持类型方面仍然是准确的。
使用浏览器渲染引擎打印
有多种方法可以使用浏览器渲染引擎打印到 PDF,而无需通过浏览器中的打印菜单,并以页眉和页脚结束,就像您打印了文档一样。 响应我的推文的最受欢迎的选项是 wkhtmltopdf,以及使用无头 Chrome 和 Puppeteer 进行打印。
wkhtmltopdf
在 Twitter 上多次提到的解决方案是一个名为 wkhtmltopdf 的命令行工具。 该工具采用 HTML 文件或多个文件以及样式表并将它们转换为 PDF。 它通过使用 WebKit 渲染引擎来做到这一点。
我们使用 wkhtmltopdf。 它并不完美,尽管这可能是用户错误,但对于生产应用程序来说已经足够好了。
— Paul Cardno (@pcardno) 2019 年 2 月 15 日
因此,从本质上讲,此工具与从浏览器打印的功能相同,但是,您不会获得自动添加的页眉和页脚。 从积极的一面来看,如果您的内容有一个有效的打印样式表,那么它也应该使用这个工具很好地输出到 PDF,因此一个简单的布局可能会很好地打印出来。
然而不幸的是,您仍然会遇到与直接从 Web 浏览器打印时相同的问题,因为您仍然使用浏览器渲染引擎进行打印,因此缺乏对 Paged Media 规范和碎片属性的支持。 您可以将一些标志传递到 wkhtmltopdf 中,以便添加一些缺少的功能,这些功能默认情况下使用 Paged Media 规范。 然而,这确实需要在编写好的 HTML 和 CSS 之上做一些额外的工作。
无头镀铬
另一个有趣的可能性是使用 Headless Chrome 和 Puppeteer 打印到 PDF。
傀儡师。 这太神奇了。
——亚历克斯·罗素 (@slightlylate) 2019 年 2 月 15 日
但是,您再次受到浏览器对分页媒体和碎片的支持的限制。 有一些选项可以传递到page.pdf()
函数中。 与 wkhtmltopdf 一样,如果有浏览器支持,这些添加了一些 CSS 可能提供的功能。
很可能其中一个解决方案可以满足您的所有需求,但是,如果您发现自己正在打一场战斗,那么您很可能已经达到了当前浏览器渲染引擎的极限,并且将需要寻找更好的解决方案。
用于分页媒体的 JavaScript Polyfills
有一些尝试使用 JavaScript 在浏览器中复制 Paged Media 规范——本质上是创建一个 Paged Media Polyfill。 这可以在使用 Puppeteer 时为您提供分页媒体支持。 看看 paged.js 和 Vivliostyle。
是的。 对于简单的文档,比如课程证书,我们可以使用 Chrome,它对 @page 的支持最少。 对于其他任何事情,我们在 Chrome 中使用 PrinceXML 或 paged.js polyfill。 这是使用 paged.js 书籍的 WIP 概念证明:https://t.co/AZ9fO94PT2
— Electric Book Works (@electricbook) 2019 年 2 月 15 日
使用打印用户代理
如果您想继续使用 HTML 和 CSS 解决方案,那么您需要寻找专为从 HTML 和 CSS 打印而设计的用户代理 (UA),它具有用于从文件生成 PDF 的 API。 这些用户代理实现了分页媒体规范,并且对 CSS Fragmentation 属性有更好的支持; 这将使您更好地控制输出。 主要选择包括:
- 王子
- 天线屋
- PDF反应器
打印 UA 将使用 CSS 格式化文档——就像 Web 浏览器一样。 与浏览器对 CSS 的支持一样,您需要查看这些 UA 的文档以了解它们支持的内容。 例如,Prince(我最熟悉的)在撰写本文时支持 Flexbox,但不支持 CSS 网格布局。 将页面发送到您正在使用的工具时,通常会使用特定的样式表进行打印。 与常规打印样式表一样,您在网站上使用的 CSS 并不都适用于 PDF 版本。
为这些工具创建样式表与创建常规打印样式表非常相似,根据显示或隐藏的内容做出决定,可能使用不同的字体大小或颜色。 然后,您将能够利用 Paged Media 规范中的功能,添加脚注、页码等。
在您的 Web 应用程序中使用这些工具时,您需要将它们安装在您的服务器上(当然,已经购买了许可证)。 这些工具的主要问题是价格昂贵。 也就是说,鉴于您可以轻松地使用它们生成打印文档,它们很可能会为自己节省开发人员的时间。
可以通过 API 使用 Prince,按文档付费,通过名为 DocRaptor 的服务。 对于许多应用程序来说,这肯定是一个很好的起点,就好像托管您自己的应用程序会变得更具成本效益,切换的开发成本将是最小的。
WeasyPrint 是一个免费的替代品,它不像上述工具那么全面,但可以很好地达到您需要的结果。 它没有完全实现所有分页媒体,但是,它实现的不仅仅是浏览器引擎。 绝对,一个尝试!
其他声称支持从 HTML 和 CSS 转换的工具包括 PDFCrowd,它大胆地声称支持 HTML5、CSS3 和 JavaScript。 但是,我无法找到有关所支持的确切内容的任何详细信息,以及是否有任何 Paged Media 规范。 在我的推文的回复中还提到了 mPDF。
远离 HTML 和 CSS
还有许多其他解决方案不再使用 HTML 和 CSS,而是要求您为该工具创建特定的输出。 几个 JavaScript 竞争者如下:
- jsPDF
- pdf制作
无头浏览器 + 保存到 PDF 曾经是我的第一选择,但对于单页文档以外的任何内容,总是产生低于标准的结果。 我们切换到 https://t.co/3o8Ce23F1t 获取多页报告,这需要付出更多的努力,但最终还是值得的!
— JimmyJoy (@jimle_uk) 2019 年 2 月 15 日
建议
除了需要您创建完全不同的打印内容表示的基于 JavaScript 的方法之外,许多这些解决方案的美妙之处在于它们是可互换的。 如果您的解决方案基于调用命令行工具,并将您的 HTML、CSS 和可能的一些 JavaScript 传递给该工具,那么在工具之间切换是相当简单的。
在撰写本文的过程中,我还发现了一个 Python 包装器,它可以运行许多不同的工具。 (请注意,您需要已经安装了工具本身,但是,这可能是在示例文档中测试各种工具的好方法。)
对于 Paged Media 和碎片化的支持,Prince、Antenna House 和 PDFReactor 将名列前茅。 作为商业产品,它们也附带支持。 如果您有预算、要打印到 PDF 的复杂页面,并且您的限制是开发人员的时间,那么您很可能会发现这些是让您的 PDF 创建工作正常的最快途径。
但是,在许多情况下,免费工具对您很有效。 如果您的要求非常简单,那么 wkhtmltopdf 或基本的无头 Chrome 和 Puppeteer 解决方案就可以解决问题。 对于许多回复我原始推文的人来说,这似乎确实有效。
但是,如果您发现自己难以获得所需的输出,请注意这可能是浏览器打印的限制,而不是您做错了什么。 如果您想要更多 Paged Media 支持,但又无法购买商业产品,不妨看看 WeasyPrint。
我希望这是对可用于从 Web 应用程序创建 PDF 的工具的有用汇总。 如果不出意外,这表明如果您最初的选择效果不佳,则存在多种选择。
请在评论中添加您自己的经验和建议,这是我们很多人最终要处理的事情之一,分享的个人经验可能会非常有帮助。
延伸阅读
本文中提到的各种资源和工具的汇总,以及其他一些用于处理来自 Web 应用程序的 PDF 文件的有用资源。
规格
- 分页媒体模块
- 碎片化
文章和资源
- 使用 CSS 进行打印设计
- 用 CSS 分片打破盒子
- 2018 年打印样式表状态指南
- 无头 Chrome 和 Puppeteer 入门
- 打印-css.rocks
工具
- wkhtmltopdf
- paged.js
- Vivliostyle
- 王子
- 天线屋
- PDF反应器
- 猛禽博士
- WeasyPrint
- PDF人群
- mPDF
- jsPDF
- pdf制作
- 生产和发布服务器