Print and handbook
One-click printable handbook plus PDF export via any HTML-to-PDF tool in CI.
The printable handbook is a plain, self-contained HTML page at a predictable URL:
docs/<version>/<lang>/handbook.htmlNo app chrome, no JavaScript. Just the cover, a linked table of contents and every page in order, styled for print. That makes it ideal input for any HTML-to-PDF tool run in your CI. The theme ships no PDF toolchain of its own, so you keep full control over the renderer and its output.
Why generate the PDF yourself
The handbook’s in-browser print uses the browser’s print dialog, which cannot compute page numbers. That is why its table of contents has no page numbers. A dedicated HTML-to-PDF engine can: it lays the document out into real pages and back-fills numbers and leader dots. If you want a true page-numbered PDF, render the handbook with one of the tools below.
With WeasyPrint (no browser)
WeasyPrint renders HTML and CSS to PDF with its own engine. No headless browser is required, so it is light in CI (Python plus a few system libraries).
# after hugo has built the site into ./public
weasyprint public/docs/v1_0_0/en/handbook.html handbook-v1.0.0-en.pdfWith Paged.js CLI (headless Chrome)
pagedjs-cli drives headless Chromium, so the PDF matches Chrome exactly. Heavier (Node plus Chromium) but pixel-faithful.
npx pagedjs-cli public/docs/v1_0_0/en/handbook.html -o handbook-v1.0.0-en.pdfPrince, wkhtmltopdf or your own headless-Chrome script work the same way: point them at handbook.html.
Page numbers and a leader-dot TOC
To get the classic Chapter …………… 12 table of contents, add a small print stylesheet that your engine understands (WeasyPrint and Paged.js both support CSS Generated Content for Paged Media):
/* number the pages */
@page { @bottom-center { content: counter(page); } }
/* fill the handbook TOC leaders with the target page number */
.handbook-toc__link::after {
content: leader('.') target-counter(attr(href), page);
}Pass it to the tool (e.g. weasyprint -s pdf.css …). Because the handbook’s TOC links already point at each page’s anchor, target-counter resolves the real page numbers for you.
In a CI job
A minimal GitLab CI example that produces a downloadable PDF alongside the site:
pdf:
stage: deploy
image: python:3.12-slim
rules: [{ if: $CI_COMMIT_TAG }]
script:
- pip install weasyprint
- sh ci/scripts/build-docs.sh ci/config.json "$PAGES_PATH"
- hugo --source exampleSite --environment production --baseURL "${CI_PAGES_URL}/"
- weasyprint exampleSite/public/docs/<version>/en/handbook.html public/handbook.pdf
artifacts: { paths: [public] }The result is a real, page-numbered PDF, generated by the engine you chose, with no PDF dependency baked into the theme.
Tip
Looking for the shortcode that embeds a PDF viewer on a page? See PDF embed.