Convert HTML to PDF in C#

The .NET ecosystem has some solid options for HTML to PDF conversion, but they all come with pros and cons that you’ll want to be aware of.
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 for modern sites, DinkToPdf for simplicity
If you need bulletproof rendering of modern websites with complex CSS and JavaScript, PuppeteerSharp is your best bet. It’s essentially Chrome automation, so it handles everything a browser can.
For pages with simpler HTML and CSS, DinkToPdf is the easiest and fastest solution. It’s a wrapper around wkhtmltopdf and works well for most basic use cases.
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.
Here’s how to use it for HTML to PDF conversion. First, instsall it with the .NET CLI:
dotnet add package PuppeteerSharp --version 20.2.2
When you first run it, PuppeteerSharp will download a compatible version of Chromium automatically. If not, you can download Chromium with:
await new BrowserFetcher().DownloadAsync(BrowserFetcher.DefaultChromiumRevision);
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 very powerfull, but it comes at a cost. Since you’re running a browser for every conversion. That comes with high memory usage and slower generation times. A lot of paralel conversions in particular are going to be more complicated to scale.
DinkToPdf
DinkToPdf is a .NET wrapper around wkhtmltopdf, which is the go-to tool in most programming languages for HTML to PDF conversion. Wkhtmltopdf is still very popular despite its age and despite it not being actively maintained anymore. And since it’s based on QtWebKit, which is no longer maintained either, don’t hold your breath for new updates.
Still, for basic HTML and CSS it’s a good solution that performs well. Don’t expect it to render any new CSS features like flexbox or CSS grid.
Before you can use DinkToPdf, you need to install the native wkhtmltopdf libraries. The easiest way is through the companion NuGet packages. Or look at your wkhtmltopdf guide for detailed installation instructions.
Install-Package DinkToPdf
# For Windows
Install-Package DinkToPdf.Native.Windows
# For Linux
Install-Package DinkToPdf.Native.Linux
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 is fast and reliable, but it’s oldschool. You might need to make changes to your HTML and CSS to simplify it and make it work with wkhtmltopdf. Installation also isn’t straightforward, you need to make sure you have it installed on your cloud 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 pgood quality 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.
Conclusion
Here’s my take on each library after using them in real projects:
| Library | Speed | Best For | Licensing | Learn More |
|---|---|---|---|---|
| PuppeteerSharp | Slow | Modern websites, JavaScript apps, complex CSS | MIT | PuppeteerSharp docs |
| DinkToPdf | Fast | Business reports, invoices, simple HTML | LGPL | DinkToPdf docs |
| Weasyprint.Wrapped | Medium | High-quality documents, custom CSS | BSD | WeasyPrint docs |
| iText 7 pdfHTML | Fast | Professional documents, commercial apps | AGPL/Commercial | iText docs |
For simple use cases, I’d recommend starting with DinkToPdf. It handles a lot of scenarios and is easy to use to (but a bit harder to get running). If you run into rendering issues with modern CSS or JavaScript, then consider PuppeteerSharp.
The licensing considerations for iText is an important one. If you need something with a tryly open license, PuppeteerSharp’s MIT license gives you the most freedom.