PADはPythonが使える人に助けてもらうと機能を追加出来て更に便利になるという実用的な使い方の紹介#2

# はじめに
今回の記事は、前回の記事の続きでもう少し汎用的に使えるようにしていきます。

https://qiita.com/NSsystems_DX/items/67d02fc95cedf8b40cbd

## 先回のおさらい
Pythonが得意な方にお願いして、PythonでPDFを分割するpdfsplit.exeファイルを作成してもらい、PAD側でそれを呼び出して使うという方法を紹介しました。詳しくは、先回の記事をご覧いただけますと幸いです。

### pdfsplit.exeの使い方


1. pdffilesフォルダー直下のpdfsplit.exeと同じ階層に分割対象のPDFを保存する。
2. PADでpdfsplit.exeを実行する。
3. pdffilesフォルダーに分割されたPDFが連番付きで保存され、元のPDFファイルは削除される。

## 今回の記事でやりたい事
先回はPDF分割機能を1つだけPADに追加しましたが、今回はPAD側で3つのPDF関連の機能を追加して外部関数のように使う方法を紹介します。追加する機能は、以下の3つです。今回もPythonが得意な方に頼る前提となります。

1. PDFのページ数取得
2. PDFを単一ページのPDFへ連番付きで分割
3. PDF全ページに透かし追加

# Pythonが得意な人に頼む事
今回もPythonが得意な人に、複数の機能を持つpdffunctions.exeを作成して貰います。コードは以下を参考になさって下さい。

```python:pdffunctions.rb
from pathlib import Path
from PyPDF2 import PdfReader,PdfWriter
import pyperclip

def main():
    padinput = pyperclip.paste().split(',')

    if padinput[0] == 'getpagenumbers':
        pagenumber = getpagenumbers(padinput[1])
        pyperclip.copy(pagenumber)
    
    elif padinput[0] == 'pdfsplit':
        splitpdf(padinput[1])
    
    elif padinput[0] == 'addwatermark':
        addwatermark(padinput[1],padinput[2],padinput[3])

def getpagenumbers(content_pdf: str):    
    reader = PdfReader(content_pdf)
    return len(reader.pages)

def splitpdf(content_pdf: str):
    reader = PdfReader(content_pdf)
    for i in range(len(reader.pages)):
        page=reader.pages[i] 
        writer=PdfWriter() 
        writer.add_page(page) 
        filename=str(Path(content_pdf).stem) + str(i+1) + '.pdf'
        newfilepath=Path.cwd()/filename
        with open(newfilepath,mode='wb') as f:
            writer.write(f)    

def addwatermark(
    content_pdf: str,
    stamp_pdf: str,
    pdf_result: str,
):
    reader = PdfReader(content_pdf)
    writer = PdfWriter()
    for index in range(len(reader.pages)):
        content_page = reader.pages[index]
        mediabox = content_page.mediabox

        reader_stamp = PdfReader(stamp_pdf)
        image_page = reader_stamp.pages[0]

        image_page.merge_page(content_page)
        image_page.mediabox = mediabox
        writer.add_page(image_page)

    with open(pdf_result, "wb") as fp:
        writer.write(fp)

if __name__ == '__main__':
    main()
```

Pythonのexe化は今回もcx_Freezeを使用しています。setup.pyも参考で載せておきます。

```python:pdffunctions.rb
from cx_Freeze import setup, Executable

excludes = ['PyQt4','PyQt5','tkinter']

base = None

setup(
    name='pdffunctions',
    version='1.0',
    description='PDF functions for PAD',
    options={'build_exe':{'excludes':excludes}},
    executables=[Executable('pdffunctions.py', base=base)]
)
```

## コードの解説
簡単にpdffunctions.pyの内容を説明します。

### 各関数の説明
ここでは、4つの関数を作成しています。
1. **main()**:クリップボードの値（PADからの入力）をリスト形式で取得し、リストの最初の値に応じてgetpagenumbers(), splitpdf(), addwatermark()のいずれかの関数を実行。
1. **getpagenumbers()**:PDFのページ数を取得する関数。PDFのページ数は、クリップボードにコピーされる。
1. **splitpdf()**:PDFを単一ページののPDFへ連番付きで分割する関数。分割されたPDFは、分割対象と同じフォルダーに保存される。元のファイルはそのまま残す。
1. **addwatermark()**:PDF全ページに透かしを追加する関数。透かしのみを入れたPDFファイルを透かしを入れる対象PDFと同一フォルダーに保存。watermarkは、透かしという意味です。

※addwatermark()関数は、PyPDF2の公式ドキュメントに掲載されているwatermark()関数をほぼそのまま使用しています。

https://pypdf2.readthedocs.io/en/stable/user/add-watermark.html

### PADとの入出力
PADとPythonでデータの入出力は、クリップボードを介して行います。このため、PythonではPyperclipを使用しています。Pyperclipについては、以下を参照ください。

https://github.com/asweigart/pyperclip


PADからカンマ区切りで**①関数名,　②操作対象のPDFファイルの絶対パス,　③透かし画像のPDFファイルの絶対パス,　④透かしを入れた後のPDFの絶対パス**をPythonに渡します。ここで、3番目と4番目の文字列はaddwatermark()関数の時のみ必要になります。


PADでPythonとの入出力で使用するアクションは、以下の2つです。

1. クリップボードテキストを取得
1. クリップボードテキストを設定


# pdffunctions.exeの使い方
cx_Freezeでexe化が完了すると、**exe.win-amd64-3.x**のようなフォルダーが作成されます。この中に、画像のようなpdfsplit.exeファイルと関連ファイルが保存されています。このフォルダーをご使用のPCに保存します（配布して貰います）。今回は、フォルダー名をexe.win-amd64-3.x→pdffunctionsと変更しました。使い方と動作は非常にシンプルです。
![pdffunctions最初.PNG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2260252/ebee76e4-548c-801c-ab06-cd2f14ac9317.png)


1. pdffunctionsフォルダー直下のpdffunctions.exeと同じ階層に、分割対象のPDFと透かしが保存されたwatermark.pdfを置く（addwatermark()関数を使用しないのであれば、watermark.pdfは必要ありません）。
2. PADでpdfsplit.exeを実行する。この時、リスト形式で必要な情報を渡し、使用する関数を選択する。
3. 選択した関数に応じて、以下のように動作する。
**getpagenumber()関数**： 対象PDFのページ数をクリップボードにコピーし、メッセージボックスにページ数を表示。
**pdfsplit()関数**： pdffunctionssフォルダーに分割されたPDFが連番付きで保存され、元のPDFファイルはそのまま残る。処理の完了を知らせるメッセージボックスを表示。
**addwatermark()関数**： 指定した絶対パスに透かしが追加されたPDFが保存される。処理の完了を知らせるメッセージボックスを表示。


# PAD側の操作

## 準備
pdffunctionsフォルダーに操作対象のPDF（世界一やさしいPython RPA_抜粋.pdf）と透かしが入ったPDFファイル（watermark.pdf）を保存します。

![pdffunctionsフォルダー.PNG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2260252/4eed195b-5ca2-bc93-1609-e1e3d87ea6b7.png)

世界一やさしいPython RPA_抜粋.pdfは、7ページあります。
watermark.pdfは、以下の通り透かしの画像を保存しています。
![watermarkファイル.PNG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2260252/1430ee90-4351-9be8-16f6-db9aef76e72b.png)


## PADの全体フローと各フローの説明

PADの全体フローは、。エラー処理等は含んでいませんので16行とシンプルです。画像で示すように４つのブロックに分けて説明します。
![フロー全体.PNG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2260252/5aa5be27-eb18-6da5-0746-1374d6f102cf.png)




### ①変数の設定
ここでは、1行目〜4行目で4つの変数を設定します。使う変数は、functionname、pdfpath、watermarkpath、resultpathです。それぞれの変数の説明は、以下の通りです。

#### 1行目
**functionname**：関数名を格納
今回は、addwatermark()関数を選択するので、addwatermarkを値としています。PDFのページ数を取得したい場合は、getpagenumbers、PDFを分割したい場合はpdfsplitを値として設定します。
![変数の設定1.PNG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2260252/2bc6a6d1-481f-faf3-135a-2b77b3835bcd.png)
#### 2行目
**pdfpath**：　操作対象のPDFファイルの絶対パスを格納
![変数の設定2.PNG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2260252/d7f908c3-65ce-c497-2d89-a86c853a6c04.png)
#### 3行目
**watermarkpath**：透かし画像のPDFファイルの絶対パスを格納
![変数の設定3.PNG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2260252/0faa3506-4a58-eeec-4f81-e5897a8b9d0b.png)
#### 4行目
**resultpath**：透かしを入れた後のPDFの絶対パスを格納
透かしを入れた後のPDFは、pdffunctionsフォルダー内にresult.pdfという名前で保存します。
![変数の設定4.PNG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2260252/87f26779-f86a-f3ba-dc2b-565fc718e8e0.png)



### ②Pythonへの入力準備
PAD→Pythonへ情報をそのまま渡すためのアクションがありませんので、クリップボードテキストを設定のアクションを使用します。1行目〜４行目で設定した変数をカンマで区切ってクリップボードへコピーします。Pythonのプログラム内でカンマ区切りの入力をリスト形式に変換しています。
![クリップボードの設定.PNG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2260252/17bc1f29-1963-2437-6468-0955fb25285b.png)


### ③Pythonの関数実行
Pythonの関数を「アプリケーションの実行」のアクションで実行します。「アプリケーションパス：」には、pdffunctions.exeの絶対パスを指定します。「作業フォルダー：」は、pdffunctionsフォルダーを指定します。10行目で「wait」アクションを使って、Pythonのプログラムが実行完了するまでの間の2秒待機させます。今回は、2秒にしていますがPDFのページ数が多い場合にはもう少し時間がかかるかもしれません。
![アプリケーションの実行設定.PNG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2260252/1ac209cd-4ece-aab3-d270-c133da8a25df.png)

### ④実行結果の表示
「Switch - Case」アクションで 変数functionnameの値によって、アクションを変えます。
#### 9行目〜11行目：
**pagenumbers**が選択された場合のアクションです。Pythonのプログラムを実行すると、指定したPDFのページ数がクリップボードに保存されますので、10行目で「クリップボードテキストを取得」アクションで変数ClipboardTextにページ数を格納します。

![クリップボードテキストを取得設定.PNG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2260252/ffc1c5a1-c129-d8d0-94b2-728cfd904f66.png)
「メッセージを表示」アクションで、取得したページ数をメッセージボックスで表示させるようにします。
![クリップボードテキストのメッセージを表示設定.PNG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2260252/7dd2b67e-3722-09b7-8847-b0fc4e892c4c.png)

#### 12行目〜13行目：
**pdfsplit**が選択された場合のアクションです。Pythonのプログラムを実行すると、操作対応のPDFが同じフォルダー内に1ページ毎に連番付きで分割保存されます。13行目の「メッセージを表示」アクションで、PDF分割が完了したことを知らせるメッセージボックスを表示させます。

![pdfsplitメッセージ表示の設定.PNG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2260252/2840b4c1-0d45-aa4b-3c3f-c9869a5ad2c6.png)

#### 14行目〜15行目：
**addwatermark**が選択された場合のアクションです。Pythonのプログラムを実行すると、操作対応のPDFにwatermark.pdfの透かしが追加され、result.pdfとして保存されます。15行目の「メッセージを表示」アクションで、透かしの追加処理が完了したことを知らせるメッセージボックスを表示させます。
![addwatermarkのメッセージ表示設定.PNG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2260252/22cc2822-9952-6267-05d2-a5c11f216a3f.png)


## PADの実行結果
それぞれの関数を実行した結果と全ての関数を実行した後のpdffunctionsフォルダーを示します。

### pagenumbers関数を実行した結果
以下のメッセージが表示されます。
![getpagenumbersの実行結果1.PNG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2260252/dd872395-f897-e9ff-385b-597d23c5b1aa.png)


### pdfsplit関数を実行した結果
以下のメッセージが表示されます。
![splitpdfの実行結果1.PNG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2260252/0a98f002-873f-8732-ba0f-bacc6f064d30.png)


### addwatermark関数を実行した結果
以下のメッセージが表示されます。
![addwatermarkの実行結果1.PNG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2260252/6904e5e9-eb29-7569-0043-9b5cb469de03.png)
result.pdfは、以下のように透かしが追加されます。
![addwatermarkの実行結果2.PNG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2260252/96c31a22-5da1-8847-5c18-b2082a62e385.png)

### 全ての関数を実行した後のpdffunctionsフォルダー
addwatermark()関数、getpagenumbers()関数、pdfsplit()関数の全てを実行した後は、pdffunctionsフォルダーは以下のようになります。
![全ての実行結果.PNG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2260252/44486574-a223-a2f0-677d-d259f778932a.png)


## おわりに

今回はPDF関連の機能を1まとめにして、PADから外部関数のように使う方法を紹介させていただきました。
Pythonで実装できる機能は多岐に渡ります。たとえば、データの揺れを一括で修正するデータクレンジングの機能をython x Pandasで作成する等の使い方もあるかと思います。PADで見つからないアクションがあればPythonが得意な人に相談してみて下さい。そして、今回のようにPADに機能を追加して更に便利に使いこなしましょう。

この記事がどなたかのお役に立てれば幸いです。