v1.0.4 — PDF/A conformance hardening (trailer /ID, /Info ↔ XMP parity, veraPDF CI)
Released 2026-04-25
v1.0.4 is a patch release that begins a multi-step PDF/A conformance
hardening sweep. Two of the five real bugs found by the official veraPDF
reference validator on medical-800p.pdf and barcode-showcase.pdf are
fixed in this release; a third (Latin font embedding under PDF/A) is
scheduled for v1.0.5 and tracked transparently in
issue.
A new CI workflow installs the veraPDF CLI on every PR and validates
every sample PDF that claims PDF/A — preventing future regressions.
100 % API-backward-compatible with v1.0.3.
Highlights
- Trailer
/IDis now emitted on every PDF (was previously
encrypted-only). Fixes ISO 19005-1 §6.1.3 / ISO 32000-1 §14.4. /Info CreationDateandxmp:CreateDateshare a single timestamp
source and now both carry timezone offsets, eliminating veraPDF rule
6.7.3 t1 mismatches.dc:creatoris emitted only when an author is present and is
XML-escaped, fixing rule 6.7.3 mismatches on documents without an
explicit author.- New
npm run validate:pdfascript +verapdf.ymlCI workflow
protect the project against PDF/A regressions going forward.
Fixed
- fix(core): trailer
/IDalways emitted (previously encrypted-only).
Unencrypted ID isMD5("pdfnative|" + title + "|" + creationDate + "|" + totalObjs)for deterministic regeneration. (src/core/pdf-assembler.ts) - fix(core):
/Info CreationDate↔xmp:CreateDatebyte-equivalent
via newbuildPdfMetadata()helper; both include local timezone offset
(D:YYYYMMDDHHmmSS+HH'mm'and ISO 8601±HH:MM). Closes veraPDF rule
6.7.3 t1. (src/core/pdf-tags.ts) - fix(core): XMP
dc:creatoremitted conditionally and XML-escaped;
matches/Info /Authorexactly when present. Closes false-positive
rule 6.7.3 mismatches on author-less documents. (src/core/pdf-tags.ts) - fix(core): XMP packet adds matching
xmp:ModifyDateand
xmp:MetadataDate(ISO 19005-2 §6.7.3 best practice).
Added
- feat(scripts):
scripts/validate-pdfa.ts— batch veraPDF runner.
Walkstest-output/, detects PDF/A claims via XMP, invokes veraPDF CLI,
reports compliant/failed counts. Skips silently when veraPDF is missing.
Prints per-OS install hints (macOS / Linux / Windows / online demo)
and aScanned N; M claim PDF/A, K skipped (not PDF/A)summary line. - feat(ci):
.github/workflows/verapdf.yml— installs veraPDF CLI on
Temurin 17, regenerates samples, runsnpm run validate:pdfa. Pinned
veraPDF version1.26.5. Supportsworkflow_dispatchso the
workflow can be triggered manually against any branch from the
GitHub Actions UI before opening a pull request. - feat(scripts):
npm run validate:pdfascript. - test(core): tests/core/pdf-trailer-id.test.ts
— 18 new tests covering trailer/ID, date format with timezone,
XMP ↔ /Info parity, dc:creator escaping & conditional emission. - docs(guides): new Installing veraPDF locally + Troubleshooting
sections in docs/guides/pdfa.html
document the install path on each OS, the online demo as a no-install
fallback, and why plain ISO 32000-1 files are auto-skipped instead of
being forced through a PDF/A profile. - docs(landing): new "Designed for low-impact computing" section
on docs/index.html — factual sustainability
bullets only (zero deps, on-device generation, no telemetry,
tree-shakeable ESM, streaming output, external tooling stays
external). No carbon claims, no badges we cannot verify. - docs(readme): two factual bullets added to Highlights covering
on-device generation and the absence of network calls.
Changed
- chore(meta): version bump to
1.0.4. - chore(meta): existing CreationDate regex tests updated to allow
the new mandatory timezone offset.
Documentation
- docs: release-notes/v1.0.4.md, tracking issue
release-notes/draft-issue-v1.0.4-pdfa-conformance.md,
v1.0.5 epic [issue](#28 d). - docs: new guide docs/guides/pdfa.html
documents PDF/A modes, current limitations, validator workflow. - docs(kb): new instruction file
.github/instructions/pdfa-conformance.instructions.md
captures ISO clauses, validator rules, and the v1.0.5 roadmap.
Known limitations
- Latin font embedding under PDF/A modes is not yet implemented.
Files generated withtagged: true | 'pdfa1b' | 'pdfa2b' | 'pdfa2u' | 'pdfa3b'still emit unembedded standard 14 Helvetica and therefore
still fail veraPDF rule 6.3.4. Thepdfaid:partclaim in XMP must be
treated as aspirational until v1.0.5 lands. The CI guardrail expects
this and is wired to start blocking PRs once v1.0.5 ships.
Verification
npm run typecheck:all— cleannpm run lint— 0 errorsnpm run test— 1 624 / 1 624 passing (1 606 prior + 18 new)npm run build— ESM + CJS + DTS produced
Migration
No code changes required. Outputs may differ byte-for-byte (new /ID
array, timezone in CreationDate, conditional dc:creator). If your
test fixtures snapshot full PDF bytes, regenerate them.
FAQ
Why does veraPDF still report failures on doc-lists.pdf (or any
other plain sample)? Those samples are not generated with
tagged: true, so they do not claim PDF/A in their XMP packet. The
veraPDF online demo lets you pick a profile manually, which then
forces the file through PDF/A rules and surfaces unrelated failures
by design (missing XMP, unembedded fonts, DeviceRGB without an
OutputIntent — all expected for an ISO 32000-1 document). The
npm run validate:pdfa wrapper avoids this trap by skipping every
file that does not declare pdfaid:part in XMP. See the
Troubleshooting section
for details.
Is this a breaking change? No public API breaks. Output bytes
do change (new /ID, timezone offsets in CreationDate,
conditional dc:creator). Consumers that snapshot full PDF bytes
in their test fixtures will need to regenerate those snapshots.
SemVer-wise this is a patch.