「这是我参与2022首次更文挑战的第4天,活动详情查看:2022首次更文挑战」
前言
最近这段时间接受了一个业务需求,实现网页内容的pdf预览打印,感觉非常有必要总结一篇文章作为文档来记录一下,以防止大家碰到相同的问题。
pdf预览
内容分页
内容分页?为什么要分页?直接将页面转换为图片,插入到pdf中,不就完事大吉了么。好吧,我怂了,客户爸爸的需求还是要努力实现的。
其实内容分页大致有两种方式:
-
将内容导出一张图片,然后通过偏移不同的位置插入到不同的页面中
-
将内容提前按照页面大小切割好,分别插入对应的页面中
两种方式用到的api基本一致,区别只是思路,我们下面针对第二种方式进行详细的说明。
- 将网页导出为图片
这里用到了 html2canvas,具体api使用,可以通过传送门前往查看。
html2canvas(container, {
windowWidth: container.offsetWidth, // 计算元素宽高,防止出现空白页
windowHeight: container.scrollHeight,
allowTaint: false, // 允许不同源的图片污染画布
useCORS: true, // 开启跨域,解决图片不显示问题
scale: 2
}).then((canvas) => { });
细心的小伙伴可能注意到了,我采用了2倍率,这样做的目的是什么呢?其实就是为了防止导出的pdf内容失真,如果直接使用1倍图,这样有可能出现失真的情况。
- 将图片进行切割
imgContainer.getContext('2d')
.putImageData(ctx.getImageData(0, renderedHeight, canvasWidth, currentImgHeight), 0, 0);
const currentPageHeight = Math.min(A4_HEIGHT, A4_WIDTH * currentImgHeight / canvasWidth);
pdf.addImage(imgContainer.toDataURL('image/jpeg', 1.0), 'JPEG', 19, 21, A4_WIDTH, currentPageHeight);
这里需要注意的是,需要进行宽高的换算,宽高比是A4纸的宽高比为准进行计算。特别是对于最后一页来说有其特殊,可能会存在不足一页的情况,这时如果不进行处理,可能出现竖向拉伸的情况,所以计算页面的像素高度时候,需要取A4纸高度和图片的实际高度的最小值。
- pdf添加导出
pdf.addPage();
pdf.save(`pdf文件名称_${Date.now()}.pdf`);
到这里其实剩下的就很简单了,只需要调用相关的api将pdf生成,然后导出即可。这里有个小的点需要特殊提一下,为了防止每次下载文件名都相同,下载时在文件后面添加一些不是很好看的数字内容,这里可以使用时间戳处理一下。
文字切割
上面的方式是实现了pdf内容的预览,也实现了pdf内容的下载,但是,其实还是有问题的。因为不知道每一页的分割点,所以就有可能在任何位置进行切割,这也就容易出现一种情况:将文字或者图片内容进行了切割。
我经过研究之后,找到一种解决这个问题的方法,并经过线上测试发现完全可行。
在写代码的时候,先给每一个最小不可分割的块添加一个标记(这个标记可以根据个人喜好自定义),然后在生成代码之前进行动态处理。
因为涉及到保密问题,这里就详细介绍一下处理的方法,然后粗略展示一下大致的处理代码。初始高度设置为0,然后不断往上添加带有自定义标记的块的高度,并拿这个高度跟A4纸的高度进行比较,一旦发现内容大于A4纸的高度,那么说明如果不换页的话,这个内容就会被切割。所以,这时我们通过插入剩余内容的空白块,这这块内容顶到下一页,这样就可以解决内容被切割的问题了。
rows.forEach((row, index) => {
const rowHeight = row.offsetHeight;
if (rawPageHeight + rowHeight > pageHeight - 30) {
// 插入空白元素
const blankHeight = pageHeight - rawPageHeight;
const blankElement = document.createElement('div');
blankElement.style.width = '100%';
blankElement.style.height = `${blankHeight}px`;
row.parentElement.insertBefore(blankElement, row);
// 开启新的页面
rawPageHeight = rowHeight;
} else {
rawPageHeight += rowHeight;
}
});
在这里,在客户使用过程中还碰到了一点小问题也跟大家说一下,防止大家踩坑。就是随着页数的增多,页面切割位置出现细微的偏差,导致没有按照想想中的位置进行切割。
解决方法:在计算页面高度和块的高度时保留两位小数,页面的高度向下取整,块的高度向上取整(通过先乘以100,再除以100,来保留两位小数)。这种方法经过客户的大量使用测试,目前没有问题,说明完全可行。
好的,我们到这里就结束了。如果有任何疑问或者是更好的解决方案,欢迎大家在下面留言进行交流。