Accesibilidad PDF/UA
PDF/UA (ISO 14289) define los requisitos para documentos PDF universalmente accesibles. PDF Oxide valida árboles de estructura, secuencias de encabezados, texto alternativo, encabezados de tabla, declaraciones de idioma y más.
Cobertura por binding. La validación PDF/UA está expuesta en Python (
doc.validate_pdf_ua()), Rust (validate_pdf_ua+ el builderPdfUaValidator) y Go (doc.ValidatePdfUa()). WASM expone una comprobación básica de aprobado/reprobado mediantevalidatePdfUacuando está disponible. Aún no se ha publicado un wrapper público para C# — usa la CLI de Rust (pdf-oxide validate --pdfua doc.pdf) o realiza la llamada a través de uno de los bindings compatibles.
Niveles compatibles
| Nivel | Estándar | Descripción |
|---|---|---|
| PDF/UA-1 | ISO 14289-1:2014 | Requisitos básicos de accesibilidad |
| PDF/UA-2 | ISO 14289-2:2024 | Requisitos ampliados, alineados con la 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");
}
doc, err := pdfoxide.Open("accessible.pdf")
if err != nil { log.Fatal(err) }
defer doc.Close()
valid, errs, err := doc.ValidatePdfUa()
if err != nil { log.Fatal(err) }
if valid {
fmt.Println("Document is PDF/UA-1 compliant")
} else {
fmt.Println("Not PDF/UA-1 compliant:")
for _, e := range errs {
fmt.Printf(" %s\n", e)
}
}
API del validador
El builder PdfUaValidator permite configurar comprobaciones 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
| Método | Predeterminado | Descripción |
|---|---|---|
check_heading_sequence(bool) |
true |
Valida que H1-H6 no salten niveles |
check_color_contrast(bool) |
true |
Señala posibles problemas de contraste |
allow_custom_types(Vec<String>) |
[] |
Permite tipos de estructura no estándar sin advertencia |
Inspección del árbol de estructura
Antes de ejecutar la validación, puedes inspeccionar el árbol de estructura del documento y la información de marcado:
use pdf_oxide::PdfDocument;
let mut doc = PdfDocument::open("tagged.pdf")?;
// Check if the document claims to be tagged
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() devuelve:
| Campo | Tipo | Descripción |
|---|---|---|
marked |
bool |
Si el documento se declara como etiquetado (tagged) |
suspects |
bool |
Si las asignaciones de etiquetas podrían ser incorrectas |
user_properties |
bool |
Si hay propiedades de usuario 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, que podría no ser fiable.
Qué se comprueba
El validador cubre los siguientes requisitos de PDF/UA:
Nivel de documento
| Comprobación | Cláusula | Descripción |
|---|---|---|
| Idioma | 7.2 | Entrada /Lang presente en el catálogo |
| Título | 7.1 | Título del documento definido y mostrado en la barra de título |
| Etiquetado | 7.1 | El diccionario MarkInfo declara Marked = true |
| Metadatos XMP | 7.1 | pdfuaid:part declarado en el flujo XMP |
Estructura
| Comprobación | Cláusula | Descripción |
|---|---|---|
| Árbol de estructura | 7.1 | Árbol de estructura completo con raíz en StructTreeRoot |
| Asignación de roles | 7.5 | Tipos no estándar asignados a elementos de estructura estándar |
| Jerarquía de encabezados | 7.4.2 | Los encabezados (H1-H6) no saltan niveles |
| Marcado de artefactos | 7.3 | Contenido decorativo marcado como artefacto |
| Orden de lectura | 7.2 | El árbol de estructura define un orden de lectura lógico |
Contenido
| Comprobación | Cláusula | Descripción |
|---|---|---|
| Texto alternativo para imágenes | 7.3 | /Alt o /ActualText en los elementos Figure |
| Encabezados de tabla | 7.5 | Elementos TH presentes en las estructuras de tabla |
| Etiquetas de formulario | 7.6.2 | Los campos de formulario tienen etiquetas o sugerencias asociadas |
| Texto de enlace | 7.18 | Las anotaciones de enlace tienen contenido descriptivo |
| Estructura de lista | 7.4.3 | Las listas usan la estructura L, LI, Lbl, LBody |
Fuente y texto
| Comprobación | Cláusula | Descripción |
|---|---|---|
| Asignación Unicode | 7.21.3 | Todo el texto tiene una representación Unicode |
| Incrustación de fuentes | 7.21.4 | Fuentes incrustadas o fuentes Base14 estándar |
| ActualText | 7.21.5 | Las ligaduras y los glifos especiales tienen /ActualText |
UaValidationResult
pub struct UaValidationResult {
pub level: PdfUaLevel,
pub errors: Vec<UaComplianceError>,
pub warnings: Vec<ComplianceWarning>,
pub stats: UaValidationStats,
}
UaComplianceError
Cada error incluye una alineación opcional con la WCAG:
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 asigna la infracción de PDF/UA al criterio de éxito correspondiente de la WCAG (por ejemplo, "1.1.1" para contenido no textual, "1.3.1" para información y relaciones).
Categorías de UaErrorCode
El enum UaErrorCode incluye categorías de error como:
MissingLanguage– no hay entrada/Langen el catálogo del documentoMissingStructureTree– el documento no está etiquetadoMissingAltText– el elemento Figure carece de texto alternativoHeadingSkipped– los niveles de encabezado saltan (por ejemplo, de H1 a H3)MissingTableHeaders– la tabla carece de elementosTHFormFieldNoLabel– el campo de formulario no tiene etiqueta asociadaInvalidRoleMapping– tipo no estándar sin asignar a un elemento estándarArtifactNotMarked– contenido decorativo no marcado como artefactoMissingUnicode– texto sin asignación Unicode
Ejemplo práctico: informe de accesibilidad
Genera un informe de accesibilidad legible por humanos a partir de los resultados de la 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
- Validación PDF/A – conformidad para archivado
- PDF/X para producción de impresión – conformidad para producción de impresión
- Referencia de la API – API completa de Rust