マクロツイーター

はてダから移行した記事の表示が崩れてますが、そのうちに直せればいいのに(えっ)

latexrelease が pTeX でアレな話(2)

パッケージを latexrelease に対応させる

先述の通り、latexrelease パッケージは、カーネルだけでなく一般のパッケージについて“バージョンを変える”ためにも使える。*1それをするための方法は latexrelease の説明書に書かれている。基本的に、コードの中で、バージョンによって変わる箇所を次のような「\IncludeInRelease ブロック」に入れることになる。

\IncludeInRelease{<コード日付>}{<ラベル>}{<説明>}
<任意のコード>
\EndIncludeInRelease

この場合:

  • 当該のラベル*2をもつブロックで(スキップされず)実行されたものが未だ存在せず、かつリリース日付(latexrelease のオプション)が“コード日付”以降であれば、内部のコードをその場で実行する。
  • それ以外の場合は、内部のコードをスキップする(実行しない)。

リリース日付に応じて適当なバージョンのコードを実行させたい場合、原則として、同じラベルをもつ \IncludeInRelease ブロックを、コード日付の降順に整列した上で並べて*3書くことになる。

\IncludeInRelease{2015/08/08}{\xx@sort}{Default sorting}
%% default sorting is 'newnewalpha'
\def\xx@sort{newnewalpha}
\EndIncludeInRelease
\IncludeInRelease{2015/04/04}{\xx@sort}{Default sorting}
%% default sorting is 'newalpha'
\def\xx@sort{newalpha}
\EndIncludeInRelease
\IncludeInRelease{0000/00/00}{\xx@sort}{Default sorting}
%% default sorting is 'alpha'
\def\xx@sort{alpha}
\EndIncludeInRelease
%% (*) here \xx@sort has some value

上の例の場合、(*) の位置での \xx@sort の意味がリリース日付によって異なることになる。

カーネルは latexrelease にどう対応させているか

前節の話は、一般のパッケージで latexrelease を利用するときの記述情報である。実は、カーネルはこの記述方法には則していない。実際、先の記述方法だと、\IncludeInRelease ブロックが出現した時点で、ユーザが指定したリリース日付(latexrelease のオプション)が判明していなければならない。latexrelease を読み込む位置は文書の先頭と決まっているから、一般のパッケージ(プリ案ブルで \usepackage で読み込まれる)については問題がない。((ちなみに、\IincludeInRelease を処理するためのプログラムはカーネル中で定義されている。だから latexrelase が読み込まれない場合も、ブロックは処理可能で、current オプションを付けた場合と同等の動作をする。))しかし、カーネルのコードが読み込まれるのは、ユーザの文書ソースが読み込まれる前である。ゆえに、仮にカーネルコードに \IincludeInRelease を置いたとしても、その箇所を読み込む時点では、「ユーザが将来 latexrelease を読み込んだ時にリリース日付を何にするか」を前もって知ることは当然できない。だからカーネルのコードは別の対処方式を求められる。

カーネルにおける“バージョンにより変わる箇所”の記述の方法は以下のようになっている。

  • 本来の出現箇所には、「current を指定時に有効になるブロックの中身」だけを直接書く。つまり、カーネルのソース(latex.ltx)では \IncludeInRelease ブロックを使わない。
  • その上で、全てのラベルに対する全ての \IncludeInRelease ブロックを集めて、それらを latexrelease パッケージのソース(latexrelease.sty)の末尾に追記しておく。*4
  • つまり、latexrelease が読み込まれた時点で“バージョンによって変わる箇所”だけ読み直しているということになる。*5“未来の latexrelease”を入手すると未来のバージョンに書き換えることができるのも、それにカーネルの全てのブロックが含まれてりうからである。
pTeXカーネルも latexrelease させたいのだが……

さて、やっと本題の pTeX(そうなんですよ)の話になる。(なお以下では pTeX にのみ言及するが、upTeX でも話は全く同じである。)

pTeX 上で動作する LaTeX である pLaTeX では、元の LaTeX に比べると、pTeX の日本語組版機能を活用するための改修が加えられている。具体的にいうと、LaTeXカーネルの(docstrip 済の)ソース latex.ltx を読んだ後に、pLaTeX 用のカーネル修正コードのソース plcore.ltx(以下単に「pLaTeXカーネル」と呼ぶ)が読み込まれている。*6

LaTeXカーネルのソースが latexrelease に対応させているのに対し、pLaTeXカーネルが積極的に対応すべきか否かはそれ自身議論すべきことである。しかし、何れにしても、pLaTeXカーネルは latexrelease の使用について完全に無視することは難しい。それは、LaTeX カーネルでの“バージョンで変わる箇所”と pLaTeX カーネルの改修部分が重なる場合があるからである。

実際、現時点でそういう“重なる箇所”は存在している。例えば、LaTeX の命令 \em((強調のためのフォント変更を行う命令。なお、\emph も内部で \em を呼び出している。))の定義は(2015/01/01 の latexrelease.sty において)以下のようになっている。

\IncludeInRelease{2015/01/01}{\eminnershape}{\eminnershape}%
\DeclareRobustCommand\em
        {\@nomath\em \ifdim \fontdimen\@ne\font >\z@
                       \eminnershape \else \itshape \fi}%
\def\eminnershape{\upshape}%
\EndIncludeInRelease
\IncludeInRelease{0000/00/00}{\eminnershape}{\eminnershape}%
\DeclareRobustCommand\em
        {\@nomath\em \ifdim \fontdimen\@ne\font >\z@
                       \upshape \else \itshape \fi}%
\let\eminnershape\@undefined
\EndIncludeInRelease

両バージョンの違いは \eminnershape の有無である。((これは「イタリックで組まれた文章の中で強調(\em)が行われた場合、どのようなフォント変更を行うか」を表すマクロである。2015 年版のカーネルではこれがマクロになって(LaTeX レベルで)容易にカスタマイズ可能であるのに対し、2014 年以前では \upshape に固定されている。))ところが、pLaTeX カーネルでは、\em は次のように改修されていて、和文側のファミリ変更が追加されている。*7

\DeclareRobustCommand\em
        {\@nomath\em \ifdim \fontdimen\@ne\font >\z@
                       \mcfamily \upshape \else \gtfamily \itshape \fi}

ここでは \eminnershape を参照していない(2014 年版 LaTeX と同じ)ので、2015 年版の pLaTeX では、命令 \eminnershape は「存在はするが設定しても用いられない」という不快な状態になっている。だが、この点自体については、「pLaTeX ではそういう仕様である」と決めれば済むことかも知れない。

もっと問題なのが latexrelease をユーザが読み込んだ場合の挙動である。この場合、先述の通り、latexrelease を読んだ時点で改めて LaTeX カーネル\IncludeInRelease ブロックが読み込まれる。その結果、pLaTeX で行った改修が消えてしまうので、結局さっき決めた「仕様」が満たされないことになってしまうのである。

つまり、LaTeX カーネル\IncludeInRelease ブロック と pLaTeX カーネルの改修部分が重なる場合、その箇所については latexrelease が正常に動作しなくなる、といえる。この問題を解決するのは厄介そうである。

*1:この場合も文書作成者側の指定方法は変わらず、単にファイル先頭で latexrelease を読み込めば、カーネルと“latexrelease 対応のパッケージ”の全てが指定日付の状態に書き換わる。特定のパッケージだけ書換を適用することはできない。

*2:ラベルは単一の制御綴、または非特殊な文字の列の何れかにする。

*3:離れた場所に書いてもよいが、とにかく各ブロックは(実行されるとすれば)書かれた場所で実行されるので、少なくともそのことを考慮してブロックを配置する必要がある。

*4:一つのラベルの中でのブロックの順序は先述の規則(コード日付の降順)に従う。

*5:従って、当該の箇所のコードは「後から読み直される」ことを前提にして書かれる必要がある。

*6:その状態で dump されたのが platex.fmt フォーマットである。plcore.ltx も latex.ltx と同様、複数の docstrip ソースから生成されたものである。

*7:この定義は組版的にいってかなり奇妙であるが、それは今は措いておく。