Excel ワークブックは EMF と WMF の画像を運べます。その従来の描き方は、バイトストリームを OS のメタファイルプレーヤーへ渡すことです。これは直接見つめる価値のある判断です。メタファイルはグラフィックス API 向けのシリアライズされたコマンドストリームであり、それを再生するとは、メールで届いたファイルにグラフィックスドライバーを運転させることを意味します。HotXLS はもう一方の経路を取ります。XLSDecodeVectorScene はメタファイルを自前で解析し、ヘッダー、すべてのレコードサイズ、宣言されたレコード総数、EOF レコードの正確な位置を検証し、エスケープレコードをきっぱり拒否し、Canvas と SVG バックエンドが自分のコードで再生するプリミティブ描画コマンドの TXLSVectorScene を返します。ドライバー再生はどの時点でも関与しません
この取引は、封じ込めと引き換えにカバレッジを差し出すものです。矩形指向のコマンドホワイトリストは、デザイナーが作れるすべてのメタファイルを再現しません。そのためシーンは表現できなかった描画レコードの数を報告し、呼び出し側がそれをどうするか決めます。自分では作らなかった文書をレンダリングするサーバープロセスにとって、この取引の向きは正しいです
メタファイル再生は信頼できない入力にそぐわないのはなぜか
フォーマットが画像ではなくプログラムだからです。EMF のレコードストリームはデバイスコンテキストの状態スタックを操作し、ハンドルテーブルからオブジェクトを割り当てて選択し、ペイロードがデバイスドライバーへ素通しされるエスケープレコードを運び得ます。それを再生することは、メタファイルが同じマシン上の協調的なアプリケーションから来たという前提で書かれた、プラットフォームグラフィックススタックの経路を通します。入力がスプレッドシート添付ファイルであるとき、その前提は消えており、スプレッドシートライブラリ内部のどれほどの注意も役に立ちません。解析を行っているコンポーネントはライブラリではないからです
これはコンテナレイヤーを支配するのと同じ論理です。ワークブックは ZIP アーカイブであり、HotXLS は宣言されたオフセットを信頼するのではなく中央ディレクトリを検証します。ZIP の end-of-central-directory 検証の記事に述べたとおりです。メタファイルのペイロードは、同じ問題の次のレイヤーです
デコーダが何かを描く前に確認すること
検証は構造的であり、前もって行われます。描画を始めてから進みながら検証するパーサーは、すでに検証していないデータへ基づいて行動しているからです。ヘッダーはそれらしくではなく厳密に一致しなければなりません。すべてのレコードは、残りバッファに収まり、自分自身の固定フィールドに十分な大きさのサイズを宣言しなければなりません。ヘッダーが宣言するレコード数は、実際に存在するレコードと一致しなければなりません。EOF レコードは、その近くではなく、ストリームが終わる正確な位置になければなりません。これは、有効な画像の背後に 2 つ目のペイロードを隠す、末尾ゴミの手口を封じます
構造を超えて、デコーダはセマンティクスに対してフェイルクローズです。エスケープレコードはスキップされず拒否されます。デコーダがモデル化しない状態変更レコードは、無視されるのではなくデコードを失敗させます。状態変更を無視するとは、その後のすべての描画コマンドがファイルが求めていない状態で実行されることであり、結果は誰にも予測できない形で誤った画像になります。サポートされるコマンド集合の外にある描画レコードは別の話です。それらは数えられてスキップされます。欠けた図形は見えて報告できる隙間であり、静かな破壊ではないからです
予算はフォーマット契約の一部
ベクターフォーマットには、展開爆弾の独自版があります。数キロバイトのレコードが、数億の点を持つポリラインや、宣言された寸法がテラバイトに掛け合わさる画像を宣言し得ます。したがって上限は、マシンがたまたま耐えられるものではなく、明示的な定数でなければなりません
// lxVectorScene から:デコード予算。暗示ではなく明示されたもの
XL_VECTOR_MAX_RECORDS = 1000000;
XL_VECTOR_MAX_HANDLES = 4096;
XL_VECTOR_MAX_DC_DEPTH = 32;
XL_VECTOR_MAX_COMMANDS = 100000;
XL_VECTOR_MAX_POINTS_PER_RECORD = 100000;
XL_VECTOR_MAX_TOTAL_POINTS = 2000000;
XL_VECTOR_MAX_TEXT_CHARS = 4096;
XL_VECTOR_MAX_TOTAL_TEXT_CHARS = 1000000;
XL_VECTOR_MAX_IMAGE_SIDE = 8192;
XL_VECTOR_MAX_IMAGE_PIXELS = 32 * 1024 * 1024;
XL_VECTOR_MAX_IMAGE_BYTES = 64 * 1024 * 1024;
XL_VECTOR_MAX_COORD = 1000000000;
このうち 2 つには注釈が要ります。デバイスコンテキストの深さ上限 32 が存在するのは、SaveDC と RestoreDC のレコードがネストし、バランスの壊れたストリームは永遠にプッシュし得るからです。32 は実際のメタファイルには寛大であり、強制も安価です。座標の上限が存在するのは、座標が変換への入力であり、整数範囲の限界近くの値は、無限になるか桁あふれする変換結果を生み、その後の下流のバウンディングボックス計算はすべて無意味になるからです。座標をパース時にクランプするほうが、ジオメトリの消費者すべてを防御するよりはるかに考察しやすいです
シーンの使い方
デコーダは、あなたが所有するオブジェクト、コマンド数、公称サイズ、そして表現しないことを選んだ描画レコードの数を渡します
uses
lxVectorScene;
var
Scene: TXLSVectorScene;
Error: WideString;
I: Integer;
begin
// Data はワークブックから取り出した生の画像ペイロードを保持する
if not XLSDecodeVectorScene(Data, xlsvfEmf, Scene, Error) then
begin
// 拒否された:ヘッダー、上限、総数、EOF 位置、予算のいずれか
LogReject('metafile rejected: ' + Error);
Exit;
end;
try
if Scene.SkippedDrawRecords > 0 then
LogWarning(Format('%d drawing records outside the safe subset',
[Scene.SkippedDrawRecords]));
for I := 0 to Scene.Count - 1 do
case Scene.Commands[I].Kind of
xlsvcRectangle: DrawRect(Scene.Commands[I]);
xlsvcEllipse: DrawEllipse(Scene.Commands[I]);
xlsvcPolyline,
xlsvcPolygon,
xlsvcBezier: DrawPath(Scene.Commands[I]);
xlsvcText: DrawText(Scene.Commands[I]);
xlsvcImage: DrawImage(Scene.Commands[I]);
end;
finally
Scene.Free;
end;
end;
コマンドレコードは、バックエンドに必要なすべてを運び、デバイスを要求するものを何も運びません。ペンの有無、色、幅、スタイル。ブラシの有無と色。ジオメトリ。テキストについては文字列、フォント名、サイズ、スタイル、配置です。これが同じシーンを、画面上のキャンバスレンダラーと SVG ライターの両方で使えるようにするものであり、ベクター経路がプレビューとエクスポートの間で分岐しない理由です。ワークシートコンテンツの画面レンダリング全般は、カスタム VCL グリッドレンダリングの記事で扱っています
画像の拒否はワークブックを損傷しない
この設計の重要な性質は、拒否されたデコードがレンダリングにしか影響しないことです。元のペイロードはモデルに残るため、開いて再度保存されたワークブックは、安全なデコーダが描けたかどうかにかかわらず、メタファイル画像をバイト単位でそのまま運び出します。既存の上限付きラスタ経路もフォールバックとして引き続き利用できます。つまり、厳格なパーサーがゲートするのは実行されるものであって保存されるものではない。これが、セキュリティを動機とする変更が、データ損失の変更に変わらずに出荷できる理由の区別です
ラウンドトリップで無変更のまま生き延びるオブジェクトモデルの部分を含む、描画オブジェクト処理の全般は、チャート、画像と描画の記事で扱っています
サーバー配備がここから得る立場
サービスでユーザーがアップロードしたワークブックをレンダリングするなら、実務上の立場は今や擁護可能です。メタファイル画像は、監査できるコードによって解析され、読める定数によって上限が設けられ、グラフィックスドライバーへ渡されることはありません。正直な注意点はカバレッジです。描画ツールが生成した複雑なメタファイルは、スキップレコードカウンターに届きます。その答えは、ホワイトリストを黙って広げるのではなく、カウンターを表面化することです。部分的にレンダリングし、そう告げる画像はサポートの会話です。誤ってレンダリングし、何も告げない画像は顧客からのバグ報告です
HotXLS は、Excel をインストールせずに Delphi と C++Builder で XLS、XLSX、ODS、CSV をネイティブに処理し、同じ上限付き解析の哲学がコンテナ、数式、描画の各レイヤーを貫いています。フォーマットとセキュリティの詳細は、HotXLS Delphi spreadsheet component の製品ページに一覧があります