Skip to content

Acessibilidade PDF/UA

A PDF/UA (ISO 14289) define os requisitos para documentos PDF universalmente acessíveis. O PDF Oxide valida árvores de estrutura, sequências de títulos, texto alternativo, cabeçalhos de tabela, declarações de idioma e muito mais.

Cobertura por binding. A validação PDF/UA está exposta em Python (doc.validate_pdf_ua()), Rust (validate_pdf_ua + o builder PdfUaValidator) e Go (doc.ValidatePdfUa()). O WASM expõe uma verificação básica de aprovado/reprovado via validatePdfUa quando disponível. Um wrapper público em C# ainda não foi disponibilizado — use a CLI em Rust (pdf-oxide validate --pdfua doc.pdf) ou faça a chamada por meio de um dos bindings suportados.

Níveis suportados

Nível Padrão Descrição
PDF/UA-1 ISO 14289-1:2014 Requisitos básicos de acessibilidade
PDF/UA-2 ISO 14289-2:2024 Requisitos aprimorados, alinhados com a 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");
}
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 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);

Opções de configuração

Método Padrão Descrição
check_heading_sequence(bool) true Valida se H1-H6 não pulam níveis
check_color_contrast(bool) true Sinaliza possíveis problemas de contraste
allow_custom_types(Vec<String>) [] Permite tipos de estrutura não padronizados sem aviso

Inspeção da árvore de estrutura

Antes de executar a validação, você pode inspecionar a árvore de estrutura do documento e as informações de marcação:

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 Se o documento se declara marcado (tagged)
suspects bool Se as atribuições de tags podem estar incorretas
user_properties bool Se há propriedades de usuário presentes

Quando suspects é true, o PDF Oxide recorre automaticamente à ordenação geométrica para a extração de texto, em vez de depender da árvore de estrutura, que pode ser pouco confiável.

O que é verificado

O validador cobre os seguintes requisitos da PDF/UA:

Nível do documento

Verificação Cláusula Descrição
Idioma 7.2 Entrada /Lang presente no catálogo
Título 7.1 Título do documento definido e exibido na barra de título
Marcado 7.1 O dicionário MarkInfo declara Marked = true
Metadados XMP 7.1 pdfuaid:part declarado no fluxo XMP

Estrutura

Verificação Cláusula Descrição
Árvore de estrutura 7.1 Árvore de estrutura completa enraizada em StructTreeRoot
Mapeamento de papéis 7.5 Tipos não padronizados mapeados para elementos de estrutura padrão
Hierarquia de títulos 7.4.2 Títulos (H1-H6) não pulam níveis
Marcação de artefatos 7.3 Conteúdo decorativo marcado como artefato
Ordem de leitura 7.2 A árvore de estrutura define uma ordem de leitura lógica

Conteúdo

Verificação Cláusula Descrição
Texto alternativo para imagens 7.3 /Alt ou /ActualText em elementos Figure
Cabeçalhos de tabela 7.5 Elementos TH presentes nas estruturas de tabela
Rótulos de formulário 7.6.2 Campos de formulário têm rótulos ou dicas associados
Texto de link 7.18 Anotações de link têm conteúdo descritivo
Estrutura de lista 7.4.3 Listas usam a estrutura L, LI, Lbl, LBody

Fonte e texto

Verificação Cláusula Descrição
Mapeamento Unicode 7.21.3 Todo o texto tem uma representação Unicode
Incorporação de fontes 7.21.4 Fontes incorporadas ou fontes Base14 padrão
ActualText 7.21.5 Ligaduras e glifos especiais têm /ActualText

UaValidationResult

pub struct UaValidationResult {
    pub level: PdfUaLevel,
    pub errors: Vec<UaComplianceError>,
    pub warnings: Vec<ComplianceWarning>,
    pub stats: UaValidationStats,
}

UaComplianceError

Cada erro inclui um alinhamento opcional com a WCAG:

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 de PDF/UA para o critério de sucesso correspondente da WCAG (por exemplo, "1.1.1" para conteúdo não textual, "1.3.1" para informação e relações).

Categorias de UaErrorCode

O enum UaErrorCode inclui categorias de erro como:

  • MissingLanguage – nenhuma entrada /Lang no catálogo do documento
  • MissingStructureTree – o documento não está marcado
  • MissingAltText – elemento Figure sem texto alternativo
  • HeadingSkipped – níveis de título saltam (por exemplo, de H1 para H3)
  • MissingTableHeaders – a tabela não tem elementos TH
  • FormFieldNoLabel – campo de formulário sem rótulo associado
  • InvalidRoleMapping – tipo não padronizado não mapeado para um elemento padrão
  • ArtifactNotMarked – conteúdo decorativo não marcado como artefato
  • MissingUnicode – texto sem mapeamento Unicode

Exemplo prático: relatório de acessibilidade

Gere um relatório de acessibilidade legível por humanos a partir dos resultados da 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