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);var converter = new HtmlToPdfConverter(new PdfOptions
{
PageSize = PageSize.A4,
Orientation = PageOrientation.Portrait,
Margins = new PageMargins(top: 20, right: 15, bottom: 20, left: 15, unit: Unit.Mm),
DefaultFont = "Arial",
Title = "My Document"
});
// To byte[]
byte[] pdf = await converter.RenderAsync(htmlString);
// To file
await converter.RenderToFileAsync(htmlString, "report.pdf");
// To HTTP response (streaming, no buffering)
await converter.RenderAsync(htmlString, Response.Body, HttpContext.RequestAborted);dotnet add package EggPdf.AspNetCore// DI Registration
services.AddEggPdf(options =>
{
options.PageSize = PageSize.A4;
options.DefaultFont = "Arial";
});
// Controller
[HttpGet("invoice/{id}/pdf")]
public async Task<IActionResult> GetInvoice(int id)
{
var model = await _invoiceService.GetAsync(id);
string html = await _viewRenderer.RenderAsync("Invoice", model);
return new PdfResult(html) { FileName = $"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, Grid, Multi-column)
- CSS Custom Properties (
var()) @media printsupport@font-facewith WOFF/WOFF2- SVG rendering (vector output, not rasterized)
- All image formats (JPEG, PNG, GIF, WebP, SVG, Base64)
- 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)
- Repeating table headers across pages
- Mixed page orientations (portrait + landscape)
- Watermarks
- TrueType/OpenType font embedding with subsetting
- Font fallback chain
- CJK support (Chinese, Japanese, Korean)
- Vietnamese, Thai, Arabic, Hebrew
- Emoji rendering (color emoji)
- Automatic hyphenation
- Variable font support
- PDF/A (archival: 1b, 2b, 3b)
- PDF/UA (accessibility)
- Digital signatures (PAdES)
- AcroForm fields
- QR codes and barcodes
- File attachments (ZUGFeRD/Factur-X e-invoicing)
- PDF merging
- AES-256 encryption
- 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 input.html -o output.png --format png
./eggpdf input.html --watch # live reload
./eggpdf --serve # start REST API serverOpen 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