Skip to content

Repository files navigation

html2pdf

CI Go Reference License: BSD-3-Clause

Pure-Go, zero-C, static HTML to vector PDF. No headless browser, no screenshot-then-slice: it drives go-webengine's own layout tree straight into go-pdfkit text/rect/stroke calls, so the PDF's text is real text — selectable, searchable, small — not a raster of it.

Quick start

doc, err := html2pdf.Export(htmlSource, html2pdf.Options{})
if err != nil {
    log.Fatal(err)
}
f, _ := os.Create("out.pdf")
defer f.Close()
doc.Write(f)

Or from the shell:

go run ./cmd/html2pdf -in report.html -out report.pdf

Pagination

Page breaks land between atoms — a text line, or a whole <tr> — never through one. A table row that would overflow the page moves to the next page whole; a paragraph may still break between its own lines, same as printed text always has.

Layout width vs. print width

A page is laid out at Options.ViewportPx (default 1024px), then scaled down to fit the print column — not laid out directly at the print column's own width (a plain A4 page is under 650px wide). Many real pages carry a fixed-width element sized for a desktop viewport (a sidebar, a multi-column nav) that a browser's own responsive CSS only collapses below some breakpoint; laying out narrower than that breakpoint just squeezes the rest of the page into a sliver instead of dropping the sidebar. Confirmed against RFC 9110's HTML edition, whose table-of-contents sidebar did exactly this — see corpus/CORPUS.md for the before/after page counts across all 8 corpus pages.

Scope

This renders static HTML: no JavaScript, no external stylesheets, no @font-face. Text is set in the three families go-webengine's own paint package bundles — Inter (sans), Lora (serif), Go Mono (mono) — so the glyphs drawn always match the metrics the layout pass measured against; there is no web-font fetch to fail silently.

Images — raster <img>, <img src="*.svg"> and inline <svg> — go through the engine's own fetch/decode/size pipeline (Engine.LoadImages) and are embedded as bitmaps, so they're laid out and drawn exactly as the engine's raster canvas would draw them. A relative src resolves against Options.BaseURL; an image that fails to fetch or decode is simply left out, as on the raster canvas. This is the one place Export touches the network.

One gap remains, inherited from — not introduced by — the layout engine:

  • Inline-level background/border/padding does not paint. A styled <span> never gets its own box in go-webengine's layout (confirmed against its reference raster painter too — a shared engine limitation). Style the containing block/table-cell instead of an inner inline element when you need a filled badge or pill.

Status

Validated against a hand-built regression suite (html2pdf_test.go, ~94% statement coverage) and a corpus of 8 real public pages (corpus/, in the spirit of go-webengine's own bench/) — see corpus/CORPUS.md for current results and the bugs the corpus run has found so far.

License

BSD-3-Clause, see LICENSE.

About

Pure-Go static HTML to vector PDF, driven by go-webengine's layout tree and go-pdfkit's writer

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages