Skip to content

Валідація PDF/X

PDF/X (ISO 15930) — це стандарт для надійного обміну PDF-файлами, готовими до друку. PDF Oxide валідує всі основні рівні PDF/X, перевіряючи блоки сторінок, колірні простори, прозорість, ICC-профілі та вихідні наміри.

Підтримувані рівні

Level Standard Transparency RGB Layers External ICC External Graphics
X-1a:2001 ISO 15930-1 No No No No No
X-1a:2003 ISO 15930-4 No No No No No
X-3:2002 ISO 15930-3 No Yes No No No
X-3:2003 ISO 15930-6 No Yes No No No
X-4 ISO 15930-7 Yes Yes Yes No No
X-4p ISO 15930-7 Yes Yes Yes Yes No
X-5g ISO 15930-8 Yes Yes Yes No Yes
X-5n ISO 15930-8 Yes Yes Yes No Yes
X-5pg ISO 15930-8 Yes Yes Yes Yes Yes
X-6 ISO 15930-9 Yes Yes Yes No No
X-6n ISO 15930-9 Yes Yes Yes No Yes
X-6p ISO 15930-9 Yes Yes Yes Yes No

PDF/X-1a — найбільш обмежувальний: лише CMYK, без прозорості, без шарів. PDF/X-4 — найбільш поширений сучасний рівень, що дозволяє прозорість та RGB з ICC-профілями.

Швидка валідація

from pdf_oxide import PdfDocument

doc = PdfDocument("document.pdf")
result = doc.validate_pdf_x("4")
print(f"Valid: {result.valid}")
use pdf_oxide::PdfDocument;
use pdf_oxide::compliance::pdf_x::{validate_pdf_x, PdfXLevel};

let mut doc = PdfDocument::open("print-ready.pdf")?;
let result = validate_pdf_x(&mut doc, PdfXLevel::X4)?;

if result.has_errors() {
    println!("Not PDF/X-4 compliant ({} errors):", result.errors.len());
    for error in &result.errors {
        println!("  [{}] {} (clause {})",
            error.code, error.message,
            error.clause.as_deref().unwrap_or("n/a"));
    }
} else {
    println!("Document is PDF/X-4 compliant");
}

API валідатора

Builder PdfXValidator налаштовує запуск валідації:

use pdf_oxide::PdfDocument;
use pdf_oxide::compliance::pdf_x::{PdfXValidator, PdfXLevel};

let mut doc = PdfDocument::open("artwork.pdf")?;

let result = PdfXValidator::new(PdfXLevel::X1a2001)
    .stop_on_first_error(false)
    .include_warnings(true)
    .validate(&mut doc)?;

println!("Errors: {}", result.errors.len());
println!("Warnings: {}", result.warnings.len());
println!("Total issues: {}", result.total_issues());

Що перевіряється

XMP Identification

Валідатор підтверджує, що метадані XMP декларують правильну версію PDF/X:

  • pdfxid:GTS_PDFXVersion must match the target level
  • The declared version is compared against the gts_pdfx_version() for the target level

Сторінка Box Relationships

PDF/X requires specific nesting of page boxes:

TrimBox <= BleedBox <= MediaBox
ArtBox  <= MediaBox

Валідатор перевіряє кожну сторінку, щоб переконатися:

  • TrimBox is present (required by all PDF/X levels)
  • TrimBox is contained within BleedBox (if BleedBox is defined)
  • BleedBox is contained within MediaBox
  • ArtBox is contained within MediaBox (if ArtBox is defined)
  • Tolerance of 0.01 points for floating-point rounding

Transparency Detection

For PDF/X-1a and PDF/X-3, no transparency is permitted. The validator checks:

  • SMask in ExtGState dictionaries (must be /None or absent)
  • CA (stroke opacity) must equal 1.0
  • ca (fill opacity) must equal 1.0
  • BM (blend mode) must be Normal or Compatible

PDF/X-4 and later levels permit transparency.

Color Space Validation

Валідатор перевіряє використання залежних від пристрою кольорів:

  • DeviceRGB is not allowed in PDF/X-1a (CMYK-only)
  • DeviceRGB, DeviceCMYK, and DeviceGray used without an output intent trigger errors in stricter levels
  • Color operators rg, RG, k, K, g, G in page content streams are scanned

ICC Profile Validation

For ICCBased color spaces, the validator checks:

  • The profile stream contains the required /N (number of components) entry
  • The /N value matches the expected color space dimension (1 for gray, 3 for RGB, 4 for CMYK)
  • The profile data is present and non-empty

Output Intent

PDF/X requires an output intent that describes the intended print condition:

  • /OutputIntents array must be present in the document catalog
  • At least one entry with subtype GTS_PDFX is required
  • The output intent should reference an ICC profile or a registered print condition

XValidationResult

pub struct XValidationResult {
    pub level: PdfXLevel,
    pub errors: Vec<XComplianceError>,
    pub warnings: Vec<XComplianceError>,
    pub stats: XValidationStats,
}

XComplianceError

pub struct XComplianceError {
    pub code: XErrorCode,
    pub message: String,
    pub severity: XSeverity,
    pub page: Option<usize>,
    pub object_id: Option<u32>,
    pub clause: Option<String>,
}

Errors include the page number and object ID where the violation was found, making it straightforward to locate and fix issues in the source file.

XErrorCode Categories

Enum XErrorCode містить понад 40 специфічних кодів помилок, організованих за категоріями:

Metadata: MissingOutputIntent, InvalidGtsPdfxVersion, MissingXmpIdentification

Page boxes: MissingTrimBox, TrimBoxOutsideBleedBox, BleedBoxOutsideMediaBox, ArtBoxOutsideMediaBox

Transparency: TransparencyNotAllowed, InvalidBlendMode, InvalidSMask, InvalidOpacity

Color: DeviceRgbNotAllowed, DeviceDependentColorWithoutIntent, InvalidIccProfile, MissingIccComponents

Content: ExternalContentNotAllowed, EncryptionNotAllowed, JavaScriptNotAllowed

Методи PdfXLevel

Метод Повертає Опис
iso_standard() &str ISO standard number (e.g., "ISO 15930-7")
required_pdf_version() &str Minimum Версія PDF (e.g., "1.6")
allows_transparency() bool Whether transparency groups are permitted
allows_rgb() bool Whether RGB-колір space is permitted
allows_layers() bool Whether optional content groups are permitted
allows_external_icc() bool Whether external ICC profiles are permitted
allows_external_graphics() bool Whether external graphics references are permitted
gts_pdfx_version() &str Expected GTS_PDFXVersion value
xmp_version() &str Expected XMP version identifier
from_gts_version(version) Option<Self> Parse level from a GTS version string

Practical Example: Prepress Check

use pdf_oxide::PdfDocument;
use pdf_oxide::compliance::pdf_x::{validate_pdf_x, PdfXLevel, XSeverity};

let mut doc = PdfDocument::open("magazine-cover.pdf")?;
let result = validate_pdf_x(&mut doc, PdfXLevel::X4)?;

println!("=== PDF/X-4 Prepress Report ===");
println!("Status: {}", if result.has_errors() { "REJECT" } else { "ACCEPT" });

// Group errors by severity
let critical: Vec<_> = result.errors.iter()
    .filter(|e| e.is_error())
    .collect();
let advisory: Vec<_> = result.warnings.iter().collect();

if !critical.is_empty() {
    println!("\nCritical ({}):", critical.len());
    for e in &critical {
        let page_str = e.page
            .map(|p| format!("page {}", p + 1))
            .unwrap_or_else(|| "document".into());
        println!("  [{}] {} ({})", e.code, e.message, page_str);
    }
}

if !advisory.is_empty() {
    println!("\nAdvisory ({}):", advisory.len());
    for w in &advisory {
        println!("  [{}] {}", w.code, w.message);
    }
}

Наступні кроки