LaTeXのテーブルを縦罫線ありにする
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}{}{}
でいけます。
Discussion