Acetaminophen’s diary

化学に関すること,TeXに関すること,ゆきだるまに関すること。

LaTeX で PDF の一部だけ表示したい(3)

TeX Live 2015 時点での「PDF の BoundingBox」まとめ

第1回第2回の更新から実に3か月が空いてしまいました… ほかの作業*1に追われておりました。この長いブランク(というより、第1回・第2回の記事を公開した直後の一週間以内)に、この PDF の BoundingBox 問題に大きな進展がありました。このことについて説明する前に、改めて TeX Live 2015 時点での BoundingBox の扱いについてまとめると以下のようになります:

  • pdfLaTeX:デフォルトは CropBox である。pagebox オプションを使えば好きな Box を選択できる。これらを bb= で上書きすることはできないが、これは間違いを起こさないという点で安全で、最も理想的な状態
  • LuaLaTeX:デフォルトは CropBox である。現時点では pagebox オプションを使えず、bb= で上書きすることもできないので「常に CropBox である」。pagebox オプションさえ使えるようになれば理想的(つまり pdftex.def の改修が必要)。
  • XeLaTeX:デフォルトは CropBox である。現時点では pagebox オプションを使えないが、bb= で上書きすることは可能。pagebox オプションさえ使えるようになれば理想的*2
  • dvipdfmx:「CropBox → ArtBox → TrimBox → BleedBox → MediaBox の順で明示されている最初のもの」を使う。pagebox オプションは使えないが、bb= で上書きすることは可能。これは改善の余地が相当あるといってよい。
  • いずれにおいても、viewport= オプション単独指定の場合は「自動取得された正しい BoundingBox 値と、そこからの相対座標」によって期待どおりの出力が得られる。

のようになります。見てのとおり、dvipdfmx はかなり深刻です。bb= で上書きできることは諸刃の剣で、良く知っているユーザなら自分で好きな ナントカBox を指定するインタフェースになりうるのですが、そうでない場合はマチガッタ使い方の温床になります。実際、bb= をまるで viewport= かのように使った失敗例は数多く、それに ZR さんが警鐘を鳴らしてきたわけです。

dvipdfmx の互換性を維持したまま状況を改善するには

といった改修が必要になるなあ…

 

TeX Live 2016 へ向けた「PDF の BoundingBox」まとめ

こうした議論を経て、実際に角藤先生を中心に dvipdfmx / xdvipdfmx の機能を拡張して pagebox= オプションを使えるようにする拡張が行われました。これは既に W32TeX には反映されており、TeX Live では 2016 から反映されることになるでしょう。

この改修によって、最新版(開発版)では PDF の BoundingBox の扱いは以下のようになっています:

  • pdfLaTeX:変更なし。
  • LuaLaTeX:変更なし。pdftex.def さえ改修されれば pagebox オプションが使えるようになり、好きな Box を選択できる。
  • XeLaTeX:デフォルトは CropBox で、新設された pagebox オプションを使えば好きな Box を選択できる後方互換のため bb= で上書きすることは可能なままである。
  • dvipdfmx:「CropBox → ArtBox → TrimBox → BleedBox → MediaBox の順で明示されている最初のもの」を使う。ただし、新設された pagebox オプションを使えば好きな Box に変えられる後方互換のため bb= で上書きすることは可能なままである。
  • いずれにおいても、viewport= オプション単独指定の場合の挙動は変更なし。

エンジンによって挙動が異なる点がややこしいのは確かですが、(x)dvipdfmx の改修によってかなり柔軟に Box を指定できるようになりました

 

で、結局 PDF の一部だけ表示する話は?

さて、いよいよ本題の「PDF ファイルの一部だけ表示する」を考えます。この必要性が生じる顕著な例は、やはり最初に挙げた「余白が大きな PDF の余白部分を切り取って、描画範囲だけを表示させたい」という場合でしょう。このときに使うべきオプションはもちろん bb ではなく viewport オプションであることは既に述べました。では、そのとき viewport オプションで指定すべき値とはなんでしょうか?

楽をしたい人は「余白を除いた範囲を bp 単位で自動で教えてくれるツールはないのか?」と思うわけでしょう。それに応えてくれるツールは一応あり、それは Ghostscript の bbox デバイスというものです。以下のようなコマンドで起動します(gs は Windows なら gswin32c / gswin64c / rungs に読み替えてください)。

$ gs -dBATCH -dNOPAUSE -sDEVICE=bbox hoge.pdf

こうすると、hoge.pdf の余白を除いた領域の BoundingBox の相対値を返してくれます。Ghostscript でいうところの BoundingBox は TeX とは異なり、余白を含まないことに注意してください。めでたしめでたし…と思いきや、この「相対値」が曲者です。何に対する相対値でしょうか?

Ghostscript の bbox デバイスのデフォルト仕様は「MediaBox の左下の座標からの相対値」です。ところが、いままでみてきたとおり TeX が使う BoundingBox は MediaBox だったり CropBox だったり、はたまた別の Box だったりします。幸い Ghostscript はバージョンが上がるにつれて、MediaBox ではなく CropBox その他に対する相対値を出力するインタフェース (-dUseCropBox, etc.) も実装されました。しかし、問題はこの Ghostscript の機能を活かせるような知識をユーザが持っているかどうかにかかってきます。

実際に TeX がどの Box を使うかを正しく把握する → その Box に対する相対値を Ghostscript の bbox デバイスに返させる → その値を viewport に指定する

という非常に困難な作業を経なければ、期待どおりの出力を得ることができないのです。もし仮に全ての PDF が MediaBox しか持っていなければ、機械的に「bbox デバイスの値を viewport に貼り付ける!」で済みます。しかし残念ながら、実際には「Illustrator で出力すると4つの Box が存在するので…」とか「トリミングツールで保存すると CropBox が存在して…」というように、さまざまなツールがさまざまな Box を弄ってきます。結局、いちばん手間のかからない確実な方法は

もう、最初から pdfcrop しようよ!

これに尽きますね。

 

(付録)PDF ファイルが不正な場合は?

いままでは PDF ファイルが正常な「MediaBox をほかのどの Box もはみ出していない」かつ「ArtBox, BleedBox, TrimBox は CropBox をはみ出していない」という場合だけを考えてきました。これらに反する不正な PDF を敢えて使った場合にどうなるのか、念のため調べてみました。なお、これは PDF の仕様に反するので「何が起きてもおかしくない未定義動作である」ことは心に留めておきましょう。

1. pdfLaTeX の場合の BoundingBox(不正な PDF の場合)

pdfLaTeX は PDF の仕様にのっとり、はみだした不正な Box をクリップします。すなわち、pdfinfo が判断するのと同じものが使われます。

2. LuaLaTeX の場合の BoundingBox(不正な PDF の場合)

LuaLaTeX は pdfLaTeX とは少し異なり、「採用しようとした Box がはみだし不正を起こしていれば、その Box 自体を無視する」ようです*3

3. XeLaTeX + xdvipdfmx の場合の BoundingBox(不正な PDF の場合)

XeLaTeX も LuaLaTeX と同様で「採用しようとした Box がはみだし不正を起こしていれば、その Box 自体を無視する」ようです。その仕様にさらに xdvipdfmx との齟齬が加わり、ややこしい結果になります。

4. dvipdfmx の場合の BoundingBox(不正な PDF の場合)

dvipdfmx は Box のはみだし不正には無関心で、クリッピングも無視もせず単純に書かれている値を鵜呑みにします。ただし、新設された pagebox オプションを用いた場合は pdfLaTeX と同様のクリッピングが行われます。

 

私も実験したい! 場合は

doraTeX さんが「すべての ナントカBox を自由に指定・非指定にできる pdfLaTeX ソース例」を示してくださっています。TeX エンジンで出力した PDF は通常 MediaBox のみを持ち、これは左下を (0,0) 、右上を (w,h) とする長方形になります。しかし、pdfLaTeX の場合は \pdfpageattr によって自由に全ての ナントカBox を明示することができ、これを利用したのが doraTeX さんの例示ソースです。私の例で使用した不正な PDF も含めて、すべて pdfLaTeX を使えば簡単に作り出せます(とはいえ、そんな PDF はわざとでない限り描画ツールなどが生成することはないでしょう)。

*1:毎日更新してきたサブブログに記録が残っています。

*2:bb= が上書きできる機能は pagebox= さえ使えれば不要になるが、これを排除することは後方互換性を損なうので控えるべきだろう。

*3:より詳細には「x 座標が左右の一方でもはみ出していれば、左右両方の x 座標を無視」と「y 座標が上下の一方でもはみ出していれば、上下両方の y 座標を無視」という判定が互いに独立に行われているようです。