2015-10-23
dvipdfmx は /Rotate に対応していないという話、への対処法
TeX, Ghostscript, メモ
昨日の実験で、2つの問題点が露呈した:
- (x)dvipdfmx で EPS を貼りつける際に Ghostscript の "majority decision" に由来する “無意味な回転とそれに伴う警告表示” が起こる
- (x)dvipdfmx で PDF を貼りつける際に /Rotate [angle] に対応していないため、ビューアでの見え方と異なる向きで図が挿入されてしまう場合がある
前者の対策は簡単で「dvipdfmx.cfg の rungs コマンド行(D で始まる)に -dAutoRotatePages=/None
を追加」で済む(2015-11-05 コミット済み)。後者は問題で、仮に「たまたま持っている PDF が既に /Rotate を含んでいた場合」には思わぬ回転*1が起きる恐れがある。そこで、今日は「PDF に /Rotate が含まれる場合」の対処法を考える。根本的には (x)dvipdfmx が /Rotate をサポートしない限りどうしようもないのだが、他の対策はないか考えてみようというわけである。
「(x)dvipdfmx に /Rotate を解釈して回転してもらうのは無理だ」という条件下で考えるならば、対策の方向性は一つ:
対応済みのツールで前処理
であろう。そして、それは既に説明したツールで十分可能である。対処法1:pdfcrop を使う(または類似処理を手動で)
昨日確認したとおり、pdfLaTeX(と LuaLaTeX)は /Rotate を解釈して正しく回転する能力を持つ。これさえわかれば「pdfTeX で前処理しよう」という発想に至る。「pdfTeX で前処理」といえばすぐに pdfcrop(または僕が作った bcpdfcrop)が思いつくので、試しにこれを使ってみよう。
"majority decision" のせいで /Rotate が付いてしまった dnorm.pdf を
$ pdfcrop dnorm.pdf
で処理すると、dnorm-crop.pdf という「余白がクロップされた PDF」が出てくる。そして、それだけではない。pdfTeX は /Rotate を持つ PDF を食わせても、出力するほうの PDF には /Rotate を含まないのである(確認法は後述)。もし余白が消えてしまって困るのであれば、以下のような pdfTeX ソースを処理すればページサイズを維持した新しい PDF を出力することも可能である*2:
% pdftex \pdfoutput=1 \def\procinclude{\pdfhorigin0bp \pdfvorigin0bp \setbox0=\hbox{\pdfximage page 1 mediabox{dnorm.pdf}\pdfrefximage\pdflastximage} \pdfpagewidth\wd0\relax \pdfpageheight\dimexpr\ht0+\dp0\relax \shipout\hbox{\raise\dp0\box0\relax}} \procinclude \end
これで、既に /Rotate がストリームに書き込まれてしまっている PDF を dvipdfmx で使いたい場合も警告なく “ビューアで見えるとおりの向き” で取り込める。一件落着! ほかにも方法はありそうだ*3が、TeX を使うのだから TeX でなんとかしようというのはアリだと思うので、いまはここで留めておこう。
追記 (2015-10-26):本質的には同じく pdfTeX を使う方法であるが、別の選択肢もいくつか出てきたので追加情報をまとめておく。
手持ちの PDF に /Rotate があるかどうかの確認法
いま手元にある PDF に /Rotate があるかどうかは、ビューアがこれを正しく解釈して回している限り見た目で判断することはできないだろう。これが dvipdfmx 使用時に「あれっ?」となる原因であるが、予め /Rotate が PDF にあるかどうか調べておけば驚くことはなくなるだろう。
確認する方法としては以下が挙げられる:
- 現時点で /Rotate 非対応の extractbb に PDF を通して警告が出るかどうか試す
- pdfinfo の出力に回転情報が出てくるかどうか確認する
- QPDF を使って PDF のストリームを QDF 形式に分解して読みにいく
1つめの方法は、現在の dvipdfmx が /Rotate に非対応であることを逆利用して知る方法。dvipdfmx が解釈すべき /Rotate は画像のサイズ取得時に引っかかる場所に書かれているので、“dvipdfmx そのもの”である extractbb が警告を発するかどうかで dvipdfmx サポート内かどうかを見積もることができるというわけである。dvipdfmx を使いたいという目的ならばこれが簡単かもしれない。3つめの方法は高度だが、手動で QPDF を使って PDF を QDF というテキスト形式に変換して読んでみるというもの。PDF の中身を詳しく知りたければ常套手段である(こちらは後日)。
2つめの方法は汎用的な方法である。Xpdf や Poppler に含まれる pdfinfo というプログラムは、PDF のプロパティにあたる情報やページサイズ情報をコンソールに表示する。このなかに PDF の回転情報が含まれるのである。Poppler に含まれる pdfinfo の場合は
$ pdfinfo dnorm.pdf Title: R Graphics Output Creator: R Software Producer: GPL Ghostscript 9.16 CreationDate: Thu Oct 22 20:27:16 2015 ModDate: Thu Oct 22 20:27:16 2015 Tagged: no UserProperties: no Suspects: no Form: none JavaScript: no Pages: 1 Encrypted: no Page size: 435 x 434 pts Page rot: 90 File size: 3637 bytes Optimized: no PDF version: 1.5
上記の Page rot:
に /Rotate の値(ここでは右90度回転)が表示されている。また Xpdf に含まれる pdfinfo の場合(または doraTeX さんの pdfinfo-extractbb の場合)は
$ xpdf-pdfinfo dnorm.pdf Title: R Graphics Output Creator: R Software Producer: GPL Ghostscript 9.16 CreationDate: Thu Oct 22 20:27:16 2015 ModDate: Thu Oct 22 20:27:16 2015 Tagged: no Form: none Pages: 1 Encrypted: no Page size: 435 x 434 pts (rotated 90 degrees) File size: 3637 bytes Optimized: no PDF version: 1.5
上記の Page size:
に /Rotate の値(ここでは右90度回転)が表示されている。
この記事へのコメントより
なるほど,/Rotate による回転を“確定”させるために pdfTeX が役立つと。pdfTeX 万能神話は健在だ。 URL
そう、PDF の /Rotate は「全部描画してから全体を最後に回転する」ようなものなので、未対応のツールがあったとすれば失敗してしまう(今回の dvipdfmx など)。しかし一旦 pdfTeX を通して “確定” させれば OK、というのが今回の記事。
dvipdfmx.cfgのrungs実行コマンドの中に「-dAutoRotatePages=/None」を追加したほうが幸せになるのかもしれないと思ったけどどうだろう? どうせdvipdfmxは/Rotateをサポートしていないし。 URL
これはホントに依頼してみようかな?
「現状のpdfcropで唯一足りないのは『クロップしない機能』だな」 #えっ
「クロップしないpdfcrop」として、bcpdfcrop.batを流用したbcpdftk.batを作ってしまおうかという構想がないわけではない URL
こっちはすぐに可能。bcpdftk という名称を検討しているのは、単に PDF のサイズ維持だけだと必要性が伝わりづらいので pdftk ライクな結合処理にも対応させてしまおうという構想。ヒマがあれば…
*1:正確にはむしろ「回転すべきところで回転しない」のだが、そんなことは PDF のストリームを見て /Rotate の有無を確かめるまでわからないので、ビューアで見た目を確認するユーザにとっては「思いがけない向きに回ってしまう」と思っても無理はなく、敢えてこの表現にした。
*2:以前の記事で pdfcrop の心臓部となる「余白を切り詰める pdfTeX ソース」を示したが、その類例である。そして、これら2つの手法は bcpdfcrop の中で生きている。是非探してみよう!
*3:今回の対処法の別解として pdfjam(実体は pdfLaTeX を動かすシェルスクリプト)を使うという手も考えられる。もちろん出力 PDF には /Rotate を含まないのだが、問題はサイズが a4paper などの用紙サイズかユーザが手動で指定したサイズにしか変更できないらしいこと。したがって「pdfjam でページサイズを維持して新しい PDF にする」が思い浮かばない…(追記:--fitpaper
オプションがあるそうだ)。しかも、シェルスクリプトで書かれているので Windows のネイティブ環境では動かない。したがって、Perl で書かれていて汎用的な(かつ有益な場合の多い余白カットまで行ってくれる)pdfcrop のほうを better と判断している。