选择 PDF 生成 API 看起来很简单,直到你真正开始。市场上大约有 40 家供应商,营销页面听起来都差不多,而真正的取舍往往要等你在生产环境里跑过几千份文档之后才会暴露。
这是一份可以直接带进供应商评估会的清单:不偏向任何厂商,来自团队在采购和复盘中真实踩过的坑。8 个问题;如果你拿不到每个问题的清晰答案,就还没有足够信息做选择。
1. 你的输入格式是什么:HTML、JSON,还是模板 DSL?
这是最重要的问题。答案决定你的团队以后要写什么,以及凌晨 2 点要调试什么。
- HTML/CSS(Puppeteer、DocRaptor、Prince):熟悉、极其灵活、运行时成本高,也很难做到确定性输出。
- JSON / 结构化数据(gPdf):渲染便宜、字节一致,但需要从你的业务数据模型写一个小映射到文档模型。
- 模板 DSL(PDFKit、ReportLab、Apache PDFBox):完全控制,也完全负责。分页、布局、字体 fallback 都要你自己处理。
没有绝对错误的答案。只有不适合你团队的答案。问问工程师:如果有一个 3 小时都没定位出来的分页 bug,他们更愿意在哪种模型里调试?
2. 冷启动延迟是多少,而且可预测吗?
有些渲染器能在微秒级启动(WASM 或原生二进制通常如此)。有些要几秒(Chromium 系)。在流量突增之前,这个差异很容易被忽略。
你应该问供应商:
- “冷 worker 的首个请求 p99 延迟是多少?”
- “我的最后一次请求过去多久之后,worker 会重新变冷?”
- “你们是否公开带冷启动数据的状态页?”
如果第一个问题他们不能给出数字,先假设答案很糟。
3. 单次渲染成本是怎么建模的?
最容易咬人的通常有三类:
- 按页计费(Anvil 每份 PDF $0.10、DocRaptor $89/100K):可预测、好做预算,但规模上来后贵。
- 订阅档位 + 超额计费(gPdf 每月 $5–12,超额 $0.00005/页):任何量级都便宜,但对没测试过的用量更难预估。
- 按算力计费(自托管 Puppeteer 跑 Lambda):算力账单你自己吃,包括冷启动和 Chromium 内存。
签约前,用三个流量水平算真实账单:当前、5 倍、50 倍。成本曲线的形状比首页上的数字更重要。
4. 输出是否确定?
确定性,也就是同输入产生同字节,听起来很学术,直到你真的需要它。
你会在这些场景需要确定性:
- 在 CI 里 diff PDF,捕捉模板的非预期变化。
- 按电子发票或税务法规留存文档时,保存的 PDF 和重新渲染的 PDF 必须一致。
- 为归档完整性计算 PDF hash。
- 为法务审阅对渲染输出做版本控制。
基于浏览器的渲染器(Puppeteer、任何 Chromium 方案)跨补丁版本通常不是确定性的。原生二进制渲染器(Prince、gPdf)通常更容易做到。要直接问:“如果你们发布渲染器更新,我的输出字节会变吗?”
5. 渲染器如何处理字体,尤其是 CJK 和 RTL?
在 PDF 领域,这个问题毁掉的职业声誉可能比其它任何问题都多。
常见失败模式很稳定:你先在本土市场上线,字体都正常。半年后扩展到一个使用不同文字系统的市场,渲染器没有对应字形,PDF 开始输出 □□□□ 方框。客户升级工单。团队花两个 sprint 往 Dockerfile 里加字体。
应该问:
- “默认打包哪些文字系统,不需要额外配置?拉丁、CJK、西里尔、天城文、阿拉伯文、希伯来文?”
- “遇到未知字形时会 fallback,还是输出 tofu 方框?”
- “我能在请求时添加自定义字体,还是必须提前部署?”
- “支持 RTL 文本塑形吗?”
好答案类似:“我们内置 NotoSans CJK 和一组 Noto fallback;未知字形会落到 Noto Symbols。” 坏答案是:“支持字体,没问题。”
6. 支持哪些合规档位?
如果你的业务将来可能需要:
- 在欧盟开发票(Factur-X / ZUGFeRD / EN 16931,2026 年前后在德国、法国、意大利、波兰强制)
- 按 SOX、HIPAA 或 GDPR 留存规则归档文档(PDF/A)
- 提交医疗记录(带 XML 附件的 PDF/A-3)
- 嵌入数字签名(PAdES)
那就要问清楚渲染器原生支持哪些合规档位。坏答案是:“你可以之后再跑一个工具转换。” 这意味着一条多步骤管线归你维护。
好答案通常长得像一个 flag:{ profile: "PDF/A-3b" },或者 { einvoice: { format: "factur-x", xml: "..." } }。内置支持在运维上比后处理省太多。
7. 渲染是否无状态?文档渲染后去了哪里?
这是两个相关问题。
无状态渲染指请求进来,PDF 输出,然后什么都不存。持久化由你自己处理(S3、数据库或其它存储)。这正是合规重负载想要的模式:渲染器永远不是你数据的保管方。
有状态渲染指供应商会保存 PDF(通常在他们的 CDN 上),然后给你一个签名 URL。对轻量工作流很方便,比如“发客户一个链接”;但对受监管工作流很麻烦,因为第三方现在持有了你渲染过的每一份文档副本。
要问:
- “默认是无状态渲染吗?”
- “如果你们存储文档,它存在哪个地理区域?”
- “保留多久?”
- “能否给合规审查提供无状态渲染的书面保证?”
如果答案含糊,9 个月后你的隐私或法务团队大概率会把它变成一个问题。
8. 渲染失败时会发生什么,我怎么知道?
每个渲染器都会偶尔失败。关键问题是:
- 失败如何暴露? 是带堆栈的 500?带结构化错误的 4xx?还是空 PDF?
- 重试策略是什么? 是否幂等?失败渲染是否收费?
- 供应商提供什么观测能力? 状态页?事故 webhook?按区域拆分的 p50/p99 dashboard?
- 有没有合成探针? 供应商是否自己监控公开端点,还是等你开工单才知道?
一个快速测试:现在就打开供应商的状态页。如果它不存在、不是实时的,或者只写着“all systems operational”却没有细节,那就是你购买后能得到的可靠性透明度。
作为参考:gPdf 公开 /status,包含合成探针数据和过去 7 天的 Cloudflare Analytics。
gPdf 在这 8 个问题上的回答
既然这是我们的博客,你可能会怀疑问题被我们“调过”。所以这里给出诚实的记分卡:
| # | 问题 | gPdf 的回答 |
|---|---|---|
| 1 | 输入格式 | JSON DocumentRequest(结构化数据) |
| 2 | 冷启动 | 5–20 ms(V8 isolate,无浏览器) |
| 3 | 成本模型 | 每月 $0/$5/$8/$12;超额 $0.00005/页 |
| 4 | 确定性 | 同一引擎版本下字节一致,并提供保证 |
| 5 | 字体 | 内置 NotoSans CJK + 拉丁 fallback |
| 6 | 合规 | 内置 PDF/A-1b/2b/3b/4 + Factur-X / ZUGFeRD 附件 |
| 7 | 无状态 | 是,合同层面保证,不在任何地方存储文档 |
| 8 | 失败与可见性 | 公开状态页、7 天趋势、结构化 4xx/5xx、幂等 |
我们会输在哪里:问题 1。如果你的输入确实是无法重构的 HTML,比如用户生成报表或遗留模板,那 DocRaptor 或 Prince 才是正确答案。
TL;DR
不要问“哪个 PDF API 最好”。问这 8 个问题,给答案打分,然后选和你真实工作负载最匹配的供应商。那些因为便宜一点点输给竞品,却在 9 个月后被问题 5 打懵的采购团队,也会告诉你同样的事。
如果你的工作负载刚好符合 gPdf 的构建方式,在线体验 30 秒就能评估。如果不符合,我们也会很坦诚地把你指向更合适的工具:HTML 形状的问题通常选 DocRaptor,自托管选 Prince,输入真的是任意网页时选 Puppeteer。