chinmay-sawant/gowkhtmltopdf

Pure-Go HTML template engine: HTML→PDF and HTML→image for invoices, certificates, storybooks, posters, statements, and tables. No browser, no cgo, wkhtmltopdf work-alike.

Go

37

640 commits

updated Sep 17, 2026

See the code
cli
converter
css
documents
go
golang
html
html2pdf
html-template
html-template-engine
html-to-image
html-to-pdf
invoice
no-cgo
pdf
pdf-generation
pure-go
report-generator
template-engine
wkhtmltopdf

See what people are saying (1)

SourceMessageScoreDate

Built a native Go HTML-to-PDF engine as an alternative to wkhtmltopdf / headless Chrome (v0.2.6 with 350+ CSS properties) (r/golang)

Hey r/golang, I have been working on an HTML to PDF converter in pure Go as a lightweight alternative to `wkhtmltopdf` and heavyweight headless Chromium instances for the past month. I just released **v0.2.6**, which brings full support for **350+ CSS properties**. Most backend teams rely on…

65

Sep 16, 2026

README

gowkhtmltopdf
gowkhtmltopdf

gowkhtmltopdf is a no-cgo PDF engine based on HTML templates (and HTML→image) for structured templates and documents: invoices, receipts, certificates, storybooks, posters, statements, tables, and multi-page documents with headers, footers, tables of contents, and PDF outlines — without any wrappers.

It is a clean-room work-alike of the wkhtmltopdf CLI surface. Native builds use no browser process, no cgo, and no native converter process. Two static binaries (gowkhtmltopdf, gowkhtmltoimage) and a Go library run an in-repo pipeline (load → parse → style → layout → paginate → paint → write). An opt-in browser build runs the same pipeline through WebAssembly for inline HTML previews. Direct modules are allowlisted: go-text/typesetting (OpenType shaping) and tdewolff/canvas (SVG rasterization). The product is HTML templates and documents, not Chrome visual parity.

Status: v0.2.6 (current release). The native Document API and explicit CLI grammar are now the supported surface. Opt-in PDF 1.7 / 2.0 and PDF/A + PDF/UA profiles. License: MIT.

Note on master: master tracks active development and may be broken at times. For a stable build, use the latest tagged release.

What it is for

You need…This project
Invoices, tables, page breaks from GoYes
Headers, footers, TOC, PDF bookmarksYes
PDF 1.4 / PDF 1.7 / PDF 2.0 outputDefault PDF 1.4. Opt-in 1.7 / 2.0 via --pdf-version. Version alone is not a PDF/A or PDF/UA claim
PDF/A-3a & PDF/UA-1 complianceOpt-in via --pdf-profile a3a-ua1 / WithPDFProfile (implies PDF 1.7)
PDF/A-4 & PDF/UA-2 complianceOpt-in via --pdf-profile a4-ua2 / WithPDFProfile (implies PDF 2.0)
Offline native static binaries; no browser process / no cgoYes
Browser preview for inline HTMLYes, through the opt-in WASM adapter
Full CSS, JavaScript, or Chrome parityNo — print CSS subset; no JS
CJK / complex UnicodePartial — Type0/CID + --font-path; see fonts.md

Quick start

Requires Go 1.26+.

make build
./bin/gowkhtmltopdf --allow-local-files -o /tmp/invoice.pdf \
  testdata/golden/fixture-01-simple-invoice.html

Committed samples live in output/ (make samples). Python API samples land under output/python/ (make samples-python; needs CGO_ENABLED=1). Install, flags, and HTTP URLs: getting-started.md. Browser build and preview: wasm.md.

Documentation

DocumentWhat it covers
documentation/README.mdDocumentation index
documentation/overview.mdProduct overview and design principles
documentation/getting-started.mdInstall and first conversion
documentation/cli.mdCLI grammar and flags
documentation/library-api.mdGo library API
documentation/wasm.mdBrowser WASM conversion and previews
documentation/python.mdPython bindings: in-process pip install gowkhtmltopdf
documentation/MIGRATION-0.2.4.md0.2.3 library/CLI to the 0.2.4 Document API
documentation/architecture.mdPackage map and pipeline
documentation/architecture/README.mdDeep-dive architecture notes
documentation/fidelity.mdFidelity tiers and claims language
documentation/compatibility-matrix.mdPer-element / per-property / per-flag contract
documentation/fonts.mdBundled faces, --font-path, @font-face
documentation/samples.mdGolden fixtures and output/
documentation/performance.mdBenchmarks and how to measure
documentation/benchmarks.mdConsolidated current benchmark capture (2026-09-13)
testdata/golden/benchmarks/README.mdCurrent CLI vs wkhtmltopdf snapshot
documentation/deferred.mdDeferred features and next gates
documentation/THREAT-MODEL.mdSecurity / ACL / network policy
documentation/integration-security.mdEmbedding in HTTP apps (SSRF)
comparison: go-wkhtmltopdfBinary wrapper vs this in-process engine
comparison: 2026 landscapeChromium, wkhtmltopdf, WeasyPrint, Prince
CONTRIBUTING.mdSetup, tests, PR workflow
CHANGELOG.mdRelease history
examples/Library example programs
output/Regenerable sample PDFs/PNG
plans/README.mdImplementation ledger index
LICENSEMIT license

Library

The supported Go API is Document / ImageDocument with explicit Content sources. The pre-0.2.4 wkhtml-shaped root exports are removed.

doc := gowkhtmltopdf.Document{
    Pages: []gowkhtmltopdf.Page{{
        Source: gowkhtmltopdf.Content{
            HTML: []byte(`<html><body><h1>Invoice</h1></body></html>`),
        },
    }},
    PageSize: "A4",
}
pdfBytes, err := doc.PDF(ctx)

Local files, TOC/cover fields, network policy, and the migration table: documentation/library-api.md, documentation/MIGRATION-0.2.4.md.

Python callers get the same engine in-process through an opt-in shared library: documentation/python.md.

from gowkhtmltopdf import PDFOptions, convert_html_to_pdf

pdf_bytes = convert_html_to_pdf(
    b"<html><body><h1>Invoice</h1></body></html>",
    options=PDFOptions(page_size="A4"),
)

Performance

Current snapshot (2026-09-13): generic bin/gowkhtmltopdf (VERSION 0.2.6, release tree 8aab63a) versus installed wkhtmltopdf 0.12.6.1 (patched Qt) on Linux amd64 (WSL2), 13th Gen Intel Core i7-13700HX. Same report fixture (20 invoice rows per requested page), median of three timed process runs after one warmup.

PagesgowkhtmltopdfwkhtmltopdfFaster by
213 ms258 ms19.68x
1024 ms279 ms11.65x
100124 ms532 ms4.30x
500573 ms1.718 s3.00x

Faster at every tested size. Gowk also used less peak RSS at every tested size in this capture, including 500 pages (80,448 KiB versus 123,068 KiB).

Same host, same fixture family against other engines (default external matrix: 2 / 10 / 50 / 100 pages):

Pagesvs WeasyPrintvs Puppeteer / Chrome
249.18x111.73x
1059.78x61.63x
5082.04x26.65x
10088.33x17.40x

The public Go library (Document.WritePDF) measures 5.50 ms at 2 pages (about 47x against the same capture's 258 ms wkhtmltopdf baseline) and 554.56 ms at 500 pages; the Python bindings measure 3.52 ms and 504.93 ms (warm medians).

Full matrices, RSS, PDF sizes, internal-engine and public-library go test -bench rows, and historical snapshots:

Reproduce:

make build
make bench-cli-compare
./scripts/bench-external.sh
./scripts/bench-performance-recovery.sh --mode=<mode>
make bench
make bench-engine
make bench-lib
make python-benchmarks

make python-benchmarks rebuilds dist/libgowkhtmltopdf.so (CGO_ENABLED=1) and times the in-process Python Document.pdf() / ImageDocument.image() path on the same report.html.tmpl fixture (20 invoice rows per page) that make bench-lib uses. Override sizes with GOWKHTMLTOPDF_BENCH_SIZES=2,10,50. The Python architecture sample is make python-api (testdata/golden/python_api -> output/python/architecture-diagram.pdf).

Development

gowkhtmltopdf is a pure-Go rendering engine written from scratch. Humans own the architecture and domain design. AI tools including Grok, OpenAI Codex, OpenCode, and Cursor help with implementation drafts, visual checks against golden fixtures, and test suites.

Much of the code starts as an AI draft. It does not ship unchecked. Human maintainers own the pipeline of load, parse, style, layout, paginate, paint, and write. Humans review changes and require proof before merge: make test, make lint, make golden, and make claim-scan, and they also validate all 50+ sample templates manually.

Performance is part of that proof. The full record lives in documentation/performance.md. Raw numbers and reproduce steps live in testdata/golden/benchmarks/README.md. Profile with go test -cpuprofile plus go tool pprof -top; see How to measure. The CI perf budget is TestTenPageTableReportPerformance in internal/convert/perf_test.go.

License

MIT License — Copyright (c) 2026 Chinmay Sawant.

Bundled Liberation and DejaVu fonts are SIL OFL / Bitstream Vera; see internal/pdf/assets/NOTICE. The Noto KR test subset ships testdata/fonts/OFL.txt.

Contributors

chinmay-sawant

640 commits

chinmay-sawant/gowkhtmltopdf

Pure-Go HTML template engine: HTML→PDF and HTML→image for invoices, certificates, storybooks, posters, statements, and tables. No browser, no cgo, wkhtmltopdf work-alike.

Go

37

640 commits

updated Sep 17, 2026

See the code
cli
converter
css
documents
go
golang
html
html2pdf
html-template
html-template-engine
html-to-image
html-to-pdf
invoice
no-cgo
pdf
pdf-generation
pure-go
report-generator
template-engine
wkhtmltopdf

See what people are saying (1)

SourceMessageScoreDate

Built a native Go HTML-to-PDF engine as an alternative to wkhtmltopdf / headless Chrome (v0.2.6 with 350+ CSS properties) (r/golang)

Hey r/golang, I have been working on an HTML to PDF converter in pure Go as a lightweight alternative to `wkhtmltopdf` and heavyweight headless Chromium instances for the past month. I just released **v0.2.6**, which brings full support for **350+ CSS properties**. Most backend teams rely on…

65

Sep 16, 2026

README

gowkhtmltopdf
gowkhtmltopdf

gowkhtmltopdf is a no-cgo PDF engine based on HTML templates (and HTML→image) for structured templates and documents: invoices, receipts, certificates, storybooks, posters, statements, tables, and multi-page documents with headers, footers, tables of contents, and PDF outlines — without any wrappers.

It is a clean-room work-alike of the wkhtmltopdf CLI surface. Native builds use no browser process, no cgo, and no native converter process. Two static binaries (gowkhtmltopdf, gowkhtmltoimage) and a Go library run an in-repo pipeline (load → parse → style → layout → paginate → paint → write). An opt-in browser build runs the same pipeline through WebAssembly for inline HTML previews. Direct modules are allowlisted: go-text/typesetting (OpenType shaping) and tdewolff/canvas (SVG rasterization). The product is HTML templates and documents, not Chrome visual parity.

Status: v0.2.6 (current release). The native Document API and explicit CLI grammar are now the supported surface. Opt-in PDF 1.7 / 2.0 and PDF/A + PDF/UA profiles. License: MIT.

Note on master: master tracks active development and may be broken at times. For a stable build, use the latest tagged release.

What it is for

You need…This project
Invoices, tables, page breaks from GoYes
Headers, footers, TOC, PDF bookmarksYes
PDF 1.4 / PDF 1.7 / PDF 2.0 outputDefault PDF 1.4. Opt-in 1.7 / 2.0 via --pdf-version. Version alone is not a PDF/A or PDF/UA claim
PDF/A-3a & PDF/UA-1 complianceOpt-in via --pdf-profile a3a-ua1 / WithPDFProfile (implies PDF 1.7)
PDF/A-4 & PDF/UA-2 complianceOpt-in via --pdf-profile a4-ua2 / WithPDFProfile (implies PDF 2.0)
Offline native static binaries; no browser process / no cgoYes
Browser preview for inline HTMLYes, through the opt-in WASM adapter
Full CSS, JavaScript, or Chrome parityNo — print CSS subset; no JS
CJK / complex UnicodePartial — Type0/CID + --font-path; see fonts.md

Quick start

Requires Go 1.26+.

make build
./bin/gowkhtmltopdf --allow-local-files -o /tmp/invoice.pdf \
  testdata/golden/fixture-01-simple-invoice.html

Committed samples live in output/ (make samples). Python API samples land under output/python/ (make samples-python; needs CGO_ENABLED=1). Install, flags, and HTTP URLs: getting-started.md. Browser build and preview: wasm.md.

Documentation

DocumentWhat it covers
documentation/README.mdDocumentation index
documentation/overview.mdProduct overview and design principles
documentation/getting-started.mdInstall and first conversion
documentation/cli.mdCLI grammar and flags
documentation/library-api.mdGo library API
documentation/wasm.mdBrowser WASM conversion and previews
documentation/python.mdPython bindings: in-process pip install gowkhtmltopdf
documentation/MIGRATION-0.2.4.md0.2.3 library/CLI to the 0.2.4 Document API
documentation/architecture.mdPackage map and pipeline
documentation/architecture/README.mdDeep-dive architecture notes
documentation/fidelity.mdFidelity tiers and claims language
documentation/compatibility-matrix.mdPer-element / per-property / per-flag contract
documentation/fonts.mdBundled faces, --font-path, @font-face
documentation/samples.mdGolden fixtures and output/
documentation/performance.mdBenchmarks and how to measure
documentation/benchmarks.mdConsolidated current benchmark capture (2026-09-13)
testdata/golden/benchmarks/README.mdCurrent CLI vs wkhtmltopdf snapshot
documentation/deferred.mdDeferred features and next gates
documentation/THREAT-MODEL.mdSecurity / ACL / network policy
documentation/integration-security.mdEmbedding in HTTP apps (SSRF)
comparison: go-wkhtmltopdfBinary wrapper vs this in-process engine
comparison: 2026 landscapeChromium, wkhtmltopdf, WeasyPrint, Prince
CONTRIBUTING.mdSetup, tests, PR workflow
CHANGELOG.mdRelease history
examples/Library example programs
output/Regenerable sample PDFs/PNG
plans/README.mdImplementation ledger index
LICENSEMIT license

Library

The supported Go API is Document / ImageDocument with explicit Content sources. The pre-0.2.4 wkhtml-shaped root exports are removed.

doc := gowkhtmltopdf.Document{
    Pages: []gowkhtmltopdf.Page{{
        Source: gowkhtmltopdf.Content{
            HTML: []byte(`<html><body><h1>Invoice</h1></body></html>`),
        },
    }},
    PageSize: "A4",
}
pdfBytes, err := doc.PDF(ctx)

Local files, TOC/cover fields, network policy, and the migration table: documentation/library-api.md, documentation/MIGRATION-0.2.4.md.

Python callers get the same engine in-process through an opt-in shared library: documentation/python.md.

from gowkhtmltopdf import PDFOptions, convert_html_to_pdf

pdf_bytes = convert_html_to_pdf(
    b"<html><body><h1>Invoice</h1></body></html>",
    options=PDFOptions(page_size="A4"),
)

Performance

Current snapshot (2026-09-13): generic bin/gowkhtmltopdf (VERSION 0.2.6, release tree 8aab63a) versus installed wkhtmltopdf 0.12.6.1 (patched Qt) on Linux amd64 (WSL2), 13th Gen Intel Core i7-13700HX. Same report fixture (20 invoice rows per requested page), median of three timed process runs after one warmup.

PagesgowkhtmltopdfwkhtmltopdfFaster by
213 ms258 ms19.68x
1024 ms279 ms11.65x
100124 ms532 ms4.30x
500573 ms1.718 s3.00x

Faster at every tested size. Gowk also used less peak RSS at every tested size in this capture, including 500 pages (80,448 KiB versus 123,068 KiB).

Same host, same fixture family against other engines (default external matrix: 2 / 10 / 50 / 100 pages):

Pagesvs WeasyPrintvs Puppeteer / Chrome
249.18x111.73x
1059.78x61.63x
5082.04x26.65x
10088.33x17.40x

The public Go library (Document.WritePDF) measures 5.50 ms at 2 pages (about 47x against the same capture's 258 ms wkhtmltopdf baseline) and 554.56 ms at 500 pages; the Python bindings measure 3.52 ms and 504.93 ms (warm medians).

Full matrices, RSS, PDF sizes, internal-engine and public-library go test -bench rows, and historical snapshots:

Reproduce:

make build
make bench-cli-compare
./scripts/bench-external.sh
./scripts/bench-performance-recovery.sh --mode=<mode>
make bench
make bench-engine
make bench-lib
make python-benchmarks

make python-benchmarks rebuilds dist/libgowkhtmltopdf.so (CGO_ENABLED=1) and times the in-process Python Document.pdf() / ImageDocument.image() path on the same report.html.tmpl fixture (20 invoice rows per page) that make bench-lib uses. Override sizes with GOWKHTMLTOPDF_BENCH_SIZES=2,10,50. The Python architecture sample is make python-api (testdata/golden/python_api -> output/python/architecture-diagram.pdf).

Development

gowkhtmltopdf is a pure-Go rendering engine written from scratch. Humans own the architecture and domain design. AI tools including Grok, OpenAI Codex, OpenCode, and Cursor help with implementation drafts, visual checks against golden fixtures, and test suites.

Much of the code starts as an AI draft. It does not ship unchecked. Human maintainers own the pipeline of load, parse, style, layout, paginate, paint, and write. Humans review changes and require proof before merge: make test, make lint, make golden, and make claim-scan, and they also validate all 50+ sample templates manually.

Performance is part of that proof. The full record lives in documentation/performance.md. Raw numbers and reproduce steps live in testdata/golden/benchmarks/README.md. Profile with go test -cpuprofile plus go tool pprof -top; see How to measure. The CI perf budget is TestTenPageTableReportPerformance in internal/convert/perf_test.go.

License

MIT License — Copyright (c) 2026 Chinmay Sawant.

Bundled Liberation and DejaVu fonts are SIL OFL / Bitstream Vera; see internal/pdf/assets/NOTICE. The Noto KR test subset ships testdata/fonts/OFL.txt.

Contributors

chinmay-sawant

640 commits

Languages

Go

100.0%