こしあん
2020-12-12

PythonでPDFをトリミングする方法


12.6k{icon} {views}


印刷用の原稿では「塗り足し」に気をつける必要があります。印刷用と表示用でページサイズが変わり、表示用の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の中の人が運営しているサークル「じゅ~しぃ~すくりぷと」の本のご案内

技術書コーナー

北海道の駅巡りコーナー


Add a Comment

メールアドレスが公開されることはありません。 が付いている欄は必須項目です