ScreenshotNeo

BlogHTML to image & PDF

How to Add Dynamic Section Names to EvoHTMLtoPDF Headers

Add section-aware headers to EvoHTMLtoPDF PDFs with Classic per-page callbacks, Next templates, and reliable pagination strategies.

By the ScreenshotNeo team30 September 20267 min read

How to Add Dynamic Section Names to EvoHTMLtoPDF Headers

Short answer: EvoHTMLtoPDF does not document a built-in currentSection, h1, or h2 header variable. In Classic .NET, use the per-page PrepareRenderPdfPageEvent callback, read eventParams.PageNumber, and supply the section name from your own page-to-section mapping. In Next browser mode, the documented header values are date, title, url, pageNumber, and totalPages; section headings are not included.

The important limitation is pagination. EvoPDF exposes the page number in Classic, but the reviewed documentation does not provide an automatic map from flowing HTML headings to output pages. Your application must create that association, or render sections separately when exact labels are required.

Choose the EvoPDF API you are using

API or mode Header mechanism How to show a section name
Classic .NET PrepareRenderPdfPageEvent Look up a label using the callback’s PageNumber.
Next browser mode PdfDocumentOptions.HeaderTemplate Use the documented values only; provide section labels at the application or document level.

Verify the package and rendering mode before copying an example. Classic event APIs must not be assumed to exist in Next.

Classic .NET: add a section label per page

The Classic documentation describes PrepareRenderPdfPageEvent as a per-page customization point. Its event parameters expose PageNumber, and the event can add custom header or footer content for that page. The following pattern keeps the section lookup in your application.

A section-to-page map lets a per-page callback choose the correct header label.
A section-to-page map lets a per-page callback choose the correct header label.

1. Define the page-to-section map

using System;
using System.Collections.Generic;

public sealed class SectionRange
{
    public int FirstPage { get; init; }
    public int LastPage { get; init; }
    public string Name { get; init; } = "";
}

public static class SectionLookup
{
    public static string ForPage(
        IReadOnlyList<SectionRange> sections,
        int pageNumber)
    {
        foreach (var section in sections)
        {
            if (pageNumber >= section.FirstPage &&
                pageNumber <= section.LastPage)
            {
                return section.Name;
            }
        }

        return "";
    }
}

2. Attach the callback

Use the event signature and header-element classes from the Classic package version installed in your project. The exact type names can differ between releases; the documented contract is the page callback and its PageNumber value.

// Illustrative wiring for the Classic .NET API.
// Keep the event registration and header element types from your
// installed EvoPDF version's Classic documentation/sample.

var sections = new List<SectionRange>
{
    new() { FirstPage = 1, LastPage = 3, Name = "Introduction" },
    new() { FirstPage = 4, LastPage = 8, Name = "Implementation" },
    new() { FirstPage = 9, LastPage = 11, Name = "Troubleshooting" }
};

converter.PrepareRenderPdfPageEvent += (sender, eventParams) =>
{
    var sectionName = SectionLookup.ForPage(
        sections,
        eventParams.PageNumber);

    // Add the sectionName to the page header using the custom
    // header/footer element API shown by your Classic package.
    // Keep page-number tokens separate from this label.
};

This is the safe architecture: the callback chooses a value; your code owns the map. Do not expect EvoPDF to infer the current h1 or h2 from arbitrary flowing HTML unless your exact release documents such a feature.

Page numbers are a separate feature

Classic footer TextElement templates document &p; for the current page and &P; for the total page count. Keep these substitutions independent from your section lookup.

// Conceptual Classic footer text:
"Section name     Page &p; of &P;"

How to build a reliable map

  1. Use stable section boundaries. If a section is a separately rendered document, its first and last pages are known.
  2. Use a pagination pass. For one long HTML document, generate or inspect the pagination first, then persist the page ranges used by the header callback.
  3. Prefer explicit page breaks when labels must be exact. A section that can start on a predictable page is easier to label than content that flows across variable pages.
  4. Handle unassigned pages. Return an empty label or a document-level label for cover, table-of-contents, and blank pages.

Fonts, images, browser differences, margins, and late-loading content can move a heading to another page. A character-count estimate is not a dependable page map.

Next browser mode: use the documented template values

Next browser-mode headers use PdfDocumentOptions.HeaderTemplate. The documented injected values are:

Value Meaning
date Formatted print date
title Document title
url Document URL
pageNumber Current page number
totalPages Total page count

There is no documented current-section value in that list. Do not add {section} or {subsection} and assume EvoPDF will populate it.

// Next browser-mode shape; use the exact option/container names
// from the Next package version in your project.
var options = new PdfDocumentOptions
{
    HeaderTemplate = """
      <div style='font-size:9px;width:100%;padding:0 24px;'>
        <span class='title'></span>
        <span style='float:right'>
          Page <span class='pageNumber'></span>
          of <span class='totalPages'></span>
        </span>
      </div>
    """
};

If every section needs a different header in Next, a practical application-level design is to render each section as its own PDF with its own template, then merge the PDFs. This is an implementation option to validate against your exact Next package and merging workflow; it is not a documented automatic heading lookup.

Document shape Recommended design
Short report with fixed page breaks Create a small page-range map and use the Classic callback.
Long article with unpredictable wrapping Run a pagination-aware preparation step, or render sections separately.
Next browser-mode output Use title, URL, date, and page values; handle section labels outside the standard template.
Cover and table of contents Assign explicit labels such as an empty string, “Contents,” or a document title.

Troubleshooting

The header always shows the same section

Cause: the callback is not using PageNumber, or the lookup ranges overlap incorrectly.
Fix: log the page number and selected range, sort ranges by first page, and ensure each page belongs to at most one range.

The section changes on the wrong page

Cause: content reflow changed pagination after the map was created.
Fix: regenerate the map with the same fonts, margins, assets, and rendering settings used for production. Avoid estimates based only on HTML length.

{section} prints literally in Next

Cause: section is not one of the documented Next template values.
Fix: use the documented values, or render sections independently with application-provided templates.

Page numbers work but section names do not

Cause: page-number substitution and section lookup are separate mechanisms.
Fix: keep &p;/&P; (Classic) or pageNumber/totalPages (Next) for numbering, and supply section text from your own data.

The callback is never invoked

Cause: the project is using Next browser mode, a different converter class, or an event registration from another package version.
Fix: confirm the installed API generation and follow that version’s event-registration sample. Do not transfer Classic APIs to Next without verification.

Headers overlap content

Cause: header height and page margins do not reserve the same space.
Fix: increase the top margin or header spacing, and test with the longest section label and largest font.

Performance, reliability, and cost considerations

  • Per-page callbacks are inexpensive compared with rendering large pages, but complex header construction still runs for every output page.
  • Cache or reuse the section map when the source HTML and rendering settings are unchanged.
  • Fonts, remote images, CSS, JavaScript timing, and browser version can alter page boundaries. Pin these inputs for reproducible output.
  • For high-value documents, verify the generated PDF visually or extract header text in a post-render check. A successful conversion does not prove that every label matches its section.
  • When section labels must be legally or operationally exact, separate section rendering gives you a clearer failure boundary than guessing page ranges in one flowing document.

Or skip the browser setup

If your goal is a clean screenshot or PDF of a web page rather than a custom EvoHTMLtoPDF pipeline, ScreenshotNeo provides a single HTTP request. Its cleanup step accepts cookie or consent banners and removes more than 60 known consent platforms, newsletter popups, and chat widgets before capture; each cleanup step can be disabled. Only clean shots are billed, while bot checks, blank pages, timeouts, failed loads, and cache hits are free, with the result identified by X-Page-Verdict and X-Billed headers. An MCP server also lets Claude, Cursor, and other MCP clients call take_screenshot, get_page_info, and capture_pdf.

ScreenshotNeo removes common overlays before capture so the output is ready to use.
ScreenshotNeo removes common overlays before capture so the output is ready to use.

See the ScreenshotNeo API documentation for the complete option list.

curl -G "https://api.screenshotneo.com/v1/shot" \
  -d access_key=YOUR_API_KEY \
  --data-urlencode url=https://stripe.com \
  -o shot.webp
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)
const q = new URLSearchParams({
  access_key: 'YOUR_API_KEY',
  url: 'https://stripe.com'
});
const res = await fetch(`https://api.screenshotneo.com/v1/shot?${q}`);

There are 1,000 free screenshots each month with no card. Paid plans start at $5 for 3,000 shots. Create a free ScreenshotNeo account.

FAQ

Can EvoHTMLtoPDF read the current h1 automatically?

The reviewed documentation does not document an automatic current-heading token. Provide the label through your application or render sections independently.

Can I combine a dynamic section name with page numbers?

Yes. Keep the section lookup separate from the documented page-number substitutions for the API generation you use.

Should I use Classic event code in Next?

No. Confirm the exact Next package and version. The documented Next mechanism is the browser header template.

What happens when a section spans several pages?

Map every page in that range to the same label, or choose a policy such as labeling only the first page. Decide this in application code.

Why did a heading move after deployment?

Pagination depends on rendering inputs such as fonts, assets, margins, and timing. Keep those inputs consistent and rebuild the page map when they change.