技術記事

DelphiでPDFを一つのカラースペースに変換する方法

RecolorDocumentはPDF全体を一つのカラースペースに移し、RecolorPageは単一ページに対して同じことをする。両方とも1でグレイ、2でRGB、3でCMYKを受け取り、何色が変換されたかを返す。ジオメトリ、テキスト、画像は正確に元の場所に留まり、変わるのは色だけだ

これが欲しくなるのは、単一スペースを期待するプレスに送る前や、カラードキュメントを再生成せずにグレイに変えなければならない場面だ。対抗馬である「ソースデータから対象スペースでドキュメントを再構築する」方法は、ソースデータがまだ手元にある場合にしか選べない。外部から受け取ったファイルでは、ソースデータは手元にない

何が変換され、どんな順序か

カラーオペレータが最初だ。ページが描画中に設定する塗りと線の色である。対象スペースにすでにある色はそのまま残され、変更の必要がない色を持つページは、同一の値に書き直されるよりも元のバイト列を保持する。この性質は巨大なドキュメントでは見かけ以上に重要だ。触れられなかったオブジェクトはファイル内でも触れられないままだからだ

次に画像が来る。かつてこれが欠けていた部分だ。オペレータだけを変換してグレイにしたドキュメントは、グレイのページの上にカラーの写真を表示し続けた。両極端のどちらより悪い状態だ。画像は、コンポーネントあたり8ビットで、デバイスカラースペースを持ち、圧縮がないか単純なFlateのいずれかである場合に変換される

インデックス画像はカラーテーブルを経由して変換される。パレットが変換され、各サンプルはすでに持っていたインデックスを保つ。スクリーンショットやチャートが通常保存されるパレット画像をカバーし、サンプルがどのようにパック、圧縮されていても変換する。サンプル自体は変わらないからだ

var
  Lib: TPDFlib;
  Converted: Integer;
begin
  Lib := TPDFlib.Create;
  try
    Lib.LoadFromFile('brochure.pdf', '');
    Converted := Lib.RecolorDocument(1);       // 1 grey, 2 RGB, 3 CMYK
    Log(Format('%d colours converted', [Converted]));
    Lib.SaveToFile('brochure-grey.pdf');
  finally
    Lib.Free;
  end;
end;

なぜJPEGはそのまま放置されるのか

色を変換することが再エンコードを意味し、再エンコードが異なる結果をもたらす異なる操作だからだ。JPEGやファクシミリ画像は自身のバイト列を保つ。デコード、変換、再圧縮は画像データそのものを変え、第二世代の非可逆アーティファクトを持ち込み、呼び出し側が求めていない形でファイルサイズを変えてしまう

ソフトマスクとステンシルマスクは別の理由で放置される。どちらも色としてではなくカバレッジとして読まれ、変換すればページがマスクを絵として表示してしまう。透明マスクが、本来整形する対象だったコンテンツの上にグレイのアートワークとして重ねて描かれる状態だ。放置することは回避すべき制限ではなく、正しい結果だ

ICCBased、Separation、Indexed、Patternスペースを経由して設定された色も、オペレータレベルではそのまま残る。プロファイルを読まずにこれらを変換すれば、見た目を保つのではなく変えることになる。保存を全体の約束とする再彩色パスは、プロファイルを推測し始めるわけにはいかない

グラデーションは背後のストップと一緒に変換される

グラデーションは「カラースペース」と「その中で色を生み出す関数」の組合せであり、片方だけを変換すればナンセンスな結果になる。ストップを取り残して変換されたグラデーションは、チャネルをグレイレベルと勘違いするように読まれ、ページ上ではまったく色の違う帯に見える。グラデーションのカラースペースと背後のストップは一緒に移動する

グラデーションは住む場所を問わず見つかる。独自のオブジェクトとして与えられていても、それを塗るパターンの中に書かれていても、ページのシェーディングリソースに置かれていてもだ。この網羅性は重要だ。デザインツールによって配置場所が異なり、一つの配置しか扱わないコンバーターは、失敗のパターンが読めないままドキュメントを半分だけ変換してしまう

2種類は意図的にそのまま残される。サンプリング関数やPostScript関数で作られたグラデーションは自身の定義を保ち、メッシュ型も同じだ。メッシュ型は関数ではなくストリームの中に色を保持する。どちらの書き換えも色の変換とは別の操作であり、再彩色パスの中で黙って行えば驚きを生む

// Convert a single page - useful when only the cover needs to change
Lib.SelectPage(1);
Lib.RecolorPage(3);          // this page to CMYK
Lib.SaveToFile('cover-cmyk.pdf');

プレス提出前に再彩色が当てはまる場所

カラーマネジメントではなく、正規化ステップとして使う。再彩色はデバイススペース間で値を変換するが、ICCプロファイルを適用せず、レンダリングインテントを優先せず、プレス条件をシミュレートしない。契約プルーフに合わせなければならないジョブでは、プロファイル駆動のワークフローが答えであり、再彩色はせいぜい、まずすべてを1つのスペースに揃えるためのステップにすぎない

再彩色が本領を発揮するのは、日常の生産現場の問題だ。グレイでなければならない提出物にRGBのロゴが混ざっている、カラーで印刷されてはいけない社内ドキュメント、プレフライトステップが受け付ける前に1つのスペースに揃えなければならないファイル群などだ。チェックと組み合わせる。PDF/AとPDF/UAのプレフライトコンプライアンスと署名ワークベンチのノートは、同じジョブの中で評定を実行する方法を扱っている

ドキュメントが変換ではなくベクターアートワークの精査も必要とするなら、ベクターグラフィックス、シェーダー、グラデーションの解説がそもそもそれらのオブジェクトがどう構築されるかを扱っており、再彩色パスがどこまで届き、どこまで届かないかを理解する上で有用な文脈になる

PDFlibPasはDelphi、C++Builder、Lazarus向けのネイティブPascal PDFライブラリであり、外部ランタイムを持たない。だから色の正規化ステップは、コンバーターをシェル呼び出しするのではなく自社サービスの中で動く。完全なドキュメント処理APIについてはPDFlibPas製品ページを参照のこと