ボックス

TeX でもっとも根本的な考えは何か、と問われたら、答えはフォントでも数式でも行分割アルゴリズムでもなく ボックス(箱) です。LaTeX がページに置くものはすべて箱で、幅・高さ・深さという三つの寸法を持ち、糊(グルー)で隣とつながれ、いったん組み上がったら中身は二度と覗かれません。\mbox が語のハイフネーションを止めるのも、はみ出した行が Overfull \hbox と報告されるのも、\parboxminipage の振る舞いが分かれるのも、すべてこの一点から来ています。このページでは実際に打ち込む箱の命令——\mbox\makebox\framebox\raisebox\parboxminipage\newsavebox——を、その一つの発想からたどります。

幅・高さ・深さ——箱に寸法が三つある理由

箱の寸法は二つではなく三つです。高さ深さ。高さはベースラインから上へ、深さはベースラインから下へ測ります。つまり箱はインクを囲む長方形ではなく、文字が乗っている線に吊るされた長方形 です。値は \the\wd\the\ht\the\dp で読み出せます。10pt の Computer Modern で Hx を箱にすると深さは 0.0pt ですが、Hxg にすると深さは 1.94444pt になります。g がベースラインより下へ伸びるからです。もちろん幅も増えていますが、深さが増えた理由はまったく別のところにあります。

この一点が、箱を知る前に誰もがぶつかる謎の答えになります。なぜ \fbox{Hx} の枠は文字にぴったりなのに、\fbox{Hxg} の枠は下にずれて見えるのか。 \fbox は文字ではなく箱を囲むからで、Hxg の箱は 1.94pt だけ深いのです。枠付きの語を表の一行に並べれば、枠は下降部(ディセンダ)のあるなしに合わせて上下に踊ります。直し方は 支柱(strut) を入れること。\fbox{\strut Hx}\fbox{\strut Hxg} は高さも深さも完全に一致します。\strut は幅ゼロの見えない箱で、現在の \baselineskip の 0.7 倍をベースラインの上に、0.3 倍を下に主張するからです。支柱についてはこのページの後半で詳しく扱います。

latex
\newsavebox{\probe}
\newcommand{\dims}[1]{\sbox{\probe}{#1}%
  \typeout{[#1] w=\the\wd\probe\space h=\the\ht\probe\space d=\the\dp\probe}}

\dims{Hx}    % w=12.77782pt  h=6.83331pt  d=0.0pt      <- no descender
\dims{Hxg}   % w=17.77783pt  h=6.83331pt  d=1.94444pt  <- the g adds depth

% same words framed: only the second frame sinks below the baseline
\fbox{Hx}\quad\fbox{Hxg}\quad \fbox{\strut Hx}\quad\fbox{\strut Hxg}

\mbox で改行とハイフネーションを止める

\mbox{...} は中身を 1 行に留め、その内部でハイフネーションを無効にします。理由は定義そのものにあります。LaTeX の \mbox\leavevmode\hbox{#1} にすぎません。\hboxすでに組み上がった箱 で、TeX は完成した箱の中へ戻って改行位置やハイフン位置を探すことをしません。だから \mbox{antidisestablishmentarianism} は「折らないでほしい」とお願いされているのではなく、折ることができない のです。中身を左から右へ流すだけで決して折り返さないこの種の箱を、LaTeX の用語では LR ボックス(内部は LR モード)と呼びます。\mbox\makebox\fbox\framebox がその仲間で、幅を決めて折り返す \parboxminipage——段落ボックス——とは対になります。組版とは箱を糊でつなぐ操作だ、という TeX の世界観がそのまま一つの命令になっています。

代償は、折れない箱が行に収まらないことがある点です。そのとき TeX が出すのが Overfull \hbox (29.62221pt too wide) in paragraph at lines 5--5 という警告で、この文面は字義どおりに読む価値があります。「行」ではなく \hbox と書いてある のは、TeX が作る行そのものが hbox だからです。段落分割器は語と伸縮する糊を 1 行につき 1 個の hbox に詰めます——ドナルド・クヌースとマイケル・プラスが 1981 年に『Breaking paragraphs into lines』として発表したモデルです。文書クラスに draft オプションを付けると LaTeX は \overfullrule を 5pt に設定し、はみ出した行の余白に黒い棒を印字します。その棒自体が塗りつぶされた箱です。\mbox は本当に切ってはいけないもの——製品名、電話番号、数値と単位——に使い、下手な改行の万能薬にはしないでください。

latex
% never split, never hyphenated
Released for \mbox{Mac OS X} and \mbox{Windows 11}; call \mbox{+81 3-1234-5678}.

% too wide for the measure -> Overfull \hbox (...pt too wide) in the log
\parbox{3.4cm}{aaa \mbox{antidisestablishmentarianism}}

% \documentclass[draft]{article} sets \overfullrule=5pt and marks the line

\makebox で幅と揃えを決める——幅ゼロの箱、\rlap\llap

\makebox[幅][位置]{中身} は、幅の決定権を TeX から取り上げて自分の手に移した \mbox です。[位置] は、要求した幅のなかで中身をどこに置くかを指定します。l は左寄せ、r は右寄せ、c は中央(既定)、そして s は語間の糊を伸ばして中身を幅いっぱいに広げます——\makebox[3cm][s]{a b c d} は本当にちょうど 3cm の箱になります。それ以外の文字も受け付けられますが、LaTeX が Unexpected alignment という警告を出して c に戻します。

幅の引数のなかでは、四つの名前が「いま箱に詰めようとしているものの自然な寸法」を指します。\width\height\depth、そして \totalheight(高さ+深さ)です。これらは幅が評価される前に中身から設定されるので、\makebox[2\width]{word}word を 21.4167pt と測ってから 42.8334pt の箱を作ります。「これの 2 倍の幅」を、これ が何になるか決めないまま書けるわけです。同じ四つは \framebox\savebox\raisebox の引数でも使えます。

そして、もっとも役に立つ幅は 0pt です。幅ゼロの箱は組版されるのに現在位置を進めないので、後に続くものの上に重なって印字されます。\makebox[0pt][l]{...} は現在位置の右へ、\makebox[0pt][r]{...} は左へ中身を垂らし、[c] は左右にまたがります。この二つには短い名前があり——\rlap\llap——LaTeX はまさにそのとおり \hbox to 0pt{#1\hss}\hbox to 0pt{\hss#1} として定義しています。重ね打ち、ぶら下げ、余白への印、そして自作の打ち消し線マクロの大半は、すべてこの一つの手品です。

latex
\makebox[3cm][l]{left}|\makebox[3cm][r]{right}|
\makebox[3cm][s]{a b c d}|   % interword glue stretched to exactly 3cm
\makebox[2\width]{word}|      % twice the natural width of "word"

% zero width: the slash overprints the space that follows the X
X\rlap{/}\quad Y              % identical to X\makebox[0pt][l]{/}\quad Y

\fbox\framebox の違い、\fboxsep\fboxrule

\fbox{中身}\framebox[幅][位置]{中身} の違いは、\mbox\makebox の違いとまったく同じです。\fbox は中身を自然な幅のまま囲み、\framebox は指定した幅のなかに囲みます。位置指定も lcrs で共通です。どちらも箱の四辺に罫線を引き、見た目は二つの長さで決まります。罫線の太さ \fboxrule0.4pt、罫線と中身のあいだの余白 \fboxsep3pt。算数で確かめられます。Hx の幅は 12.77782pt、\fbox{Hx} の幅は 19.5778pt——差はちょうど 3.4pt の 2 倍です。\setlength{\fboxsep}{0pt} とすれば枠は文字に密着します。

\fbox は LR ボックスなので、囲めるのは 1 行分です。段落を囲みたければ 段落ボックスを囲みます——\fbox{\parbox{6cm}{...}}。外側の箱は中身が何であるかを気にせず、どれだけの大きさかだけを見ます。この組み合わせ可能性こそが箱モデルの要点で、同時に、\parbox を囲んだ枠が前節の下降部問題をそのまま受け継ぐ理由でもあります。枠付きの箱を縦に並べるなら、中身の先頭と末尾に \strut を置いておくと高さがそろいます。角丸・背景色・ページをまたぐ枠が必要になったら framedtcolorbox といった専用パッケージの領分で、そちらは「枠で囲む」のページで扱います。

latex
\setlength{\fboxrule}{1pt}   % default 0.4pt
\setlength{\fboxsep}{6pt}    % default 3pt
\fbox{one line}\quad\framebox[4cm][r]{4cm wide, flush right}

% frame a whole paragraph: put a paragraph box inside the frame
\fbox{\parbox{6cm}{\strut A framed paragraph. The frame follows the box,
and the struts keep its height independent of ascenders and descenders.\strut}}

\parboxminipage の違い

どちらも幅を固定して中身を折り返します。違いは、よく言われるような「\parbox は段落を一つしか入れられない」ことでは ありません。いくつでも入りますし、リストも入ります——\parbox{4cm}{First.\par Second.} はちゃんと二段落を出力します。本当の違いは、minipage環境\parbox命令 だという点にあり、そこから「中に何を書いてよいか」が決まります。書式は揃っていて、\parbox[位置][高さ][内部位置]{幅}{中身}\begin{minipage}[位置][高さ][内部位置]{幅}。どちらも \parindent\parskip を 0 にリセットし、\linewidth を箱の幅に設定します。だから中で \includegraphics[width=\linewidth]{...} と書けば期待どおりに動きます。

[位置] は箱が周囲の行とどう出会うかを決める引数で、いちばん誤解されています。[t]箱の上端をそろえるのではありません。箱の 最初の行 のベースラインを、周囲のベースラインに乗せます。3 行の \parbox[t] を測ると高さは 4.30554pt——小文字の o の高さであって、箱の高さではありません——で、深さは 24pt、つまりベースラインの下に 2 行分がぶら下がっています。[b] は最終行で同じことをし、既定の [c] は箱を行の中央に合わせます。だから [t] を付けた二つの minipage は「最初のベースライン」でそろい、片方が画像や大きな文字で始まっていれば上端は依然としてずれます。直すには、それぞれの先頭に \strut を置くか、片方を \raisebox でずらします。四引数の形 \parbox[位置][高さ][内部位置]{幅}{中身} を使えば箱の高さも強制でき、[内部位置] は中身をその高さのなかで上(t)・中央(c)・下(b)に置くか、s で上下いっぱいに伸ばすかを選びます。

両者が本当に袂を分かつのは、中に何を入れられるか です。\parbox の本文はマクロの引数として、カテゴリコードが確定した状態で読まれます。だから \parbox{5cm}{\begin{verbatim}...\end{verbatim}}! Argument of \@xverbatim has an extra }. で即座に失敗します——同じ verbatimminipage の中では通ります。脚注はエラーより厄介です。\parbox の中の \footnote記号だけ印字して、脚注の本文をそっくり失います。挿入物が箱に閉じ込められ、ページまで届かないからです。minipage はこれを意図的に処理し、専用の mpfootnote カウンタによる イタリックの小文字 を記号として、脚注を minipage 自身の直下に置きます。フロートはどちらでも禁止で、minipage の中に figure を書くと ! LaTeX Error: Not in outer par mode. で止まります。そしてどちらもページをまたげません。箱は定義上、丸ごと置かれるものだからです。

入れるもの\parboxminipage
\par複数段落は入る複数段落は入る
itemize入る入る(深さもリセットされる)
verbatim! Argument of \@xverbatim has an extra }.入る
\footnote記号は出るが本文が消える箱の直下にイタリックの小文字で出る
figure使えない! LaTeX Error: Not in outer par mode.
\parindent0 にリセットされる0 にリセットされる

minipage のもっとも多い用途は横並びの二段組みです。\textwidth の 0.48 倍ほどの minipage を二つ並べ、あいだに \hfill を入れます。合計を 1 未満にしておかないと二つ目が入りません。そして誰もが一度は半日を溶かす落とし穴が一つ。\end{minipage}\begin{minipage} のあいだにあるソースの 改行がふつうの空白になる ので、打っていない語間空きの分だけ二つの箱が押し広げられます。行末を % で閉じればその空白は消えます。列の高さがそろわない、フロートとして回り込ませたい、といった話になったら「フロートと配置」のページへ進んでください。

latex
\noindent
\begin{minipage}[t]{0.48\textwidth}
  \strut Left column. Several paragraphs, lists and a local footnote
  are all fine here.\footnote{Printed under this box, marked a, b, \dots}
\end{minipage}\hfill%   <- the % kills the newline; without it the boxes drift apart
\begin{minipage}[t]{0.48\textwidth}
  \strut Right column. [t] aligns the FIRST BASELINES, not the tops --
  the struts are what make the tops line up as well.
\end{minipage}

\raisebox で上下にずらす——寸法を偽って申告する引数

\raisebox{移動量}{中身} は中身を指定量だけ持ち上げ、値が負なら下げます。大きさの違う二つの素材をベースラインでそろえる定番の道具です。面白いのは二つの省略可能引数のほうで、\raisebox{移動量}[高さ][深さ]{中身} は、実際に何が描かれるかとは無関係に、その高さ・深さだと周囲に申告させます\raisebox{0pt}[0pt][0pt]{\Huge Ag} は幅 28.66008pt をきちんと占めるのに、高さ 0pt・深さ 0pt と測れます(本当は高さ 16.99782pt・深さ 4.83937pt です)。だから行送りは、そこに何も無いかのように計算されます。飾り文字や記号を本文に落とし込んでも行間が空かないのはこの仕組みで、\width\height\depth\totalheight の四つもここで使えます。ただし申告と実物が食い違えば当然重なりますから、意図的な装飾以外で高さを偽るのは避けてください。

\rule と支柱(strut)——見えない箱で行の高さを稼ぐ

\rule[持ち上げ]{幅}{高さ} は塗りつぶした黒い長方形を描きます。箱そのものの形でページにインクを置く、ほぼ唯一の LaTeX 命令です。\rule{0.5\linewidth}{0.4pt} は行幅の半分の細い横線になります。省略可能な [持ち上げ] は、長方形の大きさを保ったままベースラインに対する位置をずらします。\rule[-2pt]{10pt}{8pt} は幅 10pt・高さ 6pt・深さ 2pt と測れ、つまり 8pt の長方形のうち 2pt がベースラインより下にぶら下がります。

幅を 0 にすると、罫線は印字をやめて場所の確保だけを続けます。これが 支柱(strut) です。\rule{0pt}{2.6ex} は目に見えず、幅 0pt、高さ 11.19443pt。これを表の行頭に置けば、その行は必ずその高さ以上になります。LaTeX 自身の \strut は現在の行送りに合わせた既製品で、\baselineskip が 12pt のとき高さ 8.39996pt・深さ 3.60004pt——行の 0.7 と 0.3 の分割です。\fbox{\strut Hx}\fbox{\strut Hxg} が完全に一致したのはこのためで、支柱がどちらの語よりも高く深いので、下降部が箱の寸法を決められなくなるのです。文字が \hline に張りつくときの定番の直し方であり、箱を並べたときに縦のリズムをそろえる道具でもあります。

latex
% a centred horizontal rule
\noindent\hfil\rule{0.5\linewidth}{0.4pt}\hfil

% a strut forces a taller, roomier table row
% (\strut is the ready-made one, sized to the current \baselineskip)
\begin{tabular}{|l|}
  \hline
  \rule{0pt}{2.6ex}Tall, uncramped row \\
  \hline
  \strut Same idea, sized to the current leading \\
  \hline
\end{tabular}

% put two differently sized boxes on one baseline
big \raisebox{-0.4ex}{\Huge A} small

\newsavebox\sbox\usebox で箱を保存して使い回す

二度以上出てくる素材は、一度だけ組んで再生できます。\newsavebox{\名前} で箱レジスタを一つ確保し、\sbox{\名前}{中身}——\makebox 式の書式が欲しければ \savebox{\名前}[幅][位置]{中身}——で中身を詰め、\usebox{\名前} で取り出します。中身が実際に組まれるのは \sbox の瞬間だけで、それ以降は一度もありません。長い表や込み入った図を三か所で使うなら、組版の手間は一回分で済み、三つの複製は寸分たがわず一致します。

箱レジスタは有限の資源で、二十年ほどのあいだは 希少な 資源でした。オリジナルの TeX が持っていた箱レジスタはちょうど 256 個、番号 0 から 255 まで。使いすぎた文書は ! No room for a new \box で止まりました。eTeX が上限を 32768 に、LuaTeX が 65536 に引き上げたので、いまではまず当たりません——LaTeX はフォーマット構築時にエンジンを見て上限を決めており、内部の \e@alloc@top はそれぞれ 255/32767/65535 になります。インターフェイスについて注意が二つ。保存箱は LR ボックスなので、複数行を入れるならまず \parboxminipage でくるみます。そして verbatim を含む中身を保存するときは、環境形式の \begin{lrbox}{\名前} … \end{lrbox} を使います。なお古い解説には「\savebox は脆弱(fragile)」とありますが、現在の LaTeX では違います\mbox\makebox\fbox\parbox\raisebox\sbox\savebox はいずれも堅牢(robust)に宣言されています。

\usebox の相棒が \settowidth{\長さ}{中身}\settoheight\settodepth です。中身を目に見えない形で組み、その結果の寸法を一つだけ長さ変数に格納します。「別の何かとぴったり同じ大きさのものを描く」ための道具で、署名欄の幅を測ってから \rule で下線を引く、といった使い方をします。

latex
\newsavebox{\mylogo}
\sbox{\mylogo}{\fbox{\textbf{Draft}}}   % typeset once, here

Header: \usebox{\mylogo} \dots\ Footer: \usebox{\mylogo}

% verbatim inside a save box needs the environment form
\newsavebox{\snippet}
\begin{lrbox}{\snippet}
  \begin{minipage}{6cm}\begin{verbatim}int main(void) { }\end{verbatim}\end{minipage}
\end{lrbox}
\usebox{\snippet}

% measure a box into a length, then rule exactly that wide
\newlength{\siglen}
\settowidth{\siglen}{Signature}
Signature\par\rule{\siglen}{0.4pt}

どの箱の命令をいつ使うか

命令種類これを使うのはこんなとき
\mboxLRこの語句を絶対に折らせたくない
\makeboxLR幅を自分で決めたい/[0pt] で重ね印字したい
\rlapLR\makebox[0pt][l] の短縮形(\llap は左へ)
\fboxLR自然な幅のまま枠で囲みたい
\frameboxLR幅と位置を決めた枠(\fboxsep\fboxrule で調整)
\parbox段落幅を決めて折り返したい。表のセルの中など
minipage段落中に verbatim・脚注・リストを入れたい/横並びの段
\raisebox変形上下にずらす/高さ・深さを 0 と申告する
\rule塗り箱線を引く。幅 0 なら見えない支柱になる
\strut支柱行や表のセルの高さをそろえたい
\usebox保存\newsavebox\sbox で組んだ箱を取り出す
\settowidth計測中身の幅(\settoheight\settodepth も)を長さに入れる