PeachPDF is a pure .NET HTML -> PDF rendering library. This library does not depend on Puppeteer, wkhtmltopdf, or any other process to render the HTML to PDF. As a result, this should work in virtually any environment where .NET 10+ works. As a side benefit of being pure .NET, performance improvements in future .NET versions immediately benefit this library.
Try it in your browser — the same library running on WebAssembly, rendering your own HTML, MHTML or ZIP with nothing uploaded anywhere.
- Native vector SVG rendering (inline
<svg>, standalone<img src="x.svg">/data:image/svg+xml, and as abackground-image/list-style-imagesource) — vector, except an SVG<filter>that uses a blur, lighting or other pixel primitive, which is rendered as a high-resolution bitmap - Native vector MathML rendering (inline
<math>, targeting the MathML Core layout algorithm) — fraction bars, radicals, and stretchy operators built from the font's own OpenTypeMATHtable, never rasterized - PDF 2.0 (ISO 32000-2) output by default (PDF 1.x via
PdfGenerateConfig.PdfVersion), with<math>formulas tagged as accessibleFormulastructure elements carrying their original MathML source as a PDF 2.0 Associated File - CSS custom properties (
--foo) andvar(), including fallbacks and inheritance - CSS math functions:
calc(),min(),max(),clamp() - 2D and 3D CSS transforms (
translate,scale,rotate,skew,matrix, and their variants) - Flexbox layout (CSS Flexbox Level 1) and CSS Multi-column Layout
- All five CSS-wide keywords:
inherit,initial,unset,revert,revert-layer - Gradients (
linear-gradient,radial-gradient,conic-gradient, and repeating variants) with CSS Color Level 4 interpolation - CSS Paged Media:
@pagerules, named pages, margin boxes, and running headers/footers viastring-set/string()andposition: running()/element() - Automatic PDF metadata extraction from HTML
<title>and<meta>elements - Optional Tagged PDF output — logical structure tree, automatic document language, CSS-driven tag mapping via
-peachpdf-pdf-tag-type(seePdfGenerateConfig.EnableTaggedPdf) — and PDF/UA-1 and PDF/UA-2 conformance (seePdfGenerateConfig.PdfUAConformance) - Optional PDF/A (ISO 19005) conformance — parts 1 to 4 (A-1: levels A and B; A-2 and A-3: levels A, B and U; A-4: the base level plus A-4e and A-4f; see
PdfGenerateConfig.PdfAConformance) — including file attachments for PDF/A-3 (seePdfGenerateConfig.Attachments) and ZUGFeRD / Factur-X hybrid e-invoices (seePdfGenerateConfig.FacturX) - Optional PDF/X (print-production) conformance — X-1a, X-3, X-4 and X-6 (see
PdfGenerateConfig.PdfXConformance) - Optional AES-256 encryption — user and owner passwords and permissions (print, copy, read-only, and more) — using only .NET's own cryptography (see
PdfGenerateConfig.Encryption) - Automatic PDF outline (bookmark sidebar) generation from headings, with full CSS control via
bookmark-level/bookmark-label/bookmark-state(CSS Generated Content Module Level 3) — no configuration required - Web fonts (
@font-face), custom fonts loaded from a stream, and system font discovery — with per-character font matching (@font-faceunicode-rangeand coverage-based fallback across thefont-familystack), supplementary-plane text viacmapformat-12, and searchable/selectableCOLR/CPALcolor emoji rendered as native PDF vectors - A declarative, code-first document-building API (
PdfGenerator.CreateDocument) — pages, containers, text, images, rows/columns, tables, lists, repeating headers/footers with page numbers, a document-level stylesheet, and HTML fragments with slots, built directly in C# with no HTML/CSS strings required
See HTML & CSS Support for the full compatibility matrix, Supported SVG Features for the full SVG compatibility matrix, and Supported MathML Features for the full MathML compatibility matrix.
- .NET 10 or newer (every currently-supported .NET version, per Microsoft's .NET support policy). Which PeachPDF releases receive fixes, and on which .NET versions, is set out in the support policy.
Note: This package embeds a PDF writing engine derived from PDFsharp directly in its source tree; that engine carries its own license (see the License page), but the end result is still open source
Portions of this software are copyright © 1996-2026 The FreeType Project (https://freetype.org). All rights reserved. This software is based in part on the work of the FreeType Team (see License).
Install the PeachPDF package from nuget.org
dotnet add package PeachPDF
Simple example to render PDF to a Stream. All images and assets must be local to the file on the file system or in data: URIs
using PeachPDF;
using PeachPDF.Network; // the network loaders below
PdfGenerateConfig pdfConfig = new(){
PageSize = PageSize.Letter,
PageOrientation = PageOrientation.Portrait
};
PdfGenerator generator = new();
var stream = new MemoryStream();
var document = await generator.GeneratePdf(html, pdfConfig);
document.Save(stream);PdfGenerateConfig controls page geometry, the PDF flavor, metadata and security. A few common settings:
PdfGenerateConfig pdfConfig = new()
{
PageSize = PageSize.A4,
MarginTop = 40, MarginBottom = 40, MarginLeft = 30, MarginRight = 30, // points
Metadata = new PdfDocumentMetadata { Title = "Q3 report", Author = "Finance" },
EnableTaggedPdf = true, // logical structure tree (accessibility)
PdfUAConformance = PdfUAConformance.UA1, // PDF/UA-1
PdfAConformance = PdfAConformance.PdfA2A // PDF/A-2a, the level that pairs with PDF/UA-1
};Title, author, subject and keywords also come from the HTML <title> and <meta> elements when Metadata is not set. Encryption (AES-256) is configured separately, because PDF/A and PDF/X do not allow it:
PdfGenerateConfig secured = new()
{
Encryption = PdfEncryptionOptions.ReadOnly(userPassword: "open-me", ownerPassword: "owner-secret")
};Other PdfGenerateConfig options worth knowing: PdfVersion (PDF 2.0 by default, Pdf17 for older readers), PdfXConformance, Attachments and FacturX (embedded files and e-invoices), Linearize (fast web view), CompressContentStreams, EnableInteractivePdfForms, PreferredColorScheme, AllowLocalFileAccess (turn off when rendering untrusted HTML), and RasterizationDpi for the few effects drawn as bitmaps. See Usage Examples for each of them in context.
You can generate PDF documents using self contained MHTML files (what Chrome calls "single page documents") by using the included MimeKitNetworkLoader
PdfGenerateConfig pdfConfig = new(){
PageSize = PageSize.Letter,
PageOrientation = PageOrientation.Portrait,
NetworkLoader = new MimeKitNetworkLoader(File.OpenRead("example.mhtml"))
};
PdfGenerator generator = new();
var stream = new MemoryStream();
// Passing null to GeneratePdf will load the HTML from the provided network loader instance instead
var document = await generator.GeneratePdf(null, pdfConfig);
document.Save(stream);You can also render HTML from the Internet to a PDF
HttpClient httpClient = new();
PdfGenerateConfig pdfConfig = new(){
PageSize = PageSize.Letter,
PageOrientation = PageOrientation.Portrait,
NetworkLoader = new HttpClientNetworkLoader(httpClient, new Uri("https://www.example.com"))
};
PdfGenerator generator = new();
var stream = new MemoryStream();
// Passing null to GeneratePdf will load the HTML from the provided network loader instance instead
var document = await generator.GeneratePdf(null, pdfConfig);
document.Save(stream);You can render a local HTML file and all of its relative resources (stylesheets, images, fonts) from disk with FileUriNetworkLoader. It sets the base URL to the file's own location, just like opening the file in a browser.
PdfGenerateConfig pdfConfig = new(){
PageSize = PageSize.Letter,
PageOrientation = PageOrientation.Portrait,
NetworkLoader = new FileUriNetworkLoader("report/index.html")
};
PdfGenerator generator = new();
var stream = new MemoryStream();
// Passing null to GeneratePdf will load the HTML from the provided network loader instance instead
var document = await generator.GeneratePdf(null, pdfConfig);
document.Save(stream);Note that loading resources using relative paths resolves against the configured NetworkLoader's BaseUri (e.g. an HttpClientNetworkLoader or FileUriNetworkLoader), or a <base href> element if the HTML has one. With the default loader, relative paths resolve against the current working directory and load from the local file system. file: URIs are always loaded from disk regardless of which loader is configured, the same way data: URIs always are.
A local file's content type is resolved from the OS's own MIME mechanism by default (Windows shell associations, macOS/iOS Uniform Type Identifiers, or Linux /etc/mime.types), falling back to a built-in set for HTML, CSS, SVG, PeachPDF's raster image formats, and TTF/OTF/WOFF/WOFF2 fonts. For a local file with an extension outside that set, register its MIME type with the OS so PeachPDF can resolve it. See Rendering a local HTML file for details.
If you'd rather build a document directly in C# than author HTML, PdfGenerator.CreateDocument gives you pages, containers, text, images, rows/columns, tables, lists, and repeating headers/footers with page numbers — no HTML/CSS strings involved:
var generator = new PdfGenerator();
var document = await generator.CreateDocument(doc =>
{
doc.Page(page =>
{
page.Size(PageSize.A4);
page.Margin(20);
page.Content(container =>
{
container.Padding(20).Border(1, PdfColor.FromHex("#CCCCCC")).Text("Hello, declarative PeachPDF!");
});
});
});
var stream = new MemoryStream();
document.Save(stream);See the Declarative Document-Building API guide for the full surface.
Several documents merge into one PDF with DocumentMerge (original or continuous page numbers, see the guide), existing PDFs go before, between and after the generated pages with doc.Append(...) / doc.Prepend(...) on the builder (Adding existing PDFs), and existing PDF files are merged, cut up, layered and protected with PdfOperation from PeachDrawing.PDF:
using PeachDrawing.PDF;
PdfOperation.LoadFile("a.pdf").TakePages("1-5").MergeFile("b.pdf", "1,3,5").Save("merged.pdf");If you just want to convert HTML to PDF from the shell, the standalone peachpdf command-line tool needs no .NET runtime — it is a single, self-contained Native AOT binary:
peachpdf report.html -o report.pdfPrebuilt binaries for Windows (x64/ARM64), Linux (x64/ARM64), and macOS (Apple Silicon) are attached to each GitHub Release. See the Command-Line Tool guide for the full option reference.
A PdfGenerator instance is not thread-safe — don't call it concurrently from multiple threads, and don't reuse one instance across overlapping renders.
Using a separate PdfGenerator instance per thread (one per web request, one per item in a parallel batch, etc.) is safe and is the intended way to generate PDFs concurrently:
// Safe: each thread/task gets its own PdfGenerator.
var results = await Task.WhenAll(htmlDocuments.Select(async html =>
{
var generator = new PdfGenerator();
var document = await generator.GeneratePdf(html, pdfConfig);
var stream = new MemoryStream();
document.Save(stream);
return stream;
}));See Thread safety for more detail.
PeachPDF is trimming-safe and Native AOT-compatible (IsTrimmable + IsAotCompatible). It publishes cleanly under PublishTrimmed and PublishAot — all platform interop uses source-generated LibraryImport marshalling, so nothing PeachPDF needs is trimmed away and there is no runtime code generation. See Trimming and Native AOT for more detail.
By default, PeachPDF uses Segoe UI on Windows and the system font, San Francisco, on macOS and iOS. Neither is installed by default on the other platforms, so PeachPDF picks a different platform-appropriate default there instead. You can remap the default font (or any other family) to another one using
PdfGenerator generator = new();
generator.AddFontFamilyMapping("Segoe UI","sans-serif"); // or any other system installed fontserif, sans-serif, monospace, cursive, fantasy, math, system-ui and the ui-serif, ui-sans-serif, ui-monospace and ui-rounded generics resolve to a real installed font, matching actual browser behavior per platform (Times New Roman/Arial/Consolas/Comic Sans MS/Impact on Windows, Noto Serif/Roboto/Droid Sans Mono/Dancing Script on Android, delegated to the system's own fontconfig on Linux, and WebKit's choices on macOS and iOS: Times/Helvetica/Courier/Apple Chancery/Papyrus, with San Francisco, New York, SF Mono and SF Rounded for system-ui and the ui-* generics; -apple-system and BlinkMacSystemFont are accepted as names for system-ui) rather than one invented cross-platform table — see Fonts for the full breakdown. Every mapping, including custom ones set via AddFontFamilyMapping, is verified against what's actually installed before use.
A requested font-weight/font-style/font-stretch PeachPDF can't find an exact face for is matched to the nearest registered face (CSS Fonts Level 4 §5.2 — the same algorithm real browsers use), not just Regular. When nothing close enough exists, PeachPDF synthesizes a faux-bold (fill+stroke) or faux-italic/oblique (glyph shear, following an explicit oblique <angle> when declared) instead of rendering with zero visual distinction. See Fonts for details.
The recommended way to install custom fonts is to install them into your operating system.
PeachPDF by default picks up TrueType/OpenType fonts and font collections (.ttf, .otf, .ttc, .otc — every face of a collection) from the operating system (%SystemRoot%\Fonts and %LOCALAPPDATA%\Microsoft\Windows\Fonts on Windows; every installed font CoreText reports on macOS, Mac Catalyst, iOS and tvOS, including the system's own San Francisco and New York; primarily the system's own fontconfig on Linux, falling back to /usr/share/fonts, /usr/local/share/fonts, and $HOME/.fonts if fontconfig isn't available; /system/fonts, /product/fonts, and /data/fonts on Android). Fonts an app registers itself (below) are added to those.
You can also add a font at runtime by loading the font into a Stream, and then using the AddFontFromStream API:
PdfGenerator generator = new();
await generator.AddFontFromStream(fontStream); // Supports TrueType (TTF), CFF, WOFF, and WOFF2 formatsWeb fonts loaded via @font-face (url(), with fallback lists, and local()) are also supported.
We support TrueType, CFF, WOFF, and WOFF2 font formats.
PeachPDF is built on libraries you can also use on their own, all versioned together:
- PeachBrackets — the HTML/CSS/SVG/MathML engine without any PDF: load, lay out and paint pages onto any canvas. Hosts: WinUI, Android, Apple.
- PeachDrawing.PDF — the PDF writer and PDF operations (merge, split, overlay, encrypt, linearize), usable with no HTML.
- PeachDrawing.Core — the
Canvasabstraction, with backends PeachDrawing (CPU raster), PeachDrawing.Svg, PeachDrawing.Skia, PeachDrawing.Win2D and PeachDrawing.CoreGraphics. - PeachDrawing.Text — fonts, shaping, bidi, hyphenation and paragraph layout.
- PeachImage — image decoding and encoding.
PeachPDF is free and open source. If it saves you time, you can sponsor jhaygood86 on GitHub; sponsors can get paid support, see Sponsorship.