PowerPoint for Macのアドインを作る

はじめに

パワポに複数の画像を貼り付けてトリミングサイズを合わせたいと思った時に,同時にトリミングがで来ませんでした.
一つずつ数値を入力するのはとてもとても時間と労力かかるので,同時にできるマクロをつくろうってわけです.

マクロに,数値を入力できるインターフェースが欲しい時があります.文字サイズのような.
そんな時に,アドインが使えるんじゃないかと思いました.知らんけど.

基礎ちしき

そもそもパワポファイルはxmlファイルをzipで圧縮したもの(エクセル,ワードも).
このxmlをいじると,自作のマクロをタブしちまうことだってできるでやんす.

ちなみに,拡張子をzipに変えて展開すればメディアファイル等を拾えるらしいです.やったことないけど.

拡張子

pptx:マクロ無し
pptm:マクロ有効化
ppam:アドイン(昔は.ppa)
Macの場合,アドインは名前をつけて保存ではなく,エクスポート…から出力できます.


必要なもの

・マクロ開発済み.pptmファイル.1つ
・zip圧縮・展開できるファールアーカイバアプリ.1つ.(ここでは,Kekaを使うよ!)
・エディタ.1つ.(必須ではないけど,扱うのはxmlなのであると便利!必須!)

やりかた

ながれ

マクロ有効化ファイル(.pptm)でタブの見た目を調整・確認後に,タブにします.なんならアドインにします.
1..pptmを展開
2.ファイルの数箇所を変更する
3..pptmに戻す.ボタンの配置とかの確認.
4.マクロの数箇所を変更する
5..ppamにしてアドインにしてしまう
注意点として,マクロを変更すると1から始めることになる.
というのも,2でxmlの編集を行い,編集の確認をするには3でpptmに必要がある.
が,実はマクロは1で展開した時点で.binファイルになり,変更できなくなる(したくなくなる).
そのため,3でpptmに戻すとマクロは1の展開時のものになり,2でマクロを編集しても無かったことになる.
xmlの編集と,マクロの編集は片方ずつでないとダメ.


1..pptmファイルの展開

ファイルアーカイバアプリで展開しましょう.
右クリック -> ファイルアーカイバKekaに送信(など)
フォルダが生まれます.

2.ファイルを変更する

VSCodeなら,拡張機能"XML"があれば,shft alt f でコードの整列ができます.

次の2つのファイルを変更と作成すると新しくタブを作ることができます.

_res/.rels

<Relationships>タグ内に,次の1行を追加.

<Relationship Id="myCustomUI" Type="http://schemas.microsoft.com/office/2006/relationships/ui/extensibility" Target="customUI/customUI.xml"/>


customUI/customUI.xml

customUIフォルダを作成し,以下の中身のcustom.xmlを作成.

<customUI xmlns="http://schemas.microsoft.com/office/2006/01/customui" onLoad="InitCustomTab">
  <ribbon startFromScratch="false">
    <tabs>
      <tab id="customTab" label="CUSTOM TAB" insertBeforeMso="TabHome">
        <group id="customGroup1" label="Shapes and Pictures" insertBeforeMso="TabHome">
          <editBox id="EditBoxCropWidth" label="Width:"  sizeString="WW" getText="GetEditBoxText" onChange="SetEditBoxText" />
          <editBox id="EditBoxCropHeight" label="Height"  sizeString="WW" getText="GetEditBoxText" onChange="SetEditBoxText" />
          <button id="GetSize" label="Get Crop Size" imageMso="PicturePropertiesSize" size="large" onAction="GetImgCropSize" />
          <button id="Apply" label="Apply Crop Size" imageMso="PicturesCompress" size="large" onAction="CropImgs" />
        </group>
      </tab>
    </tabs>
  </ribbon>
</customUI>

<customUI>

たまにxmlns属性に2007/09のものを指定しているものを見るが,Macでは無理ぽい?バージョンなのかな.
onLoar属性にマクロ名を指定するとそのマクロがタブを表示した時に呼び出される.

<tabタグ>

labelで指定したものがタブ名として表示される.
タブを並び替える場合,属性"insertAfterMso"or"insertBeforeMso"を追加すると可能.各タブのMsoを指定すればok.(調べてね)
例:ホームタブの左側
insertBeforeMso="TabHome"

<editbox>

フォントサイズ等入力する部分のやつ.
sizeSttingは入力部分のサイズ.指定した文字の分のサイズになる.文字幅の広いWを指定するものをよく見る.
onChangeは,入力後にEnterか別のものを選択した場合に呼び出すマクロ名
getTextは,入力ボックスがユーザに表示される時(タブを切り替えた時など)と,onChangeの呼び出し後によびだされる(と思う).

<button>

ボタン.
・imageMsoはボタンのアイコン.たくさんある.
参考:https://ymrt.jp/imagemso/index.html(2022年9月)
・sizeは大きさ.largeかnormal.
・onActionは,ボタン押された時に呼び出すマクロ.

3..pptmに戻す

展開したフォルダの中にある全てのファイルをまとめてアーカイブにします.
右クリック -> Kekaで圧縮
アーカイブ.zipが生まれます.
アーカイブ.zipの拡張子を.pptmに変更すると,開けるようになります.
タブができてるはず.

4.マクロの変更

Option Explicit
Dim crop_ratio As Single    ' 倍率の値
Public CustomRibbon As IRibbonUI    ' EditBoxの更新に必要
Private current_crop_width As String    ' 幅
Private current_crop_height As String    ' 高さ
Dim c As Single    ' ピクセル - cm間の変換に使う
Dim CustomIds As ids    ' xmlのid属性を間違えないようにユーザ定義
Private Type ids
    CropWidth As String
    CropHeight As String
End Type
    
Sub InitCustomTab(ribbon As IRibbonUI)    ' onLoadで絵呼び出す
    CustomIds.CropWidth = "EditBoxCropWeigh"
    CustomIds.CropHeight = "EditBoxCropHeight"
    
    Set CustomRibbon = ribbon
    current_crop_width = "-1"
    current_crop_height = "-1"

    c = 0.03527777777

End Sub

Sub GetEditBoxText(ByRef control As IRibbonControl, ByRef text)
    Dim size_pt_str As String
    size_pt_str = ""
    
    If control.Id = "EditBoxCropWeigh" Then size_pt_str = current_crop_width
    If control.Id = "EditBoxCropHeight" Then size_pt_str = current_crop_height
    
    If Val(size_pt_str) < 0 Then
        text = ""
    Else
        text = CStr(CInt(Val(size_pt_str) * 100) / 100)
    End If
End Sub

Sub SetEditBoxText(ByRef control As IRibbonControl, ByRef text As String)
    ' 入力してEnter 
    If Not IsNumeric(text) Then
        If control.Id = CustomIds.CropWidth Then text = current_crop_width
        If control.Id = CustomIds.CropHeight Then text = current_crop_height
        CustomRibbon.InvalidateControl (control.Id)

    Else
        If control.Id = CustomIds.CropWidth Then current_crop_width = text
        If control.Id = CustomIds.CropHeight Then current_crop_height = text
        CropImgs
    End If
    
End Sub

Sub GetImgCropSize()
    Dim w_before As Single, w_org As Single

    With ActiveWindow.Selection
        If .Type = ppSelectionShapes Then
            If .ShapeRange.Count > 0 Then
                If .ShapeRange(1).Type = msoPicture Then
                    .ShapeRange(1).LockAspectRatio = msoTrue

                ' crop size
                    current_crop_width = .ShapeRange(1).PictureFormat.Crop.ShapeWidth * c
                    current_crop_height = .ShapeRange(1).PictureFormat.Crop.ShapeHeight * c
                    CustomRibbon.Invalidate    ' EditBox 更新

                ' crop_ratio
                    w_before = .ShapeRange(1).PictureFormat.Crop.ShapeWidth
                    .ShapeRange(1).ScaleWidth 1, msoTrue
                    w_org = .ShapeRange(1).PictureFormat.Crop.ShapeWidth
                    crop_ratio = w_before / w_org
                    .ShapeRange(1).Width = w_before

                End If
            End If
        End If
    End With
End Sub

Sub CropImgs()

    Dim shp As Shape

    With ActiveWindow.Selection
        If .Type = ppSelectionShapes Then
            For Each shp In .ShapeRange
                If shp.Type = msoPicture Then
                    If crop_ratio > 0 Then
                        shp.LockAspectRatio = msoTrue
                        shp.ScaleHeight crop_ratio, msoTrue
                    End If
                    With shp.PictureFormat.Crop
                        If Val(current_crop_width) > 0 _
                            And Val(current_crop_height) > 0 Then
                            .ShapeWidth = Val(current_crop_width) / c
                            .ShapeHeight = Val(current_crop_height) / c
                        End If
                    End With
                End If
            Next
        End If
    End With
End Sub


ポイントは,マクロからEditBoxへ値を取得することができないという点.
Apply!ってボタン押しても,誰に値聞けばいいですか??になる.
ので,入力があったときにその値を保持する必要があり,それ(current_crop_width, height)をはじめに宣言しておく.


keytip
他のと競合があると、自動的に割り振られたものになる。
競合したものが表示されてなければ(有効になっていなければ)大丈夫。


5..ppamにしてアドインにしてしまう

エクスポート…から.ppamを選択して出力.
VBAコードはコンパイルできません...の場合,何か未定義の変数がある可能性あり.

アドイン化はマクロが1つもないとアドイン(*.ppam)形式で保存できない。
アドインの追加は、開発タブを表示させて、開発タブのアドインの追加化じゃないとできない?


要素
[MS-CUSTOMUI]: Custom UI | Microsoft Learn
idMsoを指定して、Labelを表示させなくするには、showLabel="false"を追加。

idMso
[MS-CUSTOMUI]: idMso Tables | Microsoft Learn