方案一:html2canvas + jspdf
缺点:不能获取页面上的文字,超链接等(用途有限)
savePdf=(element, title) => { //html2canvas+jspdf实现方案
html2canvas(element, {
useCORS: true, // 【重要】开启跨域配置
allowTaint: true, // 允许跨域图片
taintTest: false, // 是否在渲染前测试图片
scale: 2,
}).then((canvas) => {
const contentWidth = canvas.width;
const contentHeight = canvas.height;
// 一页pdf显示html页面生成的canvas高度;
const pageHeight = contentWidth / 596 * 842;
// 未生成pdf的html页面高度
let leftHeight = contentHeight;
// 页面偏移
let position = 0;
// 每页的尺寸[595,842],html页面生成的canvas在pdf中图片的宽高
const imgWidth = 596;
const imgHeight = 596 / contentWidth * contentHeight;
const pageData = canvas.toDataURL('image/jpeg', 1.0);
const pdf = new JsPDF('', 'pt', 'a4');
// 有两个高度需要区分,一个是html页面的实际高度,和生成pdf的页面高度(842)
// 当内容未超过pdf一页显示的范围,无需分页
if (leftHeight < pageHeight) {
pdf.addImage(pageData, 'JPEG', 20, 0, imgWidth, imgHeight);
} else {
while (leftHeight > 0) {
pdf.addImage(pageData, 'JPEG', 0, position, imgWidth, imgHeight)
leftHeight -= (pageHeight + 16);// 16是页面上每页的margin
position -= 842 + 16;// 16是页面上每页的margin
// 避免添加空白页
console.log(leftHeight)
if (leftHeight > 300) {
pdf.addPage();
}
}
}
console.log(pdf)
pdf.save(`${title}.pdf`);
});
}
方案二: 使用puppeteer + node端实现
能够获取页面上的文字,超链接等。但是出现问题难以调试
官方文档:https://zhaoqize.github.io/puppeteer-api-zh_CN/
const puppeteer = require('puppeteer');
const fs = require('fs');
const send = require('koa-send');
const path = require('path');
const moment = require('moment')
// 生成pdf
async function generatePdf(pdfname, date, cookies) {
const browser = await puppeteer.launch({
args: ['--disable-dev-shm-usage', '--no-sandbox'],
ignoreHTTPSErrors: true,
headless: true,
});
const page = await browser.newPage();
const waitUntil = 'networkidle0'; // 代表页面的接口都返回后再渲染
await page.setCookie(...cookies);
await page.goto(url, {// 导航到的地址
waitUntil, // 不再有网络连接时触发
timeout: 30 * 60 * 60 * 1000,
});
await page.addStyleTag({// 跟需要转pdf的页面修改样式,可以隐藏一些不想展示的内容
content: `
html {
-webkit-filter: opacity(1) !important;
}
`,
});
await page.pdf({
path: `${pdfname + date}.pdf`, // 名称
printBackground: true, // 展示背景图
width: 596, // 每页画的宽度
height: 843, // 每页画的高度
});
await browser.close();
return pdfname;
}
class ApiController {
async getPdf(ctx) {
const dateTime = moment().format('YYYYMMDD')
const { start_day } = ctx.query;
//可以传一些需要渲染页面所需的参数,类似登录的token、页面需要的参数等(无法通过其他方式传值,需要转pdf的页面直接从cookie里边读取想要的信息即可)
const cookies = [
{
name: 'token',
value: ctx.cookies.get('token'),
domain: 'localhost',
path: '/',
},
{
name: 'start_day',
value: start_day,
domain: 'localhost',
path: '/',
},
];
const retPdf = await generatePdf(pdfPreName, dateTime, cookies)
.then((pdfname) => {
ctx.body = {
result_code: 0,
url: `${pdfname}${dateTime}.pdf`,
state: 'success',
};
});
console.log('数据', retPdf);
}
async downloadPdf(ctx) {
const fileName = ctx.query.name;
const filePath = path.resolve(`${__dirname}`, `../../${fileName}`);
ctx.attachment(filePath);
await send(ctx, fileName);
setTimeout(() => {
fs.unlink(fileName, (err) => {
console.log('文件已被删除');
});
}, 15000);
}
}
fix1: 开始的时候生成的pdf都是只有一页,并且生成的是从页面左上角开始的,并不是我们想要的。经过多番修改,发现是因为页面的外部元素含有min-height min-width等样式导致页面的宽高出现问题。
fix2: 项目部署,包安装问题,puppeteer依赖的包无法安装,需要sre同学支持安装。
fix3: 字体问题,需要sre同学协助安装字体,或者引动通过cdn方式引用字体包
fix4: 导出pdf部分样式问题,兼容处理。
fix4: 无头浏览器登录问题,cookie值的传递
网友评论