Create, convert, and validate archival PDFs

A guide for developers using PDFluent to build accessible, standards-compliant PDFs that remain valid for decades.

Documents that stay valid for 50 years.

Long-term PDF archiving with PDFluent. Convert documents to PDF/A-1b, PDF/A-2b, and PDF/A-3b for compliance with ISO 19005 and EU archiving mandates.
rust
use pdfluent::{PdfDocument, PdfAProfile, CompressOptions};

fn main() -> pdfluent::Result<()> {
    let mut doc = PdfDocument::open("report.pdf")?;

    // Check PDF/A conformance for long-term archiving
    let report = doc.validate_pdfa(PdfAProfile::A2b)?;
    println!("PDF/A-2b compliant: {}", report.is_compliant());
    for v in &report.violations {
        println!("[{}] {}", v.rule, v.message);
    }

    // Subset fonts and compress for a smaller archival file
    doc.subset_fonts()?;
    doc.compress(CompressOptions::archival())?;
    doc.save("report-archival.pdf")?;
    Ok(())
}
What PDF/A versions does PDFluent support?
PDFluent supports PDF/A-1a, PDF/A-1b, PDF/A-2a, PDF/A-2b, PDF/A-2u, PDF/A-3a, PDF/A-3b, and PDF/A-3u. All ISO 19005 conformance levels are supported for validation and conversion.
Can LTV signatures be verified offline?
Yes. A PAdES B-LT signature carries the certificate chain, the revocation evidence and the RFC 3161 timestamp token inside the PDF, so a verifier needs no CRL or OCSP server. You pass the timestamp token in when you sign: the signing path has no HTTP client and never fetches one for you.
Is the validation engine compatible with veraPDF?
Yes. PDFluent's validation engine passes the veraPDF test suite. You can integrate it as an interchangeable substitute for veraPDF in automated compliance pipelines.
Can I batch-convert thousands of documents?
Yes. PDFluent supports parallel batch processing with configurable worker threads. Each document gets a detailed validation report. Failed conversions report which rules were violated and what auto-fix was applied.

Convert PDFs to archival PDF/A format.

Turn ordinary PDFs into ISO-compliant PDF/A-1b, PDF/A-2b, or PDF/A-3b files for long-term archiving. Embed fonts, fix color profiles, and attach files automatically.
rust
use pdfluent::{PdfDocument, compliance::PdfALevel};

fn convert_to_archival(input: &str, output: &str) -> anyhow::Result<()> {
    let mut doc = PdfDocument::open(input)?;

    // Convert to PDF/A-2b — auto-embeds fonts and attaches sRGB color profile
    let report = doc.convert_to_pdf_a(PdfALevel::A2b)?;

    if !report.warnings().is_empty() {
        for w in report.warnings() {
            eprintln!("Warning: {}", w);
        }
    }

    doc.save(output)?;
    println!("Saved PDF/A-2b to {output}");
    Ok(())
}
Can PDFluent always produce a fully compliant PDF/A from any input?
No. Some PDFs contain content that cannot be made archival-compliant without human review — for example, JavaScript actions that must be removed, transparency layers that must be flattened, or fonts that are neither embedded nor available on the conversion system. PDFluent reports these as errors. For most office and print PDFs the conversion succeeds automatically.
Does conversion change the visual appearance of the document?
For the vast majority of documents, no. Font embedding and color profiling are transparent operations. The only visual change that may occur is transparency flattening in PDF/A-1b, which renders transparent objects to opaque equivalents. PDF/A-2b and PDF/A-3b support transparency natively, so flattening is not required.
How do I attach a ZUGFeRD XML invoice to a PDF/A-3b output?
Use doc.attach_file() with the RelationshipType::Alternative relationship type and the factur-x.xml filename required by the standard. PDFluent sets the correct AFRelationship entry and MIME type automatically when you use the attach_zugferd() convenience method.
What happens to PDF forms and interactive features?
PDF/A-1b prohibits JavaScript and interactive features that reference external resources. PDFluent removes these automatically and logs a warning. AcroForm fields (text boxes, checkboxes) are preserved because they are permitted by the standard.
How does performance compare to Ghostscript for batch conversion?
For font embedding and metadata conversion, PDFluent operates directly on the PDF object graph without re-rendering. For transparency flattening (PDF/A-1b), rasterization is required.

PDF/A validation.

Validate PDF/A conformance against ISO 19005 for PDF/A-1b, 2b, and 3b, with detailed preflight reports.
rust
use pdfluent::{PdfDocument, PdfAProfile};

fn main() -> pdfluent::Result<()> {
    let doc = PdfDocument::open("archive.pdf")?;

    let report = doc.validate_pdfa(PdfAProfile::A2b)?;
    println!("Compliant: {}", report.is_compliant());
    println!("Violations: {}", report.violations.len());

    for v in &report.violations {
        println!("[{}] {} (page {:?})", v.rule, v.message, v.page);
    }
    Ok(())
}
Is the validation compatible with veraPDF?
Yes. PDFluent's validation engine passes the full veraPDF test suite. You can use it as a verified replacement for veraPDF in compliance pipelines and automated testing.
Can PDFluent auto-fix non-conformant PDFs?
Yes. The fix_pdfa() method automatically corrects common issues: embeds missing fonts, repairs XMP metadata, flattens disallowed transparency, and adds the PDF/A identifier. The result is a conformant PDF/A document.
Which PDF/A versions are supported?
All ISO 19005 versions: PDF/A-1a, PDF/A-1b, PDF/A-2a, PDF/A-2b, PDF/A-2u, PDF/A-3a, PDF/A-3b, and PDF/A-3u. Both validation and conversion are supported for all levels.

Validate and enforce PDF compliance standards.

Check PDFs against PDF/A, PDF/UA, and PDF/X standards. Get machine-readable violation reports. Enforce standards in CI/CD pipelines and batch workflows.
rust
use pdfluent::{PdfDocument, PdfAProfile};

fn main() -> pdfluent::Result<()> {
    let doc = PdfDocument::open("document.pdf")?;

    let report = doc.validate_pdfa(PdfAProfile::A2b)?;
    if report.is_compliant() {
        println!("PDF/A-2b compliant");
    } else {
        for v in &report.violations {
            println!("[{}] {}", v.rule, v.message);
        }
    }
    Ok(())
}
What is the difference between PDF/A conformance levels?
PDF/A-1b requires baseline font embedding and metadata. PDF/A-2b adds JPEG 2000 compression and optional content support. PDF/A-3b allows arbitrary file attachments (used by ZUGFeRD invoices). The "b" suffix means basic conformance; "a" (accessible) also requires tagged structure. PDFluent validates all three levels.
Can PDFluent validate and fix violations in the same pass?
Validation and remediation are separate operations by design. Use Document::validate() to get a report, then use Document::convert_to_pdfa() to attempt automatic remediation. Not all violations can be fixed automatically — for example, missing semantic tagging requires manual authoring intent.
Does PDFluent validation match the output of veraPDF?
PDFluent targets the same ISO clause set as veraPDF for PDF/A. Results are highly consistent for the most common violation categories (font embedding, color space, XMP metadata). Edge cases involving very old or malformed PDFs may differ. If veraPDF parity is critical to your workflow, run both tools and compare reports.
How fast is batch validation?
Documents can be validated in parallel with Rayon. Validation is I/O-bound for large documents; use SSDs or memory-mapped files for best throughput.
Is ISO 32000-2 (PDF 2.0) supported?
PDFluent can open and validate PDF 2.0 documents. Full clause-level validation for the PDF 2.0 standard (ISO 32000-2) is on the roadmap. Current compliance validators target PDF/A, PDF/UA-1, and PDF/X which remain the dominant standards in regulated industries.

Build accessible PDFs that meet PDF/UA-1.

Validate tag structure, alt text, reading order, and language declarations. Produce PDFs that pass screen readers and meet government accessibility mandates.
rust
use pdfluent::{PdfDocument, accessibility::{AltText, Standard, Validator}};

fn check_and_fix_accessibility(path: &str) -> anyhow::Result<()> {
    let mut doc = PdfDocument::open(path)?;

    // Validate against PDF/UA-1 (ISO 14289-1)
    let validator = Validator::new(Standard::PdfUa1);
    let report = validator.validate(&doc)?;

    println!("{} violation(s):", report.violations().len());
    for v in report.violations() {
        println!("  [{}] {}", v.clause(), v.message());
    }

    // Add alt text to untagged images
    for mut img in doc.images_without_alt_text() {
        img.set_alt_text(AltText::new("Decorative figure"))?;
    }

    doc.save(path)?;
    Ok(())
}
What is PDF/UA and who requires it?
PDF/UA (ISO 14289-1) is the international standard for universally accessible PDF. It is required by many government accessibility mandates, including the EU Web Accessibility Directive, Section 508 in the United States, and EN 301 549 in Europe. Organizations publishing public-sector documents — forms, reports, legislation — typically must meet PDF/UA-1.
What is the difference between PDF/UA and WCAG 2.1?
WCAG 2.1 applies to web content. PDF/UA is the equivalent standard for PDF documents. The two are complementary: a PDF that is PDF/UA-1 conformant satisfies the PDF-related success criteria in WCAG 2.1. PDFluent validates PDF/UA-1 directly; meeting PDF/UA is the accepted path to WCAG 2.1 compliance for PDF content.
Can PDFluent automatically make a scanned PDF accessible?
No. A scanned PDF is image-only and has no text layer. Accessibility tagging needs a text layer, and that text layer comes from OCR. In pdfluent 1.1.0, OCR is opt-in from Python, through a system tesseract binary or your own ocr_function; the Rust facade has no OCR in 1.1.0. After OCR, you can apply accessibility tagging. Full semantic tagging of a scanned document still needs human review of the generated structure.
Does PDF/UA require every image to have alt text?
Yes, unless the image is decorative. Decorative images must be marked as Artifact elements, which removes them from the accessibility tree. Any Figure element that carries information meaningful to the reader must have an /Alt text entry.
How do I integrate PDF/UA validation into a document generation pipeline?
Generate the PDF with PDFluent, then immediately validate it with Validator::new(Standard::PdfUa1).validate(&doc). Treat violations as a pipeline error and fail the job. This catches accessibility regressions before documents reach end users.