PDF/A Validation
PDF/A (ISO 19005) is the international standard for long-term archival of electronic documents. PDF Oxide validates all major PDF/A levels and can convert non-compliant documents toward compliance.
Binding coverage. PDF/A validation is available in Python (
doc.validate_pdf_a(level)), Rust (validate_pdf_a+PdfAValidatorbuilder), WASM (doc.validatePdfA(level)), and Go (doc.ValidatePdfA(level)). A public C# wrapper is not yet exposed — use the Rust CLI (pdf-oxide validate --pdfa 2b doc.pdf) or invoke through one of the supported bindings. PDF/A conversion (PdfAConverter) is currently exposed in Rust and Python only.
Supported Levels
| Level | Standard | Structure | Unicode | Transparency | Embedded Files |
|---|---|---|---|---|---|
| 1a | ISO 19005-1 | Required | Required | No | No |
| 1b | ISO 19005-1 | No | No | No | No |
| 2a | ISO 19005-2 | Required | Required | Yes | No |
| 2b | ISO 19005-2 | No | No | Yes | No |
| 2u | ISO 19005-2 | No | Required | Yes | No |
| 3a | ISO 19005-3 | Required | Required | Yes | Yes |
| 3b | ISO 19005-3 | No | No | Yes | Yes |
| 3u | ISO 19005-3 | No | Required | Yes | Yes |
Level “a” (accessible) requires a tagged structure tree and Unicode character mapping. Level “b” (basic) requires only visual reproducibility. Level “u” (Unicode) requires Unicode text mapping without the full structure tree.
Quick Validation
Use the convenience function for a one-call check:
from pdf_oxide import PdfDocument
doc = PdfDocument("document.pdf")
result = doc.validate_pdf_a("2b")
print(f"Valid: {result.valid}")
print(f"Level: {result.level}")
for error in result.errors:
print(f" Error: {error}")
const doc = new WasmPdfDocument(bytes);
const result = doc.validatePdfA("2b");
console.log(`Valid: ${result.valid}`);
console.log(`Errors: ${result.errors.length}`);
doc.free();
use pdf_oxide::PdfDocument;
use pdf_oxide::compliance::{validate_pdf_a, PdfALevel};
let mut doc = PdfDocument::open("archive.pdf")?;
let result = validate_pdf_a(&mut doc, PdfALevel::A1b)?;
if result.has_errors() {
println!("Not PDF/A-1b compliant:");
for error in &result.errors {
println!(" [{}] {} (clause {})",
error.code, error.message,
error.clause.as_deref().unwrap_or("n/a"));
}
} else {
println!("Document is PDF/A-1b compliant");
}
package main
import (
"fmt"
"log"
pdfoxide "github.com/yfedoseev/pdf_oxide/go"
)
func main() {
doc, err := pdfoxide.Open("archive.pdf")
if err != nil { log.Fatal(err) }
defer doc.Close()
// Level encoding: 0 = 1b, 1 = 1a, 2 = 2b, 3 = 2a, 4 = 2u, 5 = 3b, 6 = 3a, 7 = 3u
result, err := doc.ValidatePdfA(0) // PDF/A-1b
if err != nil { log.Fatal(err) }
if result.Valid {
fmt.Println("Document is PDF/A-1b compliant")
} else {
fmt.Println("Not PDF/A-1b compliant:")
for _, e := range result.Errors {
fmt.Printf(" %s\n", e)
}
}
}
Validator API
The PdfAValidator provides a builder pattern for fine-grained control:
use pdf_oxide::PdfDocument;
use pdf_oxide::compliance::{PdfAValidator, PdfALevel};
let mut doc = PdfDocument::open("report.pdf")?;
let result = PdfAValidator::new()
.stop_on_first_error(false)
.include_warnings(true)
.validate(&mut doc, PdfALevel::A2b)?;
println!("Errors: {}", result.errors.len());
println!("Warnings: {}", result.warnings.len());
Targeted Checks
Run individual validation categories instead of the full suite:
use pdf_oxide::PdfDocument;
use pdf_oxide::compliance::{PdfAValidator, PdfALevel};
let mut doc = PdfDocument::open("report.pdf")?;
let validator = PdfAValidator::new();
// Check only metadata
let result = validator.check_metadata(&mut doc, PdfALevel::A1b)?;
// Check only fonts
let result = validator.check_fonts(&mut doc, PdfALevel::A1b)?;
// Check only color spaces
let result = validator.check_colors(&mut doc, PdfALevel::A1b)?;
// Check only transparency
let result = validator.check_transparency(&mut doc, PdfALevel::A2b)?;
// Check only structure tags
let result = validator.check_structure(&mut doc, PdfALevel::A1a)?;
Standalone Validators
Each validation category is also available as a standalone function for maximum flexibility:
use pdf_oxide::PdfDocument;
use pdf_oxide::compliance::validators::*;
use pdf_oxide::compliance::{PdfALevel, ValidationResult};
let mut doc = PdfDocument::open("document.pdf")?;
let mut result = ValidationResult::new(PdfALevel::A1b);
// Run each validator independently
validate_xmp_metadata(&mut doc, PdfALevel::A1b, &mut result)?;
validate_fonts(&mut doc, PdfALevel::A1b, &mut result)?;
validate_colors(&mut doc, PdfALevel::A1b, &mut result)?;
validate_encryption(&mut doc, PdfALevel::A1b, &mut result)?;
validate_transparency(&mut doc, PdfALevel::A1b, &mut result)?;
validate_structure(&mut doc, PdfALevel::A1b, &mut result)?;
validate_javascript(&mut doc, PdfALevel::A1b, &mut result)?;
validate_embedded_files(&mut doc, PdfALevel::A1b, &mut result)?;
validate_annotations(&mut doc, PdfALevel::A1b, &mut result)?;
println!("Total errors: {}", result.errors.len());
Validator Summary
| Function | What It Checks |
|---|---|
validate_xmp_metadata() |
XMP stream exists, pdfaid:part and pdfaid:conformance entries present, metadata consistency |
validate_fonts() |
All fonts embedded, glyph widths present, Unicode mapping available (for level “a” and “u”) |
validate_colors() |
No device-dependent color operators (rg, RG, k, K, g, G) without output intent |
validate_encryption() |
No encryption permitted in PDF/A documents |
validate_transparency() |
No transparency in PDF/A-1; allowed in PDF/A-2 and later |
validate_structure() |
Tagged structure tree present with valid role mapping (required for level “a”) |
validate_javascript() |
No JavaScript actions or triggers present |
validate_embedded_files() |
Not allowed in PDF/A-1 or PDF/A-2; PDF/A-3 requires AFRelationship key on each file spec |
validate_annotations() |
Annotation types restricted per the relevant ISO 19005 part |
ValidationResult
The ValidationResult struct contains the full outcome of a validation run:
pub struct ValidationResult {
pub level: PdfALevel,
pub errors: Vec<ComplianceError>,
pub warnings: Vec<ComplianceWarning>,
pub stats: ValidationStats,
}
| Field | Type | Description |
|---|---|---|
level |
PdfALevel |
The target compliance level |
errors |
Vec<ComplianceError> |
Blocking violations that prevent compliance |
warnings |
Vec<ComplianceWarning> |
Non-blocking issues that may affect quality |
stats |
ValidationStats |
Counts of pages, fonts, and objects checked |
ComplianceError
pub struct ComplianceError {
pub code: ErrorCode,
pub message: String,
pub location: Option<String>,
pub clause: Option<String>,
}
The code field uses the ErrorCode enum with categories like MissingXmpMetadata, FontNotEmbedded, DeviceDependentColor, EncryptionPresent, TransparencyNotAllowed, MissingStructureTree, JavaScriptPresent, and InvalidEmbeddedFile.
ComplianceWarning
pub struct ComplianceWarning {
pub code: WarningCode,
pub message: String,
pub location: Option<String>,
}
PDF/A Conversion
Convert a non-compliant document toward PDF/A compliance:
use pdf_oxide::PdfDocument;
use pdf_oxide::compliance::{convert_to_pdf_a, PdfALevel};
let mut doc = PdfDocument::open("input.pdf")?;
let result = convert_to_pdf_a(&mut doc, PdfALevel::A1b)?;
println!("Conversion actions taken:");
for action in &result.actions {
println!(" - {}: {}", action.action_type, action.description);
}
if result.remaining_errors.is_empty() {
println!("Document is now PDF/A-1b compliant");
} else {
println!("{} issues could not be resolved automatically",
result.remaining_errors.len());
}
Conversion Config
Fine-tune the conversion process:
use pdf_oxide::PdfDocument;
use pdf_oxide::compliance::{PdfAConverter, PdfALevel, ConversionConfig};
let mut doc = PdfDocument::open("input.pdf")?;
let config = ConversionConfig::new()
.embed_fonts(true)
.remove_javascript(true)
.flatten_transparency(true)
.add_structure(true);
let result = PdfAConverter::new(PdfALevel::A2b)
.with_config(config)
.convert(&mut doc)?;
The converter performs these actions automatically:
- XMP metadata injection – adds
pdfaid:partandpdfaid:conformanceentries - Font embedding – embeds any referenced but non-embedded fonts
- JavaScript removal – strips JavaScript actions and triggers
- Transparency flattening – renders transparent elements to opaque (PDF/A-1 only)
- ICC profile conversion – converts device-dependent colors to ICC-based color spaces
- Structure tagging – adds basic structure tags (for level “a” targets)
Workflow: Validate, Fix, Re-validate
A typical archival workflow validates, attempts automatic conversion, then re-validates:
use pdf_oxide::PdfDocument;
use pdf_oxide::compliance::{validate_pdf_a, convert_to_pdf_a, PdfALevel};
let level = PdfALevel::A2b;
let mut doc = PdfDocument::open("input.pdf")?;
// Step 1: Initial validation
let result = validate_pdf_a(&mut doc, level)?;
if !result.has_errors() {
println!("Already compliant");
return Ok(());
}
println!("{} errors found, attempting conversion...", result.errors.len());
// Step 2: Automatic conversion
let conversion = convert_to_pdf_a(&mut doc, level)?;
println!("{} actions taken", conversion.actions.len());
// Step 3: Re-validate
let result = validate_pdf_a(&mut doc, level)?;
if result.has_errors() {
println!("{} errors remain after conversion:", result.errors.len());
for e in &result.errors {
println!(" {} -- {}", e.code, e.message);
}
} else {
println!("Document is now PDF/A-2b compliant");
}
PdfALevel Methods
The PdfALevel enum includes helper methods for querying level capabilities:
| Method | Return | Description |
|---|---|---|
part() |
PdfAPart |
ISO 19005 part (Part1, Part2, Part3) |
conformance() |
char |
Conformance letter (‘a’, ‘b’, or ‘u’) |
requires_structure() |
bool |
Whether tagged structure tree is mandatory |
requires_unicode() |
bool |
Whether Unicode mapping is mandatory |
allows_transparency() |
bool |
Whether transparency is permitted |
allows_jpeg2000() |
bool |
Whether JPEG 2000 images are permitted |
allows_embedded_files() |
bool |
Whether file attachments are permitted |
xmp_part() |
&str |
XMP pdfaid:part value |
xmp_conformance() |
&str |
XMP pdfaid:conformance value |
from_xmp(part, conformance) |
Option<Self> |
Parse level from XMP metadata values |
Next Steps
- PDF/UA Accessibility – accessibility validation
- PDF/X Print Production – print production validation
- API Reference – complete Rust API