pdfMake前端导出pdf
目前导出PDF还是后端(或nodejs)比较好. (如果没有必要)
导出方案
后端: IText,wkhtmltopdf...等等.
前端: jsPdf,pdfKit,react-pdf...等等.
现在网上一提到前端导出pdf的绝大多数都是html2canvas + jspdf 实现html转pdf.这是非常棒的一个解决方案,不过它有一个弊端就是内容放大之后会失真.如果遇到这类需求就只能含泪放弃了.html2canvas主要解决的是jspdf中文乱码的问题,因为是外国小哥开发的,所以就没考虑过中文兼容.这些或多或少的pdf框架都有中文乱码的问题.
还有一种是pdfKit的二次封装 pdfMake .这也是我所采用的方案,相比较jspdf和pdfkit,使用起来更易上手.也有中文乱码的解决方案.github地址 官方网站
使用pdfMake
这里我又用到了webWorker,具体原因和配置我后面解释.
// UI线程--------------------------------------------
import Worker from './components/imppdf.worker.js';
let worker = new Worker(); // 传入 worker 脚本文件的路径即可
const dd = {
content: [
'中英文测试',
'Another paragraph, this time a little bit longer to make sure, this line will be divided into at least two lines'
],
defaultStyle: {
font: '方正姚体'
}
};
worker.postMessage(dd);
worker.onmessage = function (event: any) {
if (event.data?.type === "progress") {
// 返回的进度信息0-1
} else {
const link = document.createElement("a");
link.style.display = "none";
link.href = URL.createObjectURL(event.data);
link.setAttribute("download", decodeURI('下载.pdf'));
document.body.appendChild(link);
link.click();
document.body.removeChild(link);
};
}
// webWorker-----------------------------------------
import pdfMake from "pdfmake/build/pdfmake";
import FZvfs from "./FZYTvfs_fonts"; // .ttf字体文件打包后的js文件
pdfMake.vfs = FZvfs;
pdfMake.fonts = { // 添加字体库
方正姚体: {
normal: 'FZYTK.TTF',
bold: 'FZYTK.TTF',
italics: 'FZYTK.TTF',
bolditalics: 'FZYTK.TTF',
}
};
onmessage = function ({ data }) {
// 工作线程收到主线程的消息
if(!data) return;
let pdf = pdfMake.createPdf(data);
pdf.getBlob((blob) => {
postMessage(blob)
}, { progressCallback(num) { postMessage({ type:'progress',num})}});
};
使用中的问题
虽然支持svg,但是svg如果很大它无法配置跨页φ(* ̄0 ̄)
这个问题很重要,因为我用echarts生成的svg图片,无法通过fit属性压缩大小(长宽).所以考虑手动切割图片,但因为对svg的了解不深所以放弃了.后面采用position定位属性手动偏移和计算,在通过pagebreak配置分页.实现跨页效果
下贴手动偏移的代码
interface SvgProps {
src: string;
wh: number[]; // [宽,高]
}
const getSvgContents = (svg: SvgProps) => {
if (!svg) return null;
let { src, wh } = svg;
let [width, height] = wh;
let rw = pageWidth - pageMargins[0] - pageMargins[2];
let ratio = rw / width;
let rH = height * ratio;
let spliteNum = Math.ceil(height / pageHeight);
let content = [];
for (let i = 0; i < spliteNum; i++) {
content.push({ svg: src, margin: [0, 0, 0, 0], fit: [rw, rH], absolutePosition: { x: 0, y: i && -1 * i * pageHeight }, pageBreak: 'before', preserveLeadingSpaces: true },)
}
return content
}
问题解决了,但是这就导致生成pdf的时间变得更长了 o( ̄▽ ̄)o
渲染中会阻塞线程
通过回调本应该拿到进度并绘制在进度条上给用户了解,但是因为它的执行是同步代码,所以会优于页面渲染.同时页面也是阻塞状态,无法操作.这个很难受,想了一下就用webWorker来生成吧.使用中要注意配置<img src="https://webpack.docschina.org/d19378a95ebe6b15d5ddea281138dcf4.svg" width = "20" height = "20" alt="图片名称" align=center />webpack,官方配置链接很简单.我使用的是 chainWebpack,下面就贴一下代码吧.
chainWebpack(config) {
config.module.rule()
.test(/\.worker\.js$/)
.use('worker-loader')
.loader('worker-loader')
.end()
},
生成时间很长,相对chrome打印而言
我这是页数*1秒,给了一个进度条让用户感觉在工作,尽量减少不适感.
中文乱码(配置个字体)
配置字体就行,不同的字体可以自己生成.我用的一个现成的,地址:https://files.cnblogs.com/files/s313139232/方正姚体vfs_fonts.js
打包教程:http://www.cnblogs.com/xrab/p/7210588.html
打包步骤:
1.在https://github.com/bpampuch/pdfmake下载pdfmake的源文件
2.在根目录用 npm 安装 gulp
npm install gulp --save-dev
3.安装pdfmake依赖包
npm install
4.在cmd运行打包examples/fonts中的.ttf文件的命令。
gulp buildFonts
5.然后在 build 文件中可以找到vfs_fonts.js文件。
由于字体打包文件较大,建议examples/fonts中的.ttf文件只放置一个字体文件。
结语
使用起来非常的人性化,很棒哈.但是最好还是后端借助工具导出,控制更给力,性能更好,前端处理还是有很多不适.
大家有什么问题或者建议可以留言讨论哈 ( •̀ ω •́ )✧