Skip to content

Releases: chinmay-sawant/gowkhtmltopdf

v0.2.6 - Print CSS coverage and browser WASM

Choose a tag to compare

@github-actions github-actions released this 13 Sep 15:11
5896872

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.


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 --version

Library pin:

go get github.com/chinmay-sawant/gowkhtmltopdf@v0.2.6

Python (in-process; wheels are published from this tag):

pip install gowkhtmltopdf
from 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, and font-language-override.
  • mix-blend-mode and isolation moved 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 --check guards drift.

2. Browser WASM adapter (PRs #68, #69, #71, #72)

  • bindings/wasm exports 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 wasm builds the version-stamped artifact into the site; make wasm-test runs 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-demo route replaces the earlier /wasm route and adds paginated previews and image downloads.

3. Performance and memory (PR #70)

  • Style resolution is memoized with a declared-property mask, ResolvedStyle records are interned through generated fingerprint code, grid border ops batch into OpGridRun, 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.WritePDF at 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)

  • thead rows repeat on every continuation page and table bodies no longer overlap the repeated band.
  • Table ce...
Read more

v0.2.5 - Python bindings and PyPI

Choose a tag to compare

@chinmay-sawant chinmay-sawant released this 27 Aug 14:19
b0e39cd

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.


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 --version

Library pin:

go get github.com/chinmay-sawant/gowkhtmltopdf@v0.2.5

Python (in-process):

pip install gowkhtmltopdf
from 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-benchmarks

What landed in v0.2.5

1. C ABI and c-shared build (#58, #35)

  • Committed header bindings/c/include/gowkhtmltopdf.h as the ABI source of truth.
  • bindings/c is the only package allowed to contain import "C"; stub builds keep CGO_ENABLED=0 green.
  • Exports wrap Document.WritePDF / ImageDocument.WriteImage with timeout and status mapping.
  • make c-shared refuses to run unless CGO_ENABLED=1.

2. Python package and PyPI (#58)

  • bindings/python: Document / ImageDocument parity, helpers, exceptions, py.typed.
  • convert_file_to_pdf sets a file:// base so linked CSS (fixture-56) resolves.
  • CI: purity guard, build-shared, python-binding jobs; publish workflow for wheels on v* tags.
  • make check-versions keeps VERSION and pyproject.toml aligned.

3. Samples, benches, and docs (#58)

  • testdata/golden/python_api/: architecture diagram generator, inline invoice sample, compliance smokes, full generate_samples.py.
  • make python-api, make samples-python, make python-benchmarks.
  • documentation/python.md plus README / deferred / samples index updates.
  • Bulk output/python/fixture-*.pdf gitignored; fixture-55 / fixture-56 kept as committed smoke exceptions.

Documentation

Doc Link
Site https://chinmay-sawant.github.io/gowkhtmltopdf/
Python https://github.com/chinmay-sawant/gowkhtmltopdf/blob/v0.2.5/documentation/python.md
Overview https://github.com/chinmay-sawant/gowkhtmltopdf/blob/v0.2.5/documentation/overview.md
Getting started https://github.com/chinmay-sawant/gowkhtmltopdf/blob/v0.2.5/documentation/getting-started.md
CLI https://github.com/chinmay-sawant/gowkhtmltopdf/blob/v0.2.5/documentation/cli.md
Library API https://github.com/chinmay-sawant/gowkhtmltopdf/blob/v0.2.5/documentation/library-api.md
Samples https://github.com/chinmay-sawant/gowkhtmltopdf/blob/v0.2.5/output/README.md
Deferred https://github.com/chinmay-sawant/gowkhtmltopdf/blob/v0.2.5/documentation/deferred.md
Changelog https://github.com/chinmay-sawant/gowkhtmltopdf/blob/v0.2.5/CHANGELOG.md#025-2026-08-26
0.2.5 plans https://github.com/chinmay-sawant/gowkhtmltopdf/blob/v0.2.5/plans/0.2.5/README.md

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 on GwkPdfOptions, so make samples-python soft-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

Full Changelog: v0.2.4...v0.2.5

v0.2.4 - Document API, CLI redesign, and external benches

Choose a tag to compare

@chinmay-sawant chinmay-sawant released this 19 Aug 10:40
80cbb47

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.


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 --version

Library pin:

go get github.com/chinmay-sawant/gowkhtmltopdf@v0.2.4

From a source checkout:

git clone https://github.com/chinmay-sawant/gowkhtmltopdf.git
cd gowkhtmltopdf
git checkout v0.2.4
make build

Local PDF (0.2.4 CLI grammar):

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

Inline 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.html

Unclaimed PDF 1.7:

./bin/gowkhtmltopdf --pdf-version 1.7 --allow-local-files \
  -o /tmp/report-17.pdf testdata/golden/fixture-21-detailed-report.html

Dual 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.html

Library (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-returning PDF / Image; explicit WritePDFOutline when an outline sink is needed.
  • Delete the wkhtml-shaped public surface (Converter, dotted Set/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-files replaces --enable-local-file-access.
  • Old object grammar (page / cover / toc keywords, 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 via scripts/bench-external.sh, then wkhtmltopdf.
  • make bench-lib / related targets exercise the public Document.WritePDF path.
  • 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; dated CHANGELOG.md 0.2.4 (2026-08-18).

Documentation

Doc Link
Site https://chinmay-sawant.github.io/gowkhtmltopdf/
Migration 0.2.4 https://github.com/chinmay-sawant/gowkhtmltopdf/blob/v0.2.4/documentation/MIGRATION-0.2.4.md
Overview https://github.com/chinmay-sawant/gowkhtmltopdf/blob/v0.2.4/documentation/overview.md
Getting started https://github.com/chinmay-sawant/gowkhtmltopdf/blob/v0.2.4/documentation/getting-started.md
CLI https://github.com/chinmay-sawant/gowkhtmltopdf/blob/v0.2.4/documentation/cli.md
Library API https://github.com/chinmay-sawant/gowkhtmltopdf/blob/v0.2.4/documentation/library-api.md
Performance / benches https://gith...
Read more

v0.2.3

Choose a tag to compare

@chinmay-sawant chinmay-sawant released this 15 Aug 13:22
f850f6f

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.


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 --version

From a source checkout:

git clone https://github.com/chinmay-sawant/gowkhtmltopdf.git
cd gowkhtmltopdf
git checkout v0.2.3
make build

Unclaimed PDF 1.7:

./bin/gowkhtmltopdf --pdf-version 1.7 --enable-local-file-access \
  testdata/golden/fixture-21-detailed-report.html /tmp/report-17.pdf

Dual 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.pdf

Dual 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.pdf

Library (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.7 on an explicit version flag.
  • Opt-in PDF/A-3a, PDF/UA-1, and dual a3a-ua1 via --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 /FontName equals parent /BaseFont (Arlington FontDescriptorCIDType2.FontName).
  • Layout tagging no longer accumulates one document-wide mega-/P with 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 — no pdfaid / 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: /D page XYZ (Arlington / PDF/A) plus /SD structure dest (UA-2 clause 8.8). Outline items also bind /SE to 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 /SD matches heading identity; internal and TOC links resolve dest struct elems.
  • New leaf internal/pdfprofile: one alias table. WithPDFVersion / WithPDFProfile store canonical tokens. Get("pdfprofile") after "a3a-ua1" is PDF/A-3a+PDF/UA-1.
  • Unified ErrConformanceRequiresPDF17 / ErrConformanceRequiresPDF20 (with ErrProfileRequiresPDF17 / ErrProfileRequiresPDF20 aliases). ErrProfilePDF20Unsupported remains 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...
Read more

v0.2.2

Choose a tag to compare

@chinmay-sawant chinmay-sawant released this 15 Aug 13:04
d007ccb

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.


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 --version

From a source checkout:

git clone https://github.com/chinmay-sawant/gowkhtmltopdf.git
cd gowkhtmltopdf
git checkout v0.2.2
make build

Unclaimed PDF 1.7:

./bin/gowkhtmltopdf --pdf-version 1.7 --enable-local-file-access \
  testdata/golden/fixture-21-detailed-report.html /tmp/report-17.pdf

Dual 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.pdf

Dual 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.pdf

Library (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.7 on an explicit version flag.
  • Opt-in PDF/A-3a, PDF/UA-1, and dual a3a-ua1 via --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 /FontName equals parent /BaseFont (Arlington FontDescriptorCIDType2.FontName).
  • Layout tagging no longer accumulates one document-wide mega-/P with 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 — no pdfaid / 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: /D page XYZ (Arlington / PDF/A) plus /SD structure dest (UA-2 clause 8.8). Outline items also bind /SE to 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 /SD matches heading identity; internal and TOC links resolve dest struct elems.
  • New leaf internal/pdfprofile: one alias table. WithPDFVersion / WithPDFProfile store canonical tokens. Get("pdfprofile") after "a3a-ua1" is PDF/A-3a+PDF/UA-1.
  • Unified ErrConformanceRequiresPDF17 / ErrConformanceRequiresPDF20 (with ErrProfileRequiresPDF17 / ErrProfileRequiresPDF20 aliases). ErrProfilePDF20Unsupported remains 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...

Read more

v0.2.1 - Production hardening: library contracts, layout fidelity, continuous fuzzing

Choose a tag to compare

@chinmay-sawant chinmay-sawant released this 14 Aug 11:17
78a8ba7

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.


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 build

Convert any committed template:

./bin/gowkhtmltopdf --enable-local-file-access \
  testdata/golden/fixture-01-simple-invoice.html /tmp/invoice.pdf

Library (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.WithPageSize and PdfGlobalOptions.WithCopies no longer panic on malformed user strings or invalid integer ranges. Errors are deferred to validation time (ValidatePDF / RunPDF) with sentinel errors ErrInvalidPageSize and ErrInvalidPDFCopies.
  • Nil-Safe Receivers: Added nil checks across mutators (AddHTML, SetPage, SetBody, WithGlobal).
  • Ergonomic Local File Access: Added EnableLocalFileAccess() helpers on PDFRequest, ImageRequest, Converter, GlobalSettings, and ObjectSettings.

2. Layout, Pagination & Table Rendering

  • Table Continuation Borders: Multi-page tables with border-collapse: collapse now 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-shrink width 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:
    • FuzzParseHTML in internal/html (430k+ executions)
    • FuzzParseCSS in internal/css (440k+ executions)
    • FuzzConvertHTML in internal/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.NetworkPolicy with ApplyNetworkPolicy helper, cleanly aliased to gowkhtmltopdf.NetworkPolicy.
  • Pipeline Decoupling: Cleaned convert.Request by removing leftover image fields from the PDF pipeline. Documented reflection-free hand-dispatch table in internal/settings/reflect.go.
  • Package Documentation: Updated internal/pdf/doc.go to accurately reflect the pure-Go PDF 1.4 generator.

Documentation

Doc Link
Site https://chinmay-sawant.github.io/gowkhtmltopdf/
Overview https://github.com/chinmay-sawant/gowkhtmltopdf/blob/v0.2.1/documentation/overview.md
Getting started https://github.com/chinmay-sawant/gowkhtmltopdf/blob/v0.2.1/documentation/getting-started.md
Architecture https://github.com/chinmay-sawant/gowkhtmltopdf/blob/v0.2.1/documentation/architecture.md
Library API https://github.com/chinmay-sawant/gowkhtmltopdf/blob/v0.2.1/documentation/library-api.md
Fidelity https://github.com/chinmay-sawant/gowkhtmltopdf/blob/v0.2.1/documentation/fidelity.md
Performance https://github.com/chinmay-sawant/gowkhtmltopdf/blob/v0.2.1/documentation/performance.md
Deferred https://github.com/chinmay-sawant/gowkhtmltopdf/blob/v0.2.1/documentation/deferred.md
Contributing https://github.com/chinmay-sawant/gowkhtmltopdf/blob/v0.2.1/CONTRIBUTING.md
Changelog https://github.com/chinmay-sawant/gowkhtmltopdf/blob/v0.2.1/CHANGELOG.md#021-2026-08-14
Roadmap https://github.com/chinmay-sawant/gowkhtmltopdf/blob/v0.2.1/plans/0.2.1/24-canonical-0.2.1-roadmap.md

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 build

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

Full Changelog: v0.2.0...v0.2.1

What's Changed

Full Changelog: v0.2.0...v0.2.1

v0.2.0 - HTML template engine: print CSS, multi-font, faster PDF

Choose a tag to compare

@github-actions github-actions released this 14 Aug 08:33
d480dbb

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.


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)
PDF 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/gowkhtmltoimage

Or 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.pdf

Library (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-style pick 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-face for 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; optional halt / 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 + clear for logo/meta chrome. Real display: inline-block. Simple text-align: justify. Table-cell vertical-align top/middle/bottom.
  • box-sizing: content-box (now the default) and border-box. Migration: explicit width + padding without box-sizing grows vs 0.1.0; add box-sizing: border-box to keep the old visual size.
  • Flex Stage A (print CSS subset): flex / inline-flex, direction including reverse, wrap, grow/shrink/basis/order, gap, justify including space-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 | fixed lite. position: sticky is 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). CSS orphans / widows plus a geometric fallback. Nested HTML headers/footers as child documents.
  • Also in the print CSS subset: multicol (column-count / width / gap / span / fill), static 2D transform + opacity, size-only @container, print @media subset, HTML entity decoding, CSS background color token.
  • letter-spacing, text-transform, and border-radius survive into the PDF so original static templates need fewer renderer-specific workarounds.

Layout and paint correctness

  • Block backgrounds and borders paint under text. tr backgrounds show through transparent cells. rgba() fills composite against white.
  • Multi-image pages use unique XObjects (I0, I1, …; headers/footers HFI0, …). JPEG bytes pass through as DCTDecode; PNG alpha becomes a soft-mask. web.images=false skips image fetch/paint.
  • Nested tables keep document order; % widths resolve against the parent containing block; colspan contributes across spanned co...
Read more

v0.1.0 - pure-Go stdlib HTML→PDF/image MVP

Choose a tag to compare

@chinmay-sawant chinmay-sawant released this 03 Aug 20:17
02b44b7

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)
PDF 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.pdf

Sample outputs are committed under output/ (make samples).


Documentation

Doc Link
Overview https://github.com/chinmay-sawant/gowkhtmltopdf/blob/v0.1.0/documentation/overview.md
Getting started https://github.com/chinmay-sawant/gowkhtmltopdf/blob/v0.1.0/documentation/getting-started.md
Architecture https://github.com/chinmay-sawant/gowkhtmltopdf/blob/v0.1.0/documentation/architecture.md
CLI https://github.com/chinmay-sawant/gowkhtmltopdf/blob/v0.1.0/documentation/cli.md
Library API https://github.com/chinmay-sawant/gowkhtmltopdf/blob/v0.1.0/documentation/library-api.md
Samples https://github.com/chinmay-sawant/gowkhtmltopdf/blob/v0.1.0/documentation/samples.md
Compatibility matrix https://github.com/chinmay-sawant/gowkhtmltopdf/blob/v0.1.0/documentation/compatibility-matrix.md
Threat model https://github.com/chinmay-sawant/gowkhtmltopdf/blob/v0.1.0/documentation/THREAT-MODEL.md
Integration security (Gin / SSRF) https://github.com/chinmay-sawant/gowkhtmltopdf/blob/v0.1.0/documentation/integration-security.md
Docs index https://github.com/chinmay-sawant/gowkhtmltopdf/blob/v0.1.0/documentation/README.md
Root README https://github.com/chinmay-sawant/gowkhtmltopdf/blob/v0.1.0/README.md
Changelog https://github.com/chinmay-sawant/gowkhtmltopdf/blob/v0.1.0/CHANGELOG.md
Canonical plan https://github.com/chinmay-sawant/gowkhtmltopdf/blob/v0.1.0/plans/00-canonical-pure-go-rewrite.md

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/gowkhtmltoimage

Full changelog for 0.1.0: CHANGELOG.md