Skip to footer content
MIGRATION GUIDES

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

  1. Watermarked Free Version: Production use requires a paid license with opaque pricing that requires contacting sales.

  2. 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
  3. External Binary Dependency: Requires managing wkhtmltopdf binaries per platform (wkhtmltopdf.exe, wkhtmltox.dll).

  4. No Active Development: The wrapper receives maintenance without underlying engine updates.

  5. Limited Async Support: Synchronous API blocks threads in web applications.

NReco PDF Generator vs IronPDF Comparison

AspectNReco PDF GeneratorIronPDF
Rendering EngineWebKit Qt (2012)Chromium (current)
Security20+ CVEs, no patchesActive security updates
CSS SupportCSS2.1, limited CSS3Full CSS3, Grid, Flexbox
JavaScriptBasic ES5Full ES6+, async/await
DependenciesExternal wkhtmltopdf binarySelf-contained
Async SupportSynchronous onlyFull async/await
Web FontsLimitedFull Google Fonts, @font-face
LicensingOpaque pricing, contact salesTransparent pricing
Free TrialWatermarkedFull 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

  1. .NET Environment: .NET Framework 4.6.2+ or .NET Core 3.1+ / .NET 5/6/7/8/9+
  2. NuGet Access: Ability to install NuGet packages
  3. 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 IronPdf
SHELL

Also remove wkhtmltopdf binaries from your deployment:

  • Delete wkhtmltopdf.exe, wkhtmltox.dll from 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";
$vbLabelText   $csharpLabel

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" .
SHELL

Complete API Reference

Core Class Mappings

NReco PDF GeneratorIronPDFNotes
HtmlToPdfConverterChromePdfRendererMain renderer
PageMarginsIndividual margin propertiesMarginTop, MarginBottom, etc.
PageOrientationPdfPaperOrientationEnum
PageSizePdfPaperSizeEnum

Rendering Method Mappings

NReco PDF GeneratorIronPDFNotes
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 GeneratorIronPDFNotes
PageWidth = 210RenderingOptions.PaperSize = PdfPaperSize.A4Use enum or SetCustomPaperSize
PageHeight = 297RenderingOptions.SetCustomPaperSizeinMilimeters(w, h)Custom size
Orientation = PageOrientation.LandscapeRenderingOptions.PaperOrientation = PdfPaperOrientation.LandscapeLandscape
Size = PageSize.A4RenderingOptions.PaperSize = PdfPaperSize.A4Paper size enum

Margin Mappings

NReco PDF GeneratorIronPDFNotes
Margins.Top = 10RenderingOptions.MarginTop = 10In millimeters
Margins.Bottom = 10RenderingOptions.MarginBottom = 10In millimeters
Margins.Left = 10RenderingOptions.MarginLeft = 10In millimeters
Margins.Right = 10RenderingOptions.MarginRight = 10In millimeters
new PageMargins { ... }Individual propertiesNo margins object
NReco PDF Generator (wkhtmltopdf)IronPDFNotes
[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 GeneratorIronPDFNotes
byte[] pdfBytes = GeneratePdf(html)PdfDocument pdf = RenderHtmlAsPdf(html)Returns object
File.WriteAllBytes(path, bytes)pdf.SaveAs(path)Direct save
return pdfBytesreturn pdf.BinaryDataGet byte array
new MemoryStream(pdfBytes)pdf.StreamGet 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);
    }
}
$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

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);
    }
}
$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 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/PageHeightRenderingOptions.PaperSize = PdfPaperSize.A4
  • new 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);
    }
}
$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 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);
$vbLabelText   $csharpLabel

Placeholder Syntax Update

All header/footer placeholders must be updated:

NReco PDF GeneratorIronPDF
[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
};
$vbLabelText   $csharpLabel

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;
$vbLabelText   $csharpLabel

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;
}
$vbLabelText   $csharpLabel

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;
}
$vbLabelText   $csharpLabel

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();
$vbLabelText   $csharpLabel

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;
$vbLabelText   $csharpLabel

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;
$vbLabelText   $csharpLabel

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
};
$vbLabelText   $csharpLabel

Migration Checklist

Pre-Migration

  • Inventory all NReco.PdfGenerator usages in codebase
  • Document all CustomWkHtmlArgs and CustomWkHtmlPageArgs values
  • 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.PdfGenerator NuGet package
  • Install IronPdf NuGet package: dotnet add package IronPdf
  • Update namespace imports from using NReco.PdfGenerator; to using IronPdf;

Code Changes

  • Add license key configuration at startup
  • Replace HtmlToPdfConverter with ChromePdfRenderer
  • Replace GeneratePdf(html) with RenderHtmlAsPdf(html)
  • Replace GeneratePdfFromFile(url, null) with RenderUrlAsPdf(url)
  • Convert PageMargins object to individual margin properties
  • Update zoom values from float to percentage
  • Update placeholder syntax: [page]{page}, [topage]{total-pages}
  • Replace File.WriteAllBytes() with pdf.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

Curtis Chau
Technical Writer

Curtis Chau holds a Bachelor’s degree in Computer Science (Carleton University) and specializes in front-end development with expertise in Node.js, TypeScript, JavaScript, and React. Passionate about crafting intuitive and aesthetically pleasing user interfaces, Curtis enjoys working with modern frameworks and creating well-structured, visually appealing manuals.

...

Read More