Skip to content

PDF/UA Accessibility

PDF/UA (ISO 14289) define requisitos para documentos PDF universalmente acessíveis. O PDF Oxide valida árvores de estrutura, sequencias de titulos, texto alternativo, cabecalhos de tabelas, declaracoes de idioma e mais.

Níveis Suportados

Level Standard Descrição
PDF/UA-1 ISO 14289-1:2014 Base accessibility requirements
PDF/UA-2 ISO 14289-2:2024 Enhanced requirements, aligned with WCAG 2.1

Validação 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 do Validador

O builder PdfUaValidator permite configurar verificações 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);

Configuração Options

Method Padrão Descrição
check_heading_sequence(bool) true Validate H1-H6 do not skip levels
check_color_contrast(bool) true Flag potential contrast issues
allow_custom_types(Vec<String>) [] Permit non-standard structure types without warning

Inspecao da Arvore de Estrutura

Before running validation, you can inspect the document’s structure tree and mark info:

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());
}

O método mark_info() retorna:

Campo Tipo Descrição
marked bool Whether the document declares itself as tagged
suspects bool Whether tag assignments may be incorrect
user_properties bool Whether user properties are present

Quando suspects é true, o PDF Oxide automaticamente recorre à ordenação geométrica para extração de texto em vez de confiar na árvore de estrutura potencialmente não confiável.

O Que e Verificado

O validador cobre os seguintes requisitos PDF/UA:

Nível de Documento

Check Clause Descrição
Language 7.2 /Lang entry present in the catalog
Title 7.1 Título do 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

Estrutura

Check Clause Descrição
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 ordem de leitura

Conteúdo

Check Clause Descrição
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 Campos de formulário 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

Fonte e Texto

Check Clause Descrição
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 erro inclui alinhamento WCAG opcional:

pub struct UaComplianceError {
    pub code: UaErrorCode,
    pub message: String,
    pub location: Option<String>,
    pub wcag_ref: Option<String>,
    pub clause: Option<String>,
}

O campo wcag_ref mapeia a violação PDF/UA para o critério de sucesso WCAG correspondente (e.g., "1.1.1" for non-text content, "1.3.1" for info and relationships).

UaErrorCode Categories

O enum UaErrorCode inclui categorias de erro como:

  • MissingLanguage – no /Lang entry on the document catalog
  • MissingStructureTree – document is not tagged
  • MissingAltText – Figure element lacks alt text
  • HeadingSkipped – heading levels jump (e.g., H1 to H3)
  • MissingTableHeaders – table lacks TH elements
  • FormFieldNoLabel – form field has no associated label
  • InvalidRoleMapping – non-standard type not mapped to a standard element
  • ArtifactNotMarked – decorative content not marked as artifact
  • MissingUnicode – text without Unicode mapping

Exemplo Pratico: Accessibility Report

Gerar um relatório de acessibilidade legível a partir dos resultados de validação:

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 Passos