マクロツイーター

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

例の和欧文間空白が入らないアレ

次の現象も質問1の問題が原因だと考えています。
siunitxパッケージの\SIコマンドの後のスペースの問題です。
次の文書をpLaTeXでタイプセットすると、「1 m s-1」の後だけスペースが入りません。
pLaTeXで\SIコマンドの後にスペースが正しく入るようにするよい用法がありましたら教えてください。

上記のような場合で、「ゴースト」を使うと綺麗に解決できることがある。「ゴースト」を使った解決法は、TeX Q & A の別の質問への回答で紹介したことがあるが、いざという時に有用かも知れないのでここで紹介しておく。

以下のような状況において、「ゴースト」の使用が有用だと思われる。

  • あるユーザ命令(マクロ)の出力について、pTeX での和文処理において単純な欧文文字(英字)と同じ扱いになってほしいが、pTeX のバグや仕様のせいでそうならない。
  • マクロの定義を修正することはできるが、使用時に当該の命令の周りに調節用の命令を置くことは行えない(行いたくない)。
原理

「(欧文)ゴースト」というのは、「幅がゼロで、字形が空である(つまり目に見えない)欧文文字」のことである。例えば T1 エンコーディングの符号位置 23 にある「compound word mark」という文字(LICR は \textcompwordmark)がこの条件を満たす。以下では \EG という命令で「欧文ゴースト」の文字が入力できるとする。大事なことは、\EG がカーンやグル―ではなく欧文文字であるということである。

さて、今の問題は、(改修前の)pTeX では

\def\mybox{\hbox{$42$}}
ああ\mybox ああ

とした時に \mybox の前後に和欧文間空白(\xkanjiskip)が入らないことであった。これを入るようにするには \mybox の定義をどう修正すればよいか。((ただし「$4$ が hbox に入っている」という状況は変えられないという前提で。))もちろん前後が和文文字でない場合は和欧文間空白が入ってはいけない。

ここで仮に和文文字と接する箇所に欧文文字がある、つまり例えば

\def\mybox{A\hbox{$42$}A}
ああ\mybox ああ

のようになっていたとすると、今度は望み通りに \mybox の前後に和欧文間空白が入る。無論、このままでは出力に余計な「A」が入っているので解決にはならない。しかし、ここで「A」の代わりにゴーストを使うとどうなるか。

\def\mybox{\EG\hbox{$42$}\EG}
ああ\mybox ああ

ゴーストは歴とした欧文文字だからやはり和欧文間空白が入る。しかもゴースト自身は何も出力せず幅も持たないので、結果的に所望の和欧文間空白だけが入った形の出力が得られたことになる。

以上から判るように、前後にゴーストを置くことで、\mybox があたかも「普通の欧文文字」であるかのように振る舞わせることができる。

パッケージ

で、作ってみた。

このパッケージは以下の機能を提供する。和文ゴーストについての話は今回は省くが、要するにテキストを「普通の和文文字」であるように振る舞わせるためのものである。

  • \eghostguarded{<テキスト>} : テキストの前後に「欧文ゴースト」を置いて出力する。ただし数式モードでは単純にテキストを出力する。
  • \jghostguarded{<テキスト>} : テキストの前後に「和文ゴースト」を置いて出力する。ただし数式モードでは単純にテキストを出力する。

使用例。

\documentclass[a4paper]{jsarticle}
\usepackage{pxghost}
\begin{document}
\def\mybox{\hbox{$42$}}
ああ\mybox ああ\par       % NG
\def\mybox{\eghostguarded{\hbox{$42$}}}
ああ\mybox ああ\par       % OK
ああ\hbox{\mybox}ああ\par % さらにhboxに入れてもOK
\end{document}

siunitx の提供する \SI 命令にゴーストによる補正を加えた \xSI 命令を定義する例。((なお、この出力の 2 行目では \SI の前後ともに和欧文間空白は入っていない。後ろに空きが入るのは \scriptspace のためである。4 行目は \scriptspace と和欧文間空白の両方が入っているが、これは $x^2$が と書いたときと同じ挙動である。))

\documentclass[a4paper]{jsarticle}
\usepackage{siunitx}
\usepackage{xparse}
\NewDocumentCommand\xSI{o m o m}{%
  \eghostguarded{%
    \SI[#1]{#2}[#3]{#4}%
  }%
}
\begin{document}\SI{1}{\m}\par\SI{1}{\m\per\s}\par\xSI{1}{\m}\par\xSI{1}{\m\per\s}\par
\end{document}
余談

なお、本題とは全く無関係なことであるが、xparse の \NewDocumentCommand で定義された命令にフックをかけようとする場合、次のようにすると失敗する。(\SI が無限ループに入って「capacity exceeded」エラーになる。)

\let\xx@org@SI\SI
\RenewDocumentCommand\SI{o m o m}{%
  \eghostguarded{%
    \xx@org@SI[#1]{#2}[#3]{#4}%
  }%
}

この原因は、「\NewDocumentCommand で定義された命令(を表すマクロ)は、内部で『自身の名前を加工した別の名前』のマクロを呼んでいるから」である。これは丁度 \DeclareRobustCommand と同じ状況であるので、そちらの内部動作を知っている人なら何故動かないかが推測できるだろう。以前に zxjatype 0.6 版の実装でこの問題でハマったことがあるので紹介しておく。(zxjatype のソースにその時使った回避策が載っている。)