当我们把网页应用转化成 PDF
的时候有着各式各样的方法。在下面这篇文章来说,Rachel Andrew
通过她自己使用市面上各种工具的经验来帮助我们找到最合适自己的工具。
许多网页应用有着能让用户转化下载成 PDF
格式的文件的需求。在某些情况下(例如电子商店),需要快速的根据动态的数据生成 PDF
。
在这篇文章中,我会带着大家寻找各种各样可以直接把网页应用。虽然这是一个不完全的列表,主要是证明有不同的方法来达到目的。如果你们有什么喜欢的工具或经验,欢迎在评论告诉我们。
HTML
和 CSS
开始我们的网页应用基本都是先把需要在 PDF
中展示的数据加到 HTML
中。在生成发票的例子中,用户可以在线查看各种信息并且可以点击按钮下载对应记录的 PDF
。你可能开始准备小纸条;强调下,我们所需要的信息已经隐藏在系统里面了。你可能想通过一个比较好的格式去进行下载和打印。因此,一个简单的入手方式就是看下是否可以通过 HTML
和 CSS
来生成 PDF
。
CSS
有其为打印的独特规范,这就是Paged Media module。我已经在我之前的文章Designing For Print With CSS中介绍过,并且许许多多的发行社在他们发行的书中也用到了 CSS
。因为 CSS
有打印专用的规范,我们就应该可以直接使用吗?
用户最简单去生成一个 PDF
就是通过他们的浏览器。通过生成 PDF
而不是打印的方式生成了对应的 PDF
。事与愿违,这样生成的 PDF
并没办法让我们满意!最显而易见的就是当你在打印某些网页应用的时候,会被自动加上了头部和底部的信息。这个文件还会根据你有自定义的打印格式进行格式化。
我们刚才发现的问题都是因为浏览器对于分块规范支持非常的弱;这就是导致你的文档会在在不正确的地方进行断句。当我在回顾自己的文章Breaking Boxes With CSS Fragmentation时发现分块的支持是零散的。这意味着你并不能在头部被放在页面的最底部时获得比较好的断句等等。
另外,我们没办法控制在有页面空白的盒子中内容,例如在我们选择好的每一个页面新增一个头部或者为一张复杂的发票展示出这是第几页。这些只是 Paged Media
中的一小部分,并且还没被任何浏览器支持。
我的文章A Guide To The State Of Print Stylesheets In 2018已经准确的指出哪些是可以通过使用打印样式在浏览器中直接使用能力。
无需通过浏览器的打印菜单,有各种各样通过浏览器渲染引擎进行 PDF
打印,并且可以把对应页面的头部和底部都打印出来。我在我的 tweet
中最为常用的选择就是 wkhtmltopdf
(通过无头 Chrome
或者 Puppeteer
)。
在 Twitter
上被提到很多次的一个命令行工具wkhtmltopdf。这个工具可以选择一个或多个 HTML
文件,基于它们的样式转化成一个 PDF
文件。这个工具是通过 WebKit
渲染引擎来工作的。
从根本上说,这个工具虽然和浏览器打印做着一样的工作,但是你却发现它没有自动生成头部和底部。利用这一点,我们可以通过这个工具生成一个有着打印样式的内容页面或者简单布局的页面去生成 PDF
文件。
虽然你已经换成渲染引擎的模式,还是会遇到在缺少对 Paged Media specification
和分块功能支持的浏览器上进行同样的问题。这里有一个文章记录了 wkhtmltopdf
和使用 Paged Media specification
时一样的缺失的能力。但是这需要比较好的 HTML
和 CSS
功力来完善。
CHROME
另一种有趣的方式去生成 PDF
文件就是使用无头 Chrom
和 Puppeteer
。
但是我又再次发现你会被浏览器所支持的 Paged Media
和分块能力所限制。这里是 page.pdf()
方法中传入的一些参数。和 wkhtmltopdf
一样,这些被加入的函数能力使得来自 CSS
的能力需要被浏览器支持。
你会发现这些方法可以满足你的需求,但是如果你在研究某些能力的时候,你可能会发现你正在疯狂试探着现代浏览器渲染引擎的底线,然后再去寻找其他更好的解决方法。
JAVASCRIPT
中为 PAGED MEDIA
的 POLYFILLS
我们可以通过 JAVASCRIPT
中的 Paged Media Polyfill
重新生成浏览器中的 Paged Media
规范。通过这个操作可以在 Puppeteer
中加入 Paged Media
支持。我们一起来看下 paged.js
和 Vivliostyle
。
如果你还想使用 HTML
和 CSS
方法,你需要用到专为 HTML
和 CSS
设计的用于生成 PDF
文件的各种 API
的打印 UA
。这些 UA
实现了 Paged Media
规范并且对于 CSS
分块能力有着更好的支持;基于这些可以让你对生成有着更好的控制。可以看下下面的各种方案:
打印机 UA
会用 CSS
来格式化文档-就像浏览器一样处理网页。就像查找浏览器中的 CSS
支持一样,你需要去查看这些 UA
的文档确认它们支持哪些属性。例如,Prince
(我比较熟悉的)在编写的时候支持 Flexbox
但不支持 CSS Grid Layout
。在你把页面发送到你使用的工具时,留意下是否会生成你想要的打印格式。如果是一个普通的打印格式,你在页面上用到的CSS
并不一定会在 PDF
文件上正常展示出来。
为这些工具创建一个样式就和我们创建一个正常的打印样式一样,做出指定模块是否展示或隐藏的选择,可能还会用到不同的字体大小和颜色。后面你可能会利用 Paged Media
规范的有时去加上底部说明和页码等等。
对于在你的网页应用中使用这些工具而言,你需要在你的服务器上安装它们(还需要买上对应工具的证书)。这些工具的最突出的问题是它们很贵。这就是说,你可以轻松的使用它们来打印文档,但同时你也需要为节省时间付出不低的价钱。
使用 Prince
基于它提供的 API
,每次使用的时候都是基于一个叫做 DocRaptor
进行每个文档的打印计费。对于许多应用来说对于开发时候切换进程带来的花销最小化并且更加高效化是一个好的开始。
WeasyPrint
是一个相比于之前提到的工具性价比不高但是可以满足你的需求的一个免费的选择。它并没有完全实现 Paged Media
规范,但是它相比于浏览器引擎做的更多。也是一个值得去尝试的选择!
其他工具例如声称支持 Html
和 CSS
转化如 HTML5
、CSS3
和 JavaScript
的 PDFCrowd
。我并没有发现它在实际上支持了什么,并且任何关于 Paged Media
规范的支持也没有找到。同时我也在 tweet
中发现 mPDF
。
HTML
和 CSS
中移除还有着许多其他的解决方法,某些工具就是通过 HTML
和 CSS
中移除并且引用特殊的输出格式。下面有两个相关的工具: