PDF/X Print Production
PDF/X (ISO 15930) is the standard for reliable exchange of print-ready PDF files. PDF Oxide validates all major PDF/X levels, checking page boxes, color spaces, transparency, ICC profiles, and output intents.
Binding coverage. PDF/X validation is exposed in Python (
doc.validate_pdf_x(level)), Rust (validate_pdf_x+PdfXValidatorbuilder), and Go (doc.ValidatePdfX(level)). WASM and C# do not yet surface PDF/X validation — use the Rust CLI (pdf-oxide validate --pdfx 4 doc.pdf) or invoke through one of the supported bindings.
Supported Levels
| 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 is the most restrictive: CMYK-only, no transparency, no layers. PDF/X-4 is the most commonly used modern level, allowing transparency and RGB with ICC profiles.
Quick Validation
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)
}
}
Validator API
The PdfXValidator builder configures the validation run:
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());
What Gets Checked
XMP Identification
The validator confirms that XMP metadata declares the correct PDF/X version:
pdfxid:GTS_PDFXVersionmust match the target level- The declared version is compared against the
gts_pdfx_version()for the target level
Page Box Relationships
PDF/X requires specific nesting of page boxes:
TrimBox <= BleedBox <= MediaBox
ArtBox <= MediaBox
The validator checks every page to ensure:
- 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
/Noneor absent) - CA (stroke opacity) must equal 1.0
- ca (fill opacity) must equal 1.0
- BM (blend mode) must be
NormalorCompatible
PDF/X-4 and later levels permit transparency.
Color Space Validation
The validator checks for device-dependent color usage:
- 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,Gin 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
/Nvalue 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:
/OutputIntentsarray must be present in the document catalog- At least one entry with subtype
GTS_PDFXis 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
The XErrorCode enum contains 40+ specific error codes organized by category:
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 Methods
| Method | Return | Description |
|---|---|---|
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 |
Whether transparency groups are permitted |
allows_rgb() |
bool |
Whether RGB color 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);
}
}
Next Steps
- PDF/A Validation – archival compliance
- PDF/UA Accessibility – accessibility validation
- API Reference – complete Rust API