HTML to PDF in C#: .NET Libraries, Examples, and Tradeoffs
To convert HTML to PDF in C#, use PuppeteerSharp or Playwright for modern CSS and JavaScript. Keep DinkToPdf only for existing simple templates, or use a managed API when you do not want to operate Chromium.
After testing some of the major open source libraries, I’ve put together this guide to hopefully save you some time. By the end, you should know which library fits your needs best and how to get it working.
tl;dr: PuppeteerSharp or Playwright for modern sites
If you need browser-faithful rendering of modern websites with complex CSS and JavaScript, PuppeteerSharp is a strong default. Playwright for .NET provides similar Chromium rendering with a maintained browser-install workflow; see the Playwright HTML-to-PDF guide for a complete example.
Use DinkToPdf only when you already have simple templates built around wkhtmltopdf and can accept its old CSS engine and native-library deployment. For ASP.NET Core and Razor views, the dedicated .NET Core HTML-to-PDF guide covers the application integration.
PuppeteerSharp
PuppeteerSharp is the .NET port of the popular Puppeteer library. It runs a headless Chrome or Firefox browser under the hood, and it can render anything that a browser can. Modern CSS (think flexbox and CSS grid), JavaScript frameworks etc. are supported and will all work as expected. The PuppeteerSharp HTML-to-PDF guide goes deeper into headers, footers, and deployment.
Here’s how to use it for HTML to PDF conversion. First, install it with the .NET CLI:
dotnet add package PuppeteerSharp
When you first run it, PuppeteerSharp will download a compatible version of Chromium automatically. If not, you can download Chromium with:
var browserFetcher = new BrowserFetcher();
await browserFetcher.DownloadAsync();
Here’s how to generate a PDF from a URL:
using PuppeteerSharp;
public async Task GeneratePdfFromUrl()
{
await new BrowserFetcher().DownloadAsync();
using var browser = await Puppeteer.LaunchAsync(new LaunchOptions
{
Headless = true
});
using var page = await browser.NewPageAsync();
await page.GoToAsync("https://transformy.io/guides/");
await page.PdfAsync("transformy_guides.pdf", new PdfOptions
{
Format = PaperFormat.A4,
PrintBackground = true
});
}
You can also generate PDFs from HTML strings:
public async Task GeneratePdfFromHtml()
{
await new BrowserFetcher().DownloadAsync();
using var browser = await Puppeteer.LaunchAsync(new LaunchOptions { Headless = true });
using var page = await browser.NewPageAsync();
var html = @"
<html>
<head>
<style>
body { font-family: Arial, sans-serif; }
.header { color: #333; border-bottom: 2px solid #ddd; }
</style>
</head>
<body>
<h1 class='header'>Invoice #2025-001</h1>
<p>Total: $1,299.99</p>
</body>
</html>";
await page.SetContentAsync(html);
await page.PdfAsync("invoice.pdf", new PdfOptions
{
Format = PaperFormat.A4,
MarginOptions = new MarginOptions
{
Top = "1in",
Bottom = "1in",
Left = "0.5in",
Right = "0.5in"
}
});
}
Where PuppeteerSharp really shines is dealing with JavaScript-heavy content. Let’s say you want to create your PDF only once .charts-container has done loading:
// Wait for dynamic content to load
await page.WaitForSelectorAsync(".charts-container");
await page.PdfAsync("dashboard-report.pdf");
Puppeteer is powerful, but it comes at a cost. You’re running a browser for every conversion, which means higher memory use and slower generation times. A lot of parallel conversions are also more complicated to scale.
DinkToPdf
DinkToPdf is a .NET wrapper around wkhtmltopdf. Both wkhtmltopdf and its QtWebKit engine are no longer maintained, so DinkToPdf is a legacy compatibility choice rather than a default for a new application.
It can still fit an existing system with trusted, basic HTML and CSS. Don’t expect it to render newer CSS features like flexbox or CSS grid.
Before you can use DinkToPdf, you need the native wkhtmltopdf library for the operating system and architecture where the application runs. The wkhtmltopdf installation and migration guide covers the underlying binary in more detail.
dotnet add package DinkToPdf --version 1.0.8
DinkToPdf.Native NuGet and native DLL errors
NuGet cannot find DinkToPdf.Native, DinkToPdf.Native.Windows, or DinkToPdf.Native.Linux because those are not current package IDs in the official feed. Install DinkToPdf, then supply a compatible libwkhtmltox native library for your deployment target.
If you get DllNotFoundException: Unable to load DLL 'libwkhtmltox', check that:
- The native library matches the process architecture (
x64orx86). libwkhtmltox.dll,libwkhtmltox.so, orlibwkhtmltox.dylibis copied to the published output or another loadable path.- Linux has the native dependencies required by that wkhtmltopdf build.
- Your container or cloud runtime uses the same operating system and architecture you built for.
You’ll also need to configure dependency injection in your startup:
// In Startup.cs or Program.cs
services.AddSingleton(typeof(IConverter), new SynchronizedConverter(new PdfTools()));
Here’s how to generate a PDF from HTML:
using DinkToPdf;
using DinkToPdf.Contracts;
public class PdfService
{
private readonly IConverter _converter;
public PdfService(IConverter converter)
{
_converter = converter;
}
public byte[] GeneratePdfFromHtml(string html)
{
var doc = new HtmlToPdfDocument()
{
GlobalSettings = {
ColorMode = ColorMode.Color,
Orientation = Orientation.Portrait,
PaperSize = PaperKind.A4,
},
Objects = {
new ObjectSettings() {
PagesCount = true,
HtmlContent = html,
WebSettings = { DefaultEncoding = "utf-8" }
}
}
};
return _converter.Convert(doc);
}
}
You can also convert from URLs:
public byte[] GeneratePdfFromUrl(string url)
{
var doc = new HtmlToPdfDocument()
{
GlobalSettings = {
ColorMode = ColorMode.Color,
Orientation = Orientation.Portrait,
PaperSize = PaperKind.A4,
Margins = new MarginSettings() { Top = 10 }
},
Objects = {
new ObjectSettings() {
Page = url,
LoadSettings = new LoadSettings()
{
BlockLocalFileAccess = false
}
}
}
};
return _converter.Convert(doc);
}
DinkToPdf can be fast for simple templates, but it uses an old rendering engine. You might need to simplify your HTML and CSS, and you must ship the correct native library in every cloud or container environment.
Weasyprint.Wrapped
Weasyprint.Wrapped brings Python’s excellent WeasyPrint library to the .NET world. It’s particularly good if you need precise control over CSS styling and don’t mind working with a Python dependency.
Installing requires both the NuGet package and Python with WeasyPrint:
Install-Package Weasyprint.Wrapped
You’ll also need Python and WeasyPrint installed on your system:
pip install weasyprint
Here’s how to use it:
using Weasyprint.Wrapped;
public class WeasyPrintService
{
public void GeneratePdfFromHtml(string html, string outputPath)
{
var weasyprint = new WeasyPrintWrapper();
weasyprint.GeneratePdf(
html: html,
outputPath: outputPath,
options: new WeasyPrintOptions
{
PaperSize = "A4",
Margin = "1cm"
}
);
}
public void GeneratePdfFromUrl(string url, string outputPath)
{
var weasyprint = new WeasyPrintWrapper();
weasyprint.GeneratePdfFromUrl(url, outputPath);
}
}
You can also work with custom CSS:
var html = @"
<html>
<body>
<h1>Custom Styled Document</h1>
<p>This will have custom styling applied.</p>
</body>
</html>";
var css = @"
@page { size: A4; margin: 2cm; }
body { font-family: Georgia, serif; }
h1 { color: #2c3e50; }";
weasyprint.GeneratePdf(html, "styled-document.pdf", css: css);
Weasyprint.Wrapped produces high-quality PDFs with excellent CSS support, but it’s slower than other options and requires managing a Python dependency. It’s great for documents where visual fidelity is crucial.
iText 7 pdfHTML
iText 7 pdfHTML is part of the commercial iText suite, though it offers a free AGPL version. The free version requires your application to be open source under AGPL. If you’re building proprietary software, you’ll need a commercial license which can be expensive.
Here’s how to install the NuGet package:
Install-Package itext7.pdfhtml
Converting HTML to PDF is straightforward:
using iText.Html2pdf;
using iText.Kernel.Pdf;
using iText.Layout;
public class ITextService
{
public void GeneratePdfFromHtml(string html, string outputPath)
{
using var writer = new PdfWriter(outputPath);
using var pdf = new PdfDocument(writer);
HtmlConverter.ConvertToPdf(html, pdf);
}
public void GeneratePdfFromUrl(string url, string outputPath)
{
using var writer = new PdfWriter(outputPath);
using var pdf = new PdfDocument(writer);
HtmlConverter.ConvertToPdf(new Uri(url), pdf);
}
}
You can also customize the conversion with properties:
public void GenerateCustomPdf(string html, string outputPath)
{
var properties = new ConverterProperties();
properties.SetBaseUri("https://transformy.io/"); // For resolving relative URLs
using var writer = new PdfWriter(outputPath);
using var pdf = new PdfDocument(writer);
HtmlConverter.ConvertToPdf(html, pdf, properties);
}
iText produces good PDFs and has excellent documentation, but the licensing situation can be a dealbreaker. Make sure you understand the AGPL requirements or budget for a commercial license. For other .NET-native approaches, compare the dedicated iTextSharp, Aspose.HTML, and PDFSharp guides.
Conclusion
Here’s my take on each library after using them in real projects:
| Option | CSS and JavaScript fidelity | Licensing | Deployment and maintenance | Production scale |
|---|---|---|---|---|
| PuppeteerSharp | High; browser-faithful | MIT | You install and operate Chromium | Requires browser pooling and resource controls |
| Playwright for .NET | High; browser-faithful | Apache-2.0 | You install and operate Chromium | Requires browser pooling and resource controls |
| DinkToPdf | Low for modern CSS; limited JavaScript | LGPL, plus wkhtmltopdf | You ship legacy native libraries | Fast for simple templates, but carries maintenance risk |
| Weasyprint.Wrapped | Strong print CSS; no JavaScript | BSD | You also operate Python and WeasyPrint | Better for controlled documents than dynamic pages |
| iText 7 pdfHTML | Document-oriented; limited browser behavior | AGPL or commercial | .NET library with licensing review | Suitable when its HTML/CSS subset fits |
| Managed Chrome API | High; browser-faithful | Commercial service | No browser infrastructure to operate | Designed for concurrent production rendering |
For a new self-hosted implementation, start with PuppeteerSharp or Playwright. Keep DinkToPdf only when an existing simple template already depends on it. If browser deployment and scaling are the problem you want to avoid, use a managed HTML-to-PDF API.
The licensing considerations for iText are important. If you need a permissive open-source license, PuppeteerSharp’s MIT license and Playwright’s Apache-2.0 license give you more freedom.