function ValidatePdfUa: TPdfUaValidationResult;
ValidatePdfUa walks the open document looking for the PDF/UA-1 file-format markers defined by ISO 14289-1:2014:
pdfuaid:part = 1 and the matching pdfaExtension:schemas description (§5).dc:title entry inside the XMP packet (§7.1).StructTreeRoot in the catalog or somewhere in the file (§7.1) — required so the document can carry tagging at all./MarkInfo << /Marked true /Suspects false >> (§7.1)./ViewerPreferences << /DisplayDocTitle true >> (§7.1)./Lang with a BCP 47 language tag (§7.2)./ID array and the absence of encryption.xmp:CreateDate / xmp:ModifyDate (recommended for Info/XMP parity).The result is a TPdfUaValidationResult record:
puc1 (PDF/UA-1 identifier found), pucNone (no PDF/UA marker), or pucUnknown (parse failed).TPdfUaValidationIssues set containing zero or more values: pvuaiMissingXmpMetadata, pvuaiMissingPdfUaIdentifier, pvuaiMissingExtensionSchema, pvuaiMissingDcTitle, pvuaiMissingStructTreeRoot, pvuaiMissingMarkInfo, pvuaiSuspectsTrue, pvuaiMissingDisplayDocTitle, pvuaiMissingLang, pvuaiEncryptionPresent, pvuaiMissingTrailerId, pvuaiMissingXmpDates, pvuaiFontNotEmbedded, pvuaiFontMissingToUnicode, pvuaiMissingTabsOrder, pvuaiFigureMissingAlt, pvuaiFormulaMissingAlt, pvuaiNoteMissingId. Round 1 (v1.66.0) adds pvuaiRoleMapMissing (7.1), pvuaiOptionalContentAsKey (7.10), pvuaiXfaPresent (7.15), pvuaiTrapNetAnnotation (7.18.2), pvuaiReferenceXObject (7.20), and pvuaiEncryptionPBitMissing (7.16 — the precise encryption violation that supersedes the old any-encryption flag). Round 2 (v1.67.0) adds pvuaiLinkMissingContents (7.18.5), pvuaiAnnotationMissingContents (7.18.1), pvuaiEmbeddedFileMissingName (7.11), pvuaiMediaClipMissingAlt (7.18.6.2), and pvuaiCidFontMissingCidToGidMap (7.21.3.2). Round 3 (v1.68.0) adds pvuaiUriActionIsMap (7.18.5) and pvuaiHeadingSequenceViolation (7.4.2 — the first structure-tree-level rule; ValidatePdfUaStructureElements now verifies the numbered-heading sequence). Round 4 (v1.69.0) adds pvuaiWidgetOutsideForm (7.18.4 — structure-tree Widget nesting) and pvuaiSubsetMissingCharSet (7.21.4.2). Round 5 (v1.70.0) adds pvuaiSubsetMissingCidSet (7.21.4.2), pvuaiToUnicodeForbiddenValue (7.21.7), pvuaiLangInvalidFormat (7.2), and pvuaiOrderedListMissingListNumbering (7.6 — raised since v1.71.0 when an L element declares a ListNumbering value outside the Table 347 set; an absent attribute is not flagged). Round 7 (v1.80.0) adds the §7.5 table checks pvuaiTableStructureInvalid (TH/TD outside a TR, or TR/THead/TBody/TFoot outside their required container), pvuaiTableHeaderMissingScope (a TH carrying neither a Scope attribute, nor an ID, in a table where no cell uses a Headers association), and pvuaiTableHeadersUndefinedId (cells reference headers via Headers arrays while no structure element in the document defines an ID; an individual dangling entry beside resolvable ones is tolerated, matching the authoritative validator). v1.81.0 adds the §7.6 list-nesting check pvuaiListStructureInvalid (an LI outside an L container, or a Lbl/LBody outside an LI item; nested lists and Lbl inside Note/BibEntry are accepted). v1.82.0 adds the §7.1 RoleMap resolution checks pvuaiRoleMapRemapsStandard (a RoleMap entry redefines a standard structure type) and pvuaiRoleMapUnresolved (a used non-standard structure type has no RoleMap entry, or its chain is dangling or cyclic; indirect chains reaching a standard type are accepted and unused RoleMap entries are never judged). v1.83.0 adds the §7.4.4 heading-model checks pvuaiHeadingModelMixed (the generic H tag mixed with numbered H1..Hn headings) and pvuaiHeadingMultipleInNode (a structure node carrying more than one child H heading). v1.84.0 adds the §7.1 untagged-content check pvuaiUntaggedContent (a rendered text or image object neither claimed by the page’s structure tree via its marked-content ID nor marked as an Artifact; bare path objects are outside the check to avoid flagging ubiquitous decoration). v1.85.0 adds the §7.15 dynamic-XFA check pvuaiXfaDynamic (the XFA configuration sets dynamicRender to required) and retires the any-XFA candidate pvuaiXfaPresent (static XFA conforms; the enum value remains but is no longer raised). Since v1.85.1 the §7.18.3 pvuaiMissingTabsOrder check runs per page: every annotated page needs /Tabs /S in its own dictionary. v1.86.0 adds the §7.21.6 TrueType encoding checks pvuaiTrueTypeEncodingInvalid (a non-symbolic TrueType lacks Encoding, or it is neither MacRomanEncoding nor WinAnsiEncoding directly or via BaseEncoding) and pvuaiSymbolicTrueTypeEncoding (a symbolic TrueType carries a non-standard Encoding name; standard names are tolerated because compliant documents routinely mislabel standard-encoded fonts as symbolic). v1.87.0 adds the §7.21.3 composite-font checks pvuaiCidSystemInfoMismatch (embedded CMap and descendant CIDFont disagree on Registry/Ordering, or the CIDFont Supplement exceeds the CMap’s), pvuaiCMapNotEmbedded (a CMap referenced by name is not a Table 118 predefined CMap) and pvuaiCMapWModeMismatch (an embedded CMap’s dictionary WMode differs from its stream content; only byte-readable, uncompressed CMap streams can be judged), and broadens pvuaiCidFontMissingCidToGidMap to also flag a CIDToGIDMap name other than Identity. v1.93.0 adds the Pdfium object-model layer — three precise per-font / per-character issues computed from the live Pdfium object tree: pvuaiPdfiumFontNotEmbedded (§7.21.4, a font a page actually uses is not embedded per FPDFFont_GetIsEmbedded), pvuaiPdfiumUnicodeMapError (§7.21.7, a page character has an invalid Unicode mapping per FPDFText_HasUnicodeMapError), and pvuaiPdfiumNotDefGlyph (§7.21.8, a character renders to Unicode 0 / .notdef). These complement the byte-level pvuaiFontNotEmbedded / pvuaiFontMissingToUnicode heuristics (which only fire on a document-wide zero-embedded or no-/ToUnicode signal); the walk short-circuits at three levels (page loop, per-object loop, character scan). v1.94.0 adds six second-pass issues from a clause-by-clause re-audit: pvuaiHeadingFormatInvalid (§7.4.3, a heading tag starting with H that is neither the generic H nor H + pure Arabic digits — separators, spaces and Roman numerals are forbidden), pvuaiMissingDocumentOutline (§7.17, a multi-page document carrying no bookmark outline; single-page documents are exempt), pvuaiStructElementLangInvalid (§7.2, a structure element /Lang value that is not a valid BCP 47 tag), pvuaiWidgetMissingPrintField (§7.14 / §7.18.4, a Widget structure element lacking the PrintField attribute owner), pvuaiLinkNotInStructureTree (§7.18.5, a document with /Subtype /Link annotations whose structure tree exposes no Link element), and the Pdfium annotation layer pvuaiPdfiumAnnotationMissingContents / pvuaiPdfiumLinkMissingContents (§7.18.1 / §7.18.5, a precise scan that exempts hidden, off-crop-box, Popup and Widget annotations, eliminating the false positives of the byte-level heuristic). v1.95.0 improves recall and soundness: the §7.21.3.3 pvuaiCMapWModeMismatch and §7.21.7 pvuaiToUnicodeForbiddenValue checks now inflate FlateDecode-compressed CMap / ToUnicode streams before scanning (previously only uncompressed streams were judged), and pvuaiSubsetCharSetEmpty (§7.21.4.2) flags a Type 1 subset FontDescriptor whose /CharSet string value is empty (the missing-key check only caught an absent key). v1.96.0 adds pvuaiAnnotationFlagsViolation (§7.18.1, a non-exempt annotation carrying Hidden / Invisible / NoView) and pvuaiActionTargetMissing (§7.18.5, a GoToR / URI / SubmitForm action missing its target key), both ported from the PDF/A validator, and fixes a cross-page ParentIndex rebasing bug in the structure-tree merge plus a RoleMap parser that prematurely terminated on indirect-reference or array valuesConformance is a real PDF/UA level and Issues is emptyValidatePdfUa is a read-only inspection — it does not modify
the document and is safe to call repeatedly. Use it before handing a
PDF off to an archive system or accessibility checker to surface
non-compliance to the operator. For the common "is this PDF/UA-1?"
check that doesn't need the issue list, prefer the lighter-weight
PdfUaConformance property.
Conformance = pucUnknown and an empty Issues set.pvuaiSuspectsTrue when an explicit /Suspects true appears in /MarkInfo. Documents that lack the /Suspects key entirely are considered compliant on that point (the spec only forbids true).N 0 R); existing keys are preserved and the dictionary is normalized to a direct dictionary in the rewritten catalog.
uses System.TypInfo;
var
Outcome: TPdfUaValidationResult;
Issue: TPdfUaValidationIssue;
begin
Pdf1.FileName := 'C:\Incoming\report.pdf';
Pdf1.Active := True;
Outcome := Pdf1.ValidatePdfUa;
Memo1.Lines.Add('Level: ' + GetEnumName(TypeInfo(TPdfUaConformance),
Ord(Outcome.Conformance)));
for Issue in Outcome.Issues do
Memo1.Lines.Add('- ' + GetEnumName(TypeInfo(TPdfUaValidationIssue), Ord(Issue)));
end;