Skip to content

Rust

The page_validation crate validates PDF files against a supported PDF/A or PDF/UA profile. This page contains most common usages, but you can find reference documentation at docs.rs.

Add the dependency

Either run:

cargo add page_validation

Or add the following to your Cargo.toml:

[dependencies]
page_validation = "0.5.0"

Check compliance of a PDF

is_pdf_compliant() is the fastest way to get a simple true/false compliance against a profile. It stops once it finds a failing rule and returns both the selected profile and the boolean result:

use std::path::Path;
use page_validation::{SafetyLimits, is_pdf_compliant};

let result = is_pdf_compliant(
    Path::new("file.pdf"),    // path to a PDF
    None,                     // an optional profile
    &SafetyLimits::default(), // see below
)?;
println!("{}: {}", result.profile, result.is_compliant);

If the profile isn't specified, it reads the PDF/A or PDF/UA profile declared in the document's XMP metadata and returns Result<ComplianceResult, ValidationError>. A missing, malformed, or unsupported profile declaration produces a ValidationError.

Info

If you want to run it on bytes instead of a file, use is_pdf_compliant_bytes(), which provides the same API but expects a &[u8] instead of a &Path.

Validate a PDF with details

If you details about which rule failed, use validate_pdf():

use std::path::Path;
use page_validation::{SafetyLimits, validate_pdf};

let doc = Path::new("document.pdf")
let report = validate_pdf(doc, None, &SafetyLimits::default())?;

if report.is_compliant {
    println!("The document passed all implemented checks.");
} else {
    for failure in &report.failures {
        eprintln!(
            "[{}] {}",
            failure.rule_id,
            failure.message,
        );
    }
}

Info

If you want to run it on bytes instead of a file, use validate_pdf_bytes(), which provides the same API but expects a &[u8] instead of a &Path.

Select a profile explicitly

Pass a profile to validate_pdf() when the caller, rather than the document, selects it:

use std::path::Path;
use page_validation::{SafetyLimits, ValidationProfile, validate_pdf};

let report = validate_pdf(
    Path::new("document.pdf"),
    Some(ValidationProfile::PdfA1b),
    &SafetyLimits::default(),
);

The explicit-profile call returns Result<ValidationReport, ValidationError>. Unlike profile inference, it does not require the document to contain a usable profile declaration. The declaration can still fail the selected profile's metadata rules.

Failures

Each report contains a list of failures:

use std::path::Path;
use page_validation::{SafetyLimits, validate_pdf};

let report = validate_pdf(
    Path::new("file.pdf"),
    None,
    &SafetyLimits::default()
)?;

for failure in &report.failures {
    println!("Rule: {}", failure.rule_id);
    println!("Category: {:?}", failure.category);
    println!("Message: {}", failure.message);
}

Failure categories distinguish conformance problems from parser or operational errors:

use page_validation::FailureCategory;

match failure.category {
    FailureCategory::Metadata | FailureCategory::Conformance => {
        // The PDF was parsed, but failed a validation rule.
    }
    FailureCategory::Parser => {
        // The PDF could not be parsed correctly.
    }
    FailureCategory::Operational => {
        // Validation failed because of I/O or another runtime issue.
    }
}

Safety limits

The goal of the safety limits protect the validator from excessively large or complex inputs. Defaults are the following and should be sufficient for most cases:

use page_validation::SafetyLimits;

let limits = SafetyLimits {
    max_input_size: 100 * 1024 * 1024,                 // 100 MiB
    max_decoded_stream_size: 32 * 1024 * 1024,         // 32 MiB
    max_total_decoded_content_size: 100 * 1024 * 1024, // 100 MiB
    max_object_count: 500_000,                         // 500,000 objects
    max_reference_depth: 256,                          // 256 levels
};

max_decoded_stream_size bounds one decoded stream and max_total_decoded_content_size bounds the total decoded page, Form, appearance, Pattern, and Type3 content inspected for one document.

Use the exit code

For command-line integrations or automated checks, the report can provide an appropriate process exit code:

std::process::exit(report.exit_code());