Validación PDF/UA
PDF/UA (ISO 14289) define requisitos para documentos PDF universalmente accesibles. PDF Oxide valida árboles de estructura, secuencias de encabezados, texto alternativo, encabezados de tablas, declaraciones de idioma y más.
Niveles soportados
| Level | Standard | Descripción |
|---|---|---|
| PDF/UA-1 | ISO 14289-1:2014 | Requisitos base de accesibilidad |
| PDF/UA-2 | ISO 14289-2:2024 | Requisitos mejorados, alineados con WCAG 2.1 |
Validación rápida
from pdf_oxide import PdfDocument
doc = PdfDocument("document.pdf")
result = doc.validate_pdf_ua()
print(f"Valid: {result.valid}")
for error in result.errors:
print(f" {error}")
use pdf_oxide::PdfDocument;
use pdf_oxide::compliance::{validate_pdf_ua, PdfUaLevel};
let mut doc = PdfDocument::open("accessible.pdf")?;
let result = validate_pdf_ua(&mut doc, PdfUaLevel::UA1)?;
if result.has_errors() {
println!("Not PDF/UA-1 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/UA-1 compliant");
}
API del validador
El builder PdfUaValidator permite configurar verificaciones específicas:
use pdf_oxide::PdfDocument;
use pdf_oxide::compliance::{PdfUaValidator, PdfUaLevel};
let mut doc = PdfDocument::open("report.pdf")?;
let result = PdfUaValidator::new()
.check_heading_sequence(true)
.check_color_contrast(true)
.allow_custom_types(vec!["Caption".into(), "Aside".into()])
.validate(&mut doc, PdfUaLevel::UA1)?;
println!("Errors: {}", result.errors.len());
println!("Warnings: {}", result.warnings.len());
println!("Structure elements checked: {}",
result.stats.structure_elements_checked);
Opciones de configuración
| Method | Predeterminado | Descripción |
|---|---|---|
check_heading_sequence(bool) |
true |
Validar que H1-H6 no salten niveles |
check_color_contrast(bool) |
true |
Marcar posibles problemas de contraste |
allow_custom_types(Vec<String>) |
[] |
Permitir tipos de estructura no estándar sin advertencia |
Inspección del árbol de estructura
Antes de ejecutar la validación, puede inspeccionar el árbol de estructura del documento y la información de marcado:
use pdf_oxide::PdfDocument;
let mut doc = PdfDocument::open("tagged.pdf")?;
// Verificar si el documento se declara como etiquetado
let mark_info = doc.mark_info()?;
println!("Marked: {}", mark_info.marked);
println!("Suspects: {}", mark_info.suspects);
// Access the structure tree
if let Some(tree) = doc.structure_tree()? {
println!("Root tag: {}", tree.root_type);
println!("Children: {}", tree.children.len());
}
El método mark_info() retorna:
| Campo | Tipo | Descripción |
|---|---|---|
marked |
bool |
Si el documento se declara como etiquetado |
suspects |
bool |
Si las asignaciones de etiquetas pueden ser incorrectas |
user_properties |
bool |
Si las propiedades de usuario están presentes |
Cuando suspects es true, PDF Oxide recurre automáticamente al ordenamiento geométrico para la extracción de texto en lugar de depender del árbol de estructura potencialmente no confiable.
Qué se verifica
El validador cubre los siguientes requisitos de PDF/UA:
A nivel de documento
| Check | Clause | Descripción |
|---|---|---|
| Language | 7.2 | /Lang entry present in the catalog |
| Title | 7.1 | Título del documento set and displayed in title bar |
| Tagged | 7.1 | MarkInfo dictionary declares Marked = true |
| XMP metadata | 7.1 | pdfuaid:part declared in XMP stream |
Estructura
| Check | Clause | Descripción |
|---|---|---|
| Structure tree | 7.1 | Complete structure tree rooted at StructTreeRoot |
| Role mapping | 7.5 | Non-standard types mapped to standard structure elements |
| Heading hierarchy | 7.4.2 | Headings (H1-H6) do not skip levels |
| Artifact marking | 7.3 | Decorative content marked as artifact |
| Reading order | 7.2 | Structure tree defines a logical reading order |
Contenido
| Check | Clause | Descripción |
|---|---|---|
| Alt text for images | 7.3 | /Alt or /ActualText on Figure elements |
| Table headers | 7.5 | TH elements present in table structures |
| Form labels | 7.6.2 | Form fields have associated labels or tooltips |
| Link text | 7.18 | Link annotations have descriptive content |
| List structure | 7.4.3 | Lists use L, LI, Lbl, LBody structure |
Fuente y texto
| Check | Clause | Descripción |
|---|---|---|
| Unicode mapping | 7.21.3 | All text has a Unicode representation |
| Font embedding | 7.21.4 | Fonts embedded or standard Base14 fonts |
| ActualText | 7.21.5 | Ligatures and special glyphs have /ActualText |
UaValidationResult
pub struct UaValidationResult {
pub level: PdfUaLevel,
pub errors: Vec<UaComplianceError>,
pub warnings: Vec<ComplianceWarning>,
pub stats: UaValidationStats,
}
UaComplianceError
Cada error incluye alineación WCAG opcional:
pub struct UaComplianceError {
pub code: UaErrorCode,
pub message: String,
pub location: Option<String>,
pub wcag_ref: Option<String>,
pub clause: Option<String>,
}
El campo wcag_ref mapea la violación de PDF/UA al criterio de éxito WCAG correspondiente (e.g., "1.1.1" for non-text content, "1.3.1" for info and relationships).
Categorías de UaErrorCode
El enum UaErrorCode incluye categorías de error como:
MissingLanguage– no/Langentry on the document catalogMissingStructureTree– document is not taggedMissingAltText– Figure element lacks alt textHeadingSkipped– heading levels jump (e.g., H1 to H3)MissingTableHeaders– table lacksTHelementsFormFieldNoLabel– form field has no associated labelInvalidRoleMapping– non-standard type not mapped to a standard elementArtifactNotMarked– decorative content not marked as artifactMissingUnicode– text without Unicode mapping
Ejemplo práctico: reporte de accesibilidad
Genere un reporte de accesibilidad legible por humanos a partir de los resultados de validación:
use pdf_oxide::PdfDocument;
use pdf_oxide::compliance::{validate_pdf_ua, PdfUaLevel};
let mut doc = PdfDocument::open("document.pdf")?;
let result = validate_pdf_ua(&mut doc, PdfUaLevel::UA1)?;
println!("=== PDF/UA Accessibility Report ===");
println!("Level: PDF/UA-{}", result.level.xmp_part());
println!("Status: {}", if result.has_errors() { "FAIL" } else { "PASS" });
println!();
if result.has_errors() {
println!("Errors ({}):", result.errors.len());
for (i, error) in result.errors.iter().enumerate() {
print!(" {}. [{}] {}", i + 1, error.code, error.message);
if let Some(ref wcag) = error.wcag_ref {
print!(" (WCAG {})", wcag);
}
println!();
}
}
if result.has_warnings() {
println!("\nWarnings ({}):", result.warnings.len());
for warning in &result.warnings {
println!(" - [{}] {}", warning.code, warning.message);
}
}
println!("\nStats:");
println!(" Structure elements checked: {}",
result.stats.structure_elements_checked);
Próximos pasos
- PDF/A Validation – archival compliance
- PDF/X Print Production – print production compliance
- API Reference – complete Rust API