Pythonを使ってOffice文書をPDF変換してみた

## はじめに

Pythonを使ってOffice文書をPDF変換してみました。
※ [[LibreOfficeのsofficeコマンドでOffice文書をPDF変換してみた](https://qiita.com/mingchun_zhao/items/bb7166b808bfbccf39fa)]も合わせてご参照ください。

## やりたいこと

Pythonを使って、Office文書をPDFに変換したい。

## 解決方法

Pythonのsubprocessモジュールを用いて、sofficeコマンドを実行する。

## 前提知識: [LibreOfficeのユーザプロファイル](https://wiki.documentfoundation.org/UserProfile/ja)

- ユーザーに関連するすべてのデータを格納するフォルダ
    - 拡張機能やカスタム辞書、テンプレートなど
- LibreOfficeをアンインストールやアップデートしても削除されない
    - カスタマイズした内容は保存される
- デフォルトの格納場所
    - Windows: `%APPDATA%\libreoffice\4\user`
    - Linux: `/home/<ユーザー名>/.config/libreoffice/4/user`
    - macOS: `~/Library/Application Support/LibreOffice/4/user`

## 検証環境

- Ubuntu 20.04 LTS

## 検証シナリオ

ExcelファイルをPDF変換してみます。

- sofficeコマンド実行時のオプション機能
    - user_profile: sofficeユーザプロファイルパスを指定
        - 指定しない場合、デフォルトのユーザプロファイルが使用される
    - timeout: subprocess.run()のタイムアウトを設ける
        - 処理遅延した場合、終了させるため

## サンプルコード

```soffice.py
import os
import subprocess
import shutil
import glob

default_user_profile = os.environ['HOME'] + "/.config/libreoffice/4/user"

class PdfConverter:
    def __init__(self,
        file_in:str,
        file_out:str,
        timeout_sec:int=30,
        user_profile:str=None):
        self.file_in = file_in  # 変換対象のOffice文書
        self.file_out = file_out  # 変換されたPDF文書の格納ディレクトリ
        self.timeout_sec = timeout_sec  # 変換のタイムアウトリミット
        # デフォルトのユーザプロファイルから、新しいユーザプロファイルを作成
        self.user_profile = user_profile
        if self.user_profile:
            if not os.path.exists(self.user_profile):
                shutil.copytree(default_user_profile, self.user_profile)

    def __enter__(self):
        return self

    def __exit__(self):
        self.stop()

    def start(self):
        args = [
            'soffice',
            '--headless',
            '--convert-to',
            'pdf',
            self.file_in,
            '--outdir',
            self.file_out,
        ]
        if self.user_profile:
            args.append('-env:UserInstallation=file://%s' % self.user_profile)
        stdout_str = ""
        stderr_str = ""
        rc = 0
        try:
            # PDF変換実行、タイムアウトになったらsofficeプロセスを終了させる
            ret = subprocess.run(args,
                stdout=subprocess.PIPE,
                stderr=subprocess.STDOUT,
                timeout=self.timeout_sec,
                check=True,
                text=True)
            rc = ret.returncode
            stdout_str = ret.stdout
            stderr_str = ret.stderr
        except subprocess.CalledProcessError as cpe:
            rc = -1
            stdout_str = cpe.stdout
            stderr_str = cpe.stderr
        except subprocess.TimeoutExpired as te:
            rc = -2
            stdout_str = te.stdout
            stderr_str = te.stderr
        finally:
            if stdout_str:
                print(stdout_str)
            if stderr_str:
                print(stderr_str)
            self.stop()
            return rc

    def stop(self):
        # タイムアウト時に生成される一時ファイルを削除
        tmp_files = self.file_out + '/*.tmp'
        for f in glob.glob(tmp_files):
            os.remove(f)
        print('soffice finished')

if __name__ == "__main__":
    pc = PdfConverter('test1.xlsx', './output')
    # pc = PdfConverter('test1.xlsx', './output', user_profile='/tmp/user_profile')
    pc.start()
```

## 実行結果

```
$ python soffice.py
$ ls output
test1.pdf
```

PDF変換に成功し、outputディレクトリにファイル生成されました。

## おわりに

Pythonで、Office文書をPDF変換してみました。
ご参考になればなによりです。