## 与已有问题的关系 这是 #992 命令边界追踪中的一个未覆盖场景,与 #1000 的机制相同。 #1000 已由 #1001 修复,但该修复注册的是 `\unit`、`\qty`、`\num`、`\si` 和 `\SI`,未包含区间命令。 本报告不依赖任何自定义宏包。 ## 复现环境 - TeX Live 2026,XeLaTeX。 - LaTeX2e:2026-06-01。 - xeCJK:2026-08-21 v3.10.6。 - siunitx:2026-09-08 v3.5.12。 - 中文字体:TeX Live 自带的 FandolSong-Regular.otf。 2026-09-22 核对的仓库 `master` 为 4f78ab2a0ebd07604ac2372c4c8eb11d92ef7b73。 其中 `xeCJK/xeCJK.dtx` 最近一次修改为 7023032e5f0057df38ca71c0d77d390228cadde9,注册函数仍未包含这三个区间命令。 下述编译测试使用本机安装的上述版本;没有把源码检查表述为重新构建整个 `master` 后的测试。 ## 最小复现 运行 `xelatex -interaction=nonstopmode -halt-on-error range-mwe.tex`: ```latex \documentclass{article} \usepackage{xeCJK} \setCJKmainfont{FandolSong-Regular.otf} \usepackage{siunitx} \listfiles \sisetup{range-phrase={--},range-units=single} \setlength{\parindent}{0pt} \begin{document} 对照:长度约为 \SI{30}{\metre},后接中文。\par 旧命令:长度约为 \SIrange{30}{70}{\metre},后接中文。\par 新命令:长度约为 \qtyrange{30}{70}{\metre},后接中文。\par 纯数字:取值约为 \numrange{30}{70},后接中文。\par 无空格:中文\SIrange{30}{70}{\metre}中文。\par 有空格:中文 \SIrange{30}{70}{\metre} 中文。\par \end{document} ``` 预期:区间输出左侧与汉字之间保留正常的 `CJKecglue`,与单值 `\SI` 的边界行为一致。 实际:`\SIrange`、`\qtyrange` 和 `\numrange` 的左侧间距丢失;在源码中手动输入普通空格不能解决。 单值 `\SI` 的对照行正常。 修复前: <img width="1237" height="1600" alt="Image" src="https://github.com/user-attachments/assets/6589c569-372d-4139-83f7-623e64354092" /> ## 临时规避方式 在导言区、加载 `xeCJK` 和 `siunitx` 后添加: ```latex \xeCJKsetup{ experiment/boundary-register={command=\SIrange,strategy=stream,mode=default}, experiment/boundary-register={command=\qtyrange,strategy=stream,mode=default}, experiment/boundary-register={command=\numrange,strategy=stream,mode=default} } ``` ## 建议修复 在 `xeCJK/xeCJK.dtx` 的 `\@@_boundary_register_siunitx:` 中,为三个区间命令补充与 `\SI` 相同的固定西文首尾边界注册,并检查命令是否存在。 同时扩展 `siunitx-ecglue01.lvt` 的回归覆盖。 区间命令是独立入口,不能依赖已有的 `\qty` 或 `\SI` 钩子自动覆盖。 建议将本 issue 与 #992 关联;修复合并并复验后,再更新长期追踪表的覆盖范围。 列表、乘积和角度命令的行为尚未在本报告中验证,不将它们列为已经确认的同一故障。 修复后: <img width="1237" height="1600" alt="Image" src="https://github.com/user-attachments/assets/4d15b9f9-f051-4a27-a103-ca7217e9f8a0" /> --- 本 issue 报告由 [Codex : gpt-6-astra] 生成,本人确认 MWE 真实有效,本人未确认建议修复策略是否有效。
Activity
github-actions commentedon Sep 22, 2026
🐛 Bug 分析 #1092
📋 详细分析
复现路径:
xeCJK和siunitx。\SI、\SIrange、\qtyrange、\numrange前后紧邻中文时的间距。CJKecglue,三个区间命令的左侧间距丢失;导言区手动注册这三个命令后可规避问题。代码定位:
unit、qty、num,以及存在时的旧命令si、SI:xeCJK/xeCJK.dtx:13342-13370。\@@_boundary_register_stream:nn通过命令前后 hook 建立 stream capture:xeCJK/xeCJK.dtx:6420-6429。unit、qty、num、si、SI,但没有三个区间命令:xeCJK/testfiles/siunitx-ecglue01.lvt:84-94。问题原因:
siunitx 的这些命令会进入数学模式排版,数学节点会遮住 xeCJK 用于边界恢复的 marker。已注册的命令在入口和出口通过固定
defaultstream capture 报告两端为西文类别,从而重建中文与命令之间的CJKecglue。\SIrange、\qtyrange和\numrange是独立入口,不会自动经过\SI或\qty的 hook;由于没有对应注册,左侧 marker 无法被恢复,普通源码空格也不能等价替代该边界恢复过程。Issue 提供的
experiment/boundary-register临时规避方式能够直接恢复这三个命令的间距,与源码遗漏注册的根因相互印证。建议在\@@_boundary_register_siunitx:中为三个区间命令增加与\SI相同的defaultstream 注册,并用命令存在性检查保持对不同 siunitx 版本的兼容;同时扩展siunitx-ecglue01.lvt的回归矩阵。该修改范围集中、逻辑明确,预计为小规模改动。本分析基于提交
4f78ab2a0ebd07604ac2372c4c8eb11d92ef7b73的源码检查及 Issue 提供的复现信息,未修改仓库或重建整个master。由 codex / gpt-5.6-sol 生成;本流程只分析,不自动修改代码。
Liam0205 commentedon Sep 22, 2026
@github-actions[bot] 笑死,AI 和 AI 在对话。
这个其实就是
experiment/boundary-register的预期场景。很高兴你找到了它。此类问题可能无法穷举,暂时不打算「发现一处,修改一处」。待集中维护期再处理此类问题。
Fenyutanchan commentedon Sep 23, 2026
AI 好啊,要我之前就只能写个 MWE 了,我可不大会整模板😂
好好好,这也不是我找的,AI 好啊!
了解了,我现在就用那个注册函数绕过去吧。
谢谢回复!
Explorer-cc commentedon Sep 23, 2026
xeCJK 文档的源码里也有大量在命令左侧的 ecglue 丢失的 edge case,都是暂时用「~」代替的,可以到时一起改动...
Liam0205 commentedon Oct 1, 2026
已复现,根因已定位
感谢报告。问题确实存在,而且范围比 issue 里列出的三个命令更大:xeCJK 只给 siunitx 的
\unit、\qty、\num和旧名\si、\SI注册了命令边界处理(#1000)。其余排版命令左侧紧接汉字时,都缺少\CJKecglue,右侧正常。绿色是汉字与 siunitx 输出之间实际排出的间距,红色竖条表示此处没有间距。
\SI、\qty两侧都是 3.33pt;\SIrange、\qtyrange、\numrange、\SIlist、\qtylist、\numlist、\qtyproduct、\numproduct、\complexnum、\complexqty、\duration、\ang左侧都是 0pt,右侧 3.33pt。仓库 master(v3.10.7 开发版)与 TeX Live 安装的 v3.10.1 结果相同。根因:siunitx 在命令内部用
$...$排版数字和单位。直接在源码写中$30$时,xeCJK 在汉字之后看到下一个记号是$,会在公式前补上\CJKecglue。汉字之后是\numrange这样的宏时,xeCJK 只留下一对边界标记(kern),等 siunitx 在宏内部进入数学模式,\mathon节点就把标记盖住,间距再也补不上。节点列表可以直接看到这一点:已注册的命令由 stream capture 在命令入口按西文(Default)首类别补上间距;其余命令都是独立的顶层入口,不会经过
\qty、\num的命令钩子,所以没有这一步。ctex-kit/xeCJK/xeCJK.dtx
Lines 13587 to 13599 in 4a6e133
修复方向:把 siunitx 输出必然以数字、单位或括号开始和结束的排版命令都注册为固定 Default 首尾的 stream capture,与
\SI相同,并先检查命令是否存在。用 issue 里的experiment/boundary-register写法注册这些命令后,宽度矩阵在四种源码空格、中文与西文上下文中都与直接输入一致,range-units=bracket、中文range-phrase等选项下也一致。\ang在 #1000 时因为“不确定应与哪种直接输入比较”而暂不注册。现在实测两种候选基准结果相同:文本中30°文与公式中$30^{\circ}$文两侧都有\CJKecglue,所以一并注册。\tablenum用于表格单元格对齐,输出带 fill glue 的盒子,不属于行内数字,不在这次范围内。接下来我会开分支修复,并扩展siunitx-ecglue01的回归覆盖。注意:修复合入后,xeCJK 会内建处理这些命令。按照
experiment/boundary-register的接口约定,对已由 xeCJK 内建处理的命令再做用户注册会报错(boundary-register-conflict),所以升级到包含修复的版本后,请删掉导言区里这三条临时注册。测量方法与复现文件
宽度矩阵:命令写法与去掉命令后的同样可见内容(例如
中\numrange{30}{70}文对中30–70文)比较,CJKecglue设为 5pt,四种源码空格00/10/01/11。未注册的命令在中文上下文里每格差 −5pt,西文上下文为 0。文件都在
gh-assets分支的issues/1092/:issue1092-mwe.tex:issue 原 MWE,补上列表、乘积、复数、时长四行。issue1092-confirm.tex:上图的源文件,左右间距由盒子宽度差算出。issue1092-matrix.tex:宽度矩阵。issue1092-nodes.tex:上面的节点列表。测试环境:XeLaTeX,TeX Live 2026,siunitx 2026-05-15 v3.5.5。
fix(xeCJK): 适配 siunitx 3.6.0 起经 \ensuremath 进入数学模式的输出 (#1092)
fix(xeCJK): siunitx 公式以 \text{...} 以外的内容结尾时,公式之后再报告 Default (#1092)
docs(llmdoc): #1092 记录 siunitx 3.6 与 \text 末项的处理、单侧比较、空输出回退的维护者决定与 #1103
fix(xeCJK): siunitx 的区间、列表等命令左侧紧接汉字时补上 \CJKecglue (#1092)
fix(xeCJK): siunitx 命令改按实际首尾报告类别,输出以汉字开头或结尾时不多补间距 (#1092)
docs(xeCJK): #1092 的 \changes 调整断行,CHANGELOG 中“;”后不再多出空格
test(xeCJK): siunitx-ecglue01 的 #1092 注释改为 auto 注册的说法
docs(llmdoc): 记录 #1092 siunitx 命令族改为 auto 注册的设计、测试、已知限制与反思
fix(xeCJK): 适配 siunitx 3.6.0 起经 \ensuremath 进入数学模式的输出 (#1092)
fix(xeCJK): siunitx 公式以 \text{...} 以外的内容结尾时,公式之后再报告 Default (#1092)
docs(llmdoc): #1092 记录 siunitx 3.6 与 \text 末项的处理、单侧比较、空输出回退的维护者决定与 #1103
fix(xeCJK): siunitx 的区间、列表等命令左侧紧接汉字时补上 \CJKecglue (#1092)
fix(xeCJK): siunitx 命令改按实际首尾报告类别,输出以汉字开头或结尾时不多补间距 (#1092)
docs(xeCJK): #1092 的 \changes 调整断行,CHANGELOG 中“;”后不再多出空格
test(xeCJK): siunitx-ecglue01 的 #1092 注释改为 auto 注册的说法
docs(llmdoc): 记录 #1092 siunitx 命令族改为 auto 注册的设计、测试、已知限制与反思
fix(xeCJK): 适配 siunitx 3.6.0 起经 \ensuremath 进入数学模式的输出 (#1092)
fix(xeCJK): siunitx 公式以 \text{...} 以外的内容结尾时,公式之后再报告 Default (#1092)
docs(llmdoc): #1092 记录 siunitx 3.6 与 \text 末项的处理、单侧比较、空输出回退的维护者决定与 #1103