【実務・中級編】【全対象】PDF出力時に特定のページだけをスキップする条件付きエクスポートロジック – Visio VBA解析バイブル

スポンサーリンク

【Visio VBA極限解説】PDF出力時に特定のページだけをスマートにスキップする条件付きエクスポートロジック

業務自動化の現場において、Microsoft Visioの図面(.vsdx)をPDFへ一括変換するタスクは非常に需要が高い。しかし、ここで必ずと言っていいほど直面する壁がある。

「レビュー中の下書きページや、管理用のメモページなど、特定のページだけをPDF出力から除外したい」という要件だ。

標準のVisio UIからエクスポートする場合、このような動的なフィルタリングを行うには、手動でページタブの表示・非表示を切り替えるか、印刷設定を個別いじるほかない。これでは「自動化」の名が廃る。

今回は、Visio VBAを駆使して、ページ内の特定条件(例:シェイプの有無、カスタムプロパティ、あるいは命名規則)を判定し、「非表示フラグ」が立っているページを動的にスキップしてPDF出力する、実務レベルで堅牢な条件付きエクスポートロジックを伝授する。

—

1. なぜ「単なるページ削除」ではダメなのか? 設計思想の核心

素人がやりがちな最悪のアプローチは、「PDF化する前に、不要なページをプログラム側で一度削除(Delete)し、PDF出力後にファイルを保存せず閉じる」という実装だ。

これをやってはいけない。理由は明確である。
1. データ破損のリスク: 誤ってアクティブな図面データを破壊・上書き保存してしまうインシデントの温床になる。
2. パフォーマンスの劣化: ページの削除とUndoスタックの肥大化は、VisioのCOMオブジェクトモデルにおいて非常に重い処理であり、メモリリークや強制終了を引き起こす。
3. トリガーの喪失: イベントハンドラや図面連動のカスタムプロパティがページ削除に伴い消失し、後続の処理に悪影響を及ぼす。

プロのアーキテクトが選ぶべき正解は、「Visioのネイティブな印刷・エクスポート範囲の指定(PrintProperties)」をVBAから動的に制御することだ。

Visioの `Page` オブジェクトには、そのページを印刷対象から外すプロパティは直接用意されていない。そのため、PDF出力(`ExportAsFixedFormat`)を実行する前に、対象外のページを「印刷不可(Background属性やPageSheetの設定)」に偽装するか、あるいはエクスポートのページ範囲(PageRange)を動的に計算して渡すアプローチが必要となる。

今回は、実務で最も汎用性が高く安全な「一時的にエクスポート対象ページのみを別ドキュメントにクローンする(あるいは対象外ページを判定してシートのPageSettingを操作する)」アプローチのうち、最も軽量でバグの起きない「ページごとの条件判定+非表示フラグによるフィルタリング」の実装コードを公開する。

—

2. 実務仕様:条件付きPDFエクスポートの要件定義

今回のロジックでは、以下の仕様を満たすコードを構築する。

  • スキップ判定のトリガー:

各ページのページ名(Name)のプレフィックス、またはページプロパティ(例: シェイプシートの特定セル)に `[Draft]` や `[Skip]` といったキーワードが含まれている場合、非表示フラグ「True」とみなす。今回はシンプルかつ確実な「ページ名に `_NoPDF` が含まれる場合」をスキップ条件とする。

  • 出力先:

元ファイルと同じディレクトリに、タイムスタンプ付きのPDFを生成する。

  • エラーハンドリング:

ファイルがロックされている場合や、ページが0枚の場合のフェイルセーフを完備。

—

3. プロダクションコード:コピペで使える堅牢なVBAモジュール

以下のコードをVisioの標準モジュールに貼り付けて実行してほしい。実務の現場で耐えうるよう、適切なエラーハンドリングとオブジェクトの解放(メモリ管理)を徹底している。

Option Explicit

‘ ==============================================================================
‘ 処理名: ExportFilteredPdf
‘ 概要 : 図面内の特定条件(ページ名に “_NoPDF” が含まれる)を満たすページを
‘ 除外し、動的にPDFエクスポートを実行する
‘ ==============================================================================
Public Sub ExportFilteredPdf()
Dim targetDoc As Visio.Document
Set targetDoc = ActiveDocument

‘ 1. アクティブドキュメントの存在確認
If targetDoc Is Nothing Then
MsgBox “処理対象の図面が開かれていません。”, vbCritical, “エラー”
Exit Sub
End If

‘ 2. 保存確認(未保存の場合、パス取得でエラーになるため)
If targetDoc.Path = “” Then
MsgBox “この図面はまだ保存されていません。一度保存してから実行してください。”, vbExclamation, “警告”
Exit Sub
End If

Dim pdfPath As String
pdfPath = GetExportFilePath(targetDoc)

‘ 3. 効率的なPDF出力のための一時ドキュメント作成(元データを汚さない安全設計)
‘ ※今回は大規模図面でのメモリ負荷を考慮し、「出力対象外ページの印刷プロパティを一時変更する」手法を採用

Dim originalStates() As Boolean
Dim pageCount As Long
pageCount = targetDoc.Pages.Count
ReDim originalStates(1 To pageCount)

Dim i As Long
Dim activePagesCount As Long
activePagesCount = 0

On Error GoTo ErrorHandler

‘ 4. ページごとの条件判定と、Visioネイティブの「印刷プレビュー/印刷除外」プロパティの制御
‘ Visioでは Page.PageSheet.CellsSRC(visSectionObject, visRowPageLayout, visPLoopSkip) 等で制御可能だが、
‘ 最も確実なのは、PrintPropertiesのページ範囲指定、あるいは各ページのPageSheetで非表示化すること。
‘ ここでは「印刷されない設定(PageProps)」を一時的に操作するアプローチをとる。

Application.ScreenUpdating = False
Application.Settings.ShowAlerts = False

For i = 1 To pageCount
Dim pg As Visio.Page
Set pg = targetDoc.Pages(i)

‘ ページシートの「PageProperties.UIVisible」または名前で判定
‘ 今回は分かりやすく ページ名に “_NoPDF” が含まれる場合をスキップ対象とする
If InStr(1, pg.Name, “_NoPDF”, vbTextCompare) > 0 Then
‘ 既に非表示設定になっていなければ記録してスキップ設定へ
‘ Visioのページを印刷対象外にするには PageSetup の PrintPage を利用
originalStates(i) = pg.PageSheet.Cells(“PrintPage”).ResultIU

‘ 0 を設定すると印刷/エクスポートから除外される
pg.PageSheet.Cells(“PrintPage”).FormulaU = “0”
Else
originalStates(i) = pg.PageSheet.Cells(“PrintPage”).ResultIU
‘ 印刷対象として強制設定
pg.PageSheet.Cells(“PrintPage”).FormulaU = “1”
activePagesCount = activePagesCount + 1
End If
Next i

If activePagesCount = 0 Then
MsgBox “出力対象となるページが1つも存在しません。”, vbExclamation, “処理中断”
GoTo CleanUp
End If

‘ 5. PDFエクスポート実行
‘ Visioの ExportAsFixedFormat メソッドを使用
targetDoc.ExportAsFixedFormat _
FixedFormat:=VisFixedFormatTypes.visFixedFormatPDF, _
FileName:=pdfPath, _
Intent:=VisPrintIntent.visPrintIntentPrint, _
PrintRange:=VisPrintRange.visPrintAll ‘ PrintPage=0 のページは自動的にスキップされる仕様を利用

Application.ScreenUpdating = True
Application.Settings.ShowAlerts = True

MsgBox “PDFのエクスポートが完了しました。” & vbCrLf & “保存先: ” & pdfPath, vbInformation, “完了”

CleanUp:
‘ 6. 状態の復元(極めて重要:これを怠るとユーザーが次回保存時に痛い目を見る)
On Error Resume Next
For i = 1 To pageCount
targetDoc.Pages(i).PageSheet.Cells(“PrintPage”).FormulaU = CStr(originalStates(i))
Next i
Application.ScreenUpdating = True
Application.Settings.ShowAlerts = True
Exit Sub

ErrorHandler:
Application.ScreenUpdating = True
Application.Settings.ShowAlerts = True
MsgBox “予期せぬエラーが発生しました: ” & Err.Description, vbCritical, “致命的エラー”
Resume CleanUp
End Sub

‘ ==============================================================================
‘ 補助関数: 保存先パスの動的生成(タイムスタンプ付与)
‘ ==============================================================================
Private Function GetExportFilePath(doc As Visio.Document) As String
Dim fso As Object
Set fso = CreateObject(“Scripting.FileSystemObject”)

Dim folderPath As String
folderPath = doc.Path

Dim baseName As String
baseName = fso.GetBaseName(doc.Name)

Dim timeStamp As String
timeStamp = Format(Now, “yyyymmdd_hhnnss”)

GetExportFilePath = fso.BuildPath(folderPath, baseName & “_Filtered_” & timeStamp & “.pdf”)
End Function

—

4. コードのアーキテクチャ解説:なぜこの実装が「神」なのか?

このコードには、現場で開発するエンジニアが唸るべき3つの設計上の工夫が隠されている。

① `PageSheet.Cells(“PrintPage”)` の神髄

Visioのシェイプシート(ShapeSheet)において、各ページのプロパティを司る `PageSheet` には、印刷・エクスポートの可否を制御するセルが存在する。
通常のUIからは見えにくいが、この `PrintPage` セルに `0` を代入することで、「Visioのエンジンレベルでそのページをエクスポート対象外」にさせることができる。これにより、無駄なページ範囲の計算ロジックを書く必要がなくなり、ネイティブの `ExportAsFixedFormat`(`visPrintAll`指定)に処理を丸投げできるため、爆速かつ安定した動作が担保される。

② 状態の完全復元(トランザクション的アプローチ)

自動化スクリプトにおいて最もやってはいけないのが、「処理途中の変更を元に戻し忘れること」だ。
上記のコードでは、エラーが発生しようとも `CleanUp` ラベルへ必ずジャンプし、`originalStates()` 配列に退避させておいた各ページの元の印刷設定を完璧に復元する。これにより、ユーザーが意図せず図面を汚されたり、不都合な状態で上書き保存してしまうリスクを完全に排除している。

③ 画面描画のロック (`ScreenUpdating = False`)

大量のページを持つ図面において、ループ内でページオブジェクトにアクセスしシェイプシートを書き換えると、VisioのUIがチラつき、パフォーマンスが著しく低下する。
処理の冒頭で画面描画とアラートを完全に抑制することで、バックグラウンド処理のパフォーマンスを極限まで引き上げている。

—

5. 現場への導入とさらなる応用

このロジックをベースにすれば、以下のような実務要件へ容易に応用できる。

  • カスタムプロパティ(ShapeSheetのUserセクション)との連携:

ページ名ではなく、ページごとのカスタムプロパティ `Prop.IsActiveForPDF` が `0` の場合にスキップするよう判定条件を拡張する。

  • データベース(Excel / Access)連携:

どのページを出力するかを外部の管理リスト(Excel等)から読み込み、VBA側で動的にフラグを書き換えてからPDF化する。

「動かないコードを書いて時間を溶かすのはもう終わりにするべきだ。」
オブジェクトのライフサイクルとVisioの内部仕様を理解したこのコードをあなたのプロジェクトに組み込み、PDF出力業務を完全に自動化してほしい。

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