PR

Excel VBA|WindowsのcopyコマンドでPDFを簡易連結する方法【注意あり】

スポンサーリンク
この記事は約19分で読めます。

Excel VBAを使って、複数のシートをPDFとして出力し、最後にWindowsの copy /b コマンドで1つのPDFファイルにまとめる方法を紹介します。

Acrobat APIやPDFtkなどの外部ソフトを使えない環境でも試せる簡易的な方法ですが、PDFの構造によっては正常に連結できない場合があります。

そのため、正式なPDF結合処理ではなく、社内確認用・一時利用向けのサンプルとして参考にしてください。

注意:
copy /b によるPDF連結は、正式なPDF結合方法ではありません。
PDFの構造によっては、ページが正しく表示されない、ファイルが壊れる、閲覧ソフトによって結果が変わる可能性があります。
重要な書類では、Acrobat API、PDFtk、Power Automate、専用ライブラリなどの利用を検討してください。

このマクロで行う処理

このマクロでは、指定した条件に合うシートを探し、個別にPDFとして出力したあと、最後に1つのPDFファイルへ簡易連結します。

  1. 画面更新を停止して、処理速度を上げる。
  2. 「印刷」シートの前回結果をクリアする。
  3. 「データ」シートから印刷対象の店番リストを取得する。
  4. すべてのワークシートを確認する。
  5. 除外対象のシートはスキップする。
  6. B2またはC4の店番を確認し、対象シートか判定する。
  7. 印刷範囲と書式を設定する。
  8. 対象シートを一時PDFとして出力する。
  9. 出力した一時PDFを copy /b で簡易連結する。
  10. 処理が終わったら、一時PDFを削除する。

事前に確認すること

このサンプルでは、次のシート構成を想定しています。

  • データ:印刷対象となる店番リストをA列に持つシート。
  • 印刷:印刷対象となったシート名を書き出すシート。
  • その他のシート:B2またはC4に店番が入っている印刷対象候補のシート。

また、サンプルではシート保護解除用のパスワードを 1234 としています。実際に使用する場合は、ご自身の環境に合わせて変更してください。

注意:
サンプルでは説明を分かりやすくするため、シート保護のパスワードをコード内に直接記述しています。
実務で使う場合は、パスワード管理に注意してください。

① メイン処理:対象シートをPDF出力して簡易連結する

まずはメイン処理です。

すべてのシートを確認し、対象となる店番が含まれているシートだけをPDFとして出力します。最後に、作成した一時PDFを1つにまとめます。

VBAコード

Sub WriteSheetsToPrintAndMergePDFs()

    Dim ws As Worksheet
    Dim dataSheet As Worksheet
    Dim printSheet As Worksheet
    Dim fukuokaShops As Object
    Dim tempPDFs As Object
    
    Dim shopCode As Variant
    Dim shopList As Variant
    Dim excludeSheets As Variant
    
    Dim i As Long
    Dim printRow As Long
    Dim isB2 As Boolean
    Dim sheetName As String
    Dim password As String
    
    Dim tempPDFPath As String
    Dim finalPDFPath As String
    Dim printRange As Range
    
    On Error GoTo ErrHandler
    
    '=== 画面更新などを停止して処理速度を向上 ===
    Application.ScreenUpdating = False
    Application.Calculation = xlCalculationManual
    Application.DisplayAlerts = False
    Application.StatusBar = "処理を開始しています..."
    
    '=== シートをセット ===
    Set dataSheet = ThisWorkbook.Sheets("データ")
    Set printSheet = ThisWorkbook.Sheets("印刷")
    
    '=== 除外するシートリスト ===
    excludeSheets = Array("データ", "データ2", "データ3", "印刷")
    
    '=== 「印刷」シートの前回結果をクリア ===
    printSheet.Range("A2:A1000").ClearContents
    
    '=== 店番リストを辞書に格納 ===
    Set fukuokaShops = CreateObject("Scripting.Dictionary")
    
    shopList = dataSheet.Range("A2:A" & dataSheet.Cells(dataSheet.Rows.Count, 1).End(xlUp).Row).Value
    
    For i = LBound(shopList, 1) To UBound(shopList, 1)
        If Len(Trim(CStr(shopList(i, 1)))) > 0 Then
            If Not fukuokaShops.Exists(CStr(shopList(i, 1))) Then
                fukuokaShops.Add CStr(shopList(i, 1)), True
            End If
        End If
    Next i
    
    '=== 一時PDFファイルリストを作成 ===
    Set tempPDFs = CreateObject("Scripting.Dictionary")
    
    printRow = 2
    password = "1234"
    
    '=== すべてのシートを確認 ===
    For Each ws In ThisWorkbook.Worksheets
    
        isB2 = False
        sheetName = ws.Name
        Set printRange = Nothing
        
        Application.StatusBar = "処理中: " & sheetName & " を確認中..."
        
        ' 除外リストにあるシートはスキップ
        If IsSheetExcluded(sheetName, excludeSheets) Then
            GoTo NextSheet
        End If
        
        ' シート保護解除
        If ws.ProtectContents Then
            On Error Resume Next
            ws.Unprotect Password:=password
            On Error GoTo ErrHandler
        End If
        
        ' B2の店番を確認
        If Len(Trim(CStr(ws.Range("B2").Value))) > 0 Then
        
            shopCode = CStr(ws.Range("B2").Value)
            
            If fukuokaShops.Exists(shopCode) Then
                isB2 = True
                Set printRange = ws.Range("A1:T60")
            End If
        
        ' C4の店番を確認
        ElseIf Len(Trim(CStr(ws.Range("C4").Value))) > 0 Then
        
            shopCode = CStr(ws.Range("C4").Value)
            
            If fukuokaShops.Exists(shopCode) Then
                isB2 = False
                Set printRange = ws.Range("A1:M46")
            End If
        
        End If
        
        ' 印刷対象ではない場合はスキップ
        If printRange Is Nothing Then
            GoTo NextSheet
        End If
        
        ' 「印刷」シートにシート名を記録
        printSheet.Cells(printRow, 1).Value = sheetName
        printRow = printRow + 1
        
        ' 書式設定を適用
        Call ApplyFormatting(ws, printRange, isB2)
        
        ' PDFを一時出力
        tempPDFPath = Environ("TEMP") & "\" & CleanFileName(sheetName) & ".pdf"
        
        ws.ExportAsFixedFormat Type:=xlTypePDF, _
                               Filename:=tempPDFPath, _
                               Quality:=xlQualityStandard, _
                               IncludeDocProperties:=True, _
                               IgnorePrintAreas:=False, _
                               OpenAfterPublish:=False
        
        ' 一時PDFをリストに追加
        If Not tempPDFs.Exists(tempPDFPath) Then
            tempPDFs.Add tempPDFPath, tempPDFPath
        End If
        
NextSheet:
    Next ws
    
    '=== 最終PDFの保存先 ===
    finalPDFPath = Environ("USERPROFILE") & "\Documents\福岡店番データ.pdf"
    
    '=== PDFの簡易連結 ===
    If tempPDFs.Count > 0 Then
        Application.StatusBar = "PDFを簡易連結しています..."
        Call MergePDFs_Copy(tempPDFs, finalPDFPath)
    Else
        MsgBox "PDF出力対象のシートがありませんでした。", vbExclamation
    End If

ExitHandler:
    '=== 設定を必ず元に戻す ===
    Application.ScreenUpdating = True
    Application.Calculation = xlCalculationAutomatic
    Application.DisplayAlerts = True
    Application.StatusBar = False
    
    Exit Sub

ErrHandler:
    MsgBox "エラーが発生しました:" & Err.Description, vbExclamation
    Resume ExitHandler

End Sub

② 除外リストチェック関数

この関数は、現在チェックしているシートが除外対象かどうかを判定します。

たとえば、「データ」「データ2」「データ3」「印刷」など、PDF出力したくないシートをスキップするために使います。

VBAコード

Function IsSheetExcluded(sheetName As String, excludeSheets As Variant) As Boolean

    Dim i As Long
    
    IsSheetExcluded = False
    
    For i = LBound(excludeSheets) To UBound(excludeSheets)
        If sheetName = excludeSheets(i) Then
            IsSheetExcluded = True
            Exit Function
        End If
    Next i

End Function

③ copy /bを使ったPDFの簡易連結処理

この処理では、個別に出力したPDFファイルを、Windowsの copy /b コマンドで1つのファイルに連結します。

ただし、PDFの内部構造を正しく再構成する処理ではありません。PDFによっては正常に開けない場合があります。

また、通常の Shell だけで実行すると、コマンドの完了を待たずに次の処理へ進むことがあります。そこで今回は WScript.ShellRun を使い、コマンドが完了してから一時PDFを削除する形にしています。

VBAコード

Sub MergePDFs_Copy(tempPDFs As Object, outputPDF As String)

    Dim pdfList As String
    Dim tempPDF As Variant
    Dim command As String
    Dim wsh As Object
    Dim result As Long
    
    pdfList = ""
    
    ' PDFファイルのリストを作成
    For Each tempPDF In tempPDFs
        pdfList = pdfList & " """ & tempPDF & """"
    Next tempPDF
    
    ' Windowsコマンドを作成
    command = "cmd /c copy /b " & pdfList & " """ & outputPDF & """"
    
    Set wsh = CreateObject("WScript.Shell")
    
    ' 第3引数 True で、コマンドの完了を待つ
    result = wsh.Run(command, 0, True)
    
    If result = 0 Then
    
        ' コマンド成功後に一時PDFを削除
        For Each tempPDF In tempPDFs
            If Dir(CStr(tempPDF)) <> "" Then
                Kill CStr(tempPDF)
            End If
        Next tempPDF
        
        MsgBox "PDFの簡易連結が完了しました。" & vbCrLf & "保存場所:" & outputPDF, vbInformation
    
    Else
    
        MsgBox "PDFの簡易連結処理でエラーが発生しました。" & vbCrLf & _
               "一時PDFは削除していません。", vbExclamation
    
    End If

End Sub
注意:
この方法は、PDFをバイナリとして連結する簡易的な方法です。
「複数PDFのページを正しく結合する」処理とは異なるため、作成後は必ずPDFを開いて確認してください。

④ 書式設定

この処理では、PDF出力前にフォントや印刷範囲、余白などを設定します。

B2に店番があるシートと、C4に店番があるシートで印刷範囲が違う想定になっています。

VBAコード

Sub ApplyFormatting(ws As Worksheet, printRange As Range, isB2 As Boolean)

    ' フォント設定
    With printRange.Font
        .Name = "Arial"
        .Size = 12
        .Bold = True
    End With
    
    ' B2のシートのみ特別設定
    If isB2 Then
        With ws.Range("A6:T12").Font
            .Size = 14
            .Bold = True
        End With
        
        ws.Columns("S:S").AutoFit
    End If
    
    ' 印刷設定
    With ws.PageSetup
        .PrintArea = printRange.Address
        .Orientation = xlPortrait
        .PaperSize = xlPaperA4
        
        If isB2 Then
            .LeftMargin = Application.InchesToPoints(0.3)
            .RightMargin = Application.InchesToPoints(0.3)
            .TopMargin = Application.InchesToPoints(0.6)
            .BottomMargin = Application.InchesToPoints(0.6)
        Else
            .LeftMargin = Application.InchesToPoints(0.6)
            .RightMargin = Application.InchesToPoints(0.6)
            .TopMargin = Application.InchesToPoints(0.9)
            .BottomMargin = Application.InchesToPoints(0.9)
        End If
        
        .CenterHorizontally = True
        .CenterVertically = True
    End With

End Sub

⑤ ファイル名に使えない文字を除去する関数

シート名によっては、PDFファイル名に使えない文字が含まれている場合があります。

そのままPDF出力するとエラーになる可能性があるため、ファイル名として使えない文字を除去する関数を用意しておくと安全です。

VBAコード

Function CleanFileName(ByVal fileName As String) As String

    Dim invalidChars As Variant
    Dim i As Long
    
    invalidChars = Array("\", "/", ":", "*", "?", """", "<", ">", "|")
    
    For i = LBound(invalidChars) To UBound(invalidChars)
        fileName = Replace(fileName, invalidChars(i), "_")
    Next i
    
    CleanFileName = fileName

End Function

カスタマイズのポイント

保存先を変更したい場合

最終PDFの保存先は、次の部分で指定しています。

finalPDFPath = Environ("USERPROFILE") & "\Documents\福岡店番データ.pdf"

たとえばデスクトップに保存したい場合は、次のように変更できます。

finalPDFPath = Environ("USERPROFILE") & "\Desktop\福岡店番データ.pdf"

除外するシートを変更したい場合

除外するシートは、次の配列で指定しています。

excludeSheets = Array("データ", "データ2", "データ3", "印刷")

除外したいシートを増やす場合は、この中にシート名を追加してください。

印刷範囲を変更したい場合

B2に店番があるシートの印刷範囲は、次の部分です。

Set printRange = ws.Range("A1:T60")

C4に店番があるシートの印刷範囲は、次の部分です。

Set printRange = ws.Range("A1:M46")

実際の帳票レイアウトに合わせて、セル範囲を変更してください。

この方法を使うときの注意点

  • copy /b は正式なPDF結合処理ではありません。
  • PDFによっては、正常に開けない場合があります。
  • 閲覧ソフトによって表示結果が変わる可能性があります。
  • 重要書類や提出用PDFには、専用ツールの利用をおすすめします。
  • 作成後は、必ずPDFを開いて内容を確認してください。

まとめ

処理 内容
① メイン処理 印刷対象のシートを探し、個別PDFとして出力する。
② 除外リストチェック 印刷しないシートを判定してスキップする。
③ PDF簡易連結 Windowsの copy /b で複数PDFを1つのファイルに連結する。
④ 書式設定 印刷前にフォントサイズ、印刷範囲、余白を整える。
⑤ ファイル名整形 PDF出力時に使えない文字を除去する。

Excel VBAの ExportAsFixedFormat を使うと、シートをPDFとして出力できます。

さらに、Windowsの copy /b コマンドを組み合わせることで、外部ソフトを使わずに複数PDFを1つのファイルへ簡易連結できます。

ただし、この方法はPDFの内部構造を正しく結合するものではありません。重要な書類で使う場合は、PDF結合専用の方法を検討してください。

にほんブログ村 IT技術ブログ IT技術メモへ

コメント

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