### 前言 最近公司的项目提出一个新需求,需要在前端生成并输出一份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 }); ``` --- ### 最终效果 最终的效果可以参考下面的效果图,需要代码或者哪里不懂的可以私信我。涉及项目隐私的部分已经做了打码处理,见谅~ ![13653457-5cdcd73c425b9b4c](https://peal.cc/static/image/16082148571469584.jpeg) >给各位即将踩坑的同猿们几点提醒: > - 如果页面上有表格的话,应该将表格的边框属性重置为0,例如`<table border="0" cellspacing="0" cellpadding="0"></table>`,然后在css上写表格样式。这样输出的表格才完美。 > - 由于html转成canvas再转成图片,遇到分页的时候会直接被截断,关于这个问题我研究出两种方法,大致参考一下就不附上代码了。 >>1. 将内容分成一个个的DOM写死,这种适合不需要填充内容的需求。 >>2. 先遍历内容里的每一个DOM,存放在临时的DOM里,计算高度超出页面后存放在新的临时DOM里。最后再遍历临时DOM进行输出。这种方法虽然麻烦但是能实现我的需求。 > - 最终输出是图片型pdf,而不是文字型pdf,需要高清晰度导致最终文件很大,大约1页1M左右, 所以如果是生成后传到后台的同猿们放弃吧,不仅慢而且很麻烦。不如直接后台生成文字型pdf来的快和方便。(重点是不用计算分页的情况哈哈) 最后感谢观看~ 我是@一只有趣的程序猿 我叫大友~

前端JS生成PDF的一次踩坑之行

1836 105 前端 2019-02-18

© 2020 peal.cc 粤ICP备2020133024号