Skip to content

Headings and table of contents

Mark a block as a heading and it becomes navigable in every format that has a notion of navigation:

var document = Document.Create(doc => doc
.PageSize(PageSize.A4)
.Margin(48f)
.Paragraph(p => p.Text("Contents", new TextStyle(FontSize: 16f, FontWeight: FontWeight.Bold)))
.TableOfContents()
.PageBreak()
.Paragraph(p => p.Text("Introduction")).Heading(1)
.Paragraph(p => p.Text("Body text…"))
.Paragraph(p => p.Text("Background")).Heading(2)
.Paragraph(p => p.Text("More text…")));

Heading(level) chains onto the block you just added - the same shape as KeepTogether()/KeepWithNext(). Levels run from 1 to 6.

A paragraph supplies its own title from its text. Anything else - a table, row, list, image, or line - has no text to borrow, so pass one:

doc.Table(t => …).Heading(2, "Quarterly results");
doc.Image(logo).Heading(2, "Org chart");

Omitting it there throws rather than producing a silently empty bookmark.

Heading() only marks; it never moves content. To keep a heading with the section it introduces, say so explicitly - the two compose in either order:

doc.Paragraph(p => p.Text("Chapter 3")).Heading(1).KeepWithNext()
.Paragraph(p => p.Text("First paragraph of the chapter…"));
doc.TableOfContents(maxLevel: 3, leader: TocLeader.Dots, indentPerLevel: 14f);

It lists every heading at or above maxLevel, in document order, and needs no separate list of entries - the headings are the entries. Put a heading of your own above it if you want a “Contents” title; the element renders only the entries.

In PDF each entry is one line: the title, a dot leader, and the real page number right-aligned at the content edge. Entries are clickable. A long title is truncated rather than wrapped, which is what lets the page numbers be filled in after pagination without shifting anything - the number column is a fixed width, so a three-digit page shortens the leader instead of moving the line.

Format Headings Table of contents
PDF Bookmark outline in the reader’s sidebar, nested by level Real page numbers, dot leaders
DOCX w:outlineLvl (Word’s navigation pane) + a bookmark per heading A native { TOC } field Word fills in
HTML <h1>–<h6> with a stable id <nav> of in-page anchor links
XLSX ignored ignored

A level skip attaches to the nearest open ancestor: 1 → 3 makes the level-3 heading a child of the level-1 one rather than inventing an empty level 2. PDF and HTML apply the same rule, so the outline and the nav list have the same shape.

DOCX. Word computes the table of contents itself, because Papyra cannot know how Word will paginate. The document asks Word to build it on open; until it does, the field shows a short instruction. Right-click → Update Field refreshes it. One consequence: when a heading has an explicit title override, Word’s refreshed table shows the paragraph’s own text, since that is what Word collects. The override still governs PDF and HTML. A heading on a table or image needs a hidden marker paragraph, because an outline level is a paragraph property in Word.

HTML. No page numbers - a reflowable document has none, and the CSS counters that could supply them when printing are not implemented by Chrome or Firefox. Ids are slugged from the title (Chapter One → chapter-one) and deduplicated in document order, so two “Summary” headings become summary and summary-2.

XLSX. A worksheet has no pages and no outline, so headings and the table of contents are both ignored - the same as watermarks and the keep flags.

  • Headings apply to top-level blocks only; a heading inside a table cell, list item, or header band is not expressible (the method exists only on the document and section builders).
  • Page numbers appear in PDF only.
  • No per-entry styling: entries inherit the document’s default text style.
  • Levels are capped at 6, matching the HTML heading tags.