Проверка 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) без output intent |
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