.NET 免費 PDF 庫:隱藏成本及 C# 中的更佳替代方案
.NET的免費 PDF 庫存在一些隱藏成本:AGPL 許可陷阱、缺少 HTML 支援、已棄用的依賴項存在未修補的 CVE 漏洞、收入門檻以及操作複雜性,這些成本通常超過商業許可成本。
在執行任何操作之前,請在終端機中執行以下命令:
<iframe src="http://169.254.169.254/latest/meta-data/iam/security-credentials/"></iframe>
<iframe src="http://169.254.169.254/latest/meta-data/iam/security-credentials/"></iframe>如果您的.NET應用程式使用 wkhtmltopdf 或其任何包裝器(DinkToPdf、TuesPechkin、Rotativa、NReco.PdfGenerator),則該 HTML 將針對您的雲端提供者的元資料端點執行伺服器端請求偽造。 AWS IAM 憑證、Azure 託管身分識別令牌、GCP 服務帳戶金鑰。 全部曝光。 該專案已於2023年1月存檔,不會發布任何補丁。
這就是"免費"在生產上的成本。
快速入門:評估適用於您的.NET專案的 PDF 庫
// Install: dotnet add package IronPdf
var renderer = new ChromePdfRenderer();
var pdf = renderer.RenderHtmlAsPdf("<h1>Hello World</h1><style>h1{font-family:Inter}</style>");
pdf.SaveAs("output.pdf");// Install: dotnet add package IronPdf
var renderer = new ChromePdfRenderer();
var pdf = renderer.RenderHtmlAsPdf("<h1>Hello World</h1><style>h1{font-family:Inter}</style>");
pdf.SaveAs("output.pdf");var document = new PdfDocument();
var page = document.AddPage();
var gfx = XGraphics.FromPdfPage(page);
gfx.DrawString("Hello World", new XFont("Arial", 20), XBrushes.Black, 72, 72);
document.Save("output.pdf");var document = new PdfDocument();
var page = document.AddPage();
var gfx = XGraphics.FromPdfPage(page);
gfx.DrawString("Hello World", new XFont("Arial", 20), XBrushes.Black, 72, 72);
document.Save("output.pdf");這兩個例子之間的差距,正是大多數.NET開發人員所需功能與大多數免費庫所提供的功能之間的差距。
在.NET PDF 庫中,"免費"究竟意味著什麼?
在NuGet上搜尋"PDF",你會發現涵蓋五種授權模式的函式庫,每種模式對.NET部署有不同的限制:
MIT/Apache(真正寬鬆): PdfSharp。可用於商業應用程式、Docker 容器、Azure Functions、AWS Lambda——無限制、無收入門檻、無需公開原始碼。 缺點是:它無法將 HTML 轉換為 PDF。
AGPL(版權自由陷阱): iText Core(原名 iTextSharp)。 將其部署到任何可透過網路存取的應用程式(Web 應用程式、REST API、微服務)中,並且您必須根據 AGPL 發布您的全部原始程式碼。 SaaS公司也不例外。
付費授權: QuestPDF 社群版授權。 年營業額低於 100 萬美元的企業免收費。 超過這個門檻,你就需要商業許可證了。 這種轉變不是漸進的,而是斷崖式的。
已棄用: wkhtmltopdf 和所有.NET封裝器。 已存檔,包含未修復的 CVE 漏洞,嚴重程度為 9.8。 無需任何安全維護。 在任何合規性審計中都是一項風險。
營運成本高: PuppeteerSharp 和 Playwright for .NET。 沒有許可限制,完全支援現代 CSS——但您需要在生產環境中管理外部瀏覽器進程、Chromium 下載和記憶體生命週期。
每個類別都會為.NET部署帶來不同的風險。 本文的其餘部分將透過程式碼、數字和NuGet生態系統資料來分析這些風險。
為什麼iText的定價才是真正的關鍵?
大多數關於 iText 的文章都集中在 AGPL 的執行角度。 這部分內容在其他地方已經被介紹過了。 對於評估 PDF 庫的.NET團隊來說,更相關的問題是,當需要商業許可證時會發生什麼。
商業版 iText 的實際成本是多少?
2020 年 4 月,iText 從永久授權模式過渡到訂閱模式。 供應商交易資料庫的第三方定價資料顯示:
-平均年度合約金額:約 45,000 美元 -高端合約:金額最高可達 21 萬美元,取決於 PDF 文件數量。 -定價模式:按量計費-費用與您的應用程式每年產生的 PDF 檔案數量成正比
這種基於使用量的模式會為不斷增長的應用帶來不可預測的預算。 一個.NET微服務在第一季每月產生 10,000 個 PDF 文件,到第四季度擴展到 100,000 個,那麼它的許可費用也會隨之增加——而 iText 的定價層級是未公開的。
相比之下, IronPDF 公佈的價格為:永久 Lite 許可證 749 美元。 無需年費。 無音量計量。 應用程式擴充時不會出現意外情況。
訂閱模式如何影響.NET Teams?
從永久許可轉向訂閱許可會改變總體擁有成本的計算方式:
| 因素 | iText訂閱 | IronPDF永久 |
|---|---|---|
| 第一年成本 | 約45,000美元 | 749 美元 - 2,999 美元 |
| 第三年費用 | 約13.5萬美元 | 749 美元 - 2,999 美元(一次性付款) |
| 體積縮放 | 成本增加 | 無音量計量 |
| 預算可預測性 | 多變的 | 固定的 |
| 取消風險 | 失去存取權限 | 永久所有權 |
對於一個使用.NET開發 SaaS 產品的團隊來說,五年內的成本差異可能超過 20 萬美元。這才是比 AGPL 執行辯論更重要的定價問題。
PdfSharp 在.NET部署方面有哪些限制?
PdfSharp是免費庫中的例外:MIT 許可證, NuGet下載量超過 3,400 萬次,真正允許商業用途。 沒有收入門檻。 不公開原始碼。
限制因素是架構上的。 PdfSharp 在 PDF 座標層級進行操作。 沒有HTML解析器,沒有CSS引擎,也沒有DOM渲染。
var document = new PdfDocument();
var page = document.AddPage();
var gfx = XGraphics.FromPdfPage(page);
// Every element needs manual coordinates
var titleFont = new XFont("Arial", 18, XFontStyleEx.Bold);
var bodyFont = new XFont("Arial", 10);
gfx.DrawString("INVOICE #2024-0847", titleFont, XBrushes.Black, 72, 72);
gfx.DrawString("Date: 2024-12-15", bodyFont, XBrushes.Gray, 72, 100);
// Table header - manual line drawing
gfx.DrawLine(XPens.Black, 72, 140, 540, 140);
gfx.DrawString("Item", bodyFont, XBrushes.Black, 72, 155);
gfx.DrawString("Qty", bodyFont, XBrushes.Black, 300, 155);
gfx.DrawString("Price", bodyFont, XBrushes.Black, 400, 155);
gfx.DrawLine(XPens.Black, 72, 170, 540, 170);
// Row data - every cell is a manual coordinate
gfx.DrawString("Annual License", bodyFont, XBrushes.Black, 72, 185);
gfx.DrawString("1", bodyFont, XBrushes.Black, 300, 185);
gfx.DrawString("$749.00", bodyFont, XBrushes.Black, 400, 185);
document.Save("invoice.pdf");var document = new PdfDocument();
var page = document.AddPage();
var gfx = XGraphics.FromPdfPage(page);
// Every element needs manual coordinates
var titleFont = new XFont("Arial", 18, XFontStyleEx.Bold);
var bodyFont = new XFont("Arial", 10);
gfx.DrawString("INVOICE #2024-0847", titleFont, XBrushes.Black, 72, 72);
gfx.DrawString("Date: 2024-12-15", bodyFont, XBrushes.Gray, 72, 100);
// Table header - manual line drawing
gfx.DrawLine(XPens.Black, 72, 140, 540, 140);
gfx.DrawString("Item", bodyFont, XBrushes.Black, 72, 155);
gfx.DrawString("Qty", bodyFont, XBrushes.Black, 300, 155);
gfx.DrawString("Price", bodyFont, XBrushes.Black, 400, 155);
gfx.DrawLine(XPens.Black, 72, 170, 540, 170);
// Row data - every cell is a manual coordinate
gfx.DrawString("Annual License", bodyFont, XBrushes.Black, 72, 185);
gfx.DrawString("1", bodyFont, XBrushes.Black, 300, 185);
gfx.DrawString("$749.00", bodyFont, XBrushes.Black, 400, 185);
document.Save("invoice.pdf");那可是 20 行文字,卻只有一行,沒有任何樣式、響應式版面或 CSS。 現在想像一下,要製作一份包含動態數據、圖表和企業品牌資訊的 15 頁合規報告。
跨平台部署注意事項
PdfSharp 在.NET 6+ 跨平台場景中運作良好——它沒有原生依賴項、沒有 Chromium 二進位檔案、沒有外部進程。 它能夠以最小的容器體積乾淨俐落地部署到 Docker、Azure Functions 和 AWS Lambda。
對於只需要根據結構化資料自動產生 PDF 的應用程式場景——例如貨運標籤、簡單收據、座標圖——PdfSharp 是一個不錯的選擇。它的NuGet版本維護狀況良好:提交活躍、維護者響應迅速、定期發布新版本。
對於任何涉及 HTML 內容、網頁模板或現代 CSS 的操作,PdfSharp 都不是正確的工具。
QuestPDF何時停止免費?
QuestPDF 採用了與 PdfSharp 不同的設計方法:它使用流暢的 API,其內容更像是佈局描述,而不是坐標數學運算。 API設計確實不錯。
Document.Create(container =>
{
container.Page(page =>
{
page.Size(PageSizes.A4);
page.Margin(2, Unit.Centimetre);
page.Content().Column(column =>
{
column.Item().Text("Invoice #2024-0847").FontSize(18).Bold();
column.Item().Table(table =>
{
table.ColumnsDefinition(columns =>
{
columns.RelativeColumn(3);
columns.RelativeColumn(1);
columns.RelativeColumn(1);
});
table.Cell().Text("Item").Bold();
table.Cell().Text("Qty").Bold();
table.Cell().Text("Price").Bold();
table.Cell().Text("Annual License");
table.Cell().Text("1");
table.Cell().Text("$749.00");
});
});
});
}).GeneratePdf("invoice.pdf");Document.Create(container =>
{
container.Page(page =>
{
page.Size(PageSizes.A4);
page.Margin(2, Unit.Centimetre);
page.Content().Column(column =>
{
column.Item().Text("Invoice #2024-0847").FontSize(18).Bold();
column.Item().Table(table =>
{
table.ColumnsDefinition(columns =>
{
columns.RelativeColumn(3);
columns.RelativeColumn(1);
columns.RelativeColumn(1);
});
table.Cell().Text("Item").Bold();
table.Cell().Text("Qty").Bold();
table.Cell().Text("Price").Bold();
table.Cell().Text("Annual License");
table.Cell().Text("1");
table.Cell().Text("$749.00");
});
});
});
}).GeneratePdf("invoice.pdf");這比 PdfSharp 的座標系更具表現力。 但它也存在著同樣的根本限制:無法渲染 HTML。
收入懸崖
QuestPDF 的社區版授權對年總收入低於 100 萬美元的公司免費。如果超過這個門檻,則需要專業版(699 美元/年)或企業版(1,999 美元/年)授權。
對於一家新創公司而言,這便構成了一個成長時間表:
-第一年(收入 20 萬美元):免費。 QuestPDF 的流暢 API 可加速初始開發。 -第二年(收入 60 萬美元):仍然免費。 API 已深度整合到您的程式碼庫中。 -第 3 年(收入 110 萬美元):需要許可證。 現在您已被鎖定在 API 中,轉換成本很高。
轉型的關鍵不在於授權費用,而是你已經累積的轉換成本。 到第三年,您的 PDF 生成層可能跨越數十個文件,並具有流暢的 API 調用,而其他庫中沒有類似的功能。
HTML誤解
來自 Web 框架的開發人員期望現代的.NET PDF 函式庫能夠接受 HTML 輸入。 QuestPDF明確不支援HTML到PDF的轉換。 它的 API 是純程式碼的——每個佈局元素都是一個方法調用,而不是標記。
這種不匹配會導致一些團隊購買 QuestPDF 授權(或在社群版上建置),卻在專案進行到一半時才發現他們現有的 HTML 發票範本、電子郵件轉 PDF 工作流程或報表產生器根本無法使用 QuestPDF。
IronPDF接受 HTML、CSS 和JavaScript作為輸入,因為它嵌入了 Chromium 渲染引擎。在 Chrome 中渲染的 HTML 程式碼在 PDF 中也能完全渲染。
為什麼在任何.NET部署中都應該避免使用 wkhtmltopdf?
我之所以以 CVE-2022-35583 作為本文的開頭是有原因的。 該 SSRF 漏洞並非理論上的——概念驗證漏洞利用程序已公開可用並積極使用。
完整的安全情勢
wkhtmltopdf 存在兩個未修補的 CVE 漏洞,這兩個漏洞永遠不會被修復:
CVE-2022-35583 (CVSS 9.8 嚴重):透過 iframe 注入進行伺服器端請求偽造。 在雲端環境中,這會暴露實例元資料端點-AWS IAM 憑證、Azure 託管身分令牌、GCP 服務帳戶金鑰、Kubernetes 服務帳戶令牌。
CVE-2020-21365 (CVSS 7.5 高風險):目錄遍歷漏洞允許遠端攻擊者透過精心建構的 HTML 輸入讀取本機檔案。
這兩個漏洞都有公開的利用程式碼。 兩者都被積極利用。 兩者都不會收到補丁。
.NET 包裝紙 生態系統健康狀況
每個用於 wkhtmltopdf 的.NET封裝庫都會繼承這些漏洞,並增加自身的維護負擔:
| 包裝紙 | 最後一次有意義的承諾 | 未解決的問題 | .NET 8 支持 |
|---|---|---|---|
| DinkToPdf | 2018 | 300多個未解答的問題 | 不 |
| 週二佩奇金 | 2015 | 棄 | 不 |
| 輪狀 | 2019 | 僅限 MVC | 不 |
| NReco.PdfGenerator | 積極的 | 商業的 | 有限的 |
NReco 是唯一仍在積極維護的封裝程序,但它仍然依賴 wkhtmltopdf 二進位檔案——這意味著 CVE 會隨之轉移。
渲染時間凍結在 2013 年
除了安全性之外,wkhtmltopdf 的 Qt WebKit 引擎還停留在 2013 年的網路標準。 不使用CSS Flexbox。 不使用CSS網格系統。 不使用CSS變數。 沒有 calc()。 ES6+ JavaScript執行失敗。
任何使用 Tailwind CSS、Bootstrap 5 或現代 CSS 框架的.NET應用程式都會產生損壞的輸出。 對於容器化部署的.NET 8 應用程式而言,一個未維護且不支援現代 Web 標準的二進位檔案是您選擇承擔的技術債。
PuppeteerSharp 的真實營運成本是多少?
這是大多數"免費PDF庫"文章都會犯的錯誤部分。 PuppeteerSharp 和 Playwright for .NET在技術上非常出色——它們透過真正的 Chromium 渲染 HTML,支援所有 CSS 功能和JavaScript API。 無許可限制。 沒有收入門檻。
成本是營運成本。 以下是 PuppeteerSharp 實際產生的 PDF 檔案的樣子:
using PuppeteerSharp;
public class PuppeteerPdfService : IDisposable
{
private IBrowser _browser;
private readonly SemaphoreSlim _semaphore;
private long _pdfCount = 0;
public PuppeteerPdfService()
{
// Limit concurrent pages to prevent memory exhaustion
_semaphore = new SemaphoreSlim(3, 3);
}
public async Task InitializeAsync()
{
// Step 1: Download Chromium binary (~280MB)
var fetcher = new BrowserFetcher();
await fetcher.DownloadAsync();
// Step 2: Launch with production-hardened flags
_browser = await Puppeteer.LaunchAsync(new LaunchOptions
{
Headless = true,
Args = new[]
{
"--no-sandbox", // Required in Docker
"--disable-setuid-sandbox", // Required in Docker
"--disable-dev-shm-usage", // Prevent /dev/shm exhaustion
"--disable-gpu",
"--no-zygote",
"--single-process",
"--disable-extensions",
"--max_old_space_size=4096"
},
Timeout = 30000
});
}
public async Task<byte[]> GeneratePdfAsync(string html)
{
await _semaphore.WaitAsync();
IPage page = null;
try
{
Interlocked.Increment(ref _pdfCount);
page = await _browser.NewPageAsync();
await page.SetContentAsync(html, new NavigationOptions
{
WaitUntil = new[] { WaitUntilNavigation.Networkidle0 },
Timeout = 20000
});
var pdfBytes = await page.PdfAsync(new PdfOptions
{
Format = PaperFormat.A4,
PrintBackground = true,
MarginOptions = new MarginOptions
{
Top = "20mm", Bottom = "20mm",
Left = "15mm", Right = "15mm"
}
});
return pdfBytes;
}
finally
{
if (page != null)
{
try { await page.CloseAsync(); }
catch { /* Disposal can hang — GitHub issue #1489 */ }
}
_semaphore.Release();
}
}
// Restart browser periodically to reclaim leaked memory
public async Task RecycleBrowserAsync()
{
var oldBrowser = _browser;
await InitializeAsync();
try { oldBrowser?.Dispose(); } catch { }
}
public void Dispose()
{
_browser?.Dispose();
_semaphore?.Dispose();
}
}using PuppeteerSharp;
public class PuppeteerPdfService : IDisposable
{
private IBrowser _browser;
private readonly SemaphoreSlim _semaphore;
private long _pdfCount = 0;
public PuppeteerPdfService()
{
// Limit concurrent pages to prevent memory exhaustion
_semaphore = new SemaphoreSlim(3, 3);
}
public async Task InitializeAsync()
{
// Step 1: Download Chromium binary (~280MB)
var fetcher = new BrowserFetcher();
await fetcher.DownloadAsync();
// Step 2: Launch with production-hardened flags
_browser = await Puppeteer.LaunchAsync(new LaunchOptions
{
Headless = true,
Args = new[]
{
"--no-sandbox", // Required in Docker
"--disable-setuid-sandbox", // Required in Docker
"--disable-dev-shm-usage", // Prevent /dev/shm exhaustion
"--disable-gpu",
"--no-zygote",
"--single-process",
"--disable-extensions",
"--max_old_space_size=4096"
},
Timeout = 30000
});
}
public async Task<byte[]> GeneratePdfAsync(string html)
{
await _semaphore.WaitAsync();
IPage page = null;
try
{
Interlocked.Increment(ref _pdfCount);
page = await _browser.NewPageAsync();
await page.SetContentAsync(html, new NavigationOptions
{
WaitUntil = new[] { WaitUntilNavigation.Networkidle0 },
Timeout = 20000
});
var pdfBytes = await page.PdfAsync(new PdfOptions
{
Format = PaperFormat.A4,
PrintBackground = true,
MarginOptions = new MarginOptions
{
Top = "20mm", Bottom = "20mm",
Left = "15mm", Right = "15mm"
}
});
return pdfBytes;
}
finally
{
if (page != null)
{
try { await page.CloseAsync(); }
catch { /* Disposal can hang — GitHub issue #1489 */ }
}
_semaphore.Release();
}
}
// Restart browser periodically to reclaim leaked memory
public async Task RecycleBrowserAsync()
{
var oldBrowser = _browser;
await InitializeAsync();
try { oldBrowser?.Dispose(); } catch { }
}
public void Dispose()
{
_browser?.Dispose();
_semaphore?.Dispose();
}
}也就是說,在產生第一個 PDF 檔案之前,已經運行了 80 多行程式碼。 而且它仍然缺少生產系統所需的錯誤恢復、健康檢查、指標和記憶體回收計時器。
為什麼這種複雜性對.NET部署至關重要
營運負擔隨部署目標的擴大而增加:
Docker:您的容器映像中必須包含 Chromium。 這會使鏡像檔案增加約 280MB,從而增加拉取時間、註冊表儲存成本和冷啟動延遲。 您的 Dockerfile 需要明確的 apt-get install 命令來處理 Chromium 的系統依賴項 — libatk-bridge2.0-0,以及大約 15 個其他命令,這些命令會因其他基礎鏡像而像。
Azure Functions / AWS Lambda:無伺服器環境會限制記憶體和執行時間。 Chromium 的冷啟動(下載和啟動瀏覽器進程)可能需要 5-10 秒和 500MB 以上的記憶體。 Lambda 的 250MB 部署包限制意味著 Chromium 勉強能裝下,而 Azure 消耗計畫的 1.5GB 記憶體上限幾乎沒有為實際的 PDF 產生留下任何空間。
Kubernetes:瀏覽器進程與容器編排不相容。 對於應用程式程式碼看起來合適的記憶體限制,在 Chromium 產生渲染進程時可能會變得不夠用。 除非你將記憶體請求設定得遠高於應用程式實際需要的水平,否則 Pod OOMKill 將會頻繁發生。
等效IronPDF程式碼
var renderer = new ChromePdfRenderer();
renderer.RenderingOptions.PaperSize = IronPdf.Rendering.PdfPaperSize.A4;
renderer.RenderingOptions.MarginTop = 20;
renderer.RenderingOptions.MarginBottom = 20;
var pdf = renderer.RenderHtmlAsPdf(html);
pdf.SaveAs("output.pdf");var renderer = new ChromePdfRenderer();
renderer.RenderingOptions.PaperSize = IronPdf.Rendering.PdfPaperSize.A4;
renderer.RenderingOptions.MarginTop = 20;
renderer.RenderingOptions.MarginBottom = 20;
var pdf = renderer.RenderHtmlAsPdf(html);
pdf.SaveAs("output.pdf");六行。 IronPDF內部嵌入了 Chromium——瀏覽器生命週期、記憶體管理和進程池均由該庫處理。 沒有 SemaphoreSlim。 不支援瀏覽器回收。 無需修改 Dockerfile 文件。 NuGet套件包含了所有內容。
這種權衡是真實存在的:IronPDF 的NuGet套件比 PdfSharp 的套件大,因為它包含了 Chromium 二進位。 引擎初始化時,首次產生 PDF 的延遲為 2-5 秒,後續產生的延遲為 100-500 毫秒。 對於部署規模是主要限制因素的應用來說,這一點很重要。 對於開發人員時間與運作可靠性更為重要的應用場景,嵌入式方法更勝一籌。
.NET生態系健康狀況: NuGet套件比較
在選擇庫之前,請檢查其NuGet生態系統的健康狀況。 依賴關係鏈、發布頻率和問題解決時間比功能清單更能說明問題:
| 圖書館 | NuGet下載 | 最新發布 | 未解決的問題 | .NET 8 TFM | 本地依賴項 |
|---|---|---|---|---|---|
| PdfSharp | 3400萬+ | 積極的 | 低的 | ✅ | 沒有任何 |
| QuestPDF | 800萬+ | 積極的 | 低的 | ✅ | 沒有任何 |
| iText 核心 | 3000萬+ | 積極的 | 緩和 | ✅ | 沒有任何 |
| IronPDF | 1000萬+ | 積極的 | 低的 | ✅ | 鉻(捆綁式) |
| DinkToPdf | 500萬+ | 2018 | 300+ | ❌ | wkhtmltopdf 二進位 |
| PuppeteerSharp | 1500萬+ | 積極的 | 緩和 | ✅ | 鉻(外部) |
像 DinkToPdf 這樣已被棄用的軟體包的高下載量反映的是舊版軟體的使用情況,而不是當前的軟體健康狀況。 300 多個未解決的問題都沒有得到回應,這才是問題的真相。
對於面向 net8.0 TFM 的.NET 8 應用程式:PdfSharp、QuestPDF、iText Core 和IronPDF都原生支援它。 wkhtmltopdf 包裝器不會出現 DllNotFoundException 和 NU1202 目標框架不相容錯誤。
決策矩陣
| 要求 | PdfSharp | QuestPDF | iText 核心 | wkhtmltopdf | PuppeteerSharp | IronPDF |
|---|---|---|---|---|---|---|
| 真正自由(MIT/許可) | ✅ | ❌ 收入門 | ❌ AGPL | ⚠️ 廢棄 | ✅ | ❌ 商業廣告 |
| HTML 轉 PDF | ❌ | ❌ | ⚠️ 有限 | ⚠️ CSS 錯誤 | ✅ | ✅ |
| 現代 CSS(Flexbox/Grid) | ❌ | ❌ | ❌ | ❌ | ✅ | ✅ |
| JavaScript執行 | ❌ | ❌ | ❌ | ⚠️ 僅限 ES5 | ✅ | ✅ |
| 無瀏覽器管理 | ✅ | ✅ | ✅ | ✅ | ❌ | ✅ |
| 主動安全補丁 | ✅ | ✅ | ✅ | ❌ | ✅ | ✅ |
| 無收入門檻 | ✅ | ❌ | 不適用 | 不適用 | ✅ | ✅ |
| 可預測的許可成本 | 自由的 | 克里夫身價100萬美元 | 平均年收入約4.5萬美元 | 不適用 | 自由的 | $749 永久 |
| 對 Docker 友好 | ✅ 小 | ✅ 小 | ✅ 小 | ⚠️ 二元依賴 | ⚠️ +280MB | ✅ 獨立式 |
| 相容無伺服器架構 | ✅ | ✅ | ✅ | ❌ | ⚠️ 冷啟動 | ✅ |
如果您只需要從結構化資料中以程式設計方式建立 PDF: PdfSharp(MIT,無限制)或 QuestPDF(更好的 API,注意收入門檻)。
如果您需要使用現代 CSS 將 HTML 轉換為 PDF,並且不想管理瀏覽器基礎架構: IronPDF 。 嵌入式 Chromium 負責處理渲染引擎的生命週期。 公開定價僅為 iText 訂閱模式的一小部分。
如果您需要將 HTML 轉換為 PDF,並且熟悉瀏覽器進程的管理: PuppeteerSharp 為您提供完全的控制權。 編制營運費用預算。
如果您目前正在使用 wkhtmltopdf 或其任何包裝器:請遷移。 光是安全隱憂就足以證明這項工作的必要性——而且你每拖延一個月,CVE 清單上的漏洞就無法修復。
