PythonでPDFをトリミングする方法
印刷用の原稿では「塗り足し」に気をつける必要があります。印刷用と表示用でページサイズが変わり、表示用のPDFを作るのに印刷用のPDFをトリミングする必要が出てきます。この変換をPythonで一括でやってみます。
目次
PDFのサイズについて
PDFのサイズはAcrobat Readerを使うと確認できます。今「cat.pdf」というPDFがあったとしましょう。
ページサイズというのがポイントです。このPDFは216×303mmあります。塗り足しの関係で上下左右+3mmされています。これを表示用にA4サイズ「210×297mm」にトリミングしたいとします。これをPythonでやっていきます。
PyPDF2のページサイズ
事前にPyPDF2をインストールしておきましょう。
pip install PyPDF2
PDFのページは内部的にはMediaBoxで扱われています。これの四辺の座標を取得することでページサイズを計算することができます。PyPDF2では以下のようにします。
PDFのページごとにMediaBoxのオブジェクトを持っています。ページ単位でMediaBoxを独立して管理しています。各ページのページサイズを確認するには、
from PyPDF2 import PdfFileReader, PdfFileWriter
def crop_pdf():
# 用紙を確認
input_pdf = PdfFileReader("cat.pdf")
# 1ページしかないのでループは1回
for i in range(input_pdf.getNumPages()):
page = input_pdf.getPage(i)
# Media Boxの座標=サイズ(pt単位)
lower_left = page.mediaBox.getLowerLeft()
upper_right = page.mediaBox.getUpperRight()
print(lower_left)
print(upper_right)
if __name__ == "__main__":
crop_pdf()
次のような出力になります。
(0, 0)
(612.36, 858.96)
単位はポイント(pt)です。フォントサイズと同じ単位です。ここがミソです。
MediaBoxは長方形ですが、左下を原点とし、右上方向に座標を進めていきます。つまり、右上の座標をmmに変換すればAcrobat Readerで表示されている値と一致するはずです。なお、各Tupleは(横のpt、縦のpt)という構造です。
ptとmmの関係
PDFを正確にトリミングするには、フォントサイズのポイントの関係を知る必要があります。そもそもDTPにおけるポイント(pt)の定義は、1インチ=25.4mm=72ptです。ポイントは他にも定義がありますが、PDFはこの式で計算すると経験上正しく出ます。
先程のMediaBoxの右上の座標をmmに変換してみます。
- 横:612.36÷72×25.4 = 216.027mm
- 縦:858.96÷72×25.4 = 303.022mm
となり端数はありますが、216mm×303mmとAcrobat Readerの表示値と一致しました。端数はPDFを生成する過程で出たものと思われます。
CropBoxでトリミング
PDFをトリミングして表示したい場合は、PDFのCropBoxを使います。デフォルトでは、CropBoxとMediaBoxは同じ領域ですが、CropBoxを独立して指定するとトリミングすることができます。
上下左右3mmトリミングするのなら、CropBoxの座標指定は、「左下が(3mm, 3mm)、右上が(トリミング前の幅-3mm, トリミング前の高さ-3mm)」となります。ただし、これはMediaBox同様pt単位で指定する必要があります。これをコードで書くと次のようになります。
from PyPDF2 import PdfFileReader, PdfFileWriter
def crop_pdf():
# PDFをトリミングして保存
input_pdf = PdfFileReader("cat.pdf")
out_pdf = PdfFileWriter()
# 1インチ=25.4mm=72pt
pt_per_mm = 72 / 25.4
# 1ページしかないのでループは1回
for i in range(input_pdf.getNumPages()):
page = input_pdf.getPage(i)
# Media Boxの座標=サイズ(pt単位)
lower_left = page.mediaBox.getLowerLeft()
upper_right = page.mediaBox.getUpperRight()
# 上下左右3mmトリミング
page.cropBox.lowerLeft = (3*pt_per_mm, 3*pt_per_mm)
# 右上は(213*pt_per_mm, 300*pt_per_mm)と絶対指定してもよい
page.cropBox.upperRight = (float(upper_right[0])-3*pt_per_mm, float(upper_right[1])-3*pt_per_mm)
# 書き出しPDFに追加
out_pdf.addPage(page)
with open("cat_trim.pdf", "wb") as fp:
out_pdf.write(fp)
if __name__ == "__main__":
crop_pdf()
出力されたcat_trim.pdfをAcrobat Readerで確認してみましょう。
この通り上下左右3mmずつトリミングされてA4サイズにすることができました!
関連:https://stackoverflow.com/questions/457207/cropping-pages-of-a-pdf-file
Shikoan's ML Blogの中の人が運営しているサークル「じゅ~しぃ~すくりぷと」の本のご案内
技術書コーナー
北海道の駅巡りコーナー