PDF/A & Tagged PDF
Archival conformance (ISO 19005) and logical document structure (Tagged PDF). Claim detection runs instantly in the browser; full validation runs on the TruPDF document service — the same self-hosted container that powers structural redaction.
PDF/A claim detection (instant, offline)
Every PDF/A producer stamps the conformance level into XMP metadata
(pdfaid:part / pdfaid:conformance). Reading the claim is instant
and needs no server:
const claim = await sdk.getPdfAClaim();
// { part: 2, conformance: 'B', label: 'PDF/A-2B' } — or null
if (claim) badge.textContent = claim.label;Returns null when the document makes no PDF/A claim. A claim is
not proof — any tool can write the XMP entry. For proof, run full
validation.
Full ISO 19005 validation
sdk.validatePdfA() posts the loaded document to the document
service's POST /validate/pdfa endpoint, runs the full ISO 19005
rule set through the validation engine, and returns a typed report
with per-clause failures:
import { PdfAValidationError } from '@trupdf/viewer';
try {
const report = await sdk.validatePdfA({
endpoint: process.env.NEXT_PUBLIC_TRUPDF_DOCSERVICE_URL!, // your deployment — required, no default
secret: process.env.NEXT_PUBLIC_TRUPDF_DOCSERVICE_SECRET,
flavour: 'auto', // default — the validation engine picks from the document's claim
});
if (report.compliant) {
console.log(`Valid PDF/A-${report.flavour} — ${report.totalChecks} checks passed`);
} else {
for (const rule of report.failedRules) {
console.warn(
`§${rule.clause} test ${rule.testNumber}: ${rule.description} (${rule.failedChecks}×)`,
);
}
}
} catch (e) {
if (e instanceof PdfAValidationError) console.error(e.code, e.message);
}Result shape
| Field | Meaning |
|---|---|
compliant | true when every ISO 19005 rule check passed |
flavour | Flavour validated against, e.g. 2B (after auto-detection) |
statement | Human-readable compliance statement from the validation engine |
totalChecks | Number of rule checks executed |
failedRules | { clause, testNumber, description, failedChecks }[] — ISO 19005 clause per failure |
truncated | true when failedRules was capped server-side |
Flavours
auto (default), 1a, 1b, 2a, 2b, 2u, 3a, 3b, 3u.
With auto, the validation engine detects the flavour from the
document's own claim.
Errors
All failures throw PdfAValidationError with a typed code:
| Code | When |
|---|---|
NETWORK | Request never reached the service (or no endpoint) |
HTTP | Non-2xx response without a more specific cause |
UNPARSEABLE_PDF | The service could not parse the PDF binary |
INVALID_FLAVOUR | The service rejected the requested flavour |
BAD_RESPONSE | Response was not JSON / unexpected shape |
Endpoint configuration
The endpoint URL must come from your own config/env — TruPDF
never ships a default URL. When the service enforces a shared secret
(TRUPDF_INTERNAL_SECRET on the service side), pass it as secret
and it is sent as the X-TruPDF-Internal-Secret header.
The endpoint lives in the same self-hosted TruPDF document service that powers structural redaction:
docker compose --profile phase8 up -dSee Advanced setup for service env vars.
Tagged PDF
Tagged PDF is the logical structure tree screen readers and PDF/UA workflows consume. Two read APIs:
const { isTagged, suspects } = await sdk.getTaggedPdfInfo();
// Catalog /MarkInfo: isTagged = /Marked true; suspects = /Suspects true
// (Acrobat shows the latter as "tagged, with suspects")
const tree = await sdk.getStructureTree(1); // 1-based page number
// StructureNode | null (null on untagged documents)Each StructureNode is normalised for hosts:
interface StructureNode {
role: string; // after role-mapping: 'Document', 'H1', 'P', 'Table', 'Figure', …
alt?: string; // /Alt alternate description (figures)
lang?: string; // /Lang override
children: StructureNode[];
contentItems: number; // marked-content leaves under this node
}Walk it to extract headings, table semantics, or figure alt text — the same data a PDF/UA checker inspects:
function headings(node: StructureNode, out: string[] = []): string[] {
if (/^H[1-6]$/.test(node.role)) out.push(node.role);
node.children.forEach((c) => headings(c, out));
return out;
}Reading is v1 — tag authoring is on the roadmap. See Accessibility for how the viewer itself exposes content to screen readers.
Engine-level APIs (non-SDK hosts)
The same capabilities exist as standalone functions, exported from
@trupdf/viewer / @trupdf/core, for hosts that work with the
render engine directly:
import {
detectPdfAClaim,
validatePdfA,
readTaggedPdfInfo,
readStructureTree,
} from '@trupdf/viewer';
const claim = await detectPdfAClaim(pdf); // the loaded document handle
const info = await readTaggedPdfInfo(pdf);
const tree = await readStructureTree(pdf, 1);
const report = await validatePdfA(await pdf.getData(), {
endpoint: process.env.NEXT_PUBLIC_TRUPDF_DOCSERVICE_URL!,
});