メモ@inudaisho

君見ずや出版 / 興味次第の調べ物置き場

既存PDFに目次をつける PyMuPDF篇

既存PDFに目次をつける PyMuPDF篇

 10年くらい前に既存のpdfに目次をつけるためにいろいろ調べて書いといたが、それからまた状況がかわってるので新しく記事を書いて情報を共有する。

inudaisho.hatenablog.com

 いやまぁいちいち書いて知らせる必要はないかもしれんが、google で検索すると↑を改造したような記事を書いてる奴がでてくるんだがその記事の頃なら既にもっと簡単にできる方法があったのになんでそんな面倒な手法にとびついたのかよくわからん。ということでより簡単な方法を提示しておく。

PyMuPDF 1.21

 PyMuPDFの最新バージョンには目次を書きこむための set_toc(配列) というすばらしく簡単な関数がある。これがどれくらい簡単かというと

import fitz
#pdf読み込み
oPdf = fitz.open( "a.pdf" )
#目次の配列を食わせる
oPdf.set_toc( a目次の配列 )
#上書き保存
oPdf.saveIncr() 
#別名で保存
oPdf.save( "b.pdf" )

 くらい簡単に書ける。配列の構成は

[[レベル, テキスト, index],[...],[...]]

 という感じ。まぁ具体的には

github.com

 こんな感じなのでこれをみて適当にいじったらよい。この例ではcsvを食わせるようになっている。試してないが、配列の四つめに目次のページ内の位置を高さで設定できるらしい。自分はテキストに「ページ、タブ(個数でレベルを表現)、目次のテキスト」という感じで書いてそれを解析させてるがまぁそういう小手先のことはどうでもいいだろう。バージョンに注意。Debian標準のだとまだ対応していない(1.17.4)ので

pip3 install --upgrade pymupdf 

した。

同じページに違うレベルの目次を置ける!

 これのすごいのは同じページに違うレベルの目次を置けるということで、たとえば

a目次の配列= [
[1, "第一章 アインソフ", 1],
[2, "第一節 流出", 1],
[2, "第二節 光", 15],
[2, "第三節 無限光", 25],
[2, "注", 55],
[1, "第二章 アダム・カダモン", 55],
[2, "第一節 両性具有", 55],
]

 みたいにレベルが下がるときに重複できるだけではなく、レベルが上がるときでも重複できる。今まで使ってたreportlabの関数だと一つのページに一つしか置けないので階層化してもなんかあんまりキレイではなかった。というわけで、今までよりも凝った設定が簡単な記述で実現できるのでメンテナンス性もよくこれを採用することにした。ちなみにreportlabを使った方が軽いがやっぱり目次はちゃんと構造化できる方が大事なのでこれでいい。

 それから関数にget_tocなんかもあるのであるいはちゃんと掃除してから目次をつけてくれるのかと思ったが、実験したら目次をつけなおせばつけなおすほどファイルサイズが増大するので目次をつけるとき何度も修正する人は別名にしておいた方が無難。