How to Migrate from Spire.PDF to IronPDF in C#
Migrating from Spire.PDF to IronPDF moves your PDF generation off a legacy HTML path that frequently produces image-based text and onto a Chromium pipeline that produces true, selectable, searchable text. This guide provides a complete, step-by-step migration path covering Spire.PDF's HTML rendering trade-offs and reported font embedding issues.
Why Migrate from Spire.PDF to IronPDF
Understanding Spire.PDF
Spire.PDF is a robust, commercial PDF library designed for .NET developers to handle PDF documents efficiently. Spire.PDF has made its mark in the programming community for its specific capabilities, especially in legacy applications, and its integration capabilities seamlessly align with other components in the E-iceblue set of tools.
However, Spire.PDF has several fundamental problems that affect real-world usage, particularly around HTML-to-PDF conversion and modern web standards support.
Critical Technical Issues
| Issue | Impact | IronPDF Solution |
|---|---|---|
| Text often rendered as images on the legacy HTML path | PDFs not searchable, not accessible, can't copy text | Real text rendering |
| IE/Edge Legacy or QtWebKit dependency on legacy path | Outdated rendering, modern CSS issues | Bundled modern Chromium |
| Font embedding issues reported | Documents look wrong on other systems | Reliable font handling |
| Large deployment footprint | Higher memory usage | Bundled Chromium runtime |
| Limited CSS on legacy path | Modern layouts don't render correctly | Full CSS3 support |
The Core Problem: Image-Based PDFs
One reported drawback of Spire.PDF's legacy HTML path is its inclination to render text within HTML documents as images. This produces PDFs where text is not selectable or searchable — a limitation for applications needing search or text interaction.
When you use Spire.PDF's LoadFromHTML() method on the legacy engine, it often renders text as bitmap images rather than actual text, creating these problems:
- Text CANNOT be selected
- Text CANNOT be searched
- Text CANNOT be copied
- Screen readers CANNOT read it (accessibility violation)
- File size is MUCH larger
- Zooming causes pixelation
Spire.PDF vs IronPDF Comparison
| Feature | Spire.PDF | IronPDF |
|---|---|---|
| HTML to PDF Rendering | Legacy IE/QtWebKit by default; opt-in ChromeHtmlConverter (requires system Chrome) since v10.7.21 |
Bundled Chromium (modern, no extra install) |
| Text Output (HTML path) | Often rendered as images on legacy path | Real text (selectable and searchable) |
| Font Handling | Issues reported on legacy path | Reliable font handling |
| CSS3 Support | Limited on legacy path | Full |
| Flexbox/Grid | Not supported on legacy path | Supported |
| JavaScript | Limited on legacy path | Full ES6+ |
| PDF Accessibility | Poor on legacy path (image-based output) | Tagged-text output |
| API Design | Many overloads, manual page settings | Single fluent renderer |
| Licensing | Freemium/Commercial | Commercial |
On modern .NET, IronPDF addresses Spire.PDF's HTML-to-PDF trade-offs by rendering text as actual selectable text rather than images, so the resulting PDFs are searchable and accessible.
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 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
License Configuration
// 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"
Complete API Reference
Namespace Changes
// 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
Core API Mappings
| Spire.PDF | IronPDF |
|---|---|
new PdfDocument() |
new ChromePdfRenderer() |
pdf.LoadFromHTML() |
renderer.RenderHtmlAsPdf() |
pdf.LoadFromFile() |
PdfDocument.FromFile() |
pdf.SaveToFile() |
pdf.SaveAs() |
pdf.Close() |
Not needed |
pdf.Pages.Add() |
renderer.RenderHtmlAsPdf() |
pdf.InsertPageRange() |
PdfDocument.Merge() |
page.Canvas.DrawString() |
TextStamper + ApplyStamp() |
PdfFont |
CSS styling in HTML |
PdfBrush |
CSS styling in HTML |
Code Migration Examples
Example 1: HTML to PDF Conversion
Before (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
This example demonstrates the fundamental difference in HTML rendering. Spire.PDF uses LoadFromHTML() with a PdfHtmlLayoutFormat object, which often renders text as bitmap images. The result is PDFs where users cannot select, copy, or search text.
IronPDF uses a ChromePdfRenderer with RenderHtmlAsPdf(), producing true text that is fully selectable, searchable, and accessible. No Close() call is required—IronPDF uses the dispose pattern for automatic cleanup. See the HTML to PDF documentation for comprehensive examples.
Example 2: Merging Multiple PDFs
Before (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 requires manually loading each document with new PdfDocument() + LoadFromFile(), then using InsertPageRange() to specify which pages to insert, and finally calling Close() on each document.
IronPDF uses the simpler PdfDocument.FromFile() pattern and a static PdfDocument.Merge() method that accepts multiple documents. No Close() calls needed. Learn more in our tutorials.
Example 3: Adding Text to a PDF
Before (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 uses a canvas-based drawing model with PdfFont, PdfBrush, and page.Canvas.DrawString() to position text at specific coordinates using PointF.
IronPDF uses a TextStamper object with intuitive properties like Text, FontSize, VerticalOffset, and HorizontalOffset, then applies it with ApplyStamp(). This approach is more declarative and easier to maintain.
The Text-as-Images Problem
Why This Is Critical
When Spire.PDF converts HTML to PDF using image-based rendering, your documents lose essential functionality:
1. No Text Search: Users cannot use Ctrl+F to find text. Document management systems cannot index content.
2. No Text Selection/Copy: Users trying to copy a quote, reference, or data cannot select text—it's an image.
3. Accessibility Violations: Image-based PDFs fail WCAG 2.1 compliance, Section 508 compliance (US Government), ADA requirements, and screen reader compatibility.
4. Large File Sizes: Image-based output from the legacy HTML path can produce files an order of magnitude larger than the text-based output IronPDF produces for the same content.
Detection: Is Your PDF Image-Based?
Open your Spire.PDF-generated document and try these tests:
- Text Selection: Click and drag over text. If nothing highlights → IMAGE-BASED
- Ctrl+F Search: Search for any word on the page. If "No matches found" → IMAGE-BASED
- Copy/Paste: Select and copy text to notepad. If nothing pastes → IMAGE-BASED
The Legacy HTML Engine Problem
Spire.PDF's Rendering Engines
Spire.PDF's LoadFromHTML(...) historically offered two paths: a non-plugin path backed by the Windows Internet Explorer / Edge Legacy WebBrowser control, and a separately-downloaded QT/WebKit plugin path. Spire.PDF 10.7.21 (mid-2024) added a third option, ChromeHtmlConverter, which shells out to a locally installed Google Chrome — but it is opt-in and the original LoadFromHTML API still defaults to the legacy engines. IE/Edge Legacy was deprecated by Microsoft in 2022, modern CSS is unreliable on the legacy path, JavaScript support is partial, and rendering can vary across systems.
Modern CSS That Fails in Spire.PDF
<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 uses modern Chromium rendering, so all these CSS features work correctly.
New Capabilities After Migration
After migrating to IronPDF, you gain capabilities that Spire.PDF cannot provide:
Selectable, Searchable Text
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
Modern CSS Support
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-Based Watermarks
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>")
Migration Checklist
Pre-Migration
- Inventory all Spire.PDF usages in codebase
- Test existing PDFs for text selectability (critical issue detection)
- Document
LoadFromHTML()calls (these are the priority to fix) - Obtain IronPDF license key from ironpdf.com
Code Updates
- Remove
Spire.PDFNuGet package (andFreeSpire.PDFif using free version) - Install
IronPdfNuGet package - Update namespace imports (
using Spire.Pdf;→using IronPdf;) - Replace
LoadFromHTML()withRenderHtmlAsPdf()(CRITICAL FIX) - Replace
new PdfDocument()+LoadFromFile()withPdfDocument.FromFile() - Replace
InsertPageRange()withPdfDocument.Merge() - Replace
Canvas.DrawString()withTextStamper+ApplyStamp() - Replace
SaveToFile()withSaveAs() - Remove all
Close()calls (not needed in IronPDF) - Add license initialization at application startup
Testing
- Verify text is selectable in generated PDFs (CRITICAL TEST)
- Verify CSS rendering improvements (Flexbox/Grid now work)
- Verify file sizes are smaller
- Test accessibility with screen readers
- Performance comparison

