当 C# 就是产品边界时,QuestPDF 很出色
QuestPDF 值得被认真对比。它是面向 C# 开发者的现代 PDF 生成库,有 fluent API、强类型、大量文档、用于预览和调试的 Companion App,以及在 PDF SDK 里少见的清晰授权模式。
问题不在于“谁能创建 PDF”。两者都可以。真正有价值的问题是:PDF 边界应该放在哪里?是在一个拥有布局、字节和生命周期的 .NET 应用内部,还是作为多个产品和多种语言共同调用的基础设施服务?
快速决策指南
- 当 C# 是文档事实来源、应用必须离线运行,或你需要对已有 PDF 做本地操作时,选择 QuestPDF。
- 当同一层 PDF 能力需要服务 Node、Python、Go、.NET、jobs 和区域系统,并统一通过 HTTP API 调用时,选择 gPdf。
- 当布局变更应该成为模板修订,而不是 C# 重新构建和服务重新部署时,选择 gPdf。
同一类文档,不同的归属模型
使用 QuestPDF 时,应用拥有 PDF 生成。这是真正的优势:C# 紧贴领域模型,本地运行和调试,不需要在运行时调用外部 API。
代价是,你的团队也要拥有生产面的其他部分:
- 渲染所需的 CPU 与内存。
- 每个部署环境中的字体发现和回退。
- 条码库集成和打印质量检查。
- 图表或自定义图形集成所需的原生包与部署问题。
- 监控、重试和失败处理。
- 用户或仓库遍布全球时的区域部署。
- 文档布局变化时的发布流程。
用 gPdf 时,这些边界向外移动:应用发送 DocumentRequest 或 template_id + data,服务负责渲染器、Edge runtime、字体、条码原语、PDF/A 输出和电子发票打包。如果你希望每个细节都留在 C# 中,这不那么有吸引力;如果 PDF 生成应该成为任何技术栈都能调用的工具层,它就更有吸引力。
托管 API 必须诚实回答的三个取舍
大多数“库 vs API”的说法会跳过 .NET 架构师最先问的三个问题。公平的对比必须把它们讲清楚。
1. 文档数据去了哪里。 这篇文章主要讨论发票、对账单和电子发票,这些文档里满是姓名、地址、税号和金额。使用 QuestPDF 时,这些字节在你的进程内构建,不会离开。gPdf 公共 API 会把载荷传给渲染器,但渲染器零保留:请求 JSON 只在 Cloudflare Workers V8 isolate 中保留到本次渲染结束(典型约 4 ms),响应完成后即释放,不会存储、记录、抽样或用于训练;运营日志只包含 HTTP 状态和耗时(安全说明、DPA)。欧盟/全球数据驻留选择以及企业本地部署/私有部署可以进一步缩小或关闭暴露面。即便如此,零配置把生成留在进程内,依然是金融或公共部门团队选择 QuestPDF 的正当理由,有时也是决定性理由。
2. 失败模式。 库没有会宕机的第三方;生成只会在你已经拥有的基础设施上失败。托管 API 会增加一个你无法控制的可用性依赖。采用 gPdf 的正确方式,是把生成调用当作任何外部调用来处理:超时、重试、队列,最好还要有降级兜底。如果文档生成位于关键同步路径上,就要认真权衡“自己运维”和“依赖供应商可用性”。
3. 延迟特征。 进程内生成是没有网络的函数调用。托管调用是一次网络往返。对批处理和异步 jobs,这通常可以忽略;对“用户点击后 PDF 必须立刻出现”的路径,进程内在结构上更快。gPdf 的 Edge PoP 会缩短距离,但它仍然是 TLS 加一次往返,而 QuestPDF 是一次方法调用。
这些都不说明 gPdf 是错误选择;它们定义了 什么时候 gPdf 才是正确选择:文档数据可以离开进程、流程能接受一次网络跳转,并且团队宁愿依赖供应商可用性,也不想自己运营渲染集群。
授权与价格模型
QuestPDF 的公开授权页面说明,只有年总收入超过 100 万美元的公司才需要商业授权。Community tier 对符合条件的个人、开源项目、非营利组织以及低于该收入门槛的公司免费,并采用 MIT 条款。同一公开页面列出两个永久商业档位:Professional 为 999 美元加本地税费,适用于最多 10 名开发者;Enterprise 为 2,999 美元加本地税费,覆盖整个组织且不按开发者计数。两者都包含 1 年更新、不限项目、服务器和部署,并且已获得的最后版本授权不会过期。
它的执行模型也异常轻量。授权用一行代码设置:QuestPDF.Settings.License = LicenseType.Community;。没有 license key,没有激活服务器,而且按 QuestPDF 自己的配置页说明,没有网络调用,也没有数据离开机器。这是诚信申报模型:你选择自己符合的档位。没有按文档计费的供应商账单;付费授权可以在任何地方运行,包括完全离线环境。
gPdf 直接给渲染服务定价。公开 Basic 方案从每月 5 美元起,包含 10 万页,超量从每页 0.00005 美元起。这是一笔供应商账单,但它也移除了单独运营 PDF 生成的项目:不需要渲染集群,不需要 NuGet 部署路径,不需要区域预热池,不需要每个应用打字体包,也没有要打补丁的 PDF 服务。
所以成本比较不是“999 美元 vs 5 美元”,授权只是小项。真正的比较是:
QuestPDF 总成本 = 一次性授权 + 自有托管 + 工程时间 + 值班
gPdf 总成本 = 按页账单(基础设施、字体、扩容和 Edge 网络已包含)
按公开按页价格,gPdf 超量约为每 1,000 页 0.05 美元(100 万页 50 美元,1,000 万页 500 美元)。一次性 2,999 美元的 Enterprise 授权,要接近约 6,000 万页才会单纯按页费打平;这还没有计入 QuestPDF 的托管和工程人月,而这些会把真实分界点进一步推向 gPdf,除非你已经能以很低成本运营渲染基础设施。经验规则是:如果你为了使用库还要建设并配备一个渲染服务,gPdf 通常会在按页账单追上授权费之前很久就在总成本上胜出;如果基础设施已经存在且对你几乎免费,永久授权在规模上会胜出。
开发流程:Fluent C# vs templates
当开发者拥有文档形态时,QuestPDF 的 fluent API 很合适。强类型、方法链、可复用 C# 组件、IDE 重构和 Companion App,都适合 PDF 属于应用代码库的一部分的场景。
gPdf 适合另一种流程。开发者仍然可以直接编写 JSON,但生产系统通常会逐步转向模板。设计师、运营或工程师在 gPdf Studio 调整布局。通过审核的布局成为模板,后端继续用 template_id + data 渲染。
当文档经常变化时,这个差异很重要。如果物流面单、发票、装箱单或对账单布局变了,gPdf 可以保持运行时代码稳定,只移动模板。使用 QuestPDF 时,布局是 C# 代码,常规路径就是改代码、测试、构建、部署,以及准备回滚方案。
两种流程没有绝对优劣:QuestPDF 优化的是希望把文档当代码的 C# 开发者;gPdf 优化的是跨系统共享的运营模板。
合规:两个产品都很认真
这不是那种 gPdf 通过声称竞争对手缺少合规功能来取胜的对比。QuestPDF 当前公开材料列出了很强的标准支持,包括 PDF/A、PDF/UA-1,以及基于 UN/CEFACT Cross Industry Invoice (CII) 标准的 ZUGFeRD 2.1 / Factur-X 示例来实现 EN 16931 电子发票。该示例设置 PdfA = true,用 AddAttachment() 嵌入 factur-x.xml 载荷,通过 XMP 元数据扩展文档,并用 veraPDF(针对 PDF/A-3b)和 Mustang Project(针对 ZUGFeRD)验证结果。这是完整且诚实的做法,只是你的管线要拥有每一步。
gPdf 把同一类标准包装成 API 契约。JSON Render 通过 settings.profile 暴露 6 种 PDF/A 配置(1b、2b、3b、4、2u、3u)以及 PDF/UA-1,Template Render 复用同一文档模型,E-Invoice Render 则暴露专用的 POST /api/v1/e-invoice/render 端点,生成带嵌入式 EN 16931 CII XML 的 Factur-X / ZUGFeRD PDF/A-3b 包。它与 QuestPDF 做法的差异在于服务替你承担了什么:gPdf 在服务端执行 PDF/A-3b 和电子发票验证,支持同步内联返回或轮询对象交付,并把欧盟或全球数据驻留作为请求设置提供,而不是让你自己拼装和运维这些步骤。当验证应该留在你自己的 .NET 管线中,QuestPDF 合适;当它应该成为许多系统共享的托管契约,gPdf 合适。
字体与条码:真正要比的是集成工作量
QuestPDF 的字体模型很有能力。它默认自带 Lato 2.015,自动加载系统字体和部署目录字体,允许通过 FontManager 注册自定义字体,并支持回退链。这给了开发者控制权。但同一份文档也坦率指出了问题:在多数云部署中,可用字体很少甚至没有,可能导致意外结果;因此建议禁用环境字体,并显式注册需要的字体。换句话说,在容器或无服务器目标上,字体环境要由你规划、打包和测试;缺字形要么变成占位字符,要么在启用 CheckIfAllTextGlyphsAreAvailable 时抛出异常。
gPdf 把字体做成服务边界的一部分。渲染器内置多文字系统字体集,包括拉丁、希腊、西里尔、阿拉伯、希伯来、孟加拉、泰米尔、泰文、越南文、等宽字体,以及按文字系统回退到 Noto KR / JP / SC 的 CJK。它通过隐式自动选择处理默认字体选择,也通过 prefer 或 strict 处理显式选择。调用方不需要发布 CJK 字体,不需要在 .NET 应用里注册 Noto 字体资源,也不需要按部署目标调回退。调用方发送数据,渲染器负责字体环境,而且每个区域一致。
条码对比也是类似形态。QuestPDF 的条码文档展示了一个可靠做法:使用 ZXing.Net 生成矢量 SVG;同时也明确说明 ZXing.Net 不 包含在 QuestPDF 包中,需要从 NuGet 安装并接入:
// QuestPDF: add the separate ZXing.Net package, encode, render to SVG, embed.
// dotnet add package ZXing.Net
var writer = new ZXing.BarcodeWriterSvg {
Format = ZXing.BarcodeFormat.CODE_128,
Options = new ZXing.Common.EncodingOptions { Width = 320, Height = 80 }
};
string svg = writer.Write("INV-2026-001").Content;
container.Svg(svg);
// GS1-128 with Application Identifiers and FNC1 framing is hand-wired on top.
在 gPdf 中,条码生成是一等 gPdf schema 元素。请求声明格式、内容、物理尺寸和可选的人工可读文本行;GS1 格式是原生的,所以应用标识符(AI)可以直接写进 content:
{
"type": "barcode",
"format": "gs1_128",
"content": "(01)00012345678905(21)SN12345",
"x": 12, "y": 60, "width": 80, "height": 18,
"barcode_text": { "enabled": true, "position": "bottom" }
}
对单个 .NET 应用来说,安装 ZXing.Net 并测试输出可能很简单。对许多服务和模板来说,尤其是需要 GS1-128、SSCC、GTIN、GS1 DataMatrix 或 GS1 QR 并带人工可读文本行的物流与零售工作负载,把条码行为推入文档 API,比在每个服务里重复实现同一套 ZXing 接线更容易维护。
QuestPDF 明显占优的地方
除了上面提到的离线运行、让文档数据留在你的边界内,以及当 PDF 代码本身就是产品一部分、团队需要检查、扩展或拥有渲染路径时,QuestPDF 还有两个能力区域明确在 gPdf 范围之外:
- 对已有 PDF 的操作。 QuestPDF 可以加载已有文件并合并、选择 / 重排 / 反转 / 过滤页面、应用叠加层、添加附件、设置 XMP 元数据、为 Web 交付做线性化,并用 40/128/256 位安全级别加密和解密。gPdf 可以给自己生成的 PDF 设置密码保护和权限门控,但不会打开、合并或解密不是它创建的文件。
- 图表、地图和自定义图形。 QuestPDF 集成图表库(ScottPlot、LiveCharts、Microcharts)、嵌入 Mapbox 地图,并提供可任意绘制 2D 图形的 SkiaSharp Canvas。gPdf 可以用
path元素(SVG 路径数据)绘制矢量图,或嵌入上游生成的 SVG / PNG 图表,但没有内置图表引擎、地图或 Canvas;如果数据驱动图表是文档核心,这套工具链更适合留在 QuestPDF。
gPdf 明显占优的地方
当组织不希望每个产品团队都拥有自己的 PDF 服务时,gPdf 会占优:多语言技术栈、全球工作流,以及 ERP / OMS / WMS / 电商 / 金融科技 / 票务系统从结构化数据渲染文档,并且模板可以独立于代码变化。在这些环境里,本地库一开始常常很便宜,后来会变成一组服务:每种语言一套服务、每个区域一条部署路径、每个容器一份字体计划、每个团队一组条码回归。gPdf 把这组服务变成一个 HTTP 契约。
无服务器场景能最清楚地说明边界。在 AWS Lambda、Cloud Run 或 Azure Functions 上,QuestPDF 仍然运行在应用内部;你的团队要打包 .NET 运行时、字体、原生依赖,以及高峰 PDF 工作所需的 CPU / 内存,并且要负责冷启动。gPdf 已经是渲染服务:函数向 Edge POST 一个小的 template_id + data 请求并拿回 PDF 字节流,不需要预热渲染器,也不需要扩容每个区域的 Worker。
迁移形态
从 QuestPDF 迁移到 gPdf 不是逐行改写。它是边界变化:原来构建 PDF 的 C# 代码,要变成 JSON 文档请求或已发布模板。
迁移前 / 迁移后 — C# 文档构建调用收敛为一次 HTTP POST(点击展开)
- // Before: generate the PDF inside a .NET application.
- Document.Create(container =>
- {
- container.Page(page =>
- {
- page.Size(PageSizes.A4);
- page.Margin(30);
- page.Header().Text("Invoice").FontSize(24).SemiBold();
- page.Content().Column(column =>
- {
- column.Item().Text($"Invoice number: {invoice.Number}");
- column.Item().Text($"Total: {invoice.Total:C}");
- });
- });
- })
- .GeneratePdf("invoice.pdf");
+
+ // After: render through the shared gPdf template from C#.
+ using System.Net.Http.Headers;
+ using System.Net.Http.Json;
+
+ using var client = new HttpClient();
+ client.DefaultRequestHeaders.Authorization =
+ new AuthenticationHeaderValue("Bearer", key);
+
+ var response = await client.PostAsJsonAsync(
+ "https://api.gpdf.com/api/v1/template-render",
+ new {
+ template_id = "invoice-v2",
+ data = new {
+ invoice_number = invoice.Number,
+ total = invoice.Total,
+ currency = invoice.Currency
+ }
+ });
+
+ response.EnsureSuccessStatusCode();
+ byte[] pdfBytes = await response.Content.ReadAsByteArrayAsync();
边界移动之后,布局变化可以成为模板修订,而不是应用部署。应用仍然拥有业务数据和工作流决策;gPdf 拥有渲染。
价格与来源说明
本页 QuestPDF 信息已于 2026-06-02 对照 QuestPDF 官方来源核对:License and Pricing、License configuration、Features Overview、Companion App features、Barcodes、Font management 和 ZUGFeRD example。价格和功能页面可能变化;采购团队在做购买决策前应重新核对供应商页面。QuestPDF 及相关标识属于各自所有者,本对比未获其背书。
相关 PDF 生成场景
如果你的评估从 QuestPDF 替代方案或 .NET PDF 生成开始,可以按边界继续拆分:跨语言服务共享看 JSON 转 PDF API 和 模板 PDF API;发票和电子发票看 发票 PDF API、PDF/A API、Factur-X API 与 ZUGFeRD API;条码和物流面单看 GS1 条码 API 与 物流面单 API。
FAQ
gPdf 会取代 QuestPDF 吗?
不会。gPdf 替代的是为结构化业务文档运营 PDF 生成服务这件事。当 PDF 应该在应用内部生成时,QuestPDF 仍然是很强的本地 C# 库。
QuestPDF 免费吗?
QuestPDF 公开授权页面说明,Community tier 对符合条件的个人、开源项目、非营利组织以及年总收入低于 100 万美元的公司按 MIT 条款免费。超过该门槛的公司需要永久商业授权:Professional 为 999 美元加本地税费,最多 10 名开发者;Enterprise 为 2,999 美元加本地税费,覆盖整个组织。每个授权都包含 1 年更新。
gPdf 能像 QuestPDF 一样生成图表或地图吗?
没有内置引擎。QuestPDF 集成图表库(ScottPlot、LiveCharts、Microcharts)、Mapbox 地图,以及可在文档内部渲染的 SkiaSharp Canvas。gPdf 仍然可以通过 path 元素(接受 SVG 路径数据)和形状绘制矢量图表,或把任意图表库生成的 SVG / PNG 作为 image 嵌入。区别是 QuestPDF 在进程内计算并渲染图表,而使用 gPdf 时你先生成图形,再由 gPdf 放置。如果数据驱动图表或地图是文档核心,QuestPDF 更合适。
哪个产品更便宜?
取决于边界。对符合 Community 条款或已经运营渲染基础设施的 .NET 团队,QuestPDF 可能更便宜。当替代方案是跨产品或跨区域建设、托管和维护 PDF 服务时,gPdf 可能更便宜。
gPdf 会存储或记录我的文档数据吗?
不会。你发送的 JSON 和 gPdf 返回的 PDF 都不会被存储。每个请求都在单个 Cloudflare Workers V8 isolate 内渲染,只在内存中保留到渲染完成,典型约 4 ms;响应流完成后即释放。gPdf 不会保留、记录、抽样或训练 DocumentRequest 内容。运营日志只保留 HTTP 状态和耗时 30 天,不包含请求正文。参见 安全说明、隐私政策 和 DPA。对于完全不能传输数据的工作负载,本地部署/私有部署可以把数据留在你的边界内。
QuestPDF 可以在没有互联网的情况下运行吗?
可以。QuestPDF 的授权配置页面说明,没有 license key 或激活服务器,计算在本地完成。这是选择 QuestPDF 的最明确理由之一。
gPdf 可以渲染任意 C# QuestPDF 布局吗?
不能。gPdf 不执行 C# 布局代码。迁移意味着把文档形态转换为 gPdf JSON 请求或保存的 gPdf 模板。