跳至页脚内容
迁移指南

如何在 C# 中从 NReco PDF 生成器迁移到 IronPDF

为什么要从 NReco PDF 生成器迁移到 IronPDF?

NReco PDF 生成器的关键安全问题

NReco PDF Generator 封装了废弃的 wkhtmltopdf 二进制文件,继承了其所有安全漏洞。 这不是一个理论上的问题--自 wkhtmltopdf 于 2020 年被弃用以来,已有 20 多个记录在案的 CVE,但却没有可用的补丁:

  • CVE-2020-21365:服务器端请求伪造 (SSRF)
  • CVE-2022-35583:通过 HTML 注入读取本地文件
  • CVE-2022-35580:远程代码执行漏洞

由于底层 wkhtmltopdf 项目已不再维护,因此无法修补这些漏洞。

NReco PDF 生成器的其他限制

1.带水印的免费版本:生产用途需要付费许可证,定价不透明,需要联系销售人员。

2.已弃用的渲染引擎: WebKit Qt(大约在 2012 年)提供的现代 Web 支持有限:

  • 无 CSS 网格或 Flexbox
  • 无现代 JavaScript(ES6+)
  • 网页字体支持不佳
  • 无 CSS 变量或自定义属性

3.外部二进制依赖项:需要管理每个平台的 wkhtmltopdf 二进制文件( wkhtmltopdf.exewkhtmltox.dll )。

4.无主动开发:封装器得到维护,但底层引擎没有更新。

5.异步支持有限:同步 API 会阻塞 Web 应用程序中的线程。

NReco PDF 生成器与IronPDF的比较

方面 NReco PDF 生成器 IronPDF
渲染引擎 WebKit Qt (2012) Chromium (当前)
安全性 20 多个 CVE,无补丁 主动安全更新
CSS 支持 CSS2.1, 有限 CSS3 完全 CSS3、网格、Flexbox
JavaScript 基本 ES5 完整的 ES6+、async/await
依赖关系 外部 wkhtmltopdf 二进制文件 自成一体
异步支持 仅同步 完整的异步/等待
网络字体 有限的 完整的谷歌字体,@font-face
许可 定价不透明,请联系销售人员 透明定价
免费试用 带水印 全部功能

对于计划在 2025 年和 2026 年之前采用 .NET 10 和 C# 14 的团队来说,IronPDF 提供了一个面向未来的基础,具有主动开发和现代渲染功能。


开始之前

前提条件

  1. .NET 环境: .NET Framework 4.6.2+ 或 .NET Core 3.1+ / .NET 5/6/7/8/9+
  2. NuGet 访问权限:能够安装 NuGet 包
  3. IronPDF 许可证:请从ironpdf.com获取您的许可证密钥。

NuGet 软件包变更

# Remove NReco.PdfGenerator
dotnet remove package NReco.PdfGenerator

# Install IronPDF
dotnet add package IronPdf
# Remove NReco.PdfGenerator
dotnet remove package NReco.PdfGenerator

# Install IronPDF
dotnet add package IronPdf
SHELL

同时从您的部署中删除 wkhtmltopdf 二进制文件

  • 从项目中删除 wkhtmltopdf.exe, wkhtmltox.dll
  • 删除任何 wkhtmltopdf 安装脚本
  • 删除特定平台的二进制文件夹

许可配置

// Add at application startup (Program.cs or Startup.cs)
IronPdf.License.LicenseKey = "YOUR-LICENSE-KEY";
// Add at application startup (Program.cs or Startup.cs)
IronPdf.License.LicenseKey = "YOUR-LICENSE-KEY";
$vbLabelText   $csharpLabel

确定 NReco PDF 生成器的用途

# Find all NReco.PdfGenerator references
grep -r "NReco.PdfGenerator\|HtmlToPdfConverter\|GeneratePdf" --include="*.cs" .
# Find all NReco.PdfGenerator references
grep -r "NReco.PdfGenerator\|HtmlToPdfConverter\|GeneratePdf" --include="*.cs" .
SHELL

完整的 API 参考

核心类映射

NReco PDF 生成器 IronPDF 备注
HtmlToPdfConverter ChromePdfRenderer 主呈现器
页边距 个别边距属性 MarginTop、MarginBottom 等。
页面方向 PdfPaperOrientation 枚举
页面大小 PdfPaperSize 枚举

渲染方法映射

NReco PDF 生成器 IronPDF 备注
GeneratePdf(html) RenderHtmlAsPdf(html) 返回 PdfDocument
GeneratePdfFromFile(url,输出) RenderUrlAsPdf(url) 支持直接 URL
GeneratePdfFromFile(htmlPath,输出) RenderHtmlFileAsPdf(path) 文件路径
(不支持异步) RenderHtmlAsPdfAsync(html) 异步版本
(不支持异步) RenderUrlAsPdfAsync(url) 异步版本

页面配置映射

NReco PDF 生成器 IronPDF 备注
页面宽度 = 210 RenderingOptions.PaperSize = PdfPaperSize.A4 使用枚举或 SetCustomPaperSize
页面高度 = 297 RenderingOptions.SetCustomPaperSizeinMilimeters(w, h) 定制尺寸
Orientation = PageOrientation.Landscape RenderingOptions.PaperOrientation = PdfPaperOrientation.Landscape 景观
大小 = PageSize.A4 RenderingOptions.PaperSize = PdfPaperSize.A4 纸张尺寸枚举

边距映射

NReco PDF 生成器 IronPDF 备注
Margins.Top = 10 RenderingOptions.MarginTop = 10 单位:毫米
Margins.Bottom = 10 RenderingOptions.MarginBottom = 10 单位:毫米
Margins.Left = 10 RenderingOptions.MarginLeft = 10 单位:毫米
Margins.Right = 10 RenderingOptions.MarginRight = 10 单位:毫米
new PageMargins { ... } 个别属性 无保证金对象

页眉/页脚占位符映射

NReco PDF 生成器 (wkhtmltopdf) IronPDF 备注
[页面] {page} 当前页码
[topage] {总页数} 总页数
[日期] {日期} 当前日期
[时间] {时间} 当前时间
[标题] {html-title} 文件标题

输出处理映射

NReco PDF 生成器 IronPDF 备注
byte[] pdfBytes = GeneratePdf(html) PdfDocument pdf = RenderHtmlAsPdf(html) 返回对象
File.WriteAllBytes(path,字节) pdf.SaveAs(路径) 直接保存
返回 pdfBytes 返回 pdf.BinaryData 获取字节数组
new MemoryStream(pdfBytes) pdf.Stream 获取流

代码迁移示例

示例 1:将基本 HTML 转换为 PDF.

之前(NReco PDF 生成器):

// NuGet: Install-Package NReco.PdfGenerator
using NReco.PdfGenerator;
using System.IO;

class Program
{
    static void Main()
    {
        var htmlToPdf = new HtmlToPdfConverter();
        var htmlContent = "<html><body><h1>Hello World</h1><p>This is a PDF document.</p></body></html>";
        var pdfBytes = htmlToPdf.GeneratePdf(htmlContent);
        File.WriteAllBytes("output.pdf", pdfBytes);
    }
}
// NuGet: Install-Package NReco.PdfGenerator
using NReco.PdfGenerator;
using System.IO;

class Program
{
    static void Main()
    {
        var htmlToPdf = new HtmlToPdfConverter();
        var htmlContent = "<html><body><h1>Hello World</h1><p>This is a PDF document.</p></body></html>";
        var pdfBytes = htmlToPdf.GeneratePdf(htmlContent);
        File.WriteAllBytes("output.pdf", pdfBytes);
    }
}
$vbLabelText   $csharpLabel

After (IronPDF):

// NuGet: Install-Package IronPdf
using IronPdf;
using System.IO;

class Program
{
    static void Main()
    {
        var renderer = new ChromePdfRenderer();
        var htmlContent = "<html><body><h1>Hello World</h1><p>This is a PDF document.</p></body></html>";
        var pdf = renderer.RenderHtmlAsPdf(htmlContent);
        pdf.SaveAs("output.pdf");
    }
}
// NuGet: Install-Package IronPdf
using IronPdf;
using System.IO;

class Program
{
    static void Main()
    {
        var renderer = new ChromePdfRenderer();
        var htmlContent = "<html><body><h1>Hello World</h1><p>This is a PDF document.</p></body></html>";
        var pdf = renderer.RenderHtmlAsPdf(htmlContent);
        pdf.SaveAs("output.pdf");
    }
}
$vbLabelText   $csharpLabel

基本区别在于返回类型和保存模式。NReco PDF 生成器的 HtmlToPdfConverter.GeneratePdf() 返回一个 byte[] ,您必须使用 File.WriteAllBytes() 手动将其写入磁盘。IronPDF的 ChromePdfRenderer.RenderHtmlAsPdf() 返回一个带有内置 SaveAs() 方法的 PdfDocument 对象。

这种面向对象的方法还有其他好处:您可以在保存之前对 PDF 进行操作(添加水印、合并文档、添加安全性)。 如果您需要原始字节以便与现有代码兼容,请使用 pdf.BinaryData. 有关其他渲染选项,请参阅 HTML to PDF 文档

示例 2:带边距的自定义页面大小

之前(NReco PDF 生成器):

// NuGet: Install-Package NReco.PdfGenerator
using NReco.PdfGenerator;
using System.IO;

class Program
{
    static void Main()
    {
        var htmlToPdf = new HtmlToPdfConverter();
        htmlToPdf.PageWidth = 210;
        htmlToPdf.PageHeight = 297;
        htmlToPdf.Margins = new PageMargins { Top = 10, Bottom = 10, Left = 10, Right = 10 };
        var htmlContent = "<html><body><h1>Custom Page Size</h1><p>A4 size document with margins.</p></body></html>";
        var pdfBytes = htmlToPdf.GeneratePdf(htmlContent);
        File.WriteAllBytes("custom-size.pdf", pdfBytes);
    }
}
// NuGet: Install-Package NReco.PdfGenerator
using NReco.PdfGenerator;
using System.IO;

class Program
{
    static void Main()
    {
        var htmlToPdf = new HtmlToPdfConverter();
        htmlToPdf.PageWidth = 210;
        htmlToPdf.PageHeight = 297;
        htmlToPdf.Margins = new PageMargins { Top = 10, Bottom = 10, Left = 10, Right = 10 };
        var htmlContent = "<html><body><h1>Custom Page Size</h1><p>A4 size document with margins.</p></body></html>";
        var pdfBytes = htmlToPdf.GeneratePdf(htmlContent);
        File.WriteAllBytes("custom-size.pdf", pdfBytes);
    }
}
$vbLabelText   $csharpLabel

After (IronPDF):

// NuGet: Install-Package IronPdf
using IronPdf;
using IronPdf.Rendering;

class Program
{
    static void Main()
    {
        var renderer = new ChromePdfRenderer();
        renderer.RenderingOptions.PaperSize = PdfPaperSize.A4;
        renderer.RenderingOptions.MarginTop = 10;
        renderer.RenderingOptions.MarginBottom = 10;
        renderer.RenderingOptions.MarginLeft = 10;
        renderer.RenderingOptions.MarginRight = 10;
        var htmlContent = "<html><body><h1>Custom Page Size</h1><p>A4 size document with margins.</p></body></html>";
        var pdf = renderer.RenderHtmlAsPdf(htmlContent);
        pdf.SaveAs("custom-size.pdf");
    }
}
// NuGet: Install-Package IronPdf
using IronPdf;
using IronPdf.Rendering;

class Program
{
    static void Main()
    {
        var renderer = new ChromePdfRenderer();
        renderer.RenderingOptions.PaperSize = PdfPaperSize.A4;
        renderer.RenderingOptions.MarginTop = 10;
        renderer.RenderingOptions.MarginBottom = 10;
        renderer.RenderingOptions.MarginLeft = 10;
        renderer.RenderingOptions.MarginRight = 10;
        var htmlContent = "<html><body><h1>Custom Page Size</h1><p>A4 size document with margins.</p></body></html>";
        var pdf = renderer.RenderHtmlAsPdf(htmlContent);
        pdf.SaveAs("custom-size.pdf");
    }
}
$vbLabelText   $csharpLabel

NReco PDF Generator 使用数字尺寸(PageWidth = 210PageHeight = 297)和页边距对象。IronPDF使用 RenderingOptions 对象上的PdfPaperSize枚举(包括 A4、Letter、Legal 等标准尺寸)和单个页边距属性。

关键的迁移变化:

  • 页面宽度/页面高度RenderingOptions.PaperSize = PdfPaperSize.A4
  • new PageMargins { Top = 10, ... }→ 单个属性:RenderingOptions.MarginTop = 10.

对于枚举未涵盖的自定义纸张尺寸,请使用 RenderingOptions.SetCustomPaperSizeinMilimeters(width, height)。 了解页面配置选项的更多信息。

示例 3:URL 到 PDF 的转换

之前(NReco PDF 生成器):

// NuGet: Install-Package NReco.PdfGenerator
using NReco.PdfGenerator;
using System.IO;

class Program
{
    static void Main()
    {
        var htmlToPdf = new HtmlToPdfConverter();
        var pdfBytes = htmlToPdf.GeneratePdfFromFile("https://www.example.com", null);
        File.WriteAllBytes("webpage.pdf", pdfBytes);
    }
}
// NuGet: Install-Package NReco.PdfGenerator
using NReco.PdfGenerator;
using System.IO;

class Program
{
    static void Main()
    {
        var htmlToPdf = new HtmlToPdfConverter();
        var pdfBytes = htmlToPdf.GeneratePdfFromFile("https://www.example.com", null);
        File.WriteAllBytes("webpage.pdf", pdfBytes);
    }
}
$vbLabelText   $csharpLabel

After (IronPDF):

// NuGet: Install-Package IronPdf
using IronPdf;

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;

class Program
{
    static void Main()
    {
        var renderer = new ChromePdfRenderer();
        var pdf = renderer.RenderUrlAsPdf("https://www.example.com");
        pdf.SaveAs("webpage.pdf");
    }
}
$vbLabelText   $csharpLabel

NReco PDF Generator 对本地文件和 URL 均使用名称混乱的 GeneratePdfFromFile() 方法,第二个参数为空。 IronPdf 提供了专用方法:RenderUrlAsPdf() 用于 URL,RenderHtmlFileAsPdf() 用于本地 HTML 文件。

IronPdf 的方法更简洁、更直观。对于异步网络应用程序,使用 await renderer.RenderUrlAsPdfAsync(url) 可避免阻塞线程--这是NReco PDF 生成器无法做到的。


关键迁移说明

缩放值转换

NReco PDF Generator 使用浮点数值(0.0-2.0),而IronPDF使用百分比整数:

// NReco PDF Generator: Zoom = 0.9f (90%)
// IronPDF: Zoom = 90

// Conversion formula:
int ironPdfZoom = (int)(nrecoZoom * 100);
// NReco PDF Generator: Zoom = 0.9f (90%)
// IronPDF: Zoom = 90

// Conversion formula:
int ironPdfZoom = (int)(nrecoZoom * 100);
$vbLabelText   $csharpLabel

占位符语法更新

必须更新所有页眉/页脚占位符:

NReco PDF 生成器 IronPDF
[页面] {page}
[topage] {总页数}
[日期] {日期}
[标题] {html-title}
// NReco PDF Generator:
converter.PageFooterHtml = "<div>Page [page] of [topage]</div>";

// IronPDF:
renderer.RenderingOptions.HtmlFooter = new HtmlHeaderFooter
{
    HtmlFragment = "<div>Page {page} of {total-pages}</div>",
    MaxHeight = 20
};
// NReco PDF Generator:
converter.PageFooterHtml = "<div>Page [page] of [topage]</div>";

// IronPDF:
renderer.RenderingOptions.HtmlFooter = new HtmlHeaderFooter
{
    HtmlFragment = "<div>Page {page} of {total-pages}</div>",
    MaxHeight = 20
};
$vbLabelText   $csharpLabel

返回类型更改

NReco PDF Generator 可直接返回 byte[] ;IronPDF返回 PdfDocument

//NReco PDF 生成器pattern:
byte[] pdfBytes = converter.GeneratePdf(html);
File.WriteAllBytes("output.pdf", pdfBytes);

//IronPDFpattern:
var pdf = renderer.RenderHtmlAsPdf(html);
pdf.SaveAs("output.pdf");

// Or if you need bytes:
byte[] pdfBytes = renderer.RenderHtmlAsPdf(html).BinaryData;
//NReco PDF 生成器pattern:
byte[] pdfBytes = converter.GeneratePdf(html);
File.WriteAllBytes("output.pdf", pdfBytes);

//IronPDFpattern:
var pdf = renderer.RenderHtmlAsPdf(html);
pdf.SaveAs("output.pdf");

// Or if you need bytes:
byte[] pdfBytes = renderer.RenderHtmlAsPdf(html).BinaryData;
$vbLabelText   $csharpLabel

线程安全和可重用性

NReco PDF 生成器通常每次调用都会创建一个新的转换器。IronPDF的ChromePdfRenderer`是线程安全的,可以重复使用:

//NReco PDF 生成器pattern (creates new each time):
public byte[] Generate(string html)
{
    var converter = new HtmlToPdfConverter();
    return converter.GeneratePdf(html);
}

//IronPDFpattern (reuse renderer, thread-safe):
private readonly ChromePdfRenderer _renderer = new ChromePdfRenderer();

public byte[] Generate(string html)
{
    return _renderer.RenderHtmlAsPdf(html).BinaryData;
}
//NReco PDF 生成器pattern (creates new each time):
public byte[] Generate(string html)
{
    var converter = new HtmlToPdfConverter();
    return converter.GeneratePdf(html);
}

//IronPDFpattern (reuse renderer, thread-safe):
private readonly ChromePdfRenderer _renderer = new ChromePdfRenderer();

public byte[] Generate(string html)
{
    return _renderer.RenderHtmlAsPdf(html).BinaryData;
}
$vbLabelText   $csharpLabel

异步支持(新功能)

IronPDF 支持NReco PDF 生成器无法提供的异步/等待模式:

// NReco PDF Generator: No async support available

// IronPDF: Full async support
public async Task<byte[]> GenerateAsync(string html)
{
    var pdf = await _renderer.RenderHtmlAsPdfAsync(html);
    return pdf.BinaryData;
}
// NReco PDF Generator: No async support available

// IronPDF: Full async support
public async Task<byte[]> GenerateAsync(string html)
{
    var pdf = await _renderer.RenderHtmlAsPdfAsync(html);
    return pdf.BinaryData;
}
$vbLabelText   $csharpLabel

故障排除

问题 1:HtmlToPdfConverter 未找到

问题:IronPDF中不存在HtmlToPdfConverter类。

解决方案:使用ChromePdfRenderer

// NReco PDF Generator
var converter = new HtmlToPdfConverter();

// IronPDF
var renderer = new ChromePdfRenderer();
// NReco PDF Generator
var converter = new HtmlToPdfConverter();

// IronPDF
var renderer = new ChromePdfRenderer();
$vbLabelText   $csharpLabel

问题 2:GeneratePdf 返回错误类型

问题:代码期望byte[]但得到的是PdfDocument

解决方案:访问.BinaryData属性:

// NReco PDF Generator
byte[] pdfBytes = converter.GeneratePdf(html);

// IronPDF
byte[] pdfBytes = renderer.RenderHtmlAsPdf(html).BinaryData;
// NReco PDF Generator
byte[] pdfBytes = converter.GeneratePdf(html);

// IronPDF
byte[] pdfBytes = renderer.RenderHtmlAsPdf(html).BinaryData;
$vbLabelText   $csharpLabel

问题 3:未找到 PageMargins 对象

问题:IronPDF中不存在PageMargins类。

解决方法:使用单独的边距属性:

// NReco PDF Generator
converter.Margins = new PageMargins { Top = 10, Bottom = 10, Left = 10, Right = 10 };

// IronPDF
renderer.RenderingOptions.MarginTop = 10;
renderer.RenderingOptions.MarginBottom = 10;
renderer.RenderingOptions.MarginLeft = 10;
renderer.RenderingOptions.MarginRight = 10;
// NReco PDF Generator
converter.Margins = new PageMargins { Top = 10, Bottom = 10, Left = 10, Right = 10 };

// IronPDF
renderer.RenderingOptions.MarginTop = 10;
renderer.RenderingOptions.MarginBottom = 10;
renderer.RenderingOptions.MarginLeft = 10;
renderer.RenderingOptions.MarginRight = 10;
$vbLabelText   $csharpLabel

问题 4:页码未显示

问题: [page][topage]占位符不起作用。

解决方案:更新IronPDF占位符语法:

// NReco PDF Generator
converter.PageFooterHtml = "<div>Page [page] of [topage]</div>";

// IronPDF
renderer.RenderingOptions.HtmlFooter = new HtmlHeaderFooter
{
    HtmlFragment = "<div>Page {page} of {total-pages}</div>",
    MaxHeight = 20
};
// NReco PDF Generator
converter.PageFooterHtml = "<div>Page [page] of [topage]</div>";

// IronPDF
renderer.RenderingOptions.HtmlFooter = new HtmlHeaderFooter
{
    HtmlFragment = "<div>Page {page} of {total-pages}</div>",
    MaxHeight = 20
};
$vbLabelText   $csharpLabel

迁移清单

迁移前

  • 清点代码库中所有NReco.PdfGenerator使用情况
  • 记录所有CustomWkHtmlArgsCustomWkHtmlPageArgs的值 列出所有带有占位符的页眉/页脚HTML模板
  • 确定异步需求(Web 控制器、服务)
  • 检查缩放和边距设置
  • 备份现有 PDF 输出以进行比较
  • 获取IronPDF许可证密钥

软件包变更

  • 移除NReco.PdfGenerator NuGet 包 安装IronPdf NuGet 包: dotnet add package IronPdf
  • 将命名空间导入从using NReco.PdfGenerator;更新为using IronPdf;

代码更改

  • 在启动时添加许可证密钥配置
  • HtmlToPdfConverter替换为ChromePdfRenderer
  • GeneratePdf(html)替换为RenderHtmlAsPdf(html)
  • GeneratePdfFromFile(url, null)替换为RenderUrlAsPdf(url)
  • PageMargins对象转换为单独的边距属性
  • 将缩放值从浮点数更新为百分比
  • 更新占位符语法:[页面]{page}[topage]{total-pages}
  • File.WriteAllBytes()替换为pdf.SaveAs()
  • 在有利的情况下,将同步调用转换为异步调用。

后迁移

  • 从项目/部署中移除 wkhtmltopdf 二进制文件
  • 更新 Docker 文件以移除 wkhtmltopdf 安装
  • 运行回归测试,比较 PDF 输出
  • 验证页眉/页脚占位符是否正确渲染。
  • 在所有目标平台(Windows、Linux、macOS)上进行测试
  • 更新 CI/CD 流水线,移除 wkhtmltopdf 步骤
  • 更新安全扫描以确认 CVE 已移除

Curtis Chau
技术作家

Curtis Chau 拥有卡尔顿大学的计算机科学学士学位,专注于前端开发,精通 Node.js、TypeScript、JavaScript 和 React。他热衷于打造直观且美观的用户界面,喜欢使用现代框架并创建结构良好、视觉吸引力强的手册。

除了开发之外,Curtis 对物联网 (IoT) 有浓厚的兴趣,探索将硬件和软件集成的新方法。在空闲时间,他喜欢玩游戏和构建 Discord 机器人,将他对技术的热爱与创造力相结合。