Word VBAによるミリ単位のレイアウト制圧:ポイントという「呪縛」を解く
WordのVBAを扱う多くのエンジニアが、`Table.PreferredWidth` や `Row.Height` の数値指定で「なぜか微妙にズレる」「プリンタによって印刷結果が異なる」という壁に突き当たる。
結論から言おう。Wordの内部単位は「ポイント(1/72インチ)」だ。ミリ単位の厳密な設計を求めるなら、単なる数値入力ではなく、「変換係数の固定」と「レイアウトエンジンの挙動制御」をプロレベルで実装しなければならない。
今日は、Wordのレンダリングの深淵に触れる、シニアエンジニアのための極限実装術を共有する。
—
1. ポイントとミリの変換における「浮動小数点誤差」の排除
VBAにおいて `1mm = 2.83464567pt` といった係数を安易に使うのは素人だ。Wordのオブジェクトモデルは内部的に高い精度を要求する。以下の定数定義をテンプレートに埋め込むのが鉄則だ。
‘ 厳密な変換係数(定数として管理し、計算は必ずDoubleで行う)
Private Const PT_PER_MM As Double = 72 / 25.4
‘ ミリからポイントへの厳密変換関数
Public Function MmToPt(ByVal mmValue As Double) As Single
MmToPt = CSng(mmValue PT_PER_MM)
End Function
—
2. 表のレイアウトを「固定」するための必須設定
動的に表を生成する際、Wordの「自動調整機能」が最大の敵となる。表の内容が増減した瞬間に、苦労して計算したセル幅が崩壊するからだ。これを防ぐには、以下のプロパティを「生成直後」に叩き込む必要がある。
Sub InitializeStrictTable(tbl As Table)
With tbl
‘ 自動調整を無効化(最重要:これが漏れると全て無意味になる)
.AllowAutoFit = False
.PreferredWidthType = wdPreferredWidthPoints
‘ レイアウトエンジンを固定
.Rows.WrapAroundText = False
.Rows.Alignment = wdAlignRowLeft
End With
End Sub
—
3. 行・列の精密制御:オブジェクトの解放とメモリ管理
大規模な帳票出力を行う場合、Wordのオブジェクトキャッシュがメモリを圧迫し、レンダリング速度が著しく低下する。特に `Row` や `Cell` オブジェクトをループで回す際は、明示的な参照の解放と、`ScreenUpdating` の抑制が必須だ。
Public Sub GeneratePrecisionTable(doc As Document, mmWidths() As Double)
Dim tbl As Table
Dim i As Integer
‘ 描画の抑制
Application.ScreenUpdating = False
Set tbl = doc.Tables.Add(Range:=Selection.Range, NumRows:=1, NumColumns:=UBound(mmWidths) + 1)
Call InitializeStrictTable(tbl)
‘ 列幅の厳密指定
For i = 0 To UBound(mmWidths)
With tbl.Columns(i + 1)
.PreferredWidthType = wdPreferredWidthPoints
.PreferredWidth = MmToPt(mmWidths(i))
End With
Next i
‘ オブジェクトの明示的解放(VBAのスコープ内での規律)
Set tbl = Nothing
Application.ScreenUpdating = True
End Sub
—
4. なぜ「Windows API」が必要になるのか
Wordの `Table` オブジェクトだけでは解決できない事態がある。それは、「プリンタドライバごとの解像度依存」だ。
極めて高度な帳票システムでは、`GetDeviceCaps` APIを使用して、現在のデフォルトプリンタの論理解像度(LOGPIXELSX)を取得し、Wordの内部単位との差異を補正するコードを組み込むことがある。
もし貴殿が、1ミクロンのズレも許されない精密な帳票出力(例:官公庁提出用、ラベル印刷)を担当しているなら、以下のAPI定義をモジュールの先頭に置くことを検討せよ。
If VBA7 Then
Private Declare PtrSafe Function GetDeviceCaps Lib “gdi32” (ByVal hdc As LongPtr, ByVal nIndex As Long) As Long
Else
Private Declare Function GetDeviceCaps Lib “gdi32” (ByVal hdc As Long, ByVal nIndex As Long) As Long
End If
‘ LOGPIXELSX = 88 を用いて、実測値に基づいた補正係数を算出する
—
5. チーフアーキテクトからの提言
多くのエンジニアが「VBAは遅い、使いにくい」と嘆く。しかし、それはWordが持つ強力なCOMインターフェースを、ただの「マクロ記述言語」としてしか見ていないからだ。
1. レイアウトの論理分離: セル幅計算ロジックをWordの描画処理から分離せよ。
2. 中間データの活用: `Array` や `Collection` で座標情報を保持し、最後に `Range.Insert` で一気に流し込め。
3. エラーハンドリング: `On Error Resume Next` で逃げるな。セルの結合失敗は `70` や `4605` といった特定のランタイムエラーを捕捉し、再試行ロジックを組むのがプロの矜持だ。
Wordをただの文書作成ソフトと思うなかれ。これは、強力なレンダリングエンジンを備えた、高度な自動化プラットフォームだ。この「ミリ単位の制御」という視点を持てば、貴殿のシステムは他のエンジニアには決して模倣できない品質に到達するはずだ。
極限のコードは、常にシンプルで、かつ計算されている。健闘を祈る。
