以前は見開きのPDFを2ページに分割するプログラムを紹介しましたが、今回はその逆2ページのPDFを見開き1ページに結合するプログラムを作成します。コードはこちらの方がシンプルです。

pdf divide

このプログラムを使えば「A4資料」を「A3見開き」に簡単に変更できます。校閲のように見開きにした方が作業しやすい場合に重宝します。また、見開きの方が好まれる会議もあると思います。

今回のプログラムでは、Pythonでまず白紙(ブランク)のページを作成して、そこに2ページ分のPDFを貼り付け(マージ)します。座標を指定して貼り付ける方法も使いますので、いろいろと応用できます。ぜひ日頃のプログラミングの参考にしてください。

本記事の目次
  1. ライブラリ(PyPDF2)のインストール
  2. プログラムの内容
  3. プログラムのコード
  4. コードの説明
  5. 最後に

ライブラリ(PyPDF2)のインストール

今回もPDF文書のページ操作などができるライブラリ「PyPDF2 」を利用します。PyPDF2のインストールは、Windowsではコマンドプロンプトで以下のコマンドで実行できます。

> py -m pip install PyPDF2
# 環境の違いによっては以下のコマンドなどを使用> python3 -m pip install PyPDF2
> pip intall PyPDF2

プログラムの内容

以下のようにプログラム(pdf_spreader.py)と同じ階層にある「samplesフォルダー」の中にある「sample_a4s.pdf」を原稿として読み取り、見開きに結合したPDFを「sample_a3s.pdf」に保存します。

samples/
  │  ├ sample_a4s.pdf <= 元の原稿
  │  └ sample_a3s.pdf <= 見開きに結合したPDF(プログラムを実行すると生成される)
  │
  └ pdf_spreader.py <= 今回のプログラム

A4の原稿(sample_a4s.pdf)は、以下のような計8ページのPDFになっています(金融庁の新たな賃金決済サービスのパンフレット を使用させていただきました)。

sample_a4s.pdf(原稿)

sample_a4s.pdf

プログラムを実行すると以下のように見開きに結合された計4ページのPDFがsample_a3s.pdfに保存されるようにします。

sample_a3s.pdf(見開きに結合したPDF)

sample_a3s.pdf

プログラムのコード

pdf_spreader.py
import PyPDF2

input_file = "./sample/sample_a4s.pdf"  # 原稿
output_file = "./sample/sample_a3s.pdf"  # 見開きにしたPDFの保存

pdf_reader = PyPDF2.PdfFileReader(input_file, strict=False)
pdf_writer = PyPDF2.PdfFileWriter()

for i in range(0, pdf_reader.getNumPages(), 2):
    # 最後に1ページ残る場合は見開きにしない
    if i + 1 == pdf_reader.getNumPages():
        pdf_writer.addPage(pdf_reader.getPage(i))
        break

    # 繋ぎ合わせるページ(p1:左側、p2:右側)
    p1 = pdf_reader.getPage(i)
    p2 = pdf_reader.getPage(i + 1)

    # 見開きにしたページサイズ
    total_width = p1.mediaBox.getUpperRight_x() + p2.mediaBox.getUpperRight_x()
    total_height = max([p1.mediaBox.getUpperRight_y(), 
                        p1.mediaBox.getUpperRight_y()])

    # ページを貼り付ける空白ページ
    p = PyPDF2.pdf.PageObject.createBlankPage(width=total_width, 
                                              height=total_height)
    # 左側のページを貼り付け
    p.mergePage(p1)
    # 右側のページを位置を指定して貼り付け
    p.mergeTranslatedPage(p2, p1.mediaBox.getUpperRight_x(), 0)

    # 見開きにしたページを出力用オブジェクトに追加
    pdf_writer.addPage(p)

# ファイルに出力
with open(output_file, mode="wb") as f:
    pdf_writer.write(f)

コードの説明

原稿のPDFを2ページずつ取得

for文を用いて原稿のPDFを2ページずつ繰り返し取得します。それにはrange()を利用します。

range()は、range(stop)のように1つだけ引数を指定するとstop未満の整数を0から生成します。range(start, stop, step)のように3つ引数を指定すると、以下のようにstop未満の整数をstartからstepずつ生成します。

>>> list(range(8))
[0, 1, 2, 3, 4, 5, 6, 7]
>>> list(range(0, 8, 2))
[0, 2, 4, 6]

原稿の総ページ数はpdf_reader.getNumPages()で取得できるので、以下のfor文により原稿から2ページずつ取得することができるようになります。

for i in range(0, pdf_reader.getNumPages(), 2):
    p1 = pdf_reader.getPage(i)
    p2 = pdf_reader.getPage(i + 1)
白紙のPDFページを作成してページを貼り付ける

今回のように2ページを見開き1ページに結合するには、最初に2ページ分の大きさの白紙のPDFを作成して、そこに1ページずつ貼り付けます。

PyPDF2で白紙のPDFページを作成するには、PyPDF2.pdf.PageObject.createBlankPage(width=幅, height=高さ)を用います(以下のドキュメントで詳細を確認できます)。

PyPDF2 documentation > The PageObject Class > createBlankPage()

ここで、白紙のPDFページのサイズは、以下のように「幅(total_width)」はそれぞれの幅を足し合わせた値、「高さ(total_height)」はそれぞれの高さの大きい方の値とします。

pdf size

p1p2の用紙サイズは「MediaBox」というページ境界から取得します(以前の見開きのPDFページを分割する記事で詳しく説明しています)。PDFの座標は左下が原点になるので、p1の場合では幅はp1.mediaBox.getUpperRight_x()、高さはp1.mediaBox.getUpperRight_y()で取得できます。

PDFページの上に他のページを貼り付けるには、mergePage(貼り付けるPDF)を用います。この場合、ページの左下を合わせて貼り付けられるので、p1はこの方法を用います。

一方、p2は右側に貼り付けるので、位置を座標で指定できるmergeTranslatedPage(貼り付けるPDF, x, y)を使います。以下のように貼り付けるPDFの左下の座標をxyで指定します。

pdf merge position

mergeTranslatedPage()については、以下のドキュメントで詳細を確認できます。

PyPDF2 documentation > The PageObject Class > mergeTranslatedPage()

総ページが奇数の場合の措置

今回の原稿のサンプルは8ページなので問題ありませんが、原稿が奇数のページの場合には、最後の右側のページが欠損します。そのため、pdf_reader.getPage(i + 1)でページを取得できなくなり「IndexError: list index out of range」というエラーが生じます。

そこで、for文のブロックの先頭で、最後に1ページ残る場合は結合しないでそのまま出力のPDFにpdf_writer.addPage()で書き込むようにします。

最後に

これで、以前ご紹介した見開きページを分割する方法と今回の結合する方法で双方可能となりました。どちらもAcrobat®などでも可能ですが、プログラミングでできるようにしておくことで活用の幅が広がります。例えば、フォルダー内の大量のPDFファイルについて実行することもできます。ぜひ様々なビジネスシーンでの活用に参考にしてください。