adbird(広告鳥) 備忘録

python と pdftk で、複数のPDFを結合し、ページ番号をつけ、しおりをつけるスクリプト【改良版】

以前、以下のような記事を書いたが、この方法だと別途「ページ番号.pdf」と「bookmarks.txt」が必要だった。

python と pdftk でPDFを結合し、ページ番号をつけ、しおりをつけるスクリプト - adbird(広告鳥) 備忘録

今回は、「ページ番号.pdf」と「bookmarks.txt」を用意しなくても良い方法。

えぇ、今回もChat GPTさんに聞きまくりました(これやるにはどんなスクリプト書けばいい?→こうです→こんなエラーが出たよ→こうでした→こんなエラーが…の繰り返し)。

環境はUbuntu

ディレクトリ構造

├── 001_test.pdf
├── 002_テスト.pdf
├── 003_てすと.pdf
├── addbookmarks.py
├── getpagenum.py
└── mergepdf.py

pdfファイル名は「001_」というように、3ケタの連番+半角アンダーバーを必ずつける。

getpagenum.py、mergepdf.py、addbookmarks.pyの順番にスクリプトを実行していく。

getpagenum.py

pdfファイルを結合した際の、各pdfがどのページに位置づけられるかを取得。また、ファイル名を取得してbookmarks.csvに出力するスクリプト

import csv
import glob
from PyPDF4 import PdfFileReader

# PDFファイルのリストを取得する
pdf_files = sorted(glob.glob("*.pdf"))

# 結合後の各PDFファイルの開始ページを取得する
start_pages = []
current_page = 1
for pdf_file in pdf_files:
    with open(pdf_file, "rb") as f:
        reader = PdfFileReader(f)
        start_pages.append(current_page)
        current_page += reader.numPages

# bookmarks.csvファイルに出力する
csv_file = "bookmarks.csv"
with open(csv_file, mode='w', newline='') as f:
    writer = csv.writer(f)
    for idx, start_page in enumerate(start_pages):
        writer.writerow([f"1",pdf_files[idx], start_page])

print(f"CSVファイル '{csv_file}' に出力しました。")

端末で

python3 getpagenum.py

とすると、「bookmarks.csv」が生成される。

mergepdf.py

PDFの結合とページ番号の追加。

import os
import io
from PyPDF4 import PdfFileMerger, PdfFileReader, PdfFileWriter
from reportlab.pdfgen import canvas

# カレントディレクトリを取得
current_directory = os.getcwd()

# 結合後のPDFファイル名
output_pdf = os.path.join(current_directory, "input.pdf")

# ワイルドカードを使用してPDFファイルをリストアップし、ファイル名のソートを行う
pdf_files = sorted([os.path.join(current_directory, file) for file in os.listdir(current_directory) if file.endswith('.pdf')])

# PdfFileMergerオブジェクトを作成
pdf_merger = PdfFileMerger()

# PDFファイルを結合
for pdf_file in pdf_files:
    with open(pdf_file, 'rb') as file:
        pdf_merger.append(file)

# 結合したPDFを一時的なファイルに保存
temp_merged_pdf = os.path.join(current_directory, "temp_merged_file.pdf")
with open(temp_merged_pdf, 'wb') as file:
    pdf_merger.write(file)

# ページ番号を追加する関数
def add_page_numbers(input_pdf, output_pdf):
    with open(input_pdf, 'rb') as file:
        pdf_reader = PdfFileReader(file)
        pdf_writer = PdfFileWriter()

        # 全ページの数を取得
        num_pages = pdf_reader.numPages

        # ページ番号を追加して新しいPDFを作成
        for page_number in range(num_pages):
            page = pdf_reader.getPage(page_number)

            packet = io.BytesIO()
            can = canvas.Canvas(packet)
            text = str(page_number + 1)
            can.drawString(565, 810, text)  #ページ番号の位置(左下からの位置。単位はpt。)
            can.save()

            packet.seek(0)
            new_page = PdfFileReader(packet)
            page.mergePage(new_page.getPage(0))

            pdf_writer.addPage(page)

        # 新しいPDFを保存
        with open(output_pdf, 'wb') as output_file:
            pdf_writer.write(output_file)

# ページ番号を追加
add_page_numbers(temp_merged_pdf, output_pdf)

# 一時的な結合ファイルを削除
os.remove(temp_merged_pdf)

print("PDFの結合とページ番号の追加が完了しました。")

端末で

python3 mergepdf.py

とすると、「input.pdf」が生成される。Windowsは python3 ではなく、python かも(以下、同じ)。

addbookmarks.py

bookmarks.csv 中の、ファイル名=ブックマーク名の「001_」から「050_」までの連番を削除。

bookmarks.csvをpdftkのブックマーク書式に変換して、上記のinput.pdfにブックマークをつけて、output.pdfに出力。

import csv
import re
import subprocess

# ファイル名
input_file = 'bookmarks.csv'
output_file_txt = 'output.txt'
output_file_pdf = 'output.pdf'

# ブックマーク書式のテンプレート
bookmark_template = "BookmarkBegin\nBookmarkTitle: {title}\nBookmarkLevel: 1\nBookmarkPageNumber: {page}\n"

# 連番のパターン
number_pattern = r'\d{3}_'

# 連番の最大値
max_number = 50

# CSVファイルの読み込み
with open(input_file, 'r', newline='', encoding='utf-8') as csv_in:
    reader = csv.reader(csv_in)

    # ブックマーク書き出し用のテキストファイルを作成
    with open(output_file_txt, 'w', encoding='utf-8') as txt_out:
        for row in reader:
            # ファイル名から連番を削除
            file_name = row[1]

            # 連番を削除
            file_name = re.sub(number_pattern, '', file_name)

            # ページ番号
            page_number = row[2]

            # ページ番号が連番の最大値を超えている場合は調整
            page_number = min(int(page_number), max_number)

            # ブックマーク書式にフォーマットして書き出し
            bookmark_data = bookmark_template.format(title=file_name, page=page_number)
            txt_out.write(bookmark_data)

# pdftkを使ってPDFにブックマークを追加
subprocess.run(["pdftk", "input.pdf", "update_info_utf8", output_file_txt, "output", output_file_pdf, "verbose"])

print("処理が完了しました。")

端末で

python3 addbookmarks.py

とすると、「output.pdf」が生成される。完成。