【Python】２つのPDFファイルを交互に結合する

## はじめに

最近，A3，B4をスキャンできるスキャナを購入したが，
両面同時スキャン対応していなかった (´;ω;｀)

両面プリントの束をスキャンするときに，
面倒だが片面ずつスキャンしてPDFファイルをマージする必要がある

１０枚の両面プリントをスキャンしたとき（A:表面，B:裏面，数字は枚目）
１回目のスキャンでできたPDF：1A，2A，3A，・・・，10A
２回目のスキャンでできたPDF：1B，2B，3B，・・・，10B 

これを，
マージしたPDF：1A，1B，2A，2B，・・・，10A，10B
とする必要がある

## プログラム設計

書くべきほどのことではないが，簡単に書くとこんな感じであろう

- 引数は，結合する２つのファイル名と出力するファイル名
- 結合する２つのファイルの存在チェック
- 結合する２つのページ数が同じかチェック
- １ページごと交互に新しいファイルに書いていく

## PyPDF2

python で PDF ファイルを扱うなら PyPDF2 がよさそうだ

https://pythonhosted.org/PyPDF2/

インストールは，conda なら

```
conda install PyPDF2
```

### PDFファイルのページ数を得る

`PdfFileReader` の `getNumPages` を使えばよい
`PdfFileReader` の生成にはファイル名を渡す

```python
PdfFileReader(stream, strict=True, warndest=None, overwriteWarnings=True)
## stream: Could also be a string representing a path to a PDF file.（抜粋）
```

`getNumPages` はただ呼ぶだけでよい

```python
getNumPages()
```

つまり，これだけでページ数が取得できる

```python
import PyPDF2
reader = PyPDF2.PdfFileReader('src.pdf')
page_num = reader.getNumPages()
```

## PDFファイルの１ページを得る

`PdfFileReader` の `getPage` を使えばよい

```python
getPage(pageNumber)
```

`pageNumber` は 0始まりだ

つまり，これだけで全ページが取得できる

```python
import PyPDF2
reader = PyPDF2.PdfFileReader('src.pdf')
page_num = reader.getNumPages()
for page_number in range(page_num):
    page = reader.getPage(page_number)
```

### PDFファイルに出力する

`PdfFileWriter` を生成し，`addPage` でページを追加，`write` で PDF へ書き込む

`addPage`，`write` は次のようで

```python
addPage(page)
write(stream)
```

`addPage` の `page` には `PdfFileReader.getPage` で取得した `page` を渡す
`write` の `stream` はファイルオブジェクトを渡す

以下のソースで，src.pdf が dest.pdf にコピーされる

```python:copy_pdf.py
import PyPDF2
reader = PyPDF2.PdfFileReader('src.pdf')
page_num = reader.getNumPages()
writer = PyPDF2.PdfFileWriter()
for page_number in range(page_num):
    page = reader.getPage(page_number)
    writer.addPage(page)
with open('dest.pdf', 'wb') as f:
    writer.write(f)
```

## ２つのPDFファイルを交互に結合する

```python:two_file_merge.py
import os
import argparse
import PyPDF2 as pp

# 引数の解析
parser = argparse.ArgumentParser()
parser.add_argument('file1', help='元ファイル１', type=str)
parser.add_argument('file2', help='元ファイル２', type=str)
parser.add_argument('--output', '-o', help='出力ファイル名', type=str, default='output.pdf')
arg = parser.parse_args()

# 引数
file1 = arg.file1
file2 = arg.file2
output = arg.output

# 引数のチェック
if not os.path.exists(file1):
    print(f'{file1}が存在しません')
    quit()
if not os.path.exists(file2):
    print(f'{file2}が存在しません')
    quit()
reader1 = pp.PdfFileReader(file1)
reader2 = pp.PdfFileReader(file2)
if reader1.getNumPages() != reader2.getNumPages():
    print('ページ数が異なります')
    quit()

# １ページずつ交互に新しいファイルに書き込む
writer = pp.PdfFileWriter()
for page in range(reader1.getNumPages()):
    writer.addPage(reader1.getPage(page))
    writer.addPage(reader2.getPage(page))
with open(output, 'wb') as f:
    writer.write(f)
```

## おわりに

便利だな，PyPDF2
PDFファイルを扱うことが多いので，これから活用していきたい

## 追記

１回目のスキャンが完了した後，プリントの束をそのままひっくり返してスキャンする
通常このようにスキャンすると思うので，上のコードを修正をする

１回目のスキャンでできたPDF：1A，2A，3A，・・・，10A
２回目のスキャンでできたPDF：10B，9B，8B，・・・，1B
これをマージして，1A，1B，2A，2B，・・・，10A，10Bとしたい

２回目のスキャンファイルは逆順に読む必要がある

```python:two_file_merge_kai.py
import os
import argparse
import PyPDF2 as pp

# 引数の解析
parser = argparse.ArgumentParser()
parser.add_argument('file1', help='元ファイル１', type=str)
parser.add_argument('file2', help='元ファイル２', type=str)
parser.add_argument('--output', '-o', help='出力ファイル名', type=str, default='output.pdf')
arg = parser.parse_args()

# 引数
file1 = arg.file1
file2 = arg.file2
output = arg.output

# 引数のチェック
if not os.path.exists(file1):
    print(f'{file1}が存在しません')
    quit()
if not os.path.exists(file2):
    print(f'{file2}が存在しません')
    quit()
reader1 = pp.PdfFileReader(file1)
reader2 = pp.PdfFileReader(file2)
if reader1.getNumPages() != reader2.getNumPages():
    print('ページ数が異なります')
    quit()

# １ページずつ交互に新しいファイルに書き込む
writer = pp.PdfFileWriter()
num_pages = reader1.getNumPages()
for page_number in range(num_pages):
    writer.addPage(reader1.getPage(page_number))
    writer.addPage(reader2.getPage(num_pages-1-page_number)) ## 逆順に読む
with open(output, 'wb') as f:
    writer.write(f)
```