Skip to content

PDF/A 校验

PDF/A(ISO 19005)是电子文档长期归档的国际标准。PDF Oxide 可校验所有主要的 PDF/A 级别,并能将不合规的文档向合规方向转换。

绑定支持情况。 PDF/A 校验已在 Pythondoc.validate_pdf_a(level))、Rustvalidate_pdf_a + PdfAValidator 构建器)、WASMdoc.validatePdfA(level))和 Godoc.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:partpdfaid:conformance 条目是否齐全、元数据是否一致
validate_fonts() 所有字体是否嵌入、字形宽度是否存在、是否提供 Unicode 映射(用于级别 “a” 和 “u”)
validate_colors() 在没有输出意图的情况下不得使用设备相关的颜色操作符(rgRGkKgG
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 枚举,其类别包括 MissingXmpMetadataFontNotEmbeddedDeviceDependentColorEncryptionPresentTransparencyNotAllowedMissingStructureTreeJavaScriptPresentInvalidEmbeddedFile

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)?;

转换器会自动执行以下操作:

  1. 注入 XMP 元数据 – 添加 pdfaid:partpdfaid:conformance 条目
  2. 嵌入字体 – 嵌入所有被引用但未嵌入的字体
  3. 移除 JavaScript – 剥除 JavaScript 动作和触发器
  4. 拼合透明度 – 将透明元素渲染为不透明(仅限 PDF/A-1)
  5. ICC 配置文件转换 – 将设备相关的颜色转换为基于 ICC 的颜色空间
  6. 结构标记 – 添加基本的结构标签(针对级别 “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 元数据值解析级别

后续步骤