アレの話。
要するに、xunicode パッケージ(fontspec の中で読み込まれる)を読み込んだ後、(何か不幸な理由があって)従来の 8 ビットのエンコーディングのフォントに切り替えると、分割不可空白の命令〈~〉がエラーになるということである。これは XeLaTeX でも LuaLaTeX でも同様の現象を示す。
\documentclass[a4paper]{article} \usepackage[T1]{fontenc} \usepackage{fontspec} \begin{document} \usefont{T1}{ptm}{m}{n}Mr.~X \end{document}
〈~〉は \nobreakspace という命令に展開されるが、これがエラーになってしまう。
! LaTeX Error: Command \nobreakspace unavailable in encoding T1.
See the LaTeX manual or LaTeX Companion for explanation.
Type H <return> for immediate help.
...
l.5 \usefont{T1}{ptm}{m}{n}Mr.~
Xエンコーディング依存命令
LaTeX の命令の一部は、エンコーディングに依存して(内部で*1)異なる動作をする。例えば、文字〈æ〉は、OT1 では符号位置 0x1A、T1 では 0xE6 にある。そこで、〈æ〉を出力する命令 \ae は次のように定義されている。((なお、エンコーディング依存命令の定義のための命令(\DeclareText〜)については、LaTeX 公式の「LaTeX2e font selection」(「texdoc fntguide」で読める)に説明がある。))
\DeclareTextSymbol{\ae}{OT1}{"1A} \DeclareTextSymbol{\ae}{T1}{"E6}
内部動作の話になるが、この時に、「OT1 での \ae の定義」は \OT1\ae という制御綴に収められている。今の場合、これは位置 "1A の chardef に等置されている。((つまり \expandafter\chardef\csname OT1\string\ae\endcsname="1A とされている。))
同様に文字〈ŋ〉を出す命令 \ng は次のように定義される。
\DeclareTextSymbol{\ng}{T1}{"AD}
ここで「OT1 での定義」は与えられていないので、実際に現在のエンコーディングが OT1 の時に \ng を実行するとエラー(「Command \ng unavailable in encoding OT1.」)になる。ここで、
\DeclareTextCommandDefault{\ng}{ng}
とするとフォールバックが定義されて、\ng の定義のないエンコーディングで \ng を使うと、「ng」が出力されるようになる。内部的には、これは、\?\ng という制御綴を定義している。
xunicode パッケージの役割
xunicode パッケージは、Unicode エンコーディング(XeLaTeX では「EU1」、LuaLaTeX では「EU2」*2である。ここでは前者で代表する。)に対する「エンコーディング依存命令」の定義を与えている。これにより、例え文字が直接入力できなくても*3従来の LaTeX の命令(\ae、\ng 等)での入力が可能になる。例えば、\ng については
\DeclareTextCommand{\ng}{EU1}{\char"014B\relax}
と定義されていて、符号位置 0x014B の文字(つまり〈ŋ〉)が出力される。勿論この際には \EU1\ng という制御綴が定義されている。((この場合、\EU1\ng は \char"014B\relax に展開されるマクロになる。なおここで、\DeclareTextCommand{\ng}{T1}{\char"014B\relax} でも同じ目的を実現できる。))
……というのが原理的な話だが、実際には xunicode では
\DeclareUTFcharacter[\UTFencname]{x014B}{\ng} % \UTFencname は XeLaTeX の場合は EU1
という命令を実行していて、この \DeclareUTFcharacter の処理の中では \EU1\ng というマクロを直接定義している。しかし、\ng が既に「エンコーディング依存命令」として定義されているならばそれで正しく動作する。
\nobreakspace の扱い
さて、\nobreakspace はある意味で U+00A0 NO-BREAK SPACE に対応すると考えられるので、xunicode では
\DeclareUTFcharacter[\UTFencname]{x00A0}{\nobreakspace}
を実行している。これは本来なら
\DeclareTextCommand{\nobreakspace}{EU1}{\char"00A0\relax}
を実行したいところである。ところがここで問題があり、\nobreakspace の既存の定義はエンコーディング依存命令として定義されているのでなく、「\leavevmode\nobreak\␣」に展開される LaTeX-protected な((つまり、\DecalreRobustCommand で定義された、ということ。この場合、\nobreakspace 自体の定義は \protect\nobreakspace␣ となり、実体の定義は \nobreakspace␣ にある。このことは今回の話とは関係がないので、この記事では仮に、\nobreakspace の定義が \leavevmode\nobreak\␣ であると見做すことにする。))命令となっている。従って、単純に「EU1 の定義を追加」という処理ができない。この場合は、\DeclareUTFcharacter は(\EU1\nobreakspace を定義した後で)次のようなコードを実行している。(xunicode.sty v0.981;263行目)
% ... but when it isn't robust, make it so
\expandafter\let\csname?-\string#8\endcsname#8\relax
\edef\next@UTF@{{\cf@encoding}%
{\expandafter\noexpand\csname?-\string#8\endcsname}}%
\expandafter\DeclareTextCommand\expandafter
{\expandafter#8\expandafter}\next@UTF@ここで #8 が \nobreakspace である。解り易くいうと次のようになる。
\nobreakspaceの元の定義\leavevmode\nobreak\␣*4を\?-\nobreakspaceに代入する。\DeclareTextCommand{\nobreakspace}{EU1}{\?-\nobreakspace}を実行する。
この結果、\nobreakspace は次のようなエンコーディング依存命令になる。
- EU1 での定義は
\leavevmode\nobreak\␣。 - それ以外では未定義でエラー。
冒頭で述べた現象の原因は、この \nobreakspace の再定義にある。
「正しい定義」を考える
しかし、xunicode の目的を考えると、正しい動作は次のようでなければならないはずである。
- EU1 での定義は
\char"00A0\relax。 - それ以外では
\leavevmode\nobreak\␣。
この「それ以外では」の部分は、先に述べたフォールバックの機構を利用すれば実現できる。そう考えると、どうも先の動作に登場する \?-\nobreakspace は \?\nobreakspace の誤り(〈-〉が余分)ではないかと推測される。また EU1 の定義(\EU1\nobreakspace)が \char"00A0\relax にならないのは、本当は直前にこの定義を行っているのに、\DeclareTextCommand 実行時にそれが上書きされてしまうからである。((fontspec から xunicode を読むと、\cf@encoding は EU1 になるのだが、これが想定されていない?))
以上のことから、次のようなコードが正しいのではないかと考えている。
% ... but when it isn't robust, make it so
\expandafter\let\csname?\string#8\endcsname#8\relax
\edef\next@UTF@{{OT1}%
{\expandafter\noexpand\csname?\string#8\endcsname}}%
\expandafter\DeclareTextCommand\expandafter
{\expandafter#8\expandafter}\next@UTF@この中の「OT1」は「EU1 以外で確実に定義済であるエンコーディング」として用いられている。次のような文書でテストしてみる。
\documentclass[a4paper]{article} \usepackage{fontspec} \showboxdepth=100 \showboxbreadth=100 % for \showbox \begin{document} % \showbox で出力内容を調べる \setbox0\hbox{A~B\usefont{T1}{ptm}{m}{n}C~D} \showbox0 % 実際にT1で出力させる \usefont{T1}{ptm}{m}{n}Mr.~X \end{document}
「Mr. X」と書かれたが PDF 文書が出力され、ログに次のような \showbox の結果が記録される。どうやら意図通りに動いているようである。
> \box0= \hbox(7.16+0.09991)x35.9699 .\EU1/lmr/m/n/10 A?B ←〈?〉は実際は U+0080 .\T1/ptm/m/n/10 C .\penalty 10000 ←↓既存の\nobreakspace .\glue 2.5 plus 1.49998 minus 0.59998 .\T1/ptm/m/n/10 D