【テクニカル・上級編】【初心者向け】配置されたすべてのビットマップ画像に対して、リンク状態か埋め込み状態かを一発判定してログ化するツール – CorelDRAW VBA解析バイブル

スポンサーリンク

CorelDRAW VBAを掌握する極限の知見:アセット運用の防衛線

印刷入稿の現場や、複数デザイナーによる協業環境において、ベクタードキュメント内に混在する外部ビットマップの「リンク状態」と「埋め込み状態」の混同は、幾度となく致命的なトラブルを引き起こしてきた。出力直前になって「リンク切れの低解像度プレビューが出力されていた」「ファイルサイズが異常に肥大化している」といった失態を防ぐため、我々シニアエンジニアは属人的な目視確認を排除し、コードによる完全な自動スキャン体制を構築しなければならない。

今回は、CorelDRAWドキュメント内に配置されたすべてのビットマップシェイプを走査し、それが「外部参照(Linked)」なのか「内部保持(Embedded)」なのかを瞬時に判別、即座にログ化する実用的なVBAスクリプトを公開する。

単なるコードの提示にとどまらない。CorelDRAWのオブジェクトモデルの裏側で何が起きているのか、メモリ管理の鉄則と併せて、プロフェッショナルが実務で即座に使えるレベルの知見を授けよう。

—

1. CorelDRAWオブジェクトモデルの深淵:ShapeRangeとLinked Bitmaps

CorelDRAW VBAにおける最大の罠は、一見シンプルに見えるドキュメント構造の裏側に、COM(Component Object Model)特有の参照管理の重さが潜んでいる点にある。特に大量のシェイプを扱う際、不適切なオブジェクト参照はメモリリークやCorelDRAW自体のフリーズを誘発する。

ビットマップアセットの状態を判定するためには、単に `ActivePage.Shapes` をループさせるだけでは不十分だ。グループ化されたシェイプ(`cdrGroupShape`)、パワー・クリップの内部(`cdrPowerClip`)、さらにはレイヤーの階層構造を再帰的に、あるいは効率的にイテレートする必要がある。

リンク状態と埋め込み状態の判定ロジック

CorelDRAWのAPIにおいて、ビットマップシェイプは `Shape` オブジェクトであり、その `Type` プロパティが `cdrBitmapShape` である時、さらに `Bitmap` プロパティを通じて内部情報にアクセスできる。
ここで重要となるのが、そのビットマップが外部ファイルへのパスを持っているか、あるいは完全にドキュメント内にカプセル化されているかの判定である。API仕様の深部において、リンクされた画像は外部パスの参照を保持し、埋め込み画像はその参照を持たない(あるいは内部ストリームとして完結している)。この特性を利用してステータスを抽出する。

—

2. 実装コード:Bitmap Status Auditor

以下のコードは、現在のドキュメントを走査し、すべてのビットマップのステータスをイミディエイトウィンドウ(および必要に応じてテキストログ)に出力するプロダクションクオリティのスクリプトである。

‘ ==============================================================================
‘ 模块名: ModBitmapAuditor
‘ 概要: ドキュメント内の全ビットマップのリンク/埋め込み状態をスキャンする
‘ 著者: チーフアーキテクト
‘ ==============================================================================
Option Explicit

Public Sub AuditBitmapAssetsStatus()
‘ パフォーマンス最適化の基本:画面描画とイベントを完全に凍結
EventsEnabled = False
ActiveDocument.BeginCommandGroup “Bitmap Status Audit”

Dim startTime As Double
startTime = Timer

On Error GoTo ErrorHandler

‘ ルートドキュメントの存在確認
If ActiveDocument Is Nothing Then
MsgBox “アクティブなドキュメントが存在しません。”, vbCritical, “エラー”
GoTo Cleanup
End If

Debug.Print “=== ビットマップアセット・監査ログ 開始 ===”
Debug.Print “対象ドキュメント: ” & ActiveDocument.Name
Debug.Print “————————————————–”

Dim totalCount As Long
Dim linkedCount As Long
Dim embeddedCount As Long

totalCount = 0
linkedCount = 0
embeddedCount = 0

‘ ページ単位での走査(マルチページドキュメントに対応)
Dim shpPage As Page
For Each shpPage In ActiveDocument.Pages
Debug.Print “[ページ: ” & shpPage.Index & ” の走査]”

‘ 再帰的にシェイプを走査するプロシージャを呼び出し
Call ScanShapesRecursive(shpPage.Shapes, totalCount, linkedCount, embeddedCount)
Next shpPage

Debug.Print “————————————————–”
Debug.Print “【監査完了】”
Debug.Print “総ビットマップ数: ” & totalCount
Debug.Print ” – リンク状態 : ” & linkedCount
Debug.Print ” – 埋め込み状態 : ” & embeddedCount
Debug.Print “処理時間: ” & Format(Timer – startTime, “0.00秒”)
Debug.Print “==================================================”

MsgBox “スキャンが完了しました。” & vbCrLf & _
“総数: ” & totalCount & vbCrLf & _
“リンク: ” & linkedCount & vbCrLf & _
“埋め込み: ” & embeddedCount, vbInformation, “監査完了”

Cleanup:
‘ 変更がないためコマンドグループをキャンセルして終了
ActiveDocument.EndCommandGroup
EventsEnabled = True
Exit Sub

ErrorHandler:
MsgBox “予期せぬエラーが発生しました: ” & Err.Description, vbCritical, “システムエラー”
Resume Cleanup
End Sub

‘ ==============================================================================
‘ 内部関数: 再帰的シェイプ走査エンジン
‘ ==============================================================================
Private Sub ScanShapesRecursive(ByVal shapesCol As Shapes, ByRef total As Long, ByRef linked As Long, ByRef embedded As Long)
Dim shp As Shape
Dim bmp As SubMap ‘ または Bitmap オブジェクトの文脈に応じた型

For Each shp in shapesCol
‘ グループやレイヤーのコンテナを考慮し、種類ごとに分岐
Select Case shp.Type
Case cdrBitmapShape
total = total + 1

‘ リンク状態の判定
‘ ※CorelDRAWのバージョンによりプロパティの挙動が微小に異なるため、
‘ 外部パスの有無(LinkFileName等)で堅牢に判定する
On Error Resume Next
Dim linkPath As String
linkPath = “”

‘ ビットマップオブジェクトへの参照を取得
If Not shp.Bitmap Is Nothing Then
linkPath = shp.Bitmap.LinkFileName
End If
On Error GoTo 0

If linkPath <> “” Then
linked = linked + 1
Debug.Print ” [ID: ” & shp.StaticID & “] 状態: 【リンク】 Path: ” & linkPath
Else
embedded = embedded + 1
Debug.Print ” [ID: ” & shp.StaticID & “] 状態: 【埋め込み】 (内部保持)”
End If

Case cdrGroupShape
‘ グループ化されている場合は内部のシェイプを再帰処理
Call ScanShapesRecursive(shp.Shapes, total, linked, embedded)

Case cdrPowerClipShape
‘ パワー・クリップ内のオブジェクトも漏らすことなく走査
If Not shp.PowerClip Is Nothing Then
Call ScanShapesRecursive(shp.PowerClip.Shapes, total, linked, embedded)
End If

Case Else
‘ その他のシェイプコンテナ(ブレンド、コンテキスト等)の考慮が必要な場合はここに追加
If shp.Shapes.Count > 0 Then
Call ScanShapesRecursive(shp.Shapes, total, linked, embedded)
End If
End Select

‘ オブジェクトのライフサイクル管理:VBAのCOM参照解放の鉄則
Set shp = Nothing
Next shp
End Sub

—

3. チーフアーキテクトが解説するメモリ最適化と設計思想

上記のコードは一見して単なるループ処理に見えるが、CorelDRAWのアーキテクチャを熟知した者にとって、いくつかの極めて重要な設計判断が組み込まれている。

1. `EventsEnabled = False` による描画とイベントの完全遮断

CorelDRAW VBAにおいて、マクロ実行中にドキュメント内のオブジェクトにアクセスしたり走査したりする際、GUIの画面更新やバックグラウンドのイベントハンドラが動作していると、パフォーマンスが数十倍〜数百倍に低下する。それだけでなく、描画競合による予期せぬクラッシュの温床となる。処理の冒頭で `EventsEnabled = False` とし、確実に `True` に復帰させる(エラーハンドラ内でも必ず通す)ことは、シニアエンジニアにとっての絶対的なマナーである。

2. 再帰処理におけるCOMオブジェクトの明示的破棄

VBAのガベージコレクションは非常に緩慢である。特に `For Each` ループ内で `Shape` オブジェクトを次々と参照していくと、VBAの背後にあるCOMラッパーの参照カウンタが増え続け、メモリを圧迫していく。
ループの末尾で `Set shp = Nothing` を明示的に呼び出し、参照を即座に解放することで、数千点に及ぶ巨大なベクターデータであってもメモリリークを完全に防ぐことができる。

3. PowerClipとグループの網羅的捕捉

多くの初学者が見落とすのが、`cdrGroupShape` や `cdrPowerClipShape` の存在だ。これらは通常の `ActivePage.Shapes` コレクションのトップレベルには現れない、あるいは中身が隠蔽されている。コンテナ構造を持つオブジェクトを発見したならば、必ずその内部の `.Shapes` プロパティを再帰的に叩く設計にしなければ、実務レベルの監査ツールとしては使い物にならない。

—

4. さらなる高みへ:システム間連携と自動化の拡張

このスクリプトを単体のマクロとして手動実行させるだけでは、真の自動化エンジニアとは言えない。このロジックをベースにして、以下のようなシステム間連携へと昇華させることが可能だ。

  • バッチ処理(ヘッドレス実行):

Windows Script Host (WSH) や VBScript、あるいは外部の C# / Python プログラムから CorelDRAW をバックグラウンド起動(Visible = False)させ、このマクロをサイレント実行。指定フォルダ内の全 `.cdr` ファイルを夜間に自動スキャンし、リンク切れの画像をリストアップしてSlackやTeamsに通知するCI/CDパイプライン的な印刷前監査システムの構築。

  • CSV/JSONログ出力:

Debug.Print へ出力するだけでなく、`Scripting.FileSystemObject` を用いて、結果をタイムスタンプ付きのCSVとしてサーバーに吐き出すことで、品質管理データベースへの統合。

描画ソフトの内部構造を理解し、APIの特性をコントロール下に置くこと。それこそが、手作業のミスを根絶し、ビジネスの信頼性を担保する唯一無二のエンジニアリングである。

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