Skip to content

Validación PDF/X

PDF/X (ISO 15930) es el estándar para el intercambio confiable de archivos PDF listos para impresión. PDF Oxide valida todos los niveles principales de PDF/X, verificando cajas de página, espacios de color, transparencia, perfiles ICC e intents de salida.

Niveles soportados

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 es el más restrictivo: solo CMYK, sin transparencia, sin capas. PDF/X-4 es el nivel moderno más utilizado, permitiendo transparencia y RGB con perfiles ICC.

Validación rápida

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 del validador

El builder PdfXValidator configura la ejecución de validación:

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

Qué se verifica

Identificación XMP

El validador confirma que los metadatos XMP declaran la versión PDF/X correcta:

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

Relaciones de cajas de página

PDF/X requiere un anidamiento específico de cajas de página:

TrimBox <= BleedBox <= MediaBox
ArtBox  <= MediaBox

El validador verifica cada página para asegurar que:

  • 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

Detección de transparencia

Para PDF/X-1a y PDF/X-3, no se permite transparencia. El validador verifica:

  • 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 y niveles posteriores permiten transparencia.

Validación de espacios de color

El validador verifica el uso de color dependiente del dispositivo:

  • 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

Validación de perfiles ICC

Para espacios de color ICCBased, el validador verifica:

  • 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

Intent de salida

PDF/X requiere un intent de salida que describa la condición de impresión prevista:

  • /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>,
}

Los errores incluyen el número de página y el ID del objeto donde se encontró la violación, facilitando localizar y corregir problemas en el archivo fuente.

Categorías de XErrorCode

El enum XErrorCode contiene más de 40 códigos de error específicos organizados por categoría:

Metadata: MissingOutputIntent, InvalidGtsPdfxVersion, MissingXmpIdentification

Page boxes: MissingTrimBox, TrimBoxOutsideBleedBox, BleedBoxOutsideMediaBox, ArtBoxOutsideMediaBox

Transparency: TransparencyNotAllowed, InvalidBlendMode, InvalidSMask, InvalidOpacity

Color: DeviceRgbNotAllowed, DeviceDependentColorWithoutIntent, InvalidIccProfile, MissingIccComponents

Content: ExternalContentNotAllowed, EncryptionNotAllowed, JavaScriptNotAllowed

Métodos de PdfXLevel

Method Return Descripción
iso_standard() &str ISO standard number (e.g., "ISO 15930-7")
required_pdf_version() &str Minimum PDF version (e.g., "1.6")
allows_transparency() bool Si los grupos de transparencia están permitidos
allows_rgb() bool Si el espacio de color RGB está permitido
allows_layers() bool Si los grupos de contenido opcional están permitidos
allows_external_icc() bool Si los perfiles ICC externos están permitidos
allows_external_graphics() bool Si las referencias de gráficos externos están permitidas
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

Ejemplo práctico: verificación preprensa

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

Próximos pasos