Checks a document against the PDF/A, PDF/UA and PDF/X standards, and converts an editor's document to PDF/A.
{:ok, doc} = PdfElixide.Document.open("report.pdf")
{:ok, report} = PdfElixide.Compliance.validate(doc, :pdf_a_2b)
report.compliant?
#=> false
Enum.map(report.errors, & &1.code)
#=> ["XMP-001", …]validate/2 returns a PdfElixide.Compliance.Report listing the errors and
warnings found. A document that fails a standard is an ordinary report with
compliant?: false, not an error; {:error, %PdfElixide.Error{}} means the
document could not be read far enough to check it.
What a report establishes
Validation runs this library's own checks for the requested standard, which
cover a subset of each standard's requirements. compliant?: true means those
checks found no errors; it is not a conformance certification. Use a dedicated
conformance validator where a claim of compliance has to hold up.
PDF/A and PDF/UA reports list checks that were only partly performed among
their warnings, under code "WARN-007", so even a report with no errors
carries warnings.
A PDF/A report does not compare the level a document declares with the one
requested, so a document declaring PDF/A-2b can be reported compliant with
PDF/A-1b. Compare report.declared with the standard where the declaration
matters. Nor does it check that the document information dictionary agrees
with the XMP metadata, or that the file carries a document identifier
(/ID), both of which PDF/A requires. A PDF/A-1 report does not check that
the XMP metadata is stored uncompressed, which PDF/A-1 also requires.
PDF/A and PDF/X forbid encryption, so an encrypted document always fails them. PDF/UA-2 is not offered: this library performs no check specific to it.
PDF/X checks read each page's boxes from the page's own dictionary. A page
that inherits its /MediaBox from the page tree is reported as missing one
("XBOX-004"), and its boxes are not checked for consistency.
Converting to PDF/A
convert/3 rewrites the document an editor holds and returns a
PdfElixide.Compliance.Conversion describing what changed:
{:ok, editor} = PdfElixide.Editor.open("report.pdf")
{:ok, conversion} = PdfElixide.Compliance.convert(editor, :pdf_a_2b)
if conversion.report.compliant? do
{:ok, pdf} = PdfElixide.Editor.to_binary(editor)
endConversion fixes supported findings and reports anything left unresolved, so
it can finish with compliant?: false. Convert last, then validate the bytes
you write.
See the PDF/A conversion guide for the fixes, editor restrictions, metadata caveats and font-embedding trade-offs.
Memory and locking
Validation reads a private copy of the document, parsed from its bytes, so it holds about two extra copies of the file in memory for its duration. It takes a shared read on the handle, and so runs alongside other reads — with one exception: a document that cannot be opened without a password is validated in place and takes the handle exclusively. See the Concurrency guide.
Conversion takes the editor exclusively. It holds several copies of the document at once: the written bytes it starts from, the converted document and the editor rebuilt from it.
Summary
Types
Options for convert/3 and convert!/3. See convert/3 for defaults.
A PDF/A level: the part of standard/0 that convert/3 accepts.
A standard and level to validate against.
Functions
Converts the editor's document to the PDF/A level standard.
Converts the editor's document to the PDF/A level standard, raising an
error if the conversion cannot be carried out.
Validates the document against standard.
Validates the document against standard, raising an error if it cannot be
checked.
Types
@type convert_opts() :: [ embed_fonts: boolean(), remove_javascript: boolean(), remove_embedded_files: boolean(), icc_profile: binary() | nil ]
Options for convert/3 and convert!/3. See convert/3 for defaults.
@type pdf_a() ::
:pdf_a_1a
| :pdf_a_1b
| :pdf_a_2a
| :pdf_a_2b
| :pdf_a_2u
| :pdf_a_3a
| :pdf_a_3b
| :pdf_a_3u
A PDF/A level: the part of standard/0 that convert/3 accepts.
@type standard() :: pdf_a() | :pdf_ua_1 | :pdf_x_1a_2001 | :pdf_x_1a_2003 | :pdf_x_3_2002 | :pdf_x_3_2003 | :pdf_x_4 | :pdf_x_4p | :pdf_x_5g | :pdf_x_5n | :pdf_x_5pg | :pdf_x_6
A standard and level to validate against.
- PDF/A:
:pdf_a_1a,:pdf_a_1b,:pdf_a_2a,:pdf_a_2b,:pdf_a_2u,:pdf_a_3a,:pdf_a_3b,:pdf_a_3u. - PDF/UA:
:pdf_ua_1. - PDF/X:
:pdf_x_1a_2001,:pdf_x_1a_2003,:pdf_x_3_2002,:pdf_x_3_2003,:pdf_x_4,:pdf_x_4p,:pdf_x_5g,:pdf_x_5n,:pdf_x_5pg,:pdf_x_6.
Functions
@spec convert(PdfElixide.Editor.t(), pdf_a(), convert_opts()) :: {:ok, PdfElixide.Compliance.Conversion.t()} | {:error, PdfElixide.Error.t()}
Converts the editor's document to the PDF/A level standard.
Returns {:ok, conversion} whether or not the result complies; see
PdfElixide.Compliance.Conversion and
the PDF/A conversion guide. The editor is
changed in place, and a full write produces the converted document. A
document declaring a different PDF/A level, or existing XMP metadata for a
PDF/A-1 target, is among the refusals the guide lists; each returns
{:error, %PdfElixide.Error{reason: :unsupported}} and leaves the editor
unchanged. A standard that is not a pdf_a/0 raises ArgumentError.
Options
:embed_fonts— embed fonts the document uses without carrying them, using fonts installed on this machine. Output can vary by machine; see the conversion guide before enabling it. Defaults tofalse.:remove_javascript— remove document JavaScript and JavaScript actions, which PDF/A forbids. Defaults totrue.:remove_embedded_files— remove attached files, which a PDF/A-1 or PDF/A-2 report lists as a violation. They are kept for a PDF/A-3 target whatever this says. Defaults totrue.:icc_profile— the ICC profile to embed in the output intent, as the profile's bytes. Defaults tonil, which embeds an sRGB profile. Supplying one for a document that already has an output intent returns{:error, %PdfElixide.Error{reason: :unsupported}}and converts nothing. Profiles are embedded without validation and identified as sRGB, so supply an RGB profile.
@spec convert!(PdfElixide.Editor.t(), pdf_a(), convert_opts()) :: PdfElixide.Compliance.Conversion.t()
Converts the editor's document to the PDF/A level standard, raising an
error if the conversion cannot be carried out.
A conversion that finishes without achieving compliance is returned like any
other, not raised; check conversion.report.compliant?.
@spec validate(PdfElixide.Document.t(), standard()) :: {:ok, PdfElixide.Compliance.Report.t()} | {:error, PdfElixide.Error.t()}
Validates the document against standard.
Returns {:ok, report} whether or not the document complies; see
PdfElixide.Compliance.Report. An atom that is not a standard/0 raises
ArgumentError. An encrypted document opened without its password returns
{:error, %PdfElixide.Error{reason: :encrypted}} until
PdfElixide.Document.authenticate/2 succeeds.
@spec validate!(PdfElixide.Document.t(), standard()) :: PdfElixide.Compliance.Report.t()
Validates the document against standard, raising an error if it cannot be
checked.