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 |
문서가 자신을 태그된 문서로 선언하는지 여부 |
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 | 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);
다음 단계
- PDF/A 검증 – 보존용 준수
- PDF/X 인쇄 제작 – 인쇄 제작 준수
- API 레퍼런스 – 전체 Rust API