Wordの闇を暴く:段落の「フォント崩壊」をバイナリレベルで検知する極限の技術
Wordの`Range.Font`プロパティを信じてはいけない。
我々のようなシステムアーキテクトが長年VBAと格闘して学んだ真実は、「Wordのオブジェクトモデルは、時としてメモリ上の真実を隠蔽する」ということだ。
特に、ドキュメントの肥大化に伴い発生する「フォント情報の不整合」や「文字コードの破損」は、VBA標準の`Paragraph`や`Font`オブジェクトでは検知できない。Wordが解釈を諦めたその先にある、壊れたバイナリの断片。これを見抜くには、Wordの外側、あるいはWordの最も深いレイヤーに潜り込むしかない。
本稿では、レガシーシステムを保守するエンジニアに向けて、Wordの段落に潜む「フォント情報の不整合」をバイナリ解析に近いアプローチで検知する手法を伝授する。
—
1. なぜWordオブジェクトモデルは「盲目」なのか
Wordの`Range.Font`は、XML(WordML)や内部バイナリ構造を「解釈」した後の結果を表示しているに過ぎない。もし、XMLの属性が矛盾していたり、フォントマッピングテーブルが破損していたりする場合、`Font.Name`は「MS 明朝」と返しながら、実際には豆腐(□)が表示されるという怪奇現象が起きる。
これを解決するには、Wordの「内部構造(Storage/Stream)」を物理的に覗く必要がある。
—
2. 実装:Windows APIを活用したバイナリの深淵へのアクセス
ここでは、WordドキュメントがOLE複合ファイル形式(.doc/.docm)であることを利用し、`Structured Storage`を読み解くアプローチを検討する。しかし、現代の`.docx`(OpenXML)環境では、ZIP構造を解凍し、`word/document.xml`をDOM解析する方が確実だ。
以下は、`MSXML2.DOMDocument`を用いて、段落レベルで「フォント定義の不整合」を検出するためのプロトタイプコードである。
‘ 必要な参照設定: Microsoft XML, v6.0
Option Explicit
Public Sub InspectParagraphFontIntegrity()
Dim doc As Document
Set doc = ActiveDocument
‘ メモリリークを最小化するためにDOMを明示的に解放
Dim xmlDoc As MSXML2.DOMDocument60
Set xmlDoc = New MSXML2.DOMDocument60
‘ 文書のXMLを一時ファイルとして保存し解析する(低負荷・高速化の定石)
Dim tempPath As String
tempPath = Environ(“TEMP”) & “\word_analysis.xml”
doc.SaveAs2 FileName:=tempPath, FileFormat:=wdFormatXMLDocument
xmlDoc.Load tempPath
‘ 名前空間の指定(OpenXMLの仕様に従う)
xmlDoc.SetProperty “SelectionNamespaces”, “xmlns:w=’http://schemas.openxmlformats.org/wordprocessingml/2006/main'”
‘ 各段落のフォント指定(w:rFonts)を走査
Dim nodes As MSXML2.IXMLDOMNodeList
Set nodes = xmlDoc.SelectNodes(“//w:rFonts”)
Dim node As MSXML2.IXMLDOMNode
For Each node In nodes
‘ ASCII範囲外の制御文字が含まれるか、あるいは定義名が空かを確認
If IsFontDefinitionCorrupt(node.Attributes.getNamedItem(“w:ascii”).nodeValue) Then
Debug.Print “破損検知: 段落またはランに不正なフォント定義を発見”
End If
Next
‘ 明示的なクリーンアップ
Set nodes = Nothing
Set xmlDoc = Nothing
Kill tempPath
End Sub
Private Function IsFontDefinitionCorrupt(fontName As String) As Boolean
‘ 内部的なフォント名が空、あるいは不正な文字コードを含むか判定
‘ ここにバイナリレベルの正規表現や、許可リストによる検証を記述する
If Len(fontName) = 0 Or InStr(fontName, “?”) > 0 Then
IsFontDefinitionCorrupt = True
End If
End Function
—
3. シニアエンジニアが意識すべき「パフォーマンスの重み」
この手法には重要な注意点がある。
- DOM解析の罠: 大規模なドキュメントで`xmlDoc.Load`を行うと、メモリを数GB単位で食いつぶす。数万行のドキュメントを扱う場合は、XMLをストリームとして読み込む `SAX` 解析を検討すべきだ。
- API呼び出しのオーバーヘッド: `Windows API`でメモリを直接操作する場合、`CopyMemory`(RtlMoveMemory)を使用することになるが、VBAのガベージコレクションはWordのオブジェクト削除と同期しない。必ず`Set object = Nothing`を徹底し、スコープを最小限に絞ること。
- キャッシュの汚染: `ActiveDocument`のプロパティを頻繁に呼び出すと、Wordの内部キャッシュが更新されず、古い情報を掴まされ続けることがある。`doc.Repaginate`や`doc.UndoClear`を適切なタイミングで実行し、強制的にメモリの状態を最新化する勇気が必要だ。
—
4. 結論:ツールに振り回されるな
Word VBAは、魔法の杖ではない。単なる「Officeインターフェースのラッパー」に過ぎないのだ。
真に複雑なビジネス要件、あるいはシステム統合の現場で求められるのは、オブジェクトモデルの向こう側にある「データの本質」を読み取る力である。
段落が文字化けしているのではない。段落を定義するバイナリが、Wordという巨大なエンジンの解釈に耐えられなくなっているだけだ。その断層を見つけることこそが、伝説的なエンジニアの流儀である。
次回の記事では、Wordの内部ストリーム(`Storage`)を直接操作し、メタデータを破壊することなくフォント情報を強制書き換えする「バイナリ・パッチ」の実装について踏み込んでいく。
コードの海で迷子にならないために。構造を理解せよ。そして、破壊せよ。
