Skip to content

feat(api): DocumentPageNumbering — page-number offset/restart/style#230

Merged
DemchaAV merged 3 commits into
developfrom
feat/page-numbering
Jun 25, 2026
Merged

feat(api): DocumentPageNumbering — page-number offset/restart/style#230
DemchaAV merged 3 commits into
developfrom
feat/page-numbering

Conversation

@DemchaAV

@DemchaAV DemchaAV commented Jun 25, 2026

Copy link
Copy Markdown
Owner

Why

Header / footer {page} / {pages} tokens always counted from 1 in decimal, on every page. That can't express the common book/report shapes: a cover with no number, roman or alphabetic numbering instead of decimal, or a count that offsets/restarts partway through. (Switching numbering style mid-document — roman front matter then arabic body — is a per-section concern, i.e. the multi-section follow-up, not this PR.)

What changed

  • DocumentPageNumbering (document.output, immutable Lombok value) on DocumentHeaderFooter.builder().numbering(...): startAt (printed value on the first counted page), countFrom (physical page where counting begins — earlier pages are uncounted), showOnFirstPage, and a DocumentPageNumberStyle (DECIMAL, LOWER_ROMAN, UPPER_ROMAN, LOWER_ALPHA, UPPER_ALPHA). Under an offset, {pages} expands to the counted total (startAt + (totalPages - countFrom)), not the physical page count.
  • Suppression is whole-zone: a page where the number is not shown (showOnFirstPage=false, or before countFrom) skips that entire header/footer zone — what an uncounted cover / front matter wants (documented; put always-on branding in a separate zone).
  • Layering: DocumentPageNumberStyle is a backend-neutral marker; the engine owns the numeral formatting in an engine-local PageNumberStyle, mapped at the options boundary (PdfOptionsAdapter), so the shared engine keeps zero document.* imports — mirroring the existing zone-enum split. HeaderFooterConfig gains numbering fields + an instance resolveTokens + an appliesTo predicate; the static resolvePlaceholders(String,int,int) is retained for binary compatibility (public since v1.7.0).
  • The default (DocumentPageNumbering.DEFAULT: decimal, no offset, shown everywhere) reproduces the pre-1.9 output, so existing header/footer rendering is byte-identical. SessionChromeApi.header/footer signatures are unchanged — numbering rides inside DocumentHeaderFooter.

Lane: canonical public API + shared-engine.

Verification

  • ./mvnw test -pl . — 1518 tests, 0 failures, 0 visual baselines changed (DEFAULT numbering proven equal to the old static output).
  • PdfPageNumberingTest (render-level, PDFTextStripper): a roman front-matter offset renders i / iii … iii / iii with the cover uncounted, and showOnFirstPage=false hides only the first page's number.
  • PageNumberStyleTest (numerals incl. out-of-range fallback), HeaderFooterConfigNumberingTest (offset / counted-total {pages} / appliesTo / static-shim byte-identity), DocumentPageNumberingTest (DEFAULT + builder + carries through DocumentHeaderFooter).
  • Runnable PageNumberingExample (uncounted cover + lower-roman body) with a committed preview and an examples README row.

@since 1.9.0 on all new public API. All changes are additive (new types/fields/getters) + a retained static method — binary-compatible against the v1.7.0 japicmp baseline.

DemchaAV added 3 commits June 25, 2026 09:49
Header and footer {page} / {pages} tokens can now offset, restart, restyle, and
suppress-on-first-page per zone via DocumentHeaderFooter.builder().numbering(...).
DocumentPageNumbering carries startAt (printed value on the first counted page),
countFrom (physical page where counting begins), showOnFirstPage, and a
DocumentPageNumberStyle (decimal / lower+upper roman / lower+upper alpha) — e.g.
lower-roman front matter then arabic body, or no number on a cover page. Under an
offset {pages} expands to the counted total, not the physical page count.

DocumentPageNumberStyle is a backend-neutral marker; the engine owns the numeral
formatting in an engine-local PageNumberStyle (mapped at the options boundary,
keeping the engine free of document.* deps). HeaderFooterConfig gains numbering
fields plus an instance resolveTokens and an appliesTo predicate; the static
resolvePlaceholders is retained for binary compatibility. The default numbering
is decimal, no offset, shown on every page, so existing header/footer output is
byte-identical.

Verified: ./mvnw test -pl . — 1518 tests, 0 baselines changed. PdfPageNumberingTest
reads back per-page footers (roman front-matter offset, suppress-on-first-page)
with PDFTextStripper; a runnable PageNumberingExample ships with a committed preview.
The class javadoc linked the Lombok-generated getStartAt()/getCountFrom()
getters, which the javadoc tool cannot resolve from source — a hard
"reference not found" error under the JDK 17 doclint (newer JDKs were lenient,
so it passed locally and on JDK 21/25 but failed the JDK 17 CI javadoc gate).
Reference the fields as {@code startAt} / {@code countFrom} instead.
A single numbering policy applies one style per zone, so "lower-roman front
matter then arabic body" in one document is not achievable here — switching
numbering style mid-document is a per-section (multi-section) concern. Reword
the example to what one policy actually does: roman/alpha instead of decimal,
an uncounted cover, an offset/restarted count.
@DemchaAV DemchaAV merged commit 9b61478 into develop Jun 25, 2026
11 checks passed
@DemchaAV DemchaAV deleted the feat/page-numbering branch June 25, 2026 09:20
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.

1 participant