枚数の多いPDFファイルを簡単に冊子にするアプリを作った

<img width="30%" alt="icon.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/782216/e0967f4f-32bb-0689-d9e4-6841bbb72114.png">

# 序章
会議の資料を作っていたら10枚くらいまで枚数が増えてしまって、それを印刷して配ったら..
上司「資源を節約しろ！」
なんて経験ありますよね。ね。
そんなときにも活躍してほしいアプリを作りました。

### 動画を作ったのでぜひご覧ください
編集練習がてら作ったので説明がくどいですが、、
<iframe width="560" height="315" src="https://www.youtube.com/embed/v_K9xJTPkhs" title="YouTube video player" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture" allowfullscreen></iframe>

# 対応OS等
今回はPythonでアプリを開発しました。なのでMac,Windows両対応！！(ましてやios,android!?)
でもUnix採用してる意識高い系の企業ぐらいしか会社のPCにPythonの実行環境なんて整っていませんよね。なのでPyinstallerでexeファイル化もしてみました。
もちろんソースコードも載せるのでご安心を。

Pyinstallerに関しては以下を参考にさせていただきました。とても簡単なのでおすすめです。

>[Pyinstallerでexeファイル化](https://qiita.com/takanorimutoh/items/53bf44d6d5b37190e7d1)

# ハマったこと→解決済み
Pyinstallerでexeファイルに変換すると、セキュリティソフトにウイルスと認定されてしまうこと。

>[PyInstallerでexeファイル化したものがAVASTやAVGでトロイの木馬ウイルスと誤認される問題の一時的な解決法](https://qiita.com/nobody_gonbe/items/5ffdd1a767c67256032e)

こちらや

>[[Python] Pyinstallerで実行ファイルがマルウェアに分類されてしまったときの対策](https://gamingpc.one/dev/python-pyinstaller/)

こちらを参考ににさせていただいたのですが解決しませんでした。

2/2追記: WindowsUpdate をしたらなぜか解決しました。アップデートは大事ですね！

# 使い方
## ①起動
### exeファイル版
このページの[下部](#ダウンロード)からダウンロードしたexeファイルをダブルクリックして実行
### Python版
PyPDF2モジュールをインストール

例えば以下

```:コマンドプロンプト等
python3 -m pip install PyPDF2
```

モジュールをインストールした環境下にこのページ[下部](#コード)のソースコードを配置して実行

## ②ファイルを指定
![起動時2.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/782216/3a6553e9-b6c8-1904-baab-1ac1b76b4531.png)
画像のような画面になる(exe,python共通)ので、指示に従って指定します。相対パスでいけるはずです。(たいていのプロンプトはドラッグアンドドロップでフルパス入力されます)

## ③Enter
元PDFファイルの総ページ数が4枚以上であれば一発で終わります。
### 1枚だった場合
サイズをひとつ小さく(A4→A5,B3→B4)して１枚に２つ出力(画像参照)することができます。紙を半分に切る作業が必要です。
![Qiita-PDF変換1.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/782216/573bba39-d213-f337-7a13-b0a676c45a29.png)


### 2枚だった場合
表面に１枚目を２つ、裏面に２枚目を２つ出力して、元PDFファイルをそのまま両面印刷したものを小さくしたものを実現([動画](#動画を作ったのでぜひご覧ください)参照)できます。上と同様に紙を半分に切る作業が必要です。
![Qiita-PDF変換2.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/782216/cfd36129-bad6-873d-52a1-b95286fdf795.png)


### 3枚だった場合
見開きに１,２枚目を持ってくるか２,３枚目を持ってくるかを選択できます。

## ④保存ファイル名を指定
最後カレントディレクトリに保存したとの表示がありますが、パスを指定してやることで違うディレクトリに保存することもできます。

# ダウンロード
このページにアップロードしてある画像も併せてダウンロードしてぜひご活用ください。
[PDFまとめるくん(Miniturization_PDF).exeをダウンロード](https://www.dropbox.com/s/33t3fnash3zinwz/PDF%E3%81%BE%E3%81%A8%E3%82%81%E3%82%8B%E3%81%8F%E3%82%93%28Miniaturization_PDF%29.exe?dl=1)

# コード
自分で作ったつたないコードを公開するってテストの答案を公開するくらいの恥ずかしさがあります

```python:Miniaturization_PDF.py
import PyPDF2

page_objs = [] #pdfファイルの各ページデータ収納用リスト
finaly_add = 0 #元データの最後に足すブランクページ

print("-----------------------------------")
print("製作者:Taktstock(https://qiita.com/Taktstock)")
print("使い方:(https://qiita.com/Taktstock/items/e529c9338595bc90dba9)")
print("-----------------------------------")
print("このプログラムは資源の節約のためにPDFを冊子にしたりできます。")
print("途中の説明で分かりにくい箇所があったら動画(https://youtu.be/v_K9xJTPkhs)を参照してください。")
print("-----------------------------------")
print("すべてのページが同じ大きさで、一般的な紙のサイズの場合のみ対応です。")
print("暗号化が施されているファイルには非対応です。")
print("-----------------------------------")

pdf_path = input("PDFファイルを指定(またはファイルをウィンドウにドラッグ):")
print("-----------------------------------")

try: #ファイルの読み込み
    pdf_file = open(pdf_path,'rb')
except:
    print("ファイルを開くことができませんでした。暗号化されている可能性があります。")
    input("Enterを押すことで終了します")
    quit()

pdf_reader = PyPDF2.PdfFileReader(pdf_file) #pypdf2に読み込み
pdf_writer = PyPDF2.PdfFileWriter()

first_page = pdf_reader.getPage(0) #元ファイルの情報を収集
number_of_pages = pdf_reader.getNumPages()
(width_of_page,height_of_page) = first_page.mediaBox.getUpperRight()

#完成形は縦向きに
if width_of_page > height_of_page: #元PDFが横向きだったときの処理
    width_of_paper = height_of_page
    height_of_paper = width_of_page
    angle = 0

else: #元PDFが縦だったときの処理
    width_of_paper = width_of_page
    height_of_paper = height_of_page
    angle = 270

if number_of_pages % 4 != 0: #最終的にページ数が4の倍数になるようにする
    finaly_add = 4 - number_of_pages % 4

for page in range(number_of_pages): #リストに元PDFを収納
    page_objs.append(pdf_reader.getPage(page))

#元pdfファイルのページ数が1,2,3枚の場合は例外処理
if number_of_pages == 1:
    print("検出されたPDFファイルの総ページ数が１枚でした。")
    print("大きさを半分にして１枚の紙の片面に２つ出力するには y を入力します。")
    copy_double = input("y(1枚に2枚分) or other(終了):")
    print("-----------------------------------")
    if copy_double == "y":
        page_objs.append(pdf_reader.getPage(0))
        finaly_add = 0
        number_of_pages = 2
    else:
        quit()
elif number_of_pages == 2:
    print("検出されたPDFファイルの総ページ数が２枚でした。")
    print("表面に１枚目を２つ、裏面に２枚目を２つ出力するには y を入力します。(両面印刷して半分に切ることで節約が可能)")
    print("または片面に２枚分を出力するには n を入力します。")
    copy_four = input("y(両面に2つずつ) or n(片面に2つ) or other(終了):")
    print("-----------------------------------")
    if copy_four == "y":
        page_objs.append(pdf_reader.getPage(1))
        page_objs.append(pdf_reader.getPage(0))
        finaly_add = 0
        number_of_pages = 4
    elif copy_four == "n":
        page_objs[0] = pdf_reader.getPage(1)
        page_objs[1] = pdf_reader.getPage(0)
        finaly_add = 0
    else:
        quit()
elif number_of_pages == 3:
    print("検出されたPDFファイルの総ページ数が３枚でした。")
    print("1,2枚目を見開き1ページに出力するには y を入力します。")
    print("このまま続行すると2,3枚目が見開き1ページに出力されます。")
    first_spread = input("y(1,2枚目を見開き) or other(2,3枚目を見開き):")
    print("-----------------------------------")
    if first_spread == "y":
        page_objs[0] = pdf_reader.getPage(1)
        page_objs[1] = pdf_reader.getPage(2)
        page_objs[2] = PyPDF2.pdf.PageObject.createBlankPage(width=width_of_page,height=height_of_page)
        page_objs.append(pdf_reader.getPage(0))
        finaly_add = 0
        number_of_pages = 4
    else:
        pass
else:
    pass

for i in range(finaly_add): #元PDFの最後に調整用ブランクページを挿入
    page_objs.append(PyPDF2.pdf.PageObject.createBlankPage(width=width_of_page,height=height_of_page))

number_of_pages += finaly_add

for paper in range(int(number_of_pages / 2)): #核となる部分
    base_paper = PyPDF2.pdf.PageObject.createBlankPage(width=width_of_paper,height=height_of_paper)
    if (paper + 1) % 2 == 1: #奇数枚目には下に最初から上に最後から(1枚目は下に最初のページ、上に最後のページ)
        base_paper.mergeRotatedScaledTranslatedPage(page_objs[paper], angle, 0.71, 0, height_of_paper / 2, expand=False)
        base_paper.mergeRotatedScaledTranslatedPage(page_objs[number_of_pages - paper - 1], angle, 0.71, 0, height_of_paper, expand=False)
        pdf_writer.addPage(base_paper)
    else: #偶数枚目は奇数枚目の逆
        base_paper.mergeRotatedScaledTranslatedPage(page_objs[number_of_pages - paper - 1], angle, 0.71, 0, height_of_paper / 2, expand=False)
        base_paper.mergeRotatedScaledTranslatedPage(page_objs[paper], angle, 0.71, 0, height_of_paper, expand=False)
        pdf_writer.addPage(base_paper)

print("コンパクト化に成功しました!")
output_name = input("出力するファイルの名前を決めてください(拡張子なし):")

if output_name == "": #入力ミスしたとき
    output_name = 'Miniaturized'

output_name += ".pdf"

pdf_output_file = open(output_name,'wb') #ファイルを保存
pdf_writer.write(pdf_output_file)

pdf_output_file.close()
pdf_file.close()
print("'" + output_name + "'という名前でこのプログラムと同じフォルダ(カレントディレクトリ)に保存しました。")
print("短編とじで両面印刷し、まとめて折ることで簡単に冊子にすることができます。") #両面印刷に対応していないといけないことを通告
input("Enterを押すことで終了します")
```

# 最後に
GUI版も作ってみたい