Vue项目中使用html2pdf.js打印长页面

Vue项目中使用html2pdf.js打印长页面

1,安装依赖

js 复制代码
npm install html2pdf.js
pnpm add html2pdf.js

2,代码示例

html2canvas的配置项可查看html2canvas.hertzen.com/configurati...

jsPDF的配置项可查看rawgit.com/MrRio/jsPDF...

html2pdf.js的配置项可在官方仓库的readme文档查看github.com/eKoopmans/h...

js 复制代码
import html2pdf from 'html2pdf.js'
import type { jsPDF, jsPDFOptions } from 'jspdf'
import type { Options as Html2CanvasOptions } from 'html2canvas'
type PDFOptions = { name: string; cover?: HTMLDivElement }
export function exportHTMLToPDF2(pages: HTMLDivElement, options: PDFOptions = { name: '模版' }) {
  const { name, cover } = options || {}
​
  const opt = {
    margin: 10,
    filename: name + '.pdf',
    image: {
      type: 'jpeg',
      quality: 0.98
    },
    html2canvas: {
       dpi: 192,
       scale: 2,
       letterRendering: true
    } as Html2CanvasOptions,
    jsPDF: {
      unit: 'mm',
      format: 'a4'
      orientation: 'landscape'
    } as jsPDFOptions,
    pagebreak: { avoid: ['.avoid-break'] }
  }
​
  let worker = html2pdf()
​
  worker = worker
    .set(opt)
    .from(pages)
    .toContainer()
    .toCanvas()
    .toPdf()
    .get('pdf')
    .then((pdf: jsPDF) => {
      pdf.addPage()
    })
​
  worker.save()
}

执行worker.save()之后就可以下载pdf

值得一提的是html2pdf.js有多种处理分页截断的方式github.com/eKoopmans/h...

未配置pagebreak前,表格内容打印出来会被截断

但我想表格的表头如果被截断了就另起一页

只需要简单配置pagebreak属性

js 复制代码
  const opt = {
    pagebreak: { avoid: ['.avoid-break'] }
  }

给每一个表格加上class

这样打印出来的pdf,当dom含有.avoid-break的类名且当前页不能完全放置内容,就会另起一页,如下所示

也可以设置模式为avoid-all,这样会自动添加分页符,以避免跨页面拆分任何元素

js 复制代码
  const opt = {
    pagebreak: { mode: ['avoid-all'] }
  }

这种模式就无须往dom上添加额外的类名,从而实现分页

这两种模式已经满足了我的需求,官方仓库还有更多的分页配置,就没有仔细研究了

有兴趣的可以查看github.com/eKoopmans/h...

3,添加封面

封面比较简单只需要将img元素传入,先生成封面的pdf页

js 复制代码
type PDFOptions = { name: string; cover?: HTMLDivElement }
export function exportHTMLToPDF2(pages: HTMLDivElement, options: PDFOptions = { name: '模版' }) {
  const { name, cover } = options || {}
​
  const opt = {
    margin: 10,
    filename: name + '.pdf',
    image: {
      type: 'jpeg',
      quality: 0.98
    },
    html2canvas: {
      // dpi: 192,
      // scale: 2,
      // letterRendering: true
    } as Html2CanvasOptions,
    jsPDF: {
      unit: 'mm',
      format: 'a4'
      // orientation: 'landscape'
    } as jsPDFOptions,
    pagebreak: { mode: ['avoid-all'] }
  }
​
  let worker = html2pdf()
​
  if (cover) {
    // 封面
    worker = worker
      .set({ ...opt, margin: 0 })
      .from(cover)
      .toContainer()
      .toCanvas()
      .toPdf()
      .get('pdf')
      .then((pdf: jsPDF) => {
        pdf.addPage()
      })
  }
​
  worker = worker
    .set(opt)
    .from(pages)
    .toContainer()
    .toCanvas()
    .toPdf()
    .get('pdf')
    .then((pdf: jsPDF) => {
      pdf.addPage()
    })
​
  worker.save()
}

4,添加水印

添加水印只需要在.then回调中调用jsPDF.setPage()定位到每一页,然后添加文本

js 复制代码
const addWaterMark = (pdf: jsPDF) => {
  const { length } = pdf.internal.pages
  const height = pdf.internal.pageSize.getHeight()
  pdf.setTextColor('#d8d8d8')
  for (let i = 1; i < length; i++) {
    pdf.setPage(i)
    for (let i = 0; i < 6; i++) {
      for (let j = 0; j < 6; j++) {
        pdf.text('watermark', j * 50, height / 5 + i * 50, undefined, 45)
      }
    }
  }
}
​
worker = worker
    .set(opt)
    .from(pages)
    .toContainer()
    .toCanvas()
    .toPdf()
    .get('pdf')
    .then((pdf: jsPDF) => {
        pdf.addPage()
        addWaterMark(pdf)
    })
​
worker.save()

5,踩坑

(1)表格右侧被截取

当我将jsPDF的配置设置如下

css 复制代码
jsPDF: {
    unit: 'mm',
    format: 'a4',
    orientation: 'portrait'
}

会出现表格右侧被截取的情况,像这样

起初以为是因为打印元素在el-dialog组件里面导致的,尝试了一下这个方法,也不起作用

javascript 复制代码
const el = pages.cloneNode(true) as HTMLDivElement
document.body.appendChild(el)

最后在仓库issues里面找到了github.com/eKoopmans/h...

作者的回复是

不过作者已经很久没维护这个项目了,这些问题都没有修复,评论区提供了几种方法

1,改变配置
css 复制代码
jsPDF: {
    unit: 'mm',
    format: 'a4',
    orientation: 'landscape'
}

只要不打印a4,portrait的纸张大小,就不会被截断,不过我的需求就是a4纸张,所以没用这种方法

2,改变打印元素的大小

这位老哥提供的方法虽然没解决我的问题,但提供了思路

打印内容被截取无非就是内容超过了pdf本身的宽度

使用的vxe-grid组件,他的宽度会根据父元素的大小动态计算。但是当我使用html2pdf.js打印的时候,他还是固定的967px,按理说应该自适应为pdf的宽度。

这里猜测是html2pdf.js只是将dom单纯的clone然后生成canvas,因此不会触发vxe-grid组件的数据更新。

作者也说了,插件并没有适应宽度的选项

我这里的解决方案是,调用打印方法之前改变pdfDom的宽度来触发组件更新数据

打印结束之后再把宽度设置为auto(这里需要把函数改为异步)

或者直接给dialog增加固定的宽度,让生成的页面就是a4的宽度,不过这样打印预览页面的宽度就不满足UI图了

(2)文件里全是空白页

出现这种情况一般是canvas画布超过了浏览器限制的最大高度,会导致大型 PDF 在 html2pdf.js 中呈现完全空白

yaml 复制代码
Chrome:
Maximum height/width: 32,767 px
Maximum area: 268,435,456 px (例如, 16,384 x 16,384)
​
Firefox:
Maximum height/width: 32,767 px
Maximum area: 472,907,776 px (例如, 22,528 x 20,992)
​
IE:
Maximum height/width: 8,192 px
Maximum area: N/A
​
IE Mobile:
Maximum height/width: 4,096 px
Maximum area: N/A

解决办法就是分段打印(pages是一个dom数组)

js 复制代码
  for (let i = 0; i < pages.length; i++) {
    worker = worker
      .set(opt)
      .from(pages[i])
      .toContainer()
      .toCanvas()
      .toPdf()
      .get('pdf')
      .then((pdf: jsPDF) => {
        if (i < pages.length - 1) {
          pdf.addPage()
        }
      })
  }
  worker.save()

这样做的缺点也很明显,打印时间更长了

相关推荐
一路向前的月光4 小时前
Vue2中的监听和计算属性的区别
前端·javascript·vue.js
长路 ㅤ   4 小时前
vue-live2d看板娘集成方案设计使用教程
前端·javascript·vue.js·live2d
Jiaberrr5 小时前
Element UI教程:如何将Radio单选框的圆框改为方框
前端·javascript·vue.js·ui·elementui
程序员大金8 小时前
基于SpringBoot+Vue+MySQL的装修公司管理系统
vue.js·spring boot·mysql
道爷我悟了9 小时前
Vue入门-指令学习-v-html
vue.js·学习·html
无咎.lsy9 小时前
vue之vuex的使用及举例
前端·javascript·vue.js
工业互联网专业9 小时前
毕业设计选题:基于ssm+vue+uniapp的校园水电费管理小程序
vue.js·小程序·uni-app·毕业设计·ssm·源码·课程设计
计算机学姐10 小时前
基于SpringBoot+Vue的在线投票系统
java·vue.js·spring boot·后端·学习·intellij-idea·mybatis
twins352011 小时前
解决Vue应用中遇到路由刷新后出现 404 错误
前端·javascript·vue.js
qiyi.sky11 小时前
JavaWeb——Vue组件库Element(3/6):常见组件:Dialog对话框、Form表单(介绍、使用、实际效果)
前端·javascript·vue.js