JS 如何将 HTML 页面导出为多页 PDF

本文介绍了一个使用jsPDF库将HTML页面转换为多页PDF的方法。通过计算页面高度并利用html2canvas渲染技术,实现了自动分页及图片数据的添加。此方案适用于前端开发者将网页内容批量导出为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][0] / pdfFormat[format][1];
      
          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', 0, 0,pdfFormat[format][0],pdfFormat[format][1]/pageHeight*contentHeight);
      
              pdf.save(pdfName);
      
          }
    • 若前者大于后者,则需要分页:

      1. 先求出页数 count ,循环次数为 count-1
      2. 设置 pagebackground 偏移
      3. 接着渲染page为canvas获取图片url数据 pageData 插入 pdf 对象中,pdf 对象执行 addPage() 函数
      4. 添加最后一页,因为最后一页高度可能和pageHeight大小不一致,因此设置 page 的高度为计算的得出的 lastPageHeight ,渲染成canvas获取图片数据插入 `pdf
      5. 导出保存,源码如下:

            var index = 0;
            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', 0, 0,pdfFormat[format][0],pdfFormat[format][1]);
        
                        if(i + 1 < count){
                            pdf = pdf.addPage();
                            addPage(i + 1, 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', 0, 0,pdfFormat[format][0], pdfFormat[format][1]/pageHeight*lastPageHeight);
        
                        document.body.removeChild(page);
        
                        onSuccess && onSuccess();
        
                        pdf.save(pdfName);
        
                    }
                });
        
        
            });
  • 最后,body 删除名为 pagediv

写在后面

因为个人能力不足,这个方法在导出页数过多的PDF的时候(例如20页以上)会有明显的浏览器卡顿。
而且没有进行什么优化,导出的PDF有点模糊,这个大佬们若有解决方法可以私信哈。

还有这方法有点 dirty hack,其实最 smart hack 的方法是,直接js执行函数 window.print() ,导出的 PDF 效果也十分好,毕竟是浏览器的官方实现。

评论 21
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值