マクロツイーター

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

TeX の構文規則がフリーフォーマットでないことがよくわかる話(2)

前回の続き)
「段落先頭」という罠

ところで、このような割と単純な「アウトな空白」によるバグは、少しでもテストをしていれば気付くはず、と考える人もいるだろう。だが、実際には「アウトな空白」のバグは、「テストケースの作り方」が悪いために見過ごされてしまう危険性が高いのである。

\documentclass{article}
\makeatletter
% スライド掲載のコードを"そのまま"書いた
\newcount\@m
\newcount\@k

\def\factorial#1{%
  \@m = 1 \relax
  \@k = 1 \relax
  \@whilenum \@m < #1 \do {%
    \advance \@m 1 \relax
    \multiply \@k \@m \relax
  }
  \the\@k
}
\makeatother
\begin{document}
% \factorial "だけ"置いてテストしている.
720

\factorial{6}

\newcounter{myVal}
\setcounter{myVal}{6}
\factorial{\value{myVal}}

\end{document}

自分で実装したマクロ \factorial の動作を検証しようとして上記のように「\factorial だけ」書いたようなソースをコンパイルしたとする。すると結果は次のようになり、余計な空きは全く出現しない。

ここで「余計な空き」が出現しない理由は、TeX の一般的な組版規則として、「段落先頭にある空白トークンは無視される(水平空きを生じない)」*1からである。つまり、マクロを段落の先頭に書いた場合は、たとえ「アウトな空白トークン」が実行されていても、結果に空きは出現しないのである。

無論、この性質は「バグが露見しない」というだけで全く嬉しいことでない。後になって「段落内」でそのマクロを使う機会がありそこで初めてバグが発覚するという最悪の事態を迎えるだけである。*2そういう事態を避けたいのであれば、次の事を心がけることを推奨したい。

「段落内」で使われうるマクロ(命令)のテストをする場合は、
必ず、「段落内」での使用をテストケースに含める。

本当に恐ろしい「段落先頭という罠」

この「段落先頭という罠」は熟練した TeXnician も無縁ではないようである。実際、CTAN に登録されているパッケージでも、「段落内」で利用すると「アウトな空白」による空きを無残にさらけ出してしまうものが存在する。 その中でも衝撃的なのは、最近発見された次のバグである。

graphicx パッケージを dvipdfmx で利用していてかつ xbb ファイルの自動生成を有効にしている状態を仮定する。ここで「段落内」で使用した \includegraphics 命令において実際に xbb の自動生成(extractbb の起動)が行われた場合、*3挿入された画像の直前に余計な空白が出現する。

この不具合の原因はドライバ定義ファイル dvipdfmx.def に混入した「アウトな空白」である。

[dvipdfmx.def:95行目]
\def\Gread@pdf#1{%
  \begingroup
  \@tempcnta\z@
  ……(略)……
  \ifeof\@inputcheck
    \immediate\write18{extractbb \Gin@base\Gin@ext}       ←行末に % がない!!
    \immediate\openin\@inputcheck#1 %
  \fi
  ……(略)……
  \expandafter\Gread@parse@bb\@gtempa\\}

驚くべきは、このバグは 5 年以上の間気付かれずに残っていたことである。文書に画像を挿入する時、ほとんどの場合は \includegraphics だけで段落をなすように配置される。これはすなわち段落先頭にあることになるので、この場合には空白が現れない。また稀に「段落内」に \includegraphics を置くことがあっても、画像自体が余白を持っていることが多くて、そのため数 pt の欧文空白はやはり気付き難かったのであろう。

なお、このバグは最新の TeX Live および W32TeX では修正されている。

*1:この言い方は不正確で、厳密にいうと次のようになる:「段落外」で空白トークンを実行しても何も起こらない。つまり、そこで段落は開始されず、「段落外」の状態が継続される。

*2:もちろん、LaTeX の命令としての「仕様」として「この命令は段落先頭でのみ使用できる」と明示的に規定されているのであれば、「段落内」での動作については(「仕様の範囲外」なので)関知しなくてよい。

*3:既に xbb が存在する場合は extractbb の起動は行われない。