Skip to content

Producción de Imprenta PDF/X

PDF/X (ISO 15930) es el estándar para el intercambio fiable de archivos PDF listos para imprenta. PDF Oxide valida todos los niveles principales de PDF/X, comprobando las cajas de página, los espacios de color, la transparencia, los perfiles ICC y las intenciones de salida.

Cobertura en los bindings. La validación PDF/X está disponible en Python (doc.validate_pdf_x(level)), Rust (validate_pdf_x + el builder PdfXValidator) y Go (doc.ValidatePdfX(level)). WASM y C# todavía no exponen la validación PDF/X: utiliza la CLI de Rust (pdf-oxide validate --pdfx 4 doc.pdf) o llámala a través de uno de los bindings compatibles.

Niveles Compatibles

Nivel Estándar Transparencia RGB Capas ICC externo Gráficos externos
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 No No No
X-3:2003 ISO 15930-6 No No No No
X-4 ISO 15930-7 No No
X-4p ISO 15930-7 No
X-5g ISO 15930-8 No
X-5n ISO 15930-8 No
X-5pg ISO 15930-8
X-6 ISO 15930-9 No No
X-6n ISO 15930-9 No
X-6p ISO 15930-9 No

PDF/X-1a es el más restrictivo: solo CMYK, sin transparencia y sin capas. PDF/X-4 es el nivel moderno más utilizado, ya que permite 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");
}
doc, _ := pdfoxide.Open("print-ready.pdf")
defer doc.Close()

// Level encoding: 0 = X-1a:2001, 1 = X-1a:2003, 2 = X-3:2002, 3 = X-3:2003, 4 = X-4, ...
valid, errs, err := doc.ValidatePdfX(4) // PDF/X-4
if err != nil { log.Fatal(err) }

if valid {
    fmt.Println("Document is PDF/X-4 compliant")
} else {
    fmt.Printf("Not PDF/X-4 compliant (%d errors):\n", len(errs))
    for _, e := range errs {
        fmt.Printf("  %s\n", e)
    }
}

API del Validador

El builder PdfXValidator configura la ejecución de la 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 Comprueba

Identificación XMP

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

  • pdfxid:GTS_PDFXVersion debe coincidir con el nivel de destino
  • La versión declarada se compara con el gts_pdfx_version() del nivel de destino

Relaciones entre Cajas de Página

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

TrimBox <= BleedBox <= MediaBox
ArtBox  <= MediaBox

El validador comprueba cada página para garantizar que:

  • La TrimBox está presente (requerida por todos los niveles de PDF/X)
  • La TrimBox está contenida dentro de la BleedBox (si la BleedBox está definida)
  • La BleedBox está contenida dentro de la MediaBox
  • La ArtBox está contenida dentro de la MediaBox (si la ArtBox está definida)
  • Una tolerancia de 0,01 puntos para el redondeo de coma flotante

Detección de Transparencia

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

  • SMask en los diccionarios ExtGState (debe ser /None o estar ausente)
  • CA (opacidad del trazo) debe ser igual a 1.0
  • ca (opacidad del relleno) debe ser igual a 1.0
  • BM (modo de fusión) debe ser Normal o Compatible

PDF/X-4 y los niveles posteriores permiten la transparencia.

Validación del Espacio de Color

El validador comprueba el uso de colores dependientes del dispositivo:

  • DeviceRGB no está permitido en PDF/X-1a (solo CMYK)
  • DeviceRGB, DeviceCMYK y DeviceGray usados sin una intención de salida generan errores en los niveles más estrictos
  • Se analizan los operadores de color rg, RG, k, K, g, G en los flujos de contenido de las páginas

Validación del Perfil ICC

Para los espacios de color ICCBased, el validador comprueba:

  • El flujo del perfil contiene la entrada obligatoria /N (número de componentes)
  • El valor de /N coincide con la dimensión esperada del espacio de color (1 para gris, 3 para RGB, 4 para CMYK)
  • Los datos del perfil están presentes y no están vacíos

Intención de Salida

PDF/X exige una intención de salida que describa la condición de impresión prevista:

  • El array /OutputIntents debe estar presente en el catálogo del documento
  • Se requiere al menos una entrada con el subtipo GTS_PDFX
  • La intención de salida debe hacer referencia a un perfil ICC o a una condición de impresión registrada

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 de objeto donde se encontró la infracción, lo que facilita localizar y corregir los problemas en el archivo de origen.

Categorías de XErrorCode

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

Metadatos: MissingOutputIntent, InvalidGtsPdfxVersion, MissingXmpIdentification

Cajas de página: MissingTrimBox, TrimBoxOutsideBleedBox, BleedBoxOutsideMediaBox, ArtBoxOutsideMediaBox

Transparencia: TransparencyNotAllowed, InvalidBlendMode, InvalidSMask, InvalidOpacity

Color: DeviceRgbNotAllowed, DeviceDependentColorWithoutIntent, InvalidIccProfile, MissingIccComponents

Contenido: ExternalContentNotAllowed, EncryptionNotAllowed, JavaScriptNotAllowed

Métodos de PdfXLevel

Método Retorno Descripción
iso_standard() &str Número de estándar ISO (por ejemplo, "ISO 15930-7")
required_pdf_version() &str Versión mínima de PDF (por ejemplo, "1.6")
allows_transparency() bool Si se permiten los grupos de transparencia
allows_rgb() bool Si se permite el espacio de color RGB
allows_layers() bool Si se permiten los grupos de contenido opcional
allows_external_icc() bool Si se permiten los perfiles ICC externos
allows_external_graphics() bool Si se permiten las referencias a gráficos externos
gts_pdfx_version() &str Valor esperado de GTS_PDFXVersion
xmp_version() &str Identificador de versión XMP esperado
from_gts_version(version) Option<Self> Analiza el nivel a partir de una cadena de versión GTS

Ejemplo Práctico: Comprobación de Preimpresión

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