客户端 PDF 生成的隐藏复杂性

生成 PDF 通常被视为一项微不足道的工作——只需简单的“打印为 PDF”或调用一个库。然而,当需求转变为在保持高保真、文本可选择内容的同时,完全在客户端生成这些文档时,开发者便进入了一个充满惊人复杂性的世界。构建 SDocs 的过程凸显了 Web 开发中的一个根本性矛盾:我们为屏幕渲染内容的方式(HTML/CSS)与我们为 PDF 等固定布局格式定义文档的方式之间的差距。

文本可选择性的挑战

用户最常见的挫折之一是“死” PDF——这种文档看起来很正确,但表现得像一张图片,文本无法被选择、搜索或复制。在客户端生成的 PDF 中实现真正的文本可选择性,不仅仅是将字符放置在页面上;它还需要将字形(glyphs)精确映射到坐标,并包含一个 PDF 查看器可以解释的文本层。

正如社区中的开发者所指出的,这是 Web 开发中的“潘多拉魔盒”之一,类似于创建跨客户端电子邮件模板的噩梦。困难在于 PDF 不是响应式的;它们是固定布局的文档。在保持视觉字形与底层字符代码之间关系的同时,将 Web 布局的流动性转化为静态坐标系统,是一项非同寻常的工程壮举。

“PDF 地狱”:为什么它如此困难

除了最初的生成过程外,PDF 格式还引入了几个系统性问题,使得它成为现代数据交换中一种脆弱的工具:

1. 复制粘贴的噩梦

即使文本是可选择的,体验也往往是破碎的。由于 PDF 侧重于视觉定位而非语义结构,复制文本通常会导致句子断裂、空格缺失或字符顺序错误。

“你展示了仅仅为了通过字形映射、布局、链接、代码块、渲染样式等实现文本可选择性需要多少疯狂的工作。但一旦你从该 PDF 中复制内容,大多数查看器仍然只能暴露原始文本,而且往往还是破碎的原始文本……”

2. 布局刚性

软件工程师经常低估 GUI 和布局引擎的复杂性。例如,渲染一个跨越多页的表格,需要复杂的逻辑来处理页眉、页脚和行拆分——paged.js 项目正试图通过将开放的 Web 标准应用于分页媒体来解决这些问题。

3. 编辑鸿沟

编辑现有的 PDF 是出了名的困难。简单的任务,例如勾选复选框,可能会静默失败,或者需要复杂的脚本来操作底层的 PDF 结构,因为视觉表示往往与实际的数据层背道而驰。

替代方案与解决方案

鉴于原生 PDF 生成的复杂性,出现了几种替代策略:

  • 基于 WASM 的编译器: 一些开发者建议使用像 Typst 这样的工具,它可以被编译为 WebAssembly (WASM) 以在浏览器中本地运行,从而为生成高质量、可选择的 PDF 提供更稳健的流水线。
  • 中间格式: 将 Markdown 转换为像 LaTeX 这样稳定的中间格式,或者使用 Pandoc,可以提供更可靠的渲染流水线,尽管这些通常会引入更重的依赖。
  • HTML-to-PDF 桥接器: 将富文本渲染到 2D canvas 上下文并随后将其代理到 PDF 库的库,可以简化保持视觉保真度的过程。
  • “Vibecoding” 方法: 一些用户发现,对于内部工具,完全绕过 PDF 是成功的做法,即通过将 PDF 转换为 HTML 包,并使用脚本注入变量数据,有效地将 PDF 视为一种视觉模板而非文档格式。

结论:PDF 是正确的工具吗?

生成完美 PDF 的挣扎往往引发一个更广泛的哲学问题:对于数字优先的内容,我们是否应该使用 PDF?虽然它们对于打印和法律存档是不可或缺的,但它们从根本上不适合现代 Web 的响应式、可搜索和可访问的特性。对于书籍、科学论文和目录,只要阅读器生态系统继续发展,HTML 和 EPUB 等标准将提供远为优越的灵活性和可访问性。

Sources