ok,研究这是事情是我的一些研究计划在博客上写(当然被认证系统保护起来了),而我希望能将这些内容输出为 PDF 文件。浏览器的打印功能可以将整个网页输出为 PDF 文件,但是这会包括很多杂乱的内容,比如 Header, SideBar, Footer 之类的东西。因此我需要一套将制定的HTML内容转化成 PDF,并且保留网页样式的转化方案。

当然,理想的方式是在前端完成这个转化。Google 搜索前端方案,搜索结果中提到最多的是 jsPDF。不过我经过试验发现这个框架有一个非常致命的问题:不支持UTF-8。故我转而找到了一个后端的基于 Nodejs 的方案(这并不意味着前端方案不可行,只不过太难找了)。后端方案基于 html-pdf 这个框架。这个框架的描述中提到,这个框架是基于 phantomjs 的,而phantomjs 本质上一个最小浏览器内核。

html-pdf 的使用也不是一帆风顺。html-pdf 存在一个「小问题」:这个框架对内联的脚本支持有问题。就我的场景而言,内联的脚本会报$ 符号未找到的错误($ 是 jQuery 的变量)。这个问题很可能是在 phantomjs 中就存在的问题。我尚未能够找到能从根本上解决这个问题的办法。于是我想到了一个折中的方案:首先处理待转换的 html 文件,将其中的内联脚本都提取出来然后将内联脚本修改成引用外部 js 文件的方式。

我使用了 cheerio 这个库来做 html 的修改:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
$("body script").each((idx, ele) => {
if (ele.children.length === 0) {
return
}
const content = ele.children[0].data
fs.writeFileSync(`/path/to/hold/tmpjs/tmp-${idx}.js`, content)
$(ele.children[0]).remove()
// $(ele).text("console.log('hi')")
$(ele).attr("src", `/tmpjs/tmp-${idx}.js`)
})

html = $.toString()
var options = {
// base 配置的含义是当 html 中引用外部文件时从这个路径下去载入
base: 'file:///path/to/hold/',
width: '1000px',
border: {
"top": "40px",
"right": "0",
"bottom": "40px",
"left": "0"
}
}

pdf.create(html, options).toFile("output.pdf", function(err, res) {
if (err) return console.log(err);
})