pagemill

package module
v0.1.0 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Nov 13, 2026 License: MIT Imports: 16 Imported by: 0

README

pagemill - HTML to PDF in one call

You need a PDF from Go. You did not sign up to manage a headless browser.

The current options all charge a tax: raw chromedp or go-rod means pages of allocator flags and protocol calls, plus a Chrome you install and version yourself. Gotenberg means running a second service. wkhtmltopdf is archived.

pagemill is one call:

pdf, err := pagemill.HTML(invoiceHTML)

It downloads a pinned chrome-headless-shell build once, caches it, and renders your HTML with real browser layout: flexbox, grid, web fonts, your CSS as-is. PNG screenshots too.

Install

go get github.com/ofershap/pagemill@latest

Use

package main

import (
    "os"

    "github.com/ofershap/pagemill"
)

func main() {
    html := `<!DOCTYPE html><html><body><h1>Invoice #42</h1></body></html>`

    pdf, err := pagemill.HTML(html) // A4, 0.4in margins
    if err != nil {
        panic(err)
    }
    os.WriteFile("invoice.pdf", pdf, 0o644)

    png, err := pagemill.PNG(html) // 1280px-wide full-page capture
    if err != nil {
        panic(err)
    }
    os.WriteFile("invoice.png", png, 0o644)
}

With options:

pdf, err := pagemill.HTML(html, func(o *pagemill.Options) {
    o.PaperWidth, o.PaperHeight = 8.5, 11 // Letter
    o.Landscape = true
    o.Margin = 0.25
    o.Timeout = time.Minute
})

Bring your own browser (containers, CI images):

pdf, err := pagemill.HTML(html, func(o *pagemill.Options) {
    o.BrowserPath = "/usr/bin/google-chrome" // skips the download
})

What it costs

pagemill trades disk and memory for not thinking about browsers. The numbers, measured on a 2-vCPU Linux container, chrome-headless-shell 157.0.8103.78:

Cost Measured
One-time browser download 128MB compressed, 277MB on disk
PDF render, cached browser ~130-190ms per call
Memory per render a full Chrome process; budget a few hundred MB
Dependencies chromedp v0.23 (pinned; our API stays stable even if its API moves)

Each call starts a fresh browser: calls are independent, nothing leaks between them, and you pay browser startup per render. If you render thousands of documents an hour, that model is the thing to measure first.

What it is not

  • Not a new PDF engine. It drives Chrome; it inherits Chrome's layout and Chrome's appetite. The RAM complaints about driving browsers yourself apply here too: pagemill removes the plumbing, not the browser.
  • Not a template library. You write HTML and CSS; it renders them.
  • Not a service. Everything runs in your process and your cache directory; the only network call is the one-time browser download from the official Chrome for Testing endpoint (and whatever your own HTML loads).

Containers

Two container-safe flags are on by default and you should know they are: --no-sandbox (Chrome's sandbox cannot initialize in most containers) and --disable-dev-shm-usage (Docker's default /dev/shm is too small and crashes Chrome under load). To bake the browser into your image instead of downloading at runtime, install Chrome in the Dockerfile and set BrowserPath.

Q&A

How do I convert HTML to PDF in Go without wkhtmltopdf? wkhtmltopdf is archived. pagemill renders with a current Chrome: pagemill.HTML(html) returns the PDF bytes.

Does pagemill need Node, Python, or a running server? No. It is a Go library; the only external piece is the Chrome binary it downloads once into your user cache.

Can I use my own Chrome or Chromium? Yes: set Options.BrowserPath. The download is skipped entirely.

How do I get a PNG screenshot of HTML in Go? pagemill.PNG(html) returns a 1280px-wide full-page PNG.

Does it work in Docker and CI? Yes. Container-required Chrome flags are set by default, and the test suite runs on GitHub's ubuntu, macOS, and Windows runners against their preinstalled Chrome.

Which Chrome version renders my document? By default, the current Stable chrome-headless-shell, resolved from the official Chrome for Testing endpoint at first use. Pin one with Options.Version.

Status

v0.1.0. The API is HTML, PNG, and Options; that surface is the compatibility promise. Rendering is covered by tests that run real Chrome on Linux, macOS, and Windows CI runners.

License

MIT

Documentation

Overview

Package pagemill renders HTML to PDF or PNG in one call, using a pinned, SHA256-verified Chrome for Testing build it downloads and caches.

pdf, err := pagemill.HTML(invoiceHTML)

The browser download (~150MB) happens once per version and is cached. See the README for container usage and honest resource costs.

Index

Constants

This section is empty.

Variables

This section is empty.

Functions

func HTML

func HTML(html string, opts ...func(*Options)) ([]byte, error)

HTML renders an HTML document to a PDF and returns the bytes.

pdf, err := pagemill.HTML(invoiceHTML)

On first use it downloads a chrome-headless-shell build (~40MB compressed) into the user cache; later renders reuse it. Each call runs a fresh browser instance, so calls are independent of each other. Browser startup dominates the cost of a single render; see the README for measured numbers and container usage.

func PNG

func PNG(html string, opts ...func(*Options)) ([]byte, error)

PNG renders an HTML document to a full-page PNG screenshot and returns the bytes. The viewport is 1280x720; the capture extends beyond the viewport to fit the content.

Types

type Options

type Options struct {
	// BrowserPath uses an existing chrome-headless-shell or Chrome/Chromium
	// executable instead of downloading one. Useful in containers where you
	// bake the browser into the image.
	BrowserPath string
	// Version pins the chrome-headless-shell version to download
	// (e.g. "157.0.8103.78"). Empty resolves the current Stable channel.
	Version string
	// CacheDir holds downloaded browsers. Empty uses the user cache dir.
	CacheDir string
	// Timeout bounds the whole render, including any download.
	// Default 2 minutes.
	Timeout time.Duration
	// Paper size in inches. A4 by default (8.27 x 11.69).
	PaperWidth  float64
	PaperHeight float64
	// Margin in inches applied to all sides. Default 0.4.
	Margin float64
	// Landscape orientation.
	Landscape bool
	// Scale of the webpage rendering. Default 1.
	Scale float64
	// ExtraFlags passes additional command-line flags to the browser.
	ExtraFlags []string
}

Options controls rendering and browser acquisition.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL