Skip to content

PDF/X 印刷制作

PDF/X(ISO 15930)は、印刷可能な PDF ファイルを確実に交換するための標準規格です。PDF Oxide はすべての主要な PDF/X レベルを検証し、ページボックス、カラースペース、透明、ICC プロファイル、出力インテントをチェックします。

バインディングの対応状況。 PDF/X 検証は Python(doc.validate_pdf_x(level))、Rust(validate_pdf_x + PdfXValidator ビルダー)、Go(doc.ValidatePdfX(level))で利用できます。WASM と C# はまだ PDF/X 検証を公開していません。Rust CLI(pdf-oxide validate --pdfx 4 doc.pdf)を使うか、対応しているバインディングのいずれかを介して呼び出してください。

対応レベル

レベル 標準規格 透明 RGB レイヤー 外部 ICC 外部グラフィック
X-1a:2001 ISO 15930-1 不可 不可 不可 不可 不可
X-1a:2003 ISO 15930-4 不可 不可 不可 不可 不可
X-3:2002 ISO 15930-3 不可 不可 不可 不可
X-3:2003 ISO 15930-6 不可 不可 不可 不可
X-4 ISO 15930-7 不可 不可
X-4p ISO 15930-7 不可
X-5g ISO 15930-8 不可
X-5n ISO 15930-8 不可
X-5pg ISO 15930-8
X-6 ISO 15930-9 不可 不可
X-6n ISO 15930-9 不可
X-6p ISO 15930-9 不可

PDF/X-1a は最も制限が厳しく、CMYK のみで透明やレイヤーは使えません。PDF/X-4 は最も一般的に使われる現代的なレベルで、ICC プロファイルを伴う透明と RGB を許可します。

クイック検証

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

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 識別情報

バリデーターは、XMP メタデータが正しい PDF/X バージョンを宣言していることを確認します。

  • pdfxid:GTS_PDFXVersion が対象レベルと一致する必要があります
  • 宣言されたバージョンは、対象レベルの gts_pdfx_version() と比較されます

ページボックスの関係

PDF/X はページボックスの特定の入れ子構造を要求します。

TrimBox <= BleedBox <= MediaBox
ArtBox  <= MediaBox

バリデーターはすべてのページを次の点についてチェックします。

  • TrimBox が存在すること(すべての PDF/X レベルで必須)
  • TrimBox が BleedBox 内に収まっていること(BleedBox が定義されている場合)
  • BleedBox が MediaBox 内に収まっていること
  • ArtBox が MediaBox 内に収まっていること(ArtBox が定義されている場合)
  • 浮動小数点丸めのための 0.01 ポイントの許容誤差

透明の検出

PDF/X-1a と PDF/X-3 では透明は許可されません。バリデーターは次をチェックします。

  • ExtGState 辞書内の SMask(/None であるか、存在しないこと)
  • CA(線の不透明度)が 1.0 であること
  • ca(塗りの不透明度)が 1.0 であること
  • BM(ブレンドモード)が Normal または Compatible であること

PDF/X-4 以降のレベルは透明を許可します。

カラースペースの検証

バリデーターはデバイス依存色の使用をチェックします。

  • DeviceRGB は PDF/X-1a では許可されません(CMYK のみ)
  • 出力インテントなしで使用された DeviceRGBDeviceCMYKDeviceGray は、より厳格なレベルでエラーを引き起こします
  • ページのコンテンツストリーム内のカラー演算子 rgRGkKgG がスキャンされます

ICC プロファイルの検証

ICCBased カラースペースについて、バリデーターは次をチェックします。

  • プロファイルストリームに必須の /N(コンポーネント数)エントリが含まれていること
  • /N の値が期待されるカラースペースの次元と一致すること(グレーは 1、RGB は 3、CMYK は 4)
  • プロファイルデータが存在し、空でないこと

出力インテント

PDF/X は、意図された印刷条件を記述する出力インテントを要求します。

  • ドキュメントカタログに /OutputIntents 配列が存在する必要があります
  • サブタイプ GTS_PDFX を持つエントリが少なくとも 1 つ必要です
  • 出力インテントは ICC プロファイルまたは登録済みの印刷条件を参照する必要があります

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

エラーには違反が見つかったページ番号とオブジェクト ID が含まれるため、ソースファイル内の問題箇所を簡単に特定して修正できます。

XErrorCode のカテゴリー

XErrorCode 列挙型には、カテゴリー別に整理された 40 以上の具体的なエラーコードが含まれます。

メタデータ: MissingOutputIntent, InvalidGtsPdfxVersion, MissingXmpIdentification

ページボックス: MissingTrimBox, TrimBoxOutsideBleedBox, BleedBoxOutsideMediaBox, ArtBoxOutsideMediaBox

透明: TransparencyNotAllowed, InvalidBlendMode, InvalidSMask, InvalidOpacity

カラー: DeviceRgbNotAllowed, DeviceDependentColorWithoutIntent, InvalidIccProfile, MissingIccComponents

コンテンツ: ExternalContentNotAllowed, EncryptionNotAllowed, JavaScriptNotAllowed

PdfXLevel のメソッド

メソッド 戻り値 説明
iso_standard() &str ISO 標準規格番号(例: "ISO 15930-7")
required_pdf_version() &str 最小 PDF バージョン(例: "1.6")
allows_transparency() bool 透明グループが許可されるかどうか
allows_rgb() bool RGB カラースペースが許可されるかどうか
allows_layers() bool オプショナルコンテンツグループが許可されるかどうか
allows_external_icc() bool 外部 ICC プロファイルが許可されるかどうか
allows_external_graphics() bool 外部グラフィック参照が許可されるかどうか
gts_pdfx_version() &str 期待される GTS_PDFXVersion の値
xmp_version() &str 期待される XMP バージョン識別子
from_gts_version(version) Option<Self> GTS バージョン文字列からレベルを解析

実践例: プリプレスチェック

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

次のステップ