如何用 C# 從 Rotativa 轉移到 IronPDF
從Rotativa轉移到IronPDF可解決重要的安全漏洞,同時使您的 PDF 生成工作流程現代化。 本指南提供了一個完整的、循序漸進的遷移路徑,消除了廢棄的 wkhtmltopdf 相依性,啟用對現代 CSS 和 JavaScript 的支援,並提供超越 ASP.NET MVC 的跨平台相容性。
為何要從Rotativa轉移到 IronPDF?
瞭解 Rotativa
Rotativa 一直以來都是開發人員在 C# 中產生 PDF 的熱門選擇。 它利用 wkhtmltopdf 工具將 HTML 內容轉換為 PDF 格式。Rotativa是專為 ASP.NET MVC 應用程式設計的開放源碼函式庫。 然而,Rotativa 雖然吸引了相當多的受眾,但其依賴過時的技術堆疊所帶來的挑戰,可能並非每個開發人員都能立即察覺。
Rotativa 的核心功能是提供一種簡單的方法,將 PDF 生成整合到 ASP.NET MVC 專案中,並利用 wkhtmltopdf 的後端功能。
重要安全諮詢
Rotativa包裝了wkhtmltopdf,而wkhtmltopdf具有嚴重的未配對安全漏洞。
| 屬性 | 價值 |
|---|---|
| CVE ID | CVE-2022-35583 |
| 嚴重性 | 關鍵 (9.8/10) |
| 攻擊向量 | 網路 |
| 狀態 | 永不打補丁 |
| 受影響 | 所有Rotativa版本 |
wkhtmltopdf 已於 2022 年 12 月正式廢棄。維護者明確表示他們不會修復安全漏洞。 每個使用Rotativa的應用程式都會永久曝光。
攻擊如何運作
<iframe src="http://169.254.169.254/latest/meta-data/iam/security-credentials/"></iframe>
<img src="http://internal-database:5432/admin" />
<iframe src="http://169.254.169.254/latest/meta-data/iam/security-credentials/"></iframe>
<img src="http://internal-database:5432/admin" />
影響:
- 存取 AWS/Azure/GCP 雲端元資料端點
- 竊取內部 API 資料和憑證
- 連接埠掃描內部網路
- 滲出敏感組態
技術危機
Rotativa 包裝了 wkhtmltopdf,它使用:
- Qt WebKit 4.8 (自 2012 年起)
- 不支援 Flexbox
- 不支援 CSS 網格
- 破碎的 JavaScript 執行
- 不支援 ES6+
Rotativa與IronPDF的比較
| 特點 | Rotativa | IronPDF |
|---|---|---|
| 專案相容性 | 僅限 ASP.NET MVC | 任何 .NET 專案類型 (MVC、Razor Pages、Blazor 等) |
| 維護 | 棄 | 積極維護 |
| 安全性 | 由於 wkhtmltopdf 相依性而產生漏洞 (CVE-2022-35583) | 定期更新和安全修補程式 |
| HTML 渲染 | 過時的 WebKit | 現代 Chromium |
| CSS3。 | 部分的 | 支援 |
| Flexbox/網格 | 不支援 | 支援 |
| JavaScript。 | 不可靠 | 完整的 ES6+ |
| Razor 頁面 | 不支援 | 支援 |
| Blazor | 不支援 | 支援 |
| PDF 操作 | 無法提供 | 全文 |
| 數位簽名 | 無法提供 | 全文 |
| PDF/A合規性 | 無法提供 | 全文 |
| Async/等待 | 僅同步 | 完全同步 |
| 開放原始碼 | 是,MIT 授權 | 無,商業授權 |
對於計劃在 2025 年和 2026 年之前採用 .NET 10 和 C# 14 的團隊,IronPDF 可提供Rotativa無法提供的現代 Chromium 渲染和跨平台支援。
開始之前
先決條件
- .NET環境: .NET Framework 4.6.2+ 或.NET Core 3.1+ / .NET 5/6/7/8/9+
- NuGet存取權限:能夠安裝NuGet套件
- IronPDF許可證:請從IronPDF取得您的許可證密鑰。
NuGet 套件變更
# Remove Rotativa
dotnet remove package Rotativa
dotnet remove package Rotativa.AspNetCore
# Install IronPDF
dotnet add package IronPdf
# Remove Rotativa
dotnet remove package Rotativa
dotnet remove package Rotativa.AspNetCore
# Install IronPDF
dotnet add package IronPdf
移除 wkhtmltopdf 二進位文件
從您的專案中刪除這些檔案:
wkhtmltopdf.exewkhtmltox.dll- 任何
Rotativa/資料夾
這些都是CVE-2022-35583的來源。IronPDF 不需要本機二進位檔。
授權組態
// Add in Program.cs or Startup.cs
IronPdf.License.LicenseKey = "YOUR-LICENSE-KEY";
// Add in Program.cs or Startup.cs
IronPdf.License.LicenseKey = "YOUR-LICENSE-KEY";
' Add in Program.vb or Startup.vb
IronPdf.License.LicenseKey = "YOUR-LICENSE-KEY"
完整的 API 參考資料
命名空間變更
// Before: Rotativa
using Rotativa;
using Rotativa.Options;
using Rotativa.AspNetCore;
// After: IronPDF
using IronPdf;
using IronPdf.Rendering;
// Before: Rotativa
using Rotativa;
using Rotativa.Options;
using Rotativa.AspNetCore;
// After: IronPDF
using IronPdf;
using IronPdf.Rendering;
' Before: Rotativa
Imports Rotativa
Imports Rotativa.Options
Imports Rotativa.AspNetCore
' After: IronPDF
Imports IronPdf
Imports IronPdf.Rendering
核心類對應
| Rotativa 類別 | IronPDF 同等級產品 |
|---|---|
ViewAsPdf |
ChromePdfRenderer |
ActionAsPdf |
ChromePdfRenderer.RenderUrlAsPdf() |
UrlAsPdf |
ChromePdfRenderer.RenderUrlAsPdf() |
Orientation枚舉 |
PdfPaperOrientation枚舉 |
Size枚舉 |
PdfPaperSize枚舉 |
頁面占位符轉換
| Rotativa 佔位符 | IronPDF 占位符 |
|---|---|
[page] |
{page} |
[topage] |
{total-pages} |
[date] |
{date} |
[time] |
{time} |
[title] |
{html-title} |
[sitepage] |
{url} |
程式碼遷移範例
範例 1:HTML 到 PDF 的轉換
之前 (Rotativa):
// NuGet: Install-Package Rotativa.Core
using Microsoft.AspNetCore.Mvc;
using Rotativa.AspNetCore;
using System.Threading.Tasks;
namespace RotativaExample
{
public class PdfController : Controller
{
public async Task<IActionResult> GeneratePdf()
{
var htmlContent = "<h1>Hello World</h1><p>This is a PDF document.</p>";
//Rotativarequires returning a ViewAsPdf result from MVC controller
return new ViewAsPdf()
{
ViewName = "PdfView",
PageSize = Rotativa.AspNetCore.Options.Size.A4
};
}
}
}
// NuGet: Install-Package Rotativa.Core
using Microsoft.AspNetCore.Mvc;
using Rotativa.AspNetCore;
using System.Threading.Tasks;
namespace RotativaExample
{
public class PdfController : Controller
{
public async Task<IActionResult> GeneratePdf()
{
var htmlContent = "<h1>Hello World</h1><p>This is a PDF document.</p>";
//Rotativarequires returning a ViewAsPdf result from MVC controller
return new ViewAsPdf()
{
ViewName = "PdfView",
PageSize = Rotativa.AspNetCore.Options.Size.A4
};
}
}
}
Imports Microsoft.AspNetCore.Mvc
Imports Rotativa.AspNetCore
Imports System.Threading.Tasks
Namespace RotativaExample
Public Class PdfController
Inherits Controller
Public Async Function GeneratePdf() As Task(Of IActionResult)
Dim htmlContent As String = "<h1>Hello World</h1><p>This is a PDF document.</p>"
' Rotativa requires returning a ViewAsPdf result from MVC controller
Return New ViewAsPdf() With {
.ViewName = "PdfView",
.PageSize = Rotativa.AspNetCore.Options.Size.A4
}
End Function
End Class
End Namespace
After (IronPDF):
// NuGet: Install-Package IronPdf
using IronPdf;
using System;
namespace IronPdfExample
{
class Program
{
static void Main(string[] args)
{
var renderer = new ChromePdfRenderer();
var htmlContent = "<h1>Hello World</h1><p>This is a PDF document.</p>";
var pdf = renderer.RenderHtmlAsPdf(htmlContent);
pdf.SaveAs("output.pdf");
Console.WriteLine("PDF generated successfully!");
}
}
}
// NuGet: Install-Package IronPdf
using IronPdf;
using System;
namespace IronPdfExample
{
class Program
{
static void Main(string[] args)
{
var renderer = new ChromePdfRenderer();
var htmlContent = "<h1>Hello World</h1><p>This is a PDF document.</p>";
var pdf = renderer.RenderHtmlAsPdf(htmlContent);
pdf.SaveAs("output.pdf");
Console.WriteLine("PDF generated successfully!");
}
}
}
Imports IronPdf
Imports System
Namespace IronPdfExample
Class Program
Shared Sub Main(ByVal args As String())
Dim renderer = New ChromePdfRenderer()
Dim htmlContent = "<h1>Hello World</h1><p>This is a PDF document.</p>"
Dim pdf = renderer.RenderHtmlAsPdf(htmlContent)
pdf.SaveAs("output.pdf")
Console.WriteLine("PDF generated successfully!")
End Sub
End Class
End Namespace
本範例展示了基本的架構差異。Rotativa要求從 MVC 控制器操作返回 ViewAsPdf 結果,這使您與 ASP.NET MVC 框架綁定。 該模式僅在 MVC 請求管道中運作,並需要 Razor 視圖來呈現。
IronPDF 可在任何地方工作:控制台應用程式、Web API、Blazor、Razor Pages 或任何 .NET 專案類型。 你使用 HTML 字串呼叫 RenderHtmlAsPdf() 並儲存結果。 不需要 MVC 控制器,無視圖依賴性。 請參閱 HTML to PDF 文件,以瞭解全面的範例。
範例 2:URL 到 PDF 的轉換
之前 (Rotativa):
// NuGet: Install-Package Rotativa.Core
using Microsoft.AspNetCore.Mvc;
using Rotativa.AspNetCore;
using System.Threading.Tasks;
namespace RotativaExample
{
public class UrlPdfController : Controller
{
public async Task<IActionResult> ConvertUrlToPdf()
{
//Rotativaworks within MVC framework and returns ActionResult
return new UrlAsPdf("https://www.example.com")
{
FileName = "webpage.pdf",
PageSize = Rotativa.AspNetCore.Options.Size.A4,
PageOrientation = Rotativa.AspNetCore.Options.Orientation.Portrait
};
}
}
}
// NuGet: Install-Package Rotativa.Core
using Microsoft.AspNetCore.Mvc;
using Rotativa.AspNetCore;
using System.Threading.Tasks;
namespace RotativaExample
{
public class UrlPdfController : Controller
{
public async Task<IActionResult> ConvertUrlToPdf()
{
//Rotativaworks within MVC framework and returns ActionResult
return new UrlAsPdf("https://www.example.com")
{
FileName = "webpage.pdf",
PageSize = Rotativa.AspNetCore.Options.Size.A4,
PageOrientation = Rotativa.AspNetCore.Options.Orientation.Portrait
};
}
}
}
Imports Microsoft.AspNetCore.Mvc
Imports Rotativa.AspNetCore
Imports System.Threading.Tasks
Namespace RotativaExample
Public Class UrlPdfController
Inherits Controller
Public Async Function ConvertUrlToPdf() As Task(Of IActionResult)
' Rotativa works within MVC framework and returns ActionResult
Return New UrlAsPdf("https://www.example.com") With {
.FileName = "webpage.pdf",
.PageSize = Rotativa.AspNetCore.Options.Size.A4,
.PageOrientation = Rotativa.AspNetCore.Options.Orientation.Portrait
}
End Function
End Class
End Namespace
After (IronPDF):
// NuGet: Install-Package IronPdf
using IronPdf;
using System;
namespace IronPdfExample
{
class Program
{
static void Main(string[] args)
{
var renderer = new ChromePdfRenderer();
var pdf = renderer.RenderUrlAsPdf("https://www.example.com");
pdf.SaveAs("webpage.pdf");
Console.WriteLine("URL converted to PDF successfully!");
}
}
}
// NuGet: Install-Package IronPdf
using IronPdf;
using System;
namespace IronPdfExample
{
class Program
{
static void Main(string[] args)
{
var renderer = new ChromePdfRenderer();
var pdf = renderer.RenderUrlAsPdf("https://www.example.com");
pdf.SaveAs("webpage.pdf");
Console.WriteLine("URL converted to PDF successfully!");
}
}
}
Imports IronPdf
Imports System
Namespace IronPdfExample
Class Program
Shared Sub Main(ByVal args As String())
Dim renderer = New ChromePdfRenderer()
Dim pdf = renderer.RenderUrlAsPdf("https://www.example.com")
pdf.SaveAs("webpage.pdf")
Console.WriteLine("URL converted to PDF successfully!")
End Sub
End Class
End Namespace
Rotativa 的 UrlAsPdf 類別需要從 MVC 控制器返回 ActionResult。IronPDF的 RenderUrlAsPdf() 方法可以從任何上下文中調用,並直接傳回一個 PdfDocument 物件。 URL 渲染使用現代的 Chromium,而非 wkhtmltopdf 脆弱且過時的 WebKit 引擎。請參閱我們的 教學,瞭解更多資訊。
範例 3:帶有頁碼的頁首和頁尾
之前 (Rotativa):
// NuGet: Install-Package Rotativa.Core
using Microsoft.AspNetCore.Mvc;
using Rotativa.AspNetCore;
using Rotativa.AspNetCore.Options;
using System.Threading.Tasks;
namespace RotativaExample
{
public class HeaderFooterController : Controller
{
public async Task<IActionResult> GeneratePdfWithHeaderFooter()
{
return new ViewAsPdf("Report")
{
PageSize = Size.A4,
PageMargins = new Margins(20, 10, 20, 10),
CustomSwitches = "--header-center \"Page Header\" --footer-center \"Page [page] of [toPage]\""
};
}
}
}
// NuGet: Install-Package Rotativa.Core
using Microsoft.AspNetCore.Mvc;
using Rotativa.AspNetCore;
using Rotativa.AspNetCore.Options;
using System.Threading.Tasks;
namespace RotativaExample
{
public class HeaderFooterController : Controller
{
public async Task<IActionResult> GeneratePdfWithHeaderFooter()
{
return new ViewAsPdf("Report")
{
PageSize = Size.A4,
PageMargins = new Margins(20, 10, 20, 10),
CustomSwitches = "--header-center \"Page Header\" --footer-center \"Page [page] of [toPage]\""
};
}
}
}
Imports Microsoft.AspNetCore.Mvc
Imports Rotativa.AspNetCore
Imports Rotativa.AspNetCore.Options
Imports System.Threading.Tasks
Namespace RotativaExample
Public Class HeaderFooterController
Inherits Controller
Public Async Function GeneratePdfWithHeaderFooter() As Task(Of IActionResult)
Return New ViewAsPdf("Report") With {
.PageSize = Size.A4,
.PageMargins = New Margins(20, 10, 20, 10),
.CustomSwitches = "--header-center ""Page Header"" --footer-center ""Page [page] of [toPage]"""
}
End Function
End Class
End Namespace
After (IronPDF):
// NuGet: Install-Package IronPdf
using IronPdf;
using IronPdf.Rendering;
using System;
namespace IronPdfExample
{
class Program
{
static void Main(string[] args)
{
var renderer = new ChromePdfRenderer();
renderer.RenderingOptions.TextHeader = new TextHeaderFooter()
{
CenterText = "Page Header",
DrawDividerLine = true
};
renderer.RenderingOptions.TextFooter = new TextHeaderFooter()
{
CenterText = "Page {page} of {total-pages}",
DrawDividerLine = true
};
var htmlContent = "<h1>Report Title</h1><p>Report content goes here.</p>";
var pdf = renderer.RenderHtmlAsPdf(htmlContent);
pdf.SaveAs("report.pdf");
Console.WriteLine("PDF with headers and footers created successfully!");
}
}
}
// NuGet: Install-Package IronPdf
using IronPdf;
using IronPdf.Rendering;
using System;
namespace IronPdfExample
{
class Program
{
static void Main(string[] args)
{
var renderer = new ChromePdfRenderer();
renderer.RenderingOptions.TextHeader = new TextHeaderFooter()
{
CenterText = "Page Header",
DrawDividerLine = true
};
renderer.RenderingOptions.TextFooter = new TextHeaderFooter()
{
CenterText = "Page {page} of {total-pages}",
DrawDividerLine = true
};
var htmlContent = "<h1>Report Title</h1><p>Report content goes here.</p>";
var pdf = renderer.RenderHtmlAsPdf(htmlContent);
pdf.SaveAs("report.pdf");
Console.WriteLine("PDF with headers and footers created successfully!");
}
}
}
Imports IronPdf
Imports IronPdf.Rendering
Imports System
Namespace IronPdfExample
Class Program
Shared Sub Main(ByVal args As String())
Dim renderer = New ChromePdfRenderer()
renderer.RenderingOptions.TextHeader = New TextHeaderFooter() With {
.CenterText = "Page Header",
.DrawDividerLine = True
}
renderer.RenderingOptions.TextFooter = New TextHeaderFooter() With {
.CenterText = "Page {page} of {total-pages}",
.DrawDividerLine = True
}
Dim htmlContent = "<h1>Report Title</h1><p>Report content goes here.</p>"
Dim pdf = renderer.RenderHtmlAsPdf(htmlContent)
pdf.SaveAs("report.pdf")
Console.WriteLine("PDF with headers and footers created successfully!")
End Sub
End Class
End Namespace
Rotativa 使用 CustomSwitches 將命令列參數傳遞給 wkhtmltopdf,包括帶有佔位符的頁眉和頁腳配置,例如 [page] 和 [toPage]。 這種以字串為基礎的方法容易出錯,且很難在編譯時驗證。
IronPDF 使用強類型的 TextHeaderFooter 對象,具有 CenterText 和 DrawDividerLine 等屬性。 佔位語法從 [page] 變為 {page},從 [toPage] 變為 {total-pages}。 類型化的屬性提供 IntelliSense、編譯時檢查,而且沒有錯字的風險。
僅 MVC 架構的問題
Rotativa 專為 ASP.NET MVC 5 及更早版本所設計:
// ❌Rotativa- Only works with classic MVC pattern
public class InvoiceController : Controller
{
public ActionResult InvoicePdf(int id)
{
var model = GetInvoice(id);
return new ViewAsPdf("Invoice", model); // Tied to MVC Views
}
}
// Problems:
// - No Razor Pages support
// - No Blazor support
// - No minimal APIs support
// - No ASP.NET Core native integration
// ❌Rotativa- Only works with classic MVC pattern
public class InvoiceController : Controller
{
public ActionResult InvoicePdf(int id)
{
var model = GetInvoice(id);
return new ViewAsPdf("Invoice", model); // Tied to MVC Views
}
}
// Problems:
// - No Razor Pages support
// - No Blazor support
// - No minimal APIs support
// - No ASP.NET Core native integration
Imports System.Web.Mvc
Public Class InvoiceController
Inherits Controller
Public Function InvoicePdf(id As Integer) As ActionResult
Dim model = GetInvoice(id)
Return New ViewAsPdf("Invoice", model) ' Tied to MVC Views
End Function
End Class
' Problems:
' - No Razor Pages support
' - No Blazor support
' - No minimal APIs support
' - No ASP.NET Core native integration
IronPDF 將視圖渲染與 PDF 產生分離,這其實更具彈性-您可以渲染任何 HTML,而不僅僅是 MVC 視圖。
同步模式轉換
Rotativa 阻斷線程;IronPDF支援完整的 async/await:
// ❌Rotativa- Blocks the thread
public ActionResult GeneratePdf()
{
return new ViewAsPdf("Report");
// This blocks the request thread until PDF is complete
// Poor scalability under load
}
// ✅IronPDF-完全同步support
public async Task<IActionResult> GeneratePdf()
{
var renderer = new ChromePdfRenderer();
var pdf = await renderer.RenderHtmlAsPdfAsync(html);
return File(pdf.BinaryData, "application/pdf");
// Non-blocking, better scalability
}
// ❌Rotativa- Blocks the thread
public ActionResult GeneratePdf()
{
return new ViewAsPdf("Report");
// This blocks the request thread until PDF is complete
// Poor scalability under load
}
// ✅IronPDF-完全同步support
public async Task<IActionResult> GeneratePdf()
{
var renderer = new ChromePdfRenderer();
var pdf = await renderer.RenderHtmlAsPdfAsync(html);
return File(pdf.BinaryData, "application/pdf");
// Non-blocking, better scalability
}
' ❌Rotativa- Blocks the thread
Public Function GeneratePdf() As ActionResult
Return New ViewAsPdf("Report")
' This blocks the request thread until PDF is complete
' Poor scalability under load
End Function
' ✅IronPDF-完全同步support
Public Async Function GeneratePdf() As Task(Of IActionResult)
Dim renderer As New ChromePdfRenderer()
Dim pdf = Await renderer.RenderHtmlAsPdfAsync(html)
Return File(pdf.BinaryData, "application/pdf")
' Non-blocking, better scalability
End Function
遷移後的新功能
轉移到IronPDF之後,您將獲得Rotativa無法提供的功能:
PDF 合併
var merged = PdfDocument.Merge(pdf1, pdf2, pdf3);
merged.SaveAs("complete.pdf");
var merged = PdfDocument.Merge(pdf1, pdf2, pdf3);
merged.SaveAs("complete.pdf");
Dim merged = PdfDocument.Merge(pdf1, pdf2, pdf3)
merged.SaveAs("complete.pdf")
數位簽名
var signature = new PdfSignature("certificate.pfx", "password");
pdf.Sign(signature);
var signature = new PdfSignature("certificate.pfx", "password");
pdf.Sign(signature);
Dim signature = New PdfSignature("certificate.pfx", "password")
pdf.Sign(signature)
密碼保護
pdf.SecuritySettings.UserPassword = "secret";
pdf.SecuritySettings.UserPassword = "secret";
pdf.SecuritySettings.UserPassword = "secret"
水印。
pdf.ApplyWatermark("<h1 style='color:red; opacity:0.3;'>DRAFT</h1>");
pdf.ApplyWatermark("<h1 style='color:red; opacity:0.3;'>DRAFT</h1>");
IRON VB CONVERTER ERROR developers@ironsoftware.com
PDF/A 檔案合規性
pdf.SaveAsPdfA("archive.pdf", PdfAVersions.PdfA3b);
pdf.SaveAsPdfA("archive.pdf", PdfAVersions.PdfA3b);
pdf.SaveAsPdfA("archive.pdf", PdfAVersions.PdfA3b)
現代 CSS 支援
// This now works (broke in Rotativa)
var html = @"
<div style='display: flex; justify-content: space-between;'>
<div>Left</div>
<div>Right</div>
</div>
<div style='display: grid; grid-template-columns: 1fr 1fr 1fr;'>
<div>Col 1</div><div>Col 2</div><div>Col 3</div>
</div>";
var pdf = renderer.RenderHtmlAsPdf(html); // Works!
// This now works (broke in Rotativa)
var html = @"
<div style='display: flex; justify-content: space-between;'>
<div>Left</div>
<div>Right</div>
</div>
<div style='display: grid; grid-template-columns: 1fr 1fr 1fr;'>
<div>Col 1</div><div>Col 2</div><div>Col 3</div>
</div>";
var pdf = renderer.RenderHtmlAsPdf(html); // Works!
' This now works (broke in Rotativa)
Dim html As String = "
<div style='display: flex; justify-content: space-between;'>
<div>Left</div>
<div>Right</div>
</div>
<div style='display: grid; grid-template-columns: 1fr 1fr 1fr;'>
<div>Col 1</div><div>Col 2</div><div>Col 3</div>
</div>"
Dim pdf = renderer.RenderHtmlAsPdf(html) ' Works!
遷移清單
預遷移
- 識別程式碼庫中所有Rotativa的使用情況
- 文檔 CustomSwitches 用於轉換為 RenderingOptions
- 注意頁首/頁尾佔位符的轉換語法(
[page]→{page}) - 從ironpdf.com取得IronPDF許可證金鑰
套件變更
- 刪除
Rotativa和Rotativa.AspNetCoreNuGet 套件 - 刪除 wkhtmltopdf 二進位(
wkhtmltopdf.exe,wkhtmltox.dll) 安裝IronPdfNuGet 套件
程式碼變更
- 更新命名空間導入(
using Rotativa;→using IronPdf;) - 將
ViewAsPdf替換為ChromePdfRenderer+RenderHtmlAsPdf() - 將
UrlAsPdf替換為RenderUrlAsPdf() - 將
CustomSwitches轉換為RenderingOptions屬性 - 更新佔位語法(
[page]→{page},[topage]→{total-pages}) - 將
PageMargins替換為單一MarginTop/MarginBottom/MarginLeft/MarginRight - 在適當情況下改為非同步模式
- 在應用程式啟動時新增許可證初始化
後遷移
- 驗證所有 PDF 產生功能是否正常
- 比較 PDF 輸出品質 (Chromium 的渲染更精確)
- 驗證 CSS 渲染改進(Flexbox/Grid 現在可以正常運作)
- 測試 JavaScript 執行(現在使用 Chromium 核心已穩定運行)
- 驗證安全掃描通過(不再有CVE-2022-35583標誌)
- 更新 Docker 設定以移除 wkhtmltopdf 安裝

