アレの話。
要するに、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