wkhtmltopdf --toc and --outline in Chrome
wkhtmltopdf could add a printed table of contents (toc) with page numbers and PDF bookmarks (--outline). Chrome does neither on its own. We tested on Chromium 141 (Linux, 17 October 2026): bookmarks are possible, but not with the option you would guess, and page numbers need a two-pass print. Every PDF below was read back with pdfinfo and pdftotext.
1. Bookmarks (the PDF sidebar outline)
The test page has three h1 headings and one h2. Results with Playwright 1.56:
| How we printed | Outline in the PDF |
|---|---|
page.pdf({ format: "A4" }) | None. |
page.pdf({ outline: true }) | None. The option alone did nothing, and the PDF was untagged. |
page.pdf({ outline: true, tagged: true }) | Yes: Alpha, Beta, Beta sub, Gamma, nested by heading level. |
chrome --headless --print-to-pdf=out.pdf | None. |
chrome --headless --generate-pdf-document-outline --print-to-pdf=out.pdf | Yes, the same four entries. The PDF was also tagged. |
So the nearest match for --outline is outline: true together with tagged: true in Playwright, or --generate-pdf-document-outline on the command line. In our run outline: true without tagged: true gave no bookmarks, so check your own output rather than trusting the option. The bookmarks come from your heading tags, so use real h1 to h6 elements in order. wkhtmltopdf's --outline-depth has no equivalent that we tested.
await page.pdf({ path: "out.pdf", format: "A4", printBackground: true,
outline: true, tagged: true });
2. A printed contents page with page numbers
The CSS way to get page numbers is target-counter(attr(href url), page) in a ::after rule. We tried it on a link to the third section: Chrome printed the link text and no number. It is not supported, so the usual print-CSS recipes for contents pages do not work in Chrome.
What did work is printing twice. Pass 1 prints the document with a placeholder ("00") in the contents list. Then you read the PDF back, find the page where each heading appears, and pass 2 prints the same document with the real numbers. The placeholder has to take up the same space as the final number, or the page count can change between passes.
// node, with Playwright and poppler's pdftotext installed
const pages = []; // text of each page of pass 1
for (let i = 1; i <= n; i++)
pages.push(execFileSync("pdftotext", ["-f", i, "-l", i, "p1.pdf", "-"]).toString());
// skip page 1 (the contents page itself), then match the heading line exactly
const nums = heads.map(h => pages.findIndex((t, i) => i > 0 &&
t.split("\n").some(l => l.trim() === h)) + 1);
// render again with nums in the list, then page.pdf() as before
On our three-section test document this found pages 2, 3 and 4, pass 2 printed "Alpha 2, Beta 3, Gamma 4" on the contents page, and both passes were 4 pages. The dotted leader was a flex row with a dotted bottom border. We did not make the numbers clickable links, and we matched headings by exact line text, so two headings with the same words would need a unique marker.
3. If you do not need numbers
A contents list of plain in-page links (<a href="#intro">Introduction</a>) needs none of this, and together with the bookmarks from section 1 it is enough for most reports. Our translator says the same when it meets toc: Chrome cannot build a contents page with numbers, so write the list as links.
Other wkhtmltopdf contents options
--toc-header-textbecomes the heading of your own contents section.--toc-level-indentation,--toc-text-size-shrink,--xsl-style-sheetand--disable-dotted-linesstyle the generated page. With your own HTML list you style it with CSS.--disable-toc-linksand--enable-toc-back-links: with your own list these are plain anchors that you control directly.
Related guides
Headers and footers, page size and margins, waiting for JavaScript, page breaks. For ready-made invoices, quotes and reports printed through Chrome, see the Foliokit product page.
Limits of this page
Tested: Chromium 141 (Playwright 1.56 build) on Linux, small synthetic documents (three or four headings). Not tested: other Chrome versions or operating systems, Puppeteer (it has outline and tagged options, but we did not run them), long documents where the contents page spills onto a second page, and wkhtmltopdf's own output. Whether a PDF viewer shows the bookmarks depends on the viewer.