【実務・中級編】【中級者】横向き(16:9)と縦向き(A4)が混在するスライドを自動判別し、向きごとに別々のPDFへ分割・保存する制御マクロ – PowerPoint VBA解析バイブル

スポンサーリンク
スポンサーリンク

混在するレイアウトを支配せよ:PowerPoint VBAによる「向き」の自動判別とPDF分離出力の設計思想

プレゼンテーション作成の現場において、我々エンジニアが直面する「カオス」は、往々にしてユーザーの不注意によって引き起こされる。

「横向きの16:9スライドの中に、なぜか数枚だけA4縦向きの資料が混ざっている……」

このような「設計思想が崩壊したファイル」を、手作業でスライドをコピーし、新しいプレゼンテーションを作り直し、PDFとして書き出す。そんな作業を強いるのは、自動化エンジニアとしての敗北である。

しかし、ここで一つ、技術的な「罠」を共有しておこう。
PowerPointの仕様上、単一のプレゼンテーションファイル内で「スライドごとに異なるページ設定(向き)」を持つことは不可能である。

「えっ、じゃあどうやって実現するんだ?」と思った君。その問いこそが、アーキテクトへの第一歩だ。
この問題の本質は、「スライドの見た目(コンテンツの比率)」と「プレゼンテーションのページ設定」の乖離をどう検知し、いかにして「論理的な分離」を行うかにある。

本稿では、スライド内のコンテンツやノート欄のメタデータをトリガーに、横向き用と縦向き用の「仮想的なセグメント」を判別し、それぞれを最適な設定の別ファイルとして抽出し、PDF化する、堅牢なプロダクトレベルの制御マクロを伝授する。

1. アーキテクチャ設計:なぜ「単純なコピー」では失敗するのか

初心者は、スライドをコピーして新しいファイルに貼り付けようとする。しかし、それでは「ページ設定(PageSetup)」が引き継がれず、縦向きのスライドが横向きのキャンバスに無理やり押し込まれ、レイアウトが崩壊する。

プロフェッショナルが採用すべき設計パターンは「Temporary Container Pattern(一時コンテナ・パターン)」だ。

設計の要件

1. メタデータの定義: スライドの向きをどう判断するか?(例:スライドノートに “Portrait” と記載がある、あるいは特定のスライドサイズ比率を検知する)
2. 分離プロセス:

  • ターゲットとなるスライドをスキャンし、グループ化する。
  • 「横向き用コンテナ」と「縦向き用コンテナ」の2つの新しいプレゼンテーション(メモリ上)を生成する。
  • 各スライドを適切なコンテナへコピーする。

3. 環境の同期: コンテナ側の `PageSetup` を、対象の向き(16:9 または A4縦)に強制的に書き換える。
4. エクスポートとクリーンアップ: PDF出力後、メモリ上のコンテナを確実に破棄する。

2. 実装:プロダクション・クオリティのコード

以下のコードは、スライドノートに `”Orientation:Portrait”` という文字列が含まれている場合に「縦向き」と判定するロジックを採用している。これは、ユーザーに明確な指示を与え、プログラムの誤判定を防ぐ、実務において最も信頼性の高い「メタデータ駆動型」のアプローチだ。

Option Explicit

' --- 定数定義: マジックナンバーを排除し、保守性を高める ---
Private Const TAG_PORTRAIT As String = "Orientation:Portrait"
Private Const PDF_EXT As String = ".pdf"

''' <summary>
''' メイン制御ルーチン
''' </summary>
Public Sub SplitPresentationByOrientation()
    Dim sourcePres As Presentation
    Dim landscapePres As Presentation
    Dim portraitPres As Presentation
    Dim sld As Slide
    Dim targetPath As String
    Dim baseFileName As String
    
    ' エラーハンドリングの開始
    On Error GoTo ErrorHandler
    
    Set sourcePres = ActivePresentation
    baseFileName = Left(sourcePres.FullName, InStrRev(sourcePres.FullName, ".") - 1)
    targetPath = sourcePres.Path & "\"
    
    ' 1. コンテナの初期化
    ' 新規プレゼンテーションを作成(メモリ上)
    Set landscapePres = Presentations.Add(WithWindow:=msoFalse)
    Set portraitPres = Presentations.Add(WithWindow:=msoFalse)
    
    ' 2. スライドの走査と振り分け
    For Each sld In sourcePres.Slides
        If IsPortrait(sld) Then
            ' 縦向きスライドの場合
            CopySlideToPresentation sld, portraitPres
        Else
            ' 横向きスライドの場合
            CopySlideToPresentation sld, landscapePres
        End If
    Next sld
    
    ' 3. コンテナのページ設定を最適化
    ' ここが重要:コピー後にコンテナの向きを物理的に合わせる
    ConfigurePageSetup landscapePres, ppSlideOrientationHorizontal
    ConfigurePageSetup portraitPres, ppSlideOrientationVertical
    
    ' 4. PDFエクスポート
    ' スライドが存在する場合のみ出力(空のファイル作成を防ぐ)
    If landscapePres.Slides.Count > 0 Then
        landscapePres.ExportAsFixedFormat targetPath & baseFileName & "_Landscape" & PDF_EXT, ppFixedFormatTypePDF
    End If
    
    If portraitPres.Slides.Count > 0 Then
        portraitPres.ExportAsFixedFormat targetPath & baseFileName & "_Portrait" & PDF_EXT, ppFixedFormatTypePDF
    End If
    
    MsgBox "処理が完了しました。出力先: " & targetPath, vbInformation
    
CleanUp:
    ' 5. オブジェクトのライフサイクル管理(メモリ解放)
    On Error Resume Next
    If Not landscapePres Is Nothing Then landscapePres.Close
    If Not portraitPres Is Nothing Then portraitPres.Close
    Set landscapePres = Nothing
    Set portraitPres = Nothing
    Set sourcePres = Nothing
    Exit Sub

ErrorHandler:
    MsgBox "致命的なエラーが発生しました: " & Err.Description, vbCritical
    Resume CleanUp
End Sub

''' <summary>
''' スライドが縦向き(Portrait)かどうかを判定する
''' </summary>
Private Function IsPortrait(targetSlide As Slide) As Boolean
    ' スライドノートに特定のタグがあるかを確認(メタデータ駆動)
    ' 判定ロジックを変更する場合はここを修正するだけで済む
    If InStr(targetSlide.NotesPage.Shapes.Placeholders(2).TextFrame.TextRange.Text, TAG_PORTRAIT) > 0 Then
        IsPortrait = True
    Else
        IsPortrait = False
    End If
End Function

''' <summary>
''' スライドを新しいプレゼンテーションにコピーする
''' </summary>
Private Sub CopySlideToPresentation(sourceSlide As Slide, destPres As Presentation)
    ' スライドをコピーして貼り付け
    ' 注意: Slide.Copy はクリップボードを経由するため、大量処理時はDoEventsでOSの負荷を逃がす
    sourceSlide.Copy
    destPres.Slides.Paste
    DoEvents 
End Sub

''' <summary>
''' プレゼンテーションのページ設定を強制適用する
''' </summary>
Private Sub ConfigurePageSetup(targetPres As Presentation, orientation As PpSlideOrientation)
    With targetPres.PageSetup
        .SlideOrientation = orientation
        ' 縦向き(A4)の場合のサイズ設定例
        If orientation = ppSlideOrientationVertical Then
            .SlideWidth = 595.27  ' A4 Width in Points
            .SlideHeight = 841.89 ' A4 Height in Points
        Else
            ' 横向き(16:9)の場合のサイズ設定例
            .SlideWidth = 960
            .SlideHeight = 540
        End If
    End With
End Sub

3. チーフアーキテクトの視点:実務でバグを防ぐための「鉄則」

このコードを現場に投入する前に、以下の3点を肝に銘じておいてほしい。

① クリップボードの競合(Race Condition)

`Slide.Copy` と `Slides.Paste` は、Windowsのクリップボードを介する。これは非常に強力だが、非常に脆い。
大量のスライドを高速でループさせると、OSのクリップボード処理が追いつかず、`Paste` に失敗してランタイムエラーを吐く。
対策として、コード内に `DoEvents` を挟んでいる。これは単なる「おまじない」ではない。OSのメッセージキューに制御を戻し、クリップボードの更新を待つための、極めて合理的な設計だ。

② メモリリークの防止(Object Lifecycle)

`Presentations.Add` で生成したオブジェクトは、明示的に `.Close` し、`Set … = Nothing` で解放しなければならない。
特に大規模なスライドデッキを扱う場合、メモリ上に複数の「見えないプレゼンテーション」が蓄積され、最終的にPowerPoint自体がクラッシュする原因となる。`CleanUp` セクションでの解放は、エンジニアとしての最低限の礼儀である。

③ 判定ロジックの疎結合化

今回は「スライドノートの文字列」を判定基準とした。なぜか?
それは、「スライドの見た目(図形)から向きを判定する」というロジックは、極めて不安定だからだ。
図形が配置されている範囲だけで判定しようとすると、余白が多いスライドを誤判定するリスクがある。
「ユーザーにメタデータ(ノート欄)を書き込ませる」という運用ルールをセットで提示すること。これが、「プログラムに無理をさせない、堅牢なシステム設計」の極意である。

結びに代えて

自動化とは、単に「作業を速くすること」ではない。
「不確実な要素を排除し、再現可能なプロセスを構築すること」だ。

今回のコードは、そのための「型」を提供した。これをベースに、ファイル名にタイムスタンプを付与したり、DBに処理ログを書き出したりと、君自身の設計思想を肉付けしていってほしい。

健闘を祈る。

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