业务需求
网页html生成A4大小分页的pdf,翻遍了整个互联网发现没有很系统的整理与分析,甚至对jsPDF的解析也没有几篇。遇到过几次,用的比较多,完成代码编写后特此整理分析,自我记录。
业务难点
- 存在图片/组件/文字被分割的现象,即分页处理
- 包括页头、页脚、上下安全间隔的情况
- 富文本分页情况
处理思路
通过深度搜索优先遍历,从顶部遍历需要转换的HTML节点, 并将节点分为三种情况进行处理(1. 普通节点。2. 需要进行分页处理并且内部可能包含也需要分页处理子节点的节点。3. 需要进行分页内部不包含需要分页处理的节点,即深度搜索的终点节点),通过从高到低遍历维护一个分页数组pages,该数组记录每一页的起始位置,如:pages[0] 对应 第一页起始位置, pages[1] 对应 第二页起始位置
图解如下:
通过深度遍历后得出每页起始位置的数组,遍历数组,通过jspdf的addImage接口对canvas进行画面截取,由于addImage只能固定位置的左上角起始点,不能进行非常精确的上下定位截取(下一节会详解addImage
),会造成截取多余的内容(如上图页面1中pages[1] 下方的内容会和 页面2 中 pages[1] 下方的内容会一样(除长度外),而页面1中pages[1] 下方的内容是多余的(是属于页面2的内容))因此需要对页面不需要的内容 使用jspdf的addBlank进行空白遮挡处理
。
jsPDF.addImage
详解
官方文档链接addImage - Documentation (artskydj.github.io)
需要注意的点是坐标(x,y) 的取值, (x,y)对应的是添加图片的左上角取值,宽高则是根据转化成canvas的宽高取值,图解如下
因此在对一个长图片进行截取时,往往将y值设未负数,让需要截取图片的起始位置落于当前的pdf页面内,在当前案例下,每一页的图片摆放坐标y = -pages[i]
jsPDF.rect
详解
文档链接 context2d - Documentation (artskydj.github.io)
该接口的参数 (x,y)坐标、宽高 与addImage接口的一致
当前pdf页需要的内容的高度为 pages[i] - pages[i-1]
, 除去顶部这个高度外以下的内容都是不需要的,因此得到每一页添加空白的y坐标值为- pages[i] - pages[i-1]
,高度h为一页pdf的高度(此处为A4页的高度) - pages[i] - pages[i-1]
,宽度为A4宽度,x为0, 图解如下:
深度优先遍历三种类型的节点
通过深度优先遍历操作,可以从高到低去遍历需要进行跨页判断的元素,检测是否跨页,并记录分页点,从而避免跨页问题。
1. 普通节点
当遍历到普通节点,即不需要进行分页判断的节点时,只需要进行 2步操作:
- 当前节点距离顶部的高度 - pages最后一位元素的值(即上一页的分界点)得出的差值是否 大于 页面的高度 , 如果大于,则证明当前节点已经跨页,进行操作pages.push(pages[pages.length - 1] + 一页PDF的高度)
- 对子节点进行深度遍历
2. 需要进行跨页判断,且内部也含有 可能跨页/需要进行跨页判断
的节点
当元素进行到该类型的节点时, 需要进行3步操作:
- 需要进行与普通节点第一步相同的判断
- (检测当前节点距离顶部的距离 + 节点自身的高度) 是否大于 (
pages
最后一位元素(即当前页 顶部位置) + 一页PDF的高度(当前指A4的高度))
如果条件为真,则证明该节点属于跨页元素,距离页面顶部距离的值top
是分页点,往pages
中
push top
- 且由于内部还存在需要进行跨页检测的节点,因此需要对子节点进行深度遍历
3. 需要进行跨页判断,但内部不含有可能跨页/需要进行跨页判断
的节点, 即深度终点
该节点只需要进行 内部含有可能跨页/需要进行跨页判断
的节点 的第一第二步操作, 由于内部不再含有,因此不需要遍历子节点,为搜索的叶子节点。
html2Canvas生成图片模糊导致导出的PDF也模糊的问题
通过 scale
参数, 对canvas
进行等比放大,可以使canvas
生成的图片更清晰。
代码如下
1 | js复制代码// 将元素转化为canvas元素 |
样例及代码
gitee仓库: output_pdf_demo: jsPDF + html2canvas , 网页HTML导出A4格式PDF 处理分页切割问题 (gitee.com)
npm install
& npm run serve
即可运行
分页效果:
富文本分页:
table行分页:
组件分页:
样例注意事项
样例比上述讲的情况内,引入了页眉、页脚、还有上下左右间距的情况,图解如下:
需要做的额外处理:
- 图片摆放的Y坐标由原来的
-pages[i]
变成了baseY + 页头元素高度 - pages[i]
- 中间实际内容部分与页眉/页脚之间的边距也需要进行遮白处理
- 内容的高度才为PDF页面的实际高度,判断分页的依据应该以内容高度为准
- 富文本文字的分页处理
核心代码
1 | js复制代码import jsPDF from 'jspdf'; |
参考文档及博文
jsPDF - Documentation (artskydj.github.io)
配置型 | HTML2CANVAS 中文文档 (allenchinese.github.io)
【原创】jspdf+html2canvas生成多页pdf防截断处理 - 简书 (jianshu.com)
本文转载自: 掘金