Skip to content

feat(pdf): reproducible output via a deterministic() backend option#363

Merged
DemchaAV merged 1 commit into
2.0-devfrom
feat/deterministic-pdf-output
Jul 11, 2026
Merged

feat(pdf): reproducible output via a deterministic() backend option#363
DemchaAV merged 1 commit into
2.0-devfrom
feat/deterministic-pdf-output

Conversation

@DemchaAV

Copy link
Copy Markdown
Owner

Why

The same document rendered twice produced different PDF bytes: PDFBox seeds the document /ID
from the current time on every save, and that /ID was the only source of non-determinism
(GraphCompose never set a CreationDate). That blocks reproducible builds and byte-level output
assertions.

What

  • PdfFixedLayoutBackend.builder().deterministic(true) — enables reproducible output: pins the
    document CreationDate / ModDate to a fixed default timestamp and sets the PDF /ID to a stable
    value derived from the document's info dictionary (MD5), instead of PDFBox's time-seeded /ID.
  • .deterministic(Instant) — the same, with an explicit timestamp.
  • Off by default (deterministicTimestamp == null): output is unchanged — live timestamp and
    PDFBox /ID.
  • Applied in both buildDocument (render / toBytes) and buildSectionsDocument (the @Beta
    section-render path).

Tests

  • New PdfDeterministicOutputTest (3): the same document rendered twice with deterministic(true)
    is byte-identical; deterministic(Instant) pins the CreationDate to that instant; the default
    backend leaves the CreationDate unset (proving it's opt-in).
  • ./mvnw -f aggregator/pom.xml verify -pl :graph-compose-qa -am — full compile + javadoc gate +
    the qa visual/parity byte-identity suite (643): green. The default path is a no-op, so existing
    rendered output is unchanged.

Architectural note: determinism is a PDF-serialization concern (/ID, dates), so the knob lives on
the PDF backend builder rather than the canonical DocumentSession — it keeps a format-specific
detail out of the backend-neutral document model.

@DemchaAV DemchaAV force-pushed the feat/deterministic-pdf-output branch from 26d87a1 to c645864 Compare July 11, 2026 08:58
PdfFixedLayoutBackend.builder().deterministic(true) (or .deterministic(Instant))
pins the document CreationDate / ModDate and derives the PDF /ID from the
document metadata instead of PDFBox's time-seeded default, so the same document
renders to byte-identical PDF output across runs — for reproducible builds and
byte-level output tests. Off by default; existing output keeps the live
timestamp and /ID. Applied on both the render and section-render paths.
@DemchaAV DemchaAV force-pushed the feat/deterministic-pdf-output branch from c645864 to b791838 Compare July 11, 2026 09:24
@DemchaAV DemchaAV merged commit 9212317 into 2.0-dev Jul 11, 2026
12 checks passed
@DemchaAV DemchaAV deleted the feat/deterministic-pdf-output branch July 11, 2026 09:40
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