pdfMake前端導(dǎo)出pdf
目前導(dǎo)出PDF還是后端(或nodejs)比較好. (如果沒(méi)有必要)
導(dǎo)出方案
后端: IText,wkhtmltopdf...等等.
前端: jsPdf,pdfKit,react-pdf...等等.
現(xiàn)在網(wǎng)上一提到前端導(dǎo)出pdf的絕大多數(shù)都是html2canvas + jspdf 實(shí)現(xiàn)html轉(zhuǎn)pdf.這是非常棒的一個(gè)解決方案,不過(guò)它有一個(gè)弊端就是內(nèi)容放大之后會(huì)失真.如果遇到這類(lèi)需求就只能含淚放棄了.html2canvas主要解決的是jspdf中文亂碼的問(wèn)題,因?yàn)槭峭鈬?guó)小哥開(kāi)發(fā)的,所以就沒(méi)考慮過(guò)中文兼容.這些或多或少的pdf框架都有中文亂碼的問(wèn)題.
還有一種是pdfKit的二次封裝 pdfMake .這也是我所采用的方案,相比較jspdf和pdfkit,使用起來(lái)更易上手.也有中文亂碼的解決方案.github地址 官方網(wǎng)站
使用pdfMake
這里我又用到了webWorker,具體原因和配置我后面解釋.
// UI線程--------------------------------------------
import Worker from './components/imppdf.worker.js';
let worker = new Worker(); // 傳入 worker 腳本文件的路徑即可
const dd = {
content: [
'中英文測(cè)試',
'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") {
// 返回的進(jìn)度信息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 = { // 添加字體庫(kù)
方正姚體: {
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})}});
};
使用中的問(wèn)題
雖然支持svg,但是svg如果很大它無(wú)法配置跨頁(yè)φ(* ̄0 ̄)
這個(gè)問(wèn)題很重要,因?yàn)槲矣胑charts生成的svg圖片,無(wú)法通過(guò)fit屬性壓縮大小(長(zhǎng)寬).所以考慮手動(dòng)切割圖片,但因?yàn)閷?duì)svg的了解不深所以放棄了.后面采用position定位屬性手動(dòng)偏移和計(jì)算,在通過(guò)pagebreak配置分頁(yè).實(shí)現(xiàn)跨頁(yè)效果
下貼手動(dòng)偏移的代碼
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
}
問(wèn)題解決了,但是這就導(dǎo)致生成pdf的時(shí)間變得更長(zhǎng)了 o( ̄▽?zhuān)?/em>)o
渲染中會(huì)阻塞線程
通過(guò)回調(diào)本應(yīng)該拿到進(jìn)度并繪制在進(jìn)度條上給用戶了解,但是因?yàn)樗膱?zhí)行是同步代碼,所以會(huì)優(yōu)于頁(yè)面渲染.同時(shí)頁(yè)面也是阻塞狀態(tài),無(wú)法操作.這個(gè)很難受,想了一下就用webWorker來(lái)生成吧.使用中要注意配置<img src="https://webpack.docschina.org/d19378a95ebe6b15d5ddea281138dcf4.svg" width = "20" height = "20" alt="圖片名稱(chēng)" align=center />webpack,官方配置鏈接很簡(jiǎn)單.我使用的是 chainWebpack,下面就貼一下代碼吧.
chainWebpack(config) {
config.module.rule()
.test(/\.worker\.js$/)
.use('worker-loader')
.loader('worker-loader')
.end()
},
生成時(shí)間很長(zhǎng),相對(duì)chrome打印而言
我這是頁(yè)數(shù)*1秒,給了一個(gè)進(jìn)度條讓用戶感覺(jué)在工作,盡量減少不適感.
中文亂碼(配置個(gè)字體)
配置字體就行,不同的字體可以自己生成.我用的一個(gè)現(xiàn)成的,地址: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依賴(lài)包
npm install
4.在cmd運(yùn)行打包examples/fonts中的.ttf文件的命令。
gulp buildFonts
5.然后在 build 文件中可以找到vfs_fonts.js文件。
由于字體打包文件較大,建議examples/fonts中的.ttf文件只放置一個(gè)字體文件。
結(jié)語(yǔ)
使用起來(lái)非常的人性化,很棒哈.但是最好還是后端借助工具導(dǎo)出,控制更給力,性能更好,前端處理還是有很多不適.
大家有什么問(wèn)題或者建議可以留言討論哈 ( ?? ω ?? )?