TeX における段落/ボックスの内部表現

TeX の内部では,水平ボックス (hbox),垂直ボックス (vbox),(行分割前の)段落などは,ノードの線形リストの形で 表現されている.このページでは,これらのノードについて簡単に説明する.

ノードの種類

実際にボックスの中身がどのようなリストで表現されているかは,下節の方法によって調べることができる. TeX において,リストを構成する代表的なノードは次のようなものである(以下 p をノードとする). tex.web の Part 10: Data structures for boxes and their friends を参照.

  • char_node◎: 文字を表す.
    • 欧文文字の場合は単一の char_node が1文字を表す.
    • 和文文字の場合は,連続する2つの char_node が1文字を表す.
    • 但し,下節における \showbox0 等による出力では,和文文字も1行(つまり,1ノード扱いのように)で表示されている.
  • hlist_node: 水平ボックス (hbox) を表す.list_ptr(p) でそのボックスの中身を表現しているリスト(の先頭ノード)が得られる.
  • vlist_node: 同様に,垂直ボックス (vbox) を表す.
  • dir_node★: (hlist_node, vlist_node と内部では同じ構造.(リストでの組方向と)異なる組方向のボックスに対してのコンテナの役割を果たす.
  • rule_node: 黒い四角を表す.
  • ins_node: 脚注や float の挿入に使われる,insertion の処理のために使われる node である.
  • disp_node:★ 欧文文字のベースライン補正の情報を格納するノードである.このノード以降のノードは,みな disp_dimen(p) だけ下に下げられる.
  • mark_node: \markに関する node.
  • adjust_node: \vadjust(現在の行の直後に追加される物を指定できる)処理に使われる.
    • pdfTeX, XeTeX, LuaTeX では,\vadjust preを用いることで,現在の行の直前に追加される物を指定できるが,その処理にも この node が使われる.
  • ligature_node◎: 合字を表す.char_node に似ているが,lig_ptr(p) で,合字を生成した元となる文字たちのリスト(の先頭ノード)が得られる.
  • disc_node: discretionary break を表す.pの位置で行分割が行われた場合,
    • p に続く replace_couint(p) 個の node は無視される.
    • リスト pre_break(p) の内容が行分割の直前に入れられる.
    • リスト post_break(p) の内容が行分割の直後に入れられる.
  • whatsit_node: \write命令,\special命令など,「TeX の拡張」のために設けられた node の種類である. pdfTeX の各種命令もこれらを利用するようである.
  • math_node: 数式と非数式の境界を表す.
    • e-TeX, pdfTeX では,TeX--XeT 機能による右横書きの実装において,同一リスト内での「組方向の切り替え境界」用にも用いられる.
    • LuaTeX では TeX--XeT は削除されたので,数式境界という本来の機能しかない.
  • glue_node: グルー(伸縮可能な詰め物)を表す.厳密には(文章中の)グルー,数式中のグルー,leader など細かく種類がわかれる.
  • kern_node: カーンを表す.種類 (subtype) として,次の3つがある.
    • TFM 由来
    • 明示的な\kernによるもの,またはイタリック補正(\/)由来
    • アクセントについての位置補正用 pTeX では,明示的な\kernとイタリック補正とで subtype が分けられ,計4種類となっている.
  • penalty_node: ペナルティを表す.ここで行分割/ページ分割が起こった時は,この penalty_node が保持している整数値が分割にかかる罰金となる.
    • 絶対値が 10000 以上の値は無限大と扱われる.
  • unset_node: hlist_node, vlist_node と同じ構造.\halign, \valignによる表組みの処理途中に出現する node である.
  • margin_kern_node☆: pdfTeX 以降で用いられる level2 character protruding(若干余白に文字を突き出させることによって,見栄えを改善する) に用いられると推測(書きかけ).

凡例

  • ★: e-upTeX にのみ存在する node.
  • ☆: pdfTeX, LuaTeX にのみ存在する node.(XeTeX はまだ調べてない)
  • ◎: LuaTeX では,char_node, ligature_node は glyph_node という種別に統合されている.

ボックスの中身を調べる方法

本節は フォーラム 57202番の ZR さんによる投稿の転載です.

pTeX の「自動挿入」の振舞を理解するには、(ソースを読むと いう方法を採らないなら)実験して調べる必要がありますが、 「自分で実験して調べる方法」について説明しておきます。 TeX の文法に関しては、「パラメタの代入」(\kanjiskip=1pt等)さえ知っていれば十分です。

\documentclass{jsarticle}
% min10 よりも jis の方が「予想外のことが起こり難い」ので
% jsarticle にします。
\showboxdepth=100 % ボックスネスト 100 段まで表示
\showboxbreadth=100 % 各ボックスレベルで 100 ノードまで表示
%\tracingonline=1 % 中身を端末にも表示したい場合
%\nonstopmode % \showbox (やエラー)で止まらなくなります。
\tracingoutput=1
% 実際に出力されるページの中身を出力します。
\begin{document}

% \showbox で \box0 の中身が(ログに)ダンプ出力されます。
% \tracingonline=1 ならば端末にも出力されます。
\setbox0=\hbox{\textgt{漢字}.}
\showbox0

% あるいは LaTeX とのハイブリッドな記法の場合。
\newsavebox{\testbox}
\savebox{\testbox}{\textgt{漢字}.}
\showbox\testbox

\end{document}
これを普通に platex コマンドで処理すると、 (途中「! OK.」のプロンプトはそのまま Entry 空打ち) ログの中では「! OK.」の箇所に「ボックスの中身」が出力されます。 この各々の行がボックスの中にある「ノード」を表しています。
> \box0=
\hbox(7.4726+1.33438)x21.27153, yoko direction
.\JY1/gt/m/n/10 漢
.\JY1/gt/m/n/10 字
.\OT1/cmr/m/n/10 .

! OK.
l.12 \showbox0
また、\tracingoutput=1 とすると、各ページ出力時に、その全体のダンプがログに出力されます。 (上の例ではページは全く出力していない。) ただし、pTeX の場合には一つ注意を要する点があり、それは 「2 つの和文文字(ノード)が連続している場合は、 実際には『暗黙の kanjiskip』があると見なす必要がある」ということです。

ソース中で和文文字が単純に並んでいる箇所(当然和文の文書 なら大量にあります)では、ほとんどの場合、2つの文字ノード の間に kanjiskip のノードだけがある結果になります。 (pTeX 開発当時の非常に限られた)メモリ資源を無駄にしない ために、「この kanjiskipを実際に入れない」(しかし処理上は いつでもあると見なす)という最適化がなされているのです。

つまり、上の結果は実際には次のものだと見なす必要があります。

\hbox(7.4726+1.33438)x21.27153, yoko direction
.\JY1/gt/m/n/10 漢
.\glue(\kanjiskip) 0.0 plus 0.92473 minus 0.0924
.\JY1/gt/m/n/10 字
.\OT1/cmr/m/n/10 .

次のように \kanjiskip にゼロでない自然長を指定すると:

\kanjiskip=500pt
\setbox0=\hbox{\textgt{漢字}.}
\showbox0
暗黙の kanjiskip が 500pt あるのでボックスの幅はそれだけ増えて(521.27153)います:
\hbox(7.4726+1.33438)x521.27153, yoko direction
.\JY1/gt/m/n/10 漢
.\JY1/gt/m/n/10 字
.\OT1/cmr/m/n/10 .

あとは、「限定水平モード(LaTeX の『LR モード』)に 特有の振る舞い」に気をつける必要があるけど、細かい話 なので省略。

付録: pTeX 拡張による変更部分のみのソースを入手する方法

  1. TeX Live and Subversion の方法に従い,TeX Live SVN のソースを取得する. 以下,取得したソースにおいて,tex.web がある場所を $SRC とおく.
  2. 任意の作業用ディレクトリに移動し,以下を実行する:
    tie -m ptex.web $SRC/tex.web $SRC/tex.ch
    tie -c ptex.ch ptex.web $SRC/ptexdir/ptex-base.ch
    weave ptex.web ptex.ch
    
  3. これで ptex.tex が生成されるので,それを pdftex かなにかで処理すると, pTeX の(TeXに対する)変更部分のみのソースが手に入る.
    • ヘッダが「TeX82」となっているが,気になる場合は ptex.tex の l. 83 を変えること.
    • l. 88 を
      \let\maybe=\iftrue
      
      と変えると,変更されていない部分も合わせて(即ち,pTeX 全部の)ソースが手に入る.
    • l. 1 を
      \input pdfwebmac
      
      に変え,pdftex で処理すると,リンクつき pdf ができる.