紙で配布された資料を電子化したいとき、自動原稿送り装置(ADF)付きのイメージスキャナーや複合機があると便利ですが、私が持っている安い複合機(1万円台)は片面スキャンしかできないので、両面プリントされた資料の場合困ってしまいます。
そこで、表裏それぞれ連続スキャンしたpdfファイルをページ順にソートして結合するPythonスクリプトを作りました。
必要パッケージ
- pypdf
結合元pdfファイル
結合元のファイル名は下記のルールに従うものとします。
<ベース名>_<束番号>{a|b}.pdf
- ADFの容量よりも枚数が多い場合は複数の束に分けてスキャンすることになるので、束番号を整数でつけます。束を分けない場合も束番号1をつけます。
- 表面スキャンは a を、裏面スキャンは b をつけます。裏面スキャンがない場合は a のファイルのみでOKです。
- 連続スキャン後も紙の順番が反転しないADFを対象としています。
例えば6ページの両面資料の場合、表裏それぞれ連続スキャンすると次のようなページ順になります。
- 資料_1a.pdf: p1, p3, p5
- 資料_1b.pdf: p6, p4, p2
また、ページ数が奇数の場合、最後の紙の裏面は白紙なので抜いてスキャンすると次のようになります。
- 資料_1a.pdf: p1, p3, p5
- 資料_1b.pdf: p4, p2
表紙だけ片面プリントで次から両面プリントの場合
- 資料_1a.pdf: p1
- 資料_2b.pdf: p2, p4, p6
- 資料_2b.pdf: p7, p5, p3
出力ファイル
結合後の出力ファイル名は下記のようになります。
<ベース名>.pdf
同じファイル名のファイルが存在した場合は上書きされます。
なお、結合前ファイルはそのまま残っています。
スクリプトコード
実行時の引数として、結合元ファイル名をスペース区切りで与えます。引数の順番は結果に影響しません(読み込み時にソートしています)。
#表裏それぞれ連続スキャンしたpdfファイルを結合する
import sys
import re
import pypdf
rept = re.compile(r'(^.+)_(\d+)([ab]).pdf')
srcFiles = []
for sf in sys.argv[1:]:
res = rept.match(sf)
if res:
srcFiles.append([sf, res.group(1), int(res.group(2)), res.group(3)])
srcFiles.sort(key=lambda x: (x[2], x[3]))
print(srcFiles)
pages = []
pn = 0
lps = 0
for sf, basename, n, ab in srcFiles:
pdfr = pypdf.PdfReader(sf)
ps = len(pdfr.pages)
if ab == 'a':
pn2 = pn + ps * 2
pnlist = range(pn, pn2, 2)
pn = pn2
lps = ps
else:
if lps - ps == 1:
pnlist = range(pn-3, 0, -2)
else:
pnlist = range(pn-1, 0, -2)
pages += [[pn0, p] for pn0, p in zip(pnlist, pdfr.pages)]
pages.sort(key=lambda x: x[0])
pdf = pypdf.PdfWriter()
for pn, p in pages:
pdf.add_page(p)
pdf.write(basename + '.pdf')
pdf.close()
バッチファイル
Windowsでしたら、下記のバッチファイルを用意しておいて、処理したいファイル群をエクスプローラーで選択してまとめてドロップすればコマンドを打たずに処理できます。
py pdf_merge.py %*