【実務・中級編】【中級者向け】特定のレイヤーのみを非表示にしてPDF出力する「出力用レイヤー制御」の自動化 – Visio VBA解析バイブル

スポンサーリンク

設計図面や業務フロー図、ネットワーク構成図の作成において、Microsoft Visioは今なお現場のデファクトスタンダードです。しかし、次のような不毛な手作業を繰り返していませんか?

「レビュー用の内部コメントやグリッド、開発用レイヤーを手動で非表示にし、PDFをエクスポートしたあと、また手動で元に戻す」

もしあなたのチームがこの操作を毎回手動で行っているなら、今すぐ停止させてください。ヒューマンエラーによる機密情報の流出リスクがあるだけでなく、エンジニアリングリソースの完全な浪費です。

今回は、「指定したレイヤーのみを確実に不可視・非印刷化し、PDF出力完了後に何があっても元の状態へ完全復元する」という、堅牢なレイヤー制御自動化ロジックを解説します。

—

1. なぜナイーブなスクリプトは現場で破綻するのか?

初級者が書きがちなコードは、目的のレイヤーの可視性フラグをオフにし、PDFを出力して、オンに戻すという一本道の実装です。しかし、実務のプロダクション環境では以下の問題に直面します。

① 状態の破壊(ロールバックの欠如)

もともと「非表示」だったレイヤーが、スクリプト実行後に勝手に「表示」に書き換わってしまう問題です。元の状態(Visible/Printableの各論理値)をスナップショットとして保持していなければ、出力前のドキュメント状態を100%復元することは不可能です。

② 例外発生時の「半端な状態」の残留

PDF出力中にファイルロックやディスク容量不足などのI/O例外が発生した場合、エラーハンドリングが適切でなければ、ドキュメントのレイヤーが非表示になったままマクロが停止します。ユーザーはどのレイヤーが元々どう設定されていたか分からなくなります。

③ ShapeSheetとの連動および再描画コスト

Visioのレイヤーは、内部的にはページのShapeSheetにおける `LayerProperties` セクションのセル群で管理されています。ページ数やシェイプ数が多いドキュメントで安易にセル操作を繰り返すと、激しい再描画と依存関係の再計算(Formula Recalculation)が走り、処理速度が著しく低下します。

—

2. アーキテクチャ設計:状態退避・復元(Memento)パターン

実務に耐えうるツールを構築するには、「状態の取得・保存(Capture) ➡ 変更(Mutate) ➡ 出力(Export) ➡ 確実な復元(Rollback/Restore)」をトランザクション的に完結させる構造が必要です。

[実行前状態の記録]
│ (Page / Layer毎の Visible & Print セル値をDictionaryに退避)
▼
[出力用プロパティ適用]
│ (対象レイヤーの visLayerVisible / visLayerPrint を 0 に設定)
▼
[PDFエクスポート] ── (例外発生時も確実に復元処理へジャンプ)
│
▼
[クリーンアップ & 状態復元]
(退避した値で全ページの全レイヤーを完全復元)

VisioのAPIでは、レイヤーの可視性と印刷可否はそれぞれ以下のShapeSheetセル定数に対応します。

  • `visLayerVisible` (画面表示フラグ:1 = 表示, 0 = 非表示)
  • `visLayerPrint` (印刷/出力フラグ:1 = 印刷対象, 0 = 非対象)

PDF出力(`ExportAsFixedFormat`)では、画面表示だけでなく`visLayerPrint` の設定が直接影響を与えるため、両セルを同時に制御することが重要です。

—

3. プロダクションコード:完全自動化モジュール

以下のコードをVisioの標準モジュールに配置してください。単一ページだけでなく、ドキュメント内の全前景(Foreground)ページを一括処理し、例外が発生しても元のレイヤー状態を完全に巻き戻す設計です。

Option Explicit

‘ ==============================================================================
‘ Module: Mod_ExportLayerController
‘ Description: 特定のレイヤーを不可視・非印刷に制御した上でPDFエクスポートを実行し、
‘ 処理完了後(またはエラー時)に元の状態を100%復元する。
‘ ==============================================================================

Public Sub ExportDocumentToPdfWithLayerFilter()
Dim doc As Visio.Document
Set doc = Visio.ActiveDocument

If doc Is Nothing Then
MsgBox “対象となるVisioドキュメントが開かれていません。”, vbCritical, “エラー”
Exit Sub
End If

‘ ————————————————————-
‘ 設定パラメータ
‘ ————————————————————-
‘ 非表示(出力対象外)にしたいレイヤー名のリスト(カンマ区切り、大文字小文字不問)
Const TARGET_LAYERS As String = “内部コメント,下書き,レビュー用,DebugLayer”

‘ 出力先ファイルパス(ドキュメントと同一フォルダに出力)
Dim exportPath As String
If Len(doc.Path) = 0 Then
MsgBox “ドキュメントを保存してから実行してください。”, vbExclamation, “警告”
Exit Sub
End If
exportPath = doc.Path & Replace(doc.Name, “.vsdx”, “”) & “_Export.pdf”

‘ ————————————————————-
‘ 実行
‘ ————————————————————-
Dim layerFilter() As String
layerFilter = Split(TARGET_LAYERS, “,”)

Call ExecuteExportPipeline(doc, layerFilter, exportPath)
End Sub

Private Sub ExecuteExportPipeline(ByRef doc As Visio.Document, ByRef excludeLayers() As String, ByVal outputPath As String)
Dim app As Visio.Application
Set app = Visio.Application

‘ パフォーマンス最適化:画面描画と再計算をサスペンド
Dim prevScreenUpdating As Boolean
Dim prevDeferRecalc As Long
prevScreenUpdating = app.ScreenUpdating
prevDeferRecalc = app.DeferRecalc

app.ScreenUpdating = False
app.DeferRecalc = 1

‘ 状態退避用データ構造: Key = “PageID:LayerIndex”, Value = Array(visLayerVisible, visLayerPrint)
Dim stateSnapshot As Object
Set stateSnapshot = CreateObject(“Scripting.Dictionary”)

On Error GoTo ExecutionError

‘ 1. 現在の全レイヤー状態を退避
CaptureAllLayersState doc, stateSnapshot

‘ 2. 対象レイヤーのプロパティを無効化(非表示・非印刷)
ApplyLayerMask doc, excludeLayers

‘ 3. PDFエクスポート実行
‘ visFixedFormatPDF = 1, visDocExIntentPrint = 1 (高品質印刷向け)
doc.ExportAsFixedFormat _
FixedFormat:=visFixedFormatPDF, _
OutputFile:=outputPath, _
Intent:=visDocExIntentPrint, _
PrintRange:=visPrintAll, _
ColorMode:=visPrintColor

MsgBox “PDFが正常に出力されました:” & vbCrLf & outputPath, vbInformation, “完了”

Cleanup:
‘ 4. 何があっても必ず元の状態にロールバック
On Error Resume Next
RestoreLayersState doc, stateSnapshot

‘ アプリケーション設定の復元
app.DeferRecalc = prevDeferRecalc
app.ScreenUpdating = prevScreenUpdating
On Error GoTo 0
Exit Sub

ExecutionError:
MsgBox “PDF出力処理中にエラーが発生しました。” & vbCrLf & _
“エラー番号: ” & Err.Number & vbCrLf & _
“内容: ” & Err.Description, vbCritical, “処理中断”
Resume Cleanup
End Sub

‘ ——————————————————————————
‘ 全レイヤーの状態(Visible / Print)をスナップショットに保存
‘ ——————————————————————————
Private Sub CaptureAllLayersState(ByRef doc As Visio.Document, ByRef snapshot As Object)
Dim pg As Visio.Page
Dim lyr As Visio.Layer
Dim i As Long

For Each pg In doc.Pages
‘ 背景(Background)ページは除外(前景のみ処理。要件に応じて変更可)
If pg.Background = 0 Then
For i = 1 To pg.Layers.Count
Set lyr = pg.Layers(i)
Dim key As String
key = pg.ID & “:” & lyr.Index

‘ CellsC で ShapeSheet セルの生値(整数)を取得
Dim isVisible As Integer
Dim isPrintable As Integer
isVisible = lyr.CellsC(visLayerVisible).ResultInt(visNoCast, 0)
isPrintable = lyr.CellsC(visLayerPrint).ResultInt(visNoCast, 0)

snapshot.Add key, Array(isVisible, isPrintable)
Next i
End If
Next pg
End Sub

‘ ——————————————————————————
‘ 指定された除外レイヤーを非表示・非印刷化
‘ ——————————————————————————
Private Sub ApplyLayerMask(ByRef doc As Visio.Document, ByRef excludeLayers() As String)
Dim pg As Visio.Page
Dim lyr As Visio.Layer
Dim i As Long, j As Long

For Each pg In doc.Pages
If pg.Background = 0 Then
For i = 1 To pg.Layers.Count
Set lyr = pg.Layers(i)
For j = LBound(excludeLayers) To UBound(excludeLayers)
If StrComp(Trim$(lyr.Name), Trim$(excludeLayers(j)), vbTextCompare) = 0 Then
‘ レイヤーの表示と印刷フラグをゼロ(False)に設定
lyr.CellsC(visLayerVisible).FormulaU = “0”
lyr.CellsC(visLayerPrint).FormulaU = “0”
Exit For
End If
Next j
Next i
End If
Next pg
End Sub

‘ ——————————————————————————
‘ スナップショットから元の状態へ復元
‘ ——————————————————————————
Private Sub RestoreLayersState(ByRef doc As Visio.Document, ByRef snapshot As Object)
Dim pg As Visio.Page
Dim lyr As Visio.Layer
Dim i As Long

For Each pg In doc.Pages
If pg.Background = 0 Then
For i = 1 To pg.Layers.Count
Set lyr = pg.Layers(i)
Dim key As String
key = pg.ID & “:” & lyr.Index

If snapshot.Exists(key) Then
Dim values As Variant
values = snapshot(key)
lyr.CellsC(visLayerVisible).FormulaU = CStr(values(0))
lyr.CellsC(visLayerPrint).FormulaU = CStr(values(1))
End If
Next i
End If
Next pg
End Sub

—

4. プロの勘所:アーキテクトが押さえるべき重要ポイント

① FormulaU vs ResultIntの使い分け

コード内では、状態取得に `ResultInt` を、書き戻しに `FormulaU = “0”` (または `”1″`) を使用しています。
Visioのプロパティ参照において、ローカライズされた `Formula` ではなく、ユニバーサルプロパティである `FormulaU` を使用することはグローバル開発における鉄則です。OSの言語環境(日本語、英語、多言語環境)に依存した文字列評価エラーを未然に防ぎます。

② DeferRecalc による ShapeSheet エンジンの制御

レイヤーの可視性を切り替えると、そのレイヤーに所属するすべてのシェイプに対してShapeSheetの依存計算が連鎖します。
`app.DeferRecalc = 1` を設定することで、一連のセル書き込み中の内部再計算キューを一時停止させ、無駄なCPUサイクルを劇的に削減します。

③ ページID + レイヤーIndex によるユニークキー識別

スナップショットのキーに `pg.ID & “:” & lyr.Index` を採用しています。ページ名やレイヤー名はユーザーによってリネームされたり、同名レイヤーが複数ページにまたがって存在するため、内部識別子である `ID` とレイヤーのインデックス番号を組み合わせるのが最も安全です。

—

5. 実務運用のためのネクストステップ

このロジックが完成したら、さらに現場のパイプラインへ統合するために以下の拡張を検討してください。

1. 除外レイヤー設定の外部化
ハードコードされた `TARGET_LAYERS` を、ドキュメントの「ユーザー定義セル(User-Defined Cells)」や外部のJSON/INIファイルからロードするように改修します。プロジェクトごとに設定ファイルを差し替えるだけで、コードに触れずにレイヤー運用ルールを変更できます。

2. バッチモード(非表示起動)への昇格
コマンドライン引数(WSHやPowerShell経由)からこのVBAを呼び出せるようにすれば、CI/CD環境や夜間バッチで「GitにプッシュされたVisio図面から自動で納品用PDFを生成する」といった完全無人化パイプラインの構築も視野に入ります。

泥臭い手作業を確実に撲滅し、設計業務本来のクリエイティビティに集中できる環境を整えていきましょう。

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