PDF/A validation
The pikepdf.pdfa module prepares documents for PDF/A-1b, PDF/A-2b and
PDF/A-3b, checks them, and saves them with validation of the written bytes. It
needs the optional extra pikepdf[pdfa]. See PDF/A validation and repair for a discussion of
how to use it and what it does not cover.
Allowlist validator for PDF/A-1b, 2b and 3b files.
The validator approves a file only if every construct it encounters is recognized and known to conform. Anything it does not recognize is reported as a finding, so a denial may be a false alarm but an approval should never be.
This module needs optional dependencies; install them with
pip install 'pikepdf[pdfa]'.
Functions
- pikepdf.pdfa.save(pdf, filename_or_stream, flavour, *, output_intent='sRGB', output_condition_identifier=None, repair=True, **user_save_kwargs)
Prepare a document for PDF/A, write it, and validate what was written.
saveis the only call that promises anything: it reopens the file it wrote and validates it, so a returned report always describes the bytes at the destination. The cost is one extra parse of the output.The steps are:
Resolve the save settings with resolve_save_kwargs. A conflicting setting raises before the document is changed.
If repair is True, run prepare on pdf. This modifies the in-memory document (output intents, metadata, annotations and so on); the changes remain whether or not the save succeeds. If repair is False, nothing is repaired, and the caller is responsible for making the document conform.
Write the document to a temporary file and validate it. Only a
'pass'verdict is accepted;'fail'and'not_checked'both raise PdfaError.Move the verified file into place (for a path), or copy its bytes into the stream, replacing its previous contents.
A failed save leaves an existing destination file untouched, and does not create a new one. A stream destination is written only on success.
- Parameters:
pdf (pikepdf.Pdf) – The document to save.
filename_or_stream (pathlib.Path | str | bytes | os.PathLike | BinaryIO | None) – A path or a seekable, writable binary stream, positioned at its start. If None, the document is saved over the file it was opened from, which requires
pikepdf.open(..., allow_overwriting_input=True), as for pikepdf.Pdf.save.flavour (pikepdf.pdfa._flavour.Flavour | str) –
'1b','2b'or'3b'.output_intent (str | bytes | None) – Passed to prepare.
output_condition_identifier (str | None) – Passed to prepare.
repair (bool) – If True, run prepare before writing.
**user_save_kwargs (Any) – pikepdf.Pdf.save settings, as accepted by resolve_save_kwargs.
- Returns:
The report on the written file; it passed.
report.save_kwargsholds the settings used andreport.preparedthe PrepareResult (None if repair is False).- Raises:
PdfaError – If the written file did not pass;
e.reportexplains why, ande.report.preparedholds the PrepareResult as for a successful save. Nothing was written to the destination.ValueError – If flavour is not a supported flavour, a save setting conflicts with the flavour, the output intent is invalid, or the destination is the input file and overwriting it was not allowed, or a stream destination is not at its start.
PermissionError – If the destination file is not writable.
TypeError – If a keyword is not a supported save setting, or the destination is not a path or binary stream.
- Return type:
pikepdf.pdfa._report.Report
- pikepdf.pdfa.prepare(pdf, flavour, *, output_intent='sRGB', output_condition_identifier=None)
Apply the repairs that make a document acceptable to PDF/A, in memory.
The repairs change structure and metadata, never page content: the output intents are replaced with a single PDF/A output intent, image interpolation is removed, hidden or non-viewable annotations are removed and the Print flag is set on the others, alternate presentations and document-level JavaScript are removed, PDF/A-1 subset CIDFonts are given a /CIDSet, PDF/A-2 and PDF/A-3 non-symbolic TrueType fonts are given the /BaseEncoding their encoding lacks where that changes no glyph drawn, PDF/A-3 embedded files are given the MIME type, /AFRelationship and /AF entry they lack, and the XMP metadata is rewritten with only what the flavour permits and a declaration of PDF/A conformance (level B), with the DocInfo entries set to agree with it.
Calling prepare again on the same document makes no further changes (except the time recorded in xmp:MetadataDate). The document must still be saved with settings suited to the flavour and validated; prepare does not convert colour, embed fonts or fix page content.
- Parameters:
pdf (pikepdf.Pdf) – The document to change.
flavour (pikepdf.pdfa._flavour.Flavour | str) –
'1b','2b'or'3b'.output_intent (bytes | str | None) –
'sRGB'(any case) for the sRGB profile shipped with pikepdf, the bytes of an ICC output profile (RGB, CMYK or gray; version 2 for PDF/A-1, up to version 4 otherwise), or None to keep the document’s output intents as they are.output_condition_identifier (str | None) – The /OutputConditionIdentifier of the output intent; by default
'sRGB'for the sRGB profile, else the profile’s description, or'Custom'.
- Returns:
What was changed.
- Raises:
ValueError – If flavour is not a supported flavour, or output_intent is not usable. The document is not changed.
TypeError – If output_intent is of the wrong type.
- Return type:
- pikepdf.pdfa.check(pdf, flavour, **user_save_kwargs)
Check an open document against a PDF/A flavour as it would be saved.
The check evaluates the file that
pdf.save(path, **report.save_kwargs)would write: the stream filters qpdf rewrites, the trailer, the PDF version and the objects written (orphaned objects are not written, so they are not checked). Nothing is written and the document is not modified.checkis pure and cheap. It describes the document at the moment of the call, so run it after the last metadata edit. It is advisory:saveis the only call that promises anything, because it validates the bytes it writes.Problems with the document never raise: unexpected errors become
unsupportedfindings with rule idpikepdf:internal.- Parameters:
pdf (pikepdf.Pdf) – The document to check.
flavour (pikepdf.pdfa._flavour.Flavour | str) –
'1b','2b'or'3b'.**user_save_kwargs (Any) – pikepdf.Pdf.save settings the caller intends to use, as accepted by resolve_save_kwargs.
- Returns:
The report;
report.save_kwargsholds the complete settings the verdict assumes.- Raises:
ValueError – If flavour is not a supported flavour, or a save setting conflicts with the flavour.
TypeError – If a keyword is not a supported save setting.
- Return type:
pikepdf.pdfa._report.Report
- pikepdf.pdfa.repair_annotation_flags(pdf)
Make the annotation flags of every page acceptable to PDF/A.
PDF/A requires every annotation to set the Print flag and to clear the Hidden, Invisible and NoView flags; PDF/A-2 and PDF/A-3 also require ToggleNoView to be clear. An annotation with any of those four flags set is removed, as Ghostscript does, rather than made visible. This function does not take a PDF/A part, so it removes annotations with ToggleNoView for every part, including PDF/A-1, whose rule predates that flag and does not mention it; the flags that remain are acceptable to every part. Along with a removed annotation goes its
/Popupannotation, and a/Popupentry that refers to a removed annotation is deleted. The remaining annotations are given the Print flag if they lack it, leaving their other flags alone. Only annotations listed in a page’s/Annotsare considered. The repair is idempotent.pikepdf.pdfa.prepare()calls this function, but it may also be used on its own, for example to preserve hyperlinks before handing a file to another PDF/A converter: Ghostscript’s PDF/A mode silently deletes any annotation without the Print flag, which includes a/Linkannotation written with no/Fentry. It needs nothing beyond importingpikepdf.pdfa, and it neither validates the document nor changes anything other than page annotations.Logs up to two debug messages to a
pikepdf.pdfalogger: one if any annotation was removed, and one if any annotation was given the Print flag.- Parameters:
pdf (pikepdf.Pdf) – An open pikepdf.Pdf object, modified in place.
- Returns:
What was changed.
- Return type:
- pikepdf.pdfa.resolve_save_kwargs(flavour, **user)
Return the complete
pikepdf.Pdf.save()keyword arguments for PDF/A.Settings that could make the written file differ from what the validator checked, or that PDF/A forbids, are pinned; passing a pinned key with its pinned value is allowed, any other value raises
ValueError. The remaining settings are the caller’s choice, with defaults. The result is the complete set of keyword arguments forpikepdf.Pdf.save(), includingprogressandlinearize, sopdf.save(path, **resolve_save_kwargs('2b'))works directly. Pass your own choices intoresolve_save_kwargs(for exampleresolve_save_kwargs('2b', linearize=True, progress=callback)) rather than merging them into the result afterwards, so that they are checked against the flavour. The destination is not included.- Raises:
TypeError – A keyword is not an argument of
pikepdf.Pdf.save().ValueError – A pinned setting conflicts, or a version is invalid or too high for the flavour.
- Parameters:
flavour (pikepdf.pdfa._flavour.Flavour | str)
user (Any)
- Return type:
dict[str, Any]
- pikepdf.pdfa.validate_written(input_file, flavour, save_kwargs=None)
Validate a saved PDF/A candidate file.
Problems with the file never raise: unexpected errors become
unsupportedfindings with rule idpikepdf:internal.- Parameters:
input_file (pathlib.Path | str | BinaryIO) – The saved candidate file, or a seekable binary stream holding it.
flavour (pikepdf.pdfa._flavour.Flavour | str) –
'1b','2b'or'3b'.save_kwargs (collections.abc.Mapping[str, Any] | None) – The pikepdf.Pdf.save settings the file was written with, recorded in
report.save_kwargs.
- Returns:
The report;
report.passedis True if the file is approved.- Raises:
ValueError – If flavour is not a supported flavour.
- Return type:
pikepdf.pdfa._report.Report
Results
- class pikepdf.pdfa.Report
Result of checking one document against one PDF/A flavour.
- flavour
The PDF/A flavour checked.
- findings
Every reason the document was not approved.
- save_kwargs
The pikepdf.Pdf.save keyword arguments the check assumed, or that were used to write the file.
- output_intent
ICC colour space signature of the PDF/A OutputIntent profile (
'RGB','CMYK'or'GRAY'), or None if unknown.
- prepared
What prepare changed before save wrote the file, or None if the report did not come from save or nothing was prepared.
- summary(limit=20)
Return a multi-line description of the result.
- Parameters:
limit (int) – Maximum number of findings to list.
- Return type:
str
- property unsupported: tuple[Finding, ...]
Findings of constructs the validator does not check.
- Return type:
tuple[Finding, …]
- property verdict: Verdict
'pass','fail'or'not_checked'.'fail'if any finding is a violation;'not_checked'if there are findings but all of them are constructs the validator does not check;'pass'if there are no findings.- Return type:
Verdict
- class pikepdf.pdfa.Finding
One reason a file was not approved.
- rule
veraPDF rule id (
ISO_19005_2:6.2.8-3),pikepdf:schema-<role>for an unrecognized construct in a schema role,pikepdf:<name>for a local policy, orpikepdf:internalfor an unexpected error.
- where
Human-readable location, e.g.
obj 12 0 (Page) /AA.
- message
What was wrong.
- kind
violationif the construct breaks PDF/A;unsupportedif it may be valid PDF/A but the validator does not check it.
- class pikepdf.pdfa.PrepareResult
What prepare changed.
- output_intent_replaced
True if the output intents were replaced. False if the requested intent was already the document’s only output intent, or if the output intents were kept.
- output_intent
The colour space (
'RGB','CMYK'or'GRAY') of the requested output intent, whether it was installed now or already present; None if the document’s output intents were kept.
- interpolation_removed
The number of images whose /Interpolate entry was removed.
- annotations_removed
The number of hidden or non-viewable annotations removed, by subtype (without the leading slash).
- annotations_removed_pages
The pages (1-based) annotations were removed from.
- print_flags_set
The number of annotations given the Print flag.
- cidsets_added
The number of /CIDSet streams added to subset CIDFonts (PDF/A-1 only).
- base_encodings_added
The number of non-symbolic TrueType fonts whose encoding, with /Differences but no /BaseEncoding, was given
/BaseEncoding /WinAnsiEncoding(PDF/A-2 and PDF/A-3 only).
- alternate_presentations_removed
True if the name dictionary’s /AlternatePresentations (slideshows) was removed.
- document_javascript_removed
True if the name dictionary’s /JavaScript (document-level JavaScript) was removed.
- embedded_file_subtypes_set
The number of embedded file streams without a MIME type given
/application/octet-stream(PDF/A-3 only).
- af_relationships_set
The number of file specifications of embedded files given
/AFRelationship /Unspecified(PDF/A-3 only).
- associated_files_added
The number of embedded files added to the catalog’s /AF array, making them associated files (PDF/A-3 only).
- xmp_dropped
Labels (
prefix:name) of the XMP properties removed because PDF/A does not permit them.
- xmp_unreadable
True if an XMP packet that could not be read was replaced.
- xmp_problem
Why the XMP packet could not be read, if xmp_unreadable; otherwise None.
- dates_zoned
The number of document dates given the local time zone.
- pdfa_declared
True if the metadata did not already declare the flavour’s PDF/A conformance.
- property changed: bool
True if prepare changed anything but the metadata timestamps.
prepare always records pikepdf as the producer and the time in xmp:MetadataDate; that alone does not count as a change.
- Return type:
- describe()
Return one sentence for each kind of change made.
The sentences of messages, without their levels.
- Return type:
list[str]
- messages()
Return a
(level, sentence)pair for each kind of change made.The level suggests how prominently to report the change:
'warning'for annotations removed, which discards content;'info'for Print flags set, alternate presentations and document-level JavaScript removed, XMP properties removed and an unreadable XMP packet replaced;'debug'for the rest. It is one of the lowercase names of the logging levels, sologging.getLevelName(level.upper())gives the level number.- Return type:
list[tuple[str, str]]
- class pikepdf.pdfa.AnnotationRepairResult
What
pikepdf.pdfa.repair_annotation_flags()changed.An empty result (no removals and no Print flags set) means the annotation flags of every page were already acceptable to PDF/A.
- print_flags_set: int = 0
The number of annotations that were kept and given the Print flag, because they had no
/Fentry or had one without the Print bit.
- removed: collections.Counter[str]
The number of annotations removed, keyed by subtype without the leading slash (for example
'Link'or'Popup');'unknown'counts annotations that have no valid/Subtype.
- removed_pages: set[int]
The page numbers (1-based) from which annotations were removed.