前言
最近在WLAF防伪上面通过pdf输出文档进行喷码,需要实现不同效果,由于大多数工具并不支持css3的效果,所以需要找到一个合适的框架来处理html转pdf的解决方案。
html转pdf的方式概览
- 前端html转pdf
- html url转pdf
- 命令行html转pdf
前端转pdf方案
在客户端和服务器端都可以生成 PDF 文件。但是,让后端处理它可能更有意义,因为我们不能大量消耗用户浏览器提供的资源。
当然,本文仍会展示基于两个端的不同解决方案。
方案 1:基于 DOM 的屏幕快照
乍一看,这个方案似乎是最简单的,但它有其自身的局限性。如果没有特殊的需求,这是一种很好且简单的方案,例如 PDF 中包含可选择或可搜索文本。
方案很简单:从页面创建一个屏幕快照,并将其放入 PDF 文件中。实现时使用了两个包:
- Html2canvas,基于DOM生成快照
- jsPdf,一个生成PDF的库
下面是相关代码。
import html2canvas from 'html2canvas';
import jsPdf from 'jspdf';
function printPDF () {
const domElement = document.getElementById('your-id');
html2canvas(domElement, {
onclone: (document) => {
document.getElementById('print-button').style.visibility = 'hidden';
}
})
.then((canvas) => {
const img = canvas.toDataURL('image/png');
const pdf = new jsPdf();
pdf.addImage(imgData, 'JPEG', 0, 0, width, height);
pdf.save('your-filename.pdf');
})
需要特别注意一下 html2canvas onclone 方法。它是当DOM被克隆后进行渲染时的回调函数,可用于修改将要渲染的内容,而不会影响原始DOM。当你需要在生成快照前操作 DOM(例如隐藏打印按钮)时,会非常有用。
方案 2:仅使用 PDF 库
NPM 上有几个库,例如 jsPDF(上面提到的)或 PDFKit。如果想使用这些库,我们不得不再次重新创建页面结构。这肯定会降低代码可维护性,因为需要将所有后续更改同时应用于 PDF 模板和 React 页面。
看看下面的代码。你需要手动创建 PDF 文档,遍历 DOM 并弄清楚如何将每个元素转换为 PDF 元素,但这是一项枯燥的工作。要寻找一个更简单的方法。
const PDFDocument = require('pdfkit');
const fs = require('fs');
const doc = new PDFDocument;
doc.pipe(fs.createWriteStream('output.pdf'));
doc.font('fonts/PalatinoBold.ttf')
.fontSize(25)
.text('Some text with an embedded font!', 100, 100);
doc.image('path/to/image.png', {
fit: [250, 300],
align: 'center',
valign: 'center'
});
doc.addPage()
.fontSize(25)
.text('Here is some vector graphics...', 100, 100);
doc.end()
此代码段来自 PDFKit 文档。但是,如果您的目标是 PDF 文件,而不是已经存在(并且不断变化)的 HTML 页面的转换,那么它会很有用。
方案 3:使用 Puppeteer
什么是 Puppeteer?下面是它的文档介绍:
Puppeteer 是一个 Node 库,它提供了一个高级 API 来通过 DevTools 协议控制 Chrome 或 Chromium。Puppeteer 默认无头运行,但可以配置为运行完整(非无头)Chrome 或 Chromium。
Puppeteer基本上是一个可以从 Node.js 运行的浏览器。我们可以用它来生成页面的屏幕快照和 PDF。这正是我们要找的。
下面是一个用例:
const puppeteer = require('puppeteer');
async function printPDF() {
const browser = await puppeteer.launch({ headless: true });
const page = await browser.newPage();
await page.goto('https://blog.risingstack.com', {waitUntil: 'networkidle0'});
const pdf = await page.pdf({ format: 'A4' });
await browser.close();
return pdf;
})
这是一个简单的功能,可以导航到 URL 并生成站点的 PDF 文件。
首先,启动浏览器(PDF 生成仅在无头浏览器模式下支持),然后打开一个新页面,设置视口大小,并导航到提供的 URL。
设置该 waitUntil: 'networkidle0' 选项意味着当至少 500 毫秒没有网络连接时,Puppeteer 认为导航已完成。(查看 API 文档 以获取更多信息。)
开源url转pdf方案doctron
简介
Doctron是基于Docker、无状态、简单、快速、高质量的文档转换服务。目前支持将html转为pdf、图片(使用chrome(Chromium)浏览器内核,保证转换质量)。支持PDF添加水印。
资源
GitHub仓库: github.com/lampnick/do…
gitee仓库代码:gitee.com/cloudDevice…
在线体验: doctron.lampnick.com/
容器化服务
docker命令:
#使用默认配置
docker run -p 8080:8080 --rm --name doctron-alpine lampnick/doctron
#使用自定义配置文件
docker run -p 8080:8080 --rm --name doctron-alpine \
-v <本地doctron.yaml配置文件>:/doctron.yaml \
lampnick/doctron
k8s命令:
kubectl apply -f https://raw.githubusercontent.com/lampnick/doctron/master/manifests/k8s-doctron.yaml
本地转换pdf
文字旋转效果:
http://127.0.0.1:8080/convert/html2pdf?u=doctron&p=lampnick&url=http://172.16.100.63:8197/html/180deg.html
正常文字效果:
http://127.0.0.1:8080/convert/html2pdf?u=doctron&p=lampnick&url=http://172.16.100.63:8197/html/furen.html
从上面的效果来看,doctron已经满足条件了,我们还需要看别的吗,当然,有些方案也很简洁,下面再介绍一款命令行的方式实现工具。
命令行工具html转pdf
简介
wkhtmltopdf和wkhtmltoimage是开源(LGPLv3)命令行工具,使用Qt WebKit渲染引擎将HTML渲染成PDF和各种图像格式。它们完全“无头”运行,不需要显示器或显示服务。
还有一个C库,如果你对这类东西感兴趣的话。
下载地址
目前所有的下载都是通过GitHub发布的,所以你可以浏览一个特定的下载或使用下面的链接。
不要将wkhtmltopdf与任何不受信任的HTML一起使用——确保清除任何用户提供的HTML/JS,否则它可能导致完全接管运行它的服务器!请阅读项目状态血腥的细节。
我这里下载了windows版本的Installer。
本地实验
文字旋转效果:
E:\Program Files\wkhtmltopdf\bin>wkhtmltopdf http://admin-local.liweijia.com/180deg.html 180deg.pdf
正常文字效果:
E:\Program Files\wkhtmltopdf\bin>wkhtmltopdf http://admin-local.liweijia.com/furen.html furen.pdf
——由此可见,这里的选择效果是不符合我们的解决方案的。
全部测试输出文件: