Pure C# HTML/CSS to PDF rendering engine. Zero dependencies. Chrome-quality output.
Write normal HTML and CSS. Get a perfect PDF. No WebKit, no Chromium, no native binaries.
| Feature | EggPdf | SelectPdf | wkhtmltopdf | Puppeteer |
|---|---|---|---|---|
| Pure C# | Yes | No | No | No |
| Dependencies | Zero | ~50MB WebKit | ~40MB Qt | ~300MB Chrome |
| .NET Framework | 4.6.2+ | Limited | N/A | N/A |
| .NET Core/5+ | All | Some | N/A | Yes |
| CSS Flexbox | Yes | Yes | No | Yes |
| CSS Grid | Yes | Yes | No | Yes |
| SVG Support | Yes (vector) | Yes | Partial | Yes |
| PDF/A | Yes | No | No | No |
| PDF/UA | Yes | No | No | No |
| Tagged PDF | Yes | No | No | No |
| Digital Signatures | Yes | No | No | No |
| License | MIT | Commercial | LGPL | Apache 2 |
dotnet add package EggPdf// One-liner
byte[] pdf = await EggPdf.HtmlToPdf.RenderAsync("<h1>Hello World</h1>");
File.WriteAllBytes("output.pdf", pdf);Page size, margins, and orientation are set with regular CSS @page rules in the HTML itself:
string html = @"
<html><head><style>@page { size: A4; margin: 20mm 15mm; }</style></head>
<body><h1>My Document</h1></body></html>";
// To byte[]
byte[] pdf = await EggPdf.HtmlToPdf.RenderAsync(html);
// To a file
await EggPdf.HtmlToPdf.RenderToFileAsync(html, "report.pdf");
// To a stream (e.g. an HTTP response body), with cancellation
await EggPdf.HtmlToPdf.RenderAsync(html, Response.Body, HttpContext.RequestAborted);Prefer C# properties over writing CSS? PdfRenderOptions is translated into the equivalent
@page rule internally — it's not a separate layout path, just a convenience wrapper:
byte[] pdf = await EggPdf.HtmlToPdf.RenderAsync(html, new EggPdf.PdfRenderOptions
{
PageSize = "Letter",
Orientation = "landscape",
MarginTop = 40, MarginBottom = 40, // CSS pixels
Title = "Q4 Report", // PDF document metadata
});dotnet add package EggPdf.AspNetCore// Plain HTML -> PDF download, no DI registration needed
[HttpGet("report/pdf")]
public IActionResult GetReport()
=> new PdfResult("<h1>Report</h1>", "report.pdf");
// Render a .cshtml Razor view directly -> PDF download, one line.
// Requires services.AddEggPdfRazor() at startup (EggPdf.AspNetCore already
// references EggPdf.Razor, so installing just this package is enough).
[HttpGet("invoice/{id}/pdf")]
public async Task<IActionResult> GetInvoice(int id)
{
var model = await _invoiceService.GetAsync(id);
return new RazorPdfResult("Invoice", model, $"invoice-{id}.pdf");
}dotnet add package EggPdf.Razorservices.AddEggPdfRazor();
// Render .cshtml template directly to PDF
public class InvoiceService(IRazorToPdfConverter pdf)
{
public async Task<byte[]> Generate(InvoiceModel model)
=> await pdf.RenderViewAsync("Invoice", model);
}- Full HTML5 parsing (WHATWG spec-compliant)
- CSS 2.1 complete + CSS3 (Flexbox with auto margins & baseline alignment, Grid, Multi-column)
- CSS Custom Properties (
var()) @media printsupport- Webfonts: remote
<link>stylesheets (Google Fonts) and@font-faceover http(s), data: URIs, or files - SVG rendering (vector output, not rasterized)
- All image formats (JPEG, PNG incl. 1-bit QR codes, GIF, WebP, SVG, Base64)
- Cloudflare email obfuscation (
data-cfemail) decoded automatically
- PDF 1.4 / 1.5 / 1.7 / 2.0
- Clickable hyperlinks and internal links
- Auto-generated bookmarks from headings
- Table of contents with page numbers
- Running headers/footers
- Page numbers (Page X of Y)
- Tables spanning any number of pages without row loss
- Mixed page orientations (portrait + landscape)
- Watermarks
- Pin content (e.g. a signature/acceptance box) to the bottom of whichever page dynamic content ends on (
-eggpdf-pin-bottom: page)
- TrueType/OpenType font embedding with subsetting
- Weight-accurate faces:
font-weight: 300–900each select their own variant - Font fallback chain + per-codepoint symbol-font fallback (⚠ ✔ …)
- Full Unicode: Vietnamese and extended Latin out of the box
- Browser-parity metrics: text measured with the real font, baselines like Chrome
- Automatic hyphenation
- Digital signatures — one-call X.509 signing (
PdfSigner.Sign(pdf, cert), detached CMS/PKCS#7) or external-CMS two-step flow for HSMs - Password protection with permission flags — view-only PDFs that block editing/copying (RC4 40/128-bit)
- AcroForm fields (fillable forms from HTML form elements)
- PDF merging
- QR codes and barcodes
- Streaming output (constant memory for large documents)
- Font caching across renders
- Thread-safe converter (one instance per app)
- Streaming table layout for 10,000+ row tables
| Scenario | Mean | Memory |
|---|---|---|
| Simple page (h1 + p) | 13 µs | 20 KB |
| Invoice (table + styles) | 86 µs | 85 KB |
| Large table (100 rows) | 1.2 ms | 939 KB |
Benchmarks run on every PR (results posted as comment) and on every merge to main (artifacts uploaded). Targets: simple < 50ms, invoice < 100ms, large table < 5s.
| Target | Coverage |
|---|---|
netstandard2.0 |
.NET Framework 4.6.2+, .NET Core 2.0+, Mono, Xamarin, Unity |
netstandard2.1 |
.NET Core 3.0+ |
net6.0 |
.NET 6+ |
net8.0 |
.NET 8+ |
net9.0 |
.NET 9+ |
net10.0 |
.NET 10+ |
| Package | Description | Dependencies |
|---|---|---|
| EggPdf | Core library | None |
| EggPdf.Razor | Razor template integration | ASP.NET Core |
| EggPdf.AspNetCore | ASP.NET Core middleware | ASP.NET Core |
# REST API service (with Web UI)
docker run -p 8080:8080 eggspot/eggpdf:latest
# Open http://localhost:8080 for Web UI, or call REST API from any language
# CLI (convert files)
docker run -v $(pwd):/work eggspot/eggpdf:latest eggpdf /work/input.html -o /work/output.pdfDownload a single executable for your platform -- no installation required:
| Platform | Download |
|---|---|
| Windows x64 | eggpdf-win-x64.exe |
| Windows ARM64 | eggpdf-win-arm64.exe |
| Linux x64 | eggpdf-linux-x64 |
| Linux ARM64 | eggpdf-linux-arm64 |
| macOS x64 (Intel) | eggpdf-osx-x64 |
| macOS ARM64 (Apple Silicon) | eggpdf-osx-arm64 |
./eggpdf input.html -o output.pdf
./eggpdf https://example.com -o page.pdf # fetch and render a URL
echo "<h1>Hi</h1>" | ./eggpdf - -o output.pdfOpen http://localhost:8080 after starting the Docker service. Paste HTML, get PDF. No coding required.
See the Wiki for full documentation:
- Getting Started
- Configuration
- Page Layout & CSS
- Headers, Footers & Page Numbers
- Images & SVG
- Tables
- Fonts & Typography
- PDF Features
- Performance
- API Reference
Contributions are welcome! We follow strict TDD — write the test before the code:
- Fork the repository
- Create a feature branch:
git checkout -b feat/my-feature - Write the failing test first — run it, confirm it fails
- Write minimal code to make the test pass
- Run the test — if it fails, fix code and run again; repeat until it passes
- Run ALL tests — fix any regressions and repeat until the full suite passes
- Check performance if touching hot paths
- Commit with conventional prefixes:
feat:,fix:,perf:,test: - Push and create a PR
See CLAUDE.md for detailed development guidelines.
EggPdf is free and open source. If you find it useful, please consider sponsoring:
Your sponsorship helps us:
- Maintain and improve the library
- Add new CSS features and PDF capabilities
- Keep the documentation up to date
- Respond to issues and PRs
MIT License. See LICENSE for details.
Copyright (c) 2025 Eggspot