【実務・中級編】【上級者向け】Wordの「段落」に適用された複雑な書式を、別の文書へ完璧にコピーするツール – Word VBA解析バイブル

スポンサーリンク

序文:クリップボードに頼る「三流の自動化」との決別

Word VBAの世界へようこそ。諸君の中には、書式のコピーと言えば「`Selection.Copy` して `PasteFormat` すればいい」と考えている者がいるかもしれない。もしそうなら、今すぐその考えを捨てろ。

クリップボードを経由する自動化は、ユーザー操作と競合し、バックグラウンド実行を阻害し、何より「どのプロパティが継承され、どのプロパティが捨てられたか」を制御できない。真のアーキテクトが求めるのは、プロパティの完全な支配だ。

今回のミッションは、ある文書の段落(Paragraph)が持つ複雑な書式――インデント、行間、タブ位置、そして厄介な枠線と網掛け――を、別の文書のターゲット段落へ「外科手術」のように精密に移植することだ。

1. なぜ「段落書式」の同期は難しいのか

Wordの段落オブジェクト(`Paragraph`)は、単なるテキストの塊ではない。背後には `ParagraphFormat` オブジェクトが存在し、さらにその下層には `Borders`(枠線)、`Shading`(網掛け)、`TabStops`(タブ位置)といった複雑なコレクションがぶら下がっている。

陥りやすいアンチパターン

1. Styleの代入だけで済ませる: スタイルを適用しても、元の段落に「直接設定(オーバーライド)」された書式は無視される。
2. プロパティの列挙不足: インデントだけコピーして、タブ位置や網掛けを忘れる。これはプロの仕事ではない。
3. TabStopsの不適切な処理: `TabStops` はコレクションだ。単純な代入はできず、既存のタブをクリアしてから一つずつ再構築する必要がある。

2. 実装:プロパティ・トランスファー・エンジン

以下に、ソース段落の書式をターゲット段落へ完全に複製する、プロダクションレベルのコードを示す。このコードは、単なるコピーではなく「属性の同期」を行う。

Option Explicit

”’

”’ 指定したソース段落の書式をターゲット段落へ精密にコピーする
”’

”’ コピー元段落オブジェクト ”’ コピー先段落オブジェクト Public Sub SyncParagraphFormatting(ByRef srcPara As Paragraph, ByRef destPara As Paragraph)
On Error GoTo ErrorHandler

‘ 描画停止によるパフォーマンス最適化
Dim app As Word.Application
Set app = srcPara.Application
app.ScreenUpdating = False

‘ 1. ParagraphFormatの基本プロパティの複製
‘ 個別にプロパティを叩くのは非効率だが、制御を確実にするために主要なものを列挙する
With destPara.Format
.Alignment = srcPara.Format.Alignment
.LeftIndent = srcPara.Format.LeftIndent
.RightIndent = srcPara.Format.RightIndent
.SpaceBefore = srcPara.Format.SpaceBefore
.SpaceAfter = srcPara.Format.SpaceAfter
.LineSpacingRule = srcPara.Format.LineSpacingRule
.LineSpacing = srcPara.Format.LineSpacing
.FirstLineIndent = srcPara.Format.FirstLineIndent
.KeepWithNext = srcPara.Format.KeepWithNext
.KeepTogether = srcPara.Format.KeepTogether
.PageBreakBefore = srcPara.Format.PageBreakBefore
.NoLineNumber = srcPara.Format.NoLineNumber
.Hyphenation = srcPara.Format.Hyphenation
.OutlineLevel = srcPara.Format.OutlineLevel
.CharacterUnitLeftIndent = srcPara.Format.CharacterUnitLeftIndent
.CharacterUnitRightIndent = srcPara.Format.CharacterUnitRightIndent
.CharacterUnitFirstLineIndent = srcPara.Format.CharacterUnitFirstLineIndent
.LineUnitBefore = srcPara.Format.LineUnitBefore
.LineUnitAfter = srcPara.Format.LineUnitAfter
End With

‘ 2. タブ位置 (TabStops) の再構築
‘ TabStopsは一度クリアしてから追加し直すのがセオリー
destPara.Format.TabStops.ClearAll
Dim srcTab As TabStop
For Each srcTab In srcPara.Format.TabStops
destPara.Format.TabStops.Add _
Position:=srcTab.Position, _
Alignment:=srcTab.Alignment, _
Leader:=srcTab.Leader
Next srcTab

‘ 3. 枠線 (Borders) の精密コピー
‘ Bordersは列挙体でループを回し、各辺の属性(線種、太さ、色)を同期する
Dim i As Long
For i = -1 To -6 Step -1 ‘ wdBorderTop, Bottom, Left, Right, Horizontal, Vertical
On Error Resume Next ‘ 存在しない境界線のエラー回避
With destPara.Borders(i)
.LineStyle = srcPara.Borders(i).LineStyle
.LineWidth = srcPara.Borders(i).LineWidth
.Color = srcPara.Borders(i).Color
End With
On Error GoTo ErrorHandler
Next i

‘ 4. 網掛け (Shading) の同期
With destPara.Shading
.Texture = srcPara.Shading.Texture
.ForegroundPatternColor = srcPara.Shading.ForegroundPatternColor
.BackgroundPatternColor = srcPara.Shading.BackgroundPatternColor
End With

‘ 5. フォント属性の同期(段落全体に適用)
‘ 段落記号に含まれるフォント情報を反映させる
destPara.Range.Font = srcPara.Range.Font

CleanUp:
app.ScreenUpdating = True
Exit Sub

ErrorHandler:
‘ ログ出力やエラーハンドリングのロジックをここに実装
Debug.Print “Error in SyncParagraphFormatting: ” & Err.Description
Resume CleanUp
End Sub

3. チーフアーキテクトによる深掘り解説

TabStopsの「ClearAll」はなぜ必須か

Wordの段落は、デフォルトでスタイル由来のタブ位置を持っている。単純に `Add` だけを行うと、元の段落には存在しなかった不要なタブが残る可能性がある。「一度更地にしてから、設計図通りに建てる」。これが不整合を防ぐ鉄則だ。

Bordersループの負のインデックス

`wdBorderTop` 等の定数は、実際には負の整数値(-1, -2…)である。これらをループで回す際は、`On Error Resume Next` を併用せよ。なぜなら、段落のコンテキスト(表の中か、通常のテキストか)によって、有効な境界線が異なるからだ。

パフォーマンスとメモリ管理

このコードを数千段落に対して実行する場合、`Application.ScreenUpdating = False` は最低条件だ。さらに、大量の文書を跨ぐ場合は、`Paragraph` オブジェクトを明示的に `Nothing` で解放し、ガベージコレクションを助ける設計にせよ。

4. 実務での拡張:外部設定(JSON/DB)との連携

もし君が「複数の定型フォーマットを動的に切り替えたい」なら、このプロパティ群をデータベースやJSONファイルに外出しすることを検討すべきだ。

  • データベース連携: `LeftIndent` や `LineSpacing` の値を数値としてテーブルに保持する。
  • バリデーション: コピー先の文書が「読み取り専用」でないか、または「保護されたビュー」でないかを、処理の冒頭で必ずチェックせよ。

‘ 堅牢なツールのための事前チェック例
If destPara.Range.Document.ProtectionType <> wdNoProtection Then
Err.Raise vbObjectError + 513, , “ターゲット文書が保護されています。”
End If

結論:コードの背後にある「思想」を写せ

コピー&ペーストは「魔法」だが、エンジニアリングではない。
今回提示したようなプロパティ単位の同期手法は、一見遠回りに見える。しかし、「何が起きているかを完全に把握している」という状態こそが、保守性の高い、バグのないツールを生む唯一の道だ。

このコードをベースに、自社の業務要件に合わせてさらに磨きをかけてほしい。君が書く一行のコードが、現場の誰かの「手作業」を永遠に消し去るのだ。健闘を祈る。

タイトルとURLをコピーしました