0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

スクリプトを切っても本文は残る — Word を 1 つの HTML で配る前に測ったこと

0
Posted at

社外に出した技術提案書に対して、相手から返ってきたのは「開けない」の一言だった。先方の業務端末は古い Office で、資料の中にある 36 行の表がそこで崩れていた。こちらの画面では何ともなかった。

こういうのは初めてではない。少し前には、送った提案書の数字を相手側が書き換えてそのまま転送し、話がこちらに戻ってきたこともある。原本をそのまま送るという行為には、開けないリスクと、変えられるリスクの二つが同時に乗っている。

それ以来、確定した文書を社外に出すときは、自己完結した読み取り専用の HTML を一緒に送るようにした。ファイルは一つ。ダブルクリックで開く。Office もネットワークもログインも要らないし、編集もできない。

面白いのは発想そのものではなく、受け入れ条件が測れるようになったことだ。「開けること」は検証しようがない。「相手の端末に何も入っていなくてもファイルが自立していること」なら測れる。

何で試したか

実案件のファイルで実験する気にはなれなかったので、普段書いている提案書に似せたサンプルを自作した。会社名も伝票番号も金額も人名も全部架空だ。壊れやすいものを意図的に詰め込んである。A4 レイアウト、6 か所の改ページ、3 階層の番号付きリスト、36 行 6 列の長い表、ヘッダーとフッター、脚注 4 件、画像 3 枚、数式 1 つ、そしてワードアートの表紙タイトル。28,516 バイト。

変換にはブラウザ上で動く ImgIng(https://imging.ai/)を使った。DOCX から HTML への変換はローカルで完結する。ネットワークパネルを開いたまま 6 回まわしたが、読み込みから変換完了まで GET 以外のリクエストは 0 件だった。出力は 85,903 バイトの HTML ファイル一つで、同名のリソースフォルダは付いてこない。

ちなみにサイズが 3 倍になったことには意味がない。.docx は zip アーカイブで、出力は無圧縮の単一ファイルだ。比べるべきなのは「相手が何をインストールしなければならないか」のほうだ。

確認したのは三点

ファイルが一つであること。 いわゆる「Web ページとして保存」は HTML と同名フォルダを吐くことが多い。メールに添付するときフォルダを落とせばそれで終わりだ。

外部リソースがゼロであること。 外部の script、link、img が一つでも残っていれば、それはいつか失敗するリクエストになる。オフラインの端末、社内プロキシ、数年後に移動した CDN のパス。この出力にはどれも無い。画像 3 枚は base64 で埋め込まれていて全体の 31.0%、本文とスタイルが 56.8%、スクリプトが 12.2% だった。

1 つの HTML ファイルの中身:本文とスタイル 56.8%、埋め込み画像 31.0%、スクリプト 12.2%、外部リソース 0 件、パーサーの残骸 0 か所

スクリプトを切っても本文が残ること。 相手のブラウザが社内ポリシーで制限されていたり、ブロッカーが入っていたり、組み込みの WebView だったりする可能性があるので、ここが一番気になっていた。

もう一つ、見抜きにくい失敗の型がある。.docx 丸ごととパーサーを HTML に詰め込み、開いた瞬間にブラウザ内で展開して描画する作りだ。確かに単一ファイルで、確かにオフラインで開ける。だが本文はファイルの静的なバイト列の中には無いし、結局のところ原本をそのまま渡してしまっている。JSZip、fflate、pako、mammoth、inflateRaw、OOXML の名前空間、<w: タグ、zip を base64 化したときの先頭 UEsDB、.wasm を順に検索した。すべて 0 件だった。

スクリプトを切って比べた結果

デスクトップ表示領域 1440×1000。同じファイルをスクリプト有効で一度、無効で一度開く。

最初の計測は間違っていたので、先に書いておく。document.body.innerText を本文の文字数として使ったところ、有効時 5,997 文字、無効時 3,224 文字となり、半分近く消えたように見えた。本文が落ちたと結論づける寸前で、左側のサムネイルレーンに気づいた。スクリプト有効時にはそこが各ページの内容をもう一度描画する。差分の 2,773 文字は全部その重複だった。

本文コンテナだけを測り直したら数字が揃った。両方とも 9,822 バイト、326 行。行単位の差分を取ると、有効側だけに出る行が 0 行、無効側だけに出る行も 0 行。全選択でコピーできる文字数は 3,178 文字で、こちらも両方同じ。7 ページの改ページ、各ページのヘッダー、37 行 222 セルの表、画像 3 枚、脚注 4 件はすべて元の位置に残り、コンソールエラーも 0 件だった。

同じ出力 HTML。左は JavaScript 有効、右は無効で、本文の行単位の差分は 0 行、3,178 文字を選択できる。デスクトップ表示領域 1440×1000

検証したのはデスクトップ幅だけで、狭い画面は挙動が別なので、そこまで広げて言うつもりはない。

本文の欠落も突き合わせた。document.xml から可視段落を 286 段落抜き出し、出力側で一つずつ探して 285 段落が一字一句一致した。合わなかった一つは、脚注の上付き番号が文の途中に挿入されていただけで、文字は何も失われていない。数式は MathML になっていて選択もできた。これは期待以上だった。

代償のほう

サンプル上で崩れた箇所は三つあり、送る前に毎回そこを見ている。フッターのページ番号フィールドは再計算されず、7 ページすべてが「1 ページ / 全 10 ページ」と表示される。元ファイルに保存されていたキャッシュ値だ。表紙のワードアートは、場所だけ取って中身が空の塊になる。埋め込んだ Excel オブジェクトは丸ごと消える。36 行の表もページをまたがず、4 ページ目の高さが 1123 px から 1733 px に伸びる。画面では気づかないが、紙に出すとはみ出す。

埋め込みフォントも付いてこない。別のサンプルで所定の方式でフォント本体を .docx に埋め込んでみたが、出力に @font-face は 0 件で、フォント名だけが残った。これは見た目の問題では済まない。ページの高さは変換時に計算されてマークアップに書き込まれるので、相手の端末に同名のフォントが入っていると、文字の高さだけが 45% から倍以上に伸び、ページの枠は動かず、内容が枠の外に押し出される。実務的な結論は一つ、社外に出す文書にシステムフォント以外を使わないこと。

予想より良かった点も一つ。VBA のマクロプロジェクト、OLE オブジェクト、ActiveX コントロールをそれぞれ固有のマーカー文字列付きでサンプルに仕込み、出力を平文で検索し、さらに base64 を全部デコードしてから検索し直した。どちらも 0 件で、CFB や ZIP のファイルヘッダーも出てこない。渡すファイルに実行可能なものが一切入っていないということで、こちらの社内ではセキュリティレビューが一回減る。

一方で「Word で開いたのと同一」とは書かない。実際の Office のレンダリングとピクセル単位でもページ数でも比較していないし、そもそも改ページはフォントメトリクスやプリンタ設定に左右される。担保できるのは三つだけだ。ファイルが一つ、外部リソースがゼロ、スクリプトを切っても本文が残る。

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?