ラベルのカスタム

LaTeX の itemize が階層ごとに出す四つの行頭ラベル(マーカー)を、たいていの人は「•、-、、·」だと覚えています。ところが article.cls を開くと、後ろの三つはどれも予想と違います。第 2 階層はハイフン二つではなく 太字の en ダッシュ\labelitemfont \bfseries \textendash)、第 3 階層はふつうのアスタリスクではなく 上下中央に載ったアスタリスク* \textasteriskcentered、第 4 階層が \textperiodcentered です。ラベルをカスタムするというのは、まずこれらのマクロが実際に何であるかを知ることから始まります。このページでは itemize の四つのマーカー、enumerate\theenumi\labelenumi の役割分担、\arabic から \fnsymbol までのカウンタ表現コマンドとその限界、そして入れ子の本当の上限(! LaTeX Error: Too deeply nested. が出るのはいつか)を順に見ていきます。

itemize の四つの既定マーカーは、実際には何か

itemize の行頭記号は、階層ごとに \labelitemi\labelitemii\labelitemiii\labelitemiv という四つのマクロに入っています(語尾の i・ii・iii・iv はローマ数字で第 1〜第 4 階層を表します)。標準クラスでの定義は下の表のとおりで、四つとも先頭に \labelitemfont が付きます。これは article.cls\normalfont と定義されている小さなフックで、周囲がイタリックでも太字でもマーカーだけは本文の書体で出る、という保証を与えています。第 2 階層だけは \labelitemfont のあとにさらに \bfseries が続くので、en ダッシュが太く組まれます——「なんとなく他より濃く見える」のはそのためです。

マクロ標準クラスでの定義実際に出る字
\labelitemi\labelitemfont \textbullet• 黒丸
\labelitemii\labelitemfont \bfseries \textendash– 太字の en ダッシュ(ハイフン二つではない)
\labelitemiii\labelitemfont \textasteriskcentered∗ 上下中央に載ったアスタリスク(* とは別字)
\labelitemiv\labelitemfont \textperiodcentered· 中点

階層全体 の記号を変えるには、対応するマクロを \renewcommand で再定義します。プリアンブル、あるいは効かせたい範囲の直前に置いてください。ここで注意したいのは、これらのマクロが数式モードに入らないことです。$\diamond$ のように自分でドルを書かないと、\diamond は本文で使えずエラーになります。よく使う代替記号は $\diamond$(◇)、$\ast$(∗)、$\cdot$(·)、$\triangleright$(▷)あたりで、pifont パッケージを読み込めば \ding{"6C} のような装飾記号も使えます。ただし記号を凝りすぎると階層の区別がつきにくくなるので、変えるのはたいてい第 1 階層だけにしておくのが無難です。

preamble
% the whole of level 1 becomes a diamond -- note the explicit math shift
\renewcommand{\labelitemi}{$\diamond$}

% or, with enumitem, the same thing scoped to one list
\begin{itemize}[label=$\diamond$]
  \item this list only
\end{itemize}

一項目だけラベルを差し替える \item[...]

一項目だけ 印を変えたいときは、\item の直後に角括弧でラベルを与えます。\item[$\star$] ならその項目の行頭が星になり、\item[--] ならダッシュになります。ここで一つ、広く信じられている誤解を正しておきます。角括弧で与えたラベルは太字にはなりません。 itemize の中で \item[Word] と書いた PDF に埋め込まれるフォントは本文用のローマン体だけで、太字は一切現れません。itemizeenumerate\makelabel\hss\llap{...} と定義されているので、ラベルは 箱の中で右寄せ(幅を超えたぶんは左へはみ出す)になるだけで、書体には手を触れないのです。太字になるのは description だけで、それは \descriptionlabel\bfseries を持っているからです。

\theenumi と \labelenumi の違い——数の表現とラベルの体裁

\theenumi数をどう表現するか(1 か I か a か)を、\labelenumiその数を行頭にどう飾って出すか(前後の括弧やピリオド)を決めます。enumerate は階層ごとにカウンタ enumienumiienumiiienumiv を使い、その値を \theenumi が文字に変え、\labelenumi がそれを呼び出して約物を添える、という二段構えです。標準クラスでの既定は下表のとおりで、第 2 階層だけが丸括弧、ほかはピリオドという非対称になっています。

カウンタ\the… の既定ラベルの既定行頭に出る形
enumi\arabic{enumi}\theenumi.1. 2. 3. …
enumii\alph{enumii}(\theenumii)(a) (b) (c) …
enumiii\roman{enumiii}\theenumiii.i. ii. iii. …
enumiv\Alph{enumiv}\theenumiv.A. B. C. …

この二段構えが効いてくるのは相互参照です。\ref が返すのは \labelenumi の飾りを取り除いた素の表現、つまり \theenumi のほうです。だから \labelenumi(\theenumi) に変えて行頭を (1) にしても、\ref は括弧のない 1 を返します——「手順 (1) を見よ」と書きたければ、参照側で括弧を書き足すか、enumitemref= を使う必要があります。正確には LaTeX は \theenumi の前に \p@enumi を付けて参照文字列を組み立てており、標準クラスでは \p@enumii\theenumi\p@enumiii\theenumi(\theenumii) と定義されています。だから第 2 階層の項目を \ref すると「1a」のように親の番号込みで返るわけです。カウンタそのものの作り方や増やし方は、カウンタと長さのページが扱います。

preamble
% change only the representation: the head reads I. II. III. and \ref gives I
\renewcommand{\theenumi}{\Roman{enumi}}

% change only the dressing: the head reads (1) but \ref still gives a bare 1
\renewcommand{\labelenumi}{(\theenumi)}

カウンタ表現コマンドと、その限界

\theenumi\labelenumi の中で数の見た目を選ぶには、LaTeX 標準の六つの カウンタ表現コマンド を使います。いずれもカウンタ名を引数に取り(\Roman{enumi} のように)、その値を指定の体裁で印字します。重要なのは、そのうち三つに 上限がある ことです。\alph\Alph は 26 を超えると、\fnsymbol は 1〜9 の外に出ると、いずれも ! LaTeX Error: Counter too large. を出して止まります。27 項目ある label=\alph* のリストは、27 番目で必ずこのエラーになります。

コマンド出る形範囲と限界
\arabic1, 2, 3 …(アラビア数字)上限なし。負の数もそのまま出る(−3 は −3)
\alpha, b, c …(小文字)1〜26。27 以上は Counter too large。0 は無言で空になる
\AlphA, B, C …(大文字)1〜26。限界は \alph と同じ
\romani, ii, iii …(小文字ローマ数字)エラーは出ないが、0 以下は無言で空になる
\RomanI, II, III …(大文字ローマ数字)上限なし。4000 は MMMM と、M を並べて出る
\fnsymbol* † ‡ § ¶ ‖ ** †† ‡‡(脚注記号)1〜9 のみ。10 以上・0 以下は Counter too large

厄介なのは、エラーになる場合とならない場合が混在している ことです。\alph{0}\roman{0} は文句も言わずに空文字列を返すので、カウンタを 0 のまま参照してしまうと「ラベルが消えたリスト」ができあがり、原因の見当がつきません。一方 \alph{27}Counter too large で止まってくれるので、まだ親切です。\fnsymbol の九つの記号(\@fnsymbol の定義そのまま)は脚注のための並びで、リストのラベルに転用すると 10 項目目で破綻します——短い注記リストにだけ使ってください。

enumitem の label= で書く——カウンタから組み立てるラベル

マクロの再定義は強力ですが、リストごとに体裁を変えたい場面では煩雑です。多くの場合は enumitemlabel= のほうが見通しよく書けるので、まずこちらを試してください。label= の中では \arabic*\alph*\Alph*\roman*\Roman* という アスタリスク付き の形が、その階層の現在のカウンタを表します。星なしの \arabic{...} も併用できるので、両者を混ぜれば 他のカウンタから組み立てたラベル が作れます——たとえば label=\thesection.\arabic* と書けば「3.1」「3.2」のように節番号を先頭に付けた項目番号になり、節を移動しても自動で追随します。

preamble
\usepackage{enumitem}
\setlist[enumerate,1]{label=\Roman*.}    % level 1 becomes I. II. III.
\setlist[enumerate,2]{label=(\alph*)}    % level 2 becomes (a) (b)
\setlist[itemize,1]{label=$\diamond$}    % itemize level 1 becomes a diamond

% a label built from another counter
\begin{enumerate}[label=\thesection.\arabic*, leftmargin=*]
  \item numbered 3.1 inside section 3
\end{enumerate}

% label*= accumulates onto the parent label: 1. then 1.1. 1.2.
\begin{enumerate}[label*=\arabic*.]
  \item one
  \begin{enumerate}[label*=\arabic*.]
    \item one-point-one
  \end{enumerate}
\end{enumerate}

label*= は親のラベルに連結する形で、入れ子にすると 1.、1.1.、1.2. と積み上がります(実際に組んで確認しました)。ただし 連結が効くのは行頭の表示だけで、相互参照には親の番号が自動では付きません。「1.a」のような参照を出したいときは ref=\theenumi.\alph* のように ref= で明示してください。幅の広いラベルを与えるときは、leftmargin=* を添えるのを忘れずに——既定のラベル箱は 20pt しかないので、\thesection.\arabic* のような長いラベルは左へはみ出します。全体に効かせたいときは \setlist[enumerate,1]{...} のように種類と階層を指定します。

なお enumitem\usepackage[shortlabels]{enumitem} と読み込むと、古い enumerate パッケージ風に \begin{enumerate}[(1)] と短く書けます。角括弧の最初の要素として AaIi1 を置くだけで、それぞれ \Alph*\alph*\Roman*\roman*\arabic* を意味します。

Too deeply nested — 入れ子の本当の上限

「LaTeX のリストは 4 段までしか入れ子にできない」とよく言われますが、これは 半分しか正しくありません。上限は二つあり、latex.ltx を読むとその構造が分かります。itemize\@itemdepthenumerate\@enumdepth という 別々のカウンタ を持ち、それぞれ >3\@toodeep を呼びます。つまり 4 段までという制限は 同じ環境を重ねたとき の話です。そのうえで \list 自体が \ifnum \@listdepth >5 という大域の関門を持っており、こちらは種類を問いません。

実際に試すと、そのとおりになります。itemize を 5 段重ねると 5 段目で ! LaTeX Error: Too deeply nested. が出ます。enumerate を 5 段重ねても同じです。ところが itemizeenumerate交互に 重ねると 6 段目までまったくエラーが出ず、7 段目ではじめて同じエラーが出ます——このとき止めているのは \@listdepth のほうです。つまり実用上の上限は「同種なら 4 段、交互なら 6 段」。ただしこれは知識としての話で、6 段の入れ子が読める文書になることはまずありません。深い階層が必要なら、リストではなく \subsubsection などの見出しか、easylist パッケージを検討してください。

このエラーの厄介なところは、エラーが出ても組版が止まらない ことです。\itemize の定義では深さの検査が \ifnum ... \@toodeep \else ... \fi という形になっていて、上限を超えた場合は単にリストを始めずに本文を素通しします。結果として、エラーは 1 回だけ記録され、PDF は生成され、しかし その階層の項目だけが平文として流れ出す という分かりにくい壊れ方をします。ログに Too deeply nested を見つけたら、PDF が出ていても必ず該当箇所を直してください。もう一つ、深い入れ子で \labelitemv を定義しても意味がありません——第 5 階層に到達する前に検査で弾かれるからです。