xeCJK 请教:如何取消 `\textsuperscript` 与后续汉字之间的空白
检查清单
- [x] 我已在 issues 中进行搜索(包括已关闭的问题)
操作系统
macOS 13.7
TeX 发行版
TeX Live 2025
描述问题
上标式引文标注如果后续紧跟汉字,xeCJK 会自动插入多余的空白。这是因为其底层 \textsuperscript 是通过数学环境实现的: \m@th\ensuremath{^{\mbox{\fontsize\sf@size\sf@size#1}}}},xeCJK 会自动处理数学式与汉字之间的空白。请问如何取消这一空白?
最小工作示例(MWE)
\documentclass{article}
\usepackage{xeCJK}
% `natbib` 的 `\NAT@citesuper` 简化版
\newcommand\supercite[1]{%
\unskip
\kern 1pt\relax
\textsuperscript{[#1]}%
% \kern 1pt\relax % 方案 1
% \allowbreak % 方案 2
}
\begin{document}
\fbox{
\begin{minipage}{9em}
劳伦衣普桑,劳伦\supercite{1}衣普桑。\par
劳伦衣普桑,劳伦衣\supercite{1}普桑。\par
劳伦衣普桑,劳伦\supercite{1}。衣普桑\par
劳伦衣普桑,劳伦衣\supercite{1}。普桑\par
劳伦衣普桑:“劳。”\supercite{1}伦衣普桑\par
劳伦衣普桑:“劳伦。”\supercite{1}衣普桑\par
\end{minipage}
}
\end{document}
链接
No response
其他信息
我尝试过:
-
\kern 1pt\relax,但是这会导致与后续文字无法断行。
-
\allowbreak,但如果后面接句号则可能错误地断行。
附件
No response
与 https://github.com/CTeX-org/ctex-kit/issues/491 相似。
我照着 https://github.com/CTeX-org/ctex-kit/issues/491#issuecomment-611502652 的提示抄了一个("after 1" 组),第三组例子(劳伦衣普桑:“劳伦。”\supercite{1}衣普桑)仍然有与后续汉字之间的额外空白,其他两组似乎没问题。
为了修复第三组例子,我从 xeCJK.dtx 找了几个看起来相关的宏,瞎鼓捣了 "after 2" 组:当 \supersite 前一个 node 是标点时,假装它的最后一个 node 是 CJK node(所有可能的 node 种类见这里)。这时,似乎三组例子都好了。但我只是瞎鼓捣,不确切知道 \lastkern 和 \xeCJK_if_last_node:nTF 为 true 之间的关系。
也可能,第三组例子里,\supercite 与后续汉字之间应该有空白。它是默认的全角式标点样式下,标点和汉字之间的空白(和 xeCJK 处理标点宽度的方式有关,见 https://github.com/CTeX-org/ctex-kit/issues/511 )。原来是「<标点><空白><汉字>」,现在是「<标点>\supercite{...}<空白><汉字>」。第四组例子展示了,"after 2" 不能处理所有情况。
完整例子
% !TeX program = xelatex
\documentclass{article}
\usepackage{xeCJK}
\usepackage{xpatch}
\ExplSyntaxOn
% `natbib` 的 `\NAT@citesuper` 简化版
\newcommand\supercite[1]{%
\unskip
\kern 1pt\relax
\textsuperscript{[#1]}%
% \kern 1pt\relax % 方案 1
% \allowbreak % 方案 2
}
\dim_new:N \l__my_xeCJK_tmp_dim
\tl_new:N \l__my_xeCJK_restore_node_tl
\seq_new:N \g__my_xeCJK_restore_node_seq
\cs_new_protected_nopar:Npn \__my_xeCJK_save_node:
{
\scan_stop:
\tl_clear:N \l__my_xeCJK_restore_node_tl
\dim_compare:nNnF \tex_lastkern:D = \c_zero_dim
{ \__my_xeCJK_save_node_aux: }
\seq_gpush:No \g__my_xeCJK_restore_node_seq { \l__my_xeCJK_restore_node_tl }
}
\cs_new_protected_nopar:Npn \__my_xeCJK_restore_node:
{
\seq_gpop:NNTF \g__my_xeCJK_restore_node_seq \l__my_xeCJK_restore_node_tl
{ \l__my_xeCJK_restore_node_tl }
{ \BOOM }
}
\cs_new_protected_nopar:Npn \__my_xeCJK_save_node_aux:
{
\dim_set_eq:NN \l__my_xeCJK_tmp_dim \tex_lastkern:D
\tex_unkern:D
\dim_compare:nNnTF \tex_lastkern:D = { - \l__my_xeCJK_tmp_dim }
{
\tl_set:Ne \l__my_xeCJK_restore_node_tl
{
\tex_kern:D \dim_use:N \tex_lastkern:D \exp_stop_f:
\tex_kern:D \dim_use:N \l__my_xeCJK_tmp_dim \exp_stop_f:
}
\tex_unkern:D
}
{ \tex_kern:D \l__my_xeCJK_tmp_dim }
}
\ExplSyntaxOff
\NewDocumentCommand{\test}{m}{%
\fbox{
\begin{minipage}[t]{9em}
#1\par
劳伦衣普桑,劳伦\supercite{1}衣普桑。\par
劳伦衣普桑,劳伦衣\supercite{1}普桑。\par\bigbreak
劳伦衣普桑,劳伦\supercite{1}。衣普桑\par
劳伦衣普桑,劳伦衣\supercite{1}。普桑\par\bigbreak
劳伦衣普桑:“劳。”\supercite{1}伦衣普”桑\par
劳伦衣普桑:“劳伦。”\supercite{1}衣普”桑\par\bigbreak
劳伦衣普桑:“劳。”\supercite{1}“伦衣普“桑\par
劳伦衣普桑:“劳伦。”\supercite{1}“衣普“桑\par
\end{minipage}
}
}
\begin{document}
\test{before}\quad
%
%
\ExplSyntaxOn
\AddToHook{cmd/supercite/before}{\__my_xeCJK_save_node:}
\AddToHook{cmd/supercite/after} {\__my_xeCJK_restore_node:}
\ExplSyntaxOff
%
\test{after 1}\quad
%%
\ExplSyntaxOn
\cs_set_protected_nopar:Npn \__my_xeCJK_save_node:
{
\scan_stop:
\tl_clear:N \l__my_xeCJK_restore_node_tl
\dim_compare:nNnTF \tex_lastkern:D = \c_zero_dim
{
\xeCJK_if_last_punct:TF
{
\tl_set:Ne \l__my_xeCJK_restore_node_tl
{ \xeCJK_make_node:n { CJK } }
}
{}
}
{ \__my_xeCJK_save_node_aux: }
\seq_gpush:No \g__my_xeCJK_restore_node_seq { \l__my_xeCJK_restore_node_tl }
}
\ExplSyntaxOff
%
\test{after 2}\quad
\end{document}
@muzimuzhi 非常感谢!没想到这么复杂……