【テクニカル・上級編】【上級者向け】VisioのPDF出力エンジンが生成するPDFのしおり(アウトライン)をVBAでカスタマイズする – Visio VBA解析バイブル

スポンサーリンク

極限の技術:Visio PDF出力における「しおり」構造の再構築

Visio VBAを極めた者にとって、単なる`ExportAsFixedFormat`の呼び出しは「初歩」に過ぎない。しかし、エンタープライズレベルの要件、例えば数百ページに及ぶ設備図面や、複雑な階層構造を持つシステムインフラ図をPDF化する際、標準機能が生成する「ページ名が並ぶだけのしおり」は、もはや使い物にならない。

真のアーキテクトが求められるのは、図面内の特定のシェイプ(例えば「ビル名」や「ラック番号」)を動的に抽出し、PDFのメタデータ構造(アウトライン・ディクショナリ)へと再マッピングする技術だ。

本稿では、Visioの描画エンジンと外部PDFライブラリを高度に連携させ、標準機能の限界を突破する手法を詳説する。

—

1. 標準エンジンの限界と「外部介入」の必要性

Visioの`ExportAsFixedFormat`メソッドは、PDF/A準拠などの優れた点はあるが、しおり(ブックマーク)の生成ロジックはブラックボックス化されている。基本的には「ページ名」がそのまましおりになるだけであり、ページ内の階層構造を反映させることはできない。

これを制御するには、以下の2つのアプローチしかない。

1. Post-Process(後処理): PDFを出力した後、Adobe Acrobat APIやiTextSharp等を用いてアウトラインを書き換える。
2. Virtual Print(仮想印刷): PDFプリンタドライバを制御し、印刷命令の中にブックマーク制御コードを埋め込む。

現代的な自動化システムにおいて、最も堅牢なのは「後処理」だ。特に、Adobe Acrobat SDK(Acrobat Proが必要)を利用したCOM経由の操作は、Windows環境におけるデファクトスタンダードである。

—

2. オブジェクトのライフサイクルとメモリの厳密な管理

VBAからVisioとAcrobatという2つの巨大なCOMサーバを操作する場合、最も警戒すべきは「ゴーストプロセス」の発生だ。不適切な参照保持は、メモリリークを引き起こし、バッチ処理を数時間で停止させる。

‘ 参照設定: Adobe Acrobat 10.0 Type Library (もしくはそれ以降)
‘ 注意: Acrobat Readerでは不可。Pro版が必要。

Option Explicit

Public Sub ExportHighPerformancePDF()
Dim vsoApp As Visio.Application
Dim vsoDoc As Visio.Document
Dim pdfPath As String: pdfPath = “C:\Output\ComplexDiagram.pdf”

Set vsoApp = GetVisioInstance()

‘ ドキュメントのオープン(リードオンリー・非表示で高速化)
Set vsoDoc = vsoApp.Documents.OpenEx(“C:\Source\MasterTemplate.vsdx”, visOpenRO + visOpenHidden)

Try
‘ 1. 標準PDFエクスポート(まずはベースを生成)
‘ IntentはPrint(0)、OptimizationはOnScreen(1)を推奨
vsoDoc.ExportAsFixedFormat visFixedFormatPDF, pdfPath, visDocExIntentPrint, visDocExOptimizeOnScreen

‘ 2. PDFアウトラインの再構築(後述のコアロジック)
CustomizePDFBookmarks pdfPath, vsoDoc

Catch:
‘ エラー処理の実装
Finally:
‘ 決定的な解放プロセス
If Not vsoDoc Is Nothing Then vsoDoc.Close: Set vsoDoc = Nothing
‘ Visio本体を落とす場合は慎重に
End Try
End Sub

Private Function GetVisioInstance() As Visio.Application
On Error Resume Next
Set GetVisioInstance = GetObject(, “Visio.Application”)
If GetVisioInstance Is Nothing Then
Set GetVisioInstance = New Visio.Application
End If
End Function

—

3. Acrobat APIを用いたアウトラインの動的インジェクション

ここが本題だ。Visioのシェイプ情報を解析し、PDFの「ページ番号」と「座標」に紐付いたしおりを作成する。

Private Sub CustomizePDFBookmarks(ByVal pdfPath As String, ByRef vsoDoc As Visio.Document)
Dim acroApp As Object ‘ Acrobat.AcroApp
Dim avDoc As Object ‘ Acrobat.CAVGetDoc
Dim pdDoc As Object ‘ Acrobat.CApPDDoc
Dim jso As Object ‘ JavaScript Object (Acrobatの強力なバックドア)
Dim i As Integer

Set acroApp = CreateObject(“AcroExch.App”)
Set pdDoc = CreateObject(“AcroExch.PDDoc”)

‘ PDFファイルを開く
If pdDoc.Open(pdfPath) Then
‘ AcrobatのJSオブジェクトを取得(VBAの限界を超えるためのテクニック)
Set jso = pdDoc.GetJSObject

‘ 既存のしおりを全削除(クリーンな状態から構築)
‘ ※標準エクスポートが作ったゴミを排除する
jso.BookmarkRoot.remove

‘ Visioの各ページ、あるいは特定のシェイプを走査
Dim vsoPage As Visio.Page
For Each vsoPage In vsoDoc.Pages
If vsoPage.Background = 0 Then ‘ 背景ページを除外
‘ ページレベルのしおり追加
‘ jso.bookmarkRoot.createChild(名前, JSコード, インデックス)
Dim parentBM As Object
Set parentBM = jso.BookmarkRoot.createChild(vsoPage.Name, “this.pageNum = ” & (vsoPage.Index – 1), vsoPage.Index – 1)

‘ 【上級テクニック】ページ内の特定の重要シェイプを子しおりとして登録
‘ 例えば「機能ブロック」シェイプのみを抽出
Dim vsoShape As Visio.Shape
For Each vsoShape In vsoPage.Shapes
If vsoShape.CellExists(“User.IsBookmark”, visExistsAnywhere) Then
If vsoShape.CellsU(“User.IsBookmark”).ResultStr(“”) = “1” Then
‘ 子しおりを作成
parentBM.createChild vsoShape.Text, “this.pageNum = ” & (vsoPage.Index – 1), 0
End If
End If
Next vsoShape
End If
Next vsoPage

‘ 保存とクローズ
pdDoc.Save 1, pdfPath ‘ 1 = PDSaveFull
pdDoc.Close
End If

‘ 解放の儀式
Set jso = Nothing
Set pdDoc = Nothing
acroApp.Exit
Set acroApp = Nothing
End Sub

4. 実践における勘所:Win32 APIによるファイルロック待機

Visioの`ExportAsFixedFormat`は非同期的にファイルI/Oを完了させることがある。つまり、メソッドを抜けた直後にAcrobatでファイルを開こうとすると、「ファイルが使用中」でエラーになるリスクがある。

これを回避するために、Win32 APIを用いてファイルの書き込み完了を監視するコードを挟むのがプロの仕事だ。

Private Declare PtrSafe Sub Sleep Lib “kernel32” (ByVal dwMilliseconds As LongPtr)

Private Sub WaitForFileReady(filePath As String)
Dim fso As Object
Set fso = CreateObject(“Scripting.FileSystemObject”)
Dim startTime As Double
startTime = Timer

‘ 最大10秒待機
Do While Timer < startTime + 10 On Error Resume Next Dim f As Integer: f = FreeFile Open filePath For Binary Access Read Write Lock Read Write As #f Close #f If Err.Number = 0 Then Exit Do On Error GoTo 0 Sleep 500 ' 0.5秒待機 DoEvents Loop End Sub ---

5. アーキテクトとしての結論

Visio VBAによるPDF出力制御の本質は、Visioという「描画エンジン」から、PDFという「構造化文書」へのセマンティックな変換にある。

1. Visio側では、`User-Defined Cells`を活用して、どのシェイプがメタデータ(しおり)として価値を持つかを定義しておく。
2. VBA側では、VisioのCOMツリーを舐めるように走査し、必要な情報をシリアライズする。
3. Acrobat APIを介して、生成されたPDFのバイナリ構造を操作し、インデックスを再構築する。

この一連の流れを、`Try-Catch-Finally`による厳密なオブジェクト管理下で実行することで、初めて「止まらない自動化システム」が完成する。レガシーなVBAを現代的なシステムインテグレーションの武器に変えるのは、こうした細部への執拗なまでのこだわりである。

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