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



# Table of Contents

1.  [はじめに](#orga18657e)
2.  [ReportLab PDFライブラリ](#org7441e75)
3.  [ReportLab + pdfrwを組み合わせる](#org2258be2)
4.  [使用方法](#orgefcf672)
    1.  [カスタマイズ方法](#orga183ab3)


<a id="orga18657e"></a>

# はじめに

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

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

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

この記事は https://achiwa912.github.io/ にも載せました。

<a id="org7441e75"></a>

# 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ファイルを読み込む説明はありませんでした。


<a id="org2258be2"></a>

# 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()

そして実行すると、、、、
<img width="690" alt="pdfpage.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/678675/e4b0533c-4e20-e650-ffcc-819a030ebc56.png">
あれ、さくっと動いてしまいました。念のため、ページの下に書いてある7/88というところが、今回入れたページ番号です。あのdisclaimerは何だったのか。。。


<a id="orgefcf672"></a>

# 使用方法

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

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

    pip install reportlab
    pip install pdfrw

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

実行

    python addpagenum.py <pdf_filename>

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


<a id="orga183ab3"></a>

## カスタマイズ方法

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

    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マニュアルを参照してください。