I got tired of every .NET HTML-to-PDF library so I built a free one from scratch

1 points by recursivewiner ↗ HN
Hi all, I've just open-sourced NetPDF, a project I've been building to scratch an itch that's bugged the .NET community for as long as I can remember, there has never been a genuinely free, high-quality HTML-to-PDF library.

Every existing route has a real tradeoff:

Headless-browser wrappers (Puppeteer/Playwright/Chromium): huge footprint, a native browser to ship and secure, a process to spawn per render.

wkhtmltopdf wrappers: built on an unmaintained engine.

Commercial libraries: excellent, but licensed per seat/server.

"Free" libraries: AGPL (viral copyleft) or a revenue cap that becomes a license fee once you grow.

After years of working around this, I decided to build the thing I wanted. NetPDF is a real HTML/CSS layout engine written from scratch in C# - the same category as Prince or WeasyPrint, not "automate a browser."

What it is

A pure C# / .NET 10 engine that parses HTML + CSS and lays it out itself: block, inline, flexbox, grid, and tables, fragmentation across pages, paged-media (@page, margin boxes, running headers/footers, counter(page)), gradients, shadows, transforms, and SVG.

Why it's built differently

Clean-room. Every algorithm comes from the W3C / ISO / Unicode (UAX) specs, not from reading other engines' source. Legally clean to depend on.

No System.Drawing. No GDI+, no platform-specific rendering baggage.

No AGPL, no revenue caps, no commercial tier. Apache-2.0. Ship it in closed-source commercial products for free, permanently.

No browser, no native Chromium, no process spawning at render time. It's a library, not an orchestrator.

Deterministic. Same input → identical PDF bytes. Golden-file testing, content-hash caching, and byte diffs just work.

AOT-clean & fast. No reflection in hot paths; designed for PublishAot. A 3-page invoice renders in well under 200 ms; memory scales linearly with page count.

Proper international text. HarfBuzz shaping, UAX #9 bidi, UAX #14 line breaking — not naive char-by-char layout.

Try it

Package & docs: https://raroche.github.io/NetPdf/ Repo: https://github.com/raroche/NetPdf

// The gist:

HtmlPdf.Convert(html, "invoice.pdf");

I'd love your feedback

This is a solo labor of love and it's still evolving. I want to hear what breaks, what CSS you throw at it that falls over, and what you'd prioritize next. Issues, PRs, and blunt critique all welcome. If it saves you a dependency headache, that's the whole point!

A like or share would mean the world and help it reach the devs who need it.

2 comments

[ 4.4 ms ] story [ 16.7 ms ] thread