チャーハンノート

チャーハンの作り方に関する覚書

自炊したPDFファイルに目次を追加する

自炊した書籍のPDFファイルに目次を追加した様子

ここでいう自炊とは

本が溜まってくると、年に1回ほどのペースで自炊をしている。
ここでいう自炊とは「書籍をスキャナーで取り込んで電子書籍化する」ことだ。

「自」分で「吸い」出すから自炊。
いかにもネットスラング的な言い回しなので、まるで違法行為のようだが、個人の利用においては合法であり、権利として保障されている。もちろんデータを他人に配布すればたちまち違法となる。
自分はトラ技のような専門誌や、買ってから1年以上経った技術書をひたすら電子化してはライブラリを眺めてニヤニヤしている。(読んでない)

目次

自炊の方法

自炊の方法はいくつかある。
主な分類は本を裁断する方法と裁断しない方法の2つ。ちなみに裁断しない方法は「非破壊型」と呼んだりもする。(裁断する方法を「破壊型」とは呼ばない)
とはいえ今も主流なのは、書籍を裁断してドキュメントスキャナーで取り込む方法だろう。自分も専らこの方法を採っている。

ドキュメントスキャナー

ドキュメントスキャナーとはこんなもの
ハイスピードモデル ScanSnap iX1400 | スキャナーならScanSnap | RICOH

これはADF(Automatic Document Feeder)方式に分類され、 数十枚をセットしたらあとは自動で1枚ずつ両面同時に取り込んでくれるすぐれもの。

フラットベッドスキャナ

書籍を裁断しない非破壊型でいちばんイメージしやすいのは、複合機のような大型のスキャナーをつかう方法だろう。

デメリットとはいくつかある。
まず、見開きで取り込むので中央に影ができ、見栄えに難がある。そして何より、スキャンする毎にページをめくっては書籍をひっくり返すという作業を延々と繰り返す手間が半端ない。

ブックスキャナー

書籍を裁断しない自炊方法にはもう一つある。
デスクライトのようなデバイスの下で書籍をめくり、カメラ型センサーで1枚ずつ取り込むというもの。
スキャナー ScanSnap: SV600 特長 : RICOH

フラットベッドスキャナーのような手間が減るかわりに、書面が湾曲したり書籍を固定するときの指が映り込んだりするので、PC側で補正するためのアプリが付属していることが多い。
このタイプのスキャナーが登場したころは、映り込んだ指を消したり、湾曲補正するときの精度がイマイチという印象だった。
ところが、ここ数年で中華圏を含む様々なメーカーが続々と参戦してきて、だいぶ状況が変わってきたように思う。
指が写り込まないように特殊な押さえ具が付属したり、湾曲補正の精度も向上していたり、そして何よりも低価格化が激しい。

このタイプのスキャナーが増えてきた背景は、ドキュメントスキャナーのような複雑なメカ的な機構がないことや、スマホの普及によってCMOSセンサの高性能化&低価格化が進んだことで参入障壁が下がったことによるものだろう。
AI技術が進歩したことで補正の精度が向上したことも背景にあるかもしれない。

ベストな方法はない

このように自炊の方法はいくつかあるが、どの方法がベストというものはない。
・自炊したい書籍が裁断可能か
・平面を歪みなくスキャンしたいか
の2点について、予算や自炊作業に費やせる時間から勘案して決めるべきだろう。

自炊本には目次がない

ところで、Kindleのような電子書籍であれば目次から所望の項目へジャンプすることは朝飯前だ。しかし、当然だが自炊した書籍には目次機能はないので、自力で目次を用意する必要がある。

PCやタブレットで自炊した雑誌や新書を読んでいても目次機能が必要と思ったことはないが、専門書のように分厚くて何年も通じて読むような本だと、目次が必要だと感じることが多い。

目次が必要となれば、PDFリーダーのブックマーク編集機能をつかってポチポチと入力すればいいのだが、書籍によっては目次の量も膨大なので面倒くさくてやってられない。

そこで、なるべく最小限の手間でブックマークを一括追加する方法を考えることにした。
書籍の目次テキストデータは、出版社のサイトやAmazonのような販売サイトから見つけることができることがわかった。ページ番号は自力で入力する必要があるが、その程度なら耐えられる。
目次データが割りと簡単に用意できそうな目処が立ったので、Pythonでpdfが扱えるライブラリがないか探してみた。

pypdfをつかう

今回pdfの目次編集に使用したライブラリはpypdfというもの。
pypdf · PyPI

pythonでpdfを扱おうとしてgoogleなどで調べると、pypdf, pypdf2, pypdf3, pypdf4, pypdf5・・・など類似品が出てきて大混乱。
これらをざっくり分類すると以下の通り。

・pypdf : オリジナル
・pypdf2 : pypdfのバージョンアップ版として本家から発生
・pypdf3,4,5 : 有志により発生

現在はpypdf2からpypdfに回帰するべく更新が再開されているため、pypdf2は非推奨とのこと。この乱世も幕を閉じつつあるのだろうか。
ということで今回はオリジナルのpypdfを使用する。

しおり派?目次派?

PDFリーダーによっては「目次」の表記が異なることが多い。
Adobe系とFoxit系は「しおり」と表現することが多く、WebブラウザーのEdgeやMacのプレビューでは「目次」と表現している。Chromeは「ドキュメントの概要」(オチではない)。
ひとまずこれ以降は「目次」で統一する。ところどころ「ブックマークファイル」という表現も出てくるが、これはpypdfの表現に合わせたものなのでご容赦いただきたい。

ブックマークファイルの作成

さてここからようやく実践編。
今回用意したpdfは、いつものようにドキュメントスキャナーで取り込んだ書籍。(Kindle版がないので、どこでも読めるように自炊させていただきました)
当然、自炊したばかりのこの時点では目次は存在しない。

まず前準備として必要なのがブックマークファイルというもの。
ここでいうブックマークファイルとは、
・階層 (章、節、項など)
・タイトル
・ページ番号
が羅列されたテキスト(CSV)ファイルのこと。

そもそもブックマークファイルを作成するためには、目次ページにズラッとならんだタイトルをすべてテキスト化する必要がある。とても面倒な作業だ。
ひとまず書籍名でググってみると出版社のサイトやAmazonのページが見つかる。そこには目次一覧が乗っている場合が多いので、そこから拝借する。
今回は honto.jp から目次一覧を入手した。

ところで、印刷されたページ番号とpdfのページ番号は一致しないことが多い。今回の場合だと
pdfのページ番号 = 印刷されたページ番号 + 12
だった。今回の書籍では目次までローマ数字(ⅰ, ⅱ, ⅲ, …)、本章からローマ数字(1, 2, 3, ...)となるためだ。

もう一つの注意点として、pdfのページ番号は1から始まるのに対して、ブックマークファイルは0から始まる。例えばpdfファイルのp.10はブックマークファイルだと9に相当する。

そんな制約事項を考えながら、いきなりブックマークを作っていたらページ番号をミスしてしまうので、下図のようなブックマークファイル作成用シートをExcelで作成した。
以下はExcelによる補助シートを使ったブックマークファイルの作成例。

手順としては、まずWebから入手した目次一覧をExcelに貼り付け、pdfファイルの目次ページをみながら階層番号を入力 (章→0、節→1、項→2 など) したり、ページ番号を入力した。

それらの書籍情報に基づいた内容をインプットしたら、シートの右側にブックマークファイル用のシートが生成されるので、それをテキストエディタにペーストすればブックマークファイルが完成。
そんなこんなでブックマークファイルも用意できた。

PDFファイルに目次を追加する

ここでようやくPythonをつかったPDFファイルへの目次追加を試みる。使用するライブラリはosとpypdfの2つ。

用意したpythonプログラムは下記のとおり。

import os
from pypdf import PdfReader, PdfWriter

# 関数の定義
def add_bookmarks(input_pdf_path):

    # ブックマークファイルを読み込み"bookmarks"に格納
    bookmark_file_path = input_pdf_path.replace(".pdf",".txt") # ブックマークファイルのパス
    bookmarks = []
    with open(bookmark_file_path, 'r') as f:
        for line in f.readlines():
            parts = line.strip().split('\t')
            indent_level, title, page_num = int(parts[0]), parts[1], int(parts[2])
            bookmarks.append((indent_level, title, page_num))

    # 元pdfファイルにブックマークを追加
    pdf_writer = PdfWriter()
    pdf_reader = PdfReader(input_pdf_path)
    for page_num in range( len(pdf_reader.pages) ):
        page = pdf_reader.pages[page_num]

        pdf_writer.add_page(page)

    parent_bookmarks = []
    for indent_level, title, page_num in bookmarks:
        parent = None
        if indent_level > 0:
            parent = parent_bookmarks[indent_level - 1]
        bookmark = pdf_writer.add_outline_item(title, page_num, parent)
        parent_bookmarks = parent_bookmarks[:indent_level] + [bookmark]
    
    # ブックマークを追加したpdfを "〜〜_BM.pdf" として保存
    output_pdf_path = input_pdf_path.replace( ".pdf", "_BM.pdf" )
    with open(output_pdf_path, 'wb') as out_pdf:
        pdf_writer.write(out_pdf)

上記の関数を実行してOKであれば、関数の定義は完了。続けて下記コマンドを実行する。

実行するディレクトリと同じ場所にpdfファイルとブックマークファイルを置いておく。
名前はそれぞれ
・サンプル.pdf
・サンプル.txt
とする(拡張子以外は同じ)。

実行コマンド

add_bookmarks('サンプル.pdf')

実行してエラーがなければ 、元ファイルと同じ場所に ”サンプル_BM.pdf” というファイルが現れる。(BMはブックマークの略のつもり)

PDFビューワで開き、目次一覧ができていることを確認できた。試しに第1講をクリックするとそのページにジャンプしたので成功。

最後に

自炊したPDFファイルについて、pythonを使って目次を追加する方法について紹介した。
最小限の手間で目次を追加するためのフローが確立できたので、今後は過去に自炊した専門書についてもどんどん目次を追加していきたい。

繰り返しになるが、書籍を電子化することは個人の利用においては合法。ただし、その電子化データを他者に配布したら、たちまち違法となるので、やってはならない。
そのことに念を押しつつ、終わりとさせていただきます。