美文网首页让前端飞
前端JS生成PDF的一次踩坑之行

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

作者: 叫我汪汪 | 来源:发表于2019-02-18 22:48 被阅读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再转成图片,遇到分页的时候会直接被截断,关于这个问题我研究出两种方法,大致参考一下就不附上代码了。
    1. 将内容分成一个个的DOM写死,这种适合不需要填充内容的需求。
    2. 先遍历内容里的每一个DOM,存放在临时的DOM里,计算高度超出页面后存放在新的临时DOM里。最后再遍历临时DOM进行输出。这种方法虽然麻烦但是能实现我的需求。
    • 最终输出是图片型pdf,而不是文字型pdf,需要高清晰度导致最终文件很大,大约1页1M左右,
      所以如果是生成后传到后台的同猿们放弃吧,不仅慢而且很麻烦。不如直接后台生成文字型pdf来的快和方便。(重点是不用计算分页的情况哈哈)

    最后感谢观看~
    我是@一只有趣的程序猿 我叫大友~

    相关文章

      网友评论

        本文标题:前端JS生成PDF的一次踩坑之行

        本文链接:https://www.haomeiwen.com/subject/lxltyqtx.html