实现前端页面局部转 pdf 及 打印 加分页防止内容截断

1,585 阅读3分钟

核心库

html2Canvas

jspdf

print-js

实现思路 1、html2Canvas 可以将特定容器标签的内容转换为图片,通过特定API 转换为对应的base64 格式的数据

2、jspdf 库 调用核心API addImage() 及 save() 方法可以将base64 的数据转换为pdf 保存到本地

3、print-js 打印的核心库,直接传入数据实现打印,不需要再重新刷新页面

重点思考 !!!分页内容防截断 由于要转pdf 和 打印 的管理报告不是一页范围内的,因此需要考虑分页,关于分页转pdf 有很多分享,可是忽略了一点,分页的时候一定不能把有些内容给截断 ,比如一个表格,一个图片,或者报告中的echars 图标等,怎么办?刚开始一点思路没有后,后来简书上看了一篇文章打开了思路。 www.jianshu.com/p/9f6ebc0e0… ,但最后没有采用,因为这个思路还是有点绕,而且需要基本所有内容标签都加上对应防截断tag,其实是没必要的。

解决思路!!!!

使用html2Canvas + jspdf 实现前端页面转pdf ,改变pdf 分页格式的唯一思路是 先改变转 pdf 的 dom (因为我在没有了解清楚的时候也在,dom 转换后pdf 分页的时候动过心思,不成功因为一切都是基于一个完整的base64 格式的 image 数据)

将确保不被分割的dom 元素 加上特定的tag 标签,判断此dom 元素的最上面和最下面是否在同一页中,如果不在说名不处理就会被截断,怎么处理,不能被截断的元素上方插入对应的空白快占位,已达到将当前元素放到下一页的目的(当前页面高度 - dom元素最上方位置 = 需要插入空白块的高度)

关于局部分页打印

正常将dom 元素按照A4 纸的标准进行分页后,dom 转pdf 的数据在打印时可以自动分页,万万没想到,预览状态下打印的一页内容,比设计的一页内容多,导致打印的时候元素又被截断,分析的原因是电脑里A4纸的标准和我设定的不一样,我设定的高度不够,因此试着调试了一下高度,解决了,还有待多个电脑测试。 ———————————————— 版权声明:本文为CSDN博主「小智大愚」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。 原文链接:blog.csdn.net/Sandy_zhi/a…

convertPdf(isPrint) {
      let title = "你想要的保存pdf文件的名字"
      // 如果这个页面有左右移动,canvas 也要做响应的移动,不然会出现canvas 内容不全
      const xOffset = window.pageXOffset
      // 避免笔下误 灯下黑 统一写
      const A4_WIDTH = 592.28
      // const A4_HEIGHT = 841.89
      const A4_HEIGHT = 880
      let printDom = document.querySelector('#printBox')
      // 根据A4的宽高计算DOM页面一页应该对应的高度
      let pageHeight = printDom.offsetWidth / A4_WIDTH * A4_HEIGHT
      // 将所有不允许被截断的元素进行处理
      let wholeNodes = document.querySelectorAll('.whole-node')
      for (let i = 0; i < wholeNodes.length; i++) {
        //1、 判断当前的不可分页元素是否在两页显示
        const topPageNum = Math.ceil((wholeNodes[i].offsetTop) / pageHeight)
 
        const bottomPageNum = Math.ceil((wholeNodes[i].offsetTop + wholeNodes[i] + offsetHeight) / pageHeight)
 
        if (topPageNum !== bottomPageNum) {
          //说明该dom会被截断
          // 2、插入空白块使被截断元素下移
          let divParent = wholeNodes[i].parentNode
          let newBlock = document.createElement('div')
          newBlock.className = 'emptyDiv'
          newBlock.style.background = '#fff'
 
          // 3、计算插入空白块的高度 可以适当流出空间使得内容太靠边,根据自己需求而定
          let _H = topPageNum * pageHeight - wholeNodes[i].offsetTop
          newBlock.style.height = _H + 30 + 'px'
          divParent.insertBefore(newBlock, wholeNodes[i])
        }
 
        // 以上完成dom层面的分页 可以转为图片进一步处理了
 
        html2Canvas(printDom, { height: printDom.offsetHeight, width: printDom.offsetWidth, scrollX: -xOffset, allowTaint: true }).then(canvas => {
          //dom 已经转换为canvas 对象,可以将插入的空白块删除了
          let emptyDivs = document.querySelectorAll('.emptyDiv')
          for (let i = 0; i < emptyDivs.length; i++) {
            emptyDivs[i].parentNode.removeChild(emptyDivs[i])
          }
          // 有一点重复的代码
          let contentWidth = canvas.width
          let contentHeight = canvas.height
          let pageHeight = contentWidth / A4_WIDTH * A4_HEIGHT
          let leftHeight = contentHeight
          let position = 0
 
          let imgWidth = A4_WIDTH
          let imgHeight = A4_WIDTH / contentWidth * contentHeight
          let pageData = canvas.toDataURL('image/jpeg', 1.0)
          if (isPrint) {
            //如果是打印,可以拿着分号页的数据 直接使用
            printJs({ printable: pageData, type: 'image', base64: true, documentTitle: '\u200E' })
            return
          }
          //计算分页的pdf 
          let PDF = new JsPDF('', 'pt', 'a4')
          if (leftHeight <= pageHeight) {
            PDF.addImage(pageData, 'JPEG', 0, 0, imgWidth, imgHeight)
          } else {
            while (leftHeight > 0) {
              PDF.addImage(pageData, 'JPEG', 0, position, imgWidth, imgHeight)
              leftHeight -= pageHeight
              position -= A4_HEIGHT
              if (leftHeight > 0) {
                PDF.addPage()
              }
            }
          }
          PDF.save(title + '.pdf')
        })
      }
    },