🎄

モダンTeX on LaTeX入門

2022/12/24に公開

TeX & LaTeX Advent Calendar 2022の24日めの記事です。昨日は @wtsnjp さんによる「WEB言語に挑戦」でした。)

モダンかどうか以前に、まずは「TeX on LaTeX」について説明する必要があるでしょう。命名者(たぶん)である@zr_tex8r氏の定義によれば、TeX on LaTeXとは「LaTeXフォーマットの上で、TeX言語(プリミティブ、LaTeX内部マクロ)を自由に用いるスタイル」のことです(出典)。

この定義だけを示しても、多くの人にはLaTeXフォーマットTeX言語が自明ではないと思うので、不十分なのは承知しています。しかし本稿ではこれらの定義については踏み込まないこととします。その代わり、20世紀と21世紀の「TeX on LaTeX」の実務の移り変わりをちょっとだけ紹介していきます。題材としては、「longtableでキャプションのスタイルを変えたい」という状況を考えてみましょう。

longtableに限らず、LaTeXにおいてキャプションのスタイルを変えたい場合のもっとも汎用の手段はcaptionパッケージです。本稿は「longtableでキャプションのスタイルを変える方法」の説明ではないのでcaptionパッケージの使い方は説明しませんが、たとえばキャプションの書体をデフォルトのセリフ(日本語だと明朝に相当)からサンセリフ(同ゴシックに相当)にしたければこう書くだけです。

\usepackage{caption}
\captionsetup[longtable]{labelfont=sf,textfont=sf}

こんなふうに適切なパッケージを使ってドキュメントのスタイルや機能を設定するのがLaTeXの本来の使い方です。しあわせなLaTeXユーザーであれば、標準的なパッケージで提供されていないことをLaTeXでやろうとはしないものです。基本、LaTeXはこうやって使ってください。

しかし、場合によってはその制限を越えたいときもあります。そこでTeX言語を使おうというのがTeX on LaTeXです。実際のところ、たとえばcaptionパッケージにしても、もともとはLaTeXに備わっていなかった機能を実現するためにTeX on LaTeXを駆使して開発されたものだと言えます。その成果を「しあわせなLaTeXユーザー」が使えるようにパッケージという形に整備してくれた人がいるので、ぼくらは「しあわせなLaTeXユーザー」でいられるわけです。ありがたいですね。

TeX on LaTeXしてみる

いま、captionパッケージでは実現できないような表キャプションを作りたくなったとします。たとえば、「表のキャプションが長すぎるのは許容できないので、複数行になるような長いキャプションは無条件で☃に圧縮する」という機能が必要になったとしましょう。

longtableのようなLaTeXの基本的な機能でこのような無茶をやる場合には、LaTeXのソースに手を入れることになります。とはいえ、LaTeX本体を書き換えて再インストールするわけではありません。大本のソースのマクロと同じ名前でマクロを定義し、それを自分のドキュメントで読み込めば、そちらの新しい定義が使われるようになります。いわゆるモンキーパッチです。いまの例であれば、大本のLaTeXのソースで「longtableのキャプションを定義しているマクロ」を探し、そのマクロと同じ名前の別のマクロを定義することになります。このような一連の作業がおおむねTeX on LaTeXの実務に相当すると思ってもいいでしょう。

というわけで、「longtableのキャプションを定義しているマクロ」を探します。探し方はとくにないので、マニュアルにある「実装解説」もしくはソースそのものを地道に読んでください。最近はLaTeXのソースもすべてGitHubにあるので、その検索機能を使ってもいいでしょう。この例の場合は、longtable.dtxというファイルに次のような\LT@makecaptionというマクロがあり、これがlongtableのキャプションを定義しています(以下は2022/12/24時点の実装)。

\def\LT@makecaption#1#2#3{%
  \LT@mcol\LT@cols c{\hbox to\z@{\hss\parbox[t]\LTcapwidth{%
    \sbox\@tempboxa{#1{#2: }#3}%
    \ifdim\wd\@tempboxa>\hsize
      #1{#2: }#3%
    \else
      \hbox to\hsize{\hfil\box\@tempboxa\hfil}%
    \fi
    \endgraf\vskip\baselineskip}%
  \hss}}}

上記をみるとわかるように、これを読み解いて改造するにはTeX言語の知識が必須です。もっとも、この例くらいなら「たぶんこのへんをいじればなんとかなるやろ」というふんわりした理解でもやっていけそうです。幸い、キャプションの長さに応じて条件分岐しているようなので、表5行めを修正してこんなふうに再定義するだけで目的(「1行に収まらないときは☃に圧縮される表キャプション」)を達成できそうです。

\def\LT@makecaption#1#2#3{%
  \LT@mcol\LT@cols c{\hbox to\z@{\hss\parbox[t]\LTcapwidth{%
    \sbox\@tempboxa{#1{#2: }#3}%
    \ifdim\wd\@tempboxa>\hsize
      \hbox to\hsize{\hfill\hfill}% ここを再定義した
    \else
      \hbox to\hsize{\hfil\box\@tempboxa\hfil}%
    \fi
    \endgraf\vskip\baselineskip}%
  \hss}}}

この\LT@makecaptionの再定義をプリアンブルに書くなりしてドキュメントに適用すれば、期待どおりの結果が得られるでしょう。

\patchcmdによるTeX on LaTeX

オリジナルのマクロを自分で再定義して読み込めば挙動を変えられるのはいいとして、マクロ全とっかえ、つまり「元の定義のソースをコピペしてきて一部を差し替える」という作業はなんとも不格好です。いまの例の\LT@makecaptionくらいの行数ならまだしも、場合によってはオリジナルが長大な定義だったりすることもあります。「どのへんの挙動を変えたかったか」という意図を残すのに自然言語でコメントを書くくらいしかないというのも微妙です。

そこで昨今のLaTeXでは、マクロ全とっかえという従来のTeX on LaTeXに代わり、「挙動を変えるためのマクロの再定義」のための統一された手法が徐々に広く利用されるようになっています。
歴史的にいくつか種類があるのですが、現在ではetoolboxというパッケージで提供されている\patchcmdなどのコマンドを利用するのが基本のようです。オプション引数の置き換えにも対処できるxpatchや、パッチをあてるパターンを正規表現で指定できるregexpatchなどの亜種も存在します。

ここでは、これらの手法を「\patchcmdによるTeX on LaTeX」などと総称することにします。個々のコマンドの使い方については各パッケージのマニュアルを参照してください、と言って済ませたいところなんですが、実はどれも従来のTeX on LaTeXによる実務についての直感があることが前提なので、そのへんがない状態でいきなり見ても何もわからないマニュアルだと思います。というわけで、とりあえず\patchcmdコマンドの使い方だけ軽く紹介しておきます。

\patchcmdは、第一引数に「パッチをあてたいコマンド」、第二引数に「パッチをあてたい箇所のTeX言語のコード」、第三引数に「置き換えるコード」を指定して使います。あと2つ必須の引数がありますが、これらはデバッグ用なので、とりあえずは{}を指定しておけば問題ありません。上記と同じ\LT@makecaptionの改造であれば、このように記述できます。

\usepackage{etoolbox}
\patchcmd{\LT@makecaption}
  {\hsize#1{#2: }#3}
  {\hsize\hbox to\hsize{\hfill#1{#2: }\hfill}}{}{}

書き換えたかったのはオリジナルの\LT@makecaptionの定義にある5行めの「#1{#2: }#3」なので、その前に\hsizeを書くのが意味わからんと思うかもしれませんが、これがないとオリジナルの3行めにある同じ文字列が置き換えられてしまい、期待する挙動になりません。\patchcmdで置き換えてくれるのは、あくまでも「最初に出現した第二引数のパターン」なのです。そんなわけで、5行めのほうに対してユニークになる「\hsize#1{#2: }#3」というパターンで指定しています。パターンにマッチするかどうかは、文字列ではなくTeXの字句解析レベルで判断されるので、改行や空白は不要です。

そんなやり方で大丈夫?

\patchcmdによるTeX on LaTeXをみて、「大本の定義に変更があったときに大丈夫なの?」と思う人もいるでしょう。マクロ全とっかえであれば、大本のマクロに変更があっても、独自に再定義したものは引き続き動き続ける可能性があります。

しかしマクロ全とっかえをすれば、当然、上流のマクロに施された改善は取り込めません。また、大本のコードには「ぱっと見は不要にみえるけどさまざまな理由で変更すべきではない箇所」もあります。\patchcmdによるTeX on LaTeXであれば、「自分が実現したい挙動の変更を達成するために大本のコードにおいて中核となる秘孔はどこか?」をうまく見つけなければならないので、そういう「変更すべきではない箇所」を下手に葬ってしまう危険性は下がります。その意味ではマクロ全とっかえよりも安全とさえ言えます。Unix系システムのpatchコマンドのような「コードの字面上の文字列に対するパッチ」ではなく「TeXの字句解析に基づいたパッチ」なので、インデントの変更くらいなら大本のマクロで発生しても困ることはないというのもあります。

TeX on LaTeXは難しい

「TeXでプログラミング」というと、定型文字列の置き換えみたいなのを想像する人も多いかもしれませんが、実務でLaTeXを使っていて必要になるプログラミングの大半は本稿で紹介したようなTeX on LaTeXです。従来のマクロ全とっかえによるTeX on LaTeXでは必然的にコードがとっちらかりがちでメンテナンスも困難になることが多かったのですが、最近は\patchcmdのおかげで比較的すっきりとTeX on LaTeXできるようになりました。

とはいえ、本稿からも想像できると思いますが、TeX on LaTeXをするには単純なTeXのプログラミングよりも前提知識は増えます。とくにLaTeXやパッケージのソースを読んで秘孔を探さなければならない\patchcmdによる方法は、がっと\renewcommandとかで自分に都合がいいコードを書き直してもなんとかなるマクロ全とっかえより、場合によっては面倒です。ぼく自身、とりあえず意図した挙動を得るまではマクロ全とっかえで試行錯誤しつつオリジナルのマクロの秘孔を見定めたら\patchcmdなどで書き直し、というのを理想形とみなしていますが、めんどくさくてマクロ全とっかえのまま放置している場合のほうがまだ多いのが実状です。

さらに、実を言うと本稿で紹介したのはTeX on LaTeXのもっとも単純な側面だけです。この先には、LaTeXに加えた挙動の変更をもっと汎用化してパッケージやクラスにするという、本稿の話とはまた別の知識が必要になるTeX on LaTeXの側面があります。CTANに登録された数々の便利パッケージや便利クラスの開発者たちにはほんとうに感謝しかありません。

Discussion