PDF/A 校验
PDF/A(ISO 19005)是电子文档长期归档的国际标准。PDF Oxide 可校验所有主要的 PDF/A 级别,并能将不合规的文档向合规方向转换。
绑定支持情况。 PDF/A 校验已在 Python(
doc.validate_pdf_a(level))、Rust(validate_pdf_a+PdfAValidator构建器)、WASM(doc.validatePdfA(level))和 Go(doc.ValidatePdfA(level))中提供。目前尚未公开 C# 封装——请使用 Rust CLI(pdf-oxide validate --pdfa 2b doc.pdf),或通过受支持的绑定之一进行调用。PDF/A 转换(PdfAConverter)目前仅在 Rust 和 Python 中公开。
支持的级别
| 级别 | 标准 | 结构 | Unicode | 透明度 | 嵌入文件 |
|---|---|---|---|---|---|
| 1a | ISO 19005-1 | 必需 | 必需 | 否 | 否 |
| 1b | ISO 19005-1 | 不要求 | 不要求 | 否 | 否 |
| 2a | ISO 19005-2 | 必需 | 必需 | 是 | 否 |
| 2b | ISO 19005-2 | 不要求 | 不要求 | 是 | 否 |
| 2u | ISO 19005-2 | 不要求 | 必需 | 是 | 否 |
| 3a | ISO 19005-3 | 必需 | 必需 | 是 | 是 |
| 3b | ISO 19005-3 | 不要求 | 不要求 | 是 | 是 |
| 3u | ISO 19005-3 | 不要求 | 必需 | 是 | 是 |
级别 “a”(accessible,可访问)要求带标记的结构树和 Unicode 字符映射。级别 “b”(basic,基本)仅要求视觉上可还原。级别 “u”(Unicode)要求 Unicode 文本映射,但无需完整的结构树。
快速校验
使用便捷函数即可一次调用完成检查:
from pdf_oxide import PdfDocument
doc = PdfDocument("document.pdf")
result = doc.validate_pdf_a("2b")
print(f"Valid: {result.valid}")
print(f"Level: {result.level}")
for error in result.errors:
print(f" Error: {error}")
const doc = new WasmPdfDocument(bytes);
const result = doc.validatePdfA("2b");
console.log(`Valid: ${result.valid}`);
console.log(`Errors: ${result.errors.length}`);
doc.free();
use pdf_oxide::PdfDocument;
use pdf_oxide::compliance::{validate_pdf_a, PdfALevel};
let mut doc = PdfDocument::open("archive.pdf")?;
let result = validate_pdf_a(&mut doc, PdfALevel::A1b)?;
if result.has_errors() {
println!("Not PDF/A-1b 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/A-1b compliant");
}
package main
import (
"fmt"
"log"
pdfoxide "github.com/yfedoseev/pdf_oxide/go"
)
func main() {
doc, err := pdfoxide.Open("archive.pdf")
if err != nil { log.Fatal(err) }
defer doc.Close()
// Level encoding: 0 = 1b, 1 = 1a, 2 = 2b, 3 = 2a, 4 = 2u, 5 = 3b, 6 = 3a, 7 = 3u
result, err := doc.ValidatePdfA(0) // PDF/A-1b
if err != nil { log.Fatal(err) }
if result.Valid {
fmt.Println("Document is PDF/A-1b compliant")
} else {
fmt.Println("Not PDF/A-1b compliant:")
for _, e := range result.Errors {
fmt.Printf(" %s\n", e)
}
}
}
校验器 API
PdfAValidator 提供构建器模式,可实现细粒度的控制:
use pdf_oxide::PdfDocument;
use pdf_oxide::compliance::{PdfAValidator, PdfALevel};
let mut doc = PdfDocument::open("report.pdf")?;
let result = PdfAValidator::new()
.stop_on_first_error(false)
.include_warnings(true)
.validate(&mut doc, PdfALevel::A2b)?;
println!("Errors: {}", result.errors.len());
println!("Warnings: {}", result.warnings.len());
定向检查
可以只运行单个校验类别,而非完整的校验套件:
use pdf_oxide::PdfDocument;
use pdf_oxide::compliance::{PdfAValidator, PdfALevel};
let mut doc = PdfDocument::open("report.pdf")?;
let validator = PdfAValidator::new();
// Check only metadata
let result = validator.check_metadata(&mut doc, PdfALevel::A1b)?;
// Check only fonts
let result = validator.check_fonts(&mut doc, PdfALevel::A1b)?;
// Check only color spaces
let result = validator.check_colors(&mut doc, PdfALevel::A1b)?;
// Check only transparency
let result = validator.check_transparency(&mut doc, PdfALevel::A2b)?;
// Check only structure tags
let result = validator.check_structure(&mut doc, PdfALevel::A1a)?;
独立校验器
为获得最大的灵活性,每个校验类别也都以独立函数的形式提供:
use pdf_oxide::PdfDocument;
use pdf_oxide::compliance::validators::*;
use pdf_oxide::compliance::{PdfALevel, ValidationResult};
let mut doc = PdfDocument::open("document.pdf")?;
let mut result = ValidationResult::new(PdfALevel::A1b);
// Run each validator independently
validate_xmp_metadata(&mut doc, PdfALevel::A1b, &mut result)?;
validate_fonts(&mut doc, PdfALevel::A1b, &mut result)?;
validate_colors(&mut doc, PdfALevel::A1b, &mut result)?;
validate_encryption(&mut doc, PdfALevel::A1b, &mut result)?;
validate_transparency(&mut doc, PdfALevel::A1b, &mut result)?;
validate_structure(&mut doc, PdfALevel::A1b, &mut result)?;
validate_javascript(&mut doc, PdfALevel::A1b, &mut result)?;
validate_embedded_files(&mut doc, PdfALevel::A1b, &mut result)?;
validate_annotations(&mut doc, PdfALevel::A1b, &mut result)?;
println!("Total errors: {}", result.errors.len());
校验器一览
| 函数 | 检查内容 |
|---|---|
validate_xmp_metadata() |
XMP 流是否存在、pdfaid:part 和 pdfaid:conformance 条目是否齐全、元数据是否一致 |
validate_fonts() |
所有字体是否嵌入、字形宽度是否存在、是否提供 Unicode 映射(用于级别 “a” 和 “u”) |
validate_colors() |
在没有输出意图的情况下不得使用设备相关的颜色操作符(rg、RG、k、K、g、G) |
validate_encryption() |
PDF/A 文档不允许加密 |
validate_transparency() |
PDF/A-1 不允许透明度;PDF/A-2 及更高版本允许 |
validate_structure() |
存在带有有效角色映射的标记结构树(级别 “a” 必需) |
validate_javascript() |
不存在任何 JavaScript 动作或触发器 |
validate_embedded_files() |
PDF/A-1 和 PDF/A-2 不允许;PDF/A-3 要求每个文件规范上都带有 AFRelationship 键 |
validate_annotations() |
注释类型受相应 ISO 19005 部分的限制 |
ValidationResult
ValidationResult 结构体包含一次校验运行的完整结果:
pub struct ValidationResult {
pub level: PdfALevel,
pub errors: Vec<ComplianceError>,
pub warnings: Vec<ComplianceWarning>,
pub stats: ValidationStats,
}
| 字段 | 类型 | 说明 |
|---|---|---|
level |
PdfALevel |
目标合规级别 |
errors |
Vec<ComplianceError> |
阻碍合规的阻断性违规 |
warnings |
Vec<ComplianceWarning> |
可能影响质量的非阻断性问题 |
stats |
ValidationStats |
已检查的页面、字体和对象数量 |
ComplianceError
pub struct ComplianceError {
pub code: ErrorCode,
pub message: String,
pub location: Option<String>,
pub clause: Option<String>,
}
code 字段使用 ErrorCode 枚举,其类别包括 MissingXmpMetadata、FontNotEmbedded、DeviceDependentColor、EncryptionPresent、TransparencyNotAllowed、MissingStructureTree、JavaScriptPresent 和 InvalidEmbeddedFile。
ComplianceWarning
pub struct ComplianceWarning {
pub code: WarningCode,
pub message: String,
pub location: Option<String>,
}
PDF/A 转换
将不合规的文档向 PDF/A 合规方向转换:
use pdf_oxide::PdfDocument;
use pdf_oxide::compliance::{convert_to_pdf_a, PdfALevel};
let mut doc = PdfDocument::open("input.pdf")?;
let result = convert_to_pdf_a(&mut doc, PdfALevel::A1b)?;
println!("Conversion actions taken:");
for action in &result.actions {
println!(" - {}: {}", action.action_type, action.description);
}
if result.remaining_errors.is_empty() {
println!("Document is now PDF/A-1b compliant");
} else {
println!("{} issues could not be resolved automatically",
result.remaining_errors.len());
}
转换配置
精细调节转换过程:
use pdf_oxide::PdfDocument;
use pdf_oxide::compliance::{PdfAConverter, PdfALevel, ConversionConfig};
let mut doc = PdfDocument::open("input.pdf")?;
let config = ConversionConfig::new()
.embed_fonts(true)
.remove_javascript(true)
.flatten_transparency(true)
.add_structure(true);
let result = PdfAConverter::new(PdfALevel::A2b)
.with_config(config)
.convert(&mut doc)?;
转换器会自动执行以下操作:
- 注入 XMP 元数据 – 添加
pdfaid:part和pdfaid:conformance条目 - 嵌入字体 – 嵌入所有被引用但未嵌入的字体
- 移除 JavaScript – 剥除 JavaScript 动作和触发器
- 拼合透明度 – 将透明元素渲染为不透明(仅限 PDF/A-1)
- ICC 配置文件转换 – 将设备相关的颜色转换为基于 ICC 的颜色空间
- 结构标记 – 添加基本的结构标签(针对级别 “a” 目标)
工作流:校验、修复、再校验
典型的归档工作流是先校验,再尝试自动转换,然后重新校验:
use pdf_oxide::PdfDocument;
use pdf_oxide::compliance::{validate_pdf_a, convert_to_pdf_a, PdfALevel};
let level = PdfALevel::A2b;
let mut doc = PdfDocument::open("input.pdf")?;
// Step 1: Initial validation
let result = validate_pdf_a(&mut doc, level)?;
if !result.has_errors() {
println!("Already compliant");
return Ok(());
}
println!("{} errors found, attempting conversion...", result.errors.len());
// Step 2: Automatic conversion
let conversion = convert_to_pdf_a(&mut doc, level)?;
println!("{} actions taken", conversion.actions.len());
// Step 3: Re-validate
let result = validate_pdf_a(&mut doc, level)?;
if result.has_errors() {
println!("{} errors remain after conversion:", result.errors.len());
for e in &result.errors {
println!(" {} -- {}", e.code, e.message);
}
} else {
println!("Document is now PDF/A-2b compliant");
}
PdfALevel 方法
PdfALevel 枚举包含若干辅助方法,用于查询各级别的能力:
| 方法 | 返回值 | 说明 |
|---|---|---|
part() |
PdfAPart |
ISO 19005 部分(Part1、Part2、Part3) |
conformance() |
char |
合规字母(‘a’、‘b’ 或 ‘u’) |
requires_structure() |
bool |
是否强制要求带标记的结构树 |
requires_unicode() |
bool |
是否强制要求 Unicode 映射 |
allows_transparency() |
bool |
是否允许透明度 |
allows_jpeg2000() |
bool |
是否允许 JPEG 2000 图像 |
allows_embedded_files() |
bool |
是否允许文件附件 |
xmp_part() |
&str |
XMP pdfaid:part 值 |
xmp_conformance() |
&str |
XMP pdfaid:conformance 值 |
from_xmp(part, conformance) |
Option<Self> |
从 XMP 元数据值解析级别 |
后续步骤
- PDF/UA 无障碍 – 无障碍校验
- PDF/X 印刷生产 – 印刷生产校验
- API 参考 – 完整的 Rust API