HotXLSはAddCameraを通じて、DelphiからXLSXワークシート上にExcelカメラオブジェクトを作成する。カメラオブジェクトとは、セル範囲に恒久的にリンクされた画像である:Excelはワークブックが開かれるたび、またはソースが変更されるたびに、ソース範囲からそれを再描画するため、ダッシュボードは別シートにあるテーブルのライブビューを、任意のサイズで、必要なら回転させても表示できる
この機能はExcelにカメラツールとして存在するが、既定ではリボンに表示されないため、ほとんどのユーザーが見たことのないボタンである。これは、代替手段よりもうまくダッシュボードの実際の課題を解決する:コピーされた範囲は古くなり、チャートは任意のセル内容を表示できず、手作業で貼り付けたリンク画像はコードから生成できない。カメラオブジェクトは、ライブでありながら任意の内容を表示できる唯一の構造である
これはどこに文書化されているのか、そしてそれがなぜ重要か
主要なスプレッドシート仕様書はカメラオブジェクトを説明しておらず、実装の詳細として扱われている。権威ある説明は、drawing-markupのドキュメント内、Camera Toolという見出しの下にあり、それを知っておけば間違ったドキュメントを探し回る午後の時間を節約できる
構造的には、カメラオブジェクトは、非視覚的な画像プロパティに拡張リストを持つ通常の画像要素である。その拡張は固定のGUIDで識別され、その内部には2010年版drawing名前空間の要素があり、2つのことを記録する:A1形式の絶対参照であるソース範囲(オプションでシート修飾付き)、そして図形識別子である。それ以外の画像に関するすべては通常通りである
カメラオブジェクトを作成する
範囲名の指定方法として便利なものが2種類あるため、2つのオーバーロードが存在する。テキスト形式は、保存される通りの参照をそのまま受け取り、シートをまたぐソースにはこれを使う。座標形式は同じシート上の4つのセル座標を受け取り、絶対参照を自動的に組み立てる
uses
lxHandleX;
var
Book: TXLSXWorkbook;
Dashboard: TXLSXWorksheet;
Cam: TXLSXImage;
begin
Book := TXLSXWorkbook.Create;
try
if Book.Open('reporting.xlsx') <> 1 then
Exit;
Dashboard := Book.Sheets[0];
// dashboard上のB10:F20に配置したData!$B$2:$D$4のlive view
Cam := Dashboard.AddCamera('Data!$B$2:$D$4', 10, 2, 20, 6);
// placement boxはExcel自身の64 px columnをdefaultとし
// 20 px row;必要なら後で描画sizeをEMUで調整
Cam.WidthEMU := Round(12.5 * 914400 / 2.54); // 12.5 cm
Cam.HeightEMU := Round(6.0 * 914400 / 2.54);
Book.SaveAs('reporting-dashboard.xlsx');
finally
Book.Free;
end;
end;
返されるオブジェクトは、1つの追加プロパティCameraRangeを持つ通常の画像オブジェクトであり、この画像がカメラである場合にのみ空でない値を持つ。これは、自分が作成していないワークブックの中でカメラを検出する方法でもある:画像を列挙し、そのプロパティを確認すればよい
プレースホルダー画像が本物である必要がない理由
XLSXパッケージ内のすべての画像には画像パートが必要であり、カメラオブジェクトも例外ではない。しかしExcelはその画像を無視する:ロード時にリンクされた範囲を再描画し、その結果を描画する。埋め込まれたバイト列は、カメラの挙動を実装していないツール向けの表示キャッシュとして存在するに過ぎない
この事実により、実装からまるごと1つのサブシステムが不要になる。ソース範囲をラスタライズする必要はなく、スナップショットを生成するレンダリングエンジンも不要であり、キャッシュされた画像がExcel上のライブな画像と食い違うリスクもない。HotXLSは最小限のプレースホルダーメタファイル——ヘッダーレコードとファイル終端レコード、合計108バイト——を書き込み、パッケージの構造を完全に保ちながらコストをかけない
考慮しておくべき帰結が1つある:カメラオブジェクトを実装せずにXLSXを描画するビューアでは、プレースホルダーが表示され、それは空白である。ワークブックがそのようなツールで消費される場合、カメラオブジェクトは適切な構造ではなく、範囲をレンダリングした画像の方が正しい選択である
間違えやすい単位変換
Open XMLにおける描画のジオメトリはEnglish Metric Unit(EMU)で測定され、1インチは914,400 EMU、1センチメートルは360,000である。しかしメタファイルヘッダーは、そのフレームを0.01ミリメートル単位で記録する。両者の間の変換は360で割ることである
これを明記しておく価値があるのは、この誤りが静かに起こるからである。代わりに96 DPIでピクセルを経由して変換すると——2540を掛けて9525で割ると——正しい値の96倍という値になってしまい、それを拒否する仕組みは何もない:パッケージは有効であり、画像はアンカーによって正しく配置されるが、メタファイルの宣言されたフレームだけが意味をなさなくなる。EMUモデルとその丸め挙動については画像のジオメトリ、EMU単位、スケーリングで扱われている
すでにカメラを持つワークブックを往復編集する
ワークブックを開いて保存しても、拡張要素とその範囲参照を含め、カメラオブジェクトは保持される。これは作成することよりも重要である:カメラオブジェクトを持つほとんどのワークブックはアナリストによってExcelで作成されたものであり、保存時にそれらを静かに通常の画像に変換してしまうライブラリは、そのアナリストが頼りにしていたライブな挙動を破壊してしまう
// どのpictureがcameraで、どこを向いているかaudit
for I := 0 to Sheet.Images.Count - 1 do
if Sheet.Images[I].CameraRange <> '' then
Writeln(Format('camera %d -> %s',
[I, Sheet.Images[I].CameraRange]));
ダッシュボードをプログラムで生成する場合、この監査は回帰テストでもある:保存して再度開くサイクルの後も、同じ数のカメラが同じ範囲を指していなければならない。カメラを配置するアンカーモデルを含む、一般的な画像と描画の扱いについてはチャート、画像、描画で扱われている
カメラが代替手段に勝る場面
同じライブなテーブルが異なるサイズで複数のシートに現れなければならない場合、印刷レイアウトである1つのシートの領域を他のシートの領域と並べて構成する必要がある場合、あるいは要約ブロックが、すべてのセルを個別に数式でリンクすることなく、他所で行われた編集に追従すべき場合には、カメラオブジェクトを使うとよい
対象がわずか数個のセルである場合は通常の数式を優先すること。シートをまたぐ数式の方が単純であり、どのツールも理解できる。データが真に系列であり書式付きのブロックではない場合はチャートを優先すること。そしてワークブックがExcel以外のツールで消費される場合や、スナップショットが納品後に変化してはならない場合はレンダリング済みの画像を優先すること——アーカイブされたレポートはライブであってはならない
実務上のレイアウトに関する注意点を1つ。カメラは、結合セル、条件付き書式、列幅を含め、ソース範囲を書式そのままに正確に表示する。したがって見た目のきれいなダッシュボードブロックを得るには、ソース範囲をあたかも完成した出力であるかのように書式設定することから始まる。これは結合セルとレポートテンプレートのレイアウトで説明されているのと同じ規律である
カメラオブジェクト、描画、そしてその背後にあるXLSXパッケージライターは、DelphiおよびC++Builder向けの1つのライブラリにまとめられている。機能一覧の全体はHotXLS Delphiスプレッドシートコンポーネントページにある