目录
最近在开发项目的时候,接到了一个需求。听起来是比较简单的,将后台系统的报表以pdf文件的形式导出。excel导出,图片导出都做过了,现在轮到pdf了。在实现的过程中遇到了一些困难,通过查阅资料,网上各位朋友使用过的经验后,差不多完成。写这篇博客希望将自己的经验留下,帮助到更多的朋友。
插件
PDF类
-
pdfmake 官网地址: https://pdfmake.github.io/docs/getting-started/
-
jspdf 官网地址: http://raw.githack.com/MrRio/jsPDF/master/docs/index.html
DOM转化为Canva
1. html2canvas 官网地址: http://html2canvas.hertzen.com/getting-started
实现
最初我是想使用pdfmake来自定义报告表单的,无奈自定义起来太麻烦,比如一些合并单元格的操作需要花费太多时间去研究,后面直接转投pdf,原因还是觉得jspdf与html2canvas直接转换图片使用起来比较快,直接把图片放入pdf即可。
先根据网址进到每个插件中,选择适当的方法将插件下载下来。接下来直接开撸代码,话不多说,贴上测试代码。
简单分页
下面试简单的将一个指定的表格转换为pdf,包含了最简单的分页。
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>PDF导出测试</title>
<style>
* {
margin: 0;
}
.container {
height: 400px;
overflow-y: auto;
}
.content {
margin-top: 20px;
}
table {
border-collapse: collapse;
font-size: 16px;
width: 80%;
margin: 0 auto;
word-break: break-all;
}
td {
padding: 5px;
border: 1px solid;
text-align: center;
}
</style>
</head>
<script src="./js/html2canvas.min.js"></script>
<script src="https://unpkg.com/jspdf@latest/dist/jspdf.min.js"></script>
<body>
<div>
<button onclick="getContent()">导出PDF</button>
</div>
<div class="container">
<div class="content">
<table>
<tr>
<td>报告</td>
<td>结果</td>
<td>数值</td>
<td>数值</td>
<td>数值</td>
<td>数值</td>
<td>数值</td>
</tr>
<tr>
<td>1</td>
<td>2</td>
<td>3</td>
<td>3</td>
<td>3</td>
<td>3</td>
<td>3</td>
</tr>
</table>
</div>
</div>
</body>
<script src="../pdfmake/build/vfs_fonts.js"></script>
<script>
// 增加测试数据
function addTr() {
const table = document.querySelector('table');
const tr = document.querySelectorAll('tr')[1];
for (let i = 1; i < 100; i++) {
var nTr = document.createElement('tr');
nTr.innerHTML = tr.innerHTML;
table.appendChild(nTr);
}
}
addTr();
function getContent() {
const content = document.querySelector('.content'); // 获取待导出的pdf内容DOM
const rect = content.getBoundingClientRect(); // 获取pdf内容DOM的位置
var pdf = new jsPDF('', 'pt', 'a4') // 初始化jspdf对象
generatePDF(content, pdf, rect);
}
function generatePDF(content, pdf, rect) {
console.log(rect.top);
const op = {
scale: 1,
logging: true,
width: Number(getComputedStyle(content).width.replace('px', '')), // 设置画布的宽度
height: Number(getComputedStyle(content).height.replace('px', '')) // 设置画布的高度
};
html2canvas(content, op).then(
canvas => {
const data = canvas.toDataURL('png', 1); // 将生成的canva转换为base64
var imgWidth = 595.28; // 设置图片在pdf中宽度
var imgHeight = 592.28 / canvas.width * canvas.height; //设置图片在pdf中的高度
var singlePdfHeight = 841.89; // pdf单页的高度
var singlePageHeight = canvas.width / 592.28 * 841.89; // A4纸单页高度在canvas中对应的高度
var position = 0; //每一页pdf偏移量
var leftHeight = canvas.height; // 剩余高度
if (leftHeight < singlePageHeight) {
// 如果canvas图片总高度比pdf单页高度小可直接生成图片
pdf.addImage(data, 'PNG', 0, 0, imgWidth, imgHeight);
} else {
// 如果canvas图片总高度比pdf单页高度大, 循环生成pdf
while (leftHeight > 0) {
pdf.addImage(data, 'PNG', 0, position, imgWidth, imgHeight);
leftHeight -= singlePageHeight;
position -= singlePdfHeight;
if (leftHeight > 0) {
pdf.addPage(); // 若一页高度无法显示完整个内容,增加新一页Pdf
}
}
}
pdf.save("content.pdf");
}
);
}
</script>
</html>
进一步分页
这里我就提供一下思路:
那这个table表格做列子, 可以通过单页A4纸换算到html的高度,然后根据这个高度将html中要截取的内容进行分割。最后分割为几部分就有几页即调用几次html2canvas。 通过调整html2canvas配置中的y参数来达到分页效果。
关于html2canvas的一点小经验
这里要提一个关于html2canvas生成的canvas中内容偏移量的问题。 你有可能遇到使用html2canvas得到的内容与自己的预期不一致,想要明白为什么主要要搞清楚html2canvas的默认配置。
假设你要截取的html内容块的外层DOM为 warpper
const warpper = document.querySelector('.warpper');
option {
scale: 默认值为window.devicePixelRatio--如果你不设置值可能会导致你的canvas画布比实际设置的值偏大
y: 默认值为 wapper.getBoundingClientRect().top + window.pageYOffset
x: 默认值为 wapper.getBoundingClientRect().left + window.pageXOffset
}
在官方文档中,我没有找到对y的默认值,翻看源码后才发现。所以我在开发过程中相同的代码,不同的样式中可能得到的效果不一样。因此根据源码中的默认值为基础来调整canvas中内容偏移位置,得到你想要的效果。
比如想要设置纵向上的偏移量,要注意window.pageYOffset是否考虑进去。
使用addImage插入不同图片只显示第一张图片问题
最近再次使用jspdf开发,使用addImage插入多张不同的图片,发现无论怎么插入pdf中所有页面都只显示第一张图片。看了源码后发现, 要在pdf中插入不同图片的话,addImage方法的入参alias要么不要设置,如果一定要设置的情况下一定要为每张图片的alias设置不同的值,我这里使用时间戳。
原因是每次addImage会根据alias这个字段去查图片缓存,如果alias已经在之前的操作中存在,就会用之前alias对应的图片。
希望可以帮助到各位,如果文中有问题的地方欢迎大家留言一起讨论。