天天看点

JS 如何将 HTML 页面导出为多页 PDF前话原理写在后面

前话

之前写了一篇博文 JS 如何将 HTML 页面导出为 PDF 。

当时只是自己有个需求,只是导出一页PDF,写个了示例。

之后就有同学私信我问我怎么导出多页PDF。好吧,其实这些看文档画画图自己是可以写出来的。以后也可能有转换HTML导出多页的PDF需求,就决定写一个库

renderPDF

吧。

地址在这里:https://github.com/pwcong/how-transform-html-into-multipage-pdf

原理

因为依赖了

jsPDF

这个库,所以导出 PDF 就查阅

jsPDF

的相关文档。

通过查阅文档可知,

jsPDF

提供添加新一页的 API 函数

addPage()

,因此我考虑给过长的div分页就围绕它进行思考。

这里说明一下大概步骤:

  • 首先将要导出PDF的 div (这里命名为

    content

    ) 渲染成canvas,获取该canvas的图片url数据

    imgData

  • 新建div命名为

    page

    插入到

    body

    中:
    1. 设置style、class、id均与

      content

      一致,然后再继续下面的步骤
    2. 设置宽度为

      content

      的宽度,高度设置为计算后得出的pageHeight,这里源码如下:
      var pdfProportion = pdfFormat[format][] / pdfFormat[format][];
      
          var contentHeight = content.offsetHeight;
          var contentWidth = content.offsetWidth;
      
          var pageHeight = contentWidth / pdfProportion;
          var pageWidth = contentWidth;
                 
      其中

      pdfFormat

      为预定义对象,

      format

      为输入的PDF格式
    3. 设置其背景为

      url(imgData)

  • 接着判断contentHeight和pageHeight
    • 若前者小于后者说明不用分页,直接添加图片数据导出,源码如下:
      if(contentHeight < pageHeight){
      
              pdf.addImage(imgData, 'JPEG', , ,pdfFormat[format][],pdfFormat[format][]/pageHeight*contentHeight);
      
              pdf.save(pdfName);
      
          }
                 
    • 若前者大于后者,则需要分页:
      1. 先求出页数

        count

        ,循环次数为

        count-1

      2. 设置

        page

        background

        偏移
      3. 接着渲染page为canvas获取图片url数据

        pageData

        插入

        pdf

        对象中,

        pdf

        对象执行

        addPage()

        函数
      4. 添加最后一页,因为最后一页高度可能和

        pageHeight

        大小不一致,因此设置

        page

        的高度为计算的得出的

        lastPageHeight

        ,渲染成canvas获取图片数据插入

        `pdf

      5. 导出保存,源码如下:
        var index = ;
            var count = parseInt(contentHeight / pageHeight);
        
            var page = document.createElement("div");
        
            page.style.position = "absolute";
            page.style.width = contentWidth + "px";
            page.style.height = pageHeight + "px";
            page.style.backgroundImage = "url(" + imgData + ")";
            page.style.backgroundRepeat = "norepeat";
        
            document.body.appendChild(page);
        
            function addPage(i, onFinished){
        
                page.style.backgroundPositionY = -pageHeight * i + "px";
        
                html2canvas(page, {
        
                    onrendered: function(canvas) {
        
                        var pageData = canvas.toDataURL('image/jpeg');
        
                        pdf.addImage(pageData, 'JPEG', , ,pdfFormat[format][],pdfFormat[format][]);
        
                        if(i +  < count){
                            pdf = pdf.addPage();
                            addPage(i + , onFinished);
                        }
                        else{
                            onFinished()
                        }
        
                    }
                });
        
            }
        
            addPage(index, function(){
        
                page.style.backgroundPositionY = -pageHeight * count + "px";
        
                var lastPageHeight = contentHeight % pageHeight;
                page.style.height = lastPageHeight + "px";
        
                html2canvas(page, {
        
                    onrendered: function(canvas) {
        
                        var pageData = canvas.toDataURL('image/jpeg');
        
                        pdf = pdf.addPage();
                        pdf.addImage(pageData, 'JPEG', , ,pdfFormat[format][], pdfFormat[format][]/pageHeight*lastPageHeight);
        
                        document.body.removeChild(page);
        
                        onSuccess && onSuccess();
        
                        pdf.save(pdfName);
        
                    }
                });
        
        
            });
                   
  • 最后,

    body

    删除名为

    page

    div

写在后面

因为个人能力不足,这个方法在导出页数过多的PDF的时候(例如20页以上)会有明显的浏览器卡顿。

而且没有进行什么优化,导出的PDF有点模糊,这个大佬们若有解决方法可以私信哈。

还有这方法有点 dirty hack,其实最 smart hack 的方法是,直接js执行函数

window.print()

,导出的

PDF

效果也十分好,毕竟是浏览器的官方实现。