zoukankan      html  css  js  c++  java
  • 使用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,//
             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,
              //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');
          })
    

     

  • 相关阅读:
    嵌入式工程师C语言面试常见的0x10个问题
    C语言初学者网站推荐
    strlen和sizeof
    基于Docker搭建GitLab和Maven私服
    linux暴露端口可以被外部访问
    MySQL新增用户及赋予权限
    Docker添加域名解析
    Netstat 网络命令详解
    Mysql索引太长导致同步数据结构失败解决方法
    完美解决Cannot download "https://github.com/sass/node-sass/releases/download/binding.nod的问题
  • 原文地址:https://www.cnblogs.com/wanggang2016/p/15155344.html
Copyright © 2011-2022 走看看