forum icon indicating copy to clipboard operation
forum copied to clipboard

xeCJK 请教:如何取消 `\textsuperscript` 与后续汉字之间的空白

Open zepinglee opened this issue 8 months ago • 2 comments

检查清单

  • [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}
Image

链接

No response

其他信息

我尝试过:

  1. \kern 1pt\relax,但是这会导致与后续文字无法断行。
Image
  1. \allowbreak,但如果后面接句号则可能错误地断行。
Image

附件

No response

zepinglee avatar May 01 '25 05:05 zepinglee

与 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" 不能处理所有情况。

Image

完整例子

% !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 avatar May 01 '25 11:05 muzimuzhi

@muzimuzhi 非常感谢!没想到这么复杂……

zepinglee avatar May 02 '25 01:05 zepinglee