【実務・中級編】【中級者向け】ページ設定の「プリンタ用紙サイズ」と「図面サイズ」の不一致を検出し修正する監査ツール – Visio VBA解析バイブル

スポンサーリンク

Visio VBAの世界へようこそ。
この領域で真に価値あるツールを生み出すためには、単なる構文の羅列では不十分です。オブジェクトの振る舞い、ライフサイクル、そして何よりも「なぜそうあるべきか」という設計思想を深く理解する必要があります。

今回は、Visioドキュメントにおける見落とされがちな落とし穴、「ページ設定の『プリンタ用紙サイズ』と『図面サイズ』の不一致」を検出し、必要に応じて修正する監査ツールの構築に挑みます。この問題は、印刷時に意図しない余白が発生したり、スケールが狂ったりする原因となり、多くの企業で無駄な修正作業を発生させています。

一般的なリファレンスは、この問題の根源まで踏み込みません。しかし、我々は違います。堅牢性、保守性、そしてパフォーマンス。これら全てを兼ね備えた「プロフェッショナルな」ツールを、私が直々に伝授します。

—

ページ設定の「闇」:プリンタ用紙サイズと図面サイズの乖離を暴く

Visioは、その直感的な操作性ゆえに、多くのユーザーが深い設定を意識せずに利用しています。しかし、その裏側には、印刷品質やドキュメントの一貫性を左右する重要な設定が隠されています。その最たるものが、各ページの「プリンタ用紙サイズ」と「図面サイズ」です。

なぜ不一致が起きるのか?

この不一致は、様々なシナリオで発生します。

  • テンプレートの不適切な利用: 古いテンプレートや、異なる環境で作成されたテンプレートを使い回すことで、デフォルトのプリンタ設定が引き継がれず、図面サイズとの乖離が生じることがあります。
  • ページのコピー&ペースト: 異なるページ設定を持つページをコピーして貼り付けると、元の図面サイズが維持されず、プリンタ用紙サイズと整合性が取れなくなることがあります。
  • 手動でのサイズ変更の誤り: ユーザーが図面サイズを手動で変更する際に、プリンタ用紙サイズへの連動を意識せずに操作してしまうケースです。
  • Visioバージョンの違い: 異なるVisioバージョン間でファイルを開き直した際に、内部的な解釈の違いから微細なズレが生じることも稀にあります。

不一致が引き起こす深刻な問題

この一見些細な不一致は、特に製造業や建設業など、精密な図面が求められる現場で大きな問題を引き起こします。

  • 意図しない余白: 印刷時に図面の周囲に不要な余白が発生し、用紙が無駄になるだけでなく、視覚的な一貫性を損ないます。
  • スケールの狂い: 図面サイズとプリンタ用紙サイズが正しく整合していないと、印刷時のスケールが意図せず変更され、実寸と異なる出力になってしまいます。これは、現場での混乱や手戻りの原因となります。
  • ドキュメント品質の低下: チーム内で共有されるドキュメントの品質にばらつきが生じ、プロフェッショナルな印象を損ないます。
  • 手作業による修正コスト: 不一致が多数のページにわたって存在する場合、手動での修正は膨大な時間と労力を要し、生産性を著しく低下させます。

我々が目指すのは、この「手作業による修正コスト」をゼロに近づけ、ドキュメント品質を自動的に保証するツールです。

—

堅牢な監査ツールの設計思想

単に値を比較して修正するだけならば、簡単なVBAコードで実現できるでしょう。しかし、それは「プロダクションコード」とは呼べません。バグの温床となり、やがては保守不能に陥ります。伝説的なチーフアーキテクトたる私がお伝えするのは、以下の設計原則に基づいたアプローチです。

1. オブジェクトライフサイクルの厳格な管理

Visio VBAにおけるパフォーマンスと安定性の鍵は、`Visio.Application`、`Visio.Document`、`Visio.Page` といったオブジェクトのライフサイクル管理にあります。特にループ処理内でオブジェクトを参照する際は、不要になったオブジェクトは速やかに `Set obj = Nothing` で解放し、メモリリークやCOMオブジェクト参照カウントの異常増加を防ぐ必要があります。これを怠ると、Visioが不安定になったり、最悪の場合クラッシュを引き起こしたりします。

2. 防御的プログラミングとエラーハンドリング

ユーザーがどのような状況でマクロを実行するかは予測できません。Visioアプリケーションが開いていない、アクティブなドキュメントがない、特定のページが破損しているなど、あらゆる例外を想定し、適切にハンドリングする必要があります。`On Error GoTo` は基本ですが、それだけでなく、事前に条件をチェックする「防御的プログラミング」が重要です。

3. パフォーマンス最適化への配慮

数百ページにも及ぶ大規模なVisioドキュメントを扱う場合、各ページへのアクセスやプロパティの読み書きは、無視できないオーバーヘッドとなります。不必要なDOMアクセスを避け、必要な情報のみを効率的に取得するよう心がけます。また、画面更新を一時的に停止することで、処理速度を向上させます。

4. ユーザーフィードバックとログ出力

自動修正は強力な機能ですが、ユーザーには何が起こっているのか、何が変更されたのかを明確に伝える責任があります。進捗状況、検出された不一致の数、修正されたページの詳細などを、ステータスバーやログ(今回は `Debug.Print` を利用しますが、実運用ではファイル出力やGUI表示も検討すべきです)に出力することで、ユーザー体験を向上させ、トラブルシューティングを容易にします。

5. 「元に戻す」機能のサポート (Undo Scope)

自動修正はドキュメントに直接変更を加えるため、万が一の事態に備え、「元に戻す」機能をサポートすることが不可欠です。Visio VBAには `Application.BeginUndoScope` と `Application.EndUndoScope` という強力な機能があります。これにより、一連の修正操作を単一のUndoアクションとしてまとめることができ、ユーザーはワンクリックで元の状態に戻せるようになります。これは、自動化ツールの信頼性を飛躍的に高める設計です。

—

実践コード:ページ設定監査ツール

それでは、上記の設計思想を盛り込んだ監査ツールを構築しましょう。このコードはVisioのVBAエディタ(Alt + F11)の標準モジュールに記述してください。

コードの概要

このツールは、以下の機能を提供します。
1. 現在開いているVisioドキュメントの全ページをスキャンします。
2. 各ページの「プリンタ用紙サイズ」と「図面サイズ」を取得します。
3. これらのサイズが一致しないページを検出します。
4. 監査モード (`blnFix = False`) の場合は、不一致を報告するのみです。
5. 修正モード (`blnFix = True`) の場合は、図面サイズをプリンタ用紙サイズに合わせて自動修正します。
6. 全ての操作を単一のUndoアクションとして登録します。
7. 処理の進行状況と結果をステータスバーとDebug.Printに出力します。

Option Explicit

‘
‘ モジュール名: PageSettingsAuditor
‘ 説明: Visioドキュメントの全ページのプリンタ用紙サイズと図面サイズの
‘ 不一致を検出し、必要に応じて自動修正するツールです。
‘ 目的: 印刷時の意図しない余白やスケール問題を解消し、ドキュメントの
‘ 一貫性と品質を向上させます。
‘ 作成者: 伝説的なチーフアーキテクト
‘

‘===============================================================================
‘ グローバル定数・変数(必要に応じて)
‘===============================================================================
‘ 浮動小数点数比較のための許容誤差 (Visio内部単位はインチなので、小数点以下の誤差を考慮)
‘ 厳密な比較が必要な場合は 0 に設定しても良いが、システム環境によっては微小な誤差が生じる可能性も考慮
Private Const C_EPSILON As Double = 0.0001 ‘ 0.0001インチ (約 0.0025 mm)

‘===============================================================================
‘ メインプロシージャ: ページ設定監査ツール実行
‘===============================================================================
Public Sub RunPageSettingsAuditor()
‘ 監査モードで実行
Call AuditAndFixPageSizes(False)
End Sub

Public Sub RunPageSettingsFixer()
‘ 修正モードで実行
If MsgBox(“全ページの図面サイズをプリンタ用紙サイズに合わせて修正します。よろしいですか?” & vbCrLf & _
“この操作は元に戻せますが、念のためドキュメントのバックアップをお勧めします。”, _
vbYesNo + vbCritical, “ページ設定修正の確認”) = vbYes Then
Call AuditAndFixPageSizes(True)
Else
MsgBox “修正処理はキャンセルされました。”, vbInformation, “処理キャンセル”
End If
End Sub

‘===============================================================================
‘ コアロジック: ページ設定の監査と修正
‘ 引数: blnFix (Boolean) – True: 修正モード, False: 監査モード
‘===============================================================================
Private Sub AuditAndFixPageSizes(ByVal blnFix As Boolean)
Dim vsoApp As Visio.Application
Dim vsoDoc As Visio.Document
Dim vsoPage As Visio.Page
Dim lngPageCount As Long
Dim i As Long
Dim dblPrinterWidth As Double
Dim dblPrinterHeight As Double
Dim dblPageWidth As Double
Dim dblPageHeight As Double
Dim lngMismatchCount As Long
Dim lngFixedCount As Long
Dim strStatus As String
Dim lngUndoScopeID As Long ‘ UndoスコープID

‘ エラーハンドリングの開始
On Error GoTo ErrorHandler

Set vsoApp = Visio.Application

‘ Visioが編集中または保存中の場合、ユーザーに警告して処理を中断
If vsoApp.IsVisioBeingEdited Or vsoApp.IsVisioBeingSaved Then
MsgBox “Visioが編集中または保存中のため、処理を実行できません。しばらく待ってから再試行してください。”, vbExclamation, “処理中断”
Exit Sub
End If

‘ アクティブなドキュメントが存在するか確認
If vsoApp.ActiveDocument Is Nothing Then
MsgBox “開いているVisioドキュメントがありません。”, vbExclamation, “処理中断”
Exit Sub
End If
Set vsoDoc = vsoApp.ActiveDocument

‘ 画面更新を一時的に停止し、パフォーマンスを向上させる
vsoApp.ScreenUpdating = False

‘ 修正モードの場合、一連の操作をUndo可能な単一のブロックとして開始
If blnFix Then
lngUndoScopeID = vsoApp.BeginUndoScope(“ページ設定の自動修正”)
End If

lngPageCount = vsoDoc.Pages.Count
lngMismatchCount = 0
lngFixedCount = 0

Debug.Print “— ページ設定監査レポート (” & IIf(blnFix, “修正モード”, “監査モード”) & “) —”
Debug.Print “ドキュメント: ” & vsoDoc.Name
Debug.Print “総ページ数: ” & lngPageCount
Debug.Print “——————————————————————”

‘ 全てのページをループ処理
For i = 1 To lngPageCount
Set vsoPage = vsoDoc.Pages.Item(i)

‘ ステータスバーに進捗を表示
strStatus = “ページ ” & i & “/” & lngPageCount & ” を処理中… (” & vsoPage.Name & “)”
vsoApp.StatusBar = strStatus

‘ ページが非表示の場合でも設定は取得できるが、ユーザーへの報告時に考慮する
‘ 今回は非表示ページも対象とする。必要であれば If vsoPage.Background Then Continue For 等でスキップ可能

‘ プリンタ用紙サイズ (Page.PrintPageWidth, Page.PrintPageHeight) を取得
‘ これらのプロパティはVisioの内部単位 (インチ) で値を返す
dblPrinterWidth = vsoPage.PrintPageWidth
dblPrinterHeight = vsoPage.PrintPageHeight

‘ 図面サイズ (Page.PageWidth, Page.PageHeight) を取得
‘ これらのプロパティもVisioの内部単位 (インチ) で値を返す
dblPageWidth = vsoPage.PageWidth
dblPageHeight = vsoPage.PageHeight

‘ プリンタ用紙サイズと図面サイズを比較
‘ 浮動小数点数の比較のため、C_EPSILON (許容誤差) を使用
If Abs(dblPrinterWidth – dblPageWidth) > C_EPSILON Or _
Abs(dblPrinterHeight – dblPageHeight) > C_EPSILON Then

lngMismatchCount = lngMismatchCount + 1

Debug.Print “不一致検出: ページ ‘” & vsoPage.Name & “‘”
Debug.Print ” プリンタ用紙サイズ: ” & Format(dblPrinterWidth, “0.000”) & “x” & Format(dblPrinterHeight, “0.000”) & ” (インチ)”
Debug.Print ” 図面サイズ: ” & Format(dblPageWidth, “0.000”) & “x” & Format(dblPageHeight, “0.000”) & ” (インチ)”

If blnFix Then
‘ 修正モードの場合、図面サイズをプリンタ用紙サイズに合わせる
vsoPage.PageWidth = dblPrinterWidth
vsoPage.PageHeight = dblPrinterHeight
lngFixedCount = lngFixedCount + 1
Debug.Print ” => 修正済み: 図面サイズをプリンタ用紙サイズに合わせました。”
Else
Debug.Print ” => 監査モードのため、修正は行いません。”
End If
End If

‘ オブジェクトの解放 (重要: ループ内で参照されるオブジェクトは毎回解放する)
Set vsoPage = Nothing
Next i

‘ 最終結果の表示
Debug.Print “——————————————————————”
Debug.Print “監査完了。”
Debug.Print “検出された不一致ページ数: ” & lngMismatchCount & ” ページ”
If blnFix Then
Debug.Print “修正されたページ数: ” & lngFixedCount & ” ページ”
If lngFixedCount > 0 Then
MsgBox “ページ設定の修正が完了しました。” & vbCrLf & _
“修正されたページ数: ” & lngFixedCount & vbCrLf & _
“詳細はイミディエイトウィンドウ (Ctrl+G) を参照してください。”, _
vbInformation, “修正完了”
Else
MsgBox “ページ設定の不一致は検出されませんでした。”, vbInformation, “修正不要”
End If
Else
If lngMismatchCount > 0 Then
MsgBox “ページ設定の不一致が ” & lngMismatchCount & ” ページで検出されました。” & vbCrLf & _
“詳細はイミディエイトウィンドウ (Ctrl+G) を参照してください。” & vbCrLf & vbCrLf & _
“修正するには、RunPageSettingsFixer を実行してください。”, _
vbExclamation, “監査結果”
Else
MsgBox “ページ設定の不一致は検出されませんでした。”, vbInformation, “監査完了”
End If
End If

‘ 後処理
GoTo CleanUp

ErrorHandler:
‘ エラーが発生した場合の処理
MsgBox “エラーが発生しました: ” & Err.Description & ” (コード: ” & Err.Number & “)”, vbCritical, “エラー”
If blnFix And lngUndoScopeID <> 0 Then
‘ 修正モードでUndoスコープを開始していた場合、エラー時はロールバック
vsoApp.EndUndoScope lngUndoScopeID, True ‘ Trueでロールバック
End If

CleanUp:
‘ 画面更新を再開
vsoApp.ScreenUpdating = True
‘ ステータスバーをリセット
vsoApp.StatusBar = “”

‘ 修正モードでUndoスコープを開始していた場合、正常終了時はコミット
If blnFix And lngUndoScopeID <> 0 Then
vsoApp.EndUndoScope lngUndoScopeID, False ‘ Falseでコミット
End If

‘ オブジェクトの解放 (重要: 常に解放を忘れずに)
Set vsoPage = Nothing
Set vsoDoc = Nothing
Set vsoApp = Nothing

Debug.Print “— 処理終了 —”
End Sub

コードの解説と「なぜそう書くのか」

`Option Explicit`

これはVBAの「おまじない」ではありません。変数宣言の強制は、タイプミスによるバグを防ぎ、コードの可読性と保守性を高める絶対的なルールです。この設定を怠る開発者は、私から見ればまだ半人前です。

`Private Const C_EPSILON As Double = 0.0001`

Visioのプロパティ値は `Double` 型で、内部的にはインチ単位で保持されます。浮動小数点数の比較は、`A = B` のように厳密に比較すると、ごくわずかな計算誤差で「異なる」と判断されてしまうことがあります。そのため、`Abs(A – B) < C_EPSILON` のように許容誤差 (ε: イプシロン) を設けて比較することが、堅牢な数値比較の常識です。0.0001インチは非常に小さい値であり、実用上問題になることはありません。

`vsoApp.IsVisioBeingEdited Or vsoApp.IsVisioBeingSaved`

これは「防御的プログラミング」の典型です。もしVisioが編集中や保存中にドキュメント構造を変更しようとすれば、予期せぬエラーやデータ破損に繋がる可能性があります。事前に状態をチェックし、安全が確認できてから処理を進めるのが、プロの作法です。

`vsoApp.ScreenUpdating = False`

Visioは、オブジェクトの変更を即座に画面に反映しようとします。多数のページをループし、プロパティを書き換えるたびに画面更新が行われると、処理速度は劇的に低下します。これを一時的に停止することで、パフォーマンスを大幅に向上させ、処理が完了してから一気に画面を更新します。処理の最後に `vsoApp.ScreenUpdating = True` で元に戻すのを忘れないでください。

`lngUndoScopeID = vsoApp.BeginUndoScope(“ページ設定の自動修正”)`

この一行こそが、このツールの信頼性を決定づける重要な要素です。`BeginUndoScope` と `EndUndoScope` で囲まれた一連のVisio操作は、ユーザーにとって単一の「元に戻す」アクションとして認識されます。これにより、万が一の誤操作やバグが発生しても、ユーザーは簡単に元の状態に戻すことができ、自動化ツールへの信頼感が格段に向上します。エラー発生時には `EndUndoScope lngUndoScopeID, True` でロールバック(操作取り消し)し、正常終了時には `EndUndoScope lngUndoScopeID, False` でコミット(操作確定)します。

`Set vsoPage = Nothing` (ループ内)

これが「オブジェクトライフサイクル管理」の真髄です。ループ内で `vsoPage` オブジェクトが何度も生成・参照されます。もしループの最後に `Set vsoPage = Nothing` を記述しなければ、古い `vsoPage` オブジェクトへの参照が残り続け、メモリを圧迫したり、COMオブジェクトの参照カウントがおかしくなったりする可能性があります。これにより、Visioが不安定になるなどの問題を引き起こします。ループ内で参照するオブジェクトは、ループの最後に必ず解放する。これは鉄則です。

`vsoPage.PrintPageWidth`, `vsoPage.PrintPageHeight`, `vsoPage.PageWidth`, `vsoPage.PageHeight`

これらのプロパティは、それぞれプリンタ用紙と図面ページの実際の物理的な幅と高さをVisio内部単位(インチ)で返します。`Page.PrintPageSize` はA4やLetterといった列挙型を返すため、直接サイズ比較には使えません。物理サイズを比較し、修正する際にはこれらのプロパティに直接値を設定するのが最もシンプルかつ正確な方法です。

`Debug.Print`

処理の進行状況や結果を詳細に記録するための最も簡単な方法です。実運用では、この `Debug.Print` の出力をファイルにリダイレクトしたり、専用のログファイルに書き出したりすることで、監査ログとしての価値を高めます。これにより、いつ、誰が、何を修正したのか、という履歴を追跡できるようになります。

`ErrorHandler` と `CleanUp`

エラー発生時でも、画面更新の再開やオブジェクトの解放といった後処理を確実に実行するために、`GoTo ErrorHandler` と `GoTo CleanUp` を使用します。これにより、マクロが途中で中断してもVisioの状態がクリーンに保たれ、システム全体の安定性が保証されます。

—

発展的な考慮事項と今後の展望

今回のツールは、Visio VBAの基本と堅牢な設計原則を学ぶための第一歩です。しかし、真の業務自動化エンジニアであれば、常に「さらに良くするにはどうすればよいか」を考えるべきです。

1. ユーザーインターフェース (UI) の実装

現状は `MsgBox` と `Debug.Print` に頼っていますが、より使いやすいツールにするには、カスタムフォーム (UserForm) を実装し、以下の機能を追加することが考えられます。

  • 監査/修正モードの選択チェックボックス
  • 処理対象ページの選択(全ページ、選択ページのみ、特定のレイヤーのみなど)
  • ログのリアルタイム表示
  • レポートのエクスポート機能 (CSV, Excelなど)

2. 設定の外部化

`C_EPSILON` のような定数や、ログファイルのパスなどは、コード内にハードコードするのではなく、設定ファイル(INIファイル、XMLファイル、レジストリ)から読み込むようにすることで、コードを変更せずにツールの挙動を調整できるようになります。

3. ファイルシステム連携とデータベース連携

もし監査結果を永続的に記録したり、複数のVisioドキュメントを一括で処理したりする必要があるならば、CSVやExcelファイルへの出力、あるいはAccessデータベースなどへの記録を検討すべきです。特に、大規模なプロジェクトでは、共有データベースに監査履歴を集中管理することで、プロジェクト全体の品質管理を高度化できます。

‘ (参考: ログをファイルに出力する場合の簡易的な例)
‘ Function SaveLogToFile(strLogContent As String, strFilePath As String)
‘ Dim FSO As Object ‘ Scripting.FileSystemObject
‘ Dim TS As Object ‘ Scripting.TextStream
‘ Set FSO = CreateObject(“Scripting.FileSystemObject”)
‘ Set TS = FSO.CreateTextFile(strFilePath, True) ‘ Trueで上書き
‘ TS.Write strLogContent
‘ TS.Close
‘ Set TS = Nothing
‘ Set FSO = Nothing
‘ End Function
‘
‘ ‘ 使用例
‘ ‘ Dim strLog As String
‘ ‘ strLog = “何かのログ内容”
‘ ‘ Call SaveLogToFile(strLog, vsoDoc.Path & “\” & Replace(vsoDoc.Name, “.vsd”, “”) & “_AuditLog.txt”)

この例は `Scripting.FileSystemObject` を使いますが、COMオブジェクトなので、ライフサイクル管理を適切に行う必要があります。

4. Visioイベントの活用

ドキュメントが開かれたり、ページが追加されたりするタイミングで自動的に監査処理をトリガーするよう、`Visio.Application` オブジェクトのイベント (`DocumentOpened`, `PageAdded` など) を活用することも可能です。これにより、品質チェックプロセスをさらに自動化し、予防的な対策を講じることができます。

—

結論:Visio VBAを掌握し、業務を支配せよ

今回のテーマは「ページ設定の不一致」という、一見地味な問題でした。しかし、その解決策を構築する過程で、我々はVisio VBAにおけるオブジェクト管理、エラーハンドリング、パフォーマンス、そしてユーザー体験といった、開発の根幹をなす要素を深く掘り下げてきました。

単なるVBAコードのコピペに終わらず、その背後にある「なぜそうあるべきか」という設計思想を理解し、実践すること。これこそが、あなたが業務自動化エンジニアとして次のレベルに進むための鍵です。

Visio VBAは、そのオブジェクトモデルの奥深さゆえに、まだまだ多くの業務課題を解決するポテンシャルを秘めています。今日学んだ知識と経験を活かし、あなたの現場で、真に価値あるツールを創造してください。

これで、君もチーフアーキテクトの一歩を踏み出した。この知見を胸に、Visio VBAを完全に掌握し、あなたの業務を支配する存在となることを期待しています。

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