🦛

PDFの注釈情報を抽出してMarkdown等で出力するPythonライブラリpdfannots

2025/02/12に公開

学術論文のレビューや組織内のドキュメント作成など,PDF文書に注釈を付けて複数人とやりとりする場面がある。
注釈はAcrobat Readerなどで閲覧・編集できるが,これをテキストデータで抽出できると嬉しいことも多いと思われる。

例えば,理工系文書のPDFで数式の一部をハイライトしてLaTeXの数式記法で修正内容のコメントを書いたとすると,Acrobat Readerなどでは可読性が非常に低いだろう。
つまり,\int_a ^b f(x) dx$\int_a ^b f(x) dx$となるわけで,より複雑な数式であればさらにわかりにくくなる。

そこでpdfannotsというPythonライブラリが使える。
https://github.com/0xabu/pdfannots
こちらのREADMEを見れば,どういう結果が得られるかなんとなくわかるだろう。
注釈の付いたPDFから,それを抽出したテキストが得られる。MarkdownとJSONで出力することができる。
Markdownなら前述のような数式を含む場合の可読性はマシにできるし,データとして活用したいときはJSONにするのがよいだろう。

以下,基本的な使い方を簡単に解説し,日本語PDFを扱う場合の注意点について述べる。

基本

pdfannotsをインストールしたのち,抽出したい注釈が付いたPDF(sample.pdf)を準備して,

pdfannots sample.pdf -o result.md

とすると抽出結果のMarkdown(result.md)が得られる。-o result.mdを指定しなければ標準出力。

Markdownでの抽出結果は3つのセクションに分けられる。

  1. Highlights:ハイライトのみの注釈
  2. Detailed comments:コメント付きハイライト・独立したテキスト注釈
  3. Nits:下線・取り消し線・波線による注釈

ページ番号の情報や目次情報があれば,それも抽出結果に含まれる。

JSON出力では,以下の9種類のキー・バリューペアが記述されている。

  1. name:注釈の名前
  2. type:注釈のタイプ(ハイライトがある注釈Highlightと独立したテキスト注釈Textの2種類)
  3. page:ページ番号
  4. start_xy:注釈のPDF上での座標
  5. text:ハイライトしたテキスト(typeがTextのときはなし)
  6. contents:コメントや独立したテキスト注釈のテキスト
  7. author:注釈の作成者
  8. created:注釈の作成日時
  9. color:注釈のカラーコード

オプション

オプションはpdfannots --helpで確認できる。このうち,使用する可能性が高そうなものを一部抜き出す。

  • --cols n:n段横組みを前提として読み込む。デフォルトでは内部で動いているpdfminerが判断。
  • --keep-hyphens:ハイフネーション含めハイフンを残す。デフォルトではすべてハイフネーションによるハイフンとして削除するので,複合語中のハイフンなども削除されてしまう。
  • --format 〇〇jsonにしたらJSON出力。デフォルトではMarkdown。

Markdown出力用オプション

Markdown出力用のオプションもいくつかある。こちらも,使用する可能性が高そうなものを一部抜き出す。

  • --no-group:セクションでグループ化(Highlights,Detailed comments,Nits)をせず出現順にする。
  • --group-highlights-by-color:ハイライトの色ごとにグループ化。
  • --sections 〇〇:グループ(Highlights,Detailed comments,Nits)のうち指定したセクションだけ抽出。2つでもよい。
  • --no-condense :ハイライトされたテキストの長さにかかわらず引用ブロックにする。詳細は後述するが,日本語PDFの場合には付けるのがよさそう。

日本語PDFで試してみる

以下のような注釈付き日本語PDFでpdfannotsを試してみる。

結果と問題

Markdown出力は以下のようになる。

## Highlights
 * Page #1 (1 はじめのセクション): "日本国民は、正当に選挙された国会に"


## Detailed comments
 * Page #1 (1 はじめのセクション): "この憲法 を確定する。" -- コメント1

 * Page #1 (1 はじめのセクション): 独立したテキスト

 * Page #1 (2 次のセクション):
   > この憲法 を確定する。

   コメント2

   改行あり


## Nits
 * Page #1 (1 はじめのセクション) suggested deletion:
   > ...戦争の惨禍が起ることのないやうにすることを決意し、ここに主権が国民に存することを宣言し、この憲法 を確定する。そもそも国政は、国民の厳粛な信託によるものであつて、その権威は国民に由来し、その権力 は国民の代表者がこれを行使し、その福利は国民がこれを享受する。これは人類普遍の原理であり、この憲 法は、かかる原理に基くものである。~~われらは、~~これに反する一切の憲法、法令及び詔勅を排除する。 日本国民は、恒久の平和を念願し、人間相互の関係を支配する崇高な理想を深く自覚するのであつて、平 和を愛する諸国民の公正と信義に信頼して、われらの安全と生存を保持しようと決意した。われらは、平和 を維持し、専制と隷従、圧迫と偏狭を地上から永遠に除去しようと努めてゐる国際社会において、名誉ある...

   取り消し線

ここでDetailed commentsセクションの1,4番目の注釈に注目する。それぞれPDFにおける「はじめのセクション」における紫色のコメント付きハイライト,「次のセクション」における黄緑色のコメント付きハイライトである。

どちらも同じテキストをハイライトしてコメントを付けているが,出力結果のスタイルが異なっていることがわかる。
ここでハイライトされたテキストとコメントのみを抜き出すと,1番目は

"この憲法 を確定する。" -- コメント1

であり,4番目は

> この憲法 を確定する。

コメント2

改行あり

と引用ブロックになっていることがわかる。
以下,便宜上1番目のスタイルをプレーンスタイル,4番目のスタイルを引用スタイルと呼ぶこととする。
GitHubのREADME中のデモ画像を見ると,コメント付きハイライトはすべて引用スタイルで出力されてほしいようにも思われるが,プレーンスタイルも混在している。

解決策

なぜこうなるのかを調べるために,Markdown出力に関係するリポジトリのpdfannots/printer/markdown.pyを見てみる。
253〜278行あたりをながめると,Detailed commentsセクションのスタイルは3パターンあり,条件分岐でプレーンスタイル,独立したテキスト注釈向けのスタイル,引用スタイルに分かれることがわかる。

詳細に条件を見ると,プレーンスタイルに分類される条件に「ハイライトされたテキストが10単語以下」がある。
そのため,Detailed commentsセクションの1番目がプレーンスタイルになっている。
pdfannotsはそもそも英語のPDF文書を前提としており,短い単語数のテキストがハイライトされていたらこの条件は満たされるが,日本語では通常分かち書きをしないため,多くのパターンがこの条件を満たしてしまう。

それでは,同じテキストをハイライトしている4番目の注釈はなぜ引用スタイルになっているのかというと,プレーンスタイルに分類される条件に「コメントが1行」という条件もあるためである。
4番目の注釈はコメントが2行になっているため,引用スタイルになっている。

このような状況を避けるには,前述の--no-condenseオプションを使う。
これにより単語数の条件は無効にでき,コメント付きハイライトの抽出結果は引用スタイルになるので,日本語PDFの場合には付けておくのが望ましいと思われる。

Discussion