（LaTeXで作った）PDFのスライドを画像化してPowerPointに貼り付けるマクロ

:::note info
この記事は <a href="https://adventar.org/calendars/6724">TeX ＆ LaTeX Advent Calendar 2021</a> の10日目の記事です.
9日目は <a href="https://blog.wtsnjp.com/2021/12/09/new-packages/">wtsnjp</a>さんです. 11日目は<a href="https://ubutun.blogspot.com/2021/12/opencv-pythonpdflatexpdf.html">hilssh</a>さんです.
:::


## 背景

私はこれまで LaTeX (Beamerなど) を利用してプレゼン資料を作っていましたが, その際に「スライドの中に動画を入れたい」と思うことがしばしばありました.
しかし, LaTeXでPDFファイルの中に動画を埋め込むには, それなりの苦労が必要です (不可能ではない).
Microsoft PowerPointであれば簡単に動画を入れることができますが, 今度は数式を入れるのが大変になってしまいます.
また, オンラインでのプレゼンの場合, PowerPointのレーザーポインタ機能が結構便利で重宝しますが, PDFによるプレゼンの場合は (簡単には) できません.

そういうわけで, 「スライド (数式) はLaTeXで作るが, PowerPointでプレゼンしたい」という気持ちになったため, 色々と検討した結果, 「**LaTeXでスライドを作り, それを画像化し, PowerPointに貼り付ければ良い**」という~~おバカな~~結論に至りました.
ただし, その作業を1ページづつ手作業でやるとなると非常に面倒なため, **PowerPointのマクロを使って自動化する**ことにしました.

:::note alert
本記事は, <strong>PDFのスライドを画像 (SVG) 化してPowerPointに貼り付けるマクロを紹介する</strong>記事です. <strong>LaTeXは一切出てきません.</strong>
:::

## 効能

PowerPointの機能をフルに使えるので, 動画を埋め込めるだけでなく, 他のこともできるようになります.
例えば, PDFを変換した画像の上から白い図形を重ねて, アニメーション機能によって図形が消えるようにしておくと, スライドの一部分だけ表示しておいて, 後から残りの部分を表示することができます. 
Beamerの場合, `\pause` などのコマンドを使ってこのような効果を出すことができましたが, もっと楽にできるようになります.
ということは, そもそもBeamerを使う必要がなくなります. (！)[^1]
また, 図の挿入もPowerPoint側でやることで楽することができます (出力位置の調整とか...！).

[^1]: ちなみに著者は最近, tcolorbox + preview パッケージによってスライドを作っています.

## どういうマクロを作ったか

以下の手順でPDFファイルを画像化してPowerPointに貼り付けています.

1. `pdfseparate` によってPDFファイルを1ページ毎に分割する
1. `pdftocairo` によって, 分割したPDFファイルをSVGに変換する
1. マクロの機能で1ページ毎にSVGファイルを全体に拡大して貼り付ける

![flow.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/111178/bd172a82-0453-0fcb-d2a1-b81f23e6b169.png)


`pdfseparate` と `pdftocairo` は, [Poppler](https://poppler.freedesktop.org/) という,「PDFドキュメントの閲覧に用いられるフリーのプログラミングライブラリ」(by [Wikipedia](https://ja.wikipedia.org/wiki/Poppler)) に含まれるツールです.

実際には, マクロによって `pdfseparate` と `pdftocairo` を呼び出して実行しているので, 上のすべての手順をマクロのみで実行してます.


作ったファイル ( `pdf2pptx.pptm` ) をGitHubに置いておきます:

https://github.com/t-kemmochi/pdf2pptx


ソースコードに興味のある方は[後述のセクション](#マクロのコード)を参照してください.


## 本記事の前提

ここからはマクロの使い方について紹介します. まずは, マクロを使うにあたって, 以下の2つを仮定します.

### 1. Windows環境である

他の環境下で同じようにマクロが動くかはわからないので... (動くかもしれません)


### 2. Poppler ライブラリがインストールされており, コマンドラインで使える

先述の通り, 本マクロはPopplerに含まれるツールを利用するので, これらがインストールされている必要がありますが, **Windowsの場合, TeX Live (2017以降) でTeXをインストール済みであれば, 一緒にインストールされているはず**です.

Mac, Linuxの場合は, 別途インストールが必要のようです. (cf. [TeX Wiki](https://texwiki.texjp.org/?Xpdf))

PowerShell等で `pdfseparate` や `pdftocairo` を実行可能であれば, 大丈夫です.


## マクロの使い方

以下の手順によって, PDFファイルをSVG化し, PowerPointに1ページづつ貼り付けます.


1. PowerPointに「開発」タブを表示する
1. 上記マクロファイルを変換したいPDFと同じディレクトリに置く 
1. マクロファイルを開き, 「コンテンツの有効化」をクリック
1. 「開発」タブの「マクロ」ボタンをクリック
1. 出てきたウィンドウで `PDFtoPPTX` を選択し, 「実行」をクリック
1. PDFのファイル名を拡張子込みで入力し, 「OK」をクリック
1. PDFファイルが分割され, SVGファイルに変換され, PowerPointに1ページづつ貼り付けられるのを眺める

以下でもう少し説明します.

:::note warn 
マクロ <code>PDFtoPPTX</code> は<strong>最初にPowerPointの全ページを削除します.</strong>
すでにPowerPointの方を編集済みである場合には, 後述する<a href="#すでに出来上がっているスライドの背景を差し替えるマクロ">別のマクロ</a>を用いてください.
:::


### 1. PowerPointに「開発」タブを表示する

下記の手順4のような「開発」タブが表示されていない場合, 次のリンクを参考に設定してください.

https://support.microsoft.com/ja-jp/topic/-%E9%96%8B%E7%99%BA-%E3%82%BF%E3%83%96%E3%82%92%E8%A1%A8%E7%A4%BA%E3%81%99%E3%82%8B-e1192344-5e56-4d45-931b-e5fd9bea2d45



### 2. 上記マクロファイルを変換したいPDFと同じディレクトリに置く 

マクロの方のファイル名をPDFと同じにしておくと少しだけ楽です.

### 3. マクロファイルを開き, 「コンテンツの有効化」をクリック

デフォルトのセキュリティ設定だと, 下のようなポップアップが出ると思いますので, 「コンテンツの有効化」をクリックしてください.

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/111178/c7f6dcd7-32a0-fffe-2805-2e44c38a79cb.png)

ダウンロードした直後の場合は, 先に以下のようなポップアップが出ると思いますが, その場合は「編集を有効にする」をクリックしてください.

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/111178/9b4b7a2b-0db6-3971-feb6-392c07050886.png)


### 4. 「開発」タブの「マクロ」ボタンをクリック

上のタブの右の方の「開発」タブをクリックし, 左の方の「マクロ」をクリックします.

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/111178/d100b2d1-04fb-737a-23e1-c7dabac3a693.png)


### 5. 出てきたウィンドウで `PDFtoPPTX` を選択し, 「実行」をクリック

「マクロ」をクリックすると, 以下のようなウィンドウが出てきます.
上記ファイルにはマクロが2つ書かれているのですが, `PDFtoPPTX` の方をクリックし, 実行します.
`Replaceback` の方は[後述](#すでに出来上がっているスライドの背景を差し替えるマクロ)します.

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/111178/65a62a85-1961-6aa4-dcb5-6c5359b47aae.png)


### 6. PDFのファイル名を拡張子込みで入力し, 「OK」をクリック

以下のようなウィンドウが現れるので, PDFファイル名を入力し, 「OK」をクリックしてください.
デフォルトでマクロファイルのファイル名が入力されているので, PDFファイルとマクロファイルを同じ名前にしている場合は, 入力の必要はありません.

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/111178/e07d446b-e79a-f075-ea70-2c61f7baa9b0.png)


### 7. PDFファイルが分割され, SVGファイルに変換され, PowerPointに1ページづつ貼り付けられるのを眺める

手順6まででやることは終わりです. PDFが変換されるのを待ってください.


## すでに出来上がっているスライドの背景を差し替えるマクロ

PDFをPowerPointに貼り付けて, PowerPointのファイルを編集した後に, PDFファイルにミスが見つかったとします.
その場合, 上の `PDFtoPPTX` を再実行してしまうと, スライドの全ページが削除されてしまうので, これまでの編集が無に帰します.

そのために, 背景となっているPDFを元にした画像のみを差し替えるマクロ `ReplaceBack` を用意しています.
使い方は `PDFtoPPTX` と同じですが, 実行すると, 全ページの背景画像のみが差し替わります.

:::note warn
<code>ReplaceBack</code>は「PDFから変換した画像が一番下にある」という前提のもとで動作します.
そうでない場合 (PDFから変換した画像の下に何かしらのオブジェクトがある場合) は, うまくいきません.
:::

## マクロのコード

以下にマクロのコードを記載します. 特に説明はしませんが, 何をやっているかは雰囲気でわかるのではと思います.
結構変な部分もあると自覚していますが, 動くので良しとしています.

<details>
<summary>`PDFtoPPTX` のコード</summary>
<div>

```vb
Sub PDFtoPPTX()
    Set Presen = ActivePresentation
    ChDir Presen.Path

    ' Get file name
    DefaultFileName = CreateObject("Scripting.FileSystemObject").GetBaseName(ActivePresentation.Name) & ".pdf"
    PdfFileName = InputBox("Enter PDF file name with extension", "Create a PPTX file from a PDF file", DefaultFileName)
    
    If PdfFileName = "" Then
        Exit Sub
    End If
    
    If Dir(PdfFileName) = "" Then
        MsgBox "File '" & PdfFileName & "' not exists... Try again."
        Exit Sub
    End If
    
    
    ' Separate the PDF file
    SvgDir = "svgs"
    isdir = Dir(SvgDir, vbDirectory)
    
    If isdir = "" Then
        MkDir SvgDir
    Else
        If Dir(SvgDir & "/*") <> "" Then
            Kill SvgDir & "/*"
        End If
    End If
    
    CreateObject("Wscript.Shell").Run "pdfseparate " & PdfFileName & " " & SvgDir & "/slide-%d.pdf", 0, True

        
    ' Get page numbers and make SVG files
    PdfFiles = Dir(SvgDir & "/*.pdf")
    PageNum = 0
    
    Do While PdfFiles <> ""
        PdfFiles = Dir()
        PageNum = PageNum + 1
    Loop
    
    For n = 1 To PageNum
        CreateObject("Wscript.Shell").Run "pdftocairo -svg " & SvgDir & "/slide-" & n & ".pdf " & SvgDir & "/slide-" & n & ".svg", 0, True
        Kill SvgDir & "/slide-" & n & ".pdf"
    Next n
    
    ' Initialize presentation and make pptx from pdf
    
    SlideWidth = 1600 * 0.75
    SlideHeight = 1200 * 0.75
    ' 単位の変換: cf. https://vbabeginner.net/mutual-conversion-of-points-pixels-inches-centimeters-and-millimeters/
    Presen.PageSetup.SlideWidth = SlideWidth
    Presen.PageSetup.SlideHeight = SlideHeight
    
    With Presen.SlideMaster.Background.Fill
        .Solid
        .ForeColor.RGB = RGB(0, 0, 0)
    End With
    With Presen.Slides
        Do While .Count > 0
            .Item(1).Delete
        Loop
        For i = 1 To PageNum
            .AddSlide _
                Index:=i, _
                pCustomLayout:=Presen.SlideMaster.CustomLayouts(7)
            .Item(i).Shapes.AddPicture _
                FileName:="svgs/slide-" & i & ".svg", _
                LinkToFile:=msoFalse, _
                SaveWithDocument:=msoTrue, _
                Left:=0, Top:=0, _
                Width:=SlideWidth, _
                Height:=SlideHeight
        Next i
    End With
    
End Sub
```

</div>
</details>

<details>
<summary>`ReplaceBack` のコード</summary>
<div>

```vb
Sub ReplaceBack()
    Set Presen = ActivePresentation
    ChDir Presen.Path
    
    ' Get file name
    DefaultFileName = CreateObject("Scripting.FileSystemObject").GetBaseName(ActivePresentation.Name) & ".pdf"
    PdfFileName = InputBox("Enter PDF file name with extension", "Replace background SVG files", DefaultFileName)
    
    If PdfFileName = "" Then
        Exit Sub
    End If
    
    If Dir(PdfFileName) = "" Then
        MsgBox "File '" & PdfFileName & "' not exists... Try again."
        Exit Sub
    End If
    
    
    ' Separate the PDF file
    SvgDir = "svgs"
    isdir = Dir(SvgDir, vbDirectory)
    
    If isdir = "" Then
        MkDir SvgDir
    Else
        If Dir(SvgDir & "/*") <> "" Then
            Kill SvgDir & "/*"
        End If
    End If
    
    CreateObject("Wscript.Shell").Run "pdfseparate " & PdfFileName & " " & SvgDir & "/slide-%d.pdf", 0, True

        
    ' Get page numbers and make SVG files
    PdfFiles = Dir(SvgDir & "/*.pdf")
    PageNum = 0
    
    Do While PdfFiles <> ""
        PdfFiles = Dir()
        PageNum = PageNum + 1
    Loop
    
    For n = 1 To PageNum
        CreateObject("Wscript.Shell").Run "pdftocairo -svg " & SvgDir & "/slide-" & n & ".pdf " & SvgDir & "/slide-" & n & ".svg", 0, True
        Kill SvgDir & "/slide-" & n & ".pdf"
    Next n
    
    ' Replace background SVG files
    For i = 1 To PageNum
        Presen.Slides.Item(i).Shapes(1).Delete
        Set Back = Presen.Slides.Item(i).Shapes.AddPicture("svgs/slide-" & i & ".svg", msoFalse, msoTrue, 0, 0, Presen.PageSetup.SlideWidth, Presen.PageSetup.SlideHeight)
        Back.ZOrder msoSendToBack
    Next i
End Sub
```

</div>
</details>


## 終わりに

PowerPoint VBAしましょう！