How to Migrate from NReco PDF Generator to IronPDF in C#
Why Migrate from NReco PDF Generator to IronPDF
Critical Security Issues with NReco PDF Generator
NReco PDF Generator wraps the deprecated wkhtmltopdf binary, inheriting all its security vulnerabilities. This is not a theoretical concern—there are 20+ documented CVEs with no patches available since wkhtmltopdf was abandoned in 2020:
- CVE-2020-21365: Server-side request forgery (SSRF)
- CVE-2022-35583: Local file read via HTML injection
- CVE-2022-35580: Remote code execution potential
These vulnerabilities cannot be patched because the underlying wkhtmltopdf project is no longer maintained.
Additional NReco PDF Generator Limitations
Watermarked Free Version: Production use requires a paid license with opaque pricing that requires contacting sales.
Deprecated Rendering Engine: WebKit Qt (circa 2012) provides limited modern web support:
- No CSS Grid or Flexbox
- No modern JavaScript (ES6+)
- Poor web font support
- No CSS variables or custom properties
External Binary Dependency: Requires managing wkhtmltopdf binaries per platform (
wkhtmltopdf.exe,wkhtmltox.dll).No Active Development: The wrapper receives maintenance without underlying engine updates.
- Limited Async Support: Synchronous API blocks threads in web applications.
NReco PDF Generator vs IronPDF Comparison
| Aspect | NReco PDF Generator | IronPDF |
|---|---|---|
| Rendering Engine | WebKit Qt (2012) | Chromium (current) |
| Security | 20+ CVEs, no patches | Active security updates |
| CSS Support | CSS2.1, limited CSS3 | Full CSS3, Grid, Flexbox |
| JavaScript | Basic ES5 | Full ES6+, async/await |
| Dependencies | External wkhtmltopdf binary | Self-contained |
| Async Support | Synchronous only | Full async/await |
| Web Fonts | Limited | Full Google Fonts, @font-face |
| Licensing | Opaque pricing, contact sales | Transparent pricing |
| Free Trial | Watermarked | Full functionality |
For teams planning .NET 10 and C# 14 adoption through 2025 and 2026, IronPDF provides a future-proof foundation with active development and modern rendering capabilities.
Before You Start
Prerequisites
- .NET Environment: .NET Framework 4.6.2+ or .NET Core 3.1+ / .NET 5/6/7/8/9+
- NuGet Access: Ability to install NuGet packages
- IronPDF License: Obtain your license key from ironpdf.com
NuGet Package Changes
# 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 IronPdfAlso remove wkhtmltopdf binaries from your deployment:
- Delete
wkhtmltopdf.exe,wkhtmltox.dllfrom project - Remove any wkhtmltopdf installation scripts
- Delete platform-specific binary folders
License Configuration
// 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";Identify NReco PDF Generator Usage
# 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" .Complete API Reference
Core Class Mappings
| NReco PDF Generator | IronPDF | Notes |
|---|---|---|
HtmlToPdfConverter | ChromePdfRenderer | Main renderer |
PageMargins | Individual margin properties | MarginTop, MarginBottom, etc. |
PageOrientation | PdfPaperOrientation | Enum |
PageSize | PdfPaperSize | Enum |
Rendering Method Mappings
| NReco PDF Generator | IronPDF | Notes |
|---|---|---|
GeneratePdf(html) | RenderHtmlAsPdf(html) | Returns PdfDocument |
GeneratePdfFromFile(url, output) | RenderUrlAsPdf(url) | Direct URL support |
GeneratePdfFromFile(htmlPath, output) | RenderHtmlFileAsPdf(path) | File path |
| (async not supported) | RenderHtmlAsPdfAsync(html) | Async version |
| (async not supported) | RenderUrlAsPdfAsync(url) | Async version |
Page Configuration Mappings
| NReco PDF Generator | IronPDF | Notes |
|---|---|---|
PageWidth = 210 | RenderingOptions.PaperSize = PdfPaperSize.A4 | Use enum or SetCustomPaperSize |
PageHeight = 297 | RenderingOptions.SetCustomPaperSizeinMilimeters(w, h) | Custom size |
Orientation = PageOrientation.Landscape | RenderingOptions.PaperOrientation = PdfPaperOrientation.Landscape | Landscape |
Size = PageSize.A4 | RenderingOptions.PaperSize = PdfPaperSize.A4 | Paper size enum |
Margin Mappings
| NReco PDF Generator | IronPDF | Notes |
|---|---|---|
Margins.Top = 10 | RenderingOptions.MarginTop = 10 | In millimeters |
Margins.Bottom = 10 | RenderingOptions.MarginBottom = 10 | In millimeters |
Margins.Left = 10 | RenderingOptions.MarginLeft = 10 | In millimeters |
Margins.Right = 10 | RenderingOptions.MarginRight = 10 | In millimeters |
new PageMargins { ... } | Individual properties | No margins object |
Header/Footer Placeholder Mappings
| NReco PDF Generator (wkhtmltopdf) | IronPDF | Notes |
|---|---|---|
[page] | {page} | Current page number |
[topage] | {total-pages} | Total page count |
[date] | {date} | Current date |
[time] | {time} | Current time |
[title] | {html-title} | Document title |
Output Handling Mappings
| NReco PDF Generator | IronPDF | Notes |
|---|---|---|
byte[] pdfBytes = GeneratePdf(html) | PdfDocument pdf = RenderHtmlAsPdf(html) | Returns object |
File.WriteAllBytes(path, bytes) | pdf.SaveAs(path) | Direct save |
return pdfBytes | return pdf.BinaryData | Get byte array |
new MemoryStream(pdfBytes) | pdf.Stream | Get stream |
Code Migration Examples
Example 1: Basic HTML to PDF
Before (NReco PDF Generator):
// 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);
}
}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");
}
}The fundamental difference is in the return type and save pattern. NReco PDF Generator's HtmlToPdfConverter.GeneratePdf() returns a byte[] that you must manually write to disk using File.WriteAllBytes(). IronPDF's ChromePdfRenderer.RenderHtmlAsPdf() returns a PdfDocument object with a built-in SaveAs() method.
This object-oriented approach provides additional benefits: you can manipulate the PDF (add watermarks, merge documents, add security) before saving. If you need the raw bytes for compatibility with existing code, use pdf.BinaryData. See the HTML to PDF documentation for additional rendering options.
Example 2: Custom Page Size with Margins
Before (NReco PDF Generator):
// 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);
}
}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");
}
}NReco PDF Generator uses numeric dimensions (PageWidth = 210, PageHeight = 297) and a PageMargins object. IronPDF uses the PdfPaperSize enum (which includes standard sizes like A4, Letter, Legal) and individual margin properties on the RenderingOptions object.
The key migration changes:
PageWidth/PageHeight→RenderingOptions.PaperSize = PdfPaperSize.A4new PageMargins { Top = 10, ... }→ Individual properties:RenderingOptions.MarginTop = 10
For custom paper sizes not covered by the enum, use RenderingOptions.SetCustomPaperSizeinMilimeters(width, height). Learn more about page configuration options.
Example 3: URL to PDF Conversion
Before (NReco PDF Generator):
// 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);
}
}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");
}
}NReco PDF Generator uses the confusingly named GeneratePdfFromFile() method for both local files and URLs, with a nullable second parameter. IronPDF provides dedicated methods: RenderUrlAsPdf() for URLs and RenderHtmlFileAsPdf() for local HTML files.
The IronPDF approach is cleaner and more intuitive. For async web applications, use await renderer.RenderUrlAsPdfAsync(url) to avoid blocking threads—something NReco PDF Generator simply cannot do.
Critical Migration Notes
Zoom Value Conversion
NReco PDF Generator uses float values (0.0-2.0), while IronPDF uses percentage integers:
// 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);Placeholder Syntax Update
All header/footer placeholders must be updated:
| NReco PDF Generator | IronPDF |
|---|---|
[page] | {page} |
[topage] | {total-pages} |
[date] | {date} |
[title] | {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
};Return Type Change
NReco PDF Generator returns byte[] directly; IronPDF returns PdfDocument:
// NReco PDF Generator pattern:
byte[] pdfBytes = converter.GeneratePdf(html);
File.WriteAllBytes("output.pdf", pdfBytes);
// IronPDF pattern:
var pdf = renderer.RenderHtmlAsPdf(html);
pdf.SaveAs("output.pdf");
// Or if you need bytes:
byte[] pdfBytes = renderer.RenderHtmlAsPdf(html).BinaryData;// NReco PDF Generator pattern:
byte[] pdfBytes = converter.GeneratePdf(html);
File.WriteAllBytes("output.pdf", pdfBytes);
// IronPDF pattern:
var pdf = renderer.RenderHtmlAsPdf(html);
pdf.SaveAs("output.pdf");
// Or if you need bytes:
byte[] pdfBytes = renderer.RenderHtmlAsPdf(html).BinaryData;Thread Safety and Reusability
NReco PDF Generator typically creates a new converter per call. IronPDF's ChromePdfRenderer is thread-safe and can be reused:
// NReco PDF Generator pattern (creates new each time):
public byte[] Generate(string html)
{
var converter = new HtmlToPdfConverter();
return converter.GeneratePdf(html);
}
// IronPDF pattern (reuse renderer, thread-safe):
private readonly ChromePdfRenderer _renderer = new ChromePdfRenderer();
public byte[] Generate(string html)
{
return _renderer.RenderHtmlAsPdf(html).BinaryData;
}// NReco PDF Generator pattern (creates new each time):
public byte[] Generate(string html)
{
var converter = new HtmlToPdfConverter();
return converter.GeneratePdf(html);
}
// IronPDF pattern (reuse renderer, thread-safe):
private readonly ChromePdfRenderer _renderer = new ChromePdfRenderer();
public byte[] Generate(string html)
{
return _renderer.RenderHtmlAsPdf(html).BinaryData;
}Async Support (New Capability)
IronPDF supports async/await patterns that NReco PDF Generator cannot provide:
// 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;
}Troubleshooting
Issue 1: HtmlToPdfConverter Not Found
Problem: HtmlToPdfConverter class doesn't exist in IronPDF.
Solution: Use 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();Issue 2: GeneratePdf Returns Wrong Type
Problem: Code expects byte[] but gets PdfDocument.
Solution: Access .BinaryData property:
// 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;Issue 3: PageMargins Object Not Found
Problem: PageMargins class doesn't exist in IronPDF.
Solution: Use individual margin properties:
// 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;Issue 4: Page Numbers Not Appearing
Problem: [page] and [topage] placeholders don't work.
Solution: Update to IronPDF placeholder syntax:
// 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
};Migration Checklist
Pre-Migration
- Inventory all
NReco.PdfGeneratorusages in codebase - Document all
CustomWkHtmlArgsandCustomWkHtmlPageArgsvalues - List all header/footer HTML templates with placeholders
- Identify async requirements (web controllers, services)
- Review zoom and margin settings
- Backup existing PDF outputs for comparison
- Obtain IronPDF license key
Package Changes
- Remove
NReco.PdfGeneratorNuGet package - Install
IronPdfNuGet package:dotnet add package IronPdf - Update namespace imports from
using NReco.PdfGenerator;tousing IronPdf;
Code Changes
- Add license key configuration at startup
- Replace
HtmlToPdfConverterwithChromePdfRenderer - Replace
GeneratePdf(html)withRenderHtmlAsPdf(html) - Replace
GeneratePdfFromFile(url, null)withRenderUrlAsPdf(url) - Convert
PageMarginsobject to individual margin properties - Update zoom values from float to percentage
- Update placeholder syntax:
[page]→{page},[topage]→{total-pages} - Replace
File.WriteAllBytes()withpdf.SaveAs() - Convert sync calls to async where beneficial
Post-Migration
- Remove wkhtmltopdf binaries from project/deployment
- Update Docker files to remove wkhtmltopdf installation
- Run regression tests comparing PDF output
- Verify header/footer placeholders render correctly
- Test on all target platforms (Windows, Linux, macOS)
- Update CI/CD pipeline to remove wkhtmltopdf steps
- Update security scanning to confirm CVE removal






