【実務・中級編】【中級者】スライド内の全テキストボックスからフォント名を抽出し、社内標準フォントへ一斉置換してから別名保存するフォントマネージャー – PowerPoint VBA解析バイブル

スポンサーリンク
スポンサーリンク

プレゼン資料の「フォント汚染」を根絶せよ:堅牢なフォントマネージャーの設計思想と実装

外部ベンダーから納品された資料、あるいは数年前の担当者が遺したレガシーなPowerPointファイル。それらを開いた瞬間、あなたの目に飛び込んでくるのは、バラバラなフォント、不自然な文字間隔、そしてブランドアイデンティティを損なう「フォントの不一致」ではないだろうか。

これらを一つずつ手作業で修正するのは、エンジニアリングとは呼べない。しかし、単に「全テキストを置換する」という安易なマクロを書けば、グループ化された図形を見落としたり、特定の箇所だけフォントが変わらずに残ったりといった、中途半端な成果物を生むことになる。

本記事では、単なる「置換マクロ」の枠を超えた、プロダクション環境に耐えうる「フォントマネージャー」の設計手法を伝授する。

1. アマチュアとプロフェッショナルの境界線:設計思想

多くの開発者が陥る罠がある。それは「スライド内のShapeをループで回せば済む」という短絡的な思考だ。プロフェッショナルは、以下の3つの観点から設計を組み立てる。

A. 再帰的探索(Recursive Traversal)の必要性

PowerPointのShapeオブジェクトは、単なる平坦なリストではない。`GroupShape` の中にさらに `Shape` が存在する階層構造を持っている。単純な `For Each shp In sld.Shapes` では、グループ化された中のテキストボックスは「不可視」となる。これを突破するには、再帰関数による探索が不可欠だ。

B. TextRangeの粒度(Granularity)

一つのテキストボックス内に、複数のフォントが混在しているケースは多々ある。`TextFrame.TextRange.Font.Name` を書き換えるだけでは、文字単位の細かな書式設定(Runs)を破壊する恐れがある。我々が目指すのは、「既存の書式(サイズや色)を維持したまま、フォント名だけを正確に射抜く」設計だ。

C. 冪等性と副作用の制御

「別名保存」は絶対条件だ。元のファイルを破壊するマクロは、業務自動化ツールとしては欠陥品である。また、ファイル保存時のエラー(書き込み権限不足、同名ファイルが存在するなど)を想定した例外処理が、システムの信頼性を決定づける。

2. プロダクション・コード:フォント・正規化エンジン

以下に、私が設計した堅牢なコード例を示す。このコードは、再帰的な探索、テキストのRun単位での走査、そして安全な別名保存を実装している。

Option Explicit

' =================================================================
' 設定セクション:ここを社内標準に合わせて変更する
' =================================================================
Private Const TARGET_FONT_NAME As String = "Yu Gothic UI" ' 統一したいフォント名
Private Const SAVE_SUFFIX As String = "_Standardized"    ' 保存時の接尾辞

' =================================================================
' メインルーチン:フォント正規化プロセスを実行
' =================================================================
Public Sub ExecuteFontNormalization()
    Dim pptPres As Presentation
    Dim targetPath As String
    Dim newPath As String
    Dim slideIdx As Integer
    
    Set pptPres = ActivePresentation
    
    ' 1. ユーザーへの確認(誤操作防止)
    If MsgBox("現在のプレゼンテーションの全フォントを「" & TARGET_FONT_NAME & "」に統一し、別名で保存しますか?", _
              vbQuestion + vbYesNo, "フォントマネージャー実行") <> vbYes Then Exit Sub

    On Error GoTo ErrorHandler
    
    ' 2. 全スライドの走査
    Debug.Print "--- Process Started: " & Now & " ---"
    For Each sld In pptPres.Slides
        Debug.Print "Processing Slide: " & sld.SlideIndex
        ProcessSlideShapes sld.Shapes
    Next sld
    
    ' 3. 別名保存の準備
    targetPath = pptPres.FullName
    If targetPath = "" Then
        MsgBox "ファイルを保存してから実行してください。", vbCritical
        Exit Sub
    End If
    
    ' 拡張子を除いたパスを作成
    newPath = Left(targetPath, InStrRev(targetPath, ".") - 1) & SAVE_SUFFIX & _
              Mid(targetPath, InStrRev(targetPath, "."))
    
    ' 4. 保存実行 (SaveCopyAs を使用して元のインスタンスへの影響を最小化)
    pptPres.SaveCopyAs newPath
    
    MsgBox "完了しました!" & vbCrLf & "保存先: " & newPath, vbInformation, "Success"
    Exit Sub

ErrorHandler:
    MsgBox "致命的なエラーが発生しました。" & vbCrLf & _
           "Error No: " & Err.Number & vbCrLf & _
           "Description: " & Err.Description, vbCritical, "System Error"
End Sub

' =================================================================
' スライド内のShapeを再帰的に処理する関数
' =================================================================
Private Sub ProcessSlideShapes(ByRef shpCollection As Shapes)
    Dim shp As Shape
    
    For Each shp In shpCollection
        ' グループ化されている場合は再帰呼び出し
        If shp.Type = msoGroup Then
            ProcessSlideShapes shp.GroupItems
        Else
            ' テキストを持つShapeか判定
            If HasText(shp) Then
                ReplaceFontInShape shp
            End If
        End If
    Next shp
End Sub

' =================================================================
' テキストボックス内のフォントをRun単位で精密に置換する
' =================================================================
Private Sub ReplaceFontInShape(ByRef shp As Shape)
    Dim tr As TextRange
    Dim i As Integer
    
    Set tr = shp.TextFrame.TextRange
    
    ' TextRange.Runs を走査することで、文字ごとの書式(フォント違い)を個別に制御
    ' これにより、文字サイズや色は維持しつつ、フォント名だけを確実に変更できる
    For i = 1 To tr.Runs.Count
        tr.Runs(i).Font.Name = TARGET_FONT_NAME
        ' アジア言語フォント(日本語フォント)も明示的に指定
        tr.Runs(i).Font.NameFarEast = TARGET_FONT_NAME
    Next i
End Sub

' =================================================================
' Shapeがテキストを保持しているか判定するヘルパー関数
' =================================================================
Private Function HasText(ByRef shp As Shape) As Boolean
    On Error Resume Next
    HasText = (shp.TextFrame.HasText = msoTrue)
    On Error GoTo 0
End Function

3. アーキテクトの深掘り:なぜこの書き方なのか

このコードが、なぜ「コピペで動く」だけでなく「現場で使える」のか。その技術的根拠を解説する。

「Runs」へのこだわり

多くの初心者は `shp.TextFrame.TextRange.Font.Name = “…”` と書く。しかし、これでは「そのテキストボックス内のすべての文字が、一律の書式に上書きされる」リスクがある。例えば、「重要」という文字だけ赤字で大きくしている場合、全体置換を行うと書式が崩れる可能性がある。
`TextRange.Runs` をループすることで、書式が変わる境界線ごとにオブジェクトを捉え、フォント名だけをピンポイントで狙い撃つ。これがプロの精密さだ。

再帰構造の設計

`ProcessSlideShapes` 内で `If shp.Type = msoGroup` を判定し、自分自身(`ProcessSlideShapes`)を呼び出す設計は、ツリー構造を探索する際の定石だ。これにより、グループの中にグループがある「入れ子構造」であっても、末端のテキストボックスまで確実に到達できる。

`SaveCopyAs` の選択

`SaveAs` を使うと、現在開いているPowerPointのファイル自体が新しいファイルに切り替わってしまう。これは、マクロ実行後に「あれ、元のファイルはどこへ行った?」という混乱を招く。`SaveCopyAs` を使えば、現在の作業状態を維持したまま、バックグラウンドで複製を作成・保存できる。ユーザー体験(UX)の観点から、この選択は極めて重要だ。

4. 実務への導入アドバイス

このツールを組織に展開する際は、以下の点に留意せよ。

1. フォントの存在確認: `TARGET_FONT_NAME` に指定するフォントが、実行環境(全社員のPC)にインストールされていることを前提とすること。インストールされていない場合、PowerPointは代替フォントを勝手に割り当てるが、それは「ブランド統一」の目的から外れる。
2. データベース連携の拡張: もし、複数のブランド(製品A用、製品B用など)がある場合は、`Const` ではなく、Excelファイルや外部のJSON/CSVからフォント名を取得するように設計を変更せよ。
3. ログの重要性: 大規模な処理を行う場合、どのスライドでエラーが起きたかを記録するログ出力機能を追加すると、保守性が飛躍的に向上する。

結びに

自動化の本質は、単に「速くすること」ではない。「人間が介在すべきでない、退屈でミスが起きやすい作業から人間を解放すること」にある。

このフォントマネージャーは、その哲学に基づいた設計となっている。技術の表面をなぞるのではなく、オブジェクトの構造とライフサイクルを理解したコードを書くこと。それが、伝説的なエンジニアへの第一歩だ。

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