成果物が文書ではなく、テーブルの画像であることがあります。ステータスメールのサマリーブロック、ダッシュボードに描かれた KPI パネル、検索結果の横のサムネイル。すべてがセルを求め、誰も紙を求めません。HotXLS の TXLSCellImageExporter は、クラシックまたは XLSX のセル矩形を受け取り、ページサイズも余白もヘッダーやフッターも印刷タイトルもページ区切りもない、1 枚のコンパクトな PNG または JPEG を生み出します。解像度、スケール、フォーマット、JPEG 品質は設定可能であり、オブジェクト、グリッド線、セル罫線には独立したスイッチがあり、背景は色でも透過でもかまいません。そしてファイル書き込みは、何かが失敗したときに既存のターゲットを無傷のまま残す、アトミックな同フォルダ置換を通ります
印刷経路のフラグではなく独自のエクスポーターが必要な理由は、ページ分割がオフにできるオプションのレイヤーではないからです。ページパイプラインはページ分割をするために存在します
範囲を印刷パイプライン経由でレンダリングしないのはなぜか
印刷パイプラインは、あなたとセルの間にページを挟み込むからです。用紙サイズは収まる量を決め、余白はコンテンツを内側へ押しやり、ヘッダーとフッターは頼んでいない帯を占有し、印刷タイトルはすでに持っている行を繰り返し、ページ区切りは範囲を分裂させます。たまたま区切りに跨ったサマリーブロックは、重要な行が半分に切られた 2 枚の画像として出てきます。範囲に正確に一致するカスタムページサイズを設定すれば、これらすべてを補償できます。実際にそうする人もいます。しかしそれは、範囲が変わるたびに用紙ジオメトリを計算し直すことを意味し、それでもヘッダー帯と印刷タイトルのロジックが経路に残ります
セルエクスポーターは矩形を計測し、正確にそのサイズのビットマップを割り当て、セルをそこへ描き、エンコードします。ページは存在しないため、設定して取り除くものがありません。紙が本当に必要なケースでは、PDF エクスポート経路が正しい道具であり、ワークシート PDF エクスポートの記事で扱っています
レンダリングの前に計測する
Measure は、何もエンコードせずに現在の設定が生むピクセル寸法を返します。これが重要な理由は 2 つあります。HTML やメールのテンプレートは通常、画像が存在する前に画像の寸法を必要とします。ボックスを予約し、レイアウトシフトを避けるためです。そして、ユーザーが選択した範囲をレンダリングするサービスには、割り当てる前に無茶な要求を拒否する方法が必要です
uses
lxHandleX, lxPagination;
var
Book: TXLSXWorkbook;
Sheet: TXLSXWorksheet;
Exporter: TXLSCellImageExporter;
Summary: TXLSXRange;
W, H, Bytes: Integer;
begin
Book := TXLSXWorkbook.Create;
try
Book.Open('quarter.xlsx');
Sheet := Book.Sheets.ByPos[0];
Summary := Sheet.Range['A1:F20'];
Exporter := TXLSCellImageExporter.Create;
try
Exporter.ImageFormat := xpifPng; // PNG は細い線をくっきり保つ
Exporter.DPI := 96;
Exporter.Scale := 2.0; // Retina 密度の出力
Exporter.IncludeGridlines := False;
Exporter.IncludeCellBorders := True;
Exporter.TransparentBackground := True;
Exporter.MaxPixels := 40 * 1000 * 1000;
Exporter.MaxBytes := 8 * 1024 * 1024;
if not Exporter.Measure(Summary, W, H) then
raise Exception.Create('range exceeds the configured budget');
// W と H が判明。エンコード前にレイアウトボックスを予約する
Bytes := Exporter.Save(Summary, 'summary.png');
if Bytes <= 0 then
raise Exception.Create('image export failed, previous file kept');
finally
Exporter.Free;
end;
finally
Book.Free;
end;
end;
予算が要るのは、スケールが掛け算だから
MaxPixels と MaxBytes は防御的な飾りではありません。ピクセル数はスケール係数の 2 乗で、かつ解像度比の 2 乗で成長します。96 DPI では妥当な 1200x800 である範囲が、600 DPI では約 47 メガピクセルになり、サマリーブロックではなく使用範囲全体を選んだユーザーは、その上にさらに一桁を加えます。上限がなければ障害モードは、プロセスが満たせない割り当てであり、そのプロセスが行っていた他のすべてを巻き込みます
上限があれば要求は失敗し、呼び出し側が選べます。拒否する、スケールを下げる、範囲を狭める。レポートサーバーにとってはるかに良い立場であり、上限付き EMF と WMF デコーダの記事で述べたメタファイルデコーダの明示的な予算と同じ論理です
アトミックな置換と、フォルダが重要な理由
ファイル名への Save は、ターゲットに書き込みません。同じフォルダに一時ファイルを書き、そこへエンコードし、それからターゲットを置き換えます。エンコードが失敗しても、途中で予算を超えても、プロセスが強制終了されても、前の画像はまだそこにあり、依然として有効です。したがってスケジュールでタイルを再生成するダッシュボードは、切り詰められた PNG を表示することが決してありません。宛先を開いてストリーミングを始める素朴な書き込みの典型的な症状です
同フォルダという細部は偶然ではありません。アトミックな置換は 1 つのボリュームの中でのみアトミックです。ボリュームをまたぐと OS はコピーしてから削除する必要があり、閉じようとしていた窓が再導入されます。一時ファイルをシステムの一時ディレクトリへ置くこのパターンの実装はすべて、出力が別のドライブにあるマシンではアトミックではありません
ペイントイベントは本物のキャンバスに描く
範囲エクスポーターとページエクスポーターの両方は、先行と後続のペイントイベントを公開し、キャンバスハンドルだけでなく完全な読み取り専用コンテキストを受け取ります。TXLSPagePaintContext は、ライブのキャンバス、ピクセル境界、ポイント単位のページサイズ、実際に使用中の解像度とスケール、文書ページ番号、シート内ページ番号、総ページ数、シート名、そしてクラシックと XLSX の両方の形式での元のワークシートを運びます。正しくスケールする透かしや、実行の中で自分の位置を知るページスタンプを描くには十分です
procedure TReportJob.StampDraft(Sender: TObject;
const AContext: TXLSPagePaintContext);
begin
// スケール対応のため、スタンプは 1x でも 3x でも同じに見える
AContext.Canvas.Font.Height := Round(-48 * AContext.Scale);
AContext.Canvas.Font.Color := clSilver;
AContext.Canvas.Brush.Style := bsClear;
AContext.Canvas.TextOut(AContext.Bounds.Left + Round(24 * AContext.Scale),
AContext.Bounds.Top + Round(24 * AContext.Scale), 'DRAFT');
end;
Exporter.AfterPaint := Job.StampDraft;
頼ってよい振る舞いが 3 つあります。イベントはレンダリングされたフレームごとに正確に 1 回発火します。マルチページ TIFF の各フレームも含まれ、ハンドラで増加させたカウンタは信頼できます。計測の間は静かに保たれるため、副作用のあるハンドラが 1 つの出力のために 2 回動くことはありません。そして先行イベントが例外を投げた場合、後続イベントは発火せず、部分的な画像バイトは書き込まれません。自分の描画コードの例外が、半分だけスタンプされたファイルを生み出すことはありません
フォーマットの選び方
テキスト中心のものには PNG です。JPEG はブロック変換を適用し、細い高コントラストの線の周りに目に見えるリンギングを生みます。セル罫線と小さなテキストはまさにそれであり、そのアーティファクトは、写真が完璧に見える品質設定でも生き残ります。JPEG が活きるのは、範囲が埋め込み写真に支配され、ファイルサイズがエッジの忠実さより重要なときです。透過背景には PNG が必要です。JPEG にはアルファチャネルがないためです。色付きの面上に置かれることを意図したタイルは、選択を済ませています
範囲に結合セルが含まれるなら、出力をシートと照らし合わせて確認してください。結合領域は列幅と、人を驚かせる形で相互作用します。レイアウトの規則は結合セルとレポートテンプレートの記事で扱っています。HotXLS は Excel 依存なしに Delphi と C++Builder から XLS、XLSX、ODS、CSV を読み書きし、エクスポーターの全機能面は、HotXLS Delphi spreadsheet component の製品ページに文書があります