Skip to content

feat(api): addTableOfContents(...) — native clickable table of contents#237

Merged
DemchaAV merged 1 commit into
developfrom
feat/table-of-contents
Jun 25, 2026
Merged

feat(api): addTableOfContents(...) — native clickable table of contents#237
DemchaAV merged 1 commit into
developfrom
feat/table-of-contents

Conversation

@DemchaAV

Copy link
Copy Markdown
Owner

Why

A table of contents had to be hand-assembled: a row per entry, a leader sized by hand, and a manual two-pass to resolve the page numbers. addTableOfContents(...) makes it native — and is the payoff of the table-of-contents groundwork (pageIndex() #231, line().fill() #234, columns() #235, addPageReference #236).

What changed

  • addTableOfContents(toc -> toc.entry(label, anchor)) on every flow. Each entry becomes a row whose label links to the chapter (clickable), a dotted/dashed leader fills the gap, and the page number is resolved automatically from the laid-out document.
  • TocBuilder configures the title, leader(DocumentLeader.{NONE,DOTS,DASHES}), leader color, and entry/page-number/title text styles. A blank entry label or anchor is rejected at the call site.
  • No new engine code. Each row is assembled from the existing primitives — columns(auto(), weight(1), auto()), a line().fill() leader, and an addPageReference number; the numbers resolve through the already-merged page-reference two-pass.
  • The entry rows are added to the surrounding flow (not wrapped in one block), so a long contents paginates across pages — a single Section/Container wrapper is atomic and would force the whole TOC onto one page.

Lane: canonical DSL (TocBuilder, addTableOfContents) + style (DocumentLeader). Purely additive → japicmp-safe; documents without a TOC are byte-identical.

Verification

  • ./mvnw test -pl .green, 0 visual baselines changed.
  • TocTest: each entry prints its resolved page (PDFTextStripper), each label is a clickable go-to link to its anchor's page (PDActionGoTo destination == pageOf), and a 30-entry contents paginates across pages (builds without atomic-too-large, totalPages > 1). TocBuilderTest: blank label/anchor rejected.
  • A cold review drove a fix (validate entry(...) at the call site instead of a late generic error).
  • Runnable TocExample (a Contents page + five chapters) with a committed preview — labels auto-size, dotted leaders fill, page numbers resolve.

This closes the native table-of-contents gap: pageIndex → fill → columns → page reference → TOC.

A table of contents had to be hand-assembled: a row per entry, a leader
sized by hand, and a manual two-pass to resolve the page numbers.
addTableOfContents(toc -> toc.entry(label, anchor)) builds it natively —
each entry becomes a row whose label links to the chapter, a dotted or
dashed leader fills the gap, and the page number is resolved automatically
from the laid-out document.

It is assembled entirely from existing primitives — auto/weight columns, a
fill leader line, and a page reference — with no new engine code; the
numbers resolve through the existing page-reference two-pass. The entry
rows are added to the surrounding flow rather than wrapped in one block, so
a long table of contents paginates across pages. TocBuilder configures the
title, leader (DocumentLeader NONE/DOTS/DASHES), and text styles; a blank
entry label or anchor is rejected at the call site.

Tests: TocTest renders a contents page and asserts each entry prints its
resolved page, each label is a clickable go-to link to its anchor's page,
and a 30-entry contents paginates across pages; TocBuilderTest covers the
blank label/anchor rejection. Example: TocExample (a contents page plus
five chapters). Full suite green, no visual baselines changed.
Comment on lines +150 to +151
RowBuilder row = new RowBuilder()
.gap(6)
@DemchaAV DemchaAV merged commit 724114d into develop Jun 25, 2026
11 checks passed
@DemchaAV DemchaAV deleted the feat/table-of-contents branch June 25, 2026 15:06
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants