DinkToPdf與IronPDF:技術比較指南
當 .NET 開發者評估 PDF 生成功能庫時,DinkToPdf 是一個以使用 wkhtmltopdf 可執行檔的著名開放源代碼選擇。 然而,顯著的安全漏洞、線程安全問題以及缺乏持續性維護,促使許多團隊考慮替代方案。IronPDF提供了一個現代、積極維護的解決方案,使用 Chromium 渲染引擎且無需本地二進制依賴。
此比較著眼於涉及的技術層面,以協助專業開發者和架構師在 .NET PDF 需求上做出明智決策。
了解 DinkToPdf
DinkToPdf 是 C# 生態系統中的一個開放源代碼庫,通過包裝 wkhtmltopdf 實現 HTML 到 PDF 的轉換。 該庫使用 MIT 授權,使其能夠在各類專案中集成和修改。
DinkToPdf 封裝了 wkhtmltopdf 的功能,允許開發者將包含 CSS 和JavaScript的 HTML 內容轉換成 PDF 文檔。 然而,該庫繼承了所有 wkhtmltopdf 二進制相關的安全漏洞和限制,包括嚴重的 CVE-2022-35583 SSRF(伺服器端請求偽造)問題。 wkhtmltopdf 專案自 2020 年以來被廢棄,DinkToPdf 本身最後於 2018 年獲得更新。
該庫需要部署與平台相關的本地二進制檔案(Windows 的 libwkhtmltox.dll,Linux 的 libwkhtmltox.so,macOS 的 libwkhtmltox.dylib),造成了部署複雜性和維護負擔。 此外,DinkToPdf 明顯地不是線程安全的,甚至在使用 SynchronizedConverter 包裝器時在並發執行環境中會導致失敗。
瞭解IronPDF
IronPDF 是一個商業 .NET PDF 庫,使用現代的 Chromium 渲染引擎來實現 HTML 到 PDF 的轉換。 該庫提供完整的 PDF 生成和操作能力,不依賴於外部本地二進制文件。
IronPDF 支援 .NET Framework 4.6.2+,.NET Core 3.1+,以及 .NET 5/6/7/8/9,並通過純 NuGet 包部署模式消除了本地依賴管理。 該庫設計為線程安全的並發操作,能夠可靠地進行並行 PDF 生成,而不會發生與 DinkToPdf 相關的崩潰。
安全比較
安全影響是這些 .NET PDF 庫之間最顯著的區別。
| 安全層面 | DinkToPdf | IronPDF |
|---|---|---|
| 已知漏洞 | CVE-2022-35583 (SSRF) | 無已知漏洞 |
| 漏洞狀態 | 未修補 | 通過設計緩解 |
| 核心依賴關係 | wkhtmltopdf (2020 年廢棄) | 現代Chromium |
| 安全更新 | 無(專案已廢棄) | 常規更新 |
DinkToPdf 繼承了從 wkhtmltopdf 的 CVE-2022-35583 伺服器端請求偽造漏洞。 該漏洞允許攻擊者訪問內部網絡資源,為處理不受信任 HTML 內容的應用程序創造重大安全風險。 鑑於 wkhtmltopdf 已被廢棄,這些漏洞將永遠不會接收到修補。
架構和渲染引擎比較
| 方面 | DinkToPdf | IronPDF |
|---|---|---|
| 渲染引擎 | 過時的 WebKit(約 2015 年) | 現代Chromium |
| 線程安全性 | 並發使用時崩潰 | 完全線程安全 |
| 本地依賴關係 | 與平台相關的二進制 | 純 NuGet 包 |
| CSS 支援 | 沒有 Flexbox/Grid | 完全支持CSS3 |
| JavaScript | 有限,不一致 | 支持 |
| 維護 | 廢棄(2018) | 積極維護 |
| 支持 | 僅社群 | 專業支持 |
DinkToPdf 的 wkhtmltopdf 依賴於約 2015 年的過時 WebKit 引擎。這導致現代 CSS 特性例如 Flexbox 和 Grid 佈局無法正確渲染。JavaScript執行有限且不一致,對於動態內容產生不可靠的結果。
IronPDF 使用現代的 Chromium 引擎,能夠準確地呈現 HTML 就像當代瀏覽器顯示一樣,完全支援 CSS3,包括 Flexbox 和 Grid 佈局,並且JavaScript執行可靠且有可配置的等待時間。
程式碼比較:常見的PDF操作
基本 HTML 到 PDF 轉換
最基本的操作展示了 API 複雜度的差異。
DinkToPdf:
// NuGet: Install-Package DinkToPdf
using DinkToPdf;
using DinkToPdf.Contracts;
using System.IO;
class Program
{
static void Main()
{
var converter = new SynchronizedConverter(new PdfTools());
var doc = new HtmlToPdfDocument()
{
GlobalSettings = {
ColorMode = ColorMode.Color,
Orientation = Orientation.Portrait,
PaperSize = PaperKind.A4,
},
Objects = {
new ObjectSettings() {
HtmlContent = "<h1>Hello World</h1><p>This is a PDF from HTML.</p>",
WebSettings = { DefaultEncoding = "utf-8" }
}
}
};
byte[] pdf = converter.Convert(doc);
File.WriteAllBytes("output.pdf", pdf);
}
}// NuGet: Install-Package DinkToPdf
using DinkToPdf;
using DinkToPdf.Contracts;
using System.IO;
class Program
{
static void Main()
{
var converter = new SynchronizedConverter(new PdfTools());
var doc = new HtmlToPdfDocument()
{
GlobalSettings = {
ColorMode = ColorMode.Color,
Orientation = Orientation.Portrait,
PaperSize = PaperKind.A4,
},
Objects = {
new ObjectSettings() {
HtmlContent = "<h1>Hello World</h1><p>This is a PDF from HTML.</p>",
WebSettings = { DefaultEncoding = "utf-8" }
}
}
};
byte[] pdf = converter.Convert(doc);
File.WriteAllBytes("output.pdf", pdf);
}
}Imports DinkToPdf
Imports DinkToPdf.Contracts
Imports System.IO
Module Program
Sub Main()
Dim converter = New SynchronizedConverter(New PdfTools())
Dim doc = New HtmlToPdfDocument() With {
.GlobalSettings = New GlobalSettings() With {
.ColorMode = ColorMode.Color,
.Orientation = Orientation.Portrait,
.PaperSize = PaperKind.A4
},
.Objects = {
New ObjectSettings() With {
.HtmlContent = "<h1>Hello World</h1><p>This is a PDF from HTML.</p>",
.WebSettings = New WebSettings() With {
.DefaultEncoding = "utf-8"
}
}
}
}
Dim pdf As Byte() = converter.Convert(doc)
File.WriteAllBytes("output.pdf", pdf)
End Sub
End ModuleIronPDF:
// NuGet: Install-Package IronPdf
using IronPdf;
using System;
class Program
{
static void Main()
{
var renderer = new ChromePdfRenderer();
var pdf = renderer.RenderHtmlAsPdf("<h1>Hello World</h1><p>This is a PDF from HTML.</p>");
pdf.SaveAs("output.pdf");
}
}// NuGet: Install-Package IronPdf
using IronPdf;
using System;
class Program
{
static void Main()
{
var renderer = new ChromePdfRenderer();
var pdf = renderer.RenderHtmlAsPdf("<h1>Hello World</h1><p>This is a PDF from HTML.</p>");
pdf.SaveAs("output.pdf");
}
}Imports IronPdf
Imports System
Class Program
Shared Sub Main()
Dim renderer = New ChromePdfRenderer()
Dim pdf = renderer.RenderHtmlAsPdf("<h1>Hello World</h1><p>This is a PDF from HTML.</p>")
pdf.SaveAs("output.pdf")
End Sub
End ClassDinkToPdf 需要創建一個 SynchronizedConverter 並配合 PdfTools,構建一個 HtmlToPdfDocument,並配合 GlobalSettings 和 ObjectSettings,調整 WebSettings,轉換成 byte[],並手動寫入文件。IronPDF 則創建一個 ChromePdfRenderer,調用 RenderHtmlAsPdf(),並保存 —— 三行與十五行。
有關進階HTML渲染選項,請查看HTML到PDF轉換指南。
URL到PDF轉換
將網頁捕捉成 PDF 顯示出類似的複雜度差異。
DinkToPdf:
// NuGet: Install-Package DinkToPdf
using DinkToPdf;
using DinkToPdf.Contracts;
using System.IO;
class Program
{
static void Main()
{
var converter = new SynchronizedConverter(new PdfTools());
var doc = new HtmlToPdfDocument()
{
GlobalSettings = {
ColorMode = ColorMode.Color,
Orientation = Orientation.Portrait,
PaperSize = PaperKind.A4,
},
Objects = {
new ObjectSettings() {
Page = "https://www.example.com",
}
}
};
byte[] pdf = converter.Convert(doc);
File.WriteAllBytes("webpage.pdf", pdf);
}
}// NuGet: Install-Package DinkToPdf
using DinkToPdf;
using DinkToPdf.Contracts;
using System.IO;
class Program
{
static void Main()
{
var converter = new SynchronizedConverter(new PdfTools());
var doc = new HtmlToPdfDocument()
{
GlobalSettings = {
ColorMode = ColorMode.Color,
Orientation = Orientation.Portrait,
PaperSize = PaperKind.A4,
},
Objects = {
new ObjectSettings() {
Page = "https://www.example.com",
}
}
};
byte[] pdf = converter.Convert(doc);
File.WriteAllBytes("webpage.pdf", pdf);
}
}Imports DinkToPdf
Imports DinkToPdf.Contracts
Imports System.IO
Module Program
Sub Main()
Dim converter = New SynchronizedConverter(New PdfTools())
Dim doc = New HtmlToPdfDocument() With {
.GlobalSettings = New GlobalSettings() With {
.ColorMode = ColorMode.Color,
.Orientation = Orientation.Portrait,
.PaperSize = PaperKind.A4
},
.Objects = New List(Of ObjectSettings) From {
New ObjectSettings() With {
.Page = "https://www.example.com"
}
}
}
Dim pdf As Byte() = converter.Convert(doc)
File.WriteAllBytes("webpage.pdf", pdf)
End Sub
End ModuleIronPDF:
// NuGet: Install-Package IronPdf
using IronPdf;
using System;
class Program
{
static void Main()
{
var renderer = new ChromePdfRenderer();
var pdf = renderer.RenderUrlAsPdf("https://www.example.com");
pdf.SaveAs("webpage.pdf");
}
}// NuGet: Install-Package IronPdf
using IronPdf;
using System;
class Program
{
static void Main()
{
var renderer = new ChromePdfRenderer();
var pdf = renderer.RenderUrlAsPdf("https://www.example.com");
pdf.SaveAs("webpage.pdf");
}
}Imports IronPdf
Imports System
Class Program
Shared Sub Main()
Dim renderer = New ChromePdfRenderer()
Dim pdf = renderer.RenderUrlAsPdf("https://www.example.com")
pdf.SaveAs("webpage.pdf")
End Sub
End ClassDinkToPdf 使用 Page 屬性在 ObjectSettings 中指定 URL,須相同的文檔包裝結構。IronPDF提供專用 RenderUrlAsPdf() 方法進行直接 URL 渲染。
了解有關URL渲染的更多信息,請參見URL to PDF文檔。
自定義頁面設置和邊距
配置頁面方向和邊距展示了設置 API 的差異。
DinkToPdf:
// NuGet: Install-Package DinkToPdf
using DinkToPdf;
using DinkToPdf.Contracts;
using System.IO;
class Program
{
static void Main()
{
var converter = new SynchronizedConverter(new PdfTools());
var doc = new HtmlToPdfDocument()
{
GlobalSettings = {
ColorMode = ColorMode.Color,
Orientation = Orientation.Landscape,
PaperSize = PaperKind.A4,
Margins = new MarginSettings { Top = 10, Bottom = 10, Left = 15, Right = 15 }
},
Objects = {
new ObjectSettings() {
HtmlContent = "<h1>Custom PDF</h1><p>Landscape orientation with custom margins.</p>",
WebSettings = { DefaultEncoding = "utf-8" }
}
}
};
byte[] pdf = converter.Convert(doc);
File.WriteAllBytes("custom.pdf", pdf);
}
}// NuGet: Install-Package DinkToPdf
using DinkToPdf;
using DinkToPdf.Contracts;
using System.IO;
class Program
{
static void Main()
{
var converter = new SynchronizedConverter(new PdfTools());
var doc = new HtmlToPdfDocument()
{
GlobalSettings = {
ColorMode = ColorMode.Color,
Orientation = Orientation.Landscape,
PaperSize = PaperKind.A4,
Margins = new MarginSettings { Top = 10, Bottom = 10, Left = 15, Right = 15 }
},
Objects = {
new ObjectSettings() {
HtmlContent = "<h1>Custom PDF</h1><p>Landscape orientation with custom margins.</p>",
WebSettings = { DefaultEncoding = "utf-8" }
}
}
};
byte[] pdf = converter.Convert(doc);
File.WriteAllBytes("custom.pdf", pdf);
}
}Imports DinkToPdf
Imports DinkToPdf.Contracts
Imports System.IO
Module Program
Sub Main()
Dim converter = New SynchronizedConverter(New PdfTools())
Dim doc = New HtmlToPdfDocument() With {
.GlobalSettings = New GlobalSettings() With {
.ColorMode = ColorMode.Color,
.Orientation = Orientation.Landscape,
.PaperSize = PaperKind.A4,
.Margins = New MarginSettings() With {
.Top = 10,
.Bottom = 10,
.Left = 15,
.Right = 15
}
},
.Objects = {
New ObjectSettings() With {
.HtmlContent = "<h1>Custom PDF</h1><p>Landscape orientation with custom margins.</p>",
.WebSettings = New WebSettings() With {
.DefaultEncoding = "utf-8"
}
}
}
}
Dim pdf As Byte() = converter.Convert(doc)
File.WriteAllBytes("custom.pdf", pdf)
End Sub
End ModuleIronPDF:
// NuGet: Install-Package IronPdf
using IronPdf;
using IronPdf.Rendering;
using System;
class Program
{
static void Main()
{
var renderer = new ChromePdfRenderer();
renderer.RenderingOptions.PaperOrientation = PdfPaperOrientation.Landscape;
renderer.RenderingOptions.MarginTop = 10;
renderer.RenderingOptions.MarginBottom = 10;
renderer.RenderingOptions.MarginLeft = 15;
renderer.RenderingOptions.MarginRight = 15;
var pdf = renderer.RenderHtmlAsPdf("<h1>Custom PDF</h1><p>Landscape orientation with custom margins.</p>");
pdf.SaveAs("custom.pdf");
}
}// NuGet: Install-Package IronPdf
using IronPdf;
using IronPdf.Rendering;
using System;
class Program
{
static void Main()
{
var renderer = new ChromePdfRenderer();
renderer.RenderingOptions.PaperOrientation = PdfPaperOrientation.Landscape;
renderer.RenderingOptions.MarginTop = 10;
renderer.RenderingOptions.MarginBottom = 10;
renderer.RenderingOptions.MarginLeft = 15;
renderer.RenderingOptions.MarginRight = 15;
var pdf = renderer.RenderHtmlAsPdf("<h1>Custom PDF</h1><p>Landscape orientation with custom margins.</p>");
pdf.SaveAs("custom.pdf");
}
}Imports IronPdf
Imports IronPdf.Rendering
Imports System
Module Program
Sub Main()
Dim renderer As New ChromePdfRenderer()
renderer.RenderingOptions.PaperOrientation = PdfPaperOrientation.Landscape
renderer.RenderingOptions.MarginTop = 10
renderer.RenderingOptions.MarginBottom = 10
renderer.RenderingOptions.MarginLeft = 15
renderer.RenderingOptions.MarginRight = 15
Dim pdf = renderer.RenderHtmlAsPdf("<h1>Custom PDF</h1><p>Landscape orientation with custom margins.</p>")
pdf.SaveAs("custom.pdf")
End Sub
End ModuleDinkToPdf 將頁面設置嵌入在 GlobalSettings 中包含一個嵌套的 MarginSettings 物件。IronPDF直接在渲染器上使用 RenderingOptions 屬性,使用個別的邊距屬性(MarginTop, MarginBottom, MarginLeft, MarginRight)進行更清晰的配置。
方法對應參考
對於評估 DinkToPdf 遷移或比較能力的開發者,此對應表顯示了等效操作:
核心類對應
| DinkToPdf | IronPDF |
|---|---|
SynchronizedConverter | ChromePdfRenderer |
BasicConverter | ChromePdfRenderer |
PdfTools | 不需要 |
HtmlToPdfDocument | 不需要 |
GlobalSettings | RenderingOptions |
ObjectSettings | RenderingOptions |
MarginSettings | 個別的邊距屬性 |
設置對應
| DinkToPdf | IronPDF |
|---|---|
GlobalSettings.PaperSize | RenderingOptions.PaperSize |
GlobalSettings.Orientation | RenderingOptions.PaperOrientation |
GlobalSettings.Margins.Top = 10 | RenderingOptions.MarginTop = 10 |
ObjectSettings.HtmlContent | RenderHtmlAsPdf(html) |
ObjectSettings.Page | RenderUrlAsPdf(url) |
converter.Convert(doc) 返回 byte[] | pdf.BinaryData 或 pdf.SaveAs() |
頁首/頁尾佔位符語法
| DinkToPdf | IronPDF |
|---|---|
[page] | {page} |
[toPage] | {total-pages} |
[date] | {date} |
[time] | {time} |
[title] | {html-title} |
功能比較總結
| 功能 | DinkToPdf | IronPDF |
|---|---|---|
| HTML到PDF | 是(過時引擎) | 是(Chromium) |
| URL到PDF | 是 | 是 |
| 自定義邊距 | 是 | 是 |
| 頁首/頁腳 | 是(有限制) | 是(完整 HTML) |
| CSS3 | 無 限制 | 有 完整 |
| Flexbox/Grid | 無 | 是 |
| JavaScript | 有限 | 有 完整 |
| PDF操作 | 無 | 是 |
| 表單填寫 | 無 | 是 |
| 數字簽名 | 無 | 是 |
| 加密 | 無 | 是 |
| 水印 | 無 | 是 |
| 合併/拆分 | 無 | 是 |
當團隊考慮從 DinkToPdf 移動到 IronPDF
開發團隊評估從 DinkToPdf 過渡到IronPDF出於幾個原因:
安全合規要求:wkhtmltopdf 中的 CVE-2022-35583 SSRF 漏洞對於處理不受信任 HTML 內容的應用程序帶來不可接受的風險。 安全審計標記此漏洞,沒有可用的修補程序,團隊必須遷移以滿足合規要求。
線程安全問題:DinkToPdf 在並發執行環境中崩潰,即使使用 SynchronizedConverter。 對於需要並行PDF生成的生產應用程序,可靠性問題無法在 DinkToPdf 的架構中解決。
現代 CSS 要求:使用現代 CSS 佈局(Flexbox、Grid)的應用程序發現 DinkToPdf 的過時 WebKit 引擎無法正確地渲染這些佈局。 構建現代網頁界面的團隊無法生成準確的 PDF 表現。
本地二進制檔案管理:要求平台特定的 libwkhtmltox 二進制檔案在 Windows、Linux 和 macOS 環境中造成部署的複雜性。 容器部署和 CI/CD 管道需要對本地依賴進行額外的配置。
廢棄的維護:由於 DinkToPdf 的最後更新在 2018 年,而 wkhtmltopdf 自 2020 年以來被廢棄,團隊無法依賴於現代 .NET 版本的錯誤修復、安全補丁或兼容性更新。
JavaScript 的可靠性:生成來自動態內容的 PDF 的應用程序在 DinkToPdf 上執行不一致。IronPDF的 Chromium 引擎提供可靠的JavaScript執行和可配置的等待時間。
優勢和考量
DinkToPdf 的優勢
- 開放源代碼:MIT 授權允許免費使用和修改
- 簡單性:對於簡單用例的基本 HTML 到 PDF 轉換
- 社群:成熟的用戶基礎和社群資源
DinkToPdf 的考量
- 安全漏洞:CVE-2022-35583 SSRF 漏洞,未修補
- 廢棄專案:自2018年以來無更新,wkhtmltopdf 自 2020 年以來被廢棄
- 線程安全:無法確保線程安全,即使使用 SynchronizedConverter
- 本地依賴項:需要偏平台的二進制檔案
- 過時的渲染:2015 年的 WebKit 引擎不支援 Flexbox/Grid
- JavaScript 有限:執行不一致
IronPDF的優勢
IronPDF的考量
- 商業許可:需要生產使用的許可證
結論
DinkToPdf 和IronPDF代表 .NET 應用程序中 PDF 生成功能的根本不同的方式。 DinkToPdf 提供開放源代碼的可接入性,但帶有嚴重的安全漏洞、線程安全問題和被廢棄的維護狀態,這些會對生產帶來重大風險。
IronPDF 提供一個現代替代方案,具有 Chromium 渲染引擎、線程安全的架構、無本地依賴和積極的維護。 對於需要安全合規、並行 PDF 生成、現代 CSS 支援或可靠的JavaScript執行能力的團隊,IronPDF 解決了這些特定要求。
當組織計劃 .NET 10、C# 14 以及到 2026 年的應用開發時,選擇有已知漏洞的被廢棄庫還是積極維護的解決方案,會影響到即時功能和長期安全姿態。 團隊應對他們的特定要求——安全合規、併發需求、CSS 複雜性和部署約束——進行評估,並根據每個庫的特點進行比對。
開始評估IronPDF,通過免費試用,並查看更多詳細文檔,來評估其對您的具體需求是否合適。
