PDF Library for Delphiは、AddModernImageFromFileとそのストリーム版・文字列版を通じて、AVIF、HEIF、JPEG XLの各画像を入力として受け付け、PDFの画像オブジェクトへ取り込む過程でアルファ、埋め込みICCプロファイル、16ビットチャンネルを保持します。フォーマットの検出は範囲を限定したマジックナンバーの読み取りで行われ、デコードは差し替え可能なバックエンドを通じて実行されるため、実際にはそれらのフォーマットではないファイルに対して外部の何かが呼び出されることはありません
これらのフォーマットは、スマートフォンを通じてドキュメントのワークフローに入り込んできました。iOSは何年も前からデフォルトでHEICを生成しており、Androidの端末はAVIFを生成します。破損部品を撮影する現場の技術者が送ってくる画像は、2015年に作られたPDFレポート生成器ではまったく開けません。汎用のフォールバック経路、つまりプラットフォームのビットマップを介したデコードは、決まって8ビットの色になり、その過程でアルファと色プロファイルを失います
最新の画像経路は、ビットマップ変換が失うものの何を保持するのか?
3つあり、それぞれにそれを頼りにしているワークフローがあります。アルファは保持されます。これは、ページのコンテンツの上に合成されるロゴや製品の切り抜き画像にとって重要です。ICCプロファイルは保持されます。これは、印刷やカラーマッチングを行うあらゆるものにとって重要です。そして16ビットチャンネルは保持されます。これは、8ビットへの量子化が、その画像を撮影した意味そのものである階調を破壊してしまう、医療用・科学用の画像にとって重要です
画像をプラットフォームのビットマップに通すと、これら3つすべてが一度に、しかも静かに失われます。出来上がったPDFはおおむね正しく見えるため、印刷会社から企業のコーポレートレッドがなぜ違うのかと問われるまで、誰も気づきません。最新の画像呼び出しにおけるオプション値8は、アルファ、ICC、16ビットチャンネルをまとめて保持するフラグであり、これらの呼び出しの既定値になっています
ページに追加する
この呼び出しは画像識別子を返し、その後、選択して描画するか、あるいは1回の操作で描画してから解放します:
uses
PDFlibrary, PDFlibModernImage;
var
Lib: TPDFlib;
ImageID: Integer;
begin
Lib := TPDFlib.Create;
try
Lib.NewDocument;
Lib.SetPageSize('A4');
Lib.NewPage;
// Options = 8 でアルファ、ICC、16ビットチャンネルを保持する
ImageID := Lib.AddModernImageFromFile('site-photo.heic', 8);
if ImageID > 0 then
Lib.DrawImageAndRelease(ImageID, 40, 40, 515, 340)
else
Lib.DrawText(40, 40, 'image could not be decoded');
Lib.SaveToFile('inspection-report.pdf');
finally
Lib.Free;
end;
end;
検出はデコードより前に行われ、意図的に範囲が狭く絞られています。ライブラリは、範囲を限定したヘッダーを読み取り、AVIFとHEIFを識別するISOベースメディアファイルフォーマットのブランドを認識し、JPEG XLの生の署名とコンテナ署名の両方を認識したうえで、呼び出し側のストリーム位置を復元します。未知の、あるいは偽装された入力が外部のコーデックに届くことは一切ないため、名前を変えた実行ファイルが、あたかも画像であるかのようにデコーダーへ渡されてしまうことを防ぎます
デコードは実際にどこで行われるのか?
最新の画像フォーマットは大規模で複雑なコーデックであり、それをPDFライブラリの内部に組み込むのは奇妙な設計判断になってしまいます。デフォルトのバックエンドは、配布可能なMagickWandモジュールをプロセス内に動的に読み込み、文書化された順序でそれを探します。すなわち、明示的に設定したファイルやディレクトリ、環境変数、実行ファイルのディレクトリ、システムの検索パスの順です
すでにデコーダーを同梱しているアプリケーションや、外部モジュールを一切読み込んではならないアプリケーションは、代わりに独自のコールバックを登録します。その契約はシンプルです。入力ストリームを読み取り、PNGを出力ストリームに書き込み、要求された向きを尊重する、というだけです:
function MyDecoder(InStream, OutPNG: TStream;
ImageFormat: TPDFlibModernImageFormat;
ApplyOrientation: Boolean): Boolean;
begin
// InStreamを自前のコーデックでデコードし、PNGバイト列をOutPNGに書き込む
Result := DecodeWithBundledCodec(InStream, OutPNG,
ImageFormat, ApplyOrientation);
end;
begin
RegisterModernImageDecoderBackend(MyDecoder);
// ... add images ...
ClearModernImageDecoderBackend; // デフォルトのバックエンドに戻す
end;
デプロイに関しては、1つの利便性と1つの意図的な抑制があります。コーデックのディレクトリにmodules\codersというサブディレクトリが含まれている場合、ライブラリはそのようなレイアウトが必要とするコーデック用の環境変数を設定しますが、それはホストアプリケーションがまだそれらを設定していない場合に限られます。独自のランタイムデプロイ戦略を持つアプリケーションは、その設定を維持できます
なぜ中間にPNGを挟むのか?
生のピクセルバッファではなくメモリ上のPNGを橋渡し役として使うのは、余計な手間のように見えますが、実際には最も安く済む正しいやり方です。PNGは、生き残らせなければならないもの——アルファ、カラータイプ、ビット深度、埋め込みICCプロファイル——をすべて表現でき、ライブラリにはすでに、適切なフィルタとカラースペースを備えた、PNGからPDF画像オブジェクトへの成熟した十分にテスト済みの経路があります。これを再利用することで、最新のフォーマットは、並行した別実装を持つのではなく、長年積み重ねられてきた正確性の成果を引き継ぐことになります
この橋渡しはすべてメモリ上で行われるため、一時ファイルは作成されず、クラッシュ時のクリーンアップも不要です。1つだけ明示的な対応が必要な細部がありました。フォーマットを変換する際に、一部の変換処理はICCプロファイルを落としてしまうのです。そのためバックエンドは、フォーマットの切り替えの前にソースのプロファイルを取得し、それをFlateで圧縮し、CRCを再計算した有効なiCCPチャンクを構築し、それと衝突し得るsRGBチャンクがあれば取り除きます。テストでは、デコードされたAVIFは16ビットのアルファを含む16ビットRGBAを保ち、出来上がったPDFから抽出したプロファイルは、60,960バイトのソースプロファイルとバイト単位で完全に一致しました
本番環境で有効にする前の実務上の注意点
最初の写真が届いたときではなく、起動時に利用可能かどうかを確認してください。ModernImageCodecAvailableはバックエンドが使用可能かどうかを報告し、デプロイ環境がコーデックを標準的でない場所に置いている場合には、SetModernImageCodecLibraryで明示的なファイルまたはディレクトリを指定します:
Lib.SetModernImageCodecLibrary('C:\MyApp\codecs');
if Lib.ModernImageCodecAvailable = 0 then
Log('modern image input unavailable - HEIC and AVIF will be refused');
結果のファイルサイズにも注意してください。埋め込みプロファイル付きの16ビットRGBA画像は大きなPDF画像オブジェクトになり、それが40枚含まれるレポートは大きなファイルになります。ドキュメントが印刷ではなく画面表示を目的としている場合は、埋め込む前にダウンサンプリングするのが適切なトレードオフです。サイズを調整する一般的な手段についてはPDFファイルサイズの最適化で説明しています
最後に、色に関する方針は意図的に決めてください。ソースのプロファイルを保持することは、アーカイブ用途や印刷用途では正しい選択です。混在した写真の見た目を一貫させる必要がある場合は、ドキュメント全体の色空間へ変換する方が正しく、その変換の方法はドキュメントを別の色空間へ再着色するで説明しています。実際にファイルに何が収まったかを確認したい場合は、テキスト・画像・フォントの抽出で説明した検査経路が、ドキュメントが持つ画像オブジェクトを報告します
最新の画像入力、色管理、画像の最適化は、いずれもDelphi、C++Builder、Free Pascal向けの同じライブラリの一部です。完全な機能一覧はPDF Library for Delphiページに掲載されています