复杂Pdf前端自动生成
复杂pdf前端自动生成
背景
私募每月要生成近200份基金月报,每个月报上图表很多,运营手工制作枯燥且时间来不及。想要个自动生成pdf月报的功能。
月报例子
解决方案
1.页面截图 再生成pdf
html2canvas
优点
简单,百度中文教程多
缺点
pdf文字不可选,多页样式有问题
2.PDFKit
A JavaScript PDF generation library for Node and the browser.
例子 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。
安装比较麻烦,要手动下载,配置环境变量。
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
8var 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’ 只是页面加载成o1
2
3
4
5
6
7
8
9
10
11page.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
官方pdf例子1
2
3
4
5
6
7
8
9
10const 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 | /usr/lib/x86_64-linux-gnu/libnss3.so: version `NSS_3.22' not found |
ubuntu16.04 安装chrome 或 chromium无法启动问题解决 https://blog.csdn.net/qq_22551385/article/details/78172178
服务器要安装下 NSS1
sudo apt install --reinstall libnss3
报错
1 | Error: Failed to launch chrome! |
root 环境下运行要加参数
1 | const browser = await puppeteer.launch({ |
报错
服务器超时,默认设置超时为5000。私募最慢的接口要3分钟。。所以要加长些
1 | await page.goto(url, { |
页面设置及调试
暂时解决分页问题,定义单个页面宽高 794px 1123px
做到页面web端浏览/生成pdf 复用, 用 @print 配置相应css 某些不显示等等
服务器字体安装
因为pdf在服务器生成,所以用的是服务器的字体,有些字体没有,生成出来很不好看
将字体文件夹全部复制到/usr/share/fonts/目录下
在终端中依次输入以下三条命令:
sudo mkfontscale
sudo mkfontdir
sudo fc-cache -fv
待解决
页面内容自动分页