October DealsAmazon USOctober deal check: compare before you payAmazon US: current deals, useful picks and tech finds.Check DealsWindows FixRecommendedWindows errors stealing your time? Find the fix fastScan stability, cleanup and performance issues.Fix NowOctober DealsAmazon USDeal season is back - check today's better picksAmazon US: current deals, useful picks and tech finds.See Picks×
Skip to content
HTML to PDF

How to Use JavaScript Section Counters in wkhtmltopdf

What’s actually slowing this PC down?

Pick the symptom - the matching free tool is one click away.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

Short answer: wkhtmltopdf can put the current section name in a repeated header or footer with [section] (and [subsection]), and it can print global numbers with [page] and [topage]. It does not document a numeric “page 2 of this section” value, nor a JavaScript API that reports the final PDF page boundaries. If you need numbering that restarts at every section, split sections into separately controlled documents or paginate in the application that generates the HTML, then validate the resulting PDF with the exact wkhtmltopdf build used in production.

First decide which counter you actually need

Many “section counter” requests combine three different values. Choosing the right one prevents a fragile implementation.

Requirement Supported approach What to expect
Display a section or subsection name [section] or [subsection] in a header/footer, or the matching class in an HTML header/footer A text label supplied by wkhtmltopdf
Display the document page and total [page] and [topage] Global printed-page values, such as “Page 4 of 18”
Display “page 2 of this section” and reset at each heading No documented built-in placeholder Requires explicit document boundaries or application-side pagination; a DOM scan is not authoritative

The library settings reference lists pageOffset and pagesCount, but does not describe either setting as a reset-at-section mechanism. Treat them as controls for object/page counting, not as proof that arbitrary headings can restart a counter.

Show the current section in a repeated footer

Use a text substitution for the simplest case

For a label and ordinary page number, let wkhtmltopdf perform the substitution:

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.
wkhtmltopdf 
  --footer-left '[section]' 
  --footer-right 'Page [page] of [topage]' 
  input.html output.pdf

The documented substitutions include [page] (current printed page), [frompage] (first page in the current print operation), [topage] (last page), [section], [subsection], [title], [doctitle], [sitepage], and [sitepages]. Keep the square-bracket spelling exactly as shown.

Use an HTML header or footer when you need layout or styling

An HTML header/footer receives the substitution values in its own URL query string. The documented pattern parses that query string and fills elements whose class names match supported keys. Save this as footer.html:

<!doctype html>
<html>
<head>
  <meta charset='utf-8'>
  <style>
    body { margin: 0; font: 9pt sans-serif; color: #555; }
    .row { display: flex; justify-content: space-between; }
  </style>
  <script>
    function subst() {
      var vars = {};
      var pairs = window.location.search.substring(1).split('&');
      for (var i = 0; i < pairs.length; i++) {
        var pair = pairs[i].split('=', 2);
        vars[pair[0]] = decodeURIComponent(pair[1] || '');
      }
      ['page', 'topage', 'section', 'subsection'].forEach(function (key) {
        var nodes = document.getElementsByClassName(key);
        for (var j = 0; j < nodes.length; j++) {
          nodes[j].textContent = vars[key] || '';
        }
      });
    }
  </script>
</head>
<body onload='subst()'>
  <div class='row'>
    <span class='section'></span>
    <span>Page <span class='page'></span> of <span class='topage'></span></span>
  </div>
</body>
</html>

Attach it with:

wkhtmltopdf --footer-html footer.html input.html output.pdf

You can add a subsection element in the same way. This script only inserts values that wkhtmltopdf has already supplied; it does not discover where the PDF’s physical pages begin or end.

Use JavaScript timing switches correctly

JavaScript is enabled by default in the documented command-line interface. Timing options matter when your source page or header/footer fills content asynchronously:

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.
  • --disable-javascript turns JavaScript off. Do not use it when your header/footer depends on subst() or when the source page builds content in JavaScript.
  • --javascript-delay <msec> adds a wait after loading; the documented default is 200 milliseconds.
  • --run-script <js> executes additional JavaScript after loading and may be specified repeatedly.
  • --window-status <windowStatus> waits until window.status reaches the requested value.

A delay is only a time budget. It does not prove that every asynchronous request, font, image, or widget has finished. When you control the page, setting a deliberate status value after your own work completes is generally easier to reason about than guessing a delay, but the generated PDF still needs inspection.

Why a numeric counter cannot reliably reset at an arbitrary heading

wkhtmltopdf’s documented placeholders expose section names and global page values, not a “current section page” integer. The renderer lays the document out as one long WebKit page and then cuts that layout into PDF pages. The manual warns that this process can split lines and images; patched Qt’s page-break-inside support can reduce some problems but does not expose final page boundaries to page JavaScript.

Consequently, code that scans headings, measures element offsets, or increments a counter while the DOM loads is estimating pagination in the source layout. It cannot know with certainty how a font substitution, margin, paper size, image load, or line wrap will alter the final cuts. Such a counter may appear correct for one document and fail after a small content or build change.

Practical ways to restart numbering

Separate sections into independently rendered objects

If your publishing pipeline already treats each section as a separate wkhtmltopdf object or input document, keep that boundary explicit. Render each section with its own local numbering context, then combine the outputs with the PDF workflow you already use. Investigate the object-level pagesCount behavior and global pageOffset in the settings reference for your binding, but do not assume either setting automatically produces a reset footer inside one flowing HTML object.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

This approach is strongest when section boundaries are editorial facts: a chapter, invoice, or report part can have its own HTML, assets, and validation fixture. It also makes it possible to restart a counter in the generating application rather than in browser JavaScript.

Paginate in the generating application

For a single source document that must show “section page X of Y,” calculate the section page map before generating HTML. Insert explicit page-break boundaries or render one object per section, and pass the resulting values to the template. Your application owns the definition of a section, while wkhtmltopdf remains responsible for rendering.

Do not calculate the map from character counts or a single hard-coded pixel height. Use the same fonts, paper dimensions, margins, zoom, and asset versions as production, and regard the result as a layout contract that must be regression-tested.

Use a custom JavaScript estimate only when you can tolerate drift

A script that finds headings and compares their vertical offsets can be useful for a controlled preview, but it is not an authoritative PDF counter. If you choose this route, label it as layout-dependent, test representative long and short sections, and reject the output when pagination differs from the expected map rather than silently publishing an incorrect footer.

Free tools Windows power users keep installed

One-click scans. No signup required.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

Validate against the actual binary and layout

Build differences matter. The manual distinguishes features that depend on patched Qt, and operating-system packages can ship different wkhtmltopdf builds. Record the exact executable and version used by your deployment, then test with the same binary locally or in CI.

  1. Freeze paper size, orientation, margins, zoom, fonts, and image assets.
  2. Test a section that ends near a page boundary, one that spans many pages, and one containing large images or tables.
  3. Generate the PDF after every change to CSS, font files, content, or wkhtmltopdf build.
  4. Inspect both the visible labels and the actual page breaks; do not validate only the source DOM.
  5. Keep a rendered PDF fixture or page-count assertion so an upgrade cannot silently change numbering.

Troubleshooting

The section name is blank

  • Confirm that the footer uses --footer-html or a documented text substitution, not a normal source-page element.
  • Verify the class is exactly section or subsection.
  • Check that JavaScript has not been disabled and that the footer script runs on body load.
  • Run the same command with the production binary; a build difference can change available features.

The page number never changes

  • Ensure the element has class page and that the footer is actually attached to the command.
  • Do not hard-code a value in the HTML; wkhtmltopdf supplies the number through the footer URL query.
  • Remember that [page] is a global printed-page number, not a section-relative value.

The footer shows stale or empty asynchronous data

  • Increase --javascript-delay only after measuring how long the page needs.
  • Prefer a controlled window.status signal when your own script knows that data and layout are ready.
  • Check that the source page and footer are not waiting on blocked requests or unavailable local assets.

A custom reset counter is wrong after a CSS change

That is the expected failure mode of DOM-based pagination. Recheck fonts, margins, paper settings, image dimensions, and page-break rules, then regenerate with the production binary. If the value must be correct, move the boundary and counter calculation into explicit objects or the generating application.

Performance and reliability considerations

Simple text substitutions add almost no rendering work. HTML footers and JavaScript add a separate document load and script execution, so keep the footer small, local, and free of unnecessary network requests. A long arbitrary delay slows every conversion; a status-based wait can reduce wasted time, but only when the page reliably sets the status.

For repeatable output, pin the wkhtmltopdf executable, Qt build, fonts, CSS, and input assets. Treat page counts as content-sensitive: changing a heading, image, table row, or font can move a boundary. Global [page]/[topage] numbering is therefore the dependable choice when a reset is not a hard requirement.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.
Independent reader supportYour contribution helps us test, update, and keep practical guides available for everyone.Support on Ko-Fi

Or skip the browser setup

If what you actually need is a clean image of a webpage rather than a paginated PDF with section-relative numbering, ScreenshotNeo provides a single screenshot request. Its cookie/consent handling accepts the banner like a visitor and removes more than 60 known consent platforms, newsletter popups, and chat widgets before capture; each step can be disabled.

For a direct request, see the ScreenshotNeo API documentation:

curl -G "https://api.screenshotneo.com/v1/shot" -d access_key=YOUR_API_KEY --data-urlencode url=https://stripe.com -o shot.webp

The same call in Python:

import requests
r = requests.get("https://api.screenshotneo.com/v1/shot", params={"access_key": "YOUR_API_KEY", "url": "https://stripe.com"}, timeout=90)
open("shot.webp", "wb").write(r.content)

And Node.js:

const q = new URLSearchParams({ access_key: 'YOUR_API_KEY', url: 'https://stripe.com' });
const res = await fetch(`https://api.screenshotneo.com/v1/shot?${q}`);
  • Bot checks, CAPTCHAs, blank pages, timeouts, failed loads, and cache hits cost nothing; response headers identify the page verdict and whether it was billed.
  • An MCP server exposes take_screenshot, get_page_info, and capture_pdf to Claude, Cursor, and other MCP clients.
  • The Free plan includes 1,000 screenshots per month with no card; paid plans start at $5 for 3,000 screenshots. Every feature is available on every plan.

Create a free ScreenshotNeo account to try it without a card.

FAQ

Can a footer script read the source document’s headings?

The HTML header/footer is a separate document. Its documented input is the query-string data wkhtmltopdf supplies, so it should not be treated as a live view of the source page DOM.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

Does pageOffset restart numbering at every heading?

The settings reference lists it as a global page offset. It does not document heading-level reset behavior.

What should be included in a regression test?

Include boundary cases: a heading at the bottom of a page, a section spanning several pages, large images, tables, and the exact production fonts and wkhtmltopdf build.

Frequently Asked Questions

Can a footer script read the source document’s headings?

The HTML header/footer is a separate document. Its documented input is the query-string data wkhtmltopdf supplies, not a live view of the source page DOM.

Does pageOffset restart numbering at every heading?

The settings reference lists pageOffset as a global page offset; it does not document heading-level reset behavior.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

What should be included in a regression test?

Test a heading at a page boundary, a multi-page section, large images, tables, and the exact production fonts and wkhtmltopdf build.

Product prices and availability are accurate as of the date/time indicated and are subject to change. Any price and availability information displayed on Amazon at the time of purchase will apply.

Leave a Reply

Your email address will not be published. Required fields are marked *

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

Read next

Recommended PC Tool
Recommended PC Tool
PC Slower Than It Used to Be?Free scan - under a minute
Outdated Drivers Are Slowing You DownFree scan - exact matches

Two free Windows tools

One Free Minute Could Fix That PC

Before you go - each of these free tools takes about a minute and tackles what quietly slows a Windows PC down.

Special offer. View Outbyte info, uninstall instructions, EULA, and Privacy Policy.