使用jspdf和html2canvas将当前网页保存为pdf

首先引入组件

npm install --save html2canvas
npm install jspdf --save

在需要导出pdf的页面引入

import { jsPDF } from "jspdf";
import html2canvas from 'html2canvas';

将当前页面保存为pdf,有两种处理方式,一种分页,一直分页

不分页:// 滚动到顶部,避免打印不全

      document.getElementById("pdfcontent").scrollTop = 0;
      //设置放大倍数会存在问题,后面的不少图片会丢失/最后的文字会被截断
      var scale = 2;
      html2canvas(document.getElementById("pdfcontent"), {
        allowTaint: true,
        scale: scale,//放大倍数
        dpi: 300,
      }).then(function (canvas) {
        //画布大小
        let contentWidth = canvas.width;
        let contentHeight = canvas.height;

        // 将canvas转为base64图片
        var pageData = canvas.toDataURL('image/jpeg', 1.0);

        // 设置pdf的尺寸,pdf要使用pt单位 已知 1pt/1px = 0.75   pt = (px/scale)* 0.75
// /2是因为上方放大了2倍 var pdfWidth = (contentWidth + 10) / 2 * 0.75; var pdfHeight = (contentHeight + 500) / 2 * 0.75; // 设置内容图片的尺寸,img是pt单位 var imgWidth = pdfWidth; var imgHeight = (contentHeight / 2 * 0.75); //初始化jspdf 第一个参数方向:默认''时为纵向,第二个参数设置pdf内容图片使用的长度单位为pt,第三个参数为PDF的大小,单位是pt const PDF = new jsPDF('', 'pt', [pdfWidth, pdfHeight]); PDF.addImage(pageData, 'JPEG', 0, 0, imgWidth, imgHeight); PDF.save('test.pdf'); })

分页

html2canvas(document.getElementById("pdfcontent"), {
        allowTaint: true,
        height: document.getElementById("pdfcontent").scrollHeight,//
        width: document.getElementById("pdfcontent").scrollWidth//为了使横向滚动条的内容全部展示,这里必须指定
      }).then(function (canvas) {
        let contentWidth = canvas.width;
        let contentHeight = canvas.height;
        //一页pdf显示html页面生成的canvas高度;
        let pageHeight = contentWidth / 595.28 * 841.89;
        //未生成pdf的html页面高度
        let leftHeight = contentHeight;
        //pdf页面偏移
        let position = 0;
        //a4纸的尺寸[595.28,841.89],html页面生成的canvas在pdf中图片的宽高
        let imgWidth = 595.28;
        let imgHeight = 595.28 / contentWidth * contentHeight;
        let pageData = canvas.toDataURL('image/jpeg', 1.0);
        let PDF = new jsPDF('', 'pt', 'a4');
        //有两个高度需要区分,一个是html页面的实际高度,和生成pdf的页面高度(841.89)
        //当内容未超过pdf一页显示的范围,无需分页
        if (leftHeight < pageHeight) {
          PDF.addImage(pageData, 'JPEG', 20, 0, imgWidth, imgHeight);
        } else {
          while (leftHeight > 0) {
            PDF.addImage(pageData, 'JPEG', 20, position, imgWidth, imgHeight);
            leftHeight -= pageHeight;
            position -= 841.89;
            if (leftHeight > 0) {
              PDF.addPage();
            }
          }
        }
        PDF.save('test.pdf');
      })

这两种方式都有坑,先说分页,因为 A4 是有固定页面大小的(宽:595.28, 高:841.89),保存的内容高度(缩放至A4宽度)超过该长度,则会出现截断问题。

 

 

 

 分割的位置不固定,会直接将文字、图片分割成两半;这个只能自己遍历标签,判断能否分割,增加偏移量这种方式处理了,比较麻烦,具体页面具体分析,可自行尝试。

 

最好的方式就是不分页了,没有分页需求,也没有打印需求,没必要分页;不分页,打印之后页面会直接缩放变得很小,而且也会存在直接截断文字、图片的问题。

不分页,也有坑,我这边碰到的是放大的时候,会存在图片丢失的问题,转pdf后显示的是空白,查了下资料,原因如下:

这种情况是因为html2Canvas内部又对节点内的图片进行了一次请求,而此次请求不会管加载是否完毕,将直接转换为canvas生成图

怎么处理呢,查到了一个方案:

在html2canvas执行前先替换所有图片转换为Blob,这种方式不会出现图片缺失的情况。

试了下:

getUrlBlob (url, callback) {
      const str = url.substring(0, 50)
      // 避免重复加载
      if (str.includes('blob:')) {
        return callback(false)
      }
      // 避免img未有src属性的情况,导致未返回
      if (!str) {
        return callback(false)
      }
      let canvas = document.createElement("canvas")
      let ctx = canvas.getContext("2d")
      let img = new Image
      img.crossOrigin = 'Anonymous'
      img.src = url
      img.onload = function () {
        canvas.height = img.height
        canvas.width = img.width
        ctx.drawImage(img, 0, 0)
        try {
          canvas.toBlob((blob) => {
            callback(URL.createObjectURL(blob))
          })
        } catch (err) {
          callback(img.src)
          console.error('转换失败,使用原图', err)
        }
        canvas = null
      }
    },
    imgToBlob (el, success, error) {
      let imgArr = el.querySelectorAll('img')
      imgArr = Array.from(imgArr)
      let i = 0
      if (imgArr[0]) {
        let timer = setInterval(() => {
          clearInterval(timer)
          if (imgArr.length !== i) {
            error && error('超时')
          }
        }, 30000);
        [...imgArr].forEach((dom) => {
          //dom.src = `${dom.src}?${Math.random()}`;
          this.getUrlBlob(dom.src, ((blob) => {
            if (blob !== false) {
              dom.src = blob
            }
            i++
            // 校验是否全部替换完毕
            if ((imgArr.length) === i) {
              clearInterval(timer)
              this.IsRender(el, success, error)
            }
          }))
        })
        return
      }
      this.IsRender(el, success, error)
    },
    IsRender (el, success, error) {
      setTimeout(() => {
        var width = el.offsetWidth; //获取dom 宽度
        var height = el.offsetHeight; //获取dom 高度
        var canvas = document.createElement("canvas"); //创建一个canvas节点
        // 兼容清晰度
        //const scale = window.devicePixelRatio; //定义任意放大倍数 支持小数
        console.log("window.devicePixelRatio:" + window.devicePixelRatio)
        const scale = 1; //定义任意放大倍数 支持小数
        canvas.width = width * scale; //定义canvas 宽度 * 缩放
        canvas.height = height * scale; //定义canvas高度 *缩放
        var context = canvas.getContext('2d');
        // 去图片锯齿 官网
        context.mozImageSmoothingEnabled = false;
        context.webkitImageSmoothingEnabled = false;
        context.msImageSmoothingEnabled = false;
        context.imageSmoothingEnabled = false;
        // options配置
        var opts = {
          scale: scale,
          canvas: canvas,
          logging: false,
          //width: width,
          //height: height,
          //dpi: 300,
          useCORS: true,
          //backgroundColor: "transparent",
          allowTaint: false,
        };
        // 进行截图
        html2canvas(el, opts)
          .then(canvas => {
            try {
              //画布大小
              let contentWidth = canvas.width;
              let contentHeight = canvas.height;

              // 将canvas转为base64图片
              var pageData = canvas.toDataURL('image/jpeg', 1.0);

              // 设置pdf的尺寸,pdf要使用pt单位 已知 1pt/1px = 0.75   pt = (px/scale)* 0.75
              var pdfWidth = (contentWidth + 10) / 2 * 0.75;
              var pdfHeight = (contentHeight + 500) / 2 * 0.75;

              // 设置内容图片的尺寸,img是pt单位 
              var imgWidth = pdfWidth;
              var imgHeight = (contentHeight / 2 * 0.75);

              //初始化jspdf 第一个参数方向:默认''时为纵向,第二个参数设置pdf内容图片使用的长度单位为pt,第三个参数为PDF的大小,单位是pt
              const PDF = new jsPDF('', 'pt', [pdfWidth, pdfHeight]);

              PDF.addImage(pageData, 'JPEG', 0, 0, imgWidth, imgHeight);
              PDF.save('test.pdf');
            } catch (err) {
              error && error(err)
            }
          })
          .catch(err => {
            error && error(err)
          })
      }, 500)
    }

试了下,没什么效果,放弃了,如果有好的方案请大佬指导分享,最后我是采用不分页,不放大的方式来生成的,代码如下:

// 滚动到顶部,避免打印不全
      document.getElementById("pdfcontent").scrollTop = 0;
      html2canvas(document.getElementById("pdfcontent"), {
        useCORS: true,
        dpi: 300,
      }).then(function (canvas) {
        var context = canvas.getContext('2d');
        //关闭抗锯齿
        context.mozImageSmoothingEnabled = false;
        context.webkitImageSmoothingEnabled = false;
        context.msImageSmoothingEnabled = false;
        context.imageSmoothingEnabled = false;
        //画布大小
        let contentWidth = canvas.width;
        let contentHeight = canvas.height;
        // 将canvas转为base64图片
        var pageData = canvas.toDataURL('image/jpeg', 1.0);
        // 设置pdf的尺寸,pdf要使用pt单位 已知 1pt/1px = 0.75   pt = (px/scale)* 0.75
        var pdfWidth = (contentWidth + 10) * 0.75;
        var pdfHeight = (contentHeight + 500) * 0.75;

        // 设置内容图片的尺寸,img是pt单位 
        var imgWidth = pdfWidth;
        var imgHeight = (contentHeight * 0.75);

        //初始化jspdf 第一个参数方向:默认''时为纵向,第二个参数设置pdf内容图片使用的长度单位为pt,第三个参数为PDF的大小,单位是pt
        const PDF = new jsPDF('', 'pt', [pdfWidth, pdfHeight]);

        PDF.addImage(pageData, 'JPEG', 0, 0, imgWidth, imgHeight);
        PDF.save('test.pdf');
      })

 

 

posted @ 2021-08-18 09:25  软件开发-汪七北  阅读(712)  评论(0编辑  收藏  举报