「LaTeX のリストは間延びしている」——enumitem パッケージに手を伸ばす理由はたいていこれですが、実際の数字を見た人は多くありません。article クラスの既定では、項目と項目のベースライン間隔は 20pt——ふつうの行送り 12pt に対して、項目のあいだにだけ 8pt が余分に入っています。その 8pt の正体は \itemsep(4pt)と \parsep(4pt)という二つの長さで、enumitem パッケージは、これらを itemsep=0pt のような キー=値(key=value)のオプション で直接書き換えるための道具です。このページでは、既定値が実際にいくつなのか、nosep や leftmargin=* が何を変えているのか、\setlist で文書全体の方針をどう決めるかを、すべて実測値とともに追います。
LaTeX のリストの既定値は、実際にいくつなのか
article クラス(10pt、第 1 階層)で測ると、\itemsep は 4pt、\parsep は 4pt、\topsep は 8pt、\leftmargin は 25pt、\labelwidth は 20pt、\labelsep は 5pt です。この数字が分かると「間延びして見える」の正体がはっきりします。項目のあいだに入る縦の空きは \itemsep と \parsep の 合計 8pt——行送り 12pt と足して 20pt になり、ふつうの行が 12pt 間隔で並ぶ本文のなかで、リストだけが 1.67 行分の間隔で組まれるわけです。組版としては意図的な設計(項目が視覚的に分離される)ですが、短い項目を 10 個並べると紙面が間延びします。横方向も同じで、\leftmargin 25pt に対して黒丸の実幅は 5pt 足らずしかなく、20pt はただの空白です。
| 長さ(=キー名) | article の既定(第1階層) | 効く場所 |
|---|---|---|
itemsep | 4pt(plus 2pt minus 1pt) | 項目と項目のあいだ |
parsep | 4pt(plus 2pt minus 1pt) | 1 項目の中の段落と段落のあいだ |
topsep | 8pt(plus 2pt minus 4pt) | リストの上と下 |
partopsep | 2pt(plus 1pt minus 1pt) | リストが空行のあと(新段落)で始まるときの追加分 |
leftmargin | 25pt | 本文ブロック全体の左への字下げ |
labelwidth | 20pt | ラベルを入れる箱の幅(これより広いラベルは左へはみ出す) |
labelsep | 5pt | ラベルの箱と本文のあいだ |
rightmargin | 0pt | 右側の余白。引用のようにリスト全体を左右から絞りたいときに使う |
itemindent | 0pt | 項目の 1 行目だけの追加の字下げ(負の値で左へ出せる) |
labelindent | —(enumitem が追加したキー) | 外側からラベル箱の左端までの空き |
階層が深くなると値は縮みます。itemize の第 2 階層では \itemsep と \parsep が 2pt、\topsep が 4pt、\leftmargin が 22pt に、第 3・第 4 階層では \parsep が 0pt、\topsep が 2pt まで落ち、\leftmargin は 18.7pt・17pt と細っていきます。つまり LaTeX は「浅い階層ほど大きく空ける」方針を最初から持っていて、深い入れ子はすでに詰まっています。詰めたいのはたいてい第 1 階層だけ、という実感はここから来ます。
キー=値の書き方——キー名は LaTeX の長さそのもの
プリアンブルで \usepackage{enumitem} と読み込むと、itemize・enumerate・description の 3 環境が オプション引数 を取れるようになり、角括弧の中に キー=値 をカンマ区切りで並べるだけでそのリストの体裁が決まります。この設計の要点は、キー名が上の表の長さ名とそのまま同じことです。itemsep=1ex は \itemsep を、leftmargin=1cm は \leftmargin を書き換えます。つまり enumitem の使い方を覚えることが、そのまま LaTeX のリスト内部を覚えることになる——\renewcommand で内部命令を書き換える旧来のやり方に比べて、これが最大の利点です。TeX Live 2024 に同梱されているのは Javier Bezos による v3.9(2019/06/20)で、kpsewhich enumitem.sty で手元の版を確認できます。
\usepackage{enumitem}
% ...
\begin{itemize}[itemsep=1ex, leftmargin=1cm]
\item the key names are the LaTeX length names
\item second entry
\end{itemize}同じキーは、文書全体・環境の種類ごと・個々のリスト、という三段の階層で上書きできます(後述の \setlist)。もっとも具体的に与えた値が勝つので、まず全体の方針を決めておき、必要なところだけ局所的に外す、という整理が効きます。値がまるごと波括弧で囲まれている場合は外側の波括弧が一組外される(keyval の既定動作)ので、波括弧そのものを残したいときは二重にしてください。
リストを詰める——nosep と noitemsep の違い
nosep は縦の空きを 四つとも ゼロにし、noitemsep は 二つだけ ゼロにします。パッケージのソースを見ると nosep は \partopsep・\topsep・\itemsep・\parsep の 4 本を 0pt に、noitemsep は \itemsep と \parsep の 2 本だけを 0pt にしています。忘れられがちなのが partopsep で、これはリストが空行のあと(新しい段落として)始まるときにだけ足される 2pt です。nosep を当てた itemize を実測すると、項目間のベースライン間隔はぴたり 12pt——本文のふつうの行とまったく同じ間隔になります。リスト前後の空きも消えるので、地の文に完全に溶け込みます。
% nosep -> partopsep = topsep = itemsep = parsep = 0pt
% noitemsep -> itemsep = parsep = 0pt, list keeps its 8pt topsep
\begin{itemize}[nosep]
\item item baselines land exactly 12pt apart
\item as tight as ordinary body text
\end{itemize}どちらを選ぶかは、リストを地の文の一部と見なすかどうかで決まります。手順書やスライドのように項目そのものが主役なら nosep で構いませんが、論文の本文に挟まる短いリストでは noitemsep のほうが安全です——リストの上下の 8pt が残るので、段落とリストの境目が視覚的に保たれます。nosep で上下も詰めると、リストが前後の段落に貼りついて「どこからどこまでが列挙か」が読み取りにくくなります。なお nolistsep という古いキーもありますが、これは topsep を完全な 0 ではなく 0pt plus .1pt にする点だけが nosep と違います。新しく書くなら nosep を使ってください。
label= と ref=——ラベルと相互参照を別々に決める
enumerate のラベルは label= で決めます。値の中では \arabic*・\alph*・\Alph*・\roman*・\Roman* という アスタリスク付き の形が、その階層の現在のカウンタを表します。label=(\arabic*) なら (1) (2) (3)、label=\alph*) なら a) b) c)、label=\Roman*. なら I. II. III. です。星付きの形は引数を取らず、ラベルの中でだけ意味を持つ特別な綴りだと考えてください。さらに label*= は親のラベルに連結した番号を作り、入れ子にすると 1.、1.1.、1.2. のように積み上がります。
\begin{enumerate}[label=(\arabic*), ref=\arabic*]
\item \label{step:one} prepare the ingredients
\item heat the pan
\end{enumerate}
Go back to step~\ref{step:one}. % prints "step 1", not "step (1)"既定では label= の指定が相互参照の見た目にも及びます。[label=(\arabic*)] だけのリストの項目に \label を付けて \ref すると、本文には括弧つきの (1) が出ます——「手順 (1) に戻る」となって二重に括弧が見えることがある、という実務上の煩わしさがここから生まれます。ref=\arabic* を足せばラベルは (1) のまま、\ref の戻り値だけが素の 1 になります。上のコードを実際に組んで確認したところ、まさにこの通りに出ました。なお label と ref の値は移動引数として扱われるので、壊れやすい命令を入れるときは \protect が必要になることがあります(星付きのカウンタ表現はそのまま使えます)。
ラベルを enumerate パッケージ風の短い綴りで書きたいときは、読み込み時に \usepackage[shortlabels]{enumitem} とします。すると角括弧の 最初の要素 として A・a・I・i・1 と書くだけで、それぞれ \Alph*・\alph*・\Roman*・\roman*・\arabic* の意味になり、\begin{enumerate}[(1)] のように書けます。古い原稿を enumerate パッケージから移植するときに便利ですが、新規に書くなら label= を明示したほうが読み返したときに意図が分かります。
leftmargin=* と wide——左余白を計算させる
横方向のキーは独立ではなく、\leftmargin + \itemindent = \labelindent + \labelwidth + \labelsep という一本の等式で結ばれています。だから一部のキーに値の代わりに * を渡すと、enumitem が等式の残りを解いて埋めてくれます。実測すると効果は劇的で、既定の itemize の \leftmargin は 25pt ですが、[leftmargin=*] を付けると 9.99pt まで縮みます——黒丸の実幅ぶんだけを確保した値です。逆に label=(\roman*) のような幅の広いラベルでは、既定の 20pt の箱に (viii) が収まらず左へはみ出しますが、leftmargin=* を足せばラベルに合わせて箱が広がります。幅の広いラベルを使うときは leftmargin=* をセットで書く、と覚えておくと事故が減ります。
もう一つの便利キーが wide です。左余白とラベル箱の幅をゼロにし、ラベルを本文の一部のように見せて、項目をふつうの段落と同じ形に組みます。ソースを見ると、\leftmargin と \labelwidth を 0 にしたうえで \labelindent に引数(既定は \parindent)を入れ、\listparindent をそれに合わせ、\itemindent を等式から計算しています。実測でも \leftmargin は 0pt、\itemindent は 20pt、\listparindent は 15pt(= \parindent)になりました。字下げ量を変えたいときは wide=0pt のように値を与えます。左に飛び出したリストを避けたい、本文の左端をそろえたい、という版面設計のときに効きます。align= も併せて覚えておくと便利で、ラベルを箱の中で左寄せ(left)・右寄せ(right、既定)・左寄せパボックス(parleft)のどれにするかを決めます。
番号を 5 から始める・前のリストから続ける(start・resume・series)
start= は最初の項目の番号を指定し、resume は直前の enumerate の続きから番号を振ります。手順の途中に説明の段落を挟み、そのあと「7.」から再開したい——という場面はきわめて多く、これを手作業の \setcounter でやると、あとから項目を一つ足したときに番号がずれます。実際に [start=5] で 5, 6 と組み、段落を挟んで [resume] を置いたところ、続きの項目は 7 になりました。resume* を使うと番号だけでなく 前のリストのオプション設定ごと 引き継ぎます(この星付きは環境の角括弧の中でのみ意味を持ちます)。
\begin{enumerate}[start=5]
\item numbered 5
\item numbered 6
\end{enumerate}
An explanatory paragraph goes here.
\begin{enumerate}[resume]
\item numbered 7 -- the count carries over
\end{enumerate}resume が見るのは直前のリストだけなので、章をまたいで離れた複数のリストを一つのまとまりとして通し番号にしたいときは足りません。その場合は シリーズ を使います。最初のリストに series=名前 と名札を付けておき、続きのリストで resume=名前 あるいは resume*=名前 と指定すれば、あいだに何が入っていても番号がつながります。長い手順書を節ごとに分けて書くとき、これが唯一まともに動く方法です。
\setlist で文書全体に効かせ、\newlist で自分のリストを作る
同じオプションを毎回書くのは現実的ではありません。\setlist は、リストの種類と階層を指定して設定をまとめて当てる命令です。\setlist{...}(角括弧なし)はすべてのリストのすべての階層に、\setlist[itemize]{...} は itemize の全階層に、\setlist[enumerate,1]{...} は enumerate の第 1 階層だけに効きます。星付きの \setlist* は、それまでの設定を上書きせずに 追加 します。冒頭で見た「間延び」を文書全体で直したいなら、プリアンブルに \setlist{noitemsep} と一行書くのがもっとも費用対効果の高い一手です。
\usepackage{enumitem}
\setlist{noitemsep} % tighten every list in the document
\setlist[itemize]{leftmargin=*} % let itemize size its own margin
\setlist[enumerate,1]{label=(\arabic*), ref=\arabic*}
% a list type of your own
\newlist{steps}{enumerate}{1}
\setlist[steps]{label=\textbf{Step \arabic*.}, leftmargin=*}さらに踏み込むと、自分専用のリスト型を定義できます。\newlist{名前}{型}{最大の深さ} は 3 基本リストのいずれか(enumerate/itemize/description)を複製した新しい環境を作り、続く \setlist[名前]{...} で体裁を与えます(少なくともラベルは設定が必要です)。上のコードを実際に組むと \begin{steps} が「Step 1.」「Step 2.」を出しました。利点は見た目より 原稿の可読性 です。\begin{steps} と書いてあれば、それが手順であることがソースを読んだだけで分かります——リストの意味を、体裁ではなく名前で表せるわけです。
段落の中に並べる行内リスト(enumerate*)
パッケージを \usepackage[inline]{enumitem} と読み込むと、enumerate*・itemize*・description* という アスタリスク付きの環境 が加わり、項目が改行されずに段落の中を横に流れます。実際に組むと「必要なものは (1) 小麦粉 (2) 砂糖 (3) 塩 の三つです。」のように一行に収まります。三つ四つの短い語を数えるだけなら、別行立てのリストで紙面を 4 行使うより、この形のほうが読みやすいことが多く、学術論文で条件を列挙する場面ではむしろこちらが標準的な書き方です。
\usepackage[inline]{enumitem}
% ...
We need
\begin{enumerate*}[label=(\arabic*)]
\item flour \item sugar \item salt
\end{enumerate*}
for this.区切りは itemjoin(項目のあいだ。既定は空白)、itemjoin*(最後の項目の直前だけ変えたいとき——英語の "a, b, and c" のような書き方に使います)、afterlabel(ラベルの直後。既定は ~)で整えます。ただし行内リストの項目は既定で箱に入るため、フロート・入れ子のリスト・別行立ての数式は中に置けません。必要なら mode=unboxed を指定してください。行内リストは短い語句を並べる用途に限る、というのが実務上の線引きです。