Skip to content

v1.0.4 — PDF/A conformance hardening (trailer /ID, /Info ↔ XMP parity, veraPDF CI)

Choose a tag to compare

@Nizoka Nizoka released this 25 Apr 19:29
· 8 commits to main since this release
4b15d22

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 /ID is now emitted on every PDF (was previously
    encrypted-only). Fixes ISO 19005-1 §6.1.3 / ISO 32000-1 §14.4.
  • /Info CreationDate and xmp:CreateDate share a single timestamp
    source and now both carry timezone offsets, eliminating veraPDF rule
    6.7.3 t1 mismatches.
  • dc:creator is 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:pdfa script + verapdf.yml CI workflow
    protect the project against PDF/A regressions going forward.

Fixed

  • fix(core): trailer /ID always emitted (previously encrypted-only).
    Unencrypted ID is MD5("pdfnative|" + title + "|" + creationDate + "|" + totalObjs) for deterministic regeneration. (src/core/pdf-assembler.ts)
  • fix(core): /Info CreationDatexmp:CreateDate byte-equivalent
    via new buildPdfMetadata() 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:creator emitted conditionally and XML-escaped;
    matches /Info /Author exactly 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:ModifyDate and
    xmp:MetadataDate (ISO 19005-2 §6.7.3 best practice).

Added

  • feat(scripts): scripts/validate-pdfa.ts — batch veraPDF runner.
    Walks test-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 a Scanned 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, runs npm run validate:pdfa. Pinned
    veraPDF version 1.26.5. Supports workflow_dispatch so the
    workflow can be triggered manually against any branch from the
    GitHub Actions UI before opening a pull request.
  • feat(scripts): npm run validate:pdfa script.
  • 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

Known limitations

  • Latin font embedding under PDF/A modes is not yet implemented.
    Files generated with tagged: true | 'pdfa1b' | 'pdfa2b' | 'pdfa2u' | 'pdfa3b' still emit unembedded standard 14 Helvetica and therefore
    still fail veraPDF rule 6.3.4. The pdfaid:part claim 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 — clean
  • npm run lint — 0 errors
  • npm 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.