🎄

LaTeXのテーブルを縦罫線ありにする

2023/12/24に公開

108の煩悩のひとつ、テーブルの縦罫線。LaTeXでテーブルの縦罫線を引くには、各テーブルのプリアンブルで「|」を明示的に指定しなければならず、「構造と見た目の分離」という観点でかなり不幸です。

\begin{tabular}{|c|c|c|}&&\\&&\end{tabular}

そもそもテーブルのプリアンブル自体、構造と見た目の密結合なわけですが、とりわけ罫線に関する情報なぞドキュメントの本文に書きたくありません。見た目に関する性質は本文からは分離するというのがドキュメント屋さんの基本です。「このドキュメントではテーブルを縦罫線ありにする」 といった形で大域的に見た目を制御できないようなドキュメントシステムにはがっかりです。

しかし、これをLaTeXのスタイルとして本文から外出しにするための直接的な仕組みはありません。そこでLaTeXで幸せ(構造化文書的な意味で)になるために、TeX & LaTeX Advent Calendar 2023の24日めの記事として、プリアンブルに罫線情報を持たせずに監獄テーブルを作るためのワークアラウンドを2つ紹介します。

独自のカラムタイプを定義する

近い効果を得る手段としては、プリアンブルにおけるcのようなやつ(「カラムタイプ」と呼びます)を独自に定義するための\newcolumntypeというインタフェースがあります[1]。これを使って「縦罫線付きのc」になるように独自のカラムタイプCを定義し、それをテーブルのプリアンブルで使えば、縦罫線がデフォルトで引かれます。

\usepackage{array}
\newcolumntype{C}{|c|}

\begin{document}
\begin{tabular}{CCC}&&\\&&\end{tabular}
\end{document}

隣り合うカラムのそれぞれに縦罫線が引かれるので、テーブルの左右両端以外は2本ずつ縦罫線が引かれています。この2本の縦罫線のスキマは\doublerulesepという長さで決まるので、これを適切に設定すればスキマは消せます[2]

プリアンブルの文字列をxstringとxpatchで動的に書き換える

ほとんどのニーズでは上述の\newcolumntypeで十分でしょう。しかしLaTeXソースを手書きせずにツールで生成している場合には満足できないこともあります。そうしたツールでは cのような標準のカラムタイプを生成するテーブルにハードコードしてくるので、独自のカラムタイプに書き換えるための工程が別途必要になってしまうからです。

標準のカラムタイプのまま縦罫線を生成するにはどうしたらよいでしょうか。

この問題が難しいのは、テーブルのプリアンブルがマクロでなく、ベタな文字列として引数で渡されることです。引数の文字列をマクロで都合よく独自のカラムタイプや「|」つきに変えるのが難しい。

幸い、昨今のLaTeXには、「文字列の書き換え」を容易に実現できるxstringパッケージが存在します。そこで、tabular環境がプリアンブルを処理するプロセスにxpatchで割り込み[3]、このxstringでプリアンブルの文字列を動的に変換することにしましょう。

とはいえ、言うほど簡単ではありません。まず、tabular環境がどういう機序でプリアンブルを処理しているか知る必要があります。詳しい説明は端折りますが、tabular環境に与えたプリアンブルは\@arrayコマンドの第2引数に渡され、そこで\@mkpreamコマンドによって処理されます。これをぶんどって、xstring\StrSubstituteで「c|c」に変換し、さらにプリアンブル全体の最後尾にも「|」を付けます。そうすれば「ccc」が「|c|c|c|」になりますね。この書き換えたプリアンブルを、展開された状態で\@arrayコマンドに戻すには、\edef\noexpandを適当に使う必要があります。

最終的にはこんな感じのパッチになりました。

\usepackage{xpatch}
\usepackage{xstring}

\patchcmd{\@array}{\@mkpream{#2}}
  {\StrSubstitute{#2}{c}{|c}[\pream]%
   \edef\x{\noexpand\@mkpream\noexpand{\pream|}}\x}{}{}

\StrSubstituteを追加すれば、cだけでなくlなども同様に縦罫線バージョンにできます。

\patchcmd{\@array}{\@mkpream{#2}}
  {\StrSubstitute{#2}{l}{|l}[\pream]%
   \StrSubstitute{\pream}{c}{|c}[\pream]%
   \StrSubstitute{\pream}{>}{|>}[\pream]%
   \edef\x{\noexpand\@mkpream\noexpand{\pream|}}\x}{}{}

横罫線もデフォルトで挿入されるようにする

ついでに、\hlineを明示的に挿入しなくても横罫線が入るようにしましょう。こちらはそんなに難しくありませんが、\@arrayの定義中のどこに\hlineを挿入すればいいかを見つけるのはちょっと大変でした。直感的には「\ialignの前」なのですが、ここはまだテーブルの外なので、\hlineは入れられません(\hruleなら入る)。\crの後ろがたぶん正解なのですが、パッチを充てるべき\crは、内部で\xdefされている\@preambleというトークンリストの中にあります。そこに\hlineを単純に挿入してもうまくいきません。\noexpandが必要です。

縦罫線とあわせて、全体はこんな感じになりました。

\documentclass{jlreq}
\usepackage{xpatch}
\usepackage{xstring}

\makeatletter
\patchcmd{\@array}{\cr}{\cr\noexpand\hline}{}{}    % 一番上の罫線
\patchcmd{\endtabular}{\egroup}{\hline\egroup}{}{} % 一番下の罫線
\apptocmd{\@tabularcr}{\hline}{}{}                 % 途中(\\)の罫線

% 縦罫線のためのパッチ(前項で定義したもの)
\patchcmd{\@array}{\@mkpream{#2}}
  {\StrSubstitute{#2}{c}{|c}[\pream]%
   \edef\x{\noexpand\@mkpream\noexpand{\pream|}}\x}{}{}
\makeatother

\begin{document}
\begin{tabular}{ccc}&&\\&&\end{tabular}
\end{document}

なお、縦罫線と横罫線が交わるところがくぼんでしまうのは、たぶん現在のLaTeXの仕様です[4]

longtableの場合

参考までに、longtableで縦罫線をデフォルトにしたい場合は、\@arrayではなく\LT@arrayにパッチを充てる必要があります。これはPandocで縦罫線ありのテーブルを作りたいときに便利かもしれません。

\patchcmd{\LT@array}{\@mkpream{#2}}
  {\StrSubstitute{#2}{l}{||l}[\pream]%
   \StrSubstitute{\pream}{c}{||c}[\pream]%
   \StrSubstitute{\pream}{>}{||>}[\pream]%
   \edef\x{\noexpand\@mkpream{@{}\pream||@{}}}\x}{}{}

Pandocでは、テーブルの天地の横罫線については\toprule\bottomruleをハードコードしてくるので、これらを\def\toprule{\hline}および\def\bottomrule{\hline}のように定義してしまうのが簡単です。それ以外の横罫線は\apptocmd{\LT@tabularcr}{\hline}{}{}でいけます。

脚注
  1. \newcolumntypeを使うには(LaTeXのオリジナルのテーブルではなく)arrayパッケージのものを使う必要があります。 ↩︎

  2. \setlength{\doublerulesep}{-\arrayrulewidth} ↩︎

  3. xpatchについては過去の記事を参照。 ↩︎

  4. Frank Mittelbach and David Carlisle, “A new implementation of LATEX’s tabular and array environment” の「2.1 Handling of rules」など。 ↩︎

Discussion