PDF Standards & Conformance
|
This content was generated with the assistance of AI and should be verified against the official documentation before being relied on in production. The reference material consulted while preparing these pages is listed in the section bibliography. |
A PDF file that opens in a viewer is not necessarily a file that an archive, a regulator or a screen reader accepts. Those audiences rely on profiles of the format, defined in separate ISO standards, that restrict what a file may contain and require what it must contain. This page describes the base format and the profiles, shows a Factur-X-style invoice (PDF/A-3 with an embedded XML invoice) created with iText for the Bookshelf Billing scenario, and shows how to validate generated files with veraPDF so that a regression fails the build. Versions are stated as of 2026-10-09.
PDF 1.7 and PDF 2.0
The core format is defined by ISO 32000. PDF 1.7 is ISO 32000-1:2008, the version that most files in circulation use and that PDF/A-2, PDF/A-3 and PDF/UA-1 are built on. PDF 2.0 is ISO 32000-2:2020, and PDF/A-4 and PDF/UA-2 are built on it. Adobe published the PDF 1.7 specification, and it was later submitted to ISO. ISO 32000-2 is available at no cost through the PDF Association’s sponsored standards page (see the References).
PDF 2.0 is mostly a clarification and a clean-up of 1.7, but it deprecates features that older code and older books still rely on. Three of them matter for a backend that generates files:
-
XFA forms (XML Forms Architecture) are deprecated. An XFA form is a document that the viewer renders from XML rather than from page content, which is why it cannot be archived, made accessible or processed by most libraries. Use AcroForm fields instead. iText Core can read and fill the XFA data of an existing form (
XfaForm) and remove the XFA part so that only the AcroForm fields remain (PdfAcroForm.removeXfaForm()); flattening an XFA form into static page content needs the commercial pdfXFA add-on. -
The
NeedAppearancesflag of the AcroForm dictionary is deprecated. The flag asks the viewer to rebuild every field appearance on open, so the file looks different in every viewer and cannot be validated. A producer should write an appearance stream (/AP) for each field and leave the flag unset. iText exposes the flag asPdfAcroForm.setNeedAppearances(boolean), and the field builders generate appearances for you. -
The signature subfilter
adbe.pkcs7.sha1is deprecated, together withadbe.x509.rsa_sha1. It signs a SHA-1 digest, and the standard that governs signature algorithms, ETSI TS 119 312, forbids SHA-1 for new signatures. Useadbe.pkcs7.detachedor, for PAdES,ETSI.CAdES.detachedwith SHA-256 or stronger.
Other 2.0 changes in the same direction are that only AES-256 encryption stays current and that the Info dictionary is deprecated apart from the creation and modification dates, in favour of XMP metadata. Both are consistent with what the conformance profiles below demand anyway. The outdated points of the older books on the subject are listed in the last section of this page.
The conformance profiles at a glance
A profile is a subset of a PDF version plus extra requirements. A file declares which profile it claims in its XMP
metadata (for PDF/A, the pdfaid:part and pdfaid:conformance properties), and a validator checks the claim.
| Standard | Based on | Levels | What it adds or forbids |
|---|---|---|---|
PDF/A-1, ISO 19005-1 (2005) |
PDF 1.4 |
1a, 1b |
All fonts embedded, XMP metadata required, device-independent colour (an output intent or ICC-based colour), no encryption, no JavaScript, no external content, no transparency, no LZW compression, no embedded files. Level a also requires a tagged structure. |
PDF/A-2, ISO 19005-2 (2011) |
PDF 1.7 |
2a, 2b, 2u |
Adds transparency, JPEG 2000, layers (optional content) and embedding of other PDF/A files. Level u requires a Unicode mapping for all text without requiring the tagged structure of level a. |
PDF/A-3, ISO 19005-3 (2012) |
PDF 1.7 |
3a, 3b, 3u |
As PDF/A-2, and the embedded files may be of any type (XML, CSV, spreadsheets, source documents). Every embedded file
carries an association ( |
PDF/A-4, ISO 19005-4 (2020) |
PDF 2.0 |
4, 4e, 4f |
The a, b and u levels are gone. Variant 4f permits embedded files of any type, 4e permits engineering content such as 3D, and plain 4 permits neither. Accessibility is expressed by also claiming PDF/UA. |
PDF/UA-1, ISO 14289-1 (2014) |
PDF 1.7 |
one level |
Universal accessibility: a complete logical structure (tagged PDF), reading order, alternate text for figures, a document title and language, no reliance on colour alone, form fields with labels. |
PDF/UA-2, ISO 14289-2 (2024) |
PDF 2.0 |
one level |
The same goals on PDF 2.0, using the structure vocabulary of ISO 32000-2. |
PDF/X, ISO 15930 |
varies (X-4 on PDF 1.6, X-6 on PDF 2.0) |
X-1a, X-3, X-4, X-5, X-6 |
Print exchange: colour management, output intent, fonts embedded, trim and bleed boxes. Aimed at print shops, not at business documents. |
Reading the table by use case, the Bookshelf Billing scenario ends up with these choices. Customer invoices that must be kept for years and that carry a machine-readable invoice are PDF/A-3b, or 3u when text extraction matters. The monthly sales report that is archived but has no attachment is PDF/A-2u. Both are also PDF/UA-1 when the audience requires accessible documents. A new project with no legacy viewer constraints can choose PDF/A-4 (4f when it embeds data) and PDF/UA-2. PDF/X is not relevant for a billing back office.
The letters a, b and u are only meaningful for parts 1 to 3. Level b ("basic") guarantees that the visual appearance can be reproduced. Level u adds a Unicode mapping, so that text can be searched and copied. Level a adds the tagged logical structure, so that the document is accessible, and it implies u.
Conformance classes in iText 9
The older iText books describe conformance as settings on the writer, such as setPDFXConformance and setTagged, and
they treat PDF/A-1 as the only archival profile and XFA as a current forms technology. None of that holds in iText 9.
A profile is chosen by the type of document you create. PdfADocument takes a PdfAConformance constant
(PDF_A_1A through PDF_A_4F), PdfUADocument takes a PdfUAConfig naming PDF/UA-1 or PDF/UA-2, and the
PdfConformance class (introduced in iText 9.0) describes the profile of a document that is read back. The document
then validates what you write as you write it, and fails with a conformance exception at the first violation, such as
a non-embedded font.
This early failure is useful, and it is not a substitute for an independent validator. iText checks the rules that it implements while writing, and an independent tool also checks the final bytes, which is what an auditor receives. The next sections create a PDF/A-3 file with iText and validate it with veraPDF. The deeper iText material, including PDF/UA with iText, is covered by later pages of this section.
A Factur-X-style invoice: PDF/A-3 with embedded XML
Factur-X (the French name) and ZUGFeRD (the German name, which since version 2.1 is technically the same format) are
hybrid e-invoice formats. The invoice is a PDF/A-3 file that people read, and the same invoice is attached to it as an
XML file in the UN/CEFACT Cross Industry Invoice syntax, which accounting software reads. The attachment is named
factur-x.xml, its association is Data, Source or Alternative, and it is available in profiles of increasing
detail, from MINIMUM and BASIC WL to BASIC, EN 16931 and EXTENDED. The profile that matches the European standard
EN 16931 is the usual choice for business-to-business invoices. The Factur-X and ZUGFeRD specifications define
the exact XML for each profile, and they are maintained by the FNFE-MPE in France and FeRD in Germany.
The example creates a tagged PDF/A-3b file with an embedded font and an embedded XML attachment. The XML is a
placeholder, because the full invoice XML is domain content that belongs to the invoicing library you pick, such as
the Mustang project. A production Factur-X file also needs an XMP extension schema that declares the
fx:DocumentType, fx:DocumentFileName, fx:Version and fx:ConformanceLevel properties, which the specification
describes in detail, and it uses real invoice data. The embedded font comes from a TrueType file that you supply as the
first argument, because PDF/A forbids the 14 standard fonts that are not embedded.
Maven coordinates (iText 9.8.0 is AGPL-3.0 or commercial; veraPDF is used further down):
<dependency>
<groupId>com.itextpdf</groupId>
<artifactId>itext-core</artifactId>
<version>9.8.0</version>
<type>pom</type>
</dependency>
Official documentation: itext-core on Maven Central.
package com.example.billing;
import com.itextpdf.io.font.PdfEncodings;
import com.itextpdf.kernel.font.PdfFont;
import com.itextpdf.kernel.font.PdfFontFactory;
import com.itextpdf.kernel.pdf.PdfAConformance;
import com.itextpdf.kernel.pdf.PdfName;
import com.itextpdf.kernel.pdf.PdfOutputIntent;
import com.itextpdf.kernel.pdf.PdfString;
import com.itextpdf.kernel.pdf.PdfWriter;
import com.itextpdf.kernel.pdf.filespec.PdfFileSpec;
import com.itextpdf.layout.Document;
import com.itextpdf.layout.element.Paragraph;
import com.itextpdf.pdfa.PdfADocument;
import java.awt.color.ColorSpace;
import java.awt.color.ICC_Profile;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.nio.file.Path;
public final class FacturXStyleInvoice {
private static final String XML = """
<?xml version="1.0" encoding="UTF-8"?>
<rsm:CrossIndustryInvoice xmlns:rsm="urn:un:unece:uncefact:data:standard:CrossIndustryInvoice:100">
<!-- Placeholder: a real Factur-X document carries a full CII profile here. -->
</rsm:CrossIndustryInvoice>
""";
private FacturXStyleInvoice() {
}
public static void main(String[] args) throws IOException {
Path fontFile = Path.of(args[0]);
Path output = Path.of(args[1]);
byte[] icc = ICC_Profile.getInstance(ColorSpace.CS_sRGB).getData();
try (InputStream iccStream = new ByteArrayInputStream(icc);
PdfADocument pdf = new PdfADocument(new PdfWriter(output.toString()),
PdfAConformance.PDF_A_3B,
new PdfOutputIntent("Custom", "", "http://www.color.org",
"sRGB IEC61966-2.1", iccStream));
Document document = new Document(pdf)) {
pdf.getDocumentInfo().setTitle("Invoice BB-2026-0001");
pdf.setTagged();
pdf.getCatalog().setLang(new PdfString("en-US"));
PdfFont font = PdfFontFactory.createFont(
Files.readAllBytes(fontFile), PdfEncodings.IDENTITY_H,
PdfFontFactory.EmbeddingStrategy.FORCE_EMBEDDED);
document.setFont(font);
document.add(new Paragraph("Bookshelf Billing - invoice BB-2026-0001"));
PdfFileSpec spec = PdfFileSpec.createEmbeddedFileSpec(pdf,
XML.getBytes(StandardCharsets.UTF_8), "Factur-X invoice data",
"factur-x.xml", new PdfName("text/xml"), null, new PdfName("Data"));
pdf.addAssociatedFile("factur-x.xml", spec);
}
}
}
The example was compiled with Java 21 and run against iText 9.8.0, and the resulting file passes the veraPDF PDF/A-3b
profile in the next section. It uses the Java 21 idioms that replace the Java 1.x style of the older books: a text
block for the XML, try-with-resources so that the document is closed (and the file completed) even on failure, and
Path and Files instead of File streams. pdf.addAssociatedFile writes the /AF entry of the catalog, and the
last argument of createEmbeddedFileSpec is the /AFRelationship value that PDF/A-3 requires.
The embedded file is the point where the versions diverge. The same file validated against PDF/A-2b is rejected, with the rule that a file specification may contain an embedded file only if that file is itself PDF/A, which is the difference between parts 2 and 3. This is a quick way to see that the profile you claim and the content you write must agree.
Official documentation: the iText knowledge base article
attaching files to a PDF, the iText API reference of
PdfADocument and
PdfAConformance, and
ISO 19005-3 for associated files.
Validating with veraPDF
veraPDF is the open-source PDF/A and PDF/UA validator maintained by the veraPDF Consortium. It is the validator that most archives and e-invoicing projects use as a reference, it is free software under the GPLv3+ or MPLv2+ licences, and it ships a command line, a GUI and a Java library. As of 2026-10-09 the current release on Maven Central is 1.30.3 (published 2026-10-08). It contains built-in profiles for PDF/A-1a to PDF/A-4f, PDF/UA-1, PDF/UA-2 and the WTPDF profiles.
Command line
The command line is the way to validate a batch of files. Download the installer zip from the veraPDF website, verify
its GPG signature, and run the installer, which unpacks a verapdf script. The commands below list the built-in
profiles, validate one file against a named profile, and let veraPDF pick the profile from the file’s XMP metadata.
verapdf --list
verapdf --flavour 3b --format text invoice.pdf
verapdf --format xml invoices/*.pdf > report.xml
verapdf --flavour 0 --defaultflavour 2b --format json report/*.pdf
Official documentation: veraPDF CLI validation and CLI options.
The --flavour option (short form -f) takes the profile code (1a to 4f, ua1, ua2), and 0, the default,
detects the profile from the metadata. When detection fails the profile in --defaultflavour is used, which is 1b
unless you change it. The default report is XML, and --format accepts text, raw, html, json and xml. A
file’s report carries the attribute isCompliant, but a file that veraPDF cannot parse (a truncated or encrypted one,
for example) gets a taskException element instead and no isCompliant at all. A CI step should therefore fail the
build on any taskException and whenever fewer files report isCompliant="true" than were validated:
verapdf --flavour 3b --format xml build/pdf/*.pdf > verapdf-report.xml
files=$(ls build/pdf/*.pdf | wc -l)
passed=$(grep -o 'isCompliant="true"' verapdf-report.xml | wc -l)
if grep -q '<taskException' verapdf-report.xml || [ "$passed" -ne "$files" ]; then
echo "PDF/A validation failed ($passed of $files compliant), see verapdf-report.xml" >&2
exit 1
fi
Official documentation: veraPDF CLI validation, which describes the XML report.
For large batches --processes runs several validation processes in parallel and --maxfailuresdisplayed limits how
many failures are reported per rule, which keeps the report readable when a font rule fails on every page. The CLI
snippets above follow the veraPDF documentation and were not run in this page’s verification, because the verified
path used the Java library below.
Java library in CI
The library is useful when the files are produced by your own tests, because the validation can live in the same
Maven build and no tool needs to be installed on the build agent. Add the Greenfield validation model, which is
veraPDF’s own parser and the one its documentation recommends (the Jakarta variants are
validation-model-jakarta and the others with a -jakarta suffix):
<dependency>
<groupId>org.verapdf</groupId>
<artifactId>validation-model</artifactId>
<version>1.30.3</version>
</dependency>
Official documentation: org.verapdf:validation-model on Maven Central.
The following program validates every file given as an argument against the profile given as the first argument and exits with status 1 when any file fails, which fails a CI step. Foundry initialisation is required once before the first use.
package com.example.billing;
import org.verapdf.core.EncryptedPdfException;
import org.verapdf.core.ModelParsingException;
import org.verapdf.core.ValidationException;
import org.verapdf.gf.foundry.VeraGreenfieldFoundryProvider;
import org.verapdf.pdfa.Foundries;
import org.verapdf.pdfa.PDFAParser;
import org.verapdf.pdfa.PDFAValidator;
import org.verapdf.pdfa.flavours.PDFAFlavour;
import org.verapdf.pdfa.results.TestAssertion;
import org.verapdf.pdfa.results.ValidationResult;
import java.io.IOException;
import java.io.InputStream;
import java.nio.file.Files;
import java.nio.file.Path;
public final class ConformanceCheck {
private ConformanceCheck() {
}
public static void main(String[] args) throws IOException {
VeraGreenfieldFoundryProvider.initialise();
PDFAFlavour flavour = PDFAFlavour.fromString(args[0]);
int failures = 0;
for (int i = 1; i < args.length; i++) {
Path file = Path.of(args[i]);
try (InputStream in = Files.newInputStream(file);
PDFAParser parser = Foundries.defaultInstance().createParser(in, flavour)) {
PDFAValidator validator = Foundries.defaultInstance().createValidator(flavour, false);
ValidationResult result = validator.validate(parser);
if (result.isCompliant()) {
System.out.println("PASS " + file);
} else {
failures++;
System.out.println("FAIL " + file);
for (TestAssertion a : result.getTestAssertions()) {
System.out.println(" " + a.getRuleId().getSpecification() + " "
+ a.getRuleId().getClause() + ": " + a.getMessage());
}
}
} catch (ValidationException | ModelParsingException | EncryptedPdfException e) {
failures++;
System.out.println("ERROR " + file + ": " + e.getMessage());
}
}
if (failures > 0) {
System.exit(1);
}
}
}
Running it on the invoice created above prints PASS invoice.pdf for profile 3b, and for profile 2b it prints
FAIL invoice.pdf followed by the broken rules and exits with status 1. A Maven build can run it as an
exec-maven-plugin execution in the verify phase, after the tests that generate PDFs into target/pdf, or the same
calls can be placed in a JUnit test that asserts result.isCompliant() for each generated sample. The latter keeps the
failure message next to the test that produced the file.
Official documentation: developing with veraPDF and the veraPDF library on GitHub.
Outdated points and pitfalls
Older books and tutorials describe iText conformance as the writer methods setPDFXConformance and setTagged, PDF/A-1
as the only archival profile, and XFA as a current way to build forms. The current approach is a PdfADocument or
PdfUADocument per profile, parts 1 to 4 of PDF/A, and AcroForm fields, because XFA is deprecated in PDF 2.0 and
is also forbidden by every PDF/A part. The same books use adbe.pkcs7.sha1 for signatures. That subfilter is
deprecated in PDF 2.0, and new signatures use ETSI.CAdES.detached for PAdES, which later pages of this section
cover.
Two practical pitfalls are worth remembering. A file that claims a profile in XMP but was never validated is worse than a file that makes no claim, because the claim is what auditors check. And encryption, which is forbidden by every PDF/A part, means that an encrypted and archived file has to be decrypted first and protected by the storage layer instead.
References
-
PDF Association: ISO 32000-2 (PDF 2.0) and other sponsored standards (this site returns 403 to
curl; check it in a browser) -
veraPDF and its documentation, including CLI validation, CLI options and developing with veraPDF
-
veraPDF-library on GitHub and
org.verapdf:validation-modelon Maven Central -
Mustang project, open-source library for ZUGFeRD and Factur-X XML
-
iText knowledge base: attaching files to a PDF and What does PDF 2.0 stand for?