前端打印、生成PDF的那些事

前端打印、生成PDF的那些事

概要

最近项目中经常做到前端打印、生成PDF功能这篇文章主要是记录下遇到的问题、以及实现方案

使用到的库

  • html2canvas
    npm i html2canvas
    yarn add html2canvas
  • jspdf
    npm i jspdf
    yarn add jspdf
  • print-js
    npm i print-js
    yarn add print-js

打印

这个功能我们使用到print-js库来实现

javascript 复制代码
print({
	 printable: 'print-template',//需要打印的元素id
     type: "html",// 打印内容的类型
     targetStyles: ["*"],
     scanStyles: false, // 保留样式、这里需要设置为false
     style: getPrintStyle(), // 此处接受一个 css 样式字符串
})
// 将scanStyles 设置为 false 是为了更好的解决打印时出现的内容被截断的问题、此处设置为false,然后我们就可以自己写样式来定义打印模板的展示方式。

//style 的值我们可以写一个函数返回
const getPrintStyle =() => {
	return `
		#print-template {
	        font-weight: 400;
	        font-size: 12px;
	    }
	    table td {
	        padding: 8px;
	    }
	    table th {
	        white-space: nowrap;
	        padding: 8px;
	    }
	    table tr {
	        page-break-inside: avoid;
	    }
	    #print-template .date {
	        padding-bottom: 12px;
	        font-weight: 600;
	    }`
}

使用上述的方式就能解决打印内容被截断的问题了。

生成PDF

这个功能我们使用到html2canvas 和 jspdf库来实现

实现思路:将需要生成PDF的DOM节点使用html2canvas 生成 canvas 再使用 canvas.toDataURL 方法将 canvas转成图片base64、然后使用 jspdf 的 addImage方法将图片base64 转成PDF。

将二者一起封装成方法:如下

javascript 复制代码
const generatePDF = (selector: string, fileName: string) => {
    const element: any = document.querySelector(selector);
    const opts = {
      scale: 12, // 缩放比例,提高生成图片清晰度
      useCORS: true, // 允许加载跨域的图片
      allowTaint: false, // 允许图片跨域,和 useCORS 二者不可共同使用
      tainttest: true, // 检测每张图片已经加载完成
      logging: true // 日志开关,发布的时候记得改成 false
    };

    return html2Canvas(element, opts)
      .then(canvas => {
        const contentWidth = canvas.width;
        const contentHeight = canvas.height;
        // 一页pdf显示html页面生成的canvas高度;
        const pageHeight = (contentWidth / 592.28) * 841.89;
        // 未生成pdf的html页面高度
        let leftHeight = contentHeight;
        // 页面偏移
        let position = 0;
        // a4纸的尺寸[595.28,841.89],html页面生成的canvas在pdf中图片的宽高
        const imgWidth = 595.28;
        const imgHeight = (592.28 / contentWidth) * contentHeight;
        const pageData = canvas.toDataURL("image/jpeg", 1.0);

        // a4纸纵向,一般默认使用;new JsPDF('landscape'); 横向页面
        const PDF = new JsPDF(undefined, "pt", "a4");
        // 当内容未超过pdf一页显示的范围,无需分页
        if (leftHeight < pageHeight) {
          // addImage(pageData, 'JPEG', 左,上,宽度,高度)设置
          PDF.addImage(pageData, "JPEG", 0, 0, imgWidth, imgHeight);
        } else {
          // 超过一页时,分页打印(每页高度841.89)
          while (leftHeight > 0) {
            PDF.addImage(pageData, "JPEG", 0, position, imgWidth, imgHeight);
            leftHeight -= pageHeight;
            position -= 841.89;
            if (leftHeight > 0) {
              PDF.addPage();
            }
          }
        }
        PDF.save(fileName + ".pdf");
      })
      .catch(error => {
        console.log("打印失败", error);
      });
  };

调用:generatePDF('元素选择器', '导出的PDF文件名')

小结

1.上述导出PDF的功能还有一些问题没有完善、例如如果内容时table表格 的时候需要精准计算每一行的高度、再进行计算最终动态改变position偏移量来解决table内容被截断的问题。后续有空补上这部分代码。

相关推荐
竹林818几秒前
从ethers.js迁移到Viem:我在一个DeFi项目前端重构中踩过的坑
前端·javascript
像我这样帅的人丶你还21 分钟前
从交稿到甩锅预防:AI 前端流水线
前端·ai编程
想想弹幕会怎么做22 分钟前
如何构建一颗可交互的ui树?
前端
程序员陆业聪27 分钟前
我见过的最反直觉的 Android 架构问题:UseCase 越多,项目越烂
前端
Arya_aa33 分钟前
网络:前端向后端发送网络请求渲染在页面上,将EasyMock中的信息用前端vue框架编写代码,最终展示在浏览器
前端·vue.js
LlNingyu35 分钟前
文艺复兴,什么是CSRF,常见形式(一)
前端·安全·web安全·csrf
晓131337 分钟前
React篇——第三章 状态管理之 Redux 篇
前端·javascript·react.js
子兮曰42 分钟前
🚀24k Star 的 Pretext 为何突然爆火:它不是排版库,而是在重写 Web 文本测量
前端·javascript·github
@大迁世界1 小时前
11.在 React.js 中,state 与 props 的差异体现在哪里?
前端·javascript·react.js·前端框架·ecmascript
Giant1001 小时前
🔥前端跨域封神解法:Vite Proxy + Express CORS,一篇搞定所有跨域坑!
前端·javascript·面试