Best Free C# Libraries for Converting HTML to PDF
Compare free C# HTML-to-PDF libraries, with runnable examples, licensing guidance, browser trade-offs, troubleshooting, and a ScreenshotNeo alternative.
Short answer: there is no universally best free C# HTML-to-PDF library. Choose Playwright for .NET or PuppeteerSharp when your HTML needs a real browser (JavaScript, modern CSS, web fonts, or client-side data). Choose iText pdfHTML when you need document-oriented features such as PDF/A, tagged accessibility, forms, or right-to-left text and can meet its AGPL or commercial licensing terms.
Validate the choice against representative documents. “Free” includes license obligations, browser installation and operations, deployment dependencies, and the rendering behavior your application actually needs.
Decision table
| Library | Rendering model | Best fit | Main trade-off | License |
|---|---|---|---|---|
| Playwright for .NET | Automates Chromium, Firefox, or WebKit | Browser-faithful pages and teams already using Playwright | Browser downloads, process lifecycle, and container/runtime setup | MIT |
| PuppeteerSharp | Automates Chromium through a .NET API | Chromium PDF generation with a Puppeteer-style API | Browser installation, Linux dependencies, and process management | MIT |
| iText pdfHTML | Parses HTML/CSS into the iText PDF toolchain | PDF/A, tagged PDFs, forms, custom fonts, and RTL scripts | Not a browser; verify CSS/JavaScript behavior and license obligations | AGPL or commercial |
Other names such as HtmlRenderer with PDFsharp, wkhtmltopdf, DinkToPdf, and QuestPDF can be worth investigating. The research for this guide did not independently verify their current releases, support, or licensing. QuestPDF is a code-first layout model rather than a drop-in HTML renderer, so it only fits if you can change the input model.
How to choose
1. Identify what produces the final HTML
- Static, controlled markup: a parser-based tool may be sufficient.
- JavaScript-rendered content: use a browser automation library and wait for the application’s data and fonts.
- Strict PDF requirements: test PDF/A conformance, tagging, forms, links, text extraction, and page breaks explicitly.
2. Check the runtime before writing integration code
Browser libraries add a browser binary, OS packages, temporary profiles, process cleanup, and concurrency decisions. Test the exact Linux container, Windows service, or macOS host used in production. PuppeteerSharp’s documentation specifically calls out Linux troubleshooting and an X-server prerequisite; confirm those details for the package version and launch mode you select.
3. Treat the license as an engineering requirement
Playwright for .NET and PuppeteerSharp publish under MIT. iText pdfHTML is dual licensed under AGPL or commercial terms; AGPL is copyleft. Have legal counsel review whether your distributed application or hosted service can comply, or obtain the appropriate commercial license, before committing to it.
Playwright for .NET: browser-faithful HTML
Playwright for .NET is Microsoft’s official .NET port for automating Chromium, Firefox, and WebKit through one API. It is a strong candidate when the page depends on modern browser behavior or your project already uses Playwright. Confirm the current PDF API and browser-install requirements in the current documentation; browser versions and support matrices change.
Install and generate a PDF
dotnet add package Microsoft.Playwright
# Install the browser binaries once on the machine or image:
dotnet build
pwsh bin/Debug/net8.0/playwright.ps1 install chromium
using Microsoft.Playwright;
using var playwright = await Playwright.CreateAsync();
await using var browser = await playwright.Chromium.LaunchAsync(new BrowserTypeLaunchOptions
{
Headless = true
});
var page = await browser.NewPageAsync(new BrowserNewPageOptions
{
ViewportSize = new ViewportSize { Width = 1280, Height = 900 },
DeviceScaleFactor = 1
});
await page.GotoAsync("https://example.com/invoice/123", new PageGotoOptions
{
WaitUntil = WaitUntilState.NetworkIdle,
Timeout = 60_000
});
await page.EvaluateAsync("document.fonts.ready");
await page.PdfAsync(new PagePdfOptions
{
Path = "invoice.pdf",
Format = "A4",
PrintBackground = true,
PreferCSSPageSize = true,
Margin = new Margin { Top = "16mm", Right = "14mm", Bottom = "16mm", Left = "14mm" }
});
await browser.CloseAsync();
Use a selector wait instead of a blanket network-idle wait when the page opens long-lived connections. For authenticated pages, create a context with storage state or set headers/cookies before navigation. Keep one browser process alive and create isolated contexts/pages per job; close pages and contexts in a finally block so failed jobs do not leak processes.
PuppeteerSharp: Chromium through a Puppeteer-style API
PuppeteerSharp is a .NET port of Puppeteer. Its README demonstrates downloading a browser, launching headless Chromium, navigating, waiting for document.fonts.ready, and calling PdfAsync. That font wait matters when the output contains web fonts.
dotnet add package PuppeteerSharp
using PuppeteerSharp;
await new BrowserFetcher().DownloadAsync();
await using var browser = await Puppeteer.LaunchAsync(new LaunchOptions
{
Headless = true
});
await using var page = await browser.NewPageAsync();
await page.GoToAsync("https://example.com/report", WaitUntilNavigation.Networkidle0);
await page.EvaluateExpressionAsync("document.fonts.ready");
await page.PdfAsync("report.pdf", new PdfOptions
{
Format = PaperFormat.A4,
PrintBackground = true,
PreferCSSPageSize = true,
MarginOptions = new MarginOptions
{
Top = "16mm", Right = "14mm", Bottom = "16mm", Left = "14mm"
}
});
In Linux containers, install the system libraries required by the browser image and follow the project’s current troubleshooting guidance. If your environment has no X server, use the supported headless launch configuration; do not assume a desktop package is available.
iText pdfHTML: document features and licensing
iText pdfHTML is an add-on that converts HTML and CSS into searchable, accessible PDFs in the iText toolchain. Its examples cover PDF/A-3B, tagged output, custom fonts, forms, and Arabic/Hebrew content. It is offered under AGPL or a commercial license. Read the license and current documentation before shipping.
dotnet add package itext7
dotnet add package itext7.pdfhtml
using iText.Html2pdf;
using iText.Kernel.Pdf;
using var writer = new PdfWriter("document.pdf");
using var pdf = new PdfDocument(writer);
var properties = new ConverterProperties();
HtmlConverter.ConvertToPdf(File.ReadAllText("document.html"), pdf, properties);
The exact package names and APIs can change with iText releases. Pin a tested version, configure resource/font resolvers for your deployment, and add automated checks for the PDF features you require. A parser-based converter should not be assumed to execute browser JavaScript or support every modern CSS feature; verify each requirement with fixtures.
Build a representative test corpus
- Include long tables, deliberate page breaks, images, SVG, web fonts, RTL text, links, forms, and print-specific CSS.
- Include a page whose content arrives after JavaScript fetches data.
- Run the same inputs on the production OS/container and inspect visual output, text extraction, page count, and file size.
- Record failures by feature, then choose the smallest runtime and license that satisfy all required cases.
Options that affect correctness
- Page size and margins: choose CSS
@pagerules or explicit PDF options and verify precedence. - Backgrounds and colors: enable print backgrounds when brand colors or shaded rows are required.
- Fonts: wait for
document.fonts.readyin a browser; embed or map fonts explicitly in parser-based workflows. - Assets: make image and stylesheet URLs reachable from the renderer, or inline/package them.
- Readiness: wait for a known selector or application signal; network idle can never occur on pages with analytics or sockets.
- Security: restrict outbound URLs, sanitize untrusted HTML, and isolate browser processes when converting user-controlled input.
Troubleshooting
| Symptom | Likely cause | Fix |
|---|---|---|
| Blank or partial PDF | Capture ran before client-side rendering finished | Wait for a specific loaded selector, application-ready flag, or a bounded delay; then capture. |
| Missing glyphs or fallback fonts | Font files are unreachable or not loaded | Allow the font origins, package/embed fonts, and await document.fonts.ready. |
| Images or CSS missing | Relative URLs, blocked network, or authentication | Use an absolute base URL, configure headers/cookies, and log failed requests. |
| Chromium fails to launch on Linux | Missing shared libraries, sandbox restrictions, or an unsupported display setup | Use the project’s current dependency list, a compatible browser image, and the supported headless mode. |
| Page hangs at network idle | Analytics, WebSockets, or long polling keep connections open | Replace network-idle with a selector or application readiness signal and retain a timeout. |
| Unexpected page breaks | CSS page rules, oversized elements, or different fonts | Test @page, break-inside, margins, and embedded fonts on the production renderer. |
| License review blocks release | AGPL obligations were discovered late | Stop and review the intended distribution/service model; choose MIT tooling or obtain a commercial iText license if appropriate. |
Performance, reliability, and cost
Browser startup is expensive compared with reusing a warm browser. Keep a bounded pool, isolate jobs in contexts, cap concurrency, and recycle processes periodically. Set navigation and conversion timeouts, cancel stuck jobs, and collect renderer logs. Cache immutable assets and avoid loading third-party trackers. Parser-based conversion can reduce browser operations, but only if its feature support matches your documents.
Measure cold start, warm conversion, peak memory, output size, and failure rate on your own corpus. The dossier contains no independent benchmark, so a feature matrix cannot establish which library is fastest or most reliable for your workload.
Or skip the browser setup
ScreenshotNeo is a website screenshot API that can return PNG, JPEG, WebP, or PDF from one GET request. It accepts cookie and consent banners before capture, removes 60+ known consent platforms plus newsletter popups and chat widgets, and reports the result through X-Page-Verdict and X-Billed headers. Bot checks, blank pages, timeouts, failed loads, and cache hits are not billed. Its MCP server exposes take_screenshot, get_page_info, and capture_pdf for Claude, Cursor, and other MCP clients.
For the complete option list and PDF parameters, see the ScreenshotNeo API docs.
curl -G "https://api.screenshotneo.com/v1/shot" -d access_key=YOUR_API_KEY --data-urlencode url=https://stripe.com -o shot.webp
import requests
r = requests.get("https://api.screenshotneo.com/v1/shot", params={"access_key": "YOUR_API_KEY", "url": "https://stripe.com"}, timeout=90)
open("shot.webp", "wb").write(r.content)
const q = new URLSearchParams({ access_key: 'YOUR_API_KEY', url: 'https://stripe.com' });
const res = await fetch(`https://api.screenshotneo.com/v1/shot?${q}`);
It includes full-page capture, element selectors, custom CSS and JavaScript, waits, blocking rules, headers, cookies, user agents, geolocation, caching, signed links, async webhooks, bulk capture, and a usage API. Every plan includes every feature. The Free plan includes 1,000 shots per month without a card; paid plans start at $5 for 3,000 shots. Create a free ScreenshotNeo account.
FAQ
Which library should I use for a React or Vue page?
Start with Playwright for .NET or PuppeteerSharp, because the page needs a browser to execute JavaScript. Add an explicit readiness signal so the PDF is generated after data and fonts are available.
Is iText pdfHTML free for a SaaS product?
It is available under AGPL or a commercial license. Whether AGPL fits depends on your distribution and service model; obtain legal advice or a commercial license when required.
Can I switch libraries later?
Keep HTML fixtures, PDF assertions, and a small renderer interface in your application. That lets you compare a browser renderer and a parser without rewriting business code.
How do I know a PDF is accessible?
Test tagged structure, reading order, language metadata, keyboard navigation, and text extraction with the accessibility checks required by your organization. Do not infer accessibility from visual similarity.
