如何在 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; 选择加入ChromeHtmlConverter(需要系统Chrome)自v10.7.21以来 |
捆绑 Chromium(现代,无需额外安装) |
| 文本输出(HTML 路径) | 经常在遗留路径上渲染为图像 | 真实文本(可选择和可搜索) |
| 字体处理 | 遗留路径上的问题报告 | 可靠的字体处理 |
| CSS3 支持。 | 遗留路径上的限制 | 满的 |
| Flexbox/网格 | 遗留路径不支持 | 支持 |
| 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
After (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使用释放模式进行自动清理。有关全面示例,请参阅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
After (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
After (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 生成的文档并尝试这些测试:
1.文本选择:点击并拖动选择文本。 如果没有什么亮点 → 基于图像
- Ctrl+F 搜索:搜索页面上的任何单词。 如果 "未找到匹配内容",请选择 "基于图像"。 3.复制/粘贴:选择文本并复制到记事本。 如果没有粘贴 → 基于图像
遗留 HTML 引擎问题
Spire.PDF 的渲染引擎
Spire.PDF的LoadFromHTML(...)历史上提供了两种路径:通过Windows Internet Explorer / Edge传统WebBrowser控件的非插件路径,以及通过单独下载的QT/WebKit插件路径。 Spire.PDF 10.7.21(2024年中)添加了第三种选项ChromeHtmlConverter,它调用本地安装的Google Chrome —— 但这是选择加入,原始的LoadFromHTML API仍然默认为传统引擎。 IE/Edge 遗留由微软在2022年弃用,现代 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.com获取IronPDF许可证密钥
代码更新
- 移除
Spire.PDFNuGet包(如果使用免费版本则也移除FreeSpire.PDF) - 安装
IronPdfNuGet包 - 更新命名空间导入(
using Spire.Pdf;→using IronPdf;) - 用
LoadFromHTML()(关键修复) - 用
new PdfDocument()+LoadFromFile() - 用
InsertPageRange() - 用
TextStamper+Canvas.DrawString() - 用
SaveToFile() - 移除所有
Close()调用(在IronPDF中不需要) - 在应用程序启动时添加许可证初始化
测试
- 验证生成的 PDF 文件中的文本是否可选(关键测试)
- 验证 CSS 渲染改进(Flexbox/Grid 现在可以正常工作)
- 确认文件大小较小
- 使用屏幕阅读器测试辅助功能
- 性能比较

