pTeX で \showbox すると和文間空白がアレ
TeX で複雑な組版を行うマクロの動作確認をする時に欠かせないのが \showbox
であるが、和文が絡む場合は注意すべき点がある。それについての話。
確認:\showbox の使い方
本題に入る前に、まず \showbox
の使い方を復習しよう。機能の説明は非常に単純で、\showbox<整数>
を実行すると、\box<整数>
の中身を表すダンプ文字列がログに出力される、というものである。例えば、次の LaTeX 文書をコンパイルしてみる。
\documentclass[a4paper]{article} \showboxdepth=100 \showboxbreadth=100 \begin{document} \setbox0\hbox{To\nobreak\ ?} \showbox0 \end{document}
すると、画面に次のように表示されて停止する。
! OK (see the transcript file). l.6 \showbox0 ?
エラー発生のときの表示と似ている((プロンプトの種類としては同じ。要するに \show
や \showthe
と同じ。))が、これはエラーではないので Enter を空打ちして先に進む。すると処理が終了し、ログファイル(*.log)中に次のような記述が残される。(#
以降の日本語は説明のために私が追記したもの。)
> \box0= \hbox(6.94444+0.0)x19.44446 # (高さ+深さ)x幅 を表す .\OT1/cmr/m/n/10 T # 文字〈T〉: \OT1... はfontdefトークン .\kern-0.83334 # 自動挿入のカーン .\OT1/cmr/m/n/10 o # 文字〈o〉 .\penalty 10000 # ペナルティ(←\nobreak) .\glue 3.33333 plus 1.66666 minus 1.11111 # グルー←(\ ) .\OT1/cmr/m/n/10 ? # 文字〈?〉 # なお先頭の「.」は箱のネストレベルを表す ! OK. l.6 \showbox0 ?
\showbox
の使用についていくつか注意をしておく。
- 整数パラメタ
\showboxdepth
はダンプ対象とするネストレベルの最大値を表す。 - 整数パラメタ
\showboxbreadth
は 1 つの(ネストした)箱についての表示対象の最大個数を表す。 - LaTeX の既定ではこの 2 つのパラメタは 0 でありこの状態では
\showbox
しても何も出ない。適切な値を設定する必要がある。 - 整数パラメタ
\tracingonline
の値が 1 以上の場合は(ログだけでなく)画面にもダンプ文字列が出力される。 - 画面に出力しない場合でも停止する。停止しないようにするには前もって scroll mode に切り替え(
\scrollmode
を実行)ておけばよいだろう。((scroll mode では\show<
でもエラーでも停止しない。\errorstopmode
で元のモードに戻る。))
また、(\showbox
とは別に)TeX にはページ出力をダンプする機能がある。\tracingoutput
の値を 1 以上に設定すると、ページ出力の度にその中身のダンプが \showbox
と同様の方式で行われる。((先に説明した \showbox
用のパラメタはページダンプの時にも適用される。))
和文を含む場合の出力
和文の含む場合でも \showbox
の使い方自体は変わらない。次のような例で試してみる。
\documentclass[a4paper]{jsarticle} \showboxdepth=100 \showboxbreadth=100 \begin{document} \setbox0\hbox{(C言語)} \showbox0 \end{document}
結果は次のようになる。*1
> \box0= \hbox(7.4726+1.33438)x37.46283, yoko direction .\JY1/mc/m/n/10 ( # 文字〈(〉 .\penalty 10000(for kinsoku) # 禁則ペナルティ .\OT1/cmr/m/n/10 C # 文字〈C〉 .\glue(\xkanjiskip) 2.5 plus 1.49994 minus 0.59998 # 和欧文間空白(\xkanjiskip) .\JY1/mc/m/n/10 言 # 文字〈言〉 .\JY1/mc/m/n/10 語 # 文字〈語〉 .\penalty 10000(for kinsoku) # 禁則ペナルティ .\glue(\kanjiskip) 0.0 plus 0.92473 minus 0.0924 # 和文間空白(\kanjiskip) .\JY1/mc/m/n/10 ) # 文字〈)〉 .\glue(refer from jfm) 0.0 # 無意味なグルー ! OK. l.6 \showbox0 ?
このダンプ結果を見ると、大体は入るべきものが入っているように見えるが、実は 1 つ足りないものがある。〈言〉と〈語〉の間に和文間空白(\kanjiskip
)が入るべきなのに入っていない。一方、〈語〉と〈)〉の間の \kanjiskip
は入っている。
\kanjiskip
が入らないはずがないので、入っていることを確かめるために、\kanjiskip
の自然長をゼロ以外にしてみる。
\documentclass[a4paper]{jsarticle} \showboxdepth=100 \showboxbreadth=100 \begin{document} \advance\kanjiskip20pt % 自然長を 20pt に変更した \setbox0\hbox{(C言語)} \showbox0 \box0 % 実際に箱の中身を出力させる \end{document}
> \box0= \hbox(7.4726+1.33438)x77.46283, yoko direction .\JY1/mc/m/n/10 ( .\penalty 10000(for kinsoku) .\OT1/cmr/m/n/10 C .\glue(\xkanjiskip) 2.5 plus 1.49994 minus 0.59998 .\JY1/mc/m/n/10 言 .\JY1/mc/m/n/10 語 .\penalty 10000(for kinsoku) .\glue(\kanjiskip) 20.0 plus 0.92473 minus 0.0924 .\JY1/mc/m/n/10 ) .\glue(refer from jfm) 0.0
ダンプ出力の中の箱の幅の値を見ると、40pt 増えている(37.46283 → 77.46283)。つまり \kanjiskip
は箱の中に 2 つある訳であり、出力を見る限り、それが〈言〉と〈語〉の間にあることは明らかである。
出力されない和文間グル―
実は、pTeX における \showbox
の出力には次のような「癖」があり、前述の奇妙な結果もそれによるものである。
箱の中で隣接する 2 つの和文文字(のノード*2)の間には必ず\kanjiskip
が存在するがそれは\showbox
によるダンプには表示されない。
こんな仕様になっている理由は、そもそも pTeX がメモリ使用効率を上げるために、「隣接する 2 つの和文文字ノードの間に必然的に入る \kanjiskip
のグルーのノードは(メモリ上の「箱」のデータの中に)置かず、しかし常に存在するものとして扱う」*3という処理を行っているからである。従って、pTeX の内部処理では\kanjiskip
の扱いは少し特殊になっている。例えば、〈)〉の前に禁則ペナルティを入れる処理では、論理的には「ペナルティのノードが入るだけ」であるが、そうすると、〈語〉と〈)〉のノードが隣接でなくなり暗黙のルールで「あることになっていた」\kanjiskip
が消えてしまうので、それを補うために「\kanjiskip
のノード」も一緒に挿入する、という処理をとっている。((なお、〈語〉と〈)〉の間は完全な分割禁止なので、ここには伸縮する和文間空白は入れるべきでない(行調整のため \kanjiskip
が大きく伸びてしまう場合でも、この位置には空きが入って欲しくない)という意見もあるだろう。しかし jis メトリックではこの位置の \kanjiskip
を入れる規則になっている。(無論調節は可能である。)))
またこの処理方式から考えると、和文文字ノード同士が間に(論理的に)何も挟まずに隣接することはないことが解る。例えば、次のようにして〈、〉の直後のメトリックグルーを消去する。
\hbox{ぐ、\inhibitglue はぁ}
この場合、内部動作としてメトリックグルーの挿入を単純に抑制している。ところが、そうすると和文文字が隣接することになり、\kanjiskip
があると見做されるはずである。実際に pTeX の仕様はそのようになっている。参考として、この箱を \showbox
した結果は以下の通り。
> \box0= \hbox(7.4726+1.33438)x32.36404, yoko direction .\JY1/mc/m/n/10 ぐ .\penalty 10000(for kinsoku) .\glue(\kanjiskip) 0.0 plus 0.92473 minus 0.0924 .\JY1/mc/m/n/10 、 .\JY1/mc/m/n/10 は .\penalty 150(for kinsoku) .\glue(\kanjiskip) 0.0 plus 0.92473 minus 0.0924 .\JY1/mc/m/n/10 ぁ
〈、〉と〈は〉の間に「実質的に何もない」状態にするには、\inhibitglue
に加えて、\hskip0pt
等を入れる必要がある。これまでに述べたことを踏まえると、この一見複雑な仕様が内部実装としては実は単純であることが解るだろう。
*1:前の記事で、「hbox の内側の両端にメトリックグルーが入ることはない」と書いたが、この結果を見るとダミーのグルーがあることが判る。ただしその幅も伸縮もゼロなので実際には無いのと同じである。
*2:ノードとは TeX の内部で文字やグルーやペナルティなどの「箱の中にあるもの」を表すオブジェクトのことである。「隣接する」というのは、間には「ソースにはないが自動挿入されるノード」(禁則ペナルティ等)も存在しないということに注意。
*3:約物意外の和文文字同士の間には必ず和文間空白があるという規則だから、普通の日本語文書においては、和文間空白のノードの個数は和文文字の個数に匹敵することになり、(単純計算すると)メモリの半分が和文間空白で埋まってしまう。