前端JS生成PDF的一次踩坑之行
2019-02-18 本文已影响44人
叫我汪汪
前言
最近公司的项目提出一个新需求,需要在前端生成并输出一份pdf合同。由于合同的语言是中文,所以找到最好的方法还是通过html2canvas.js转换成canvas,然后canvas输出成pdf。但还是踩了许多坑,所以总结一下此次踩的坑以及过程。
首先需要引入html2canvas.js,这个插件的作用是将页面或者DOM元素绘制到canvas画布上
<script src="http://html2canvas.hertzen.com/dist/html2canvas.min.js"></script>
还需要引入jspdf.js,作用是将绘制好的canvas转换成pdf,并输出。
<script src="https://unpkg.com/jspdf@latest/dist/jspdf.min.js"></script>
1. 合同样式
首先在html上复现出合同的样式(附上参考片段)
<p class="title_1"><span class="item">1</span>授权合作内容</p>
<p class="title_2"><span class="item">1.1</span>授权作品: 授权方享有的以音乐内容为主的录音录像制品及相关资料,资料包括但不限于歌手信息、肖像、图片、文字介绍等。除《授权歌曲清单》所列出的作品外,协议期内授权方新增音乐作品时,应主动提交更新作品清单,该清单亦属于协议,与本协议具有相同效力。</p>
<p class="title_2"><span class="item">1.2</span>授权权利:授权作品之 <strong>词曲著作权、录音录像制作者权、表演者权、信息网络传播权、邻接权、肖像权</strong>等,允许星晖使用、传播、复制、改编、表演、转授、宣传推广等的权利。</p>
<p class="title_2"><span class="item">1.3</span>授权领域:包括但不限于电脑、手机、平板电脑等智能端互联网,电信运营商网络、智能硬件厂商等。</p>
2. 生成canvas
使用html2canvas()
函数将指定的DOM生成canvas并转成base64格式,这里注意!,默认情况下生成的canvas清晰度是很差的,需要进行放大倍数处理,这样最终生成出来的pdf清晰度才高。 (附上函数代码)
// name参数为传入的一个需要复制的DOM的id,
function addPdfPage(name) {
var page = new Promise(function(resolve, reject) {
var copyDom = $("#" + name + "");
var width = copyDom.offsetWidth;
var height = copyDom.offsetHeight;
var scale = 2; //放大倍数
$('body').append('<canvas class="canvas_pdf ' + name + '"></canvas>');
var canvas = $('.' + name)[0];
canvas.width = width * scale;
canvas.height = height * scale;
var content = canvas.getContext("2d");
content.scale(scale, scale);
var rect = copyDom.get(0).getBoundingClientRect(); //获取元素相对于视察的偏移量
content.translate(-rect.left, -rect.top); //设置context位置,值为相对于视窗的偏移量负值,让图片复位
html2canvas(copyDom[0], {
dpi: window.devicePixelRatio * 2,
scale: scale,
canvas: canvas,
width: width,
heigth: height,
}).then(function(canvas) {
var contentWidth = canvas.width;
var contentHeight = canvas.height;
var imgWidth = 595.28;
var imgHeight = 592.28 / contentWidth * contentHeight;
pageData.push({ // 将当前页面的数据存入pageData数组
img: canvas.toDataURL('image/jpeg', 1.0),
width: imgWidth,
height: imgHeight
})
resolve();
});
});
return page;
}
3. 输出并保存PDF到本地
遍历pageData数组并调用pdf.addImage()
方法将页面图片逐个添加到pdf对象中。最后调用pdf.save()
方法输出最终生成的pdf。
var pdf = new jsPDF('', 'pt', 'a4'), pageData = [];
addPdfPage('page1').then(function() {
for (i in pageData) {
pdf.addImage(pageData[i].img, 'JPEG', 0, 0, pageData[i].width, pageData[i].height);
}
pdf.save('合同.pdf'); // 输出最终生成的pdf
});
最终效果
最终的效果可以参考下面的效果图,需要代码或者哪里不懂的可以私信我。涉及项目隐私的部分已经做了打码处理,见谅~
最终生成的PDF效果给各位即将踩坑的同猿们几点提醒:
- 如果页面上有表格的话,应该将表格的边框属性重置为0,例如
<table border="0" cellspacing="0" cellpadding="0"></table>
,然后在css上写表格样式。这样输出的表格才完美。- 由于html转成canvas再转成图片,遇到分页的时候会直接被截断,关于这个问题我研究出两种方法,大致参考一下就不附上代码了。
- 将内容分成一个个的DOM写死,这种适合不需要填充内容的需求。
- 先遍历内容里的每一个DOM,存放在临时的DOM里,计算高度超出页面后存放在新的临时DOM里。最后再遍历临时DOM进行输出。这种方法虽然麻烦但是能实现我的需求。
- 最终输出是图片型pdf,而不是文字型pdf,需要高清晰度导致最终文件很大,大约1页1M左右,
所以如果是生成后传到后台的同猿们放弃吧,不仅慢而且很麻烦。不如直接后台生成文字型pdf来的快和方便。(重点是不用计算分页的情况哈哈)
最后感谢观看~
我是@一只有趣的程序猿 我叫大友~