在做项目时有这么一个需求,需要将当前页面指定区域的内容导出pdf到本地。借助了两个插件分别是html2canvas.js和pdf.js来实现。使用过程中遇到的问题及解决方法
解决一些问题:
- 导出按A4纸大小排列
- 预留页面边距的问题
- 内容过多自动分页的问题
- 直接使用jspdf中文乱码的问题
- 直接使用jspdf文本自动换行的问题
一、安装依赖
- 将页面转换成图片
html2canvas 的作用就是根据 DOM 生成对应的图片。它的屏幕截图是基于 DOM 的,因此可能不会 100% 精确到真实的表示,因为它不会生成实际的屏幕截图,而是基于页面上可用的信息构建屏幕截图。
npm install html2canvas --save
- 将图片导出成PDF
JSPDF是一个JavaScript库,用于生成PDF文档。它可以直接在浏览器中生成PDF,也可以通过Node.js在服务器端生成PDF。JSPDF具有高度的自定义性和可扩展性,可以用于各种PDF生成需求。
npm install jspdf --save
二、具体使用
1、页面中创建一个容器ref=“contenterPdf”
<template> <div> <div ref="contenterPdf"> <img alt="Vue logo" src="../assets/logo.png" /> <div>JSPDF是一个用于生成PDF文件的客户端JavaScript库。它提供了简单易用的API,使得我们可以在浏览器端创建PDF文件。相比于服务端生成PDF文件,使用JSPDF可以避免服务端的压力,并且能够实现更多的交互与设计效果。基于JSPDF,我们可以生成包括图表、表格、文字、图片、图形等各种元素的PDF文档,同时也可以设置字体、颜色、边框等多种属性来调整文档的样式。</div> </div> <button @click="handleExport">导出PDF文件</button> </div> </template>
2、创建/utils/htmlToPdf.js文件
import html2canvas from "html2canvas"; import jsPDF from "jspdf"; export const downloadPDF = page => { html2canvas(page, { useCORS: true, //允许canvas画布内 可以跨域请求外部链接图片, 允许跨域请求。 allowTaint: true, //允许跨域 scale: 2, 设置放大倍数 backgroundColor: '#ffffff'//背景色 }).then((canvas)=> { canvas2PDF(canvas); }) }; const canvas2PDF = canvas => { // 新建JsPDF对象 const PDF = new jsPDF({ orientation: 'p', //参数: l:横向 p:纵向 unit: 'mm', //参数:测量单位("pt","mm", "cm", "m", "in" or "px") format: 'a4', //A4纸 }) const ctx = canvas.getContext('2d') const a4w = 190 const a4h = 277 //A4大小,210mm x 297mm,四边各保留10mm的边距,显示区域190x277 const imgHeight = Math.floor(a4h * canvas.width / a4w) //按A4显示比例换算一页图像的像素高度 let renderedHeight = 0 while (renderedHeight < canvas.height) { let page = document.createElement("canvas"); page.width = canvas.width; page.height = Math.min(imgHeight, canvas.height - renderedHeight); //可能内容不足一页 //用getImageData剪裁指定区域,并画到前面创建的canvas对象中 page.getContext('2d').putImageData(ctx.getImageData(0, renderedHeight, canvas.width, Math.min(imgHeight, canvas.height - renderedHeight)), 0, 0); // canvas转图片数据保留10mm边距 PDF.addImage(page.toDataURL('image/jpeg', 0.2), 'JPEG', 10, 10, a4w, Math.min(a4h, a4w * page.height / page.width)); renderedHeight += imgHeight; //判断是否分页,如果后面还有内容,添加一个空页 if (renderedHeight < canvas.height) { PDF.addPage() } } PDF.save("导出.pdf"); };
3、页面方法中调用
<script> import { downloadPDF } from "@/utils/htmlToPdf.js" export default { data() { return {}; }, methods: { handleExport() { downloadPDF(this.$refs.contenterPdf) }, }, }; </script>
- 页面效果
- 导出效果
- 导出多页的效果
三、解决jspdf中文乱码
如果是直接使用jsPDF生成pdf文件,中文是乱码。
PDF.text('JSPDF是一个用于生成PDF文件的客户端JavaScript库。它提供了简单易用的API,使得我们可以在浏览器端创建PDF文件。', 10, 100);
- 解决方案
需要一个支持中文的字体ttf文件,可以在网上下载,也可以使用本地window/font/路径下的文件。
1、下载jspdf
里面有个fontconverter目录
2、双击打开fontconverter.html文件
选择你本地的ttf文件,点击“Create”按钮,会生成一个js文件。
3、将js文件放入自己的项目中
在页面中引入js文件
import '@/utils/simhei-normal.js'
4、设置字体
PDF.setFont('simhei') PDF.text('JSPDF是一个用于生成PDF文件的客户端JavaScript库。它提供了简单易用的API,使得我们可以在浏览器端创建PDF文件。', 10, 100);
四、解决jspdf文本自动换行
上面乱码解决了,但是发现中文字不会自动换行。这个还需要进一步解决。
1、使用splitTextToSize解决
//文本内容 const reportTitle = 'JSPDF是一个用于生成PDF文件的客户端JavaScript库。它提供了简单易用的API,使得我们可以在浏览器端创建PDF文件。相比于服务端生成PDF文件,使用JSPDF可以避免服务端的压力,并且能够实现更多的交互与设计效果。基于JSPDF,我们可以生成包括图表、表格、文字、图片、图形等各种元素的PDF文档,同时也可以设置字体、颜色、边框等多种属性来调整文档的样式。' //设置自动换行 const splitTitle = PDF.splitTextToSize(reportTitle, 190); //添加文本内容 PDF.text(splitTitle, 10, 110);
五、添加页码
在导出多页的时候,希望在页面尾部添加页码。
这里可以通过页码尺寸计算。
//计算总页数 let pageCount = Math.ceil(canvas.height/imgHeight)
将上面的的方法改写一下:
const canvas2PDF = canvas => { // 新建JsPDF对象 const PDF = new jsPDF({ orientation: 'p', //参数: l:横向 p:纵向 unit: 'mm', //参数:测量单位("pt","mm", "cm", "m", "in" or "px") format: 'a4', //A4纸 }) const ctx = canvas.getContext('2d') const a4w = 190 const a4h = 272 //A4大小,210mm x 297mm,四边各保留10mm的边距,显示区域190x277,底部留5mm的页码显示位置,所以高度为272mm。 const imgHeight = Math.floor(a4h * canvas.width / a4w) //按A4显示比例换算一页图像的像素高度 let renderedHeight = 0 //计算总页数 const pageCount = Math.ceil(canvas.height/imgHeight) const page = document.createElement("canvas"); page.width = canvas.width; while (renderedHeight < canvas.height) { page.height = Math.min(imgHeight, canvas.height - renderedHeight); //可能内容不足一页 //用getImageData剪裁指定区域,并画到前面创建的canvas对象中 page.getContext('2d').putImageData(ctx.getImageData(0, renderedHeight, canvas.width, Math.min(imgHeight, canvas.height - renderedHeight)), 0, 0); // canvas转图片数据保留10mm边距 PDF.addImage(page.toDataURL('image/jpeg', 0.2), 'JPEG', 10, 10, a4w, Math.min(a4h, a4w * page.height / page.width)); renderedHeight += imgHeight; // 计算当前页,及文字 const pageNumberText = '第' + (renderedHeight/imgHeight) + '页,共' + pageCount + '页'; // 设置中文字体 PDF.setFont('simhei') // 设置文字大小 PDF.setFontSize(8); // 设置文字颜色 PDF.setTextColor('#999999'); // 在页脚添加页码 PDF.text(pageNumberText, (a4w/2), (a4h+16)); //判断是否分页,如果后面还有内容,添加一个空页 if (renderedHeight < canvas.height) { PDF.addPage() } } PDF.save("导出.pdf"); };
最后导出后的效果