Acetaminophen’s diary

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

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

実際に TeX が使う BoundingBox はどれなのか(続き)

昨日の記事で PDF における正解の BoundingBox とは何かを、ドライバ別に考えていた途中からです。XeLaTeX (+ xdvipdfmx) の場合に「XeLaTeX は CropBox を確保するのに xdvipdfmx が描画するのは後述する “dvipdfmx が選ぶのと同じ ナントカBox の内側” だけである」という奇妙な挙動が判明しました。というわけで、ここで一旦 dvipdfmx を調べてみましょう。

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

これが非常に問題なのです。正解は「extractbb というプログラムが返すもの」です。では extractbb が返す BoundingBox とは何かというと

CropBox → ArtBox → TrimBox → BleedBox → MediaBox の順で明示されている最初のもの

です。明示されているが曲者で、そのせいでこんなヤヤコシイ選択律の順序ができてしまっています。明示されていなければその Box は読まないという点で、通常の PDF の仕様と食い違っているわけです。実際にいくつかの PDF を取り込んで pLaTeX + dvipdfmx で処理すると、上記の extractbb の規則と一致する Box が選ばれていることを確認できます。

  • BB-a-04-dvipdfmx-TL2015.pdf の全ページより

    f:id:acetaminophen:20150816182116p:plain

確保された領域が extractbb の結果と一致していることを確認してみましょう。以下のように、全ての Box を明示した boximage-01-catbm.pdf では CropBox が返ってきます(昨日の pdfinfo の結果と比べてみてください)。

%%Title: boximage-01-catbm.pdf
%%Creator: extractbb 20150315
%%BoundingBox: 30 30 190 200
%%HiResBoundingBox: 30.000000 30.000000 190.000000 200.000000
%%PDFVersion: 1.5
%%Pages: 1
%%CreationDate: Sat Aug 15 23:12:42 2015

同様に、そこから CropBox が削除された状態にあたる boximage-02-atbm.pdf では ArtBox が返ってきます。CropBox の値は PDF 仕様書の規則によって暗に MediaBox と同一とみなされているはずですが、この値は採用されません。

%%Title: boximage-02-atbm.pdf
%%Creator: extractbb 20150315
%%BoundingBox: 60 10 140 220
%%HiResBoundingBox: 60.000000 10.000000 140.000000 220.000000
%%PDFVersion: 1.5
%%Pages: 1
%%CreationDate: Sat Aug 15 23:14:40 2015

要するに「extractbb は PDF の仕様と無関係に、独自の優先順位に従って明示された Box を探す」という仕様になっているのです。なお extractbb の独特の優先順位については、既に私の BoundingBox がとにかくややこしい話(1) でも追試しました。

このような dvipdfmx のヤヤコシイ、しかも PDF の仕様的に見てもマチガッタ BoundingBox 選択律はバグといってもいいほど酷いものですが、これが dvipdfmx にとっての正義です。なぜなら extractbb は dvipdfmx のシンボリックリンクだから、すなわち extractbb は dvipdfmx そのもの(名前が違うだけ)だからです。したがって、ユーザは否が応にもこれに準じた使い方をしなければなりません。

そのような事情で、通常は dvipdfmx 使用時に「extractbb の自動実行を許可して LaTeX によるタイプセットを実行する」という手法がとられているわけです。これこそが「ユーザにややこしい BoundingBox 事情を一切意識させずに PDF 画像の取り込みを可能にする唯一の方法」だというわけで、W32TeXTeX Live 2015 以降では extractbb の自動実行が標準で許可されていて、TeX Live 2014 以前でも texmf.cnf を編集してねと言われてきたのはそのためです*1

3. XeLaTeX + xdvipdfmx の再考(正常な PDF の場合)

先ほどの XeLaTeX の件に戻りましょう。XeLaTeX の場合、実際の描画を担う xdvipdfmx は dvipdfmx の拡張版です。したがって、先の実験で「xdvipdfmx が描画するのは “dvipdfmx が選ぶのと同じ ナントカBox の内側” だけである」という結果が得られた理由は簡単です。要するに epdf special という DVI special の描画の仕組みが自分にとって正義と判断した領域の外側は描画をサボるからだと理解できるのです。pLaTeX + dvipdfmx の場合は領域確保を担う extractbb と実際に描画する dvipdfmx が同一コードなので確保領域=描画範囲になるのですが、XeLaTeX + xdvipdfmx では領域確保する XeLaTeX と実際に描画する xdvipdfmx が別々のため、整合性が取れない場合がありうるわけですね。

 

LaTeX による確保領域の自動取得はどのような場合に行われるか

ここまで \includegraphics する際に BoundingBox を LaTeX ソース中に書き込むようなことは一切しませんでした。したがって、pdflatex / lualatex / xelatex / platex 処理中になんらかの方法で BoundingBox 情報が読み取られ、それが使われたことになります。この情報取得が TeX エンジンそのものによるものか、それとも他のプログラムによるものかは latex 実行時に -no-shell-escape オプションを付けたときに処理できるかどうかで判別できます。pdflatex / lualatex / xelatex 処理には -no-shell-escape は特に影響を及ぼしませんが、platex + dvipdfmx の場合は extractbb が自動実行できず、以下のようなエラーを返します(エラーは r34507 の変更を境に異なります*2)。

  • dvipdfmx.def 2014/04/28 v4.01 以前
    ! LaTeX Error: File `./grf-PDFs/boximage-a1-catbm.xbb' not found.
    Use -shell-escape option to generate automatically.
    
    See the LaTeX manual or LaTeX Companion for explanation.
    Type  H   for immediate help.
     ...
    
    l.26 ...hics[width=0.5\textwidth]{boximage-a1-catbm.pdf}}
                                                             END
    ?
    
  • dvipdfmx.def 2014/07/02 v4.02 以降
    ! LaTeX Error: Cannot run pipe command. Try --shell-escape
    (--enable-pipes in MikTeX) option.
    
    See the LaTeX manual or LaTeX Companion for explanation.
    Type  H   for immediate help.
     ...
    
    l.26 ...hics[width=0.5\textwidth]{boximage-a1-catbm.pdf}}
                                                             END
    ?
    

この platex + dvipdfmx で -no-shell-escape 時にエラーを発生させない方法は「extractbb を事前に実行して .xbb ファイルを作っておく」あるいは「後述する bb= オプションを書き込んでおく」の2通りがあるわけです。

 

ユーザが BoundingBox を指定すると…

では、ユーザが bb= オプションで BoundingBox を指定してみるとどうでしょうか。結論として、bb= を使うと XeLaTeX および dvipdfmx が自動で読み取る BoundingBox を上書きできるようです。「上書きできる」ことは説明書で保障されているわけではありませんが、実装上そうなっています(ZR さんの「dvipdfmx で画像する時に色々とアレな話」に記述あり)。そして、そのことは好都合で、現に「デフォルトとは異なる Box を使って欲しい」という場合に役立ちます。

念のため、各エンジンについて bb= を指定してみるという実験をやってみましょう。上書きできたかどうか確認するため、あえて正解とは全く異なる bb=50 100 150 250 というオプションを先のソースに付けてみることにします。比較のため、bb= を外して代わりに viewport=50 100 150 250 というオプションにした場合も試してみました。viewport= は「使う BoundingBox に対する相対座標を使って一部表示範囲を指定する」というもので、当然ながら採用された BoundingBox が正しい前提です。

1. pdfLaTeX で bb= 指定

pdfLaTeX の場合、タイプセット中に以下の警告が出ます。

Package pdftex.def Warning: Option `bb' does not make sense,
(pdftex.def)                using `viewport' instead on input line 26.

つまり bb というオプションは正しくないので代わりに viewport のことだなと親切に修正したと仮定したうえで処理してくれるわけです。これは pdftex.def v0.03e (2000/09/14) の時点で既にこの処置が施されています。clip オプションもほぼ同時にサポートされています。

% 2000/09/14 v0.03d 
86 %  * Fixes for `viewport' and `trim' (HO). 
87 %  * Clip support added for viewport and trim (HO).
% 2000/09/14 v0.03e
%  * Options `bbllx', `bblly', `bburx', `bbury' disabled,
%    option `bb' redirected with a warning to `viewport' (HO).

したがって、viewport= と bb= はこの親切な対応のおかげで一致します。ちなみに、pdfLaTeX は viewport 座標の原点を CropBox(非明示の場合は MediaBox に一致)の左下とするようです(なお、枠からはみ出しているのは clip オプションを付ければ解消します)。

  • BB-a-01-pdfLaTeX-TL2015-bb の全ページより

    f:id:acetaminophen:20150816184242p:plain

このことから、pdfTeX では bb= を使っても BoundingBox 自体を上書きすることはできないといえます。しかし、もともと pagebox= で別の Box を指定するインタフェースが整っていることを考慮すれば実害はないでしょう(この5つの ナントカBox 以外に正解があることはまずないでしょうから)。

2. LuaLaTeX で bb= 指定

LuaLaTeX の場合は pdfLaTeX と全く同じです。なぜなら pdftex.def が読まれるからで、bb= は viewport= にリダイレクトされます。すなわち、LuaLaTeX では bb= を使っても BoundingBox 自体を上書きすることはできないといえます。このことは LuaLaTeX で(例のトリックを使わない限り)現状の CropBox 以外を指定することが graphicx パッケージのインタフェースからは不可能であることを意味し、場合によっては不都合かもしれません。

3. XeLaTeX で bb= 指定

XeLaTeX の場合は bb= と viewport= は異なる挙動を示します。

  • BB-a-03-XeLaTeX-TL2015-bb.pdf と BB-a-03-XeLaTeX-TL2015-viewport.pdf より

    f:id:acetaminophen:20150816184343p:plain

viewport= のほうが pdfLaTeX / LuaLaTeX の viewport= に一致(ただし xdvipdfmx が認識する Box の外側は描画をサボる)しているのに対し、bb= のほうは何が起きたのかわかりません*3… そもそもマチガッタ BoundingBox 値を指定しているわけなので、何が起きても文句は言えないという良い証拠です。とりあえず bb= で上書き可能であることはわかります。ちなみに clip オプションがサポートされたのは比較的最近 (2013/02/04; r29154) で、それ以前 (xetex.def v0.94) では以下のエラーを返します。

Package xetex.def Warning: No clipping support in XeTeX yet on input line 26.

4. dvipdfmx で bb= 指定

さて、最もヤヤコシイ dvipdfmx の出番です。こちらも bb= と viewport= は異なる挙動を示します。

  • BB-a-04-dvipdfmx-TL2015-bb.pdf と BB-a-04-dvipdfmx-TL2015-viewport.pdf より

    f:id:acetaminophen:20150816184424p:plain

この結果は dvipdfmx.def v4.04 (2015/03/26) でも dvipdfmx.def v3.0i (1999/02/16) で確認できます。とりあえず bb= で extractbb の実行結果(パイプ入力か .xbb があるかにかかわらず)を上書き可能であることがわかりました。つまり bb= が最強です。なお、パイプ入力と .xbb のどちらが優先されるかというとこれは簡単で、後方互換性を保証するために「.xbb が存在すればそもそも extractbb を実行しない」ことになっています。

 

ややこしい挙動がわかってきたところで…

ここまでは正常な PDF について扱ってきましたが、各エンジンの挙動を把握するのは至難の業でした。次回はいままでわかった知見をまとめつつ、さらに検証を進めていくことにします。

ちなみに、今日のサブブログでは「pagebox オプションが使えない」についてもう少し詳しく見ていくことにします。

*1:自動実行を許可とは「texmf.cnf の shell_escape_commands という値に extractbb が書き込まれている」ということです。TeX Live や W32TeX で普通に LaTeX プログラムを実行したとき、restricted \write18 enabled. というメッセージがログに出てくるのを見たことがあるかもしれません。これは「LaTeX 処理中に別のプログラムの起動を限定許可しています」という意味で、その許可対象がこの shell_escape_commands というリストにあたります。そこに書かれていないプログラムを LaTeX が呼ぶためには -shell-escape オプションを付けて LaTeX を起動する必要があり、そこに書かれているプログラムすら実行されたくない場合には -no-shell-escape オプションを付けて LaTeX を起動します。

*2:この変更は extractbb による .xbb 生成からパイプ入力への変更を意味し、したがって .xbb ファイルでフォルダを汚さなくなったのです。これが TeX Live 2015 での extractbb 自動実行許可につながりました。

*3:深読みすれば「XeLaTeX が bb= で指定した領域を確保し、実際に描画するのは xdvipdfmx にとっての正しい Box の内側」となっているようにも見えますが。