技術的な問題を調べていたら観念的な話になりました。役に立つ情報はほぼありません。

ある日、Pythonで国税庁のPDFを読もうとしたら、文字のはずの場所が空白になっていた。

データを調べると空白ではなく U+E156 というコードが入っていた。それは標準のUnicodeではなく、Private Use Area(PUA) と呼ばれる領域の値だ。誰でも自由に意味を定義してよい、未割り当ての番号帯。

そこで少し立ち止まって考えた。この「文字」は、何者なのか。

記号は、それを解釈するシステムと一体にある

記号論の基本的な考え方に、「シニフィアン(記号表現)」と「シニフィエ(記号内容)」の区別がある。

「髙」という文字を例に取ると——

  • 人間が見るもの:画面やPDFに描かれたグリフ(字形)
  • コンピュータが扱うもの:U+9AD9 というコードポイント
  • PDFの内部にあるもの:フォント固有のグリフID

これらは同じ「髙」を指しているようで、互いに独立したレイヤーにある。人間がPDFを読めるのは、視覚パターンから字義を直接認識するから。Pythonがテキストを読めるのは、コードポイントという「共通言語」がUnicodeとして定められているから。

問題のPDFでは、旧字体の列を Windows EUDC(外字フォント) で描画していた。EUDCは企業や個人がWindowsで独自に定義できる文字のコードポイント群で、PUAの一角(U+E000〜U+F8FF)に割り当てられている。

そして重要な点として、このPDFには ToUnicode CMap — グリフIDを標準Unicodeコードポイントへ変換するテーブル — が存在しなかった。

これが意味することを言い換えると:「U+E156 が 髙(U+9AD9)であるという知識は、このPDFを作成したWindows環境のローカルフォント設定の中にしか存在しない」 ということだ。

孤独な記号

PDFビューワがこの文字を正しく表示できるのは、WinEUDCフォントをロードして「U+E156はこういう形に描けばいい」という情報を持っているから。

でも「U+E156は標準Unicodeの何番に対応するか」という情報は、PDFの外部にある。それはフォントを設計した人、あるいはPDFを作成した人の頭の中か、そのマシンのローカル設定の中にある。PDFファイル単体には含まれていない。

pdfplumberもPyMuPDFも、PDFに書かれているコードを誠実に読んでいる。返ってくるのは U+E156 という値、それ以上でも以下でもない。ライブラリが「弱い」わけではない。ただ、PDFの外部にある知識(このPUAが実際に何の字か)にはアクセスできない。

ウィトゲンシュタインは「私的言語」について論じた。自分だけが知っている内的感覚に名前をつけても、それは言語として機能しないと。EUDCはある意味でそれに似ている。そのフォントを持つシステムの中でしか解読できない記号だ。

もし読めたとして、それを解釈してよいのか

ここで一つの問いが浮かぶ。

仮に何らかの手段でEUDCのグリフ画像を取り出せたとして、「この字形はUnicodeの何番に対応する」と、自分たちが判断してよいのだろうか。

旧字体・異体字という領域は、そもそもそういう問いが難しい。文字の同一性——「これとこれは同じ字か、別の字か」——は、専門家の間でも議論がある問題だ。Unicodeの委員会が統合・分離の方針を決め、JISが独自に体系を持ち、法務省の登記統一文字がまた別の基準を持っている。

実際にこのPDFを眺めていると、人間の目にも区別がつかないような字形が並んでいる箇所があった。画数の違う似た字。点の有無が曖昧なもの。「これはどちらだ?」と言い切れない字がある。

そこに「この字はU+XXXXだ」という解釈を自分たちが後から加えるのは、単なるテキスト抽出ではなく、一種の編集行為になる。国税庁が対応表を作るにあたって行った判断——どの旧字体をどの標準字体に対応させるか——は、専門的な検討の産物のはずだ。その判断を、PDFの見た目を手がかりに外部から推測で補うのはリスクが高い。

機械が「読めない」のは確かだが、仮に読めたとしても「正しく解釈できるか」は別の問題だ、という気づきがあった。

別の手がかりを探して

PDFから直接取れないとわかって、同じ国税庁サイトの別のデータを調べた。

JIS縮退マップというExcelファイルがあった。JIS第三・第四水準の文字をJIS第一・第二水準に変換するための、機械可読なマッピング表。正規Unicodeで851件のペアが整然と入っている。

これは「読める」データだった。しかし、もともとやりたかった okikaehyouver2.pdf の内容とは別のデータセットだ。対象とする文字の範囲も、選定の基準も違う。

「別のフォーマットがある」ことは発見だったが、目的を果たせるものではなかった。

結局、人間の知識に戻る

最終的な対応方針はこうなった。

「今回問題になっている字(髙)だけ手動で変換テーブルに登録し、以後は業務で実際に引っかかった字が出てきたタイミングで都度追加していく。」

これは消極的な後退ではなく、ある意味で素直な選択だった。

PDFの中の旧字体と標準字体の対応を知っているのは、そのPDFを作った人間たちだ。どの字とどの字が対応するかという判断を行い、根拠を持って表を作った人たちだ。その判断を、外部から推測で自動復元しようとするのは、正確さの面でも、越権という面でも、はじめからうまくいかない試みだったかもしれない。

「読む」という行為には、記号とその意味をつなぐシステムへの参加が必要だ。そのシステムに参加していない状態では、見えていても読めない、あるいは読んだとしても誤読になりうる。

ひとつの文字が、見える人には見えて、Pythonには読めない。さらに言えば、見えた人間でも、それが何番の字かを断言できるとは限らない。その二重の不透明さを理解したことが、今回の調査で得た一番の収穫だったかもしれない。

余談

ほぼ全く関係ないが、この件を調べていてふと思い出したことがある。

言語系のYouTuberが出版した本で、ルビが大量に文字化けしていた事件だ。ある人名のルビが「むてえよぬよとか」になっていたり、「UTF-8」のルビが「ヤョツァョウビウアテ」になっていたりしたという。

文字化けの向こうには正しい文字があったはずで、そこに向かうマッピングがどこかの変換工程で壊れた。見た目に残っているのはただの記号で、何かを指していた形跡だけがある。

直接関係はないのだけど、「文字が文字として機能するために必要なものは、文字そのものの外にある」という感覚が、どこか重なった気がした。

補足:技術的なまとめ

試みたこと 結果
pdfplumberでextract_tables PUAコード(U+E156〜)が返る
PyMuPDFに切り替え 同じPUAコードが返る(ライブラリではなくPDF構造の問題)
国税庁のJIS縮退マップExcelを調査 正規Unicodeペア851件取得できたが、別データセットで目的外
手動登録の漸進的運用を採用 問題になった字から順次対応

「自動化できない」という結論に根拠を持てたことで、手動運用という判断をチームとお客様に説明できた。

参考