以前書いたこの記事は珍しく好評だった。
これは、greple
コマンド用の -Mmsdoc
モジュールを元にして、optex
コマンドを使って office ファイルに対して様々なコマンドを実行するという内容だった。
最近 PDF ファイルを git で管理する機会があり、やはりこれも git diff したいと考えた結果、前回とは異なるアプローチで実現してみた。
pdftotext
PDF ファイルをテキストベースで diff すること自体は、さして難しくはない。pdftotext
というコマンドを使えば、一時ファイルを作るまでもなく、たとえば bash だったらこんな風に比較することができる。
$ diff <(pdftotext A.pdf -) <(pdftotext B.pdf -)
<(command)
は、bash の「プロセス置換」と呼ばれる機能で、コマンド出力をファイルのように扱うことができる。僕は、このプロセス置換を使いたいためだけに tcsh から bash に乗り換えた。
プロセス置換を使わずとも、こんな風にトリッキーに実行することもできる。打ち込むのは面倒だが、alias や関数にしてしまえばどうっていうことはない。
$ pdftotext A.pdf - | (pdftotext B.pdf - | diff /dev/fd/3 -) 3<&0
ただ、git のために設定しようとするとどうもうまくいかず、間にスクリプトを介さなければならなかった。実現したいのは、このような仕様の違いを吸収して、diff に限らず、コマンドの引数として普通にファイルを指定する統一的なインタフェースを提供することだ。
$ diff A.pdf B.pdf
optex -Mtextconv
今回もやはり optex
のモジュールで対応することにする。
結論としては、様々な種類のファイルをテキスト形式に変換する textconv というモジュールを作った。以前 greple
のモジュールとして実現した変換機能も組み込み、単独で動作する。現在のところ、docx
, pptx
, xlsx
に加えて pdf
の拡張子に対応している。ついでに jpg
データが指定されると exif
コマンドの出力に置き換えるようになっているが、役に立つかどうかは不明だ。
追記: その後、-Mtextconv
と打ち込むのが面倒になって -Mtc
というエイリアスモジュールを同梱することにした。2020年3月リリースの Version 0.08 以降で利用可能だ。
追記: XML フォーマットの docx
等に加えて、doc
と xls
フォーマットにも対応した。(2022-12)
追記: gpg
ファイルも処理するようにした。(2023-06)
使い方は簡単で、optex -Mtextconv
に続けて実行したいコマンドを指定するだけだ。grep だろうが diff だろうが、なんだったら vi だって実行できる。試しにやってみたところ vi で開いて :w
もできたが、書いたデータを読む方法はわからない。
$ optex -Mtextconv vi shishin4.pdf
そういえば、今度は PowerPoint のページもちゃんと並び替えてます。
PDF を比較する
本来の使い方としては、-Mtextconv
はコマンドオプションとして指定するのだが、このモジュールの場合には optex
のオプションに指定しても、ほぼ同じ効果を持つ。
$ optex -Mtextconv diff -u shishin4rc.pdf shishin4.pdf
見にくいので diff
の代わりに sdif
を使うとこんな風に見える。目次のドットの数が変わっているなど、目視では確認しずらいこともわかる。
$ optex -Mtextconv sdif -u shishin4rc.pdf shishin4.pdf
ちなみに、-u
を付けずに実行した場合、古い sdif
だとエラーになることがある。/dev/fd を使う場合特有の問題だ。最新版では修正されているので、使うのならまずそれをインストールした方がいい。
Word と PDF も diff できる
-Mtextconv
モジュールは、複数種類のファイルを扱うことができるので、それらを混在させることも可能だ。だから、Word と PDF を比較することもできる。ただ、構造が変わってしまうので、思ったような結果が得られるとは限らない。可能なら、文章構造を保っている Word ファイルで比較する方が、有用な結果が得られる。
$ optex -Mtextconv sdif -u hakodate-rc.docx hakodate-gaiyo.pdf
diff.rc
前の記事にも書いたが、~/.optex.d
以下に、コマンド毎の設定ファイルを置くことができる。diff
コマンドの設定ファイルは ~/.optex.d/diff.rc
なので、ここに以下のような設定を書いておくと、自動的に -Mtextconv
オプション付きで実行される。さらに optex --ln diff
で ~/.optex.d/bin
にシンボリックリンクを作ってパスに通しておけば、単に diff
コマンドを実行するだけでテキストベースでの比較ができるようになる。これで当初の目的は達成した。
option default --textconv
option --textconv -Mtextconv $<move>
git の設定
変換ツールを統一したので git の設定は単純だ。~/.config/git/attributes
ファイルに以下のような設定をして、ファイル拡張子に対する属性を定義する。全部同じにしてしまっても別に構わない。
*.docx diff=msdoc
*.pptx diff=msdoc
*.xlsx diff=msdoc
*.pdf diff=pdf
そして textconv
にテキストに変換するコマンドを指定する。といっても、optex
経由で cat
を実行するだけだ。ちなみに、これを pdftotext
コマンドでやろうとすると、標準出力に出すオプションがなくてちょっと困る。
$ git config --global diff.msdoc.textconv "optex -Mtextconv cat"
$ git config --global diff.pdf.textconv "optex -Mtextconv cat"
[diff "msdoc"]
textconv = optex -Mtextconv cat
[diff "pdf"]
textconv = optex -Mtextconv cat
この状態で git diff
を実行すると、自分の場合、出力は sdif
を通して表示される。
git の設定には diff コマンドを指定する方法もあるが、それは以前の記事を参照して欲しい。diff を optex
経由で実行すればいいだけだ。
実は URL も diff できる
実は URL が指定されると w3m コマンドで変換するようになっているので、URL を diff することできる。たとえば、以下のようにすることで Macbook と Macbook Air の仕様を比較できる。
$ optex -Mtextconv sdif --non -u 'https://support.apple.com/kb/'{SP757,SP783}'?viewlocale=ja_JP&locale=ja_JP'
インストール
optex::textconv
cpanminus をインストールしてからでもいいが、とりあえずこれでインストールはできる。たまにしか使わないなら、むしろこの方がいいかも。
$ curl -sL http://cpanmin.us | perl - App::optex::textconv
sdif
sdif
を使いたい場合は App::sdif をインストールする。
$ curl -sL http://cpanmin.us | perl - App::sdif
細かい設定等は前の記事に書いてあるので見てください。
拡張性
モジュール構成はこんな風になっている。-Mtextconv
モジュールには変換機能は何も入っていなくて、default.pm
と msdoc.pm
という2つのモジュールに実装されている。
App/optex/
├── textconv.pm
└── textconv/
├── Converter.pm
├── default.pm
└── msdoc.pm
default.pm
の中身は @CONVERTER
という配列にファイル名のパターンと変換するコマンドを指定してあるだけだ。ファイル名は sprintf
の引数で与えられるので %s
の部分に展開される。ユーザ設定で簡単に増やせるようにはなっていないが、この部分を編集すればいくらでも拡張は可能だ。
package App::optex::textconv::default;
use App::optex::textconv::Converter 'import';
our @CONVERTER = (
# [ qr/\.pdf$/i => "pdftotext -nopgbrk \"%s\" -" ],
[ qr/\.jpe?g$/i => "exif \"%s\"" ],
[ qr[^https?://] => "w3m -dump \"%s\"" ],
);
1;
msdoc.pm
の方はもう少し凝っていて、中でテキストへの変換を実装している。この場合、to_text
というサブルーチンが定義されていると、それを呼ぶインタフェースになっている。独自の変換モジュールを作りたい場合は、同じように to_text
を定義すればいい。
特に PowerPoint が生成したものの場合そうなのだが、PDF ファイルのページの切れ目がわかった方が見やすい。そこで PDF は pdf.pm
という特別のモジュールで処理するようにした。デフォルトではページ境界にダッシュ78個の行を出力する。オプションでページ番号を表示したり、単に改ページ記号を出力することもできる。
外字と未定義文字
PDF ファイルには外字が含まれていることがある。また、理由はわからないが Word ファイルの中に Unicode の未定義文字が含まれていることがある。
そこで、Unicode のプライベート領域と未使用領域の文字は「〓」に置き換えるようになっている。
Version 0.05
SEE ALSO