Skip to content

PDF/UA 접근성

PDF/UA(ISO 14289)는 누구나 접근할 수 있는 PDF 문서에 대한 요구 사항을 정의합니다. PDF Oxide는 구조 트리, 제목 순서, 대체 텍스트, 표 헤더, 언어 선언 등을 검증합니다.

바인딩 지원 현황. PDF/UA 검증은 Python(doc.validate_pdf_ua()), Rust(validate_pdf_uaPdfUaValidator 빌더), 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 문서가 자신을 태그된 문서로 선언하는지 여부
suspects bool 태그 할당이 잘못되었을 수 있는지 여부
user_properties bool 사용자 속성이 존재하는지 여부

suspectstrue이면 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 Figure 요소에 /Alt 또는 /ActualText가 있음
표 헤더 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);

다음 단계