Доступность 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– в таблице отсутствуют элементыTHFormFieldNoLabel– у поля формы нет связанной метки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);
Следующие шаги
- Валидация PDF/A – соответствие требованиям архивного хранения
- PDF/X для полиграфии – соответствие требованиям подготовки к печати
- Справочник API – полный API на Rust