Daydreaming in Brookline, MA

PDFファイルにページ番号を追加する方法(Pythonで)

1 はじめに

小学生の娘が算数で間違った問題を、スキャンしてまとめてPDF化しています。しかし、必要なページだけ印刷したつもりが、結構ページ番号がずれていて、何度もやりなおしたりしています。電子化したつもりが意外とアナログです。そこで、自動でPDFファイルにページ番号を付けたいと思いました。ヒューマンエラーによるページ番号のずれが防げる、またはずれてもすぐに気がつくので。

しかし、PDFにページ番号を付けたいという単純なことなのに、意外にも良い実現方法が見つかりません。検索した結果、いくつかWebサービスを見つけて試してみましたが、あるものはページを入れる位置が気に入らない、またあるものは、たくさんページ番号を付けていると有料になる(しかも月極subscription)などでした。

そこで、最近使えるようになって気を良くしているPythonで、PDFにページ番号が付けられないか調べてみたのですが、このユースケースに対応するPDFライブラリは無さそう(少なくとも単体では)、ということがわかりました。

2 ReportLab PDFライブラリ

PythonでのPDFライブラリで有名なものは、PyPDF2, pdfrwあたりのようです。これらは、複数のPDFファイルをマージしたり、逆に分割したり、ページを入れ替えたりといったことが得意ですが、「既存PDFファイルにページ番号を付加する」というユースケースには対応していないようです。

更に調べてみると、どうやらReportLabライブラリでページ番号を付けられそうだ、ということがわかりました。 https://www.blog.pythonlibrary.org/2013/08/12/reportlab-how-to-add-page-numbers/

このWebページはとても期待させるタイトルなのですが、サンプルコードを見てみると疑問がわいてきます。そもそも、既存PDFファイルを読み込んでおらず、新規に作成したPDFのページにページ番号を付与しているようです。駄目じゃん。。。

マニュアルにも一通り目を通してみます。 https://www.reportlab.com/docs/reportlab-userguide.pdf

ここでも、既存PDFファイルを読み込む説明はありませんでした。

3 ReportLab + pdfrwを組み合わせる

それでも諦めずに検索を続けたところ、見つけました。 https://stackoverflow.com/questions/28281108/reportlab-how-to-add-a-footer-to-a-pdf-file

さすがstackoverflow! 日本のqiitaと並んで大好きです。 どうやら、ReportLabとpdfrwを組み合わせるとできそうなことが書いてあります。 気になる記述もありますが。。。

DISCLAIMER: Tested on Linux using as input file a pdf file generated by Reportlab. It would probably not work in an arbitrary pdf file.

「ReportLabで作成したPDFファイルでテストしたけど、任意のPDFファイルだと動かないんじゃないかな」

・・・えー!! でもこれしか頼れる物がありません。試してみましょう。

stakoverflowのページに載っているサンプルコードを改変してみます。

from reportlab.pdfgen.canvas import Canvas
from pdfrw import PdfReader
from pdfrw.toreportlab import makerl
from pdfrw.buildxobj import pagexobj
import sys
import os

if len(sys.argv) != 2 or ".pdf" not in sys.argv[1].lower():
    print(f"Usage: python {sys.argv[0]} <pdf filename>")
    sys.exit()
input_file = sys.argv[1]
output_file = os.path.splitext(sys.argv[1])[0] + "_pgn.pdf"

reader = PdfReader(input_file)
pages = [pagexobj(p) for p in reader.pages]

canvas = Canvas(output_file)

for page_num, page in enumerate(pages, start=1):
    canvas.doForm(makerl(canvas, page))

    footer_text = f"{page_num}/{len(pages)}"
    canvas.saveState()
    canvas.setStrokeColorRGB(0, 0, 0)
    canvas.setFont('Times-Roman', 14)
    canvas.drawString(290, 10, footer_text)
    canvas.restoreState()
    canvas.showPage()

canvas.save()

そして実行すると、、、、 pdfpage.png あれ、さくっと動いてしまいました。念のため、ページの下に書いてある7/88というところが、今回入れたページ番号です。あのdisclaimerは何だったのか。。。

4 使用方法

f-stringを使っているので、python 3.6以降で使ってください。

PDFライブラリインストール

pip install reportlab
pip install pdfrw

上記コードをaddpagenum.pyとしてセーブ。(ファイル名は好きに変えてください)

実行

python addpagenum.py <pdf_filename>

ページ番号はA4でページ中央下に表示します。

4.1 カスタマイズ方法

このあたりを適当に変えてください。

footer_text = f"{page_num}/{len(pages)}"
canvas.setFont('Times-Roman', 14)
canvas.drawString(290, 10, footer_text)
  • 表示内容を変える場合はfooter_textを変える
  • ページ番号表示位置を変えたい場合は、x=290, y=10を変える

なお、ReportLabのcanvasにおいて、座標の(x=0, y=0)はページ左下になっています。 A4以外のLetter等にしたい場合は、canvasオブジェクトを作成する際に指定します。 詳しくはReportLabマニュアルを参照してください。

5 終わりに

このスクリプトはgithubにも載せました。 https://github.com/achiwa912/pdfpagenum

上のコードをコピペした方が早いですが。。。