前言

前阵子需要实现一个需求:

拿到一份原始的 PDF,PDF 上会有一些空需要填充字段,需要用户自己去选择字段,放到对应位置,生成携带字段的 PDF。

生成的 PDF 再给后端解析,填充实际的字段值,生成得到一份带有实际值的 PDF。

实现之初碰到几个难点:

  • 如何将 PDF 渲染出来

  • 如何将字段放到 PDF 上对应的位置

  • 字段如何写入到 PDF 中

如何将 PDF 渲染出来

我想这类需求,应该别人也碰到过,说不定有现成的方案,果不其然,找到了 pdf-editor 这个仓库。

渲染的思路是:

  • pdf.js 去解析 PDF 文件,得到一个解析后的 PDF 对象,通过它可以得到 PDF 的每一页

这样就完成了 PDF 的渲染。

在使用 pdf.js 的时候,由于实际开发的技术栈比较旧,不支持 ES6 的一些语法,配置和调整 babel 也没整明白,还需要升级 NodeJS 版本。

虽然官方提供了 Legend biuld,但依然是包含了不支持的语法,甚至提了一个 issue 去问。

后来是一直降低 pdf.js 的版本,下了一个和 pdf-editor 一样的版本 (2.3.2),终于是能集成到项目了。

所幸功能基本够用。

如何将字段放到 PDF 上对应的位置

可以参考 pdf-editor 的实现,也可以使用 Drag and Drop API 将需要的元素拖拽到 PDF 上。

我的实现思路是:

  • 将每一页 PDF 看作一个容器,设置 position: relative;

  • 通过 Drag and Drop API 将字段元素,拖拽到对应的页上,计算对应的 x,y 坐标,设置 positon: absolute; ,这样元素就能相对每一页的 PDF 定位。 在实现时,借助 getBoundingClientRect() 以及鼠标事件返回的位置,可以很容易的计算到 x,y。

  • 接着将拖拽到 PDF 上的元素,以二维数组的形式存储, 数组元素对应每一页包含的字段。

实现的时候,碰到一个问题,当将元素拖拽到和自身重合的时候,会导致位置计算错误,元素被挪动到页面的左上角。

原因是我基于 drop 事件 返回的 target 计算 x,y,而我实际的 target 应该是对应的 PDF 页容器。

因此,只需要找到对应的 PDF 页元素作为 target,再使用 getBoundingClientRect() 去计算一个准确的 x,y 就能解决了。

字段如何写入到 PDF 中

经过上面两步,现在有一个数组,包含 PDF 的每一页。

另一个数组,包含每一页上的字段信息(坐标,宽度,及其他值)。

两个数组的下标是一一对应的。

只需要遍历其中一个数组,使用 pdf-lib 这个库,就可以将元素写入到 PDF 中。

这里我是用 createTextField API 添加字段,后端可以根据字段的 id 找到对应的字段,得到字段信息 (x, y, id 等)。

需要注意的是,使用 createTextField 生成的 text field 自带一个白色背景,可能会覆盖 PDF 的文字。

参考 [Feature Request] Transparent Fields #605,将 backgroundColor 和 borderColor 置为 undefined 就好了。