Skip to content

Доступность PDF/UA

PDF/UA (ISO 14289) определяет требования к универсально доступным PDF-документам. PDF Oxide проверяет деревья структуры, последовательность заголовков, альтернативный текст, заголовки таблиц, объявления языка и многое другое.

Поддержка в привязках. Валидация PDF/UA доступна в Python (doc.validate_pdf_ua()), Rust (validate_pdf_ua и построитель PdfUaValidator) и Go (doc.ValidatePdfUa()). WASM предоставляет базовую проверку «прошло/не прошло» через validatePdfUa, когда она доступна. Публичная обёртка для C# пока не выпущена — используйте Rust CLI (pdf-oxide validate --pdfua doc.pdf) или вызывайте проверку через одну из поддерживаемых привязок.

Поддерживаемые уровни

Уровень Стандарт Описание
PDF/UA-1 ISO 14289-1:2014 Базовые требования к доступности
PDF/UA-2 ISO 14289-2:2024 Расширенные требования, согласованные с WCAG 2.1

Быстрая проверка

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 валидатора

Построитель PdfUaValidator позволяет настраивать конкретные проверки:

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

Параметры конфигурации

Метод По умолчанию Описание
check_heading_sequence(bool) true Проверять, что H1-H6 не пропускают уровни
check_color_contrast(bool) true Помечать возможные проблемы с контрастностью
allow_custom_types(Vec<String>) [] Разрешать нестандартные типы структуры без предупреждения

Проверка дерева структуры

Перед запуском валидации можно проверить дерево структуры документа и информацию о разметке:

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

Метод mark_info() возвращает:

Поле Тип Описание
marked bool Объявляет ли документ себя размеченным (tagged)
suspects bool Могут ли назначения тегов быть некорректными
user_properties bool Присутствуют ли пользовательские свойства

Когда suspects равно true, PDF Oxide автоматически переключается на геометрический порядок при извлечении текста вместо того, чтобы полагаться на потенциально ненадёжное дерево структуры.

Что именно проверяется

Валидатор охватывает следующие требования PDF/UA:

Уровень документа

Проверка Пункт Описание
Язык 7.2 В каталоге присутствует запись /Lang
Заголовок 7.1 Заголовок документа задан и отображается в строке заголовка
Размеченность 7.1 Словарь MarkInfo объявляет Marked = true
Метаданные XMP 7.1 В потоке XMP объявлен pdfuaid:part

Структура

Проверка Пункт Описание
Дерево структуры 7.1 Полное дерево структуры с корнем StructTreeRoot
Сопоставление ролей 7.5 Нестандартные типы сопоставлены со стандартными элементами структуры
Иерархия заголовков 7.4.2 Заголовки (H1-H6) не пропускают уровни
Разметка артефактов 7.3 Декоративное содержимое помечено как артефакт
Порядок чтения 7.2 Дерево структуры задаёт логический порядок чтения

Содержимое

Проверка Пункт Описание
Альтернативный текст для изображений 7.3 /Alt или /ActualText на элементах Figure
Заголовки таблиц 7.5 Элементы TH присутствуют в структурах таблиц
Метки полей форм 7.6.2 У полей форм есть связанные метки или всплывающие подсказки
Текст ссылок 7.18 Аннотации ссылок имеют описательное содержимое
Структура списков 7.4.3 Списки используют структуру L, LI, Lbl, LBody

Шрифты и текст

Проверка Пункт Описание
Сопоставление Unicode 7.21.3 Весь текст имеет представление в Unicode
Встраивание шрифтов 7.21.4 Шрифты встроены либо являются стандартными шрифтами Base14
ActualText 7.21.5 Лигатуры и специальные глифы имеют /ActualText

UaValidationResult

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

UaComplianceError

Каждая ошибка включает необязательное сопоставление с WCAG:

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

Поле wcag_ref сопоставляет нарушение PDF/UA с соответствующим критерием успеха WCAG (например, "1.1.1" для нетекстового содержимого, "1.3.1" для информации и взаимосвязей).

Категории UaErrorCode

Перечисление UaErrorCode включает такие категории ошибок, как:

  • MissingLanguage – нет записи /Lang в каталоге документа
  • MissingStructureTree – документ не размечен
  • MissingAltText – у элемента Figure отсутствует альтернативный текст
  • HeadingSkipped – уровни заголовков перескакивают (например, с H1 на H3)
  • MissingTableHeaders – в таблице отсутствуют элементы TH
  • FormFieldNoLabel – у поля формы нет связанной метки
  • InvalidRoleMapping – нестандартный тип не сопоставлен со стандартным элементом
  • ArtifactNotMarked – декоративное содержимое не помечено как артефакт
  • MissingUnicode – текст без сопоставления с Unicode

Практический пример: отчёт о доступности

Сформируйте читаемый человеком отчёт о доступности на основе результатов валидации:

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

Следующие шаги