0%

HTML转PDF踩坑指南

近期的一个的需求,HTML 生成 PDF,整个过程让前端来实现,因为后端大神实在太忙.

网上搜索的一番,看到 有两种实现方式:

  1. 手动写入内容到 PDF, jsPDF支持文字图片写入然后直接生成 PDF,可以结合antoTable 更容易的展示表格
  2. 使用 html2pdf 这个库,其实现机理是 DOM => IMAGE => PDF

针对我们的实际情况,我们是要根据打印一个特定的页面,但是同时还会增加一些只会在 PDF 当中展示的内容,其实相当于新写了一个页面专门用于打印,打印的内容包括 chart 和 一些复杂的排版内容,给予这两点,第一种方法就被舍弃了.

第二种方法实现的过程:

  1. 我们使用的 react,所以我们新写了一个组件,在点击打印的时候载入,设置为不可见
  2. 在页面 dom 渲染结束后调用 html2pdf 库,调用类似
    html2pdf().set(option).from(element).save()

下面就说一下使用后出现的问题和解决思路:

  • 我们的 table 过长,column 非常多,这就需要非常宽的页面支持,最后我们使用的自定义页面宽度
  • PAGE-BREAK. 虽然支持 css 的 page-break,但是碰到 table 时候,就会直接从tr 断开,下一页让人无法对应每一列的内容.所以这里需要自己用 js 处理. 思路: 计算 table 在当前页面可以放多少行,然后就在边缘那一行增加一个 tr ,把 thead 的内容放进去,并在 tr 上加上 break-before 的类,这样就可以实现table分页并且thead也能在下一页继续使用
  • 分页: 这个可以在最后使用 setFont 给每一页写入页码```javascript worker.get(‘pdf’).then(pdf=>{ const totalPage = pdf.internal.getNumberOfPages(); for(let j=1;j<totalPage; j++){ pdf.setPage(j); pdf.setFontSize(20); pdf.text(page ${j})
    }
    })
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17

- canvas超出限制,因为这个插件的机制就是先用canvas画出页面的,所以canvas会限制本插件的使用,canvas有一个最大像素的限制,一旦超过这个临界值,就会出现空白页面.使用的时候经常超过15页左右就出现这种问题,所以这个问题很严重.目前能找到的方法就是分page去渲染,就是把原来一整个页面分成几块分次打印.```javascript
const pages = [...] // 分块的html list
let worker = html2pdf()
.set(option)
.from(pages[0])
.toPdf()
for(let i=1; i< pages.length;i++){
worker = worder.get('pdf')
.then(pdf=>{
pdf.addPage()
})
.from(pages[i])
.toContainer()
.toCanvas()
.toPdf()
}

使用这种方案仍然有两个 issue:

  1. 渲染时间偏长,二十页左右的要两三分钟,这可能和 pdf 要重复执行 canvas 有关
  2. 但分的小块里面,其大小仍然超过 canvas size 还是会出现空白页

这个方案目前还没找到解决方案

看来,做 PDF 这个事情还得找后端去做.