🌑

Mocha's Blog

目录
  1. 什么是 jsPDF
  2. 什么是 jsPDF-AutoText
  3. 为什么要做这个库
  4. 使用方法
  5. API 列表
    1. 实例化 AutoText 配置项
    2. Render 方法接受的参数类型
  6. 后续规划

使用jspdf-autotext改善jsPDF使用体验

发布时间:2022年1月23日

什么是 jsPDF

jsPDF 是一个基于 Javascript 的 pdf 文件生成库,目前在 npm 中每周的下载量在 60W 左右。

jsPDF 与 html2canvas 是目前比较主流的将前端页面转换成 PDF 的方案。

什么是 jsPDF-AutoText

jsPDF-AutoText 是我个人开发的基于 jsPDF 的工具库,它通过对外暴露 AutoText Class 来提供服务。

用户在实例化该类时可以将需要创建的 PDF 文件的一些配置文件传入,后续通过持续化调用 AutoText 提供的 render 方法来即可自动化向 PDF 文件中添加文字,而不用关注文字的换行以及 PDF 文件分页等问题, 提交我们使用 jsPDF 时的体验。

为什么要做这个库

最近的现场需求中客户要求可以从项目中导出对应的数据并整合成一个 PDF 文件,PDF 文件的内容包括了一页申请表以及一页文字描述,需要在页面上点击导出按钮时生成文件并自动下载至本地,供用户后续的打印及提交审批。

在调研需求的实现方案时,发现目前主流的方式是使用 jsPDF + html2canvas 的方案。这种方案需要我们先在页面上将要打印的内容渲染出来,然后使用 html2canvas 将 HTML 内容转成 Canvas,再将 Canvas 转成图片并通过 jsPDF 的 addImage 方法将图片添加到 PDF 文件中。

但是这个方案在后期进行实验的时候发现了一些问题,最终选择放弃使用 html2canvas,问题主要是以下几点:

  1. html2canvas 转换生成的图片分辨率较差,且不能根据 jsPDF 创建的 pdf 文件宽度自行调整,需要自己不停的调试样式,
  2. 当文字内容超过一页时,无法自动分页。如果手动切割图片,会存在在切割图片时,将文字也从中间切开的问题
  3. 导出的 pdf 中,我们的内容实际上是几张图片,用户无法复制其中的文字内容,体验较差

后来通过在 github 上的查找,发现了一个叫做 jsPDF-AutoTable 的库,该库用于在 jsPDF 创建的 pdf 文件中创建一个复杂的表格。通过一些测试,该库完全可以满足我们的业务需求,那么现在的问题就剩下一个:第二页的文字描述如何解决。

其实 jsPDF 本身是提供手动添加文字内容的方法的,但这个方案在使用上有几个较重的心智负担:

  1. 使用该方法时,用户需要手动设定文字渲染位置的 X轴坐标以及 Y轴坐标,如果坐标不对,文字渲染就会出现问题。
  2. 文字的长度超过 pdf 文件的宽度时并不会自动换行,需要在使用渲染方法时手动给文字添加最大宽度选项
  3. 每一行文字的位置之间存在相互依赖,上一行位置移动下一行也需要移动

基于以上问题,在梳理了文字渲染位置计算、换行、分页、缩进等情况后,根据这些写了一个 hook 方法,在项目中初始化传入一些 pdf 文件的信息,后续直接调用渲染文字方法可以毫无负担的创建我们想要的内容。

然后就有了这个库,与项目中不同的时,项目是基于 React Hook 编写的方法,并不能直接拿来作为通用方法使用,于是在原来的基础上做了一些调整:

  1. Class 来替代原有的 Hook 方法实现,使得可以满足各个框架的使用。
  2. 做了一些配置参数的调整及剥离:一些与 PDF 相纸(A4、B5)相关的常量参数(如页面的宽度),做了与用户动态设定的参数(如每行文字最大渲染宽度、每一层缩进对应多少缩进量)的剥离

目前第一个版本已经发布到 npm 上,后续会持续添加新功能,如文字自适应居中、支持行内文字加粗/斜体、页面水印等功能。

使用方法

使用方式其实很简单,首先惯例我们需要先添加相关依赖:

yarn add jspdf jspdf-autotext

然后在项目中直接引用即可,代码如下:

import jsPDF from 'jspdf';
import { AutoText as jsPDFAutoText } from 'jspdf-autotext';

// 创建 jsPDF 实例
const pdfFile = new jsPDF();

// 传入 jsPDF 实例,并使用默认的页面配置
const AutoText = new jsPDFAutoText({ pdfFile });

// 使用 render 方法渲染文字
const renderedPositions = AutoText.render([
    { text: '离骚', indent: 15 },
    { text: '屈原', indent: 15 },
    '帝高阳之苗裔兮,朕皇考曰伯庸。',
    '摄提贞于孟陬兮,惟庚寅吾以降。',
    '皇览揆余初度兮,肇锡余以嘉名:名余曰正则兮,字余曰灵均。',
]);

// 添加新的页面
AutoText.addPage();

// 渲染新的文字
AutoText.render([
    { text: '赋得古原草送别', indent: 14 },
    { text: '白居易', indent: 15 },
    '离离原上草,一岁一枯荣。',
    '野火烧不尽,春风吹又生。',
    '远芳侵古道,晴翠接荒城。',
    '又送王孙去,萋萋满别情。',
]);

// 保存文件
pdfFile.save();

打开我们保存的文件就可以看到这样的内容

image-20220123165325463

image-20220123165337536

每一行不同的文字都会自动计算其渲染的坐标位置,不过目前缺少文字自动居中功能,需要使用缩进 indent 来手动居中,后续会优化这个体验。

API 列表

以下默认值均是在 A4 纸情况下的默认,A4 纸的标注宽度为 210 毫米,高度为 297 毫米,所以以下值的单位也均为毫米

实例化 AutoText 配置项

key 名称 必填 / 默认值 数据类型 描述
pdfFile jsPDF 实例 Y jsPDF instance -
TEXT_LINE_MAX_WIDTH 每一行文字的最大宽度,超过换行 N / 190 number -
INITIAL_POSITION_X 每一行文字的起始 X 坐标 N / 10 number 页面上文字的起点与纸张左侧的距离
INITIAL_POSITION_Y 每一页文字的起始 Y 坐标 N / 10 number 每页第一行文字顶部与纸张顶部的距离
EVERY_PAGE_MAX_HEIGHT 每一页最高渲染多高的文字,超过换页 N / 280 number -
EVERY_INDENT_WIDTH 每一个缩进的缩进量 N / 5 number -
EVERY_TEXT_LINE_HEIGHT 每一行文字的行高 N / 6 number 以宋体/10号字为基准,不同字体需要自行配置

Render 方法接受的参数类型

目前 Render 方法接受两种类型的参数:配置对象或字符串,如果调用 Render 方法时传递的是数组则可以混合使用

// 纯字符串
AutoText.render('纯字符串调用');

// 配置对象调用
AutoText.render({ text: "配置对象调用", indent: 10 })

// 字符串数组
AutoText.render([
    "字符串调用1",
    "字符串调用2"
])

// 配置对象数组
AutoText.render([
    { text: "配置对象调用", indent: 10 }
    { text: "配置对象调用", indent: 10 }
])

// 混合调用
AutoText.render([
    "混合调用纯字符串"
    { text: "配置对象调用", indent: 10 }
])

配置对象的配置格式有以下几个:

key 名称 必填 / 默认值 数据类型 描述
text 文字内容 Y string -
indent 缩进数量 N number -
…… 请参照 jsPDF 中 Text Option选项

后续规划

目前的 0.1.0 版本仅能支持一些最基本的文字渲染内容,后续会在以下方向做演进:

  • 拓展文字对象配置选项,比如自动居中、加粗、斜体等
  • 提供更多纸张类型的默认配置
  • 文字超出换页逻辑优化,目前超长文字在本页无法完全渲染时会全量换页,未来希望做到只把超出部分渲染到新的一页上

Powered By Hexo.js Hexo and Minima. Support By Oracle & Docker-Compose.

友情链接: 相随