【テクニカル・上級編】【中級者】横向き(16:9)と縦向き(A4縦)のスライドが混在するプレゼンテーションを検知し、向きごとに別々のPDFへ分割・保存する制御マクロ – PowerPoint VBA解析バイブル

スポンサーリンク

【PowerPoint VBA極限解説】横向き・縦向き混在スライドを自動検知し、向き別にPDF分割出力するアーキテクチャ

プレビューを行わず、ただ機械的に一括PDF化するだけのVBAマクロは、実務の現場ではすでに「時代遅れ」だ。

近年の企画書や提案書では、一般的な「16:9(横向き)」の製品紹介スライドのなかに、詳細なデータテーブルやフローチャートを収めた「A4縦(縦向き)」のスライドが突如として混在するケースが珍しくない。これをそのまま標準の `ExportAsFixedFormat` で単一のPDFに出力すると、ビューアでの閲覧性が著しく損なわれるか、印刷時に致命的なレイアウト崩壊を引き起こす。

今回は、PowerPoint VBAのオブジェクトモデルの深層と、PDFレンダリングエンジンの挙動を完全に掌握し、「1つのプレゼンテーション内に混在するスライドの向きを動的に判定し、向きごとに独立したPDFファイルへ美しく分割・保存する」ための、実務直結型の極限最適化コードと設計思想を授ける。

—

1. 混在ドキュメントにおけるアーキテクチャの課題

PowerPointにおいて「スライドの向き」という概念は、実はプレゼンテーション全体(`Presentation.PageSetup`)で一律に管理されているわけではない。スライドマスターや個別のページレイアウトに依存するため、プログラムから厳密に判定するには以下のハードルを越える必要がある。

1. 全体プロパティの罠: `Presentation.PageSetup.SlideOrientation` は、あくまで「最後に設定された基準」を返すに過ぎず、混在ドキュメントの個々のスライドの向きを正確に表さない。
2. ピクセル単位の物理判定: 正確を期すには、個々のスライド(`Slide`オブジェクト)の `Master.Width` と `Master.Height`(あるいは `Slide.Design.SlideMaster`)を比較し、数学的にアスペクト比を割り出す必要がある。
3. COMオブジェクトのメモリリーク対策: ループ内で一時的なスライド操作やコレクション参照を行うと、VBAの背後でCOMコンポーネントの参照カウントが肥大化し、最悪の場合メモリ不足(エラー 7)を引き起こす。

これらを完全にクリアする実務コードを以下に提示する。

—

2. 実装コード:スライド自動判定・PDF分割出力エンジン

以下のコードをPowerPointの標準モジュールに配置し、実行してほしい。
エラーハンドリング、オブジェクトの明示的な解放、ファイルシステムの安全なパス構築まで、シニアエンジニアの現場基準で構築している。

Option Explicit

‘ ==============================================================================
‘ 処理名: 混在スライド自動検知・向き別PDF分割出力エンジン
‘ 概要 : アクティブなプレゼンテーション内のスライドの縦横比を判定し、
‘ 横向き(16:9等)と縦向き(A4等)に分類して別々のPDFとして出力する。
‘ ==============================================================================
Sub SplitPresentationByOrientationToPDF()
Dim targetPres As Presentation
Set targetPres = ActivePresentation

‘ プレゼンテーションが未保存の場合はカレントパスが取得できないためガード
If targetPres.Path = “” Then
MsgBox “このプレゼンテーションは一度保存されていません。” & vbCrLf & _
“保存してから実行してください。”, vbCritical, “致命的エラー”
Exit Sub
End If

Dim originalPath As String, baseName As String
originalPath = targetPres.Path
baseName = GetBaseFileName(targetPres.Name)

‘ 一時的な処理用として、オリジナルを汚さないクローンプレゼンテーションを生成
‘ ※非表示(msoFalse)で開くことで、画面描画のちらつき(スクリーニングコスト)を排除
Dim tempPresLandscape As Presentation
Dim tempPresPortrait As Presentation
Dim hasLandscape As Boolean: hasLandscape = False
Dim hasPortrait As Boolean: hasPortrait = False

Dim i As Long
Dim totalSlides As Long
totalSlides = targetPres.Slides.Count

‘ ステータスバーのロック(パフォーマンス最適化)
Application.ScreenUpdating = False

On Error GoTo ErrorHandler

‘ ————————————————————————–
‘ 1. スライドの向きを判定し、それぞれの新規プレゼンテーションへ複製・構築
‘ ————————————————————————–
For i = 1 To totalSlides
Dim currentSlide As Slide
Set currentSlide = targetPres.Slides(i)

‘ 幅と高さを比較し、縦横を判定 (Width > Height なら横向き)
Dim sWidth As Single, sHeight As Single
sWidth = currentSlide.Master.Width
sHeight = currentSlide.Master.Height

If sWidth >= sHeight Then
‘ 横向き処理
If tempPresLandscape Is Nothing Then
Set tempPresLandscape = Application.Presentations.Add(msoFalse)
‘ 基準のページ設定をコピー
tempPresLandscape.PageSetup.SlideWidth = sWidth
tempPresLandscape.PageSetup.SlideHeight = sHeight
End If
currentSlide.Copy
tempPresLandscape.Slides.Paste
hasLandscape = True
Else
‘ 縦向き処理
If tempPresPortrait Is Nothing Then
Set tempPresPortrait = Application.Presentations.Add(msoFalse)
‘ 基準のページ設定をコピー
tempPresPortrait.PageSetup.SlideWidth = sWidth
tempPresPortrait.PageSetup.SlideHeight = sHeight
End If
currentSlide.Copy
tempPresPortrait.Slides.Paste
hasPortrait = True
End If

‘ ループごとのメモリ解放
Set currentSlide = Nothing
Next i

‘ ————————————————————————–
‘ 2. 分類されたプレゼンテーションをそれぞれPDFとしてエクスポート
‘ ————————————————————————–
Dim outPathLandscape As String
Dim outPathPortrait As String

If hasLandscape And Not (tempPresLandscape Is Nothing) Then
outPathLandscape = originalPath & “\” & baseName & “_横向き.pdf”
Call ExportToPDFSecure(tempPresLandscape, outPathLandscape)
End If

If hasPortrait And Not (tempPresPortrait Is Nothing) Then
outPathPortrait = originalPath & “\” & baseName & “_縦向き.pdf”
Call ExportToPDFSecure(tempPresPortrait, outPathPortrait)
End If

‘ 終了処理
Application.ScreenUpdating = True
MsgBox “PDFの分割出力が正常に完了しました。”, vbInformation, “処理完了”
GoTo SafeExit

ErrorHandler:
Application.ScreenUpdating = True
MsgBox “予期せぬエラーが発生しました。” & vbCrLf & _
“エラー番号: ” & Err.Number & vbCrLf & _
“エラー内容: ” & Err.Description, vbCritical, “システムエラー”

SafeExit:
‘ ————————————————————————–
‘ 3. 厳格なオブジェクトの破棄(メモリリークの完全防止)
‘ ————————————————————————–
On Error Resume Next
If Not tempPresLandscape Is Nothing Then
tempPresLandscape.Saved = msoTrue
tempPresLandscape.Close
Set tempPresLandscape = Nothing
End If

If Not tempPresPortrait Is Nothing Then
tempPresPortrait.Saved = msoTrue
tempPresPortrait.Close
Set tempPresPortrait = Nothing
End If
Set targetPres = Nothing
Application.CutCopyMode = False
End Sub

‘ ==============================================================================
‘ 補助関数: 拡張子を除いたファイル名を取得
‘ ==============================================================================
Private Function GetBaseFileName(ByVal fullName As String) As String
Dim dotPos As Long
dotPos = InStrRev(fullName, “.”)
If dotPos > 0 Then
GetBaseFileName = Left(fullName, dotPos – 1)
Else
GetBaseFileName = fullName
End If
End Function

‘ ==============================================================================
‘ 補助サブルーチン: 安全なPDFエクスポート実行
‘ ==============================================================================
Private Sub ExportToPDFSecure(ByRef pres As Presentation, ByVal filePath As String)
‘ 既存ファイルが存在する場合は上書きのために一旦削除を試みる
If Dir(filePath) <> “” Then
Kill filePath
End If

pres.ExportAsFixedFormat _
Path:=filePath, _
FixedFormatType:=ppFixedFormatTypePDF, _
Intent:=ppFixedFormatIntentPrint, _
Collate:=msoTrue, _
HandoutOrder:=ppPrintHandoutVerticalFirst, _
OutputType:=ppPrintOutputSlides, _
PrintHiddenSlides:=msoFalse, _
RangeType:=ppPrintAll, _
SlideShowName:=””, _
IncludeDocProperties:=True, _
KeepIRMSettings:=True, _
DocStructureTags:=True, _
BitmapMissingFonts:=True, _
UseISO19005_1:=False
End Sub

—

3. チーフアーキテクトが解説するコードの急所

このコードが「単なるおもちゃのマクロ」ではなく「エンタープライズ仕様」である所以を、以下の3点から解説する。

① `Application.ScreenUpdating = False` による描画コストの排除

何十枚ものスライドを動的に `Copy` / `Paste` する際、GUIが裏で画面を再描画していると、処理速度が何倍にも跳ね上がる。さらに、Officeアプリケーション特有の「画面がカクつく・フリーズしたように見える」現象を防ぐため、処理の最初と最後に明示的な描画抑制を入れている。

② 一時プレゼンテーション(`Presentations.Add(msoFalse)`)の活用

元のプレゼンテーションを直接破壊・削除してPDF化するアプローチは、プログラムが途中で異常終了した際にデータ破損(Corrupt)を招くリスクがある。
メモリ上に完全な「非表示のサンドボックス(一時ファイル)」を生成し、そこにスライドを選択的に流し込むことで、オリジナルデータを絶対に汚さない堅牢性を担保している。

③ 徹底的なCOMオブジェクトの解放

VBA開発者が最も陥りがちな罠が、変数への `Nothing` 代替忘れによるガベージコレクションの失敗だ。特に `Presentation` オブジェクトや `Slide` オブジェクトは、参照が残ったままマクロが終了すると、バックグラウンドプロセス(`POWERPNT.EXE`)がゾンビとして残り続け、次回の実行時にファイルロックエラーを引き起こす。
`SafeExit` ラベルを用意し、エラー発生時であっても確実にオブジェクトを解放する構造(RAIIパターンのVBA的解釈)を実装している。

—

4. さらなる高みへ:システム間連携を見据えた拡張

このVBAマクロをベースに、さらにシステム管理者や上級エンジニアが拡張すべき方向性を提示する。

  • 完全無人化(ヘッドレス実行): Windowsのタスクスケジューラから、VBScript経由でこのPowerPointマクロをサイレント実行させることで、「社内ポータルにアップロードされた雑多なプレゼンを、深夜にバッチ処理ですべて向き別PDFに自動変換して共有サーバーに仕分ける」といったフルオートメーションパイプラインが構築可能だ。
  • ログ出力機構の追加: どのファイルが何枚の横向き・縦向きスライドを含んでいたかをJSONやCSVで吐き出すロガーを組み込むと、ドキュメントの品質監査(ガバナンスチェック)ツールとしても機能する。

現場の泥臭い課題を、洗練されたアーキテクチャと正確無比なコードで美しく解決する――これぞ、プロフェッショナルなVBAエンジニアの真骨頂である。

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