复杂pdf前端自动生成

背景

私募每月要生成近200份基金月报,每个月报上图表很多,运营手工制作枯燥且时间来不及。想要个自动生成pdf月报的功能。

月报例子

解决方案

1.页面截图 再生成pdf

html2canvas

官网 https://html2canvas.hertzen.com/

优点

简单,百度中文教程多

缺点

pdf文字不可选,多页样式有问题

2.PDFKit

A JavaScript PDF generation library for Node and the browser.

官网 http://pdfkit.org/

例子 http://pdfkit.org/demo/browser.html

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
// create a document and pipe to a blob
var doc = new PDFDocument();
var stream = doc.pipe(blobStream());

// draw some text
doc.fontSize(25)
.text('Here is some vector graphics...', 100, 80);

// some vector graphics
doc.save()
.moveTo(100, 150)
.lineTo(100, 250)
.lineTo(200, 250)
.fill("#FF3300");

doc.circle(280, 200, 50)
.fill("#6600FF");

优点

样式好看,支持svg等。

缺点

需要单独写代码,不通用,图表不好加。

3.phantomjs

PhantomJS是一个无界面的,可脚本编程的WebKit浏览器引擎。它原生支持多种web 标准:DOM 操作,CSS选择器,JSON,Canvas 以及SVG。

官网 http://phantomjs.org/

安装比较麻烦,要手动下载,配置环境变量。

alias phantomjs=’~/PATH/TO/phantomjs’

使用它自己的命令行操作。虽然有个npm的版本,但是样式有问题。

生成pdf的官方例子

例子 https://github.com/ariya/phantomjs/blob/master/examples/rasterize.js

1
2
3
page.viewportSize = { width: 600, height: 600 };
if (system.args.length > 3 && system.args[2].substr(-4) === ".pdf") { size = system.args[3].split('*');
page.paperSize = size.length === 2 ? { width: size[0], height: size[1], margin: '0px' } : { format: system.args[3], orientation: 'portrait', margin: '1cm' };

命令格式 为

路径 + 配置js + 目标url + 保存文件名 + 页面参数

路径设置

1
2
3
4
5
6
7
8
var phantomjs = '/Users/yu/Downloads/phantomjs-2.1.1-macosx/bin/phantomjs'
var url = 'http://localhost:8080/#/monthPaper'

if (process.env.NODE_ENV === 'production') {
phantomjs = '/home/app/yuxizhe/phantomjs-2.1.1-linux-x86_64/bin/phantomjs'

url = 'http://localhost:7878/#/monthPaper'
}

需要手动设置页面px参数,要不然截不全

获取不到页面渲染完成的状态
官方例子中的 status === ‘success’ 只是页面加载成o

1
2
3
4
5
6
7
8
9
10
11
page.open(address, function (status) {
if (status !== 'success') {
console.log('Unable to load the address!');
phantom.exit(1);
} else {
window.setTimeout(function () {
page.render(output);
phantom.exit();
}, 200);
}
});

优点

生成的pdf文字可选,较成熟

缺点

安装配置不方便,运行要调用子进程,需要手动设置页面大小,无法获知是否渲染完成,在私募不可用

4.puppeteer [ˌpʌpɪˈtɪr]

天宇上周介绍的,chrome官方

安装

1
npm i puppeteer

国内不下载chrome 版本

1
npm i puppeteer-cn

国内说明 https://npm.taobao.org/package/puppeteer-cn

官方pdf例子

1
2
3
4
5
6
7
8
9
10
const puppeteer = require('puppeteer');

(async () => {
const browser = await puppeteer.launch();
const page = await browser.newPage();
await page.goto('https://news.ycombinator.com', {waitUntil: 'networkidle2'});
await page.pdf({path: 'hn.pdf', format: 'A4'});

await browser.close();
})();

优点

文字可选

调用puppeteer时,不用配置页面具体px了,只要保证比例是A4就可以

不用设置setTimeout来等待页面渲染完成,可以配置 waitUntil

node使用方便

waitUntil 参数

waitUntil <string|Array> When to consider navigation succeeded, defaults to load. Given an array of event strings, navigation is considered to be successful after all events have been fired. Events can be either:

load - consider navigation to be finished when the load event is fired.

domcontentloaded - consider navigation to be finished when the DOMContentLoaded event is fired.

networkidle0 - consider navigation to be finished when there are no more than 0 network connections for at least 500 ms.

networkidle2 - consider navigation to be finished when there are no more than 2 network connections for at least 500 ms.

本地使用networkidle2,服务器使用networkidle0

本地使用 networkidle0 会超时,服务器使用 networkidle2 会提前结束

其他服务器安装报错解决

报错

1
2
3
/usr/lib/x86_64-linux-gnu/libnss3.so: version `NSS_3.22' not found

Failed to launch chrome!

ubuntu16.04 安装chrome 或 chromium无法启动问题解决 https://blog.csdn.net/qq_22551385/article/details/78172178

服务器要安装下 NSS

1
sudo apt install --reinstall libnss3

报错

1
2
Error: Failed to launch chrome!
[0428/142657.340238:ERROR:zygote_host_impl_linux.cc(88)] Running as root without --no-sandbox is not supported. See https://crbug.com/638180.

root 环境下运行要加参数

1
2
3
const browser = await puppeteer.launch({
args: ['--no-sandbox']
})

报错

服务器超时,默认设置超时为5000。私募最慢的接口要3分钟。。所以要加长些

1
2
3
4
await page.goto(url, {
waitUntil: waitUntil,
timeout: 3000000
})

页面设置及调试

暂时解决分页问题,定义单个页面宽高 794px 1123px

做到页面web端浏览/生成pdf 复用, 用 @print 配置相应css 某些不显示等等

服务器字体安装

因为pdf在服务器生成,所以用的是服务器的字体,有些字体没有,生成出来很不好看

将字体文件夹全部复制到/usr/share/fonts/目录下
在终端中依次输入以下三条命令:

sudo mkfontscale

sudo mkfontdir

sudo fc-cache -fv

待解决

页面内容自动分页