Releases: chinmay-sawant/gowkhtmltopdf
Release list
v0.2.6 - Print CSS coverage and browser WASM
v0.2.6
Seventh public release of gowkhtmltopdf: a pure-Go, no-cgo, no Qt/WebKit, no browser HTML template engine that turns structured HTML and templates into multi-page PDFs and images.
v0.2.5 shipped the in-process Python path. v0.2.6 is the print CSS coverage release. The engine now implements 354 of the 818 W3C webref CSS properties, with 0 partial states and the remaining 464 tracked as unsupported. It also adds the browser WASM adapter for PDF, PNG, and JPEG output, recovers most of the warm-path time and allocation lost during the CSS work, and fixes table continuation, border joins, form widgets, list markers, and vertical text.
Default output is still unclaimed PDF 1.4. --pdf-version / Document.PDFVersion is a version header, not a conformance claim. The claim is --pdf-profile / Document.PDFProfile. The Python and C ABI surfaces keep their v0.2.5 contract, stamped 0.2.6.
- License: MIT - Copyright (c) 2026 Chinmay Sawant
- Version source:
VERSION(0.2.6) - Site: https://chinmay-sawant.github.io/gowkhtmltopdf/
- Live demo: https://chinmay-sawant.github.io/gowkhtmltopdf/#/live-demo
- Compare: v0.2.5...v0.2.6
- PRs: #62, #64, #65, #66, #67, #68, #69, #70, #71, #72
Highlights
| Area | What ships in v0.2.6 |
|---|---|
| Print CSS coverage | 354 implemented / 0 partial / 464 unsupported of 818 webref properties (plans/0.2.6/catalog/coverage-summary.json, generated 2026-09-12). The catalog, compatibility matrix, and apply arms are checked against each other. |
| Backgrounds and borders | Multi-layer background images with background-size (contain, cover, explicit lengths), background-position, background-repeat, background-clip, and background-origin; multi-layer box-shadow including inset; border-image; logical border longhands and logical corner radii; 1-4 value border shorthands; correct joins where border widths differ. |
| Text, transforms, and compositing | text-shadow, text-decoration-thickness, text-decoration-color, text-underline-offset, text-align-last, tab-size, manual hyphens; individual translate / rotate / scale / transform-box; mix-blend-mode and isolation run as real element transparency groups in PDF (Form XObjects with /Group /S /Transparency /I true) and PNG (group buffers). |
| Browser WASM | Inline HTML to PDF, PNG, or JPEG in the browser. This release attaches gowkhtmltopdf_0.2.6_wasm.wasm, wasm_exec_0.2.6.js, wasm_sample_0.2.6.html, and wasm_manifest_0.2.6.json. No local files, no arbitrary remote resources, no document JavaScript. |
| Tables and paged media | Header rows repeat on continuation pages, contain: size cells measure as one placeholder, page-break seals ignore transformed chrome, and fixture-63 pins break-before / break-after / break-inside behavior. |
| Performance | Current Snapshot N (2026-09-13): CLI 13 ms vs wkhtmltopdf 258 ms at 2 pages (19.68x) and 573 ms vs 1.718 s at 500 pages (3.00x), with lower peak RSS at every tested size. The warm 500-page path moved from the 1,228.72 ms recovery baseline to 576.33 ms in the same-source phase-6 capture (2.13x). |
| Golden corpus | Fixtures 57-63 added: implemented-props galleries (60-62), the unsupported worklist audit (58), the Apex landing page (59), and page-level demos (63). The corpus is 63 numbered fixtures plus 3 named harness pages. |
PDF 1.7 / 2.0 and the PDF/A + PDF/UA profiles from earlier releases are unchanged.
Showcase - browser demo
Live demo: https://chinmay-sawant.github.io/gowkhtmltopdf/#/live-demo
WASM guide: documentation/wasm.md
Paste inline HTML, convert to a PDF preview, and download any page as PNG or JPEG. Multi-page image exports arrive as a ZIP built in the browser. PNG keeps transparency with a checkerboard preview; JPEG composites onto white. The conversion runs in a worker through the same Go layout and paint code, compiled to GOOS=js GOARCH=wasm, with no server round trip.
Install / build
Cross-platform Go binaries are attached to this release (gowkhtmltopdf and gowkhtmltoimage for linux / windows / darwin on amd64 and arm64), plus SHA256SUMS and the four WASM artifacts.
Install the CLIs with Go 1.26+:
go install github.com/chinmay-sawant/gowkhtmltopdf/cmd/gowkhtmltopdf@v0.2.6
go install github.com/chinmay-sawant/gowkhtmltopdf/cmd/gowkhtmltoimage@v0.2.6
gowkhtmltopdf --versionLibrary pin:
go get github.com/chinmay-sawant/gowkhtmltopdf@v0.2.6Python (in-process; wheels are published from this tag):
pip install gowkhtmltopdffrom gowkhtmltopdf import PDFOptions, convert_html_to_pdf
pdf_bytes = convert_html_to_pdf(
b"<html><body><h1>Invoice #42</h1></body></html>",
options=PDFOptions(page_size="A4"),
)Default Go builds stay CGO_ENABLED=0. The shared library and Python wheels rebuild with CGO_ENABLED=1 make c-shared and a C toolchain.
What landed in v0.2.6
1. Print CSS coverage (PRs #62, #65, #70)
The catalog program mapped all 818 names in the webref CSS list onto the engine and closed every "partial" state:
- 354 implemented, 0 partial, 464 unsupported, 0 ignored (
plans/0.2.6/catalog/coverage-summary.json); 258 apply arms are mapped to code. - 24 previously demoted properties returned with real layout, paint, or shaping consumers. That includes
contain/contain-intrinsic-*/content-visibility, print-color-adjust and forced-color-adjust, image orientation and resolution,unicode-bidi,text-orientation,text-combine-upright, andfont-language-override. mix-blend-modeandisolationmoved to implemented with element-level transparency groups shared by the PDF and PNG writers.- 28 common
-webkit-*aliases remap to their standard properties (-webkit-box-sizing,-webkit-text-fill-color,-webkit-box-shadow,-webkit-border-radius, and more). documentation/compatibility-matrix.md,catalog/mapping.json, and the fixture evidence move together;scripts/css-catalog-map.py --checkguards drift.
2. Browser WASM adapter (PRs #68, #69, #71, #72)
bindings/wasmexports one bridge function,gowkhtmltopdfWASM(jsonRequest, progressCallback), returning PDF, PNG, or JPEG bytes plus width, height, MIME type, and version metadata.- The request accepts inline HTML, mode, page size, orientation, dimensions, padding, and quality. It has no file or URL fields, and the render runs with a network policy that allows zero schemes, local files off, and system fonts off.
- Limits are explicit: 4 MiB HTML, 32 MiB output, 4096 px per image axis, 60 seconds per conversion.
make wasmbuilds the version-stamped artifact into the site;make wasm-testruns the contract tests, the frontend lint / build / tests, and a Chrome smoke test for PDF, PNG, and JPEG. CI runs the same target.- The
/live-demoroute replaces the earlier/wasmroute and adds paginated previews and image downloads.
3. Performance and memory (PR #70)
- Style resolution is memoized with a declared-property mask,
ResolvedStylerecords are interned through generated fingerprint code, grid border ops batch intoOpGridRun, paint-range checks are O(1), and pagination skips census walks. - The PDF writer keeps parallel flate workers, a bounded parsed-font cache, lazy per-face parsing, and releases page raw buffers after stream materialization.
- Image output gained a direct final-resolution raster threshold, a streaming filter-none PNG writer, strip-window raster reuse, a bounded supersample cache, pooled encode buffers, and glyph scratch pools.
- Committed Snapshot M medians: 695.42 ms internal / 698.79 ms public library / 0.70 s CLI for 500 pages, with B/op at or below target. Allocated bytes per 500-page PDF fell from 321.10 MB (Snapshot K) to 163.02 MB (Snapshot M). The image 500-tile figure fell from 94.97 MB to 27.21 MB B/op, and PDF allocations dropped 94.5 percent across the memory wave.
- Current README capture (2026-09-13):
Document.WritePDFat 5.50 ms for 2 pages and 554.56 ms for 500 pages; Python at 3.52 ms and 504.93 ms.
4. Layout, tables, and correctness (PRs #64, #65, #66, #71)
theadrows repeat on every continuation page and table bodies no longer overlap the repeated band.- Table ce...
v0.2.5 - Python bindings and PyPI
v0.2.5
Sixth public release of gowkhtmltopdf: a pure-Go, no-cgo, no Qt/WebKit, no browser HTML template engine that turns structured HTML and templates into multi-page PDFs and images.
v0.2.4 shipped the Go-native Document / ImageDocument API and CLI redesign. v0.2.5 adds the missing in-process Python path: an opt-in cgo c-shared library with a frozen C ABI, plus a pip install gowkhtmltopdf package that loads it through ctypes. Default Go builds stay CGO_ENABLED=0. The pure-Go library and both CLIs do not change their public contract.
Default output is still unclaimed PDF 1.4. --pdf-version / Document.PDFVersion / Python pdf_version is a version header, not a conformance claim. The claim is --pdf-profile / Document.PDFProfile / Python pdf_profile.
- License: MIT - Copyright (c) 2026 Chinmay Sawant
- Version source:
VERSION(0.2.5) - Site: https://chinmay-sawant.github.io/gowkhtmltopdf/
- Python guide:
documentation/python.md - Compare: v0.2.4...v0.2.5
- PR: #58 (Python cgo bindings and PyPI for #35)
Highlights
| Area | What you get in v0.2.5 |
|---|---|
| C ABI | Frozen header bindings/c/include/gowkhtmltopdf.h: GOWKHTMLTOPDF_ABI_VERSION 1, size-gated option structs, status codes 0-6, gowkhtmltopdf_html_to_pdf / html_to_image + free helpers. |
| c-shared build | Opt-in only: CGO_ENABLED=1 make c-shared writes dist/libgowkhtmltopdf.so (or .dylib / .dll). Pure-Go make build / make test stay CGO_ENABLED=0. |
| Python package | pip install gowkhtmltopdf: Document / ImageDocument snake_case parity, convert_html_to_pdf / convert_file_to_pdf / convert_html_to_image, typed errors, zero runtime deps (ctypes). |
| Wheels | manylinux_2_28 x86_64 + aarch64; macOS arm64 (macos-latest); Windows. Tag v* triggers Trusted Publishing. |
| Samples | make samples-python regenerates output/python/; make python-api for architecture / inline / compliance; make python-benchmarks on the same dirty report template as make bench-lib. |
| Version stamp | VERSION / internal/cli.Version / Python package / header macro 0.2.5. Dated CHANGELOG 0.2.5 (2026-08-26). |
PDF 1.7 / 2.0 and PDF/A + PDF/UA profiles from earlier releases remain available on both the Go and Python surfaces.
Highest compliance by version (unchanged)
| Base | Archival | Accessibility | Opt-in |
|---|---|---|---|
| PDF 1.7 | PDF/A-3a | PDF/UA-1 | --pdf-profile a3a-ua1 / pdf_profile="a3a-ua1" |
| PDF 2.0 | PDF/A-4 | PDF/UA-2 | --pdf-profile a4-ua2 / pdf_profile="a4-ua2" |
Showcase - Python path
Live gallery: https://chinmay-sawant.github.io/gowkhtmltopdf/#/showcase
Python samples: output/python/
Guide: documentation/python.md
On the same dirty report.html.tmpl fixture (20 invoice rows per page), the in-process Python Document.pdf() path sits within a few percent of Go Document.WritePDF (about 3.7 ms at 2 pages on the reference host). That is the same engine behind a thin ctypes hop, not a second renderer.
Install / build
Cross-platform Go binaries are attached to this release (gowkhtmltopdf and gowkhtmltoimage for linux / windows / darwin x amd64 / arm64) plus SHA256SUMS.
Or install the CLIs with Go 1.26+:
go install github.com/chinmay-sawant/gowkhtmltopdf/cmd/gowkhtmltopdf@v0.2.5
go install github.com/chinmay-sawant/gowkhtmltopdf/cmd/gowkhtmltoimage@v0.2.5
gowkhtmltopdf --versionLibrary pin:
go get github.com/chinmay-sawant/gowkhtmltopdf@v0.2.5Python (in-process):
pip install gowkhtmltopdffrom gowkhtmltopdf import Document, Page, Content, PDFOptions, convert_html_to_pdf
pdf_bytes = convert_html_to_pdf(
b"<html><body><h1>Invoice #42</h1></body></html>",
options=PDFOptions(page_size="A4"),
)
doc = Document(
pages=[Page(source=Content(html=b"<html><body><h1>Invoice</h1></body></html>"))],
page_size="A4",
)
pdf_bytes = doc.pdf()From a source checkout (shared library for local Python):
git clone https://github.com/chinmay-sawant/gowkhtmltopdf.git
cd gowkhtmltopdf
git checkout v0.2.5
make build
CGO_ENABLED=1 make c-shared
pip install -e bindings/python
make samples-python
make python-benchmarksWhat landed in v0.2.5
1. C ABI and c-shared build (#58, #35)
- Committed header
bindings/c/include/gowkhtmltopdf.has the ABI source of truth. bindings/cis the only package allowed to containimport "C"; stub builds keepCGO_ENABLED=0green.- Exports wrap
Document.WritePDF/ImageDocument.WriteImagewith timeout and status mapping. make c-sharedrefuses to run unlessCGO_ENABLED=1.
2. Python package and PyPI (#58)
bindings/python:Document/ImageDocumentparity, helpers, exceptions,py.typed.convert_file_to_pdfsets afile://base so linked CSS (fixture-56) resolves.- CI: purity guard,
build-shared,python-bindingjobs; publish workflow for wheels onv*tags. make check-versionskeepsVERSIONandpyproject.tomlaligned.
3. Samples, benches, and docs (#58)
testdata/golden/python_api/: architecture diagram generator, inline invoice sample, compliance smokes, fullgenerate_samples.py.make python-api,make samples-python,make python-benchmarks.documentation/python.mdplus README / deferred / samples index updates.- Bulk
output/python/fixture-*.pdfgitignored; fixture-55 / fixture-56 kept as committed smoke exceptions.
Documentation
Known limits (honest)
- v1 one-shot ABI is inline-HTML oriented. File sources are read in Python and passed as HTML with a
file://base. URL sources and header/footer HTML companions are not on the one-shot path yet. font_paths/ system font flags are not onGwkPdfOptions, somake samples-pythonsoft-skips fixture-27 (CJK font-path).- Go default builds remain pure-Go. Rebuilding the shared library from source still needs a C toolchain and
CGO_ENABLED=1.
What's Changed
- docs: lead product copy with PDF engine tagline by @chinmay-sawant in #54
- docs(readme): ship 0.2.4 library wording and 2026-08-19 benches by @chinmay-sawant in #55
- fix(architecture): close 0.2.4 improve-codebase ledger by @chinmay-sawant in #57
- feat(python): add cgo c-shared bindings and PyPI package for #35 by @chinmay-sawant in #58
- fix(pypi): repair cibuildwheel project root and macos matrix by @chinmay-sawant in #59
- fix(pypi): force platform wheel tags for ctypes-native lib by @chinmay-sawant in #60
- fix(pypi): put ctypes native lib in platlib for auditwheel by @chinmay-sawant in #61
Full Changelog: v0.2.4...v0.2.5
v0.2.4 - Document API, CLI redesign, and external benches
v0.2.4
Fifth public release of gowkhtmltopdf: a pure-Go, no-cgo, no Qt/WebKit, no browser HTML template engine that turns structured HTML and templates into multi-page PDFs and images.
v0.2.3 was the go install / GitHub module-path tag on the same engine as v0.2.2. v0.2.4 is an intentional pre-1.0 hard break of the public library and both CLIs: the wkhtml-shaped Converter / dotted Set/Get / typed request wrappers are gone; the product surface is a Go-native Document / ImageDocument model with a matching CLI grammar. Layout and PDF writers stay; the outer contract and external bench paths change.
Default output is still unclaimed PDF 1.4. --pdf-version / Document.PDFVersion is a version header, not a conformance claim. The claim is --pdf-profile / Document.PDFProfile. Encryption, AcroForm, signatures, and JavaScript remain out of scope.
- License: MIT — Copyright (c) 2026 Chinmay Sawant
- Version source:
VERSION(0.2.4) - Site: https://chinmay-sawant.github.io/gowkhtmltopdf/
- Migration:
documentation/MIGRATION-0.2.4.md - Compare: v0.2.3...v0.2.4
- PR: #53 (Document API, CLI redesign, benches, docs)
Highlights
| Area | What you get in v0.2.4 |
|---|---|
| Document API | Root-package Document / ImageDocument, explicit Content sources (HTML / File / URL), Page / Cover / TOC, named option fields, validation before engine work, writer-first WritePDF / WriteImage plus byte helpers PDF / Image. |
| Hard break | Public Converter, ImageConverter, dotted settings, and PDFRequest / ImageRequest / RunPDF / RunImage are removed (no compat package). |
| CLI redesign | Both binaries use required -o/--output, positional page files, --html / --url, --cover / --toc, and --allow-local-files. The old page / cover / toc object grammar is rejected. |
| Migration guide | Step-by-step library + CLI mapping in MIGRATION-0.2.4.md. |
| External benches | Frozen three-engine process compares: wkhtmltopdf (make bench-cli-compare), WeasyPrint + Puppeteer (make bench / scripts/bench-external.sh). Optional host tools skip with evidence. Artifacts under testdata/golden/benchmarks/. |
| Layout fixes | Inline highlight backgrounds bounded to glyph runs; sticky continuation chrome trimmed across page breaks; related goldens refreshed. |
| Docs & site | Library API, CLI, getting started, architecture, examples, and the documentation site updated for the Document model and new bench presentation. |
| Version stamp | VERSION / internal/cli.Version / dated CHANGELOG 0.2.4 (2026-08-18). |
PDF 1.7 / 2.0 and PDF/A + PDF/UA profiles from v0.2.2 remain available on the new surface (Document.PDFVersion / Document.PDFProfile and the matching CLI flags).
Highest compliance by version (unchanged from v0.2.2 / v0.2.3)
| Base | Archival | Accessibility | Opt-in |
|---|---|---|---|
| PDF 1.7 | PDF/A-3a | PDF/UA-1 | --pdf-profile a3a-ua1 |
| PDF 2.0 | PDF/A-4 | PDF/UA-2 | --pdf-profile a4-ua2 |
Showcase — Document grammar and benches
Live gallery: https://chinmay-sawant.github.io/gowkhtmltopdf/#/showcase
Benchmarks page: https://chinmay-sawant.github.io/gowkhtmltopdf/#/benchmarks
Committed PDFs: output/ · source HTML: testdata/golden/
Process-compare artifacts (shared report fixture; medians after warmup — not an SLA):
| Compare | Reproduce | Snapshot speedup (indicative) |
|---|---|---|
| vs wkhtmltopdf | make bench-cli-compare |
~15× at 2 pages → ~1.6× at 500 pages |
| vs WeasyPrint | make bench |
tens of × across 2–100 pages |
| vs Puppeteer / Chrome | make bench |
faster process wall time on the fixture (RSS methods differ — read artifact footnotes) |
Tables and footnotes: testdata/golden/benchmarks/.
Install / build
Cross-platform binaries are attached to this release (gowkhtmltopdf and gowkhtmltoimage for linux / windows / darwin × amd64 / arm64) plus SHA256SUMS.
Or install the CLIs with Go 1.26+ (puts them on GOBIN / $(go env GOPATH)/bin):
go install github.com/chinmay-sawant/gowkhtmltopdf/cmd/gowkhtmltopdf@v0.2.4
go install github.com/chinmay-sawant/gowkhtmltopdf/cmd/gowkhtmltoimage@v0.2.4
gowkhtmltopdf --versionLibrary pin:
go get github.com/chinmay-sawant/gowkhtmltopdf@v0.2.4From a source checkout:
git clone https://github.com/chinmay-sawant/gowkhtmltopdf.git
cd gowkhtmltopdf
git checkout v0.2.4
make buildLocal PDF (0.2.4 CLI grammar):
./bin/gowkhtmltopdf --allow-local-files -o /tmp/invoice.pdf \
testdata/golden/fixture-01-simple-invoice.htmlInline HTML / cover + TOC:
gowkhtmltopdf -o out.pdf --html '<h1>Hi</h1>'
gowkhtmltopdf --allow-local-files --cover cover.html --toc -o book.pdf chapter.html
gowkhtmltoimage --allow-local-files -o page.png page.htmlUnclaimed PDF 1.7:
./bin/gowkhtmltopdf --pdf-version 1.7 --allow-local-files \
-o /tmp/report-17.pdf testdata/golden/fixture-21-detailed-report.htmlDual PDF/A-3a + PDF/UA-1 (implies 1.7):
./bin/gowkhtmltopdf --pdf-profile a3a-ua1 --allow-local-files \
-o /tmp/arch-a3a-ua1.pdf testdata/golden/fixture-56-architecture-diagram.htmlLibrary (Document API):
package main
import (
"context"
"os"
"github.com/chinmay-sawant/gowkhtmltopdf"
)
func main() {
ctx := context.Background()
doc := gowkhtmltopdf.Document{
Pages: []gowkhtmltopdf.Page{{
Source: gowkhtmltopdf.Content{File: "report.html"},
}},
PageSize: "A4",
PDFVersion: "1.7", // optional: "1.4" (default), "1.7", "2.0"
PDFProfile: "a3a-ua1", // optional; implies 1.7
Title: "Quarterly report",
AllowLocalFiles: true,
}
out, err := os.Create("output.pdf")
if err != nil {
panic(err)
}
defer out.Close()
if err := doc.WritePDF(ctx, out); err != nil {
panic(err)
}
}CLI / document fields: --pdf-version / Document.PDFVersion (1.4, 1.7, 2.0); --pdf-profile / Document.PDFProfile (a3a, ua1, a3a-ua1, a4, ua2, a4-ua2, or the canonical PDF/A-… / PDF/UA-… names).
What landed in v0.2.4
1. Document / ImageDocument public API (#53)
- Add
Document,ImageDocument,Content,Page,TOC, options structs, and validation at the public boundary. - Writer-first
WritePDF/WriteImage; byte-returningPDF/Image; explicitWritePDFOutlinewhen an outline sink is needed. - Delete the wkhtml-shaped public surface (
Converter, dottedSet/Get,PDFRequest/ImageRequest,RunPDF/RunImage) with no compatibility shim. - Engine adapters remain on
internal/convert/internal/imageout. Document order is Cover → TOC → Pages.
2. CLI redesign (#53)
- Required
-o/--output; positional page files;--html/--url;--cover/--toc. --allow-local-filesreplaces--enable-local-file-access.- Old object grammar (
page/cover/tockeywords, final positional output) is no longer accepted. - Sample and benchmark commands use the native 0.2.4 grammar.
3. External benchmarks and harness freeze (#53)
make bench-cli-compare→ wkhtmltopdf process compare.make bench→ WeasyPrint + Puppeteer viascripts/bench-external.sh, then wkhtmltopdf.make bench-lib/ related targets exercise the publicDocument.WritePDFpath.- Checked-in snapshots under
testdata/golden/benchmarks/{cli,weasyprint,puppeteer}-compare.md(+ CSV).
4. Layout correctness (#53)
- Bound inline highlight backgrounds so paint does not spill past glyph runs.
- Trim sticky continuation chrome across page breaks.
- Refresh related golden / fixture outputs and showcase screenshots.
5. Docs, examples, VERSION, CHANGELOG, site (#53)
- Ship
documentation/MIGRATION-0.2.4.md. - Refresh library API, CLI, getting started, architecture, and
examples/pdf+examples/image. - Frontend / GitHub Pages copy for Document API and benchmarks.
VERSION=0.2.4; datedCHANGELOG.md0.2.4 (2026-08-18).
Documentation
v0.2.3
v0.2.3
Same engine as v0.2.2: a pure-Go, no-cgo, no Qt/WebKit, no browser HTML template engine with opt-in PDF 1.7 / 2.0 and PDF/A + PDF/UA profiles.
This is the tag to go install. Module path is github.com/chinmay-sawant/gowkhtmltopdf.
Default output is still unclaimed PDF 1.4. --pdf-version / WithPDFVersion is a version header, not a conformance claim. The claim is --pdf-profile / WithPDFProfile. Encryption, AcroForm, signatures, and JavaScript remain out of scope.
- License: MIT — Copyright (c) 2026 Chinmay Sawant
- Version source:
VERSION(0.2.3) - Site: https://chinmay-sawant.github.io/gowkhtmltopdf/
- Compare: v0.2.1...v0.2.3
- Epic: #29 (newer PDF versions and compliance)
- PRs since v0.2.1: #44 (v0.2.1 release title) · #45 (PDF 1.7 + PDF/A-3a + PDF/UA-1, #31) · #46 (PDF 2.0 + PDF/A-4 + PDF/UA-2, #32/#33) · #47 (criticality, sentinels, UA list nesting) · #48 (docs, site, samples) · #49 (lint / ObjRef) · #50 (release prep) · #51 (GitHub module path /
go install)
Highlights
| Area | What you get in v0.2.3 |
|---|---|
| PDF 1.7 | Opt-in %PDF-1.7 via --pdf-version 1.7 / WithPDFVersion("1.7"). Version only — no PDF/A or PDF/UA claim. |
| PDF 2.0 | Opt-in %PDF-2.0 via --pdf-version 2.0 / WithPDFVersion("2.0") (trailer /ID, UTF-8 document strings, non-claiming XMP). Version only. |
| PDF/A-3a + PDF/UA-1 | Opt-in --pdf-profile a3a-ua1 (also a3a, ua1). Implies PDF 1.7. Claiming XMP (pdfaid:part=3, pdfaid:conformance=A, pdfuaid:part=1), sRGB OutputIntent, /DefaultRGB, MarkInfo, and a logical structure tree. Multi-page structure elements emit MCR dictionaries. |
| PDF/A-4 + PDF/UA-2 | Opt-in --pdf-profile a4-ua2 (also a4, ua2). Implies PDF 2.0. Claiming XMP (pdfaid:part=4 / rev=2020, pdfuaid:part=2 / rev=2024), sRGB+Gray OutputIntent, structure /Namespace, ListNumbering, and dual named destinations (/D page + /SD structure). PDF/A-4 omits trailer /Info (metadata lives in XMP). |
| Tagged PDF wiring | Cloned-page MCIDs, link/outline /SD identity, single /Document, header/footer isolation from the body tree. Lists nest L → LI → LBody → Link (inline <a> inside <li> is no longer a sibling of LI). CIDFontType2 /FontName matches parent /BaseFont. |
| Profile Get and sentinels | Get("pdfprofile") returns the canonical token (a3a-ua1 → PDF/A-3a+PDF/UA-1). Wrong version + profile uses ErrConformanceRequiresPDF17 / ErrConformanceRequiresPDF20. |
| Samples | make samples writes unclaimed output/pdf-1.7/ and output/pdf-2.0/ plus claimed output/pdf-1.7-compliance/ (a3a-ua1) and output/pdf-2.0-compliance/ (a4-ua2). |
| Docs & site | Guides and the documentation site state the 0.2.1 / 0.2.2 split honestly. README brand mark, site a11y, and issue-dossier verdicts updated. |
Highest compliance by version
| Base | Archival | Accessibility | Opt-in |
|---|---|---|---|
| PDF 1.7 | PDF/A-3a | PDF/UA-1 | --pdf-profile a3a-ua1 |
| PDF 2.0 | PDF/A-4 | PDF/UA-2 | --pdf-profile a4-ua2 |
Showcase — version and compliance samples
Live gallery: https://chinmay-sawant.github.io/gowkhtmltopdf/#/showcase
Committed PDFs: output/ · source HTML: testdata/golden/
The existing 64 template fixtures remain. New trees (same two fixtures: detailed report + architecture diagram):
| Dir | How produced | Claim? |
|---|---|---|
output/pdf-1.7/ |
--pdf-version 1.7 |
No — version only |
output/pdf-1.7-compliance/ |
--pdf-profile a3a-ua1 |
PDF/A-3a + PDF/UA-1 |
output/pdf-2.0/ |
--pdf-version 2.0 |
No — version only |
output/pdf-2.0-compliance/ |
--pdf-profile a4-ua2 |
PDF/A-4 + PDF/UA-2 |
These are artifacts, not golden byte baselines. A version flag is not a conformance claim.
Install / build
Cross-platform binaries are attached to this release (gowkhtmltopdf and gowkhtmltoimage for linux / windows / darwin × amd64 / arm64) plus SHA256SUMS.
Or install the CLIs with Go 1.26+ (puts them on GOBIN / $(go env GOPATH)/bin):
go install github.com/chinmay-sawant/gowkhtmltopdf/cmd/gowkhtmltopdf@v0.2.3
go install github.com/chinmay-sawant/gowkhtmltopdf/cmd/gowkhtmltoimage@v0.2.3
gowkhtmltopdf --versionFrom a source checkout:
git clone https://github.com/chinmay-sawant/gowkhtmltopdf.git
cd gowkhtmltopdf
git checkout v0.2.3
make buildUnclaimed PDF 1.7:
./bin/gowkhtmltopdf --pdf-version 1.7 --enable-local-file-access \
testdata/golden/fixture-21-detailed-report.html /tmp/report-17.pdfDual PDF/A-3a + PDF/UA-1 (implies 1.7):
./bin/gowkhtmltopdf --pdf-profile a3a-ua1 --enable-local-file-access \
testdata/golden/fixture-56-architecture-diagram.html /tmp/arch-a3a-ua1.pdfDual PDF/A-4 + PDF/UA-2 (implies 2.0):
./bin/gowkhtmltopdf --pdf-profile a4-ua2 --enable-local-file-access \
testdata/golden/fixture-21-detailed-report.html /tmp/report-a4-ua2.pdfLibrary (preferred typed API):
package main
import (
"bytes"
"context"
"os"
"github.com/chinmay-sawant/gowkhtmltopdf"
)
func main() {
ctx := context.Background()
var out bytes.Buffer
req := &gowkhtmltopdf.PDFRequest{
Global: gowkhtmltopdf.NewPdfGlobalOptions().
WithPageSize("A4").
WithPDFVersion("1.7"). // optional: "1.4" (default), "1.7", "2.0"
WithPDFProfile("a3a-ua1"). // optional; implies 1.7
WithTitle("Quarterly report").
Build(),
Objects: []*gowkhtmltopdf.ObjectSettings{
gowkhtmltopdf.NewObjectSettings().SetPage("report.html"),
},
Output: &out,
}
req.EnableLocalFileAccess()
if err := gowkhtmltopdf.RunPDF(ctx, req); err != nil {
panic(err)
}
_ = os.WriteFile("output.pdf", out.Bytes(), 0644)
}CLI / settings keys: --pdf-version / pdfversion (1.4, 1.7, 2.0); --pdf-profile / pdfprofile (a3a, ua1, a3a-ua1, a4, ua2, a4-ua2, or the canonical PDF/A-… / PDF/UA-… names).
What landed (same as v0.2.2, plus #50–#51)
1. PDF 1.7 version path and 1.7 profiles (#45, #31)
- Writer policy for
%PDF-1.7on an explicit version flag. - Opt-in PDF/A-3a, PDF/UA-1, and dual
a3a-ua1via--pdf-profile/WithPDFProfile(implies PDF 1.7). - Claiming XMP, sRGB OutputIntent,
/DefaultRGB, MarkInfo, logical structure tree. - Multi-page structure elements emit MCR dictionaries (ISO 32000-1 §14.7.4.2).
- CIDFontType2
/FontNameequals parent/BaseFont(ArlingtonFontDescriptorCIDType2.FontName). - Layout tagging no longer accumulates one document-wide mega-
/Pwith duplicate bare MCIDs.
2. PDF 2.0 version path and 2.0 profiles (#46, #32 / #33)
- Writer policy for
%PDF-2.0: header + binary comment, trailer/ID, UTF-8 document strings, non-claiming XMP (dc:format,pdf:Producer, dates — nopdfaid/pdfuaid). - Opt-in PDF/A-4, PDF/UA-2, and dual
a4-ua2(implies PDF 2.0). - PDF/A-4:
pdfaid:part=4/rev=2020, sRGB + Gray OutputIntent,/DefaultRGB+/DefaultGray, trailer omits/Info. - PDF/UA-2:
pdfuaid:part=2/rev=2024, structure/Namespace(http://iso.org/pdf2/ssn), ListNumbering on/L, catalog/Lang//MarkInfo//ViewerPreferences << /DisplayDocTitle true >>. - Dual named destinations:
/Dpage XYZ (Arlington / PDF/A) plus/SDstructure dest (UA-2 clause 8.8). Outline items also bind/SEto the heading struct elem.
3. Tagged-PDF wiring, contracts, and cost (#47)
- Cloned pages keep MCIDs and content refs; header/footer links stay pagination artifacts (not under
/Document). - One
/Document; outline/SDmatches heading identity; internal and TOC links resolve dest struct elems. - New leaf
internal/pdfprofile: one alias table.WithPDFVersion/WithPDFProfilestore canonical tokens.Get("pdfprofile")after"a3a-ua1"isPDF/A-3a+PDF/UA-1. - Unified
ErrConformanceRequiresPDF17/ErrConformanceRequiresPDF20(withErrProfileRequiresPDF17/ErrProfileRequiresPDF20aliases).ErrProfilePDF20Unsupportedremains defined for source compatibility but is never returned. - Default unclaimed 1.4 stays isolated: no XMP, ICC, StructTree, ParentTree, named dests,
/Tabs /S, or trailer/ID. - ICC Flate bytes precomputed; structure serialize tightened; write benches cover all nine profile combinations.
4. UA list nesting (#47)
Inline <a> inside <li> used to emit L > Link (illegal under ISO 32000-1 / ISO 32005). Links now live under LI / LBody. Regression: TestStructureTreeListLinkHierarchy.
5. Docs, site, and samples (#44, #48)
- v0.2.1 Git...
v0.2.2
v0.2.2
Fourth public release of gowkhtmltopdf: a pure-Go, no-cgo, no Qt/WebKit, no browser HTML template engine that turns structured HTML and templates into multi-page PDFs and images.
v0.2.0 shipped the print-CSS template pipeline. v0.2.1 hardened embedder contracts and layout. v0.2.2 extends the existing PDF writer with an explicit version policy and opt-in archival / accessibility profiles. It does not rebuild layout, fonts, or the convert pipeline.
Default output is still unclaimed PDF 1.4. --pdf-version / WithPDFVersion is a version header, not a conformance claim. The claim is --pdf-profile / WithPDFProfile. Encryption, AcroForm, signatures, and JavaScript remain out of scope.
- License: MIT — Copyright (c) 2026 Chinmay Sawant
- Version source:
VERSION(0.2.2) - Site: https://chinmay-sawant.github.io/gowkhtmltopdf/
- Compare: v0.2.1...v0.2.2
- Epic: #29 (newer PDF versions and compliance)
- PRs since v0.2.1: #44 (v0.2.1 release title) · #45 (PDF 1.7 + PDF/A-3a + PDF/UA-1, #31) · #46 (PDF 2.0 + PDF/A-4 + PDF/UA-2, #32/#33) · #47 (criticality, sentinels, UA list nesting) · #48 (docs, site, samples) · #49 (lint / ObjRef) · #50 (release prep) · #51 (GitHub module path so
go installworks)
Highlights
| Area | What you get in v0.2.2 |
|---|---|
| PDF 1.7 | Opt-in %PDF-1.7 via --pdf-version 1.7 / WithPDFVersion("1.7"). Version only — no PDF/A or PDF/UA claim. |
| PDF 2.0 | Opt-in %PDF-2.0 via --pdf-version 2.0 / WithPDFVersion("2.0") (trailer /ID, UTF-8 document strings, non-claiming XMP). Version only. |
| PDF/A-3a + PDF/UA-1 | Opt-in --pdf-profile a3a-ua1 (also a3a, ua1). Implies PDF 1.7. Claiming XMP (pdfaid:part=3, pdfaid:conformance=A, pdfuaid:part=1), sRGB OutputIntent, /DefaultRGB, MarkInfo, and a logical structure tree. Multi-page structure elements emit MCR dictionaries. |
| PDF/A-4 + PDF/UA-2 | Opt-in --pdf-profile a4-ua2 (also a4, ua2). Implies PDF 2.0. Claiming XMP (pdfaid:part=4 / rev=2020, pdfuaid:part=2 / rev=2024), sRGB+Gray OutputIntent, structure /Namespace, ListNumbering, and dual named destinations (/D page + /SD structure). PDF/A-4 omits trailer /Info (metadata lives in XMP). |
| Tagged PDF wiring | Cloned-page MCIDs, link/outline /SD identity, single /Document, header/footer isolation from the body tree. Lists nest L → LI → LBody → Link (inline <a> inside <li> is no longer a sibling of LI). CIDFontType2 /FontName matches parent /BaseFont. |
| Profile Get and sentinels | Get("pdfprofile") returns the canonical token (a3a-ua1 → PDF/A-3a+PDF/UA-1). Wrong version + profile uses ErrConformanceRequiresPDF17 / ErrConformanceRequiresPDF20. |
| Samples | make samples writes unclaimed output/pdf-1.7/ and output/pdf-2.0/ plus claimed output/pdf-1.7-compliance/ (a3a-ua1) and output/pdf-2.0-compliance/ (a4-ua2). |
| Docs & site | Guides and the documentation site state the 0.2.1 / 0.2.2 split honestly. README brand mark, site a11y, and issue-dossier verdicts updated. |
Highest compliance by version
| Base | Archival | Accessibility | Opt-in |
|---|---|---|---|
| PDF 1.7 | PDF/A-3a | PDF/UA-1 | --pdf-profile a3a-ua1 |
| PDF 2.0 | PDF/A-4 | PDF/UA-2 | --pdf-profile a4-ua2 |
Showcase — version and compliance samples
Live gallery: https://chinmay-sawant.github.io/gowkhtmltopdf/#/showcase
Committed PDFs: output/ · source HTML: testdata/golden/
The existing 64 template fixtures remain. New trees (same two fixtures: detailed report + architecture diagram):
| Dir | How produced | Claim? |
|---|---|---|
output/pdf-1.7/ |
--pdf-version 1.7 |
No — version only |
output/pdf-1.7-compliance/ |
--pdf-profile a3a-ua1 |
PDF/A-3a + PDF/UA-1 |
output/pdf-2.0/ |
--pdf-version 2.0 |
No — version only |
output/pdf-2.0-compliance/ |
--pdf-profile a4-ua2 |
PDF/A-4 + PDF/UA-2 |
These are artifacts, not golden byte baselines. A version flag is not a conformance claim.
Install / build
Cross-platform binaries are attached to this release (gowkhtmltopdf and gowkhtmltoimage for linux / windows / darwin × amd64 / arm64) plus SHA256SUMS.
Or install the CLIs with Go 1.26+ (puts them on GOBIN / $(go env GOPATH)/bin):
go install github.com/chinmay-sawant/gowkhtmltopdf/cmd/gowkhtmltopdf@v0.2.2
go install github.com/chinmay-sawant/gowkhtmltopdf/cmd/gowkhtmltoimage@v0.2.2
gowkhtmltopdf --versionFrom a source checkout:
git clone https://github.com/chinmay-sawant/gowkhtmltopdf.git
cd gowkhtmltopdf
git checkout v0.2.2
make buildUnclaimed PDF 1.7:
./bin/gowkhtmltopdf --pdf-version 1.7 --enable-local-file-access \
testdata/golden/fixture-21-detailed-report.html /tmp/report-17.pdfDual PDF/A-3a + PDF/UA-1 (implies 1.7):
./bin/gowkhtmltopdf --pdf-profile a3a-ua1 --enable-local-file-access \
testdata/golden/fixture-56-architecture-diagram.html /tmp/arch-a3a-ua1.pdfDual PDF/A-4 + PDF/UA-2 (implies 2.0):
./bin/gowkhtmltopdf --pdf-profile a4-ua2 --enable-local-file-access \
testdata/golden/fixture-21-detailed-report.html /tmp/report-a4-ua2.pdfLibrary (preferred typed API):
package main
import (
"bytes"
"context"
"os"
"github.com/chinmay-sawant/gowkhtmltopdf"
)
func main() {
ctx := context.Background()
var out bytes.Buffer
req := &gowkhtmltopdf.PDFRequest{
Global: gowkhtmltopdf.NewPdfGlobalOptions().
WithPageSize("A4").
WithPDFVersion("1.7"). // optional: "1.4" (default), "1.7", "2.0"
WithPDFProfile("a3a-ua1"). // optional; implies 1.7
WithTitle("Quarterly report").
Build(),
Objects: []*gowkhtmltopdf.ObjectSettings{
gowkhtmltopdf.NewObjectSettings().SetPage("report.html"),
},
Output: &out,
}
req.EnableLocalFileAccess()
if err := gowkhtmltopdf.RunPDF(ctx, req); err != nil {
panic(err)
}
_ = os.WriteFile("output.pdf", out.Bytes(), 0644)
}CLI / settings keys: --pdf-version / pdfversion (1.4, 1.7, 2.0); --pdf-profile / pdfprofile (a3a, ua1, a3a-ua1, a4, ua2, a4-ua2, or the canonical PDF/A-… / PDF/UA-… names).
What landed in v0.2.2
1. PDF 1.7 version path and 1.7 profiles (#45, #31)
- Writer policy for
%PDF-1.7on an explicit version flag. - Opt-in PDF/A-3a, PDF/UA-1, and dual
a3a-ua1via--pdf-profile/WithPDFProfile(implies PDF 1.7). - Claiming XMP, sRGB OutputIntent,
/DefaultRGB, MarkInfo, logical structure tree. - Multi-page structure elements emit MCR dictionaries (ISO 32000-1 §14.7.4.2).
- CIDFontType2
/FontNameequals parent/BaseFont(ArlingtonFontDescriptorCIDType2.FontName). - Layout tagging no longer accumulates one document-wide mega-
/Pwith duplicate bare MCIDs.
2. PDF 2.0 version path and 2.0 profiles (#46, #32 / #33)
- Writer policy for
%PDF-2.0: header + binary comment, trailer/ID, UTF-8 document strings, non-claiming XMP (dc:format,pdf:Producer, dates — nopdfaid/pdfuaid). - Opt-in PDF/A-4, PDF/UA-2, and dual
a4-ua2(implies PDF 2.0). - PDF/A-4:
pdfaid:part=4/rev=2020, sRGB + Gray OutputIntent,/DefaultRGB+/DefaultGray, trailer omits/Info. - PDF/UA-2:
pdfuaid:part=2/rev=2024, structure/Namespace(http://iso.org/pdf2/ssn), ListNumbering on/L, catalog/Lang//MarkInfo//ViewerPreferences << /DisplayDocTitle true >>. - Dual named destinations:
/Dpage XYZ (Arlington / PDF/A) plus/SDstructure dest (UA-2 clause 8.8). Outline items also bind/SEto the heading struct elem.
3. Tagged-PDF wiring, contracts, and cost (#47)
- Cloned pages keep MCIDs and content refs; header/footer links stay pagination artifacts (not under
/Document). - One
/Document; outline/SDmatches heading identity; internal and TOC links resolve dest struct elems. - New leaf
internal/pdfprofile: one alias table.WithPDFVersion/WithPDFProfilestore canonical tokens.Get("pdfprofile")after"a3a-ua1"isPDF/A-3a+PDF/UA-1. - Unified
ErrConformanceRequiresPDF17/ErrConformanceRequiresPDF20(withErrProfileRequiresPDF17/ErrProfileRequiresPDF20aliases).ErrProfilePDF20Unsupportedremains defined for source compatibility but is never returned. - Default unclaimed 1.4 stays isolated: no XMP, ICC, StructTree, ParentTree, named dests,
/Tabs /S, or trailer/ID. - ICC Flate bytes precomputed; structure serialize tightened; write benches cover all nine profile combinations.
4. UA list nesting (#47)
Inline <a> inside <li> used to emit L > Link (illegal under ISO 32000-1 / ISO 32005). Links now live under LI / LBody. Regression: TestStructureTreeListLinkHierarchy.
5. Docs, site, and sa...
v0.2.1 - Production hardening: library contracts, layout fidelity, continuous fuzzing
v0.2.1
Third public release of gowkhtmltopdf: a pure-Go, no-cgo, no Qt/WebKit, no browser HTML template engine that turns structured HTML and templates into multi-page PDFs and images.
While v0.2.0 completed the core typography, print CSS, and layout pipeline, v0.2.1 is a stability and hardening release focused on embedder crash-safety, print layout precision, trust boundary unification, and arbitrary input resilience.
- License: MIT — Copyright (c) 2026 Chinmay Sawant
- Version source:
VERSION(0.2.1) - Site: https://chinmay-sawant.github.io/gowkhtmltopdf/
- Compare: v0.2.0...v0.2.1
- PRs: #41 (v0.2.1 roadmap and release closure) · #42 (fixture header alignment)
Highlights
| Area | What you get in v0.2.1 |
|---|---|
| Crash safety | Fluent builders (WithPageSize, WithCopies) no longer panic on invalid input; errors return cleanly via ValidatePDF / RunPDF sentinels (ErrInvalidPageSize, ErrInvalidPDFCopies). AddHTML safely guards against nil receivers. |
| Local file access | Added convenient EnableLocalFileAccess() helpers across PDFRequest, ImageRequest, Converter, GlobalSettings, and ObjectSettings, replacing the confusing two-key string requirement. |
| Multi-page tables | Multi-page border-collapse tables with rowspan emit clean closed top edges across continuation pages while preserving continuous rowspan cells. |
| Flow layout | Multiple same-side floats cleanly stack vertically without overlapping; block formatting context (BFC) enclosure verified; flex shrink item widths and grid row heights are deterministic. |
| Fuzz testing | Added continuous native Go fuzz targets (FuzzParseHTML, FuzzParseCSS, FuzzConvertHTML) with 400,000+ random mutations verifying parser and conversion robustness against untrusted input. |
| Security boundary | Canonical NetworkPolicy type consolidated in internal/load with ApplyNetworkPolicy helper; public NetworkPolicy is a clean type alias. Decoupled PDF and Image conversion request structs. |
| Showcase & docs | Refreshed 167 high-resolution PNG page previews and 167 WebP thumbnails across all 64 sample PDFs. Documentation site and release metadata 100% aligned to v0.2.1. |
| CI integration | GitHub Actions CI now triggers on both master and main branches, ensuring all future PRs and merges are continuously tested and linted. |
Showcase — verified document templates
Live gallery: https://chinmay-sawant.github.io/gowkhtmltopdf/#/showcase
Committed PDFs: output/ · source HTML: testdata/golden/
All 64 sample documents have been generated and validated with zero CGO or external browser dependencies:
| Category | What you can open |
|---|---|
| Invoices & receipts | Simple / CSS invoices, receipts, purchase orders, contracts, letters, shipping documents, airline boarding passes |
| Reports & tables | Detailed ops reports, multi-page tables with repeating <thead> and continuation borders, sticky print headers, colorful reports |
| Storybooks & posters | Asteria and Ember Harbor storybooks, night-train and observatory posters, certificates |
| CSS & layout fixtures | Flex, grid, float, sticky, multicol, :has(), @container, transforms, CJK + --font-path, nested HTML headers/footers |
| Architecture & API | Library architecture diagram, 20-page HTML+CSS architecture doc, font-examples (1,125 Google Fonts via --font-path), complex dossier |
Install / build
Cross-platform binaries are attached to this release (gowkhtmltopdf and gowkhtmltoimage for linux / windows / darwin × amd64 / arm64) plus SHA256SUMS.
From source (Go 1.26+):
git clone https://github.com/chinmay-sawant/gowkhtmltopdf.git
cd gowkhtmltopdf
git checkout v0.2.1
make buildConvert any committed template:
./bin/gowkhtmltopdf --enable-local-file-access \
testdata/golden/fixture-01-simple-invoice.html /tmp/invoice.pdfLibrary (preferred typed API):
package main
import (
"bytes"
"context"
"os"
"gowkhtmltopdf"
)
func main() {
ctx := context.Background()
var out bytes.Buffer
req := gowkhtmltopdf.NewPDFRequest().EnableLocalFileAccess()
req.Objects = []*gowkhtmltopdf.ObjectSettings{
gowkhtmltopdf.NewObjectSettings().SetPage("invoice.html"),
}
req.Output = &out
if err := gowkhtmltopdf.RunPDF(ctx, req); err != nil {
panic(err)
}
_ = os.WriteFile("output.pdf", out.Bytes(), 0644)
}What landed in v0.2.1
1. Library API & Crash-Safety
- Panic-Free Option Validation:
PdfGlobalOptions.WithPageSizeandPdfGlobalOptions.WithCopiesno longer panic on malformed user strings or invalid integer ranges. Errors are deferred to validation time (ValidatePDF/RunPDF) with sentinel errorsErrInvalidPageSizeandErrInvalidPDFCopies. - Nil-Safe Receivers: Added nil checks across mutators (
AddHTML,SetPage,SetBody,WithGlobal). - Ergonomic Local File Access: Added
EnableLocalFileAccess()helpers onPDFRequest,ImageRequest,Converter,GlobalSettings, andObjectSettings.
2. Layout, Pagination & Table Rendering
- Table Continuation Borders: Multi-page tables with
border-collapse: collapsenow render closed top edges across continuation pages while maintaining continuous visual borders for rowspan cells spanning multiple pages. - Float Stacking & BFC: Multiple same-side floats cleanly stack vertically without overlapping. Block formatting context (BFC) containment prevents floats from escaping their parent boundaries.
- Flex & Grid Bounds: Explicit
flex-shrinkwidth calculations verified against narrow viewports; single-span grid item heights calculate based on content height rather than line-height assumptions.
3. Continuous Fuzzing & Input Hardening
- Native Go Fuzz Targets: Added continuous fuzz testing targets:
FuzzParseHTMLininternal/html(430k+ executions)FuzzParseCSSininternal/css(440k+ executions)FuzzConvertHTMLininternal/convert(arbitrary HTML -> PDF conversion resilience)
- Proves that malicious or deeply nested HTML/CSS inputs fail gracefully without memory out-of-bounds panics or infinite loops.
4. Architecture & Security Hygiene
- Canonical Network Policy: Consolidated network trust boundaries into
internal/load.NetworkPolicywithApplyNetworkPolicyhelper, cleanly aliased togowkhtmltopdf.NetworkPolicy. - Pipeline Decoupling: Cleaned
convert.Requestby removing leftover image fields from the PDF pipeline. Documented reflection-free hand-dispatch table ininternal/settings/reflect.go. - Package Documentation: Updated
internal/pdf/doc.goto accurately reflect the pure-Go PDF 1.4 generator.
Documentation
Breaking changes / migration
| Item | Migration |
|---|---|
| None | Full backward compatibility with existing wkhtmltopdf-compatible dotted Set/Get and Converter API. |
Verification Gates
make test
make lint
make claim-scan
make buildWhat's Changed
- feat(release): v0.2.1 library contracts, layout fidelity, and verification by @chinmay-sawant in #41
- test: align opening comment header in architecture-diagram.html by @chinmay-sawant in #42
Full Changelog: v0.2.0...v0.2.1
What's Changed
- feat(release): v0.2.1 library contracts, layout fidelity, and verification by @chinmay-sawant in #41
- test: align opening comment header in architecture-diagram.html by @chinmay-sawant in #42
- docs: add v0.2.1 release notes artifact by @chinmay-sawant in #43
Full Changelog: v0.2.0...v0.2.1
v0.2.0 - HTML template engine: print CSS, multi-font, faster PDF
gowkhtmltopdf v0.2.0 - HTML template engine: print CSS, multi-font, faster PDF
Second public release of gowkhtmltopdf: a no-cgo, no Qt/WebKit, no browser HTML template engine that turns HTML you control into PDF and PNG/JPEG. It is a clean-room work-alike of the wkhtmltopdf CLI surface.
The product is structured HTML/CSS templates and documents, not “a report generator.” The Showcase is the proof: invoices and receipts sit beside certificates, storybooks, posters, boarding passes, letters, contracts, shipping docs, architecture diagrams, and CSS layout fixtures. Headers, footers, TOC, and PDF outlines are engine features those templates can use — not the whole product.
Since v0.1.0 the engine closed Tier 1 (template quality) and Tier 2 (leave wkhtmltopdf for most template jobs): real multi-face fonts, Type0/CJK/Arabic paths, flex/grid/float/sticky as a print CSS subset, repeating table headers, typed library requests, and a measured speedup versus wkhtmltopdf 0.12.6.1.
Not a browser. Prefer this when you want a static Go binary and MIT licensing for HTML templates you author. Prefer Chrome headless / upstream wkhtmltopdf when you need arbitrary-page or JavaScript fidelity. This release does not claim Chrome or Wikipedia visual parity.
- License: MIT - Copyright (c) 2026 Chinmay Sawant
- Version source:
VERSION(0.2.0) - Site: https://chinmay-sawant.github.io/gowkhtmltopdf/
- Compare: v0.1.0...v0.2.0
- Engine PRs: #7–#34
- Wrap-up PRs: #36 release prep · #37 docs site · #38 CONTRIBUTING
Highlights
| Area | What you get vs 0.1.0 |
|---|---|
| CLI | Same gowkhtmltopdf + gowkhtmltoimage grammar; mode-invalid flags now fail at parse; --font-path / --use-system-fonts; opt-in --simplify-dom |
| Library | Prefer RunPDF / PDFRequest and RunImage / ImageRequest. ConvertHTML one-shot helper. Compatibility Converter kept. Settings cloned so later mutation cannot change an in-flight job |
| Layout | Print CSS subset: flex, grid, float, inline-block, box-sizing, print-scoped sticky, repeating <thead>, CSS orphans/widows, nested HTML headers/footers |
| Templates | Showcase spans invoices, receipts, POs, contracts, letters, boarding passes, certificates, storybooks, posters, reports, architecture diagrams, and CSS fixtures |
| Fonts | Liberation Sans/Serif/Mono (R/B/I/BI) + DejaVu fallback; Type0/CID for non-Latin; @font-face TTF/OTF/WOFF1; OpenType GSUB via allowlisted go-text/typesetting |
| Image mode | TrueType outline raster with coverage AA (the 0.1.0 5×7 bitmap font is gone) |
Unique multi-image XObjects; JPEG DCT pass-through; PNG alpha soft-mask; SVG-as-<img> via allowlisted tdewolff/canvas |
|
| Performance | Faster than wkhtmltopdf 0.12.6.1 at every tested size on the 2026-08-14 snapshot (about 16× at 2 pages, 1.6× at 500 pages) |
| Security defaults | Local files still blocked unless opted in; <script> stripped; JS CLI flags are unknown options (not silent no-ops) |
| Ops | CGO_ENABLED=0 static builds; golangci-lint v1.64.8; v* tags publish 12 binaries + SHA256SUMS |
| Site | Docs, Issue Dossier (1,329 upstream issues classified), Showcase, Benchmarks |
Showcase — what the HTML engine actually prints
Live gallery: https://chinmay-sawant.github.io/gowkhtmltopdf/#/showcase
Committed PDFs: output/ · source HTML: testdata/golden/
These are HTML/CSS templates through the same pipeline. Reports are one category, not the definition of the engine.
| Category | What you can open |
|---|---|
| Invoices & receipts | Simple / CSS invoices, receipts, purchase orders, contracts, letters, shipping documents, airline boarding passes |
| Reports & tables | Detailed ops reports, multi-page tables with repeating <thead>, sticky print headers, colorful reports |
| Storybooks & posters | Asteria and Ember Harbor storybooks, night-train and observatory posters, certificates |
| CSS & layout fixtures | Flex, grid, float, sticky, multicol, :has(), @container, transforms, CJK + --font-path, nested HTML headers/footers |
| Architecture & API | Library architecture diagram, 20-page HTML+CSS architecture doc, font-examples (1,125 Google Fonts via --font-path), complex dossier |
TOC, text/HTML headers and footers, and PDF outlines are available on any of these templates (showcase-toc-hf-outline.pdf is the dedicated demo).
Install / build
Cross-platform binaries are attached to this release (gowkhtmltopdf and gowkhtmltoimage for linux / windows / darwin × amd64 / arm64) plus SHA256SUMS.
From source (Go 1.26+):
git clone https://github.com/chinmay-sawant/gowkhtmltopdf.git
cd gowkhtmltopdf
git checkout v0.2.0
CGO_ENABLED=0 go build -ldflags "-X gowkhtmltopdf/internal/cli.Version=0.2.0" \
-o bin/gowkhtmltopdf ./cmd/gowkhtmltopdf
CGO_ENABLED=0 go build -ldflags "-X gowkhtmltopdf/internal/cli.Version=0.2.0" \
-o bin/gowkhtmltoimage ./cmd/gowkhtmltoimageOr make build. Convert any committed template (invoice shown; storybooks, posters, and the rest live in testdata/golden/):
./bin/gowkhtmltopdf --enable-local-file-access \
testdata/golden/fixture-01-simple-invoice.html /tmp/invoice.pdfLibrary (typed API):
var out bytes.Buffer
err := gowkhtmltopdf.RunPDF(ctx, &gowkhtmltopdf.PDFRequest{
Objects: []*gowkhtmltopdf.ObjectSettings{
gowkhtmltopdf.NewObjectSettings().SetBody(
[]byte(`<html><body><h1>Invoice</h1></body></html>`), ""),
},
Output: &out,
})Sample PDFs live under output/ (make samples).
What landed since v0.1.0
Rendering and typography
- Liberation Sans / Serif / Mono Regular, Bold, Italic, BoldItalic, plus DejaVu Sans as Unicode fallback. CSS
font-weight/font-stylepick real faces; fake bold only if a face is missing. - Image mode (
gowkhtmltoimage) rasterizes TrueType outlines with coverage AA (2× supersample, stable baselines) instead of the MVP 5×7 bitmap font. Metrics match PDF/layout. --font-path DIR(repeatable, depth 2) and--use-system-fonts. Type0 / CID Identity-H for runes above U+00FF. Mixed Latin + CJK keeps Liberation for Latin.- Local and HTTPS
@font-facefor TTF / TrueType-outlined OTF / WOFF1 (PDF and image). WOFF2 /data:/ EOT are skipped on purpose. - Shaping without cgo HarfBuzz: OpenType GSUB when the face has it (
go-text/typesetting); Arabic presentation-form + Lam-Alef fallback; optionalhalt/palt. Indic remains Partial.writing-mode: vertical-*is parsed but lays out horizontal. - Without a capable face, CJK is still tofu. CI ships only a tiny OFL Hangul subset for smoke, not a full CJK family.
CSS for HTML templates
- Selectors:
[attr],[attr=value],:first-child/:last-child/:nth-child(odd|even|an+b), sibling+/~, plus:has()(simple compounds). - Float lite:
float: left|right+clearfor logo/meta chrome. Realdisplay: inline-block. Simpletext-align: justify. Table-cellvertical-aligntop/middle/bottom. box-sizing: content-box(now the default) andborder-box. Migration: explicitwidth+ padding withoutbox-sizinggrows vs 0.1.0; addbox-sizing: border-boxto keep the old visual size.- Flex Stage A (print CSS subset):
flex/inline-flex, direction including reverse, wrap, grow/shrink/basis/order, gap, justify includingspace-around/space-evenly, align-self, stretch, cyclic%→ auto. Not Flexbox L1 / Chrome parity. - Grid Stage B + Stage C lite: columns/rows,
fr,repeat,minmax, span, named areas, dense packing, copy-inherit subgrid (no shared-track sizing), one-axis masonry. Not Grid L1/L3 complete. position: relative | absolute | fixedlite.position: stickyis print-scoped (page content box is the scrollport; overflow boxes at offset 0). Not browser sticky scroll.- Repeating
<thead>/table-header-group(and leading all-<th>rows). CSSorphans/widowsplus a geometric fallback. Nested HTML headers/footers as child documents. - Also in the print CSS subset: multicol (
column-count/width/gap/span/fill), static 2Dtransform+opacity, size-only@container, print@mediasubset, HTML entity decoding, CSSbackgroundcolor token. letter-spacing,text-transform, andborder-radiussurvive into the PDF so original static templates need fewer renderer-specific workarounds.
Layout and paint correctness
- Block backgrounds and borders paint under text.
trbackgrounds show through transparent cells.rgba()fills composite against white. - Multi-image pages use unique XObjects (
I0,I1, …; headers/footersHFI0, …). JPEG bytes pass through as DCTDecode; PNG alpha becomes a soft-mask.web.images=falseskips image fetch/paint. - Nested tables keep document order;
%widths resolve against the parent containing block; colspan contributes across spanned co...
v0.1.0 - pure-Go stdlib HTML→PDF/image MVP
gowkhtmltopdf v0.1.0 - pure-Go HTML→PDF / HTML→image MVP
First public release of gowkhtmltopdf: a stdlib-only, no cgo, no Qt/WebKit clean-room work-alike of the wkhtmltopdf CLI, aimed at controlled server-generated reports (invoices, tables, multi-page docs with headers/footers, TOC, and PDF outlines).
Not a full browser-print engine. Prefer this when you want a static Go binary and MIT licensing for HTML you control. Prefer Chrome headless / upstream wkhtmltopdf when you need arbitrary-page or JS fidelity.
- License: MIT - Copyright (c) 2026 Chinmay Sawant
- Version source:
VERSION(0.1.0) - PR: #1 (phases 0–9 + docs)
Highlights
| Area | What you get |
|---|---|
| CLI | gowkhtmltopdf + gowkhtmltoimage - multi-object grammar (page / cover / toc), familiar flags |
| Library | NewConverter / NewImageConverter in-process Go API |
| Pipeline | load → parse HTML → CSS subset → layout → paginate → paint → PDF 1.4 (or PNG/JPEG) |
| Embedded Liberation Sans subset, images, links, outlines, TOC, text HF placeholders | |
| Security defaults | Local files blocked unless opted in; timeouts + body size caps; no JS |
| Ops | CGO_ENABLED=0 static builds, zero third-party modules, CI on every push |
Install / build
git clone https://github.com/chinmay-sawant/gowkhtmltopdf.git
cd gowkhtmltopdf
git checkout v0.1.0
CGO_ENABLED=0 go build -ldflags "-X gowkhtmltopdf/internal/cli.Version=0.1.0" \
-o bin/gowkhtmltopdf ./cmd/gowkhtmltopdf
CGO_ENABLED=0 go build -ldflags "-X gowkhtmltopdf/internal/cli.Version=0.1.0" \
-o bin/gowkhtmltoimage ./cmd/gowkhtmltoimage./bin/gowkhtmltopdf --enable-local-file-access \
testdata/golden/fixture-01-simple-invoice.html /tmp/invoice.pdfSample outputs are committed under output/ (make samples).
Documentation
Known limitations (honest MVP)
- CSS subset - no flex/grid/floats/position as real layout; not for arbitrary websites (e.g. Wikipedia chrome).
- No JavaScript - flags accepted as no-ops;
<script>stripped. - Fonts - single Liberation Sans Regular (fake bold); limited Unicode; CJK not supported.
- Image mode - 5×7 bitmap text (no anti-aliasing).
- Full deferred list: README § Deferred.
Post-MVP rendering work is tracked under epic #2.
Verify
make test
make lint
make golden
CGO_ENABLED=0 go build ./cmd/gowkhtmltopdf ./cmd/gowkhtmltoimageFull changelog for 0.1.0: CHANGELOG.md