技术文章

适用于 Delphi 的 losLab PDF 库:HotPDF、PDFium Component 与 PDFlibPas 比较

三个库,三项截然不同的任务。选错库会让你花费数周时间去寻找变通方法,而在只需要一个库时却选了全部三个,则会带来你未曾预算过的维护开销。本文将直接说明每个 losLab PDF 库的实际功能、适用场景,以及在何处交接给其他兄弟库。

HotPDF:在 Delphi 中从头开始编写 PDF

HotPDF 是一个用于生成 PDF 文档的原生 VCL 组件。它的模型是命令式且以页面为中心的:你构造一个 THotPDF 实例,设置文档属性,调用 BeginDoc,在 CurrentPage 上进行绘制,根据需要添加页面,然后使用 EndDoc 关闭。这个顺序很重要,因为 BeginDoc 会在其运行的瞬间提交加密字典和压缩设置;在此之后分配的任何内容都会被静默忽略,而不是追溯应用。

绘图表面在 Delphi 级别覆盖了完整的 PDF 操作符集:用于定位 Unicode 文本的 TextOut,带有 TrueType 嵌入的 SetFont,矢量原语(直线、贝塞尔曲线、椭圆、矩形),从文件或内存放置图像,以及条形码生成。坐标以点为单位,以左下角为原点且 Y 轴向上增加,这总会让初次使用者感到困惑。字体状态在 AddPage 之后不会保留,因此在每次分页后都需要调用一次 SetFont

AcroForm 字段是一等公民。你可以直接通过单次调用将文本字段、复选框、单选按钮、组合框、列表框和下压按钮添加到页面对象中。HotPDF 还可以通过 LoadFromFile 加载现有的 PDF,并填充或读取字段值,这使得它在两个独立的工作流中非常有用:构建表单和自动化其数据填充。

加密也在文档级别进行处理。CryptKeyLength 选择加密方案(从 40 位 RC4 到 AES-256),ActivateProtection 将其激活,而 ProtectOptions 设置 ISO 权限标志。之所以存在两种 AES-256 修订模式(R5 和 R6,由 UseAES256R6 控制),是因为修订版 6 修复了修订版 5 中的一个已知漏洞,但它需要支持 PDF 2.0 的查看器;在这两者之间做出选择是一项关乎兼容性的决定,而非为了方便。

HotPDF 中的数字签名支持涵盖了 PAdES 基线配置文件,因此它适用于签名必须满足 ETSI EN 319 142 要求的工作流。如果你的需求仅仅是生成输出,HotPDF 就是你应首选的库。

PDFium Component:渲染、查看和阅读现有 PDF

PDFium Component 将 Google 的 PDFium 引擎包装为一个 VCL 组件,这赋予了它与 HotPDF 根本不同的角色。HotPDF 负责写入,而 PDFium Component 负责读取和渲染。其核心对象是 TPdf,它是一个文档管理器,通过设置 FileName 然后设置 Active := True 来打开文件。加载失败不会引发异常;Active 只是简单地保持 False 状态,因此在赋值后对其进行检查是必不可少的。

渲染通过 TPdfView 进行,它是你可以拖放到窗体上的可视组件,并通过 PdfView.Pdf := Pdf 链接到 TPdf 实例。缩放和适应模式存在于视图上,而不是文档上。一个容易让人绊倒的微妙之处是:Pdf.PageNumberPdfView.PageNumber 是独立的属性。设置其中一个并不会更新另一个,而且基于视图的提取 API(单词边框、阅读单元)使用的是视图的当前页,而不是文档的。

在文本提取方面,PDFium Component 在 losLab 的产品线中没有直接竞争对手。ReadablePageContent 会返回具有阅读顺序感知能力的结构化文本,PageWordBoxes 提供词级边界矩形,而 DocumentReadingUnits 会遍历整个文档。对于无障碍工作,IsTagged 告诉你是否存在结构树,ValidatePdfUa 会运行 UA 验证检查。这些 API 使得 PDFium Component 成为任何需要理解现有 PDF 内容而非生成新 PDF 的工作流的自然选择。

表单填充同样可以在 PDFium 侧工作,这是通过底层引擎暴露的同一个 AcroForm 层实现的。当源文档已经存在且你正在自动化其完成过程,而不是自己构建表单字段时,它就非常适用。

PDFlibPas:操作、合规签名与直接文件访问

PDFlibPas (版本 3.73.0) 位于复杂性图谱的另一端。它在同一个文档模型之上暴露了三个 API 层:一个与 Quick-PDF 调用约定兼容的基于扁平句柄的门面(TPDFlib),一个完整的对象树层(TPDFDocument),以及一个直接对文件字节进行操作而不加载完整对象图的流式解析器(TSmartPDFReader / TSmartPDFWriter)。

流式层使得 PDFlibPas 成为处理大型文档的正确选择。TSmartPDFWriter 可以将增量更新附加到磁盘上的文件,而无需重建整个交叉引用表,这正是实现高效重新保存和 PAdES 长期验证时间戳的基础机制。对于要求签名的哈希值必须覆盖特定字节范围,且在应用签名时不重写文档的合规级签名工作流而言,这一层是唯一可行的途径。

TPDFDocument 级别的文档操作包括通过 Merge 进行合并,通过带有范围字符串的 CopyPagesFromDoc 进行选择性页面复制,以及通过 SetMinimumVersionLockSaveVersion 进行版本治理。如果你试图保存某个会将输出推高到所锁定的版本之上的功能,版本锁会引发错误 602,这在你需要保证输出保持在特定 PDF 修订版内以满足归档合规性时非常有用。

PDF/A (ISO 19005) 支持位于 PDFlibPas 的一致性工作台中。请注意,根据规范,加密与 PDF/A 是互斥的:你不能在同一个文件中同时拥有两者。如果工作流需要一个加密的分发副本和一个 PDF/A 存档副本,则必须生成两个独立的构件。

在它们之间做出选择

典型的决策树很短。如果你要从数据生成一个新文档,请使用 HotPDF。如果你在 Delphi VCL 应用程序中渲染或从现有文档提取文本,请使用 PDFium Component。如果你正在大规模地操作、合并现有 PDF,或者使用增量保存语义对其进行合规签名,请使用 PDFlibPas。许多生产系统会使用这三个库中的两个:例如,使用 HotPDF 生成输出,使用 PDFlibPas 在归档前为其应用长期验证时间戳,或者使用 PDFium Component 在将 HotPDF 产生的内容发送到下游之前对其进行预览。

这三个库均作为 Delphi 和 C++Builder 的原生 Pascal 源码发布,除了 VCL 之外没有其他运行时依赖。PDFium Component 额外捆绑了 PDFium DLL,涵盖了引擎的渲染和解析工作。每个库的产品页面上都包含其完整的 API 参考和当前版本历史记录。

各个库的详细信息:HotPDF ComponentPDFium ComponentPDFlibPas