Excel, Wordの自動操作のハードル削減計画・その3

~ Wordにおける複数の文書ファイルの結合 ~

2020年5月6日

[はじめに]

 今回は Wordにおいて複数の文書ファイルを結合します。

 JuseOffice03.zipにサンプルのスクリプトとデータが入っています。

 複数の人で執筆を分担している場合、
あるいは一人であっても章や節ごとに別ファイルにしている場合、
あとでそれらを合体する必要があります。

 そのやり方について見ていきます。

 JuseOffice03.zipを解凍すると SampleData というサブフォルダができて
その下にいくつかファイルがあります。それらを結合。

 Wordでは InsertFile を使って他の文書をカーソル位置に挿入できます。

 InsertFileは、SelectionとRangeのそれぞれに適用できますが、
多少の違いがあるのでそれにも触れます。


[目次へ]

1. Selection

 Wordを扱うVBAには Selection がよく出てきます。選択範囲を示すものです。

 JuseOfficeでは Selection という関数を定義しており、
VBAの Selection と同じように使えます。

 Selection.InsertFile を実行した場合、
ファイルを取り込んだあと、選択範囲は末尾の一点に移ります。

 取り込んだファイルの大きさに応じて選択範囲が拡がる訳ではなく、
取り込み後の末尾に選択位置が移るといったイメージです。

 この暗黙のルールがあるので、次のWordファイルの取り込みが容易になります。

 「末尾にカーソルを移動」を明示的に記述しなくてもよくなります。

 SelectionでなくRangeを使う場合は、こうはいきません。

 以下、merge01.wsfの主要部分を掲げます。

 結合結果は merge01.docx に記録されます。

mergeFile = "merge01.docx"
[ドキュメントを開く] mergeFile
wildName = ".\SampleData\*.docx"
fileList = [ファイル名の一覧をソート](wildName, "名前")
For Each docName In fileList
    Selection.InsertFile docName  ' 一つのWordファイルを取り込む
Next
[ドキュメント].Range(0, 0).Select  ' 文書先頭に焦点を戻す
[ドキュメントを保存]
[ワードを終了]

 Selection は [ワード].Selection と書いても同じです。

 でも、逐一そのように記述するとなると煩わしいので
JuseOfficeでは Selection という関数を定義しました。

 なお、Excelの方の Selection は、[エクセル].Selection と書く必要があります。


[目次へ]

2. 改ページコードの挿入およびIncludeによるスクリプトの読み込み

 一つのWordファイルを取り込むごとに改ページしたくなることがあります。

 改ページコードなどを挿入するには InsertBreak メソッドを用います。

 Selection.InsertBreak 7 とすれば改ページコードが入ります。

 VBAでは wdPageBreak のような定数を使えるので
Selection.InsertBreak wdPageBreak と書くことができます。

 数値よりも定数の方が意味が明確になり、スクリプトがわかりやすくなります。

 でも、VBScriptでは自前で定数を定義しないと使えません。

 そこで、const_wd.vbs を作りました。3248個の定数があります。

 なかなかのボリュームなので JuseOffice.vbs に組み込むのはやめて
別ファイルにしました。

 なので、この const_wd.vbs をスクリプトとして読み込む必要があります。

 それをおこなうのが Include というサブルーチンです。

 Include "const_wd.vbs" のように記述します。

 以下に merge02.wsf の主要部分を掲げます。

 一つのファイルと次のファイルの間に改ページコードを入れます。

Include "const_wd.vbs"
mergeFile = "merge02.docx"
[ドキュメントを開く] mergeFile
wildName = ".\SampleData\*.docx"
fileList = [ファイル名の一覧をソート](wildName, "名前")
lastNum = UBound(fileList)  ' 該当ファイルの最後の番号
For i = 0 To lastNum-1
    Selection.InsertFile fileList(i)
    Selection.InsertBreak wdPageBreak
Next
Selection.InsertFile fileList(lastNum)
[ドキュメント].Range(0, 0).Select  ' 文書先頭に焦点を戻す
[ドキュメントを保存]
[ワードを終了]

 InsertFileで最後に取り込むファイルの後に改ページコードを入れたくないので
最後のファイルだけ Forループの外で扱っています。


[目次へ]

3. 改ページ関連の補足

 InsertBreakの引数としてセクション区切りなども指定できます。

 Selection.InsertBreak wdSectionBreakNextPage とすれば
改ページされると同時に、セクションも別のセクションになります。

 改ページはしたくないけどセクションは別にしたい、といったことも可能です。

 たとえば、段組はセクション単位で設定できます。

 セクションを区切っておけば、このセクションは2段組、
こちらは通常の1段組、といった設定が可能です。

 参考まで InsertBreak の引数に使える定数を列記しておきます。

 その意味合いは、定数名から推測してください。


[目次へ]

4. Range.InsertFileおよび[ドキュメントの末尾]

 InsertFileは、Rangeオブジェクトにも適用できます。

 Selectionに適用したときは、暗黙のうちに焦点が末尾に移動したのに対し
Rangeの場合はRangeオブジェクトが基本的に変化しません。

 そのため「末尾に移動」を意図的に行う必要があります。

 JuseOfficeでは [ドキュメントの末尾] という関数が定義されていて
ドキュメントの末尾をRangeオブジェクトの形で返します。

rng.InsertFile "test.docx"
Set rng = [ドキュメントの末尾]
rng.InsertBreak wdPageBreak
Set rng = [ドキュメントの末尾]

 ファイルの取り込みと改ページコードの挿入は、上のように記述します。

 Selectionのときと比べて煩雑ですが、暗黙のルールに依存しないという点で安心感があるかも?

 ところで、改ページするときに気になる点があります。

 まだページ内に余白がいっぱいあるのに改ページしてしまうことへの抵抗感。

 すでにページの2/3(66.7%)が使われているなら改ページ、
そうでなければ空白行を1行挿入するだけ、そんなふうにしようとおもいます。

 merge03.wsf では、そうした条件付き改ページを行っています。

 以下にその主要部分を掲げます。

Include "const_wd.vbs"
mergeFile = "merge03.docx"
[ドキュメントを開く] mergeFile
wildName = ".\SampleData\*.docx"
fileList = [ファイル名の一覧をソート](wildName, "名前")
lastNum = UBound(fileList)  ' 該当ファイルの最後の番号
Set rng = [ドキュメント].Range(0, 0)  ' 文書の先頭位置をRangeに
For i = 0 To lastNum-1
    rng.InsertFile fileList(i)
    Set rng = [ドキュメントの末尾]
    pct = ShareInPage(rng)
    If pct >= 66.7 Then
        rng.InsertBreak wdPageBreak  ' 改ページ
    Else
        rng.Text = vbNewLine  ' 空白行
    End If
    Set rng = [ドキュメントの末尾]
Next
rng.InsertFile fileList(lastNum)
[ドキュメントを保存]
[ワードを終了]

' 焦点(Range)のページ内の位置をパーセンテージで返す
Function ShareInPage(rng)
    Dim sn, totalPoint, currentPoint, pct
    sn = rng.Information(wdActiveEndSectionNumber)
    totalPoint = rng.Document.Sections.Item(sn).PageSetup.PageHeight
    currentPoint = rng.Information(wdVerticalPositionRelativeToPage)
    pct = currentPoint / totalPoint * 100.0
    ShareInPage = pct
End Function

 関数 ShareInPage(rng) は、引数として与えられたRangeが
ページ内でどれくらいの位置にあるかをパーセンテージで返すものです。

 A4サイズの縦方向のポイント数は 800超だとおもいますが、
rngの位置が600ポイントくらいだと、ページの半分よりも下に位置します。

 ポイント数の割り出しには PageSetup とか Information などを用いています。

 その詳細については、興味があるようでしたらWebで検索してみてください。

 厳密にはページのヘッダー・フッターの余白も考慮に入れてパーセンテージを出すべきですが、面倒なのでやってません。


[目次へ]

5. Includeの探索パス

 Include "const_wd.vbs" と書いた場合、
次の順番でパスを探索します。

 パブリックフォルダ、あるいはパブリックフォルダの下の juse というフォルダに自作のスクリプトを置いておけば、どのフォルダからでもそれを Include できることになります。

~ 以上 ~

Copyright (C) T. Yoshiizumi, 2020 All rights reserved.