如何在 NET MAUI 中將 XAML 轉換為 PDF | IronPDF
從Spire.PDF遷移到IronPDF,可將您的PDF生成移出經常生成基於圖像文字的遺留HTML路徑,改為使用產生真實、可選擇、可搜尋文字的Chromium管道。 本指南提供了完整的逐步遷移路徑,涵蓋Spire.PDF的HTML渲染權衡和已知的字體嵌入問題。
為什麼從Spire.PDF遷移到IronPDF
了解Spire.PDF
Spire.PDF是一個功能強大的商業PDF程式庫,專為.NET開發人員設計,以高效處理PDF文件。 Spire.PDF已經在程式設計社群中因其特定功能,特別是在遺留應用程式中受到關注,並且其整合能力與E-iceblue工具集的其他元件無縫對接。
然而,Spire.PDF存在若干基本問題,影響其在實際使用中的效果,特別是在HTML到PDF轉換和現代網路標準支持方面。
關鍵技術問題
| 問題 | 影響 | IronPDF解決方案 |
|---|---|---|
| 經常在遺留HTML路徑中將文字呈現為圖像 | 無法搜尋的PDF,無法存取,無法複製文字 | 真實文字渲染 |
| IE/Edge遺留或QtWebKit在遺留路徑上的依賴 | 過時渲染,現代CSS問題 | 捆綁的現代Chromium |
| 已報告的字體嵌入問題 | 文件在其他系統上顯示不正確 | 可靠的字體處理 |
| 大的部署佔地 | 更高的記憶體使用量 | 捆綁的Chromium運行時 |
| 遺留路徑上的CSS有限 | 現代佈局無法正確渲染 | 完整的CSS3支持 |
核心問題:基於圖像的PDF
已知Spire.PDF的遺留HTML路徑的一個缺點是它傾向於將HTML文件中的文字呈現為圖像。 這產生了PDF,文字無法選擇或搜索——這對於需要搜索或文字交互的應用程式來說是一個限制。
當您在遺留引擎上使用Spire.PDF的LoadFromHTML()方法時,它經常將文字呈現為位圖圖像而不是實際文字,從而產生這些問題:
- 文字不能被選擇
- 文字不能被搜索
- 文字不能被複製
- 螢幕閱讀器不能讀取它(可及性違規)
- 文件大小大得多
- 放大時會導致像素化
Spire.PDF與IronPDF比較
| 功能 | Spire.PDF | IronPDF |
|---|---|---|
| HTML到PDF渲染 | 預設使用遺留IE/QtWebKit; 從v10.7.21開始,選擇取消ChromeHtmlConverter(需要系統Chrome) |
捆綁的Chromium(現代,無需額外安裝) |
| 文字輸出(HTML路徑) | 通常在遺留路徑上呈現為圖像 | 真實文字(可選擇和可搜尋) |
| 字體處理 | 遺留路徑上已報告問題 | 可靠的字體處理 |
| CSS3支援 | 遺留路徑上的限制 | 全面 |
| Flexbox/Grid | 遺留路徑不支持 | 支援 |
| JavaScript | 遺留路徑上的限制 | 完整ES6+ |
| PDF可達性 | 遺留路徑(基於圖像的輸出)很差 | 標記文字的輸出 |
| API設計 | 多重重載,手動頁面設置 | 單一流暢渲染器 |
| 授權 | 免費/商業 | 商業 |
在現代.NET上,IronPDF通過將文字呈現為選擇性文字而非圖像來解決Spire.PDF的HTML到PDF折衷,從而使生成的PDF可搜尋和可及性。
開始之前
前提條件
- .NET環境:.NET Framework 4.6.2+ 或.NET Core 3.1+ / .NET 5/6/7/8/9+
- NuGet存取: 有能力安裝NuGet套件
- IronPDF授權: 從ironpdf.com獲取您的授權金鑰
NuGet包變更
# Remove Spire.PDF
dotnet remove package Spire.PDF
dotnet remove package FreeSpire.PDF # If using free version
# Install IronPDF
dotnet add package IronPdf
# Remove Spire.PDF
dotnet remove package Spire.PDF
dotnet remove package FreeSpire.PDF # If using free version
# Install IronPDF
dotnet add package IronPdf
授權配置
// Add at application startup
IronPdf.License.LicenseKey = "YOUR-LICENSE-KEY";
// Add at application startup
IronPdf.License.LicenseKey = "YOUR-LICENSE-KEY";
' Add at application startup
IronPdf.License.LicenseKey = "YOUR-LICENSE-KEY"
完整API參考
名稱空間變更
// Before: Spire.PDF
using Spire.Pdf;
using Spire.Pdf.Graphics;
using Spire.Pdf.HtmlConverter;
// After: IronPDF
using IronPdf;
using IronPdf.Editing;
// Before: Spire.PDF
using Spire.Pdf;
using Spire.Pdf.Graphics;
using Spire.Pdf.HtmlConverter;
// After: IronPDF
using IronPdf;
using IronPdf.Editing;
Imports IronPdf
Imports IronPdf.Editing
核心API對應
| Spire.PDF | IronPDF |
|---|---|
new PdfDocument() |
new ChromePdfRenderer() |
pdf.LoadFromHTML() |
renderer.RenderHtmlAsPdf() |
pdf.LoadFromFile() |
PdfDocument.FromFile() |
pdf.SaveToFile() |
pdf.SaveAs() |
pdf.Close() |
不需要 |
pdf.Pages.Add() |
renderer.RenderHtmlAsPdf() |
pdf.InsertPageRange() |
PdfDocument.Merge() |
page.Canvas.DrawString() |
TextStamper + ApplyStamp() |
PdfFont |
HTML中的CSS樣式 |
PdfBrush |
HTML中的CSS樣式 |
程式碼遷移範例
範例1:HTML轉PDF轉換
之前(Spire.PDF):
// NuGet: Install-Package Spire.PDF
using Spire.Pdf;
using Spire.Pdf.Graphics;
using System;
class Program
{
static void Main()
{
PdfDocument pdf = new PdfDocument();
PdfHtmlLayoutFormat htmlLayoutFormat = new PdfHtmlLayoutFormat();
string htmlString = "<html><body><h1>Hello World</h1><p>This is a PDF from HTML.</p></body></html>";
pdf.LoadFromHTML(htmlString, false, true, true);
pdf.SaveToFile("output.pdf");
pdf.Close();
}
}
// NuGet: Install-Package Spire.PDF
using Spire.Pdf;
using Spire.Pdf.Graphics;
using System;
class Program
{
static void Main()
{
PdfDocument pdf = new PdfDocument();
PdfHtmlLayoutFormat htmlLayoutFormat = new PdfHtmlLayoutFormat();
string htmlString = "<html><body><h1>Hello World</h1><p>This is a PDF from HTML.</p></body></html>";
pdf.LoadFromHTML(htmlString, false, true, true);
pdf.SaveToFile("output.pdf");
pdf.Close();
}
}
Imports Spire.Pdf
Imports Spire.Pdf.Graphics
Imports System
Class Program
Shared Sub Main()
Dim pdf As New PdfDocument()
Dim htmlLayoutFormat As New PdfHtmlLayoutFormat()
Dim htmlString As String = "<html><body><h1>Hello World</h1><p>This is a PDF from HTML.</p></body></html>"
pdf.LoadFromHTML(htmlString, False, True, True)
pdf.SaveToFile("output.pdf")
pdf.Close()
End Sub
End Class
之後(IronPDF):
// NuGet: Install-Package IronPdf
using IronPdf;
using System;
class Program
{
static void Main()
{
var renderer = new ChromePdfRenderer();
string htmlString = "<html><body><h1>Hello World</h1><p>This is a PDF from HTML.</p></body></html>";
var pdf = renderer.RenderHtmlAsPdf(htmlString);
pdf.SaveAs("output.pdf");
}
}
// NuGet: Install-Package IronPdf
using IronPdf;
using System;
class Program
{
static void Main()
{
var renderer = new ChromePdfRenderer();
string htmlString = "<html><body><h1>Hello World</h1><p>This is a PDF from HTML.</p></body></html>";
var pdf = renderer.RenderHtmlAsPdf(htmlString);
pdf.SaveAs("output.pdf");
}
}
Imports IronPdf
Imports System
Class Program
Shared Sub Main()
Dim renderer = New ChromePdfRenderer()
Dim htmlString As String = "<html><body><h1>Hello World</h1><p>This is a PDF from HTML.</p></body></html>"
Dim pdf = renderer.RenderHtmlAsPdf(htmlString)
pdf.SaveAs("output.pdf")
End Sub
End Class
此範例演示了HTML渲染的基本差異。 Spire.PDF使用PdfHtmlLayoutFormat物件,經常將文字呈現為位圖圖像。 結果是PDF,使用者無法選擇、複製或搜索文字。
IronPDF使用RenderHtmlAsPdf(),生成完全可選擇、可搜索和可及性的真實文字。 不需要Close()調用——IronPDF使用dispose模式進行自動清理。參見HTML到PDF文件中的完整範例。
範例2:合併多個PDF
之前(Spire.PDF):
// NuGet: Install-Package Spire.PDF
using Spire.Pdf;
using System;
class Program
{
static void Main()
{
PdfDocument pdf1 = new PdfDocument();
pdf1.LoadFromFile("document1.pdf");
PdfDocument pdf2 = new PdfDocument();
pdf2.LoadFromFile("document2.pdf");
pdf1.InsertPageRange(pdf2, 0, pdf2.Pages.Count - 1);
pdf1.SaveToFile("merged.pdf");
pdf1.Close();
pdf2.Close();
}
}
// NuGet: Install-Package Spire.PDF
using Spire.Pdf;
using System;
class Program
{
static void Main()
{
PdfDocument pdf1 = new PdfDocument();
pdf1.LoadFromFile("document1.pdf");
PdfDocument pdf2 = new PdfDocument();
pdf2.LoadFromFile("document2.pdf");
pdf1.InsertPageRange(pdf2, 0, pdf2.Pages.Count - 1);
pdf1.SaveToFile("merged.pdf");
pdf1.Close();
pdf2.Close();
}
}
Imports Spire.Pdf
Imports System
Class Program
Shared Sub Main()
Dim pdf1 As New PdfDocument()
pdf1.LoadFromFile("document1.pdf")
Dim pdf2 As New PdfDocument()
pdf2.LoadFromFile("document2.pdf")
pdf1.InsertPageRange(pdf2, 0, pdf2.Pages.Count - 1)
pdf1.SaveToFile("merged.pdf")
pdf1.Close()
pdf2.Close()
End Sub
End Class
之後(IronPDF):
// NuGet: Install-Package IronPdf
using IronPdf;
using System;
class Program
{
static void Main()
{
var pdf1 = PdfDocument.FromFile("document1.pdf");
var pdf2 = PdfDocument.FromFile("document2.pdf");
var merged = PdfDocument.Merge(pdf1, pdf2);
merged.SaveAs("merged.pdf");
}
}
// NuGet: Install-Package IronPdf
using IronPdf;
using System;
class Program
{
static void Main()
{
var pdf1 = PdfDocument.FromFile("document1.pdf");
var pdf2 = PdfDocument.FromFile("document2.pdf");
var merged = PdfDocument.Merge(pdf1, pdf2);
merged.SaveAs("merged.pdf");
}
}
Imports IronPdf
Imports System
Class Program
Shared Sub Main()
Dim pdf1 = PdfDocument.FromFile("document1.pdf")
Dim pdf2 = PdfDocument.FromFile("document2.pdf")
Dim merged = PdfDocument.Merge(pdf1, pdf2)
merged.SaveAs("merged.pdf")
End Sub
End Class
Spire.PDF需要手動載入每個文件,使用new PdfDocument() + Close()。
IronPDF使用更簡單的PdfDocument.Merge()方法。 不需要Close()調用。 在我們的教程中了解更多。
範例3:向PDF新增文字
之前(Spire.PDF):
// NuGet: Install-Package Spire.PDF
using Spire.Pdf;
using Spire.Pdf.Graphics;
using System.Drawing;
using System;
class Program
{
static void Main()
{
PdfDocument pdf = new PdfDocument();
PdfPageBase page = pdf.Pages.Add();
PdfFont font = new PdfFont(PdfFontFamily.Helvetica, 20);
PdfBrush brush = new PdfSolidBrush(Color.Black);
page.Canvas.DrawString("Hello from Spire.PDF!", font, brush, new PointF(50, 50));
pdf.SaveToFile("output.pdf");
pdf.Close();
}
}
// NuGet: Install-Package Spire.PDF
using Spire.Pdf;
using Spire.Pdf.Graphics;
using System.Drawing;
using System;
class Program
{
static void Main()
{
PdfDocument pdf = new PdfDocument();
PdfPageBase page = pdf.Pages.Add();
PdfFont font = new PdfFont(PdfFontFamily.Helvetica, 20);
PdfBrush brush = new PdfSolidBrush(Color.Black);
page.Canvas.DrawString("Hello from Spire.PDF!", font, brush, new PointF(50, 50));
pdf.SaveToFile("output.pdf");
pdf.Close();
}
}
Imports Spire.Pdf
Imports Spire.Pdf.Graphics
Imports System.Drawing
Imports System
Class Program
Shared Sub Main()
Dim pdf As New PdfDocument()
Dim page As PdfPageBase = pdf.Pages.Add()
Dim font As New PdfFont(PdfFontFamily.Helvetica, 20)
Dim brush As PdfBrush = New PdfSolidBrush(Color.Black)
page.Canvas.DrawString("Hello from Spire.PDF!", font, brush, New PointF(50, 50))
pdf.SaveToFile("output.pdf")
pdf.Close()
End Sub
End Class
之後(IronPDF):
// NuGet: Install-Package IronPdf
using IronPdf;
using IronPdf.Editing;
using System;
class Program
{
static void Main()
{
var renderer = new ChromePdfRenderer();
var pdf = renderer.RenderHtmlAsPdf("<html><body></body></html>");
var textStamper = new TextStamper()
{
Text = "Hello from IronPDF!",
FontSize = 20,
VerticalOffset = 50,
HorizontalOffset = 50
};
pdf.ApplyStamp(textStamper);
pdf.SaveAs("output.pdf");
}
}
// NuGet: Install-Package IronPdf
using IronPdf;
using IronPdf.Editing;
using System;
class Program
{
static void Main()
{
var renderer = new ChromePdfRenderer();
var pdf = renderer.RenderHtmlAsPdf("<html><body></body></html>");
var textStamper = new TextStamper()
{
Text = "Hello from IronPDF!",
FontSize = 20,
VerticalOffset = 50,
HorizontalOffset = 50
};
pdf.ApplyStamp(textStamper);
pdf.SaveAs("output.pdf");
}
}
Imports IronPdf
Imports IronPdf.Editing
Imports System
Class Program
Shared Sub Main()
Dim renderer = New ChromePdfRenderer()
Dim pdf = renderer.RenderHtmlAsPdf("<html><body></body></html>")
Dim textStamper = New TextStamper() With {
.Text = "Hello from IronPDF!",
.FontSize = 20,
.VerticalOffset = 50,
.HorizontalOffset = 50
}
pdf.ApplyStamp(textStamper)
pdf.SaveAs("output.pdf")
End Sub
End Class
Spire.PDF使用基於畫布的繪圖模型,搭配PointF將文字定位在特定座標。
IronPDF使用帶有直觀屬性的ApplyStamp()應用它。 這種方法更具宣告性且易於維護。
文字作為圖像問題
為什麼這很重要
當Spire.PDF使用基於圖像的渲染器將HTML轉換為PDF時,您的文件將失去關鍵功能:
1. 無文字搜尋:使用者無法使用Ctrl+F查找文字。 文件管理系統無法索引內容。
2. 無文字選擇/複製:嘗試複製引用、參考或資料的使用者無法選擇文字——這是張圖像。
3. 可及性違規:基於圖像的PDF不符合WCAG 2.1規範、美國政府508條款、ADA要求以及螢幕閱讀器相容性。
4. 大文件大小:從遺留HTML路徑生成的基於圖像的輸出文件比IronPDF生成的相同內容的基於文字的輸出大得多。
檢測:您的PDF是否基於圖像?
打開您由Spire.PDF生成的文件,嘗試以下測試:
- 文字選擇:單擊並拖曳文字。 如果沒有高亮顯示 → 基於圖像
- Ctrl+F搜尋:在頁面上搜尋任何詞。 如果顯示"未發現匹配" → 基於圖像
- 複製/粘貼:選擇並將文字複製到記事本。 如果沒有任何粘貼 → 基於圖像
遺留HTML引擎問題
Spire.PDF的渲染引擎
Spire.PDF的LoadFromHTML(...)歷史上提供了兩條路徑:由Windows Internet Explorer/Edge遺留WebBrowser控制支援的非插件路徑,以及需要單獨下載的QT/WebKit插件路徑。 Spire.PDF 10.7.21(2024年中)增加了第三個選項ChromeHtmlConverter,它可以調用本地安裝的Google Chrome——但這需要選擇加入,而原本的LoadFromHTMLAPI仍然預設為遺留引擎。 Microsoft於2022年廢棄IE/Edge遺留版,現代CSS在遺留路徑上不可靠,JavaScript支持部分且渲染在不同系統上可能有所不同。
Spire.PDF中失敗的現代CSS
<div style="display: flex; justify-content: space-between; gap: 20px;">
<div style="flex: 1;">Column 1</div>
<div style="flex: 1;">Column 2</div>
</div>
<div style="display: grid; grid-template-columns: repeat(3, 1fr); gap: 10px;">
<div>Item 1</div>
<div>Item 2</div>
<div>Item 3</div>
</div>
<style>
:root { --primary-color: #007bff; }
h1 { color: var(--primary-color); }
</style>
<div style="display: flex; justify-content: space-between; gap: 20px;">
<div style="flex: 1;">Column 1</div>
<div style="flex: 1;">Column 2</div>
</div>
<div style="display: grid; grid-template-columns: repeat(3, 1fr); gap: 10px;">
<div>Item 1</div>
<div>Item 2</div>
<div>Item 3</div>
</div>
<style>
:root { --primary-color: #007bff; }
h1 { color: var(--primary-color); }
</style>
IronPDF使用現代Chromium渲染,因此所有這些CSS功能都能正確運行。
遷移後的新能力
遷移到IronPDF後,您將獲得Spire.PDF無法提供的功能:
可選擇、可搜尋的文字
var renderer = new ChromePdfRenderer();
var pdf = renderer.RenderHtmlAsPdf("<h1>Important Contract</h1>");
pdf.SaveAs("contract.pdf");
// Result:
// ✅ Text is fully selectable
// ✅ Text is searchable with Ctrl+F
// ✅ Text can be copied to clipboard
// ✅ Screen readers work perfectly
// ✅ File size is compact
// ✅ Zooming is crystal clear
var renderer = new ChromePdfRenderer();
var pdf = renderer.RenderHtmlAsPdf("<h1>Important Contract</h1>");
pdf.SaveAs("contract.pdf");
// Result:
// ✅ Text is fully selectable
// ✅ Text is searchable with Ctrl+F
// ✅ Text can be copied to clipboard
// ✅ Screen readers work perfectly
// ✅ File size is compact
// ✅ Zooming is crystal clear
Dim renderer = New ChromePdfRenderer()
Dim pdf = renderer.RenderHtmlAsPdf("<h1>Important Contract</h1>")
pdf.SaveAs("contract.pdf")
' Result:
' ✅ Text is fully selectable
' ✅ Text is searchable with Ctrl+F
' ✅ Text can be copied to clipboard
' ✅ Screen readers work perfectly
' ✅ File size is compact
' ✅ Zooming is crystal clear
現代CSS支持
var renderer = new ChromePdfRenderer();
var html = @"
<style>
:root { --primary: #007bff; }
.container { display: flex; gap: 20px; }
.grid { display: grid; grid-template-columns: repeat(3, 1fr); }
</style>
<div class='container'>
<div style='flex: 1; color: var(--primary)'>Column 1</div>
<div style='flex: 1'>Column 2</div>
</div>
<div class='grid'>
<div>Item 1</div>
<div>Item 2</div>
<div>Item 3</div>
</div>";
var pdf = renderer.RenderHtmlAsPdf(html);
// All modern CSS features render correctly!
var renderer = new ChromePdfRenderer();
var html = @"
<style>
:root { --primary: #007bff; }
.container { display: flex; gap: 20px; }
.grid { display: grid; grid-template-columns: repeat(3, 1fr); }
</style>
<div class='container'>
<div style='flex: 1; color: var(--primary)'>Column 1</div>
<div style='flex: 1'>Column 2</div>
</div>
<div class='grid'>
<div>Item 1</div>
<div>Item 2</div>
<div>Item 3</div>
</div>";
var pdf = renderer.RenderHtmlAsPdf(html);
// All modern CSS features render correctly!
Dim renderer = New ChromePdfRenderer()
Dim html = "
<style>
:root { --primary: #007bff; }
.container { display: flex; gap: 20px; }
.grid { display: grid; grid-template-columns: repeat(3, 1fr); }
</style>
<div class='container'>
<div style='flex: 1; color: var(--primary)'>Column 1</div>
<div style='flex: 1'>Column 2</div>
</div>
<div class='grid'>
<div>Item 1</div>
<div>Item 2</div>
<div>Item 3</div>
</div>"
Dim pdf = renderer.RenderHtmlAsPdf(html)
' All modern CSS features render correctly!
基於HTML的水印
var pdf = renderer.RenderHtmlAsPdf(html);
pdf.ApplyWatermark(@"
<div style='
font-size: 48px;
color: rgba(255, 0, 0, 0.5);
transform: rotate(-45deg);
'>DRAFT</div>");
var pdf = renderer.RenderHtmlAsPdf(html);
pdf.ApplyWatermark(@"
<div style='
font-size: 48px;
color: rgba(255, 0, 0, 0.5);
transform: rotate(-45deg);
'>DRAFT</div>");
Dim pdf = renderer.RenderHtmlAsPdf(html)
pdf.ApplyWatermark("
<div style='
font-size: 48px;
color: rgba(255, 0, 0, 0.5);
transform: rotate(-45deg);
'>DRAFT</div>")
遷移檢查表
遷移前
- 盤點程式碼庫中的所有Spire.PDF使用情況
- 測試現有PDF文字選擇性(關鍵問題檢測)
- 文件
LoadFromHTML()調用(這些是優先要修復的) - 獲取IronPDF授權金鑰來自ironpdf.com
程式碼更新
- 移除
Spire.PDFNuGet包(如果使用免費版本,還包括FreeSpire.PDF) - 安裝
IronPdfNuGet包 - 更新命名空間導入(
using Spire.Pdf;→using IronPdf;) - 將
RenderHtmlAsPdf()(關鍵修復) - 將
new PdfDocument()+PdfDocument.FromFile() - 將
PdfDocument.Merge() - 將
TextStamper+ApplyStamp() - 將
SaveAs() - 移除所有
Close()調用(在IronPDF中不需要) - 在應用程式啟動時新增授權初始化
測試
- 驗證生成的PDF文字是否可選擇(關鍵測試)
- 驗證CSS渲染改進(Flexbox/Grid現在可以運行)
- 驗證檔案大小是否較小
- 使用螢幕閱讀器測試可及性
- 性能比較

