Visio VBAの奥義:PDFしおりを自在に操る、生成後カスタマイズの秘術
皆さん、こんにちは! Visio VBAの世界へようこそ。今回は、一歩進んだVisio VBAの活用法、特に「Visio標準のPDF出力機能では制御しきれない、PDFのしおり(アウトライン)をVBAで高度にカスタマイズする方法」について、皆さんと一緒に深掘りしていきましょう。
「マクロの記録」を卒業し、もっと自由自在にVisioを操りたい。そんな熱意あふれる皆さんのために、今回は少し高度なテクニックに挑戦します。しかし、ご安心ください。優しく知的な先輩エンジニアとして、基礎から丁寧に、そして本質を捉えながら、皆さんの理解をしっかりとサポートしていきます。
なぜ、PDFしおりのカスタマイズが必要なのか?
Visioで作成した図面をPDFで共有する際、しおり(アウトライン)は非常に重要なナビゲーションツールとなります。ページタイトルやセクション名をしおりとして設定することで、PDFリーダー上で目次のように表示され、目的のページへ素早くアクセスできるようになります。
しかし、Visioの標準的なPDF出力機能では、しおりの階層構造や表示テキストを細かく制御するのが難しい場合があります。例えば、
- 複雑な階層構造を持ったしおりを作成したい
- 特定のシェイプやグループ名をしおりのテキストにしたい
- しおりの表示順序を細かく調整したい
- Visioの図形情報に紐づかない、独自のしおりを生成したい
といった要望に応えたい場合、標準機能だけでは限界があります。
そこで今回ご紹介するのが、「VisioでPDFを生成した後、外部ライブラリと連携してPDFのしおりを高度にカスタマイズする」という、まさに秘術とも言える手法です。
読者の皆さんへ:このブログで得られるもの
- Visio VBAにおけるPDF出力の基本と、その限界。
- PDFしおりカスタマイズの必要性を理解する。
- 外部ライブラリ(今回は`iTextSharp`を例に)との連携による高度なPDF操作の概念。
- 具体的なVBAコード例(VB.NETも一部紹介)で、実践的なスキルを習得。
- よくあるエラーとその対処法。
- 「ここをクリアすれば、Visio VBAの基本はバッチリですよ」という確信。
さあ、このエキサイティングな旅に、一緒に踏み出しましょう!
—
第1章:Visio VBAの基礎とPDF出力の現在地
まずは、Visio VBAの基本的な考え方と、PDF出力における現状を整理しておきましょう。
Visio VBAのオブジェクトモデルを理解する
Visio VBAを使いこなす上で、最も重要なのが「Visioのオブジェクトモデル」を理解することです。これは、Visioの各要素(ドキュメント、ページ、シェイプ、コネクタなど)が、プログラム上でどのように表現され、どのように操作できるかを示す構造図のようなものです。
例えるなら、Visioという「家」をプログラムで操作するために、各部屋(ドキュメント、ページ)や家具(シェイプ)の名前と配置を記した「設計図」のようなものですね。

- `Application` (Visio アプリケーション全体)
- `Documents` (開いているドキュメントのコレクション)
- `Document` (一つのVisioドキュメント)
- `Pages` (ドキュメント内のページのコレクション)
- `Page` (一つのページ)
- `Shapes` (ページ内のシェイプのコレクション)
- `Shape` (一つのシェイプ)
これらのオブジェクトを操作することで、図形の作成、プロパティの変更、テキストの編集など、様々な自動化が可能になります。
Visioの標準PDF出力機能
Visioには、図面をPDFとしてエクスポートする標準機能が備わっています。これは非常に便利で、簡単な用途であればこれで十分です。
標準的なPDF出力の手順(VBAでの操作例):
Sub ExportToPdfStandard()
Dim visDoc As Visio.Document
Dim pdfFileName As String
‘ 現在アクティブなドキュメントを取得
Set visDoc = Visio.ActiveDocument
‘ 保存するPDFファイル名を指定
pdfFileName = ThisDocument.Path & “\ExportedDiagram_” & Format(Now, “yyyymmdd_hhmmss”) & “.pdf”
‘ PDFとしてエクスポート(デフォルト設定)
‘ 詳細はヘルプやドキュメントで確認できますが、
‘ ここでは最もシンプルなエクスポート方法を紹介します。
visDoc.ExportAsFixedFormat visFixedFormatType.visFixedFormatPDF, pdfFileName
MsgBox “PDFファイルが保存されました: ” & pdfFileName
End Sub
コードの解説:
- `Dim visDoc As Visio.Document`: `visDoc`という変数に、Visioのドキュメントオブジェクトを格納できるように宣言しています。
- `Set visDoc = Visio.ActiveDocument`: 現在アクティブになっているVisioドキュメントを`visDoc`変数に代入しています。
- `pdfFileName = ThisDocument.Path & “\ExportedDiagram_” & Format(Now, “yyyymmdd_hhmmss”) & “.pdf”`: 保存するPDFファイルの名前を生成しています。`ThisDocument.Path`でVBAプロジェクトファイルが保存されているフォルダパスを取得し、現在の日時をファイル名に含めることで、重複を防いでいます。
- `visDoc.ExportAsFixedFormat visFixedFormatType.visFixedFormatPDF, pdfFileName`: ここがPDFエクスポートの核心部分です。`ExportAsFixedFormat`メソッドを使用し、`visFixedFormatPDF`を指定してPDF形式で、`pdfFileName`で指定したパスにエクスポートします。
この標準機能の限界:
しかし、前述したように、この標準機能では、PDFのしおり(アウトライン)の構造を細かく制御することはできません。Visioのページタイトルや、特定のシェイプのテキストが自動的にしおりとして生成されることはありますが、その階層化やカスタムテキストの設定は限定的です。
—
第2章:PDFしおりカスタマイズの必要性と課題
では、なぜPDFしおりのカスタマイズが重要になるのでしょうか?そして、そのカスタマイズをVisio VBAから直接行うことの難しさを、もう少し具体的に見ていきましょう。
なぜ、しおりのカスタマイズが重要なのか?
1. ナビゲーションの効率化
- 複雑な図面: 大規模なシステム構成図や、BPMN図のように多くのステップを持つプロセス図では、しおりの階層が深いほど、参照者は目的のセクションに素早くたどり着けます。
- プロジェクト管理: プロジェクトのフェーズごとにページを分け、それをしおりで階層化することで、進捗状況の確認や関係者への報告が格段にスムーズになります。
- デザインの一貫性: 企業のデザインガイドラインに沿ったしおりの命名規則や階層構造を適用することで、ドキュメント全体の統一感を保てます。
2. 情報伝達の強化
- シェイプとの連携: 特定のシェイプ(例:「フェーズ1」「重要リスク」など)に、そのシェイプが示す内容へのリンクとしおりを関連付けることで、図面とテキスト情報の連携を強化できます。
- 動的な情報: VBAで動的に生成された図面データ(例:データベースから取得した情報)を元に、しおりを生成することで、常に最新の情報に基づいたナビゲーションを提供できます。
3. 標準機能では実現できないこと
Visioの標準PDFエクスポートで「しおり」として扱われるのは、基本的に「ページ名」です。
- 複数レベルの階層化: 例えば、「フェーズ1」→「設計」→「詳細設計」といった3段階以上の階層化は、標準機能では直接行えません。
- シェイプ固有の情報: 図面内の特定のシェイプ(例えば、「主要モジュールA」という名前のシェイプ)を、ページ名ではなく、直接しおりとして設定することはできません。
- しおりの表示テキストの変更: ページ名ではなく、「モジュールAの仕様」といった、より説明的なテキストを、しおりの表示名として設定したい場合、標準機能では限界があります。
これらの高度なカスタマイズを実現するには、Visio VBA単体では難しく、PDF生成後に外部のライブラリを使ってPDFファイルを直接操作するというアプローチが必要になります。
—
第3章:外部ライブラリとの連携によるPDFしおりカスタマイズの全体像
では、具体的にどのように「PDF生成後に外部ライブラリと連携する」のか、その全体像を掴んでいきましょう。
連携の基本的な流れ
1. Visioで図面を作成・編集する。
2. VBA(またはVB.NET)を使って、Visio図面をPDFファイルとしてエクスポートする。
- この時点では、標準機能による基本的なしおり(ページ名など)しか設定されていない、または全く設定されていない状態です。
3. エクスポートされたPDFファイルを、外部のPDF操作ライブラリを使って開く。
4. ライブラリの機能を使って、PDFのしおり(アウトライン)をプログラムで追加、編集、削除する。
- ここで、VBAで取得した情報(シェイプ名、カスタムプロパティなど)を元に、複雑なしおり構造を構築します。
5. 変更を保存し、カスタマイズされたPDFファイルを生成する。

どのライブラリを使うか?
PDFファイルをプログラムで操作するためのライブラリはいくつか存在します。今回は、広く使われており、比較的扱いやすい「iTextSharp」(.NET Framework版)を例に説明します。
iTextSharpとは?
iTextSharpは、Javaで開発されたiTextの.NETポートで、PDFの生成、編集、操作などができる強力なライブラリです。商用利用にはライセンスが必要になる場合がありますので、利用規約を必ずご確認ください。
VBAから.NETライブラリを呼び出すには?
VBAから.NETのライブラリを直接呼び出すのは少しトリッキーですが、以下の方法が考えられます。
1. VB.NETでPDF操作用のDLLを作成し、VBAからCOM経由で呼び出す。
- これが最も一般的で、堅牢な方法です。
2. VBAから外部プロセス(VB.NETで作成したEXE)を起動し、PDFファイルを引数として渡して処理させる。
- 比較的シンプルですが、プロセス間通信やエラーハンドリングが煩雑になる可能性があります。
今回は、より実践的で、皆さんの環境で試しやすいように、VB.NETでiTextSharpを使ったPDFしおり操作のコードを作成し、それをVBAから呼び出すというアプローチで解説を進めます。
—
第4章:実践!VB.NET + iTextSharp でPDFしおりをカスタマイズ
さあ、いよいよ実践編です!ここでは、VB.NETを使ってiTextSharpでPDFのしおりを生成・編集する方法を解説し、それをVBAから呼び出すための橋渡しをします。
準備:VB.NETプロジェクトのセットアップ
1. Visual Studio を起動: Visual Studio Community Edition など、お使いの環境でVB.NETプロジェクトを作成します。
2. 新しいプロジェクトの作成: 「Windows フォーム アプリケーション (.NET Framework)」を選択し、プロジェクト名(例: `PdfOutlineManager`)を付けて作成します。
3. iTextSharp の追加:
- ソリューションエクスプローラーでプロジェクト名を右クリックし、「NuGet パッケージの管理」を選択します。
- 「参照」タブで「iTextSharp」と検索し、最新版(または安定版)をインストールします。
VB.NETコード例:iTextSharpでしおりを追加する
ここでは、既存のPDFファイルを受け取り、指定した構造でしおりを追加して、新しいPDFファイルとして保存するVB.NETコードを作成します。
.net
‘ PdfOutlineManager プロジェクトにて
Imports iTextSharp.text
Imports iTextSharp.text.pdf
Imports System.IO
Public Class PdfOutlineEditor
”’
”’
”’ 入力PDFファイルのパス
”’ 出力PDFファイルのパス
”’ しおり構造を表す配列 (例: {“ページ1”, “セクションA”, “セクションA-1”, “ページ2”})
Public Sub AddOutlineToPdf(inputPdfPath As String, outputPdfPath As String, outlineData As String())
If Not File.Exists(inputPdfPath) Then
Throw New FileNotFoundException(“入力PDFファイルが見つかりません。”, inputPdfPath)
End If
‘ PDFリーダーとライターの準備
Dim reader As PdfReader = Nothing
Dim stamper As PdfStamper = Nothing
Try
reader = New PdfReader(inputPdfPath)
stamper = New PdfStamper(reader, New FileStream(outputPdfPath, FileMode.Create, FileAccess.Write))
‘ しおり(アウトライン)のルートを作成
Dim outlineRoot As PdfOutline = stamper.Outlines
‘ outlineData を元に階層構造を作成
‘ ここでは単純に一次元配列を元に、ネストされた階層を作成する例を示します。
‘ より複雑な構造(例:親しおりと子しおりのペア)を扱う場合は、
‘ outlineData の形式や処理ロジックを調整する必要があります。
Dim currentParent As PdfOutline = outlineRoot
For i As Integer = 0 To outlineData.Length – 1
‘ 各要素がどのページを指すかを決定する必要があります。
‘ ここでは、単純化のため、配列のインデックスをページ番号(+1)として扱います。
‘ 実際には、Visioのページ名やカスタムプロパティから対応するページ番号を特定する必要があります。
Dim pageNumber As Integer = i + 1
If pageNumber <= reader.NumberOfPages Then ' 新しいしおりを作成 Dim newOutline As New PdfOutline(currentParent, pageNumber, outlineData(i)) ' 次のループのために、親しおりを更新(階層化する場合) ' もし、常にルート直下に作成したい場合は currentParent = outlineRoot のままにします。 ' ここでは、配列の順番で階層が深くなるように見せかける例です。 ' 実際には、しおりのレベルを管理する別の仕組みが必要です。 ' 例:outlineData にレベル情報を持たせる、あるいは特定の区切り文字を使うなど。 currentParent = newOutline ' 次のしおりはこのしおりの子になる、という見せかけ ' ルート直下に常に作成したい場合は以下のようにします。 ' Dim newOutline As New PdfOutline(outlineRoot, pageNumber, outlineData(i)) Else ' 指定されたページ番号がPDFの総ページ数を超える場合はスキップ Console.WriteLine($"警告: ページ番号 {pageNumber} はPDFの総ページ数 ({reader.NumberOfPages}) を超えています。'{outlineData(i)}' はスキップされます。") End If Next ' 変更を保存 stamper.Close() reader.Close() Console.WriteLine("しおりの追加が完了しました。") Catch ex As Exception Console.WriteLine($"エラーが発生しました: {ex.Message}") ' エラー発生時もリソースを解放する If stamper IsNot Nothing Then stamper.Close() If reader IsNot Nothing Then reader.Close() Throw ' エラーを呼び出し元に伝播させる End Try End Sub ' --- VBAから呼び出すためのラッパーメソッド --- ' VBAから直接.NETオブジェクトを生成・操作するのは難しいため、 ' COM互換のラッパーを作成するか、EXEを呼び出すのが一般的です。 ' ここでは、概念を理解していただくために、 ' VBAからEXEを呼び出す簡易的な例を想定した説明に留めます。 ' (実際のCOMラッパー作成は、より高度な設定が必要です) End Class VB.NETコードのポイント:
- `Imports iTextSharp.text` および `Imports iTextSharp.text.pdf`: iTextSharpのクラスを利用するために必要です。
- `PdfReader` と `PdfStamper`: 既存のPDFを読み込み、新しいPDFとして書き出すためのクラスです。
- `stamper.Outlines`: PDFのしおり(アウトライン)のルートオブジェクトを取得します。
- `New PdfOutline(parent, pageNumber, title)`: 新しいしおりを作成します。
- `parent`: このしおりが属する親のしおりオブジェクト。ルート直下の場合は `outlineRoot` を指定します。
- `pageNumber`: このしおりが指し示すページの番号(1から始まります)。
- `title`: しおりに表示されるテキスト。
- 階層構造の作成: 上記コードでは、`outlineData`配列を単純にループ処理し、`currentParent`を更新することで、一時的な階層構造を表現しています。ここが最もカスタマイズが必要な部分です。 実際には、Visioのシェイプの階層(グループ化など)や、シェイプのカスタムプロパティ(例: `OutlineLevel = 1` のような情報)をVBAで取得し、それを`outlineData`に変換してVB.NET側に渡す必要があります。
- エラーハンドリング: `Try…Catch`ブロックで、ファイルが見つからない場合や、PDFの処理中にエラーが発生した場合に適切に対処しています。
VBAからVB.NET(EXE)を呼び出す方法(概念)
VBAから直接VB.NETのDLLをCOMとして呼び出すのは、環境設定が複雑になりがちです。より手軽に試すなら、VB.NETで作成したEXE(実行ファイル)をVBAから呼び出す方法があります。
VB.NET側(EXEとしてビルド):
- 上記 `PdfOutlineEditor` クラスを、コンソールアプリケーションとして作成します。
- コマンドライン引数で、入力PDFパス、出力PDFパス、しおりデータをJSON形式などで受け取るように修正します。
.net
‘ PdfOutlineManager (Console Application) プロジェクトにて
‘ … (PdfOutlineEditor クラスは上記と同様) …
Module Module1
Sub Main(args As String())
‘ コマンドライン引数を解析するロジックを実装
‘ 例: args(0) = inputPdfPath, args(1) = outputPdfPath, args(2) = outlineDataJson
Dim inputPath As String = args(0)
Dim outputPath As String = args(1)
Dim outlineDataJson As String = args(2)
‘ JSON文字列をパースして String() 配列に変換する処理 (Json.NET などを使用)
Dim outlineData As String() = DeserializeOutlineData(outlineDataJson) ‘ このメソッドは別途実装が必要
Try
Dim editor As New PdfOutlineEditor()
editor.AddOutlineToPdf(inputPath, outputPath, outlineData)
Console.WriteLine(“処理が正常に完了しました。”)
Environment.Exit(0) ‘ 成功を示す終了コード
Catch ex As Exception
Console.WriteLine($”エラー: {ex.Message}”)
Environment.Exit(1) ‘ エラーを示す終了コード
End Try
End Sub
‘ JSONデシリアライズのサンプル (Newtonsoft.Json を NuGet で追加)
Private Function DeserializeOutlineData(jsonString As String) As String()
Return Newtonsoft.Json.JsonConvert.DeserializeObject(Of String())(jsonString)
End Function
End Module
VBA側(呼び出し元):
Sub CreatePdfWithCustomOutline()
Dim visDoc As Visio.Document
Dim tempPdfPath As String
Dim finalPdfPath As String
Dim vbNetExePath As String
Dim shellCommand As String
Dim outlineData As Variant ‘ VBAの配列
Dim outlineDataJson As String
Dim shellExitCode As Integer
‘ — 設定 —
Set visDoc = Visio.ActiveDocument
tempPdfPath = Visio.Application.TemporaryDirectory & “\temp_output.pdf” ‘ 一時PDFファイルパス
finalPdfPath = ThisDocument.Path & “\CustomOutlinedDiagram_” & Format(Now, “yyyymmdd_hhmmss”) & “.pdf” ‘ 最終PDFファイルパス
vbNetExePath = “C:\Path\To\Your\PdfOutlineManager.exe” ‘ 作成したVB.NET EXEのフルパス
‘ — 1. Visio図面を一時PDFとしてエクスポート —
On Error GoTo ErrorHandler ‘ エラーハンドリング設定
visDoc.ExportAsFixedFormat visFixedFormatType.visFixedFormatPDF, tempPdfPath
On Error GoTo 0 ‘ エラーハンドリング解除
‘ — 2. しおりデータをVBA配列で定義 —
‘ ここでは例として、単純な階層構造を想定します。
‘ 実際には、Visioのシェイプ情報から動的に生成するロジックが必要です。
‘ 例: ページ名、シェイプ名、カスタムプロパティなどを取得し、配列に格納します。
outlineData = Array(“Page 1 Title”, “Section 1.1”, “Section 1.2”, “Page 2 Title”, “Sub Section 2.1.1”)
‘ — 3. VBA配列をJSON文字列に変換 (VBAには標準JSONライブラリがないため、簡易的な自作 or 外部ライブラリが必要) —
‘ ここでは、簡易的なJSON変換関数を自作するか、またはMicrosoft Scripting RuntimeのDictionaryなどを使う想定です。
‘ 簡便のため、ここではCSV形式のような単純な文字列連結で代用し、VB.NET側でJSONにパースさせる例を考えます。
‘ より堅牢にするなら、VBA-JSONライブラリなどを導入することを推奨します。
outlineDataJson = Join(outlineData, “|”) ‘ 区切り文字で結合 (VB.NET側でJSONにパースさせる)
‘ — 4. VB.NET EXEを呼び出す —
‘ コマンドプロンプトを開かずに実行するために、Shell関数に vbHide フラグを使用します。
shellCommand = “””” & vbNetExePath & “”” “”” & tempPdfPath & “”” “”” & finalPdfPath & “”” “”” & outlineDataJson & “”””
‘ Shell関数は非同期実行なので、処理完了を待つための工夫が必要な場合があります。
‘ ここでは、単純に実行し、後続の処理に進みます。(EXE側で成功/失敗の終了コードを返す想定)
Dim shellResult As Variant
shellResult = Shell(shellCommand, vbHide) ‘ vbHide: ウィンドウを非表示で実行
If shellResult = 0 Then
MsgBox “VB.NETプログラムの起動に失敗しました。パスを確認してください。”, vbCritical
GoTo CleanUp
Else
‘ EXEが起動したが、処理が完了したかどうかの確認は別途必要
‘ 例: EXEが完了後に特定のファイルを作成する、EXEが標準出力にメッセージを出す、など
‘ ここでは、EXEが完了するのを待つための簡易的なループ処理(非推奨だが、デモ用)
Dim startTime As Double
startTime = Timer
Do While Timer < startTime + 5 ' 最大5秒待つ(必要に応じて調整)
DoEvents
' EXEの終了コードを取得する仕組みがないため、ここではタイムアウトのみ
Loop
' より確実な方法: COMオブジェクト化して同期呼び出し、またはプロセス監視
End If
' --- 5. 一時PDFファイルを削除 ---
If Dir(tempPdfPath) <> “” Then
Kill tempPdfPath
End If
MsgBox “カスタマイズされたPDFファイルが保存されました: ” & finalPdfPath
CleanUp:
Exit Sub
ErrorHandler:
MsgBox “エラーが発生しました: ” & Err.Description, vbCritical
If Dir(tempPdfPath) <> “” Then Kill tempPdfPath ‘ エラー時も一時ファイルを削除
Resume CleanUp ‘ クリーンアップ処理へ
End Sub
‘ —- JSON変換の簡易版 (参考) —-
‘ 実際には、より高機能なライブラリの利用を推奨します。
‘ Function ConvertArrayToJsonString(arr As Variant) As String
‘ Dim json As String
‘ Dim i As Long
‘ json = “[”
‘ For i = LBound(arr) To UBound(arr)
‘ json = json & “””” & Replace(arr(i), “”””, “\”””) & “””” ‘ ダブルクォーテーションのエスケープ
‘ If i < UBound(arr) Then
' json = json & ","
' End If
' Next
' json = json & "]"
' ConvertArrayToJsonString = json
' End Function
VBAコードのポイント:
- `vbNetExePath`: 作成したVB.NETのEXEファイルへのフルパスを指定します。
- `tempPdfPath`: Visioから一時的にエクスポートするPDFのパスです。
- `finalPdfPath`: VB.NETプログラムが生成する、しおりが追加された最終的なPDFのパスです。
- `outlineData = Array(…)`: ここでVBAの配列として、しおりのタイトル(および階層情報)を定義します。この配列を、Visioの図面情報から動的に生成するロジックが、この手法の肝となります。
- `Join(outlineData, “|”)`: VBAの配列を、VB.NET側でパースしやすいように文字列に変換しています。ここでは区切り文字として `|` を使用しました。VB.NET側で、この文字列をJSON形式に変換する処理が必要です。
- `Shell(shellCommand, vbHide)`: `Shell`関数を使ってVB.NETのEXEを実行します。`vbHide`オプションで、コマンドプロンプトウィンドウを非表示にします。
- 同期処理の課題: `Shell`関数は非同期で実行されるため、EXEの処理が完了する前にVBAの処理が進んでしまう可能性があります。EXEが完了したことを検知するには、EXE側で完了を示すファイルを作成する、EXEが標準出力に特定のメッセージを書き込む、といった工夫が必要です。あるいは、VB.NETでCOM互換のDLLを作成し、VBAから`CreateObject`で呼び出す方が、同期的に処理を待つことができます。
—
第5章:VBAでしおりデータを取得する高度なテクニック
さて、VB.NET側でしおりを操作する準備ができたとして、肝心なのは「Visioの図面から、どのような情報を取得し、それをどのようにVB.NETに渡すか」です。
1. ページ名を利用する
最も基本的なのは、Visioの各ページのページ名をそのまましおりのタイトルとして利用する方法です。
‘ VBAコード例
Dim page As Visio.Page
Dim outlineTitles As New Collection ‘ Collectionオブジェクトは動的な追加・削除に便利
For Each page In Visio.ActiveDocument.Pages
‘ ページ名を取得し、Collectionに追加
outlineTitles.Add page.Name
Next
‘ Collectionを配列に変換して、VB.NETに渡す
Dim outlineArray() As String
ReDim outlineArray(1 To outlineTitles.Count)
Dim i As Integer
i = 1
For Each title In outlineTitles
outlineArray(i) = title
i = i + 1
Next
‘ outlineArray を VB.NET の EXE に渡す処理へ…
2. シェイプのテキストを利用する
特定のシェイプのテキストをしおりのタイトルにしたい場合も多いでしょう。例えば、各セクションの開始点に「セクションタイトル」という名前のシェイプを配置しておき、そのテキストを利用するなどです。
‘ VBAコード例
Dim page As Visio.Page
Dim shp As Visio.Shape
Dim outlineTitles As New Collection
‘ 各ページをループ
For Each page In Visio.ActiveDocument.Pages
‘ ページ名も追加しておく(基本のしおりとして)
outlineTitles.Add page.Name
‘ ページ内の特定のシェイプを探す (例: シェイプ名が “SectionHeader” のもの)
For Each shp In page.Shapes
If shp.Name = “SectionHeader” Then
outlineTitles.Add shp.Text ‘ シェイプのテキストをしおりタイトルとして追加
End If
Next
Next
‘ outlineTitles を配列に変換して VB.NET に渡す…
3. シェイプのカスタムプロパティを利用する
さらに高度な制御をするために、シェイプにカスタムプロパティを設定し、それをしおりのタイトルや階層レベルの情報として利用する方法があります。
Visioでの設定:
シェイプを選択 → 右クリック → 「ShapeSheetを表示」 → 「Shape Transform」セクションなどのカスタムプロパティに情報を追加。(例: `Prop.OutlineTitle = “詳細設計”` や `Prop.OutlineLevel = 2`)
VBAでの取得:
‘ VBAコード例
Dim page As Visio.Page
Dim shp As Visio.Shape
Dim outlineInfo As New Collection ‘ しおり情報を格納するCollection (例: [タイトル, レベル, ページ番号])
For Each page In Visio.ActiveDocument.Pages
‘ ページ名とレベル1で追加
outlineInfo.Add Array(page.Name, 1, page.Index) ‘ page.Index は 1 から始まるページ番号
‘ シェイプのカスタムプロパティからしおり情報を取得
For Each shp In page.Shapes
Dim outlineTitle As String
Dim outlineLevel As Integer
On Error Resume Next ‘ プロパティが存在しない場合のエラーを無視
outlineTitle = shp.CellsU(“Prop.OutlineTitle”).ResultStrU(“”) ‘ カスタムプロパティ “OutlineTitle” を取得
outlineLevel = shp.CellsU(“Prop.OutlineLevel”).ResultInt ‘ カスタムプロパティ “OutlineLevel” を取得
On Error GoTo 0 ‘ エラーハンドリングを元に戻す
If outlineTitle <> “” Then
‘ カスタムプロパティで指定されたしおりを追加
‘ ページ番号は、そのシェイプが存在するページの番号を使用
outlineInfo.Add Array(outlineTitle, outlineLevel, page.Index)
End If
Next
Next
‘ outlineInfo (Collection of Arrays) を VB.NET に渡すためのデータ構造に変換
‘ 例: [{“Title”: “Page1”, “Level”: 1, “PageNum”: 1}, {“Title”: “Section A”, “Level”: 2, “PageNum”: 1}, …]
‘ この変換処理は、VB.NET側でJSONとして受け取るのが最も柔軟です。
‘ VBA側でJSON文字列を生成する関数を別途用意する必要があります。
VB.NET側でのJSONパースと階層構築:
VB.NET側では、VBAから受け取ったJSON文字列をパースし、しおりの階層構造を構築します。
.net
‘ VB.NET コード例 (Newtonsoft.Json を使用)
‘ VBAから受け取ったJSON文字列をパースして、しおり情報のリストを作成
‘ 例: [{“Title”: “Page1”, “Level”: 1, “PageNum”: 1}, {“Title”: “Section A”, “Level”: 2, “PageNum”: 1}, …]
Public Class OutlineItem
Public Property Title As String
Public Property Level As Integer
Public Property PageNum As Integer
End Class
‘ … (AddOutlineToPdf メソッド内) …
Dim outlineItems As List(Of OutlineItem) = JsonConvert.DeserializeObject(Of List(Of OutlineItem))(outlineDataJson)
Dim outlineRoot As PdfOutline = stamper.Outlines
Dim parentStack As New Stack(Of PdfOutline)() ‘ 階層を管理するためのスタック
parentStack.Push(outlineRoot) ‘ 最初はルートが親
Dim currentLevel As Integer = 0
For Each item In outlineItems
Dim level As Integer = item.Level
Dim title As String = item.Title
Dim pageNum As Integer = item.PageNum
‘ 階層レベルが上がった場合 (親を変更)
While level <= currentLevel
If parentStack.Count > 1 Then ‘ ルートより下の階層にいる場合のみポップ
parentStack.Pop()
End If
currentLevel = currentLevel – 1
End While
‘ 新しいしおりを作成
Dim parentOutline As PdfOutline = parentStack.Peek() ‘ 現在の親しおりを取得
Dim newOutline As New PdfOutline(parentOutline, pageNum, title)
‘ 次のしおりのために階層レベルを更新
currentLevel = level
parentStack.Push(newOutline) ‘ 次のしおりのための親としてスタックに追加 (ネスト構造を作るため)
Next
‘ … stamper.Close() へ続く …
このVB.NETコードは、`Level`プロパティに基づいてしおりの階層を構築する例です。VBA側で`Prop.OutlineLevel`などに適切な値を設定しておくことで、柔軟なしおり構造を実現できます。
—
第6章:陥りやすいエラーとその回避策
この高度な連携手法では、Visio VBA、.NET、そしてPDFの知識が複合的に必要となるため、いくつかの落とし穴があります。
1. ファイルパスの問題
- エラー: `FileNotFoundException` や `DirectoryNotFoundException`
- 原因: VB.NETプログラムのパスが間違っている、一時ファイルや出力ファイルのパスに無効な文字が含まれている、ネットワークドライブのパスが正しく解決されないなど。
- 回避策:
- VB.NETのEXEパスはフルパスで指定する。
- `Visio.Application.TemporaryDirectory` を利用して、Visioが管理する一時フォルダにファイルを置く。
- パスの区切り文字 (`\`) のエスケープや、パス全体をダブルクォーテーションで囲む (`”””” & path & “””””`) ことを忘れない。
- ファイル名に現在時刻を含めるなどして、ファイル名の重複を防ぐ。
2. .NET Framework のバージョン互換性
- エラー: `TypeInitializationException` や `BadImageFormatException`
- 原因: VBAから呼び出すVB.NET EXEが、インストールされている.NET Frameworkのバージョンと互換性がない場合。
- 回避策:
- VB.NETプロジェクトのターゲットフレームワークを、実行環境にインストールされているバージョン(例: .NET Framework 4.x)に合わせる。
- 可能であれば、VBAからCOM互換DLLを呼び出す方が、.NET Frameworkの依存関係を管理しやすい。
3. iTextSharp のライセンス
- エラー: (直接的な実行時エラーではないが) 法的な問題。
- 原因: iTextSharp は AGPL ライセンスまたは商用ライセンスの下で提供されています。AGPLライセンスの条件を満たさない(例: ソースコードの開示義務)まま商用製品に組み込むと、ライセンス違反となる可能性があります。
- 回避策:
- 利用目的(個人利用、社内利用、商用製品への組み込みなど)に応じて、iTextSharpのライセンス条項を必ず確認し、遵守する。
- 商用利用の場合は、iText 社から適切なライセンスを購入する。
4. しおりデータの構造と階層化のロジック
- エラー: PDFにしおりが正しく追加されない、階層がおかしくなる、意図しないページを指す。
- 原因:
- VBAからVB.NETへ渡すデータ(しおりタイトル、レベル、ページ番号)の形式が正しくない。
- VB.NET側でのJSONパースエラー。
- VB.NET側での階層構築ロジック(スタック処理など)に誤りがある。
- Visioのページ番号と、iTextSharpのページ番号のオフセット(0始まりか1始まりかなど)のずれ。
- 回避策:
- VBA側で生成するデータ構造(配列、JSONなど)と、VB.NET側で期待するデータ構造を正確に一致させる。
- VB.NET側で、受け取ったJSONデータをデバッグ出力して、内容を確認する。
- 階層構築ロジックは、シンプルな例から始めて、徐々に複雑にしていく。スタックのプッシュ/ポップ処理を慎重にデバッグする。
- iTextSharp の `PdfOutline` コンストラクタで指定する `pageNumber` は1から始まることを常に意識する。
5. エラーハンドリングの不足
- エラー: プログラムが予期せず終了する、エラーメッセージが不親切。
- 原因: ファイル操作、PDF処理、外部プログラム呼び出しなど、様々な箇所でエラーが発生する可能性があるにも関わらず、適切な `Try…Catch` ブロックが設定されていない。
- 回避策:
- VBA、VB.NETの双方で、重要な処理ブロックを `Try…Catch` で囲む。
- エラーが発生した場合は、エラーメッセージ、発生箇所、関連する変数などをログに出力する。
- `On Error GoTo` ではなく、`Try…Catch` を使用することを推奨(特にVB.NET)。
—
まとめ:Visio VBAの可能性を解き放つ
いかがでしたでしょうか?今回は、Visio VBAから一歩進んで、外部ライブラリと連携し、PDFのしおり(アウトライン)を高度にカスタマイズする方法について、基礎から実践までを解説しました。
この手法を習得することで、皆さんは単にVisio図面をPDFに出力するだけでなく、「PDFというフォーマット自体に、よりリッチな情報とナビゲーション機能を持たせる」という、高度な自動化を実現できるようになります。
今回のポイントを振り返りましょう:
- Visioの標準PDF出力では限界があることを理解する。
- PDF生成後に外部ライブラリ(例: iTextSharp)でPDFを直接操作するアプローチ。
- VB.NETでPDF操作用プログラムを作成し、VBAから呼び出す方法。
- Visioのページ情報、シェイプ情報、カスタムプロパティから、しおりデータを取得するテクニック。
- データ連携(JSONなど)と階層構築ロジックの重要性。
- 陥りやすいエラーとその回避策。
この技術をマスターすることは、確かに簡単ではありません。しかし、この「ここをクリアすれば、Visio VBAの基本はバッチリ」という確信を持って、ぜひ挑戦してみてください。皆さんのVisio VBAスキルが、さらに一段階レベルアップすることを心から願っています。
もし、この解説で分かりにくかった点や、さらに深掘りしたい点があれば、遠慮なくコメントで質問してくださいね。皆さんの「できた!」という声が、私にとって何よりの喜びです。
それでは、また次回のVisio VBA探求でお会いしましょう!
