Excel VBAを活用したWord差し込み印刷結果を個別PDFで保存する手順

Excel VBAからWordの差し込み印刷を操作し、宛先ごとにPDFを保存するには、1レコードずつ新規文書へ差し込み、その結果文書をPDF化します。全件を一度に差し込んでも、通常は人数分のWord文書ができるわけではありません。本記事では、Windows版デスクトップExcel・Wordを使い、元文書と既存PDFを上書きしない構成を紹介します。実データの前に、必ず少数の架空データで内容・件数・最終レコードまで確認してください。

目次

ExcelとWordの連携基礎

Excel VBAからWordを操作するには、まずExcel VBAでWordオブジェクトモデルを理解し、利用する必要があります。これにより、ExcelからWord文書を開いたり、編集したり、印刷命令を出したりすることが可能になります。

Wordオブジェクトモデルの基本

Excel VBAでWordを操作するためには、Wordアプリケーション自体をVBAから制御する必要があります。以下は、専用のWordを起動してテスト文書を作り、確認後に保存せず閉じる基本例です。ExcelのVBEで標準モジュールに貼り付けます。遅延バインディングのため、Wordへの参照設定は不要です。

Sub WordBasicsDemo()
    Dim wdApp As Object, wdDoc As Object
    Dim failure As String
    On Error GoTo Failed
    Set wdApp = CreateObject("Word.Application")
    wdApp.Visible = True
    Set wdDoc = wdApp.Documents.Add
    wdDoc.Content.InsertAfter "これはテスト文書です。"
    With wdDoc.Paragraphs(1).Range
        .Font.Name = "Arial"
        .Font.Size = 12
        .Bold = True
    End With
    MsgBox "テスト文書を確認してください。OKで保存せず閉じます。"
    GoTo CleanUp
Failed:
    failure = Err.Description
    Resume CleanUp
CleanUp:
    On Error Resume Next
    If Not wdDoc Is Nothing Then wdDoc.Close 0
    Set wdDoc = Nothing
    If Not wdApp Is Nothing Then
        If wdApp.Documents.Count = 0 Then wdApp.Quit 0
    End If
    Set wdApp = Nothing
    On Error GoTo 0
    If Len(failure) > 0 Then MsgBox failure, vbExclamation
End Sub

このコードは、Wordアプリケーションを開始し、新しい文書を追加する基本的なフレームワークを提供します。wdAppオブジェクトを通じてWordアプリケーションにアクセスし、wdDocオブジェクトで具体的な文書を操作します。

Word文書へのアクセスと操作

Word文書を操作するには、文書内のテキストの挿入、フォーマットの変更、テーブルや画像の挿入など、多様な操作が可能です。次は上の手順で文書を開いている間に行う処理の抜粋です。単独で実行するコードではなく、wdDocが有効な文書を参照している必要があります。終了時はその文書と、自分で起動したWordだけを後始末します。

' テキストの挿入
wdDoc.Content.InsertAfter "これはテスト文書です。"

' テキストのスタイル設定
With wdDoc.Paragraphs(1).Range
    .Font.Name = "Arial"
    .Font.Size = 12
    .Bold = True
End With

これらの基本をマスターすることで、Excel VBAからWord文書を効率的に操作し、自動化の第一歩を踏み出すことができます。次のセクションでは、これらの操作を応用し、差し込み印刷とPDF保存の自動化に進んでいきます。

差し込み印刷の準備

差し込み印刷を行う前に、対象となるWord文書と、差し込むデータが格納されているExcelシートの準備が必要です。この過程では、Word文書に差し込みフィールドを設定し、Excelデータとどのように連携させるかを定義します。

Word文書の設定

差し込み印刷用のWord文書を準備する際、まずは差し込みフィールドを文書内に挿入することが重要です。差し込みフィールドは、実際に差し込むデータに対応する場所を文書上で指定するためのマーカーです。例えば、顧客名や住所など、個別に変更される部分に差し込みフィールドを設定します。

  1. Word文書を開き、「差し込み文書」タブを選択します。
  2. 「差し込み印刷の開始」で「手紙」を選択します。本記事のコードは1レコードが1通になる手紙用で、ラベルや名簿・ディレクトリ形式は対象外です。
  3. 「宛先の選択」で、保存済みのExcelデータファイルと対象のシート・テーブルを指定します。「宛先の編集」で絞り込み、並べ替え、チェックが意図どおりか確認します。
  4. 「差し込みフィールドの挿入」で顧客名などを配置します。通常のMERGEFIELDを中心とした手紙テンプレートを使い、NEXT・NEXTIF・SKIPIF、複雑な入れ子フィールドや外部文書の取り込みは使用しないでください。接続情報を含めて.docxで保存し、閉じます。

Excelデータの準備

Excel側では、Word文書に差し込むデータを適切に準備します。各列には、Wordの差し込みフィールドに対応するデータが格納されている必要があります。例えば、顧客の名前、住所、注文日など、文書に差し込みたい情報を列ごとに整理します。

  1. データ用の.xlsxと、マクロを保存する.xlsmを分けて用意します。データは1行目を見出し、2行目以降を1行1レコードとし、保存して閉じます。処理中はデータ・宛先の順序・選択状態を変更しないでください。
  2. 各列の先頭には、差し込みフィールドに対応する名前をヘッダーとして設定します。これにより、Wordで差し込みフィールドを設定する際に、どのデータを使用するかが明確になります。
  3. 顧客ID、氏名、住所などの整合性と空行を確認します。郵便番号・IDの先頭ゼロ、日付、金額はWord側のプレビューでも確認してください。Excelの表示形式がそのまま差し込まれるとは限りません。

これらの準備作業を完了させることで、Word文書とExcelデータの連携がスムーズに行われ、差し込み印刷プロセスの基盤が整います。次のステップでは、これらを活用して実際に差し込み印刷を自動化し、結果をPDFとして保存する方法を見ていきます。

Wordで元文書を開き直し、先頭・途中・最終の宛先をプレビューして、接続先と内容を確認してから閉じます。SQLやデータ接続の確認が出たら、対象ファイルを確認してください。警告・保護ビュー・マクロ設定を無条件に無効化する必要はありません。信頼できる文書と、書き込み権限のあるローカル保存先だけで試してください。Web版OfficeやMac版向けのコードではありません。

VBAによる差し込み印刷の自動化

Excel VBAを使用してWordの差し込み印刷を自動実行し、その結果をPDFファイルとして保存するプロセスは、業務の効率化に大きく貢献します。ここでは、そのためのVBAスクリプトの基本構造を説明します。

自動化スクリプトの概要

自動化のためには、以下のステップをVBAコードで実装します。

  1. Wordオブジェクトの生成と設定: 最初にWordアプリケーションを開き、差し込み印刷を行う文書を開く必要があります。
  2. 差し込みデータソースの確認: Wordに保存済みのExcel接続・宛先の選択を使います。コードで接続先を決め打ちして、別のシートや全件に切り替えないようにします。
  3. 1件ずつ差し込み: FirstRecordとLastRecordを同じ番号にし、出力先を新規文書にして実行します。プリンターには出力しません。
  4. PDFとして保存: 元文書ではなく、新しく生成された結果文書をExportAsFixedFormatで保存し、その結果文書を閉じます。

基本的なVBAコード例

実装の中心は次の処理です。これは後述の完成例からの抜粋で、mainDoc・ds・wdApp・i・保存先などの初期化は省略しています。実行には次節の完成例をまとめて使ってください。

' 完成例の処理を理解するための抜粋。単独実行不可。
ds.ActiveRecord = i
ds.FirstRecord = 1
ds.LastRecord = i
ds.FirstRecord = i
mainDoc.MailMerge.Destination = 0 ' wdSendToNewDocument
mainDoc.MailMerge.Execute True
Set mergedDoc = wdApp.ActiveDocument
mergedDoc.ExportAsFixedFormat pdfPath, 17, False ' wdExportFormatPDF
mergedDoc.Close 0 ' wdDoNotSaveChanges

ActiveRecordは現在のデータ表示位置であり、それだけでは実行範囲を1件に制限しません。FirstRecord = LastRecord = iの指定が重要です。Wordの定数はExcelからの遅延バインディングでは自動定義されないため、完成例では必要な数値定数を宣言しています。

一括差し込みの結果をDocuments.Countで人数分に分けることはできません。元文書もコレクションに含まれ、文書を閉じるとインデックスも変わります。本記事ではレコード番号を反復し、各回に生成した1文書だけを扱います。

PDFとして保存

差し込み印刷されたWord文書を個別のPDFファイルとして保存するプロセスは、文書の共有やアーカイブに非常に便利です。Excel VBAを使用してこのプロセスを自動化する方法を詳しく見ていきましょう。

個別PDF保存の自動化

以下をExcelの1つの標準モジュールにまとめて貼り付けます。mainPathを準備済みの差し込み元.docx、outputRootを既存の保存先フォルダーに変更してください。Option Explicitはモジュールの先頭に1回だけ置きます。保存済みの宛先のうちチェックが付いたレコードを対象とし、1通が複数ページでもその全体を1つのPDFへ出力します。

Option Explicit

Public Sub SaveAsIndividualPDFs()
    Const wdSendToNewDocument As Long = 0
    Const wdFormLetters As Long = 0
    Const wdMainAndDataSource As Long = 2
    Const wdMainAndSourceAndHeader As Long = 4
    Const wdExportFormatPDF As Long = 17
    Const wdDoNotSaveChanges As Long = 0

    Dim wdApp As Object, mainDoc As Object, mergedDoc As Object
    Dim ds As Object, fso As Object
    Dim selectedRecords As Collection
    Dim mainPath As String, outputRoot As String
    Dim runFolder As String, pdfPath As String
    Dim recordCount As Long, i As Long, savedCount As Long
    Dim recordNumber As Variant
    Dim failure As String, cleanupNote As String
    Dim completed As Boolean

    On Error GoTo Failed
    mainPath = "C:\MergeDemo\Letter.docx"
    outputRoot = "C:\MergeDemo\PDF"
    Set fso = CreateObject("Scripting.FileSystemObject")

    If Not fso.FileExists(mainPath) Then
        Err.Raise vbObjectError + 1001, , "差し込み元文書がありません。"
    End If
    If Not fso.FolderExists(outputRoot) Then
        Err.Raise vbObjectError + 1002, , "保存先の親フォルダーがありません。"
    End If

    ' 既に開いているWordには接続しない。
    Set wdApp = CreateObject("Word.Application")
    wdApp.Visible = True
    ' 引数順: FileName, ConfirmConversions, ReadOnly, AddToRecentFiles
    Set mainDoc = wdApp.Documents.Open(mainPath, False, True, False)

    With mainDoc.MailMerge
        If .MainDocumentType <> wdFormLetters Then
            Err.Raise vbObjectError + 1003, , "手紙形式の差し込み元文書が必要です。"
        End If
        If .State <> wdMainAndDataSource And _
           .State <> wdMainAndSourceAndHeader Then
            Err.Raise vbObjectError + 1004, , "保存済みのデータソース接続を確認してください。"
        End If
        Set ds = .DataSource
    End With
    ' NEXT/NEXTIF/SKIPIFがあるテンプレートは使用しない。
    CheckRecordControlFields mainDoc

    recordCount = ds.RecordCount
    If recordCount < 1 Then
        Err.Raise vbObjectError + 1005, , _
            "件数が0または不明です。Wordの宛先一覧と接続を確認してください。"
    End If

    ' Wordの現在の検索・並べ替え結果に対する番号を保存する。
    Set selectedRecords = New Collection
    For i = 1 To recordCount
        ds.ActiveRecord = i
        If ds.ActiveRecord <> i Then
            Err.Raise vbObjectError + 1006, , "指定レコードを選択できません。"
        End If
        If ds.Included Then selectedRecords.Add i
    Next i
    If selectedRecords.Count = 0 Then
        Err.Raise vbObjectError + 1007, , "選択されている宛先がありません。"
    End If

    If MsgBox("接続先: " & ds.Name & vbCrLf & _
              "選択された " & selectedRecords.Count & " 件をPDFにします。" & vbCrLf & _
              "宛先・絞り込み条件・テスト結果を確認済みですか?", _
              vbOKCancel + vbQuestion, "実行前の確認") <> vbOK Then
        GoTo CleanUp
    End If

    ' 毎回新しいフォルダーに出す。前回のPDFは再利用しない。
    runFolder = CreateNewRunFolder(fso, outputRoot)
    mainDoc.MailMerge.Destination = wdSendToNewDocument
    ds.FirstRecord = 1
    ds.LastRecord = recordCount

    For Each recordNumber In selectedRecords
        i = CLng(recordNumber)
        If ds.RecordCount <> recordCount Then
            Err.Raise vbObjectError + 1008, , "処理中にデータ件数が変わりました。"
        End If
        ds.ActiveRecord = i
        If ds.ActiveRecord <> i Or Not ds.Included Then
            Err.Raise vbObjectError + 1009, , "宛先の選択状態が変わりました。"
        End If
        ' ActiveRecordだけでは差し込み範囲は限定されない。
        ds.LastRecord = i
        ds.FirstRecord = i

        pdfPath = fso.BuildPath(runFolder, _
                    "Record_" & Format$(i, "000000") & ".pdf")
        If fso.FileExists(pdfPath) Or fso.FolderExists(pdfPath) Then
            Err.Raise vbObjectError + 1010, , "同名の保存先があるため停止します。"
        End If
        If wdApp.Documents.Count <> 1 Then
            Err.Raise vbObjectError + 1011, , "専用Wordに別の文書が開いています。"
        End If

        mainDoc.Activate
        ' True: 差し込みエラー時にWordの確認画面で停止する。
        mainDoc.MailMerge.Execute True
        If wdApp.Documents.Count <> 2 Then
            Err.Raise vbObjectError + 1012, , "差し込み結果を1文書として取得できません。"
        End If
        Set mergedDoc = wdApp.ActiveDocument
        If mergedDoc Is mainDoc Then
            Set mergedDoc = Nothing
            Err.Raise vbObjectError + 1013, , "差し込み元文書はPDFにしません。"
        End If

        ' 引数順: OutputFileName, ExportFormat, OpenAfterExport
        mergedDoc.ExportAsFixedFormat pdfPath, wdExportFormatPDF, False
        If Not fso.FileExists(pdfPath) Then
            Err.Raise vbObjectError + 1014, , "PDFの作成を確認できません。"
        End If
        If fso.GetFile(pdfPath).Size = 0 Then
            Err.Raise vbObjectError + 1015, , "空のPDFが作成されました。"
        End If
        savedCount = savedCount + 1
        mergedDoc.Close wdDoNotSaveChanges
        Set mergedDoc = Nothing
    Next recordNumber
    completed = True
    GoTo CleanUp

Failed:
    failure = "エラー " & Err.Number & ": " & Err.Description
    Resume CleanUp

CleanUp:
    ' 通常処理のエラーを隠さず、後始末のときだけ続行する。
    On Error Resume Next
    Set ds = Nothing
    If Not mergedDoc Is Nothing Then
        Err.Clear
        mergedDoc.Close wdDoNotSaveChanges
        If Err.Number <> 0 Then cleanupNote = "結果文書を閉じられませんでした。"
    End If
    Set mergedDoc = Nothing
    If Not mainDoc Is Nothing Then
        Err.Clear
        mainDoc.Close wdDoNotSaveChanges
        If Err.Number <> 0 Then cleanupNote = cleanupNote & " 元文書を閉じられませんでした。"
    End If
    Set mainDoc = Nothing
    If Not wdApp Is Nothing Then
        Err.Clear
        ' 所有を確認できない文書が残っていたら、勝手に閉じない。
        If wdApp.Documents.Count = 0 Then
            wdApp.Quit wdDoNotSaveChanges
        Else
            wdApp.Visible = True
            cleanupNote = cleanupNote & " Wordに残った文書を確認して手動で閉じてください。"
        End If
        If Err.Number <> 0 Then cleanupNote = cleanupNote & " Wordの終了状態を確認してください。"
    End If
    Set wdApp = Nothing
    Set fso = Nothing
    Set selectedRecords = Nothing
    On Error GoTo 0

    If Len(failure) > 0 Then
        MsgBox failure & vbCrLf & "保存確認済み: " & savedCount & " 件" & _
               vbCrLf & "出力先: " & runFolder & vbCrLf & _
               "途中のPDFは残しています。配布前に確認してください。" & _
               vbCrLf & cleanupNote, vbExclamation
    ElseIf completed Then
        MsgBox savedCount & " 件を保存しました。" & vbCrLf & runFolder & _
               vbCrLf & "内容と宛先を確認してから配布してください。" & _
               vbCrLf & cleanupNote, vbInformation
    Else
        MsgBox "キャンセルしました。" & vbCrLf & cleanupNote, vbInformation
    End If
End Sub

Private Function CreateNewRunFolder(ByVal fso As Object, _
                                     ByVal root As String) As String
    Dim baseName As String, candidate As String, suffix As Long
    baseName = "Run_" & Format$(Now, "yyyymmdd_hhnnss")
    For suffix = 0 To 9999
        candidate = fso.BuildPath(root, baseName & "_" & Format$(suffix, "0000"))
        If Not fso.FolderExists(candidate) And Not fso.FileExists(candidate) Then
            ' 競合して作成に失敗した場合も、上書きせずエラーで停止する。
            fso.CreateFolder candidate
            CreateNewRunFolder = candidate
            Exit Function
        End If
    Next suffix
    Err.Raise vbObjectError + 1016, , "新しい出力フォルダーを確保できません。"
End Function

Private Sub CheckRecordControlFields(ByVal doc As Object)
    Dim firstRange As Object, currentRange As Object, fld As Object
    For Each firstRange In doc.StoryRanges
        Set currentRange = firstRange
        Do While Not currentRange Is Nothing
            For Each fld In currentRange.Fields
                Select Case fld.Type
                    Case 41, 42, 43 ' wdFieldNext, wdFieldNextIf, wdFieldSkipIf
                        Err.Raise vbObjectError + 1017, , _
                            "NEXT/NEXTIF/SKIPIFのない手紙テンプレートを使ってください。"
                End Select
            Next fld
            Set currentRange = currentRange.NextStoryRange
        Loop
    Next firstRange
End Sub

出力はRun_日時_連番という新規フォルダー内のRecord_000001.pdfなどです。番号はWordの現在の検索・並べ替え結果での位置であり、必ずしもExcelの行番号ではありません。除外された宛先の番号は欠番になります。顧客名をファイル名にしないことで、同姓同名・禁止文字による衝突を避けます。名前を追加する場合も一意のIDを残し、禁止文字・予約名・長いパスを別途処理してください。

新規フォルダー作成と既存ファイル確認で、前回のPDFを黙って上書きしない設計です。ただし、存在確認と書き込みの間の同時書き込みまでは排他制御していないため、作成中の出力フォルダーを別の処理から使用しないでください。PDFが存在することだけでは内容の正しさは検証できません。出力件数、先頭・途中・最終の宛先、複数ページ文書、文字化け、日付、PDFの文書プロパティを確認してから配布します。

エラー処理と最適化

自動化スクリプトを実行する際には、エラーが発生する可能性が常にあります。また、スクリプトの実行速度を向上させることで、全体の処理効率を高めることが可能です。このセクションでは、VBAスクリプトにおけるエラー処理の方法と実行速度の最適化について説明します。

エラー処理の基本

VBAにおけるエラー処理は、On Errorステートメントを使用して行います。これにより、エラーが発生した場合の挙動を定義することができます。エラー処理の基本的な方法としては、エラーが発生した際にユーザーに警告を表示し、安全に処理を終了させる方法があります。

Sub SampleSub()
    On Error GoTo ErrorHandler
    ' ここに通常の処理を記述

    Exit Sub
ErrorHandler:
    MsgBox "エラーが発生しました: " & Err.Description, vbCritical
    ' 必要に応じて追加のエラー処理をここに記述
End Sub

この短い例はエラー通知だけを示します。Wordを起動する処理には通知だけでは足りず、完成例のような文書・アプリケーションの後始末が必要です。元のエラー内容を退避し、通常処理全体にOn Error Resume Nextをかけないようにします。

完成例はRecordCountが0または-1(件数不明)なら開始せず、無限に次レコードを送りません。Execute Trueは差し込みエラー時にWordで確認を求めます。完全無人実行を保証するコードではありません。途中停止時は作成済みPDFを残して件数と保存先を通知し、再実行は新しいフォルダーへ出します。失敗した回の出力は、そのまま配布しないでください。

WordはCreateObjectでこの処理用に起動し、GetObjectで既存の作業中Wordへ接続しません。処理中は専用Wordで別文書を開いたり操作したりしないでください。後始末では追跡している文書だけを保存せず閉じ、他の文書が残ればWord全体を終了せず確認を促します。強制終了やEndで後始末が飛んだ場合は、Wordの残存状態を手動で確認します。

実行速度の最適化

Excel側のシート処理が重い場合には画面更新やイベントを一時停止できます。ただし、ExcelでのApplicationはExcel自身であり、Wordの画面更新や差し込み速度を直接制御するものではありません。上の完成例はExcelの状態を変更しません。必要に応じて最適化する場合も、元の状態を退避し、エラー時を含めて復元します。

Sub OptimizeSub()
    Dim oldScreen As Boolean, oldEvents As Boolean
    Dim failure As String
    oldScreen = Application.ScreenUpdating
    oldEvents = Application.EnableEvents
    On Error GoTo Failed
    Application.ScreenUpdating = False
    Application.EnableEvents = False
    ' ここに必要なExcel側の処理を記述
    GoTo CleanUp
Failed:
    failure = Err.Description
    Resume CleanUp
CleanUp:
    On Error Resume Next
    Application.EnableEvents = oldEvents
    Application.ScreenUpdating = oldScreen
    On Error GoTo 0
    If Len(failure) > 0 Then MsgBox failure, vbExclamation
End Sub

無条件に最後をTrueへ戻すのではなく、実行前の値へ戻します。Wordの確認画面が見えなくなる非表示化や警告の一括無効化を、速度改善のためだけに行わないでください。まず正しい宛先・内容・後始末を検証し、その後に測定して最適化します。

エラー処理と実行速度の最適化は、VBAスクリプトを安定して高速に実行するために重要な要素です。これらのテクニックを適切に使用することで、自動化プロセスの信頼性と効率を大きく向上させることができます。

まとめ

Excel VBAを用いたWordの差し込み印刷結果を個別のPDFファイルとして保存する方法は、大量の文書処理を効率化し、業務の自動化を実現する強力な手段です。このプロセスは、以下のステップで構成されます:

  1. ExcelとWordの連携基礎を理解し、VBAからWord文書を操作する準備をします。
  2. 差し込み印刷の準備として、Word文書に差し込みフィールドを設定し、Excelデータとの連携を確立します。
  3. VBAによる差し込み印刷の自動化を実装し、Wordの差し込み印刷を自動で実行します。
  4. PDFとして保存のステップでは、差し込み印刷された文書を個別のPDFファイルとして保存します。
  5. また、エラー処理と最適化を適切に行うことで、スクリプトの安定性を保ちつつ、実行速度を向上させることができます。

この手順を通じて、手作業による時間の浪費を削減し、文書処理の自動化を実現することが可能です。特に、顧客向けの文書を大量に処理する必要がある場合に、この方法は大きな効果を発揮します。

参考:Microsoft LearnのFirstRecord・LastRecord、ActiveRecord、RecordCount、Included、MailMerge.Execute、ExportAsFixedFormat。本記事のコードは仕様に基づく例です。Officeの版、接続方式、テンプレートによる違いがあるため、利用環境での小規模テストを省略しないでください。

この記事を書いた人

実務の現場で詰まりがちなポイントを地図にするITブログ「IT trip」を運営。Windows/Office(Teams・Excel)からSQL、サーバ運用、ガジェットまで、再現性のある手順と“なぜそうなるか”を丁寧に解説します。読んだらすぐ試せること、そして迷った人の次の一歩が見えることを大切にしています。

コメント

コメントする

目次