How to Change Fonts When Converting HTML to PDF With iTextSharp
Register the right font provider, map CSS families, support multilingual glyphs, and troubleshoot missing fonts in iTextSharp XML Worker.

Direct answer: In iTextSharp 5 XML Worker, set the desired font-family in your HTML or CSS, register the matching TTF or TTC file with an XMLWorkerFontProvider, and pass that provider to the XML Worker conversion call. The registered font must contain every glyph your document uses. A CSS family name alone does not make iTextSharp find an arbitrary font file.
This guide covers iTextSharp 5 with XML Worker, including regular, bold and italic faces, UTF-8 text, right-to-left scripts, deployment paths, performance and common failures. XML Worker is intended for controlled XHTML and CSS; it is not a full browser engine. iText documents the same family-selection and registration pattern in its Arabic conversion example (official example).
1. The minimum working pattern
Use a provider with explicit registration, declare the same family in CSS, and supply the provider to ParseXHtml:

using System.IO;
using System.Text;
using iTextSharp.text;
using iTextSharp.text.pdf;
using iTextSharp.tool.xml;
using iTextSharp.tool.xml.pipeline.css;
var html = @"<html><head>
<style>body { font-family: My Font; font-size: 12pt; }</style>
</head><body><h1>Invoice</h1><p>Text rendered with a registered font.</p></body></html>";
using (var output = File.Create("invoice.pdf"))
using (var htmlStream = new MemoryStream(Encoding.UTF8.GetBytes(html)))
{
using (var document = new Document())
{
var writer = PdfWriter.GetInstance(document, output);
document.Open();
var fontProvider = new XMLWorkerFontProvider(
XMLWorkerFontProvider.DONTLOOKFORFONTS);
fontProvider.Register("resources/fonts/MyFont-Regular.ttf");
fontProvider.Register("resources/fonts/MyFont-Bold.ttf");
XMLWorkerHelper.GetInstance().ParseXHtml(
writer,
document,
htmlStream,
null,
Encoding.UTF8,
fontProvider);
document.Close();
}
}
The CSS family and the font’s internal family metadata must resolve to the same typeface. A file called MyFont-Regular.ttf does not guarantee that its internal family is exactly My Font; inspect the font metadata if the declaration is ignored. The XML Worker API exposes registration and substitution methods through XMLWorkerFontProvider (API reference).
2. Add the font to your project
- Obtain a font file you are licensed to embed. Use TTF or TTC files supported by your installed iTextSharp/XML Worker version.
- Place the files in a predictable project directory such as
resources/fonts. - Copy them to the deployment output. A path that works from Visual Studio may fail in a service, container or published application.
- Register every face you actually use: regular, bold, italic and bold italic when those styles occur in the HTML.
For a package-based iTextSharp 5 setup, the documented HTML-to-PDF example requires itextsharp.dll and itextsharp.xmlworker.dll; those dependency names are specific to that example and version (dependency guidance).
3. Make CSS select the registered family
You can put the declaration inline, in a style block, or in a stylesheet supplied to XML Worker:
<style>
body { font-family: 'My Font'; }
h1 { font-family: 'My Font'; font-weight: bold; }
em { font-style: italic; }
</style>
<body>
<h1>Résumé 日本語 العربية</h1>
<p>The font must contain all characters used here.</p>
</body>
If the stylesheet is external, make sure it is passed through the CSS stream or resolver used by your pipeline. A correct CSS rule has no effect when the parser never receives that stylesheet.
4. Register weights and styles deliberately
XML Worker may substitute a face when only a regular file is registered. That can produce synthetic or unexpected bold and italic output. Register the actual files and, when necessary, define substitutions intentionally:
var fonts = new XMLWorkerFontProvider(
XMLWorkerFontProvider.DONTLOOKFORFONTS);
fonts.Register("resources/fonts/Cardo-Regular.ttf");
fonts.Register("resources/fonts/Cardo-Bold.ttf");
fonts.Register("resources/fonts/Cardo-Italic.ttf");
fonts.Register("resources/fonts/Cardo-BoldItalic.ttf");
iText’s XML Worker performance guidance recommends explicit registrations and discusses style-face substitution (official guidance). Registering a regular face does not add glyphs or styles that are absent from that file.
5. Use UTF-8 and verify glyph coverage
Encoding and font coverage are separate checks:

- Encoding: decode the HTML bytes using the charset that produced them. For UTF-8 HTML, pass
Encoding.UTF8and create the stream from UTF-8 bytes. - Coverage: choose a typeface containing the required Unicode characters. Registration cannot render a glyph the file does not contain.
- Direction: Arabic, Hebrew and other right-to-left scripts need direction and layout handling in addition to a suitable font. The Arabic example documents these separate concerns (iText Arabic guidance).
var htmlBytes = Encoding.UTF8.GetBytes(html);
using var htmlStream = new MemoryStream(htmlBytes);
XMLWorkerHelper.GetInstance().ParseXHtml(
writer, document, htmlStream, cssStream,
Encoding.UTF8, fontProvider);
6. A manual pipeline when you need more control
The convenience helper is sufficient for most templates. A manual pipeline lets you attach the provider through CssAppliersImpl, configure a CSS resolver, and choose a PDF writer or element-handler pipeline. The essential part is still the same provider:
var fontProvider = new XMLWorkerFontProvider(
XMLWorkerFontProvider.DONTLOOKFORFONTS);
fontProvider.Register("resources/fonts/MyFont-Regular.ttf");
var cssAppliers = new CssAppliersImpl(fontProvider);
var htmlContext = new HtmlPipelineContext(cssAppliers);
// Configure your CSS resolver, pipelines and worker for the
// XML Worker version installed in your project.
Exact constructors and namespaces vary between XML Worker releases, so compile against the assemblies installed by your application rather than copying an iText 7 pdfHTML example into an iTextSharp 5 project.
7. File paths that work after deployment
Resolve fonts from the application directory or an injected configuration value instead of assuming the process current directory:
var fontPath = Path.Combine(
AppContext.BaseDirectory,
"resources", "fonts", "MyFont-Regular.ttf");
if (!File.Exists(fontPath))
throw new FileNotFoundException("PDF font was not deployed", fontPath);
fontProvider.Register(fontPath);
In containers and serverless functions, include the font files in the published artifact and verify read permissions. Do not depend on fonts installed on a developer workstation or on a system font directory that differs between hosts.
8. XML Worker, HTMLWorker and iText 7 are different tools
| Tool | Use it for | Font configuration |
|---|---|---|
| HTMLWorker | Small, simple snippets; deprecated | Legacy APIs with limited HTML/CSS support |
| iTextSharp 5 XML Worker | Controlled XHTML/CSS templates | XMLWorkerFontProvider plus registered files |
| iText 7 pdfHTML | Later iText architecture | Different packages and APIs |
iText’s conversion overview explains these scope and API boundaries (conversion overview). Select examples that match the generation installed in your application.
9. Troubleshooting
CSS appears to be ignored
Cause: the rule is in a stylesheet that was not supplied, or the family name does not resolve to a registered face. Fix: test with an inline rule, pass the CSS stream/resolver, and confirm the font’s internal family name.
Text falls back to a different font
Cause: the file was never registered, the provider was not passed to ParseXHtml, or the requested glyph is missing. Fix: register the file explicitly, pass the same provider instance into conversion, and inspect glyph coverage.
Boxes or question marks replace characters
Cause: the typeface lacks those Unicode glyphs or the HTML bytes were decoded with the wrong charset. Fix: verify UTF-8 input and use a font covering the target script.
Bold or italic is inconsistent
Cause: only the regular face is available, so XML Worker substitutes or synthesizes styles. Fix: register the real bold, italic and bold-italic files and review substitution behavior.
It works locally but fails in production
Cause: a relative path, missing copied asset, or permissions issue. Fix: resolve from AppContext.BaseDirectory, check File.Exists, package the files, and log the resolved path.
Arabic or Hebrew layout is wrong
Cause: font selection was treated as the entire solution. Fix: use a font with the script’s glyphs and configure right-to-left direction and shaping as required by your XML Worker setup.
Parsing is slower than expected
Cause: broad font-directory discovery or repeated registration work. Fix: use DONTLOOKFORFONTS, register only required files, reuse immutable configuration where safe, and benchmark your own workload. iText describes lookup overhead but provides no universal speed multiplier.
10. Performance, reliability and cost considerations
- Register a small, explicit set of fonts instead of scanning every system directory.
- Keep font files available for the entire conversion and avoid mutating a shared provider while requests are running.
- Validate files during application startup so a missing deployment asset fails early.
- Use controlled XHTML and CSS; XML Worker is not a browser and will not reproduce every modern web layout.
- Check font embedding and redistribution rights before shipping a font. Licensing is independent of the iTextSharp API.
- Measure memory and conversion time with your document size, number of pages and font files. The cited iText material does not establish a fixed benchmark.
11. Or skip the browser setup
If your real requirement is capturing a rendered web page rather than converting a controlled HTML template, ScreenshotNeo provides a single screenshot or PDF request. Its cleanup step accepts cookie or consent banners and removes more than 60 known consent platforms, newsletter popups and chat widgets before capture. Bot checks, blank pages, failed loads, timeouts and cache hits are not billed, and response headers identify the page verdict and billing result.
See the ScreenshotNeo API documentation for all options. A direct call looks like this:
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}`);
ScreenshotNeo also has an MCP server so Claude, Cursor and other MCP clients can take screenshots, inspect pages and capture PDFs. The free plan includes 1,000 screenshots each month with no card; paid plans start at $5 for 3,000. Create a free ScreenshotNeo account.
12. FAQ
Can I change the font with only CSS?
Only when the requested family is already discoverable by your configured provider. Explicit registration is predictable and works across deployments.
Should I register a TTF or a TTC?
Use the format supported by your installed XML Worker version and register the file path explicitly. Test the exact file in the deployment environment.
Why does the file name differ from the CSS family?
Font files contain internal family and face metadata. CSS resolution uses that metadata, so the strings may differ.
Is XML Worker a replacement for a browser?
No. It is designed for controlled XHTML and CSS templates. Complex browser-only features may require a browser renderer or a different PDF tool.
Do I need a separate font for every language?
Not necessarily, but the chosen font must contain the glyphs for every script you render. Otherwise use a deliberate fallback strategy and verify the output.
13. Final checklist
- Declare the intended
font-familyin HTML or CSS. - Register the matching font files with
XMLWorkerFontProvider. - Pass that provider to the actual XML Worker conversion pipeline.
- Register every weight and style used.
- Decode HTML with the correct charset, normally UTF-8.
- Confirm glyph coverage and right-to-left requirements.
- Use deployment-safe paths and verify files exist.
- Confirm your code matches iTextSharp 5 XML Worker rather than HTMLWorker or iText 7.
- Review the font’s embedding license before distribution.


