pdfMake前端导出pdf

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来生成吧.使用中要注意配置图片名称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文件只放置一个字体文件。

结语

使用起来非常的人性化,很棒哈.但是最好还是后端借助工具导出,控制更给力,性能更好,前端处理还是有很多不适.

大家有什么问题或者建议可以留言讨论哈 ( •̀ ω •́ )✧

你可能感兴趣的:(pdfMake前端导出pdf)