Skip to content

PDF/UA 无障碍

PDF/UA(ISO 14289)定义了通用无障碍 PDF 文档的要求。PDF Oxide 验证结构树、标题序列、替代文本、表格表头、语言声明等。

支持的级别

级别 标准 描述
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, PdfUa级别};

let mut doc = PdfDocument::open("accessible.pdf")?;
let result = validate_pdf_ua(&mut doc, PdfUa级别::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

PdfUaValidator 构建器允许配置特定的检查项:

use pdf_oxide::PdfDocument;
use pdf_oxide::compliance::{PdfUaValidator, PdfUa级别};

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, PdfUa级别::UA1)?;

println!("Errors: {}", result.errors.len());
println!("Warnings: {}", result.warnings.len());
println!("结构 elements checked: {}",
    result.stats.structure_elements_checked);

配置选项

Method 默认值 描述
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

结构树检查

在运行验证之前,你可以检查文档的结构树和标记信息:

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 Whether the document declares itself as tagged
suspects bool Whether tag assignments may be incorrect
user_properties bool Whether user properties are present

suspectstrue 时,PDF Oxide 自动回退到基于几何位置的排序进行文本提取,而非依赖可能不可靠的结构树。

检查内容

验证器涵盖以下 PDF/UA 要求:

文档级

检查项 条款 描述
语言 7.2 /Lang 条目存在于目录中
Title 7.1 文档标题已设置并显示在标题栏中
Tagged 7.1 MarkInfo 字典声明 Marked = true
XMP metadata 7.1 pdfuaid:part 在 XMP 流中声明

结构

检查项 条款 描述
结构 tree 7.1 以 StructTreeRoot 为根的完整结构树
Role mapping 7.5 非标准类型映射到标准结构元素
Heading hierarchy 7.4.2 标题(H1-H6)不跳过级别
Artifact marking 7.3 装饰性内容标记为 artifact
Reading order 7.2 结构树定义逻辑阅读顺序

内容

检查项 条款 描述
Alt text for images 7.3 /Alt or /ActualText 在 Figure 元素上
Table headers 7.5 TH 元素存在于表格结构中
Form labels 7.6.2 表单字段 have associated labels or tooltips
Link text 7.18 链接注释具有描述性内容
List structure 7.4.3 列表使用 L, LI, Lbl, LBody structure

字体和文本

检查项 条款 描述
Unicode mapping 7.21.3 所有文本具有 Unicode 表示
Font embedding 7.21.4 字体已嵌入或为标准 Base14 字体
ActualText 7.21.5 连字和特殊字形具有 /ActualText

UaValidationResult

pub struct UaValidationResult {
    pub level: PdfUa级别,
    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 Categories

UaErrorCode 枚举包含以下错误类别:

  • MissingLanguage – 文档目录中没有 /Lang 条目
  • Missing结构Tree – 文档未标记
  • MissingAltText – Figure 元素缺少替代文本
  • HeadingSkipped – 标题级别跳跃(例如 H1 到 H3)
  • MissingTableHeaders – 表格缺少 TH 元素
  • FormFieldNoLabel – 表单字段 has no associated label
  • InvalidRoleMapping – 非标准类型未映射到标准元素
  • ArtifactNotMarked – 装饰性内容未标记为 artifact
  • MissingUnicode – 文本缺少 Unicode 映射

实际示例:无障碍报告

从验证结果生成人类可读的无障碍报告:

use pdf_oxide::PdfDocument;
use pdf_oxide::compliance::{validate_pdf_ua, PdfUa级别};

let mut doc = PdfDocument::open("document.pdf")?;
let result = validate_pdf_ua(&mut doc, PdfUa级别::UA1)?;

println!("=== PDF/UA Accessibility Report ===");
println!("级别: 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!("  结构 elements checked: {}",
    result.stats.structure_elements_checked);

下一步