Developers searching for a wkhtmltopdf alternative in 2024 and beyond face an unavoidable reality: the project is dead. The GitHub repository was archived in January 2023, the underlying Qt WebKit engine stopped receiving updates in 2012, and critical security vulnerabilities like CVE-2022-35583 will never be patched. For .NET developers using DinkToPdf, Rotativa, TuesPechkin, or NReco.PdfGenerator, migration is not optional—it is a technical necessity.
This guide provides a complete migration path from wkhtmltopdf and its .NET wrappers to IronPDF. It covers removing native dependencies, mapping command-line options to IronPDF settings, converting headers and footers, and simplifying Docker deployments.
Why Migrate from wkhtmltopdf
The decision to replace wkhtmltopdf is driven by five categories of problems that cannot be fixed because the project is no longer maintained.
Security Vulnerabilities
CVE-2022-35583 documents a Server-Side Request Forgery vulnerability with a CVSS score of 9.8 (Critical). Attackers can inject iframe tags that cause wkhtmltopdf to make requests to internal network resources, potentially exposing cloud metadata endpoints, internal APIs, and sensitive data. The wkhtmltopdf maintainers disputed whether this was a library-level vulnerability, but the practical effect is that any application processing untrusted HTML through wkhtmltopdf is at risk.
Additional vulnerabilities exist:
- CVE-2020-21365: Directory traversal allowing local file disclosure
- Undisclosed local file inclusion vectors documented by security researchers
Since the project is archived, these vulnerabilities will never receive patches.
Deprecated Rendering Engine
wkhtmltopdf uses Qt WebKit, a rendering engine that:
- Stopped receiving updates in 2012
- Was deprecated by Qt in 2015
- Was removed from Qt in 2016
The engine predates CSS Flexbox (standardized 2012, widespread 2015+), CSS Grid (standardized 2017), CSS Custom Properties, and ES6 JavaScript. Modern web frameworks like Bootstrap 5, Tailwind CSS, React, Vue, and Angular produce HTML that wkhtmltopdf cannot render correctly.
No ARM64 Support
wkhtmltopdf does not support ARM64 architecture. This affects:
- Apple Silicon Macs (M1, M2, M3, M4 processors)
- AWS Graviton instances
- Azure ARM-based VMs
- Raspberry Pi deployments
- ARM-based Kubernetes nodes
Developers on Apple Silicon currently run wkhtmltopdf through Rosetta 2 emulation, which impacts performance and adds complexity. There will never be a native ARM64 build.
Native Dependency Complexity
wkhtmltopdf requires platform-specific native libraries:
- Windows:
libwkhtmltox.dlland Visual C++ 2013 Redistributable - Linux:
libwkhtmltox.soplus X11 libraries, fontconfig, and often xvfb - macOS:
libwkhtmltox.dylibwith specific framework dependencies
The .NET wrappers (DinkToPdf, TuesPechkin, Rotativa) add another layer of complexity with P/Invoke configuration, architecture detection (32-bit vs 64-bit), and platform-specific deployment logic.
Archived Project Status
The wkhtmltopdf GitHub repository was archived on January 2, 2023. The wkhtmltopdf organization itself was marked as archived on July 10, 2024. This means:
- No bug fixes
- No security patches
- No new features
- No response to issues
- No pull request reviews
The dependent .NET wrappers face similar abandonment:
- DinkToPdf: Last meaningful commit in 2018
- TuesPechkin: Last update in 2015
- Rotativa: Not updated for modern .NET Core/5/6/7/8
Step 1: Remove wkhtmltopdf Dependencies
The first step in migration is removing the wkhtmltopdf-related packages and files from your project.
Remove NuGet Packages
<!-- Remove these packages from your .csproj -->
<PackageReference Include="DinkToPdf" Version="*" Remove="DinkToPdf" />
<PackageReference Include="TuesPechkin" Version="*" Remove="TuesPechkin" />
<PackageReference Include="TuesPechkin.Wkhtmltox.Win32" Version="*" Remove="TuesPechkin.Wkhtmltox.Win32" />
<PackageReference Include="TuesPechkin.Wkhtmltox.Win64" Version="*" Remove="TuesPechkin.Wkhtmltox.Win64" />
<PackageReference Include="Rotativa.AspNetCore" Version="*" Remove="Rotativa.AspNetCore" />
<PackageReference Include="NReco.PdfGenerator" Version="*" Remove="NReco.PdfGenerator" />
Or via command line:
dotnet remove package DinkToPdf
dotnet remove package TuesPechkin
dotnet remove package Rotativa.AspNetCore
dotnet remove package NReco.PdfGenerator
Add IronPDF
dotnet add package IronPdf
For ASP.NET Core MVC projects that need Razor view rendering:
dotnet add package IronPdf.Extensions.Mvc.Core
Remove Native Library Files
Delete any wkhtmltopdf binaries from your project:
# Files to remove
libwkhtmltox.dll
libwkhtmltox.so
libwkhtmltox.dylib
wkhtmltopdf.exe
wkhtmltopdf (Linux binary)
Remove project file entries that copy these libraries:
<!-- Remove these entries from .csproj -->
<ItemGroup>
<None Update="libwkhtmltox.dll" Remove="libwkhtmltox.dll">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
<None Update="libwkhtmltox.so" Remove="libwkhtmltox.so">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
</ItemGroup>
Remove Rotativa Configuration
If using Rotativa, remove the setup code from Program.cs or Startup.cs:
// Remove this line
// RotativaConfiguration.Setup(env.WebRootPath, "Rotativa");
Step 2: API Mapping - Command Line Options to IronPDF
wkhtmltopdf is configured through command-line arguments. The .NET wrappers expose these as properties on settings objects. The following table maps common wkhtmltopdf options to their IronPDF equivalents.
Page Size and Orientation
| wkhtmltopdf Option | DinkToPdf Property | IronPDF Equivalent |
|---|---|---|
--page-size A4 |
GlobalSettings.PaperSize = PaperKind.A4 |
RenderingOptions.PaperSize = PdfPaperSize.A4 |
--page-size Letter |
GlobalSettings.PaperSize = PaperKind.Letter |
RenderingOptions.PaperSize = PdfPaperSize.Letter |
--orientation Landscape |
GlobalSettings.Orientation = Orientation.Landscape |
RenderingOptions.PaperOrientation = PdfPaperOrientation.Landscape |
--page-width 210mm |
GlobalSettings.PageWidth = "210mm" |
RenderingOptions.SetCustomPaperSizeinMilimeters(210, 297) |
--page-height 297mm |
GlobalSettings.PageHeight = "297mm" |
RenderingOptions.SetCustomPaperSizeinMilimeters(210, 297) |
Margins
| wkhtmltopdf Option | DinkToPdf Property | IronPDF Equivalent |
|---|---|---|
--margin-top 10mm |
GlobalSettings.Margins.Top = 10 |
RenderingOptions.MarginTop = 10 |
--margin-bottom 10mm |
GlobalSettings.Margins.Bottom = 10 |
RenderingOptions.MarginBottom = 10 |
--margin-left 10mm |
GlobalSettings.Margins.Left = 10 |
RenderingOptions.MarginLeft = 10 |
--margin-right 10mm |
GlobalSettings.Margins.Right = 10 |
RenderingOptions.MarginRight = 10 |
JavaScript and Rendering
| wkhtmltopdf Option | DinkToPdf Property | IronPDF Equivalent |
|---|---|---|
--javascript-delay 500 |
ObjectSettings.LoadSettings.JSDelay = 500 |
RenderingOptions.WaitFor.RenderDelay(500) |
--enable-javascript |
Default enabled | RenderingOptions.EnableJavaScript = true |
--disable-javascript |
ObjectSettings.LoadSettings.DisableJavaScript = true |
RenderingOptions.EnableJavaScript = false |
--no-background |
ObjectSettings.WebSettings.Background = false |
RenderingOptions.PrintHtmlBackgrounds = false |
--background |
ObjectSettings.WebSettings.Background = true |
RenderingOptions.PrintHtmlBackgrounds = true |
--print-media-type |
ObjectSettings.WebSettings.PrintMediaType = true |
RenderingOptions.CssMediaType = PdfCssMediaType.Print |
--no-stop-slow-scripts |
N/A | RenderingOptions.Timeout = 120 |
Output Control
| wkhtmltopdf Option | DinkToPdf Property | IronPDF Equivalent |
|---|---|---|
--grayscale |
GlobalSettings.ColorMode = ColorMode.Grayscale |
RenderingOptions.GrayScale = true |
--title "Doc Title" |
GlobalSettings.DocumentTitle = "Doc Title" |
RenderingOptions.Title = "Doc Title" |
--dpi 300 |
GlobalSettings.DPI = 300 |
N/A - Chromium handles DPI automatically |
--zoom 1.5 |
GlobalSettings.Zoom = 1.5 |
RenderingOptions.Zoom = 150 (percentage) |
Code Example: Full Migration
Before (DinkToPdf):
using DinkToPdf;
using DinkToPdf.Contracts;
public class PdfService
{
private readonly IConverter _converter;
public PdfService(IConverter converter)
{
_converter = converter;
}
public byte[] GeneratePdf(string htmlContent)
{
var doc = new HtmlToPdfDocument()
{
GlobalSettings = {
ColorMode = ColorMode.Color,
Orientation = Orientation.Portrait,
PaperSize = PaperKind.A4,
Margins = new MarginSettings {
Top = 10,
Bottom = 10,
Left = 10,
Right = 10
},
DocumentTitle = "Generated Report"
},
Objects = {
new ObjectSettings {
PagesCount = true,
HtmlContent = htmlContent,
WebSettings = {
DefaultEncoding = "utf-8",
PrintMediaType = true,
Background = true
},
LoadSettings = {
JSDelay = 500
}
}
}
};
return _converter.Convert(doc);
}
}
After (IronPDF):
using IronPdf;
public class PdfService
{
public byte[] GeneratePdf(string htmlContent)
{
// No converter injection needed - IronPDF is stateless
var renderer = new ChromePdfRenderer();
// Page configuration
renderer.RenderingOptions.PaperSize = IronPdf.Rendering.PdfPaperSize.A4;
renderer.RenderingOptions.PaperOrientation = IronPdf.Rendering.PdfPaperOrientation.Portrait;
// Margins in millimeters
renderer.RenderingOptions.MarginTop = 10;
renderer.RenderingOptions.MarginBottom = 10;
renderer.RenderingOptions.MarginLeft = 10;
renderer.RenderingOptions.MarginRight = 10;
// Document metadata
renderer.RenderingOptions.Title = "Generated Report";
// Rendering options
renderer.RenderingOptions.CssMediaType = IronPdf.Rendering.PdfCssMediaType.Print;
renderer.RenderingOptions.PrintHtmlBackgrounds = true;
renderer.RenderingOptions.EnableJavaScript = true;
// JavaScript delay - wait for dynamic content
renderer.RenderingOptions.WaitFor.RenderDelay(500);
// Render and return
using var pdf = renderer.RenderHtmlAsPdf(htmlContent);
return pdf.BinaryData;
}
}
Step 3: Headers and Footers Migration
wkhtmltopdf supports headers and footers through separate HTML files or inline HTML. IronPDF provides equivalent functionality with additional features like page numbers and date stamping.
wkhtmltopdf Header/Footer Approach
wkhtmltopdf headers and footers are configured with command-line options:
wkhtmltopdf --header-html header.html --footer-html footer.html input.html output.pdf
Or with inline text:
wkhtmltopdf --header-center "Page [page] of [toPage]" --footer-left "[date]" input.html output.pdf
DinkToPdf Header/Footer Code
// DinkToPdf approach
var doc = new HtmlToPdfDocument()
{
Objects = {
new ObjectSettings {
HtmlContent = mainContent,
HeaderSettings = {
FontSize = 9,
Right = "Page [page] of [toPage]",
Line = true,
Spacing = 2.812
},
FooterSettings = {
FontSize = 9,
Left = "[date]",
Center = "Confidential",
Right = "[page]"
}
}
}
};
IronPDF Header/Footer Migration
IronPDF headers and footers support full HTML with CSS styling:
using IronPdf;
public class HeaderFooterMigration
{
public byte[] GenerateWithHeadersFooters(string mainContent)
{
var renderer = new ChromePdfRenderer();
renderer.RenderingOptions.PaperSize = IronPdf.Rendering.PdfPaperSize.A4;
// HTML Header with styling
renderer.RenderingOptions.HtmlHeader = new HtmlHeaderFooter()
{
HtmlFragment = @"
<div style='width: 100%; font-family: Arial, sans-serif; font-size: 10px;'>
<div style='float: left;'>
<img src='https://example.com/logo.png' style='height: 30px;' />
</div>
<div style='float: right; text-align: right;'>
Page {page} of {total-pages}
</div>
<div style='clear: both; border-bottom: 1px solid #ccc;'></div>
</div>",
DrawDividerLine = false,
MaxHeight = 25 // millimeters
};
// HTML Footer with date and page number
renderer.RenderingOptions.HtmlFooter = new HtmlHeaderFooter()
{
HtmlFragment = @"
<div style='width: 100%; font-family: Arial, sans-serif; font-size: 9px; color: #666;'>
<div style='border-top: 1px solid #ccc; padding-top: 5px;'>
<span style='float: left;'>{date} {time}</span>
<span style='float: right;'>Page {page} of {total-pages}</span>
</div>
</div>",
DrawDividerLine = false,
MaxHeight = 20
};
using var pdf = renderer.RenderHtmlAsPdf(mainContent);
return pdf.BinaryData;
}
}
Header/Footer Placeholder Mapping
| wkhtmltopdf Placeholder | IronPDF Placeholder |
|---|---|
[page] |
{page} |
[toPage] |
{total-pages} |
[date] |
{date} |
[time] |
{time} |
[title] |
{html-title} |
[webpage] |
{url} |
Simple Text Headers/Footers
For simple text-based headers and footers without HTML complexity:
using IronPdf;
public class SimpleHeaderFooter
{
public byte[] GenerateWithTextHeaders(string mainContent)
{
var renderer = new ChromePdfRenderer();
// Simple text header
renderer.RenderingOptions.TextHeader = new TextHeaderFooter()
{
CenterText = "Company Report",
RightText = "{page} of {total-pages}",
FontFamily = "Arial",
FontSize = 10,
DrawDividerLine = true
};
// Simple text footer
renderer.RenderingOptions.TextFooter = new TextHeaderFooter()
{
LeftText = "{date}",
CenterText = "Confidential",
RightText = "Page {page}",
FontFamily = "Arial",
FontSize = 9,
DrawDividerLine = true
};
using var pdf = renderer.RenderHtmlAsPdf(mainContent);
return pdf.BinaryData;
}
}
Step 4: CSS and JavaScript Now Work Correctly
One of the primary reasons to migrate is that modern CSS and JavaScript fail silently in wkhtmltopdf. After migration, these features work as expected.
CSS Features That Now Work
using IronPdf;
public class ModernCssExample
{
public void RenderModernCss()
{
var renderer = new ChromePdfRenderer();
string html = @"
<!DOCTYPE html>
<html>
<head>
<style>
/* CSS Flexbox - ignored by wkhtmltopdf, works in IronPDF */
.flex-container {
display: flex;
justify-content: space-between;
align-items: center;
gap: 20px;
}
/* CSS Grid - ignored by wkhtmltopdf, works in IronPDF */
.grid-layout {
display: grid;
grid-template-columns: repeat(3, 1fr);
grid-gap: 15px;
}
/* CSS Variables - ignored by wkhtmltopdf, works in IronPDF */
:root {
--primary-color: #2563eb;
--spacing: 1rem;
}
.themed-element {
color: var(--primary-color);
padding: var(--spacing);
}
/* CSS calc() - buggy in wkhtmltopdf, works in IronPDF */
.calculated-width {
width: calc(100% - 60px);
margin: 0 30px;
}
/* CSS Filters - ignored by wkhtmltopdf, works in IronPDF */
.filtered-image {
filter: drop-shadow(2px 2px 4px rgba(0,0,0,0.3));
}
/* Modern gradients work correctly */
.gradient-background {
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
}
</style>
</head>
<body>
<div class='flex-container'>
<div>Flex Item 1</div>
<div>Flex Item 2</div>
<div>Flex Item 3</div>
</div>
<div class='grid-layout'>
<div>Grid Cell 1</div>
<div>Grid Cell 2</div>
<div>Grid Cell 3</div>
</div>
<div class='themed-element'>This uses CSS variables</div>
</body>
</html>";
using var pdf = renderer.RenderHtmlAsPdf(html);
pdf.SaveAs("modern-css.pdf");
}
}
JavaScript Frameworks Now Render
React, Vue, Angular, and other JavaScript frameworks that generate DOM content at runtime now render correctly:
using IronPdf;
public class JavaScriptRenderingExample
{
public void RenderDynamicContent()
{
var renderer = new ChromePdfRenderer();
// Enable JavaScript execution
renderer.RenderingOptions.EnableJavaScript = true;
// Wait for JavaScript to complete rendering
renderer.RenderingOptions.WaitFor.JavaScript(2000);
string html = @"
<!DOCTYPE html>
<html>
<head>
<script src='https://unpkg.com/vue@3/dist/vue.global.js'></script>
</head>
<body>
<div id='app'>
<h1>{{ message }}</h1>
<ul>
<li v-for='item in items' :key='item.id'>{{ item.name }}</li>
</ul>
</div>
<script>
const { createApp, ref } = Vue;
createApp({
setup() {
const message = ref('Dynamic Content from Vue');
const items = ref([
{ id: 1, name: 'Item One' },
{ id: 2, name: 'Item Two' },
{ id: 3, name: 'Item Three' }
]);
return { message, items };
}
}).mount('#app');
</script>
</body>
</html>";
using var pdf = renderer.RenderHtmlAsPdf(html);
pdf.SaveAs("vue-rendered.pdf");
}
}
Web Fonts Load Correctly
wkhtmltopdf has inconsistent web font support. IronPDF loads Google Fonts and other web fonts reliably:
using IronPdf;
public class WebFontExample
{
public void RenderWithWebFonts()
{
var renderer = new ChromePdfRenderer();
renderer.RenderingOptions.EnableJavaScript = true;
string html = @"
<!DOCTYPE html>
<html>
<head>
<link href='https://fonts.googleapis.com/css2?family=Roboto:wght@400;700&family=Playfair+Display&display=swap' rel='stylesheet'>
<style>
body { font-family: 'Roboto', sans-serif; }
h1 { font-family: 'Playfair Display', serif; }
.bold { font-weight: 700; }
</style>
</head>
<body>
<h1>Elegant Heading in Playfair Display</h1>
<p>Body text in Roboto font.</p>
<p class='bold'>Bold text renders correctly.</p>
</body>
</html>";
using var pdf = renderer.RenderHtmlAsPdf(html);
pdf.SaveAs("web-fonts.pdf");
}
}
Step 5: Docker Simplification
wkhtmltopdf in Docker requires xvfb (X Virtual Framebuffer), X11 libraries, font packages, and often sandbox workarounds. IronPDF eliminates these requirements.
Before: wkhtmltopdf Dockerfile
FROM mcr.microsoft.com/dotnet/aspnet:8.0 AS base
# Install wkhtmltopdf and ALL its dependencies
RUN apt-get update && apt-get install -y --no-install-recommends \
# wkhtmltopdf binary
wget \
# X11 dependencies
xvfb \
libx11-6 \
libxext6 \
libxrender1 \
libxcb1 \
# Font dependencies
fontconfig \
libfreetype6 \
fonts-dejavu-core \
fonts-liberation \
xfonts-75dpi \
xfonts-base \
# Image libraries
libjpeg62-turbo \
libpng16-16 \
# Other dependencies
zlib1g \
libgdiplus \
&& rm -rf /var/lib/apt/lists/*
# Download and install wkhtmltopdf
RUN wget https://github.com/wkhtmltopdf/packaging/releases/download/0.12.6.1-3/wkhtmltox_0.12.6.1-3.bookworm_amd64.deb \
&& dpkg -i wkhtmltox_0.12.6.1-3.bookworm_amd64.deb || apt-get install -f -y \
&& rm wkhtmltox_0.12.6.1-3.bookworm_amd64.deb
# Copy native library for DinkToPdf
COPY libwkhtmltox.so /app/
# Create wrapper script with xvfb
RUN echo '#!/bin/bash\nxvfb-run -a --server-args="-screen 0 1024x768x24" wkhtmltopdf "$@"' > /usr/local/bin/wkhtmltopdf-wrapper \
&& chmod +x /usr/local/bin/wkhtmltopdf-wrapper
WORKDIR /app
COPY --from=build /app/publish .
ENTRYPOINT ["dotnet", "MyApp.dll"]
After: IronPDF Dockerfile
FROM mcr.microsoft.com/dotnet/aspnet:8.0-bookworm-slim AS base
# IronPDF minimal dependencies - NO xvfb, NO X11, NO fonts packages
RUN apt-get update && apt-get install -y --no-install-recommends \
libc6 \
libgcc-s1 \
libstdc++6 \
&& rm -rf /var/lib/apt/lists/*
WORKDIR /app
COPY --from=build /app/publish .
ENTRYPOINT ["dotnet", "MyApp.dll"]
Comparison
| Requirement | wkhtmltopdf | IronPDF |
|---|---|---|
| xvfb | Required | Not needed |
| X11 libraries | Required | Not needed |
| fontconfig | Required | Not needed |
| Font packages | Required | Not needed |
| Wrapper scripts | Required | Not needed |
| Native library files | Required | Not needed |
| Added image size | ~150MB+ | ~30MB |
| Build complexity | High | Low |
ARM64/Apple Silicon Docker Support
IronPDF supports ARM64 architecture natively, enabling deployment on Apple Silicon development machines and ARM-based cloud instances:
# Works on both AMD64 and ARM64
FROM --platform=$TARGETPLATFORM mcr.microsoft.com/dotnet/aspnet:8.0-bookworm-slim
# Same minimal dependencies work on ARM64
RUN apt-get update && apt-get install -y --no-install-recommends \
libc6 \
libgcc-s1 \
libstdc++6 \
&& rm -rf /var/lib/apt/lists/*
WORKDIR /app
COPY . .
ENTRYPOINT ["dotnet", "MyApp.dll"]
Step 6: Rotativa Migration Pattern
Rotativa provided convenient ViewAsPdf and ActionAsPdf action results for ASP.NET MVC. Migrating to IronPDF requires rendering Razor views to HTML strings first.
Before: Rotativa
using Rotativa.AspNetCore;
using Microsoft.AspNetCore.Mvc;
public class InvoiceController : Controller
{
public IActionResult PrintInvoice(int id)
{
var model = GetInvoice(id);
return new ViewAsPdf("Invoice", model)
{
FileName = $"Invoice_{id}.pdf",
PageSize = Rotativa.Options.Size.A4,
PageMargins = new Rotativa.Options.Margins(10, 10, 10, 10)
};
}
}
After: IronPDF with Razor View Rendering
using IronPdf;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.Rendering;
using Microsoft.AspNetCore.Mvc.ViewEngines;
using Microsoft.AspNetCore.Mvc.ViewFeatures;
public class InvoiceController : Controller
{
private readonly ICompositeViewEngine _viewEngine;
private readonly ITempDataProvider _tempDataProvider;
public InvoiceController(
ICompositeViewEngine viewEngine,
ITempDataProvider tempDataProvider)
{
_viewEngine = viewEngine;
_tempDataProvider = tempDataProvider;
}
public async Task<IActionResult> PrintInvoice(int id)
{
var model = GetInvoice(id);
// Render Razor view to HTML string
var htmlContent = await RenderViewToStringAsync("Invoice", model);
// Convert to PDF
var renderer = new ChromePdfRenderer();
renderer.RenderingOptions.PaperSize = IronPdf.Rendering.PdfPaperSize.A4;
renderer.RenderingOptions.MarginTop = 10;
renderer.RenderingOptions.MarginBottom = 10;
renderer.RenderingOptions.MarginLeft = 10;
renderer.RenderingOptions.MarginRight = 10;
using var pdf = renderer.RenderHtmlAsPdf(htmlContent, Request.Scheme + "://" + Request.Host);
return File(pdf.BinaryData, "application/pdf", $"Invoice_{id}.pdf");
}
private async Task<string> RenderViewToStringAsync(string viewName, object model)
{
ViewData.Model = model;
using var writer = new StringWriter();
var viewResult = _viewEngine.FindView(ControllerContext, viewName, false);
if (!viewResult.Success)
{
throw new InvalidOperationException($"View '{viewName}' not found.");
}
var viewContext = new ViewContext(
ControllerContext,
viewResult.View,
ViewData,
TempData,
writer,
new HtmlHelperOptions()
);
await viewResult.View.RenderAsync(viewContext);
return writer.GetStringBuilder().ToString();
}
}
ActionAsPdf Equivalent
For scenarios where you need to capture content from a URL (similar to Rotativa's ActionAsPdf):
using IronPdf;
using Microsoft.AspNetCore.Mvc;
public class ReportController : Controller
{
public IActionResult PrintReport(int id)
{
var renderer = new ChromePdfRenderer();
// Enable JavaScript and wait for dynamic content
renderer.RenderingOptions.EnableJavaScript = true;
renderer.RenderingOptions.WaitFor.JavaScript(2000);
// Render from URL - captures the full page
var url = Url.Action("ViewReport", "Report", new { id }, Request.Scheme);
using var pdf = renderer.RenderUrlAsPdf(url);
return File(pdf.BinaryData, "application/pdf", $"Report_{id}.pdf");
}
public IActionResult ViewReport(int id)
{
var model = GetReport(id);
return View(model);
}
}
Step 7: TuesPechkin Migration
TuesPechkin is the oldest .NET wrapper and does not work in .NET Core. Migration is mandatory for any modernization effort.
Before: TuesPechkin
using TuesPechkin;
public class PdfGenerator
{
private readonly IConverter _converter;
public PdfGenerator()
{
var deployment = new Win64EmbeddedDeployment(
new TempFolderDeployment()
);
_converter = new ThreadSafeConverter(
new RemotingToolset<PdfToolset>(deployment)
);
}
public byte[] GeneratePdf(string html)
{
var document = new HtmlToPdfDocument
{
GlobalSettings = {
ProduceOutline = true,
DocumentTitle = "Report",
PaperSize = PaperKind.A4,
Margins = {
Top = 1.0,
Bottom = 1.0,
Left = 1.0,
Right = 1.0,
Unit = Unit.Centimeters
}
},
Objects = {
new ObjectSettings {
HtmlText = html
}
}
};
return _converter.Convert(document);
}
}
After: IronPDF
using IronPdf;
public class PdfGenerator
{
// No constructor setup required - IronPDF is stateless
public byte[] GeneratePdf(string html)
{
var renderer = new ChromePdfRenderer();
// Document settings
renderer.RenderingOptions.Title = "Report";
renderer.RenderingOptions.PaperSize = IronPdf.Rendering.PdfPaperSize.A4;
// Margins in millimeters (TuesPechkin used centimeters)
renderer.RenderingOptions.MarginTop = 10; // 1cm = 10mm
renderer.RenderingOptions.MarginBottom = 10;
renderer.RenderingOptions.MarginLeft = 10;
renderer.RenderingOptions.MarginRight = 10;
// Create table of contents/outline if needed
renderer.RenderingOptions.CreatePdfFormsFromHtml = false;
using var pdf = renderer.RenderHtmlAsPdf(html);
return pdf.BinaryData;
}
}
Migration Considerations
Licensing
IronPDF is commercial software with per-developer licensing. A free trial is available for evaluation. For teams accustomed to wkhtmltopdf's open-source model, this represents a licensing cost. However, the total cost of ownership should factor in:
- Engineering time saved not debugging native library issues
- Security risk of running unpatched software
- Maintenance burden of legacy workarounds
- Development velocity with modern CSS/JavaScript support
Rendering Differences
IronPDF uses Chromium instead of Qt WebKit. While this is an improvement for modern web standards, some documents may render slightly differently:
- Font metrics may vary slightly
- Page break calculations may differ
- Legacy CSS hacks for WebKit may be unnecessary (and should be removed)
Test your output thoroughly during migration, particularly for documents with precise layout requirements like invoices and legal forms.
Package Size
IronPDF includes an embedded Chromium engine, resulting in a larger NuGet package (~200MB) compared to wkhtmltopdf wrappers. This affects:
- Initial package download time
- Build artifact size
- Container image size (though this is often offset by removing X11 dependencies)
For serverless environments with strict package size limits, check deployment constraints.
Memory Usage
Chromium-based rendering uses more memory than Qt WebKit for simple documents. For high-volume processing:
// Dispose PDFs explicitly to release memory
using (var pdf = renderer.RenderHtmlAsPdf(html))
{
var bytes = pdf.BinaryData;
// Process bytes
}
// PDF resources released here
// Or use explicit garbage collection for high-volume scenarios
if (documentsProcessed % 100 == 0)
{
GC.Collect();
GC.WaitForPendingFinalizers();
}
API Reference
For complete documentation on the methods and classes used in this migration guide:
- ChromePdfRenderer - Primary rendering class
- ChromePdfRenderOptions - All rendering configuration options
- HtmlHeaderFooter - HTML header/footer configuration
- Docker and Linux Deployment - Container deployment guide
- ASP.NET Core Integration - Web application patterns
Conclusion
Migrating from wkhtmltopdf to IronPDF addresses fundamental problems that cannot be resolved by staying with the deprecated toolchain. The archived project status, unpatched security vulnerabilities, outdated rendering engine, and missing ARM64 support make migration necessary rather than optional.
The migration process involves removing native dependencies, mapping configuration options, updating header/footer implementations, and simplifying Docker deployments. The result is a maintainable codebase with modern CSS/JavaScript support, active security updates, and cross-platform compatibility including ARM64.
Jacob Mellor has been developing commercial software for over 25 years and is CTO at Iron Software, where he originally built IronPDF.
References
- wkhtmltopdf GitHub Repository (Archived){:rel="nofollow"} - Official repository showing archived status
- CVE-2022-35583 - SSRF Vulnerability{:rel="nofollow"} - Critical security vulnerability details
- DinkToPdf GitHub Repository{:rel="nofollow"} - .NET Core wrapper (last update 2018)
- Rotativa GitHub Repository{:rel="nofollow"} - ASP.NET MVC wrapper
- TuesPechkin GitHub Repository{:rel="nofollow"} - .NET Framework wrapper
- Qt WebKit Deprecation{:rel="nofollow"} - Qt documentation on WebKit deprecation
- wkhtmltopdf Known Issues{:rel="nofollow"} - Official status page
- CVE-2020-21365 - Directory Traversal{:rel="nofollow"} - File disclosure vulnerability
For comprehensive IronPDF documentation, tutorials, and code examples, visit ironpdf.com.
Top comments (0)