How to Convert HTML to PDF with iTextSharp in .NET Core
Use iText pdfHTML and HtmlConverter to turn HTML and CSS into PDFs in .NET Core, with assets, licensing, troubleshooting and production guidance.
Use iText 7 with the pdfHTML add-on. The current package is itext.pdfhtml, and the conversion entry point is HtmlConverter.ConvertToPdf. The old iTextSharp HTMLWorker examples are for small snippets and were removed from recent versions. pdfHTML is the supported HTML/CSS path for iText 7 and later.
This guide targets .NET Core/.NET (the APIs are C#). It covers local files, strings and streams, static assets, fonts, ASP.NET Core, licensing, limits and production troubleshooting.
1. Install compatible packages
Add pdfHTML with the same version family as iText Core. Check the vendor compatibility guidance before choosing a version.
dotnet add package itext.pdfhtml --version <desired-version>
The package brings the required iText Core assemblies. Pin the version in your project file so builds are reproducible. See the official installation guidance and the pdfHTML repository.
2. Convert an HTML file to PDF
A base URI tells pdfHTML where relative CSS, images and fonts live. Without it, markup such as <img src="images/logo.png"> can produce a PDF with missing assets.
using System.IO;
using iText.Html2pdf;
using iText.Html2pdf.Converter;
var htmlPath = "input/invoice.html";
var pdfPath = "output/invoice.pdf";
Directory.CreateDirectory(Path.GetDirectoryName(pdfPath)!);
var properties = new ConverterProperties()
.SetBaseUri(Path.GetDirectoryName(Path.GetFullPath(htmlPath))!);
using var html = File.OpenRead(htmlPath);
using var pdf = File.Create(pdfPath);
HtmlConverter.ConvertToPdf(html, pdf, properties);
The exact namespace or overload can vary by package version; use the overload that accepts your input and output streams and keep ConverterProperties configured.
3. Convert a string or stream
String input
using iText.Html2pdf;
using iText.Html2pdf.Converter;
var html = "<html><body><h1>Invoice</h1><p>Paid</p></body></html>";
var properties = new ConverterProperties()
.SetBaseUri(Path.GetFullPath("wwwroot"));
HtmlConverter.ConvertToPdf(html, "invoice.pdf", properties);
ASP.NET Core endpoint
using iText.Html2pdf;
using iText.Html2pdf.Converter;
using Microsoft.AspNetCore.Mvc;
[ApiController]
[Route("pdf")]
public sealed class PdfController : ControllerBase
{
[HttpPost]
public IActionResult Create([FromBody] string html)
{
var properties = new ConverterProperties()
.SetBaseUri(Path.Combine(AppContext.BaseDirectory, "wwwroot"));
using var output = new MemoryStream();
HtmlConverter.ConvertToPdf(html, output, properties);
return File(output.ToArray(), "application/pdf", "document.pdf");
}
}
For untrusted HTML, isolate the conversion process and restrict which files or URLs the application can expose through its base directory. Do not let user markup point at private server paths.
4. Make CSS, images and fonts resolve
- Relative URLs: Set
SetBaseUrito the directory containing the HTML or to a controlled web root. - Absolute URLs: Use reachable URLs only when your deployment allows network access; package remote dependencies locally for deterministic builds.
- Images: Verify file casing on Linux, read permissions and supported formats. A browser rendering successfully does not guarantee pdfHTML will accept every format.
- Fonts: Copy font files into the deployment and reference them from CSS with a resolvable URL. Register or configure fonts according to the iText version when CSS fallback is insufficient.
- CSS: Keep print rules explicit. Test selectors, page breaks and generated content in representative documents because pdfHTML is not a browser engine.
<link rel="stylesheet" href="css/print.css">
<style>
@font-face {
font-family: InvoiceSans;
src: url("fonts/InvoiceSans-Regular.ttf");
}
@page { size: A4; margin: 18mm; }
body { font-family: InvoiceSans, sans-serif; }
</style>
5. Configure document properties
Use CSS for page size, margins, page breaks and most layout rules. Keep your HTML semantic and print-focused.
<style>
@page { size: Letter landscape; margin: 12mm 15mm; }
.page-break { break-before: page; }
table { width: 100%; border-collapse: collapse; }
td, th { border: 0.2mm solid #999; padding: 2mm; }
</style>
For advanced metadata, encryption or PDF conformance, create the appropriate iText document and writer properties for your installed version, then pass them through the matching HtmlConverter overload. Consult the API reference for that version rather than copying an overload from an older release.
6. Migrating from HTMLWorker
HTMLWorker was intended for small, simple snippets and did not support the full HTML tag and CSS surface. XML Worker and iText 5 examples are not a modern full-page conversion strategy. Replace them with:
- Install
itext.pdfhtml. - Move the HTML and CSS into normal files or strings.
- Set a base URI for relative resources.
- Call
HtmlConverter.ConvertToPdf. - Compare representative output, especially tables, fonts and page breaks.
7. Licensing for closed-source applications
pdfHTML is dual licensed. The official installation page says non-commercial use requires accepting the AGPL, while commercial use requires purchased commercial licenses for iText Core and pdfHTML. Decide this before shipping a proprietary application.
For iText 7.2 and newer, the licensing guide documents JSON license files and the licensing-base library. iText 7.1.x and older use XML license files and the older license-key library. Load the license before other iText API calls when using a proprietary license. Read the licensing documentation and retain the license files outside source control.
8. Performance and reliability
- Measure with your real templates, images and page counts; the vendor does not publish a universal throughput or memory benchmark.
- Reuse static CSS and font files and avoid unnecessarily large images.
- Convert outside the request thread for large documents, with a queue and a bounded worker count.
- Write to a stream or temporary file and dispose every stream promptly.
- Set application timeouts and capture conversion logs. A malformed resource should fail the job clearly rather than silently producing an incomplete document.
- Keep package versions aligned and test after every upgrade, especially when CSS or fonts are complex.
9. Troubleshooting
| Symptom | Likely cause | Fix |
|---|---|---|
| Images or CSS missing | No base URI, wrong working directory or case mismatch | Use an absolute SetBaseUri path, verify deployment files and check Linux filename casing. |
| Fonts fall back | Font file is not deployed or URL cannot resolve | Ship the font, reference it from CSS with a resolvable path and validate the font family name. |
| Layout differs from Chrome | pdfHTML is not a browser engine; unsupported or browser-specific CSS | Simplify CSS, use print rules and test the exact template. Avoid JavaScript-dependent layout. |
FileNotFoundException |
Relative path resolved from the process directory | Build paths with Path.GetFullPath and set the base URI explicitly. |
| Blank or partial output | Conversion exception, unreadable resource or disposed stream | Log the exception, validate every asset and keep input/output streams alive until conversion returns. |
| API method or namespace not found | Examples target a different iText version | Check the installed package API and compatibility matrix; update the code to that version’s overload. |
| License exception | Missing, invalid or late-loaded license | Install the correct licensing library and load the license before iText calls. |
10. When a browser-based renderer is a better fit
Choose a real browser when the page depends on JavaScript execution, browser-only CSS or third-party widgets. Choose pdfHTML when a deterministic, server-side HTML/CSS subset and iText’s PDF features fit your templates. Compare feature coverage, asset and font resolution, accessibility requirements, deployment footprint, measured performance and licensing before committing.
Or skip the browser setup
ScreenshotNeo provides a single GET request for a clean screenshot or PDF. It accepts consent banners and removes more than 60 known consent platforms, newsletter popups and chat widgets before capture. Bot checks, blank pages, failed loads and cache hits cost nothing, and response headers identify the page verdict and billing state. An MCP server lets Claude, Cursor and other MCP clients call take_screenshot, get_page_info and capture_pdf.
See the ScreenshotNeo API docs for PDF options such as paper size, margins, landscape mode and page ranges.
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}`);
Every feature is on every plan: 1,000 screenshots per month are free with no card; paid plans start at $5 for 3,000. Create a free ScreenshotNeo account.
FAQ
Is iTextSharp still the package name?
Modern .NET projects use iText Core packages, with itext.pdfhtml for HTML/CSS conversion.
Do I need a base URI for inline-only HTML?
No relative assets means it may not be needed, but setting a controlled base URI makes templates safer when assets are added later.
Can pdfHTML execute JavaScript?
Do not depend on browser JavaScript behavior; pdfHTML is not a browser engine.
What should I benchmark?
Measure conversion time, memory, output size and failure rate using your largest real templates, images and fonts.


