WordやExcelスタイルのグラデーション背景をヘッダーに持つPDFを画像にレンダリングすると、以前は色がブレンドされるべき領域が真っ白な長方形で出力されていました。現在のHotPDFのページレンダラーは、Type 2(線形)およびType 3(放射状)グラデーションに対するPDFのsh(シェーディング)演算子を処理できるようになりました。グラデーション軸に沿ってシェーディングの関数を評価し、グラデーションを描画します。旧バージョンでは、この演算子が暗黙的に無視され、該当領域は用紙の白地のままになっていました。ほぼすべてのオフィス文書のグラデーションをカバーするこれら2つのシェーディングタイプがサポート対象となりましたが、メッシュタイプは非対応です。この記事では、その具体的な境界線について明確に解説します
PDFグラデーションが白いボックスとしてレンダリングされるのはなぜですか?
PDFのグラデーションはベクター構造であり、レンダラーがそのままCanvasに転送できるビットマップデータではないからです。シェーディングディクショナリ(ISO 32000-1 §8.7.4.5)は、形状情報(Coords配列で定義される軸または2つの円)とカラー関数を保持し、sh演算子が現在のクリップ領域全体にそのシェーディングを描画します。これは、HotPDFでページキャンバスにパスや塗りつぶしを描画する際に公開されるグラフィックスモデルと同じです。HotPDFのページレンダラーは、コンテンツストリームを演算子ごとに走査し、対応するコードパスが存在しない演算子はスキップしてページの残りの部分をレンダリングします。バージョン2.335.0より前は、sh演算子がそのスキップ対象に含まれていたため、ヘッダーやページ全体のグラデーションが出力に反映されず、背景が白く透過していました。ファイルが破損していたわけではありません。レンダラーにその演算子を処理するハンドラーがなく、ハンドラーが欠落していると該当領域が真っ白なボックスになっていたというだけのことです
シェーディングの背後にある関数評価チェーン
関数の評価が完了しないとシェーディングは描画できないため、この仕組みをまず実装する必要がありました。Type 2(線形)およびType 3(放射状)シェーディングはどちらも、グラデーションに沿ったパラメータ位置を色にマッピングする/Function(ISO 32000-1 §7.10)を保持しており、HotPDFの関数評価エンジンがFunctionTypeを解決して処理を振り分けます。Type 2(指数補間、§7.10.3)は、DomainおよびRangeで制限しつつC0 + t^N * (C1 - C0)を計算します。Type 0(サンプル値定義、§7.10.2)は、8、16、または32ビットのサンプルグリッドを読み取り、Encodeマップを介して最も近い値を選択します。これについては、Type 0サンプルカラー参照の詳細解説を参照してください。Type 3(連結、§7.10.4)は、Bounds配列によってサブ関数を選択して再帰処理します。Type 4(PostScript計算式、§7.10.5)は評価されません。レンダリングパス全体にわたるHotPDFの安全第一の基本方針に基づき、誤った色を出力するのではなく、評価失敗として処理されます。この評価エンジンは、シェーディング描画の品質が背後の関数処理に完全に依存するため、演算子自体の実装に先立ちバージョン2.334.0で導入されました
HotPDFが連続的なグラデーションをピクセルデータに変換する仕組み
HotPDFは、連続的なグラデーションを有限の数の単色カラーバンドで近似します。シェーディング描画処理はグラデーションの軸に沿って走査し、それを最大64のバンドに分割(軸のデバイスピクセルが短い場合はさらに少なく)し、各バンドのパラメータ位置で関数を評価し、軸に対して垂直な長方形をソリッドブラシで塗りつぶします。これはバンド数と滑らかさのトレードオフです。バンド数が多いほどグラデーションは滑らかになりますが、塗りつぶし処理のコストが増加するため、最大値を64に制限しています。バンド数は軸のデバイスピクセル長に応じてスケールするため、解像度が重要になります。同じページを高DPIでレンダリングすると軸が長くなるため、短いグラデーションでもバンド数が増加し、最大値に達するまで境界線がより滑らかになります
// The shading discretizer uses up to 64 color bands along the gradient
// axis, but never more than the axis is long in device pixels. Rendering
// the same page at a higher DPI lengthens that axis, so a short gradient
// gains bands and the banding softens until the 64-band ceiling is hit.
Thumb := Pdf.RenderLoadedPageToBitmap(PageIndex, 96); // coarser bands
Proof := Pdf.RenderLoadedPageToBitmap(PageIndex, 300); // up to 64 bands
シェーディングされたページのビットマップへのレンダリング
公開されているエントリポイントは変更されていません。RenderLoadedPageToBitmapは0ベースのページインデックスとDPI値を受け取り、呼び出し側が所有権を持つ24ビットビットマップを返します。処理失敗時には例外を発生させるのではなくnilを返すため、既存のコードに一切の影響を与えることなくグラデーション表示をサポートできます。以前はグラデーションヘッダーが白く抜けていたページが、APIの変更なしに同じメソッドからグラデーションが正しく描画されたビットマップとして返されます。注意すべき点はメモリ管理のみであり、取得したビットマップは呼び出し側で解放する必要があります。コンテンツストリームの解析、埋め込みグリフ処理、およびページキャッシュを含むレンダリングパイプライン全体については、DelphiでのPDFページのビットマップへのレンダリングガイドで解説しています
var
Pdf: THotPDF;
Bmp: TBitmap;
begin
Pdf := THotPDF.Create(nil);
try
if Pdf.LoadFromFile('quarterly-report.pdf') > 0 then
begin
// Page 1 (zero-based) at 150 DPI. A gradient-filled header that
// used to come back blank now carries its color blend.
Bmp := Pdf.RenderLoadedPageToBitmap(0, 150);
if Assigned(Bmp) then
try
Bmp.SaveToFile('page1.bmp');
finally
Bmp.Free;
end;
end;
finally
Pdf.Free;
end;
end;
安価な回帰テストガードは、同じメソッド呼び出しで行うことができます。ページをレンダリングし、グラデーションの境界バンドが存在するピクセルをサンプリングし、それが以前の用紙の白色ではないことを確認します。これにより、暗黙的に以前の無効化処理に戻るようなバグを1行の検証で検出できます
Bmp := Pdf.RenderLoadedPageToBitmap(0, 150);
if Assigned(Bmp) then
try
// True when the sh operator painted; False if the region fell back to white.
HeaderPainted := Bmp.Canvas.Pixels[Bmp.Width div 2, 8] <> clWhite;
finally
Bmp.Free;
end;
HotPDFが描画しないもの:メッシュシェーディングとクリップ領域の境界
4つのメッシュシェーディングタイプは非サポートであり、文書を扱う前にその制限を把握しておく必要があります。ISO 32000-1 §8.7.4.5.5から§8.7.4.5.8で定義されている、フリーフォームGouraud三角メッシュ(Type 4)、格子型Gouraudメッシュ(Type 5)、Coonsパッチメッシュ(Type 6)、およびテンソル積パッチメッシュ(Type 7)はレンダリングされません。これらを使用しているページでも、レンダリング自体は続行されますが、該当領域はそのまま残ります。さらに、2つの仕様について明確にしておく価値があります。カラーバンドの長方形領域は、現在のクリップパスに沿って精密にクリップされるのではなく、シェーディングの境界帯全体を覆うため、丸型や不規則な形状でマスクされたグラデーションは、今後のアップデートで精密な領域クリッピングが実装されるまでは、外接する長方形として描画されます。また、HotPDFは関数の出力がRGBまたはグレースケールであると想定しているため、CMYKや特殊なカラースペースで定義されたシェーディングは、厳密なカラーマネジメントではなく近似色として処理されます。DelphiやC++Builderの開発チームが実際に処理するほとんどの文書(レポート、請求書、明細書、PDFにエクスポートされたスライド資料など)においては、線形および放射状の2つでほぼすべてのグラデーションをカバーできるため、メッシュを多用するデザイン校正用などのPDFは、最終成果物ではなくプレビュー用として扱ってください