如何用 C# 從 PDFreactor 轉移到 IronPDF
從PDFreactor轉換到 IronPDF,可以消除 Java 依賴性和伺服器基礎架構,同時透過原生的 .NET 函式庫提供等效的 HTML 至 PDF 轉換功能。 本指南提供完整的逐步遷移路徑,將您基於 Java 的伺服器架構取代為可無縫整合至 .NET 應用程式的進程內函式庫。
為什麼要從PDFreactor轉移到 IronPDF?
瞭解 PDFreactor。
PDFreactor 是一款功能強大的 HTML 至 PDF 轉換伺服器,可整合各種平台。 作為商業解決方案,PDFreactor 利用其專有技術將 HTML 和 CSS 內容轉換為高品質的 PDF 文件。 在其顯著的屬性中,PDFreactor 支援廣泛的 CSS 屬性,使其成為複雜版面呈現的有力候選。
然而,PDFreactor 對 Java 的依賴會在 .NET 環境中帶來某些挑戰,其非原生性質可能會使部署和整合變得複雜。 它對 Java 的依賴會在 .NET 應用程式中產生額外的開銷,通常需要額外的整合工作。
Java 的依賴性問題
PDFreactor 的架構在 .NET 環境中產生了一些挑戰:
1.需要 Java 執行環境:需要在所有伺服器上安裝 JRE/JDK。
2.伺服器架構:作為獨立服務運行,需要額外的基礎設施。PDFreactor是基於伺服器的解決方案,每次轉換都需要呼叫 REST API。
3.複雜的部署:在以 .NET 為主的生態系統中管理 Java 依賴項可能會使設定複雜化並增加維護成本。 在 CI/CD 管道中管理兩個執行時 (Java + .NET)。
4.進程間通訊: REST API 或套接字通訊會增加延遲。 每次 PDF 轉換都需要 HTTP 往返伺服器。
5.獨立許可證管理:許可證綁定到伺服器實例,而不是應用程式。 與 Java 服務實例相關的每伺服器授權。
6.資源隔離:分離進程記憶體和 CPU 管理。 需要監控、擴充和維護的額外伺服器。
PDFreactor與IronPDF的比較
| 功能/外觀 | PDFreactor | IronPDF |
|---|---|---|
| 原生 .NET 函式庫。 | 無(基於 Java) | 是 |
| 運行時間 | Java (外部伺服器) | 原生 .NET (處理中) |
| 架構 | REST API 服務 | NuGet 程式庫 |
| 部署 | Java + 伺服器配置 | 單一 NuGet 套件 |
| 依賴性 | JRE + HTTP 客戶端 | 自成一格 |
| 交稿時間 | 網路往返 | 直接方法呼叫 |
| 跨平台能力 | 是(依賴 Java) | 是(捆綁 Chromium) |
| CSS 支援 | 進階支援 CSS3、CSS Paged Media | 全面支援 HTML5/CSS3 |
| 部署複雜性 | 由於 Java 較為複雜 | 簡單,直接與 .NET 整合 |
| PDF 操作功能 | 基本 (僅限世代) | 廣泛,包括合併、分割、編輯和註解 |
IronPDF for .NET 與PDFreactor不同,IronPDF 是以原生的 .NET 函式庫自居,專門設計來無縫整合到 .NET 專案中,不需要像 Java 之類的外部依賴。IronPDF使用捆绑的 Chromium 渲染引擎,只需几行代碼即可将 HTML 轉换為 PDF。
對於計劃在 2025 年和 2026 年之前採用 .NET 10 和 C# 14 的團隊而言,IronPDF 提供了一個原生的 .NET 解決方案,消除了 Java 伺服器的複雜性,同時提供全面的 PDF 生命週期管理。
開始之前
先決條件
- .NET環境: .NET Framework 4.6.2+ 或.NET Core 3.1+ / .NET 5/6/7/8/9+
- NuGet存取權限:能夠安裝NuGet套件
- IronPDF許可證:請從IronPDF取得您的許可證密鑰。
NuGet 套件變更
# RemovePDFreactorNuGet packages
dotnet remove package PDFreactor.NET
dotnet remove package PDFreactor.Native.Windows.x64
# StopPDFreactorserver service (if running locally)
# Windows: net stop PDFreactor
# Linux: sudo systemctl stop pdfreactor
# Install IronPDF
dotnet add package IronPdf
# RemovePDFreactorNuGet packages
dotnet remove package PDFreactor.NET
dotnet remove package PDFreactor.Native.Windows.x64
# StopPDFreactorserver service (if running locally)
# Windows: net stop PDFreactor
# Linux: sudo systemctl stop pdfreactor
# Install IronPDF
dotnet add package IronPdf
授權組態
PDFreactor(伺服器型):
// License configured on server via config file or command line
// Client connects to licensed server
var pdfReactor = new PDFreactor("http://pdfreactor-server:9423");
// License configured on server via config file or command line
// Client connects to licensed server
var pdfReactor = new PDFreactor("http://pdfreactor-server:9423");
' License configured on server via config file or command line
' Client connects to licensed server
Dim pdfReactor = New PDFreactor("http://pdfreactor-server:9423")
IronPDF(應用程式層級):
// One-time setup at application startup
IronPdf.License.LicenseKey = "YOUR-IRONPDF-LICENSE-KEY";
// One-time setup at application startup
IronPdf.License.LicenseKey = "YOUR-IRONPDF-LICENSE-KEY";
' One-time setup at application startup
IronPdf.License.LicenseKey = "YOUR-IRONPDF-LICENSE-KEY"
辨識PDFreactor使用方式
# FindPDFreactorusage
grep -r "PDFreactor\|RealObjects\|Configuration.*Document" --include="*.cs" .
# FindCSS 分頁媒體rules to convert
grep -r "@page\|counter(page)\|counter(pages)" --include="*.cs" --include="*.css" .
# FindPDFreactorusage
grep -r "PDFreactor\|RealObjects\|Configuration.*Document" --include="*.cs" .
# FindCSS 分頁媒體rules to convert
grep -r "@page\|counter(page)\|counter(pages)" --include="*.cs" --include="*.css" .
完整的 API 參考資料
命名空間變更
// Before: PDFreactor
using RealObjects.PDFreactor;
using System.IO;
// After: IronPDF
using IronPdf;
using IronPdf.Rendering;
// Before: PDFreactor
using RealObjects.PDFreactor;
using System.IO;
// After: IronPDF
using IronPdf;
using IronPdf.Rendering;
Imports IronPdf
Imports IronPdf.Rendering
Imports System.IO
核心類對應
| PDFreactor | IronPDF |
|---|---|
PDFreactor |
ChromePdfRenderer |
Configuration |
ChromePdfRenderOptions |
Result |
PdfDocument |
config.Document = html |
renderer.RenderHtmlAsPdf(html) |
result.Document (byte[]) |
pdf.BinaryData |
組態屬性對應
| PDFreactor 組態 | IronPDF RenderingOptions |
|---|---|
config.Document = html |
renderer.RenderHtmlAsPdf(html) |
config.Document = url |
renderer.RenderUrlAsPdf(url) |
config.PageFormat = PageFormat.A4 |
RenderingOptions.PaperSize = PdfPaperSize.A4 |
config.PageOrientation |
RenderingOptions.PaperOrientation |
config.PageMargins |
RenderingOptions.MarginTop/Bottom/Left/Right |
config.EnableJavaScript = true |
RenderingOptions.EnableJavaScript = true |
config.AddUserStyleSheet(css) |
在 HTML 中嵌入 CSS |
config.Title |
pdf.MetaData.Title |
config.Encryption |
pdf.SecuritySettings |
PDFreactor不提供的新功能
| IronPDF 特點 | 說明 |
|---|---|
PdfDocument.Merge() |
合併多個 PDF |
pdf.ApplyWatermark() |
加入水印 |
pdf.ExtractAllText() |
文字擷取 |
pdf.Form |
表格填寫 |
pdf.Sign() |
數位簽名 |
程式碼遷移範例
範例 1:HTML 字串至 PDF 的轉換
之前 (PDFreactor):
// NuGet: Install-Package PDFreactor.Native.Windows.x64
using RealObjects.PDFreactor;
using System.IO;
class Program
{
static void Main()
{
PDFreactorpdfReactor = new PDFreactor();
string html = "<html><body><h1>Hello World</h1></body></html>";
Configuration config = new Configuration();
config.Document = html;
Result result = pdfReactor.Convert(config);
File.WriteAllBytes("output.pdf", result.Document);
}
}
// NuGet: Install-Package PDFreactor.Native.Windows.x64
using RealObjects.PDFreactor;
using System.IO;
class Program
{
static void Main()
{
PDFreactorpdfReactor = new PDFreactor();
string html = "<html><body><h1>Hello World</h1></body></html>";
Configuration config = new Configuration();
config.Document = html;
Result result = pdfReactor.Convert(config);
File.WriteAllBytes("output.pdf", result.Document);
}
}
Imports RealObjects.PDFreactor
Imports System.IO
Module Program
Sub Main()
Dim pdfReactor As New PDFreactor()
Dim html As String = "<html><body><h1>Hello World</h1></body></html>"
Dim config As New Configuration()
config.Document = html
Dim result As Result = pdfReactor.Convert(config)
File.WriteAllBytes("output.pdf", result.Document)
End Sub
End Module
After (IronPDF):
// NuGet: Install-Package IronPdf
using IronPdf;
using System;
class Program
{
static void Main()
{
var renderer = new ChromePdfRenderer();
string html = "<html><body><h1>Hello World</h1></body></html>";
var pdf = renderer.RenderHtmlAsPdf(html);
pdf.SaveAs("output.pdf");
}
}
// NuGet: Install-Package IronPdf
using IronPdf;
using System;
class Program
{
static void Main()
{
var renderer = new ChromePdfRenderer();
string html = "<html><body><h1>Hello World</h1></body></html>";
var pdf = renderer.RenderHtmlAsPdf(html);
pdf.SaveAs("output.pdf");
}
}
Imports IronPdf
Imports System
Class Program
Shared Sub Main()
Dim renderer = New ChromePdfRenderer()
Dim html As String = "<html><body><h1>Hello World</h1></body></html>"
Dim pdf = renderer.RenderHtmlAsPdf(html)
pdf.SaveAs("output.pdf")
End Sub
End Class
最根本的差異在於架構模式。PDFreactor需要建立一個 PDFreactor 實例(連接到 Java 伺服器),一個單獨的 Configuration 物件來保存設定和 HTML 內容,呼叫 @@--CODE-19076--@@ 來傳回一個 @@--CODE-19077--@@ 物件,最後使用 @@--CODE-19079--@@ 將 @@--CODE-19078--@@ 位元組寫入檔案。
IronPDF 將此簡化為建立一個 ChromePdfRenderer,直接使用 HTML 字串呼叫 RenderHtmlAsPdf(),然後對傳回的 PdfDocument 使用內建的 SaveAs() 方法。 無需連接伺服器、無需配置物件、無需手動處理位元組。 請參閱 HTML to PDF 文件,以瞭解全面的範例。
範例 2:URL 到 PDF 的轉換
之前 (PDFreactor):
// NuGet: Install-Package PDFreactor.Native.Windows.x64
using RealObjects.PDFreactor;
using System.IO;
class Program
{
static void Main()
{
PDFreactorpdfReactor = new PDFreactor();
Configuration config = new Configuration();
config.Document = "https://www.example.com";
Result result = pdfReactor.Convert(config);
File.WriteAllBytes("webpage.pdf", result.Document);
}
}
// NuGet: Install-Package PDFreactor.Native.Windows.x64
using RealObjects.PDFreactor;
using System.IO;
class Program
{
static void Main()
{
PDFreactorpdfReactor = new PDFreactor();
Configuration config = new Configuration();
config.Document = "https://www.example.com";
Result result = pdfReactor.Convert(config);
File.WriteAllBytes("webpage.pdf", result.Document);
}
}
Imports RealObjects.PDFreactor
Imports System.IO
Class Program
Shared Sub Main()
Dim pdfReactor As New PDFreactor()
Dim config As New Configuration()
config.Document = "https://www.example.com"
Dim result As Result = pdfReactor.Convert(config)
File.WriteAllBytes("webpage.pdf", result.Document)
End Sub
End Class
After (IronPDF):
// 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 Class
PDFreactor 對 HTML 字串和 URL 使用相同的 config.Document 屬性,自動確定類型。IronPDF提供明確方法:RenderHtmlAsPdf() 用於 HTML 字串,RenderUrlAsPdf() 用於 URL。 這種明確的方法可提高程式碼的清晰度和 IntelliSense 支援。 請參閱我們的 教學,瞭解更多資訊。
範例 3:帶有頁碼的頁首和頁尾
之前 (PDFreactor):
// NuGet: Install-Package PDFreactor.Native.Windows.x64
using RealObjects.PDFreactor;
using System.IO;
class Program
{
static void Main()
{
PDFreactorpdfReactor = new PDFreactor();
string html = "<html><body><h1>Document with Headers</h1><p>Content here</p></body></html>";
Configuration config = new Configuration();
config.Document = html;
config.AddUserStyleSheet("@page { @top-center { content: 'Header Text'; } @bottom-center { content: 'Page ' counter(page); } }");
Result result = pdfReactor.Convert(config);
File.WriteAllBytes("document.pdf", result.Document);
}
}
// NuGet: Install-Package PDFreactor.Native.Windows.x64
using RealObjects.PDFreactor;
using System.IO;
class Program
{
static void Main()
{
PDFreactorpdfReactor = new PDFreactor();
string html = "<html><body><h1>Document with Headers</h1><p>Content here</p></body></html>";
Configuration config = new Configuration();
config.Document = html;
config.AddUserStyleSheet("@page { @top-center { content: 'Header Text'; } @bottom-center { content: 'Page ' counter(page); } }");
Result result = pdfReactor.Convert(config);
File.WriteAllBytes("document.pdf", result.Document);
}
}
Imports RealObjects.PDFreactor
Imports System.IO
Module Program
Sub Main()
Dim pdfReactor As New PDFreactor()
Dim html As String = "<html><body><h1>Document with Headers</h1><p>Content here</p></body></html>"
Dim config As New Configuration()
config.Document = html
config.AddUserStyleSheet("@page { @top-center { content: 'Header Text'; } @bottom-center { content: 'Page ' counter(page); } }")
Dim result As Result = pdfReactor.Convert(config)
File.WriteAllBytes("document.pdf", result.Document)
End Sub
End Module
After (IronPDF):
// NuGet: Install-Package IronPdf
using IronPdf;
using IronPdf.Rendering;
using System;
class Program
{
static void Main()
{
var renderer = new ChromePdfRenderer();
renderer.RenderingOptions.TextHeader = new TextHeaderFooter()
{
CenterText = "Header Text"
};
renderer.RenderingOptions.TextFooter = new TextHeaderFooter()
{
CenterText = "Page {page}"
};
string html = "<html><body><h1>Document with Headers</h1><p>Content here</p></body></html>";
var pdf = renderer.RenderHtmlAsPdf(html);
pdf.SaveAs("document.pdf");
}
}
// NuGet: Install-Package IronPdf
using IronPdf;
using IronPdf.Rendering;
using System;
class Program
{
static void Main()
{
var renderer = new ChromePdfRenderer();
renderer.RenderingOptions.TextHeader = new TextHeaderFooter()
{
CenterText = "Header Text"
};
renderer.RenderingOptions.TextFooter = new TextHeaderFooter()
{
CenterText = "Page {page}"
};
string html = "<html><body><h1>Document with Headers</h1><p>Content here</p></body></html>";
var pdf = renderer.RenderHtmlAsPdf(html);
pdf.SaveAs("document.pdf");
}
}
Imports IronPdf
Imports IronPdf.Rendering
Imports System
Module Program
Sub Main()
Dim renderer As New ChromePdfRenderer()
renderer.RenderingOptions.TextHeader = New TextHeaderFooter() With {
.CenterText = "Header Text"
}
renderer.RenderingOptions.TextFooter = New TextHeaderFooter() With {
.CenterText = "Page {page}"
}
Dim html As String = "<html><body><h1>Document with Headers</h1><p>Content here</p></body></html>"
Dim pdf = renderer.RenderHtmlAsPdf(html)
pdf.SaveAs("document.pdf")
End Sub
End Module
此範例顯示了最顯著的語法差異。PDFreactor使用 CSS 分頁媒體語法,其中 @page 規則,@top-center/@bottom-center 區域,以及 counter(page) 區域,用於透過 @@----CODE-19091--CO
IronPDF 使用原生 .NET API,將 TextHeaderFooter 物件指派給 RenderingOptions.TextHeader 和 RenderingOptions.TextFooter。 頁碼使用 {page} 佔位符,而非 CSS counter(page)。 請注意,IronPDF 也要求匯入 IronPdf.Rendering 命名空間以用於頁首/頁尾類別。
關鍵遷移注意事項
不需要伺服器。
IronPDF 在流程中執行 - 無需設定 Java 伺服器:
// PDFreactor: Requires server connection
var pdfReactor = new PDFreactor("http://localhost:9423");
// IronPDF: No server URL needed
var renderer = new ChromePdfRenderer();
// PDFreactor: Requires server connection
var pdfReactor = new PDFreactor("http://localhost:9423");
// IronPDF: No server URL needed
var renderer = new ChromePdfRenderer();
' PDFreactor: Requires server connection
Dim pdfReactor = New PDFreactor("http://localhost:9423")
' IronPDF: No server URL needed
Dim renderer = New ChromePdfRenderer()
CSS 分頁媒體到IronPDFAPI
將 CSS 規則 @page 替換為 RenderingOptions:
//PDFreactorCSS: @page { @bottom-center { content: 'Page ' counter(page); } }
//IronPDFequivalent:
renderer.RenderingOptions.TextFooter = new TextHeaderFooter
{
CenterText = "Page {page}"
};
//PDFreactorCSS: @page { @bottom-center { content: 'Page ' counter(page); } }
//IronPDFequivalent:
renderer.RenderingOptions.TextFooter = new TextHeaderFooter
{
CenterText = "Page {page}"
};
'PDFreactorCSS: @page { @bottom-center { content: 'Page ' counter(page); } }
'IronPDFequivalent:
renderer.RenderingOptions.TextFooter = New TextHeaderFooter With {
.CenterText = "Page {page}"
}
頁碼占位符語法
//PDFreactorCSS: counter(page)
// IronPDF: {page}
//PDFreactorCSS: counter(pages)
// IronPDF: {total-pages}
//PDFreactorCSS: counter(page)
// IronPDF: {page}
//PDFreactorCSS: counter(pages)
// IronPDF: {total-pages}
'PDFreactorCSS: counter(page)
' IronPDF: {page}
'PDFreactorCSS: counter(pages)
' IronPDF: {total-pages}
結果處理變更
配置 + 結果模式直接變成 PdfDocument:
// PDFreactor: Configuration → Convert → Result → bytes
Result result = pdfReactor.Convert(config);
byte[] bytes = result.Document;
File.WriteAllBytes("output.pdf", bytes);
// IronPDF: Direct PdfDocument with built-in methods
var pdf = renderer.RenderHtmlAsPdf(html);
pdf.SaveAs("output.pdf");
// Or: byte[] bytes = pdf.BinaryData;
// PDFreactor: Configuration → Convert → Result → bytes
Result result = pdfReactor.Convert(config);
byte[] bytes = result.Document;
File.WriteAllBytes("output.pdf", bytes);
// IronPDF: Direct PdfDocument with built-in methods
var pdf = renderer.RenderHtmlAsPdf(html);
pdf.SaveAs("output.pdf");
// Or: byte[] bytes = pdf.BinaryData;
' PDFreactor: Configuration → Convert → Result → bytes
Dim result As Result = pdfReactor.Convert(config)
Dim bytes As Byte() = result.Document
File.WriteAllBytes("output.pdf", bytes)
' IronPDF: Direct PdfDocument with built-in methods
Dim pdf = renderer.RenderHtmlAsPdf(html)
pdf.SaveAs("output.pdf")
' Or: Dim bytes As Byte() = pdf.BinaryData
邊際單位變更
PDFreactor 使用字串;IronPDF使用毫米:
// PDFreactor: config.PageMargins.Top = "1in"
// IronPDF: renderer.RenderingOptions.MarginTop = 25.4 // 1 inch in mm
// PDFreactor: config.PageMargins.Top = "1in"
// IronPDF: renderer.RenderingOptions.MarginTop = 25.4 // 1 inch in mm
' PDFreactor: config.PageMargins.Top = "1in"
' IronPDF: renderer.RenderingOptions.MarginTop = 25.4 ' 1 inch in mm
遷移後的新功能
轉移到IronPDF之後,您將獲得PDFreactor無法提供的功能:
PDF 合併
var pdf1 = PdfDocument.FromFile("document1.pdf");
var pdf2 = PdfDocument.FromFile("document2.pdf");
var merged = PdfDocument.Merge(pdf1, pdf2);
merged.SaveAs("merged.pdf");
var pdf1 = PdfDocument.FromFile("document1.pdf");
var pdf2 = PdfDocument.FromFile("document2.pdf");
var merged = PdfDocument.Merge(pdf1, pdf2);
merged.SaveAs("merged.pdf");
Dim pdf1 = PdfDocument.FromFile("document1.pdf")
Dim pdf2 = PdfDocument.FromFile("document2.pdf")
Dim merged = PdfDocument.Merge(pdf1, pdf2)
merged.SaveAs("merged.pdf")
水印。
pdf.ApplyWatermark("<h2 style='color:red;'>CONFIDENTIAL</h2>");
pdf.ApplyWatermark("<h2 style='color:red;'>CONFIDENTIAL</h2>");
pdf.ApplyWatermark("<h2 style='color:red;'>CONFIDENTIAL</h2>")
文字萃取
string text = pdf.ExtractAllText();
string text = pdf.ExtractAllText();
Dim text As String = pdf.ExtractAllText()
密碼保護
pdf.SecuritySettings.UserPassword = "userpassword";
pdf.SecuritySettings.OwnerPassword = "ownerpassword";
pdf.SecuritySettings.UserPassword = "userpassword";
pdf.SecuritySettings.OwnerPassword = "ownerpassword";
pdf.SecuritySettings.UserPassword = "userpassword"
pdf.SecuritySettings.OwnerPassword = "ownerpassword"
功能比較摘要
| 特點 | PDFreactor | IronPDF |
|---|---|---|
| HTML 至 PDF | ✓ | ✓ |
| URL 至 PDF | ✓ | ✓ |
| 頁首/頁尾 | CSS 分頁媒體 | 原生 API |
| 頁面設定 | ✓ | ✓ |
| JavaScript 支援 | ✓ | ✓ |
| 原生 .NET | ✗ | ✓ |
| 進行中 | ✗ | ✓ |
| 合併 PDF | ✗ | ✓ |
| 分割 PDF | ✗ | ✓ |
| 水印 | ✗ | ✓ |
| 文字擷取 | ✗ | ✓ |
| 表格填寫 | ✗ | ✓ |
| 數位簽名 | ✗ | ✓ |
遷移清單
預遷移
- 清點程式碼庫中所有PDFreactor的使用情況
- 記錄所有使用的 CSS 分頁媒體規則
- 注意所有組態設定(邊距、頁面大小、JavaScript)
- 規劃IronPDF許可證密鑰儲存(建議使用環境變數)
- 先使用IronPDF試用許可證進行測試
套件變更
- 刪除
PDFreactor.NETNuGet 套件 - 刪除
PDFreactor.Native.Windows.x64NuGet 套件 安裝IronPdfNuGet 套件:dotnet add package IronPdf
程式碼變更
- 更新命名空間導入(
using RealObjects.PDFreactor;→using IronPdf;) - 新增
using IronPdf.Rendering;以新增頁首/頁尾類 - 將
PDFreactor類別替換為ChromePdfRenderer - 將
Configuration物件轉換為RenderingOptions屬性 - 將
config.Document = html替換為renderer.RenderHtmlAsPdf(html) - 將
config.Document = url替換為renderer.RenderUrlAsPdf(url) - 將
File.WriteAllBytes(path, result.Document)替換為pdf.SaveAs(path) - 將 CSS 規則轉換為對象
- 更新頁碼佔位符(
counter(page)→{page}) - 將邊距單位從字串轉換為毫米
基礎架構遷移
- 移除 Java 執行時間要求
- 停用PDFreactor伺服器
- 更新 Docker/部署配置
- 更新 CI/CD 管線
後遷移
- 測試 PDF 輸出品質是否符合預期
- 驗證頁首/頁尾渲染效果
- 如果使用,請驗證 JavaScript 的執行情況
- 根據需要新增功能(合併、浮水印、安全)。

