概要
pypdfを使い、下図のような単ページのPDFを見開きで結合する方法を紹介します。
各種インポート
インストール
pip install pypdf
各種インポート
from pypdf import PdfWriter, PdfReader, PageObject, Transformation
実行時の各種バージョン
Name | Version |
---|---|
pypdf | 3.2.0 |
注意
pypdf 3.0.0以降のバージョンで、pypdfの変数名などが大幅に変更されているので注意してください。本記事は、pypdf 3.0.0以降で機能します。
(参考:https://github.com/py-pdf/pypdf/commit/a4629d3dbdef2759ff6059446fcffd13ecf6a396)
サンプルファイル
サンプルで使用したPDFファイルです。よろしければシミュレーションとしてお使いください。
実装コード
Google Colabで作成したコードは、こちらにあります。
方法
from pypdf import PdfWriter, PdfReader, PageObject, Transformation
infile = './sample.pdf' # 単ページのPDF
outfile = './sample_merge_2page.pdf' # 見開きのPDFの保存ファイル
reader = PdfReader(infile)
writer = PdfWriter()
for i in range(0, len(reader.pages), 2):
p1 = reader.pages[i]
if i+1 == len(reader.pages): # ページ総数が奇数の場合に、右ページに空白を補完
p2 = PageObject.create_blank_page(width=p1.mediabox.right, height=p1.mediabox.top)
else:
p2 = reader.pages[i+1]
width_1_2 = p1.mediabox.right + p2.mediabox.right
height_1_2 = max(p1.mediabox.top, p2.mediabox.top)
p_1_2 = PageObject.create_blank_page(width=width_1_2, height=height_1_2)
# 見開きにするため、右ページ用のPDFを右に平行移動、mediaboxもそれに合わせて右に平行移動
op = Transformation().translate(tx=p1.mediabox.right)
p2.add_transformation(op)
p2.mediabox.left = p1.mediabox.right
p2.mediabox.right = p1.mediabox.right + p2.mediabox.right
p_1_2.merge_page(p1)
p_1_2.merge_page(p2)
writer.add_page(p_1_2)
with open(outfile, mode='wb') as f:
writer.write(f)
コードの説明
- 見開きページなので左ページと右ページを2つ同時に読み込むので、
range(0, len(reader.pages), 2)
とし、i
に左ページのindexを渡します。 -
p1 = reader.pages[i]
で、p1
に左ページ用の単ページのpdfを読み込みます。 - if文はページの総数が奇数の場合のみ、最終ページの右ページに左ページと同じサイズの余白
PageObject.create_blank_page(width=p1.mediabox.right, height=p1.mediabox.top)
を追加するため、読み込みます。 -
p2 = reader.pages[i+1]
で、p2
に右ページ用の単ページのpdfを読み込みます。 -
width_1_2
で、見開きページの横サイズを計算します。 -
height_1_2
で、見開きページの縦サイズを計算します。(max()
を使うことで、左右のページの縦サイズが異なる場合に、縦サイズの大きい方に合わせたサイズにすることができます。) -
p_1_2 = PageObject.create_blank_page(width=width_1_2, height=height_1_2)
で、見開きの空白ページを作ります。この空白ページに右ページと左ページを結合していきます。 - 見開きにしたいので、
op = Transformation().translate(tx=p1.mediabox.right)
で、右にp1.mediabox.right
だけ平行行移動する変換変数op
を用意します。 -
p2.add_transformation(op)
で、p2
を右にp1.mediabox.right
だけ平行移動させます。 -
add_transformation()
は、mediabox
(表示領域)の情報がそのまま維持されてしまうので、mediabox
も右に同じだけ平行移動させるために、p2.mediabox.left
,p2.mediabox.right
を調整します。 -
p_1_2.merge_page(p1)
,p_1_2.merge_page(p2)
で、見開きの空白ページp_1_2
に、左右のページを追加します。 -
writer.add_page(p_1_2)
で、見開きのページをwriter
に追加します。
補足
最終ページに空白を追加せず、左ページのみ追加する方法
from pypdf import PdfWriter, PdfReader, PageObject, Transformation
infile = './sample.pdf' # 単ページのPDF
outfile = './sample_merge_2page.pdf' # 見開きのPDFの保存ファイル
reader = PdfReader(infile)
writer = PdfWriter()
for i in range(0, len(reader.pages), 2):
p1 = reader.pages[i]
if i+1 == len(reader.pages): # ページ総数が奇数の場合に、そのまま左ページのみ追加
writer.add_page(p1)
break
else:
p2 = reader.pages[i+1]
width_1_2 = p1.mediabox.right + p2.mediabox.right
height_1_2 = max(p1.mediabox.top, p2.mediabox.top)
p_1_2 = PageObject.create_blank_page(width=width_1_2, height=height_1_2)
# 見開きにするため、右ページ用のPDFを右に平行移動、mediaboxもそれに合わせて右に平行移動
op = Transformation().translate(tx=p1.mediabox.right)
p2.add_transformation(op)
p2.mediabox.left = p1.mediabox.right
p2.mediabox.right = p1.mediabox.right + p2.mediabox.right
p_1_2.merge_page(p1)
p_1_2.merge_page(p2)
writer.add_page(p_1_2)
with open(outfile, mode='wb') as f:
writer.write(f)
コードの説明(変更箇所のみ)
if i+1 == len(reader.pages): # ページ総数が奇数の場合、そのまま左ページのみ追加
writer.add_page(p1)
break
ページが奇数の場合は左ページのみwriter.add_page(p1)
でwriter
に追加して、それ以降の処理をスキップするため、break
する。
まとめ
pypdfを使い、単ページのPDFを見開きで結合する方法を紹介しました。最近、pypdf 3.0.0で変数名などが変更され、私自身PDFを見開きにする機会があり、色々とハマりどころがあったため本記事を書きました。誰かのお役に立てれば幸いです。
参考