Conversation
| .margin(DocumentInsets.of(24)) | ||
| .create()) { | ||
| session.pageFlow(page -> { | ||
| page.addRow(r -> r.gap(4).columns(com.demcha.compose.document.style.DocumentRowColumn.auto(), |
| .margin(DocumentInsets.of(24)) | ||
| .create()) { | ||
| session.pageFlow(page -> { | ||
| page.addRow(r -> r.gap(4).addParagraph("Appendix") |
| session.pageFlow(page -> { | ||
| page.addRow(r -> r.gap(4).addParagraph("Appendix") | ||
| .addPageReference("appendix", DocumentTextStyle.DEFAULT, TextAlign.RIGHT)); | ||
| page.addRow(r -> r.gap(4).addParagraph("Glossary") |
…ence A "see page N" cross-reference needed a manual two-pass: lay the document out, read pageIndex(), then re-render with the number substituted. addPageReference(anchor) does it in one authoring pass — the engine resolves the number from the laid-out document and renders it. A document that contains a page reference is compiled in two passes (the first resolves every anchor's page, the next renders the references), then re-resolved to a fixed point so a reference whose own width re-wraps a neighbour and shifts a page is corrected rather than left stale; the resolve is capped. The reference reserves only its glyph width, so its footprint does not shift the pages it reports. Documents without a page reference compile once and are byte-identical. PageReferenceNode is a text leaf; the resolved page flows to it through a default layout-context accessor, so existing node definitions are untouched. Available on flows and inside rows (the number column of a table-of-contents row); pageIndex() remains for programmatic access. Tests: PageReferenceTest covers a forward reference printing the resolved page (via PDFTextStripper, equal to pageIndex().pageNumberOf), multiple references each reporting their final page, and an unresolved anchor rendering without throwing. Example: PageReferenceExample rewritten to the native one-pass form (a dot-leader entry). Full suite green, no visual baselines changed.
This was referenced Jun 25, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Why
Printing "see page N" (or a table-of-contents number) required a manual two-pass: lay the document out into a throwaway session, read
pageIndex(), then re-render the whole document with the number substituted.addPageReference(anchor)makes it a single authoring pass — the engine resolves the number from the laid-out document and renders it.This is the engine foundation split out of the table-of-contents work: the page-reference primitive + the two-pass resolve (a follow-up assembles
addTableOfContentson top).What changed
addPageReference(anchor)on flows, and on rows (the number column of a TOC entry) →PageReferenceNode, a text leaf that prints the 1-based page itsanchor(...)lands on.resolvedPage(anchor), empty by default), so existing node definitions are untouched. Documents without a page reference compile once and are byte-identical.pageIndex()remains for programmatic access.Lane: shared-engine layout (two-pass + the context accessor, both
@Internal/@BetaSPI) + canonical DSL/node (addPageReference,PageReferenceNode). Additive → japicmp-safe.Verification
./mvnw test -pl .— green, 0 visual baselines changed.PageReferenceTest: a forward reference (on page 1, target on page 2) prints the resolved page — asserted viaPDFTextStripperto equalpageIndex().pageNumberOf; multiple references each report their own final page; an unresolved anchor renders without throwing.PageReferenceExamplerewritten from the manual two-pass to the native one-pass form (a dot-leader "Appendix ···· 2" entry), with a committed preview.Next:
addTableOfContents(...)assembles these references + dotted leaders (line().fill()) + auto/weight columns into a clickable, page-numbered TOC.