Repository navigation
jiazhu: 传统直排半高夹注正文对齐求助 #714
Description
Activity
请提供一个简单又完整的例子。
Reacted by Explorer1. 对应附图左边情形的完整代码
\documentclass[a4paper]{book} \usepackage{xeCJK} \usepackage{atbegshi} \usepackage{jiazhu} \defaultCJKfontfeatures { RawFeature= { vertical:+vert:+vhal } } \setCJKmainfont{SimSun} \jiazhuset{ beforeskip=0pt, afterskip=0pt, ratio=0.5 } \textwidth=420pt \parindent=0pt \AtBeginShipout{ \global\setbox\AtBeginShipoutBox\vbox{% \special{pdf: put @thispage <</Rotate 90>>}% \box\AtBeginShipoutBox}% } \begin{document} \fontsize{20pt}{20pt}\selectfont 康誥曰克明德 \jiazhu{康誥周書克能也} 大甲曰顧諟天之明命 \jiazhu{大讀作泰諟古是字大甲商書顧謂常目在之也諟猶此也或曰審也 天之明命即天之所以與我而我之所以爲德者也常目在之則無時不明 矣} 帝典曰克明峻德 \jiazhu {峻書作俊帝典堯典虞書峻大也皆自明也結所引書皆言自明 己德之意} \end{document}
2. 对应附图中间情形的代码
\documentclass[a4paper]{book} \usepackage{xeCJK} \usepackage{atbegshi} \usepackage{jiazhu} \defaultCJKfontfeatures { RawFeature= { vertical:+vert:+vhal } } \setCJKmainfont{SimSun} \ExplSyntaxOn \cs_set_protected:Npn \__jiazhu_dim_normalize:N #1 { \int_set:Nn \l_tmpa_int { \dim_ratio:nn { #1 } { \l__jiazhu_unit_dim } } \int_if_even:nF { \l_tmpa_int } { \int_incr:N \l_tmpa_int } \dim_set:Nn #1 { \l__jiazhu_unit_dim * \l_tmpa_int } } \cs_set_protected:Npn \__jiazhu_extract_max_width:N #1 { \dim_set:Nn \l__jiazhu_width_dim { \box_wd:N #1 } \dim_set_eq:NN \l__jiazhu_max_dim \l__jiazhu_width_dim } \ExplSyntaxOff \jiazhuset{ beforeskip=0pt, afterskip=0pt, ratio=0.5 } \textwidth=420pt \parindent=0pt \AtBeginShipout{ \global\setbox\AtBeginShipoutBox\vbox{% \special{pdf: put @thispage <</Rotate 90>>}% \box\AtBeginShipoutBox}% } \begin{document} \fontsize{20pt}{20pt}\selectfont 康誥曰克明德 \jiazhu{康誥周書克能也} 大甲曰顧諟天之明命 \jiazhu{大讀作泰諟古是字大甲商書顧謂常目在之也諟猶此也或曰審也 天之明命即天之所以與我而我之所以爲德者也常目在之則無時不明 矣} 帝典曰克明峻德 \jiazhu {峻書作俊帝典堯典虞書峻大也皆自明也結所引書皆言自明 己德之意} \end{document}
3. 对应附图右侧情形的代码
\documentclass[a4paper]{book} \usepackage{xeCJK} \usepackage{atbegshi} \usepackage{jiazhu} \defaultCJKfontfeatures { RawFeature= { vertical:+vert:+vhal } } \setCJKmainfont{SimSun} \jiazhuset{ beforeskip=0pt, afterskip=0pt, ratio=0.5 } \textwidth=420pt \parindent=0pt \AtBeginShipout{ \global\setbox\AtBeginShipoutBox\vbox{% \special{pdf: put @thispage <</Rotate 90>>}% \box\AtBeginShipoutBox}% } \begin{document} \fontsize{20pt}{20pt}\selectfont 康誥曰克明德 \jiazhu{康誥周書克能也} 大甲曰顧諟天之明命 \jiazhu{大讀作泰諟古是字大甲商書顧謂常目在之也諟猶此也或曰審也 天之明命即天之所以與我而我之所以爲德者也常目在之則無時不明矣} \hspace{0.5em}帝典曰克明峻德 \jiazhu {峻書作俊帝典堯典虞書峻大也皆自明也結所引書皆言自明 己德之意} \end{document}
经尝试,得到一个无需手工插入空白解决方案的,修改
\cs_set_protected:Npn定义增加一个判断。\cs_set_protected:Npn \__jiazhu_processing: { \__jiazhu_split_lines: \__jiazhu_put_closing_box: \__jiazhu_good_break: \__jiazhu_hskip:N \l__jiazhu_after_skip_tl + \int_set:Nn \l_tmpa_int { \dim_ratio:nn { \l__jiazhu_max_dim } { \l__jiazhu_unit_dim } } + \int_if_odd:nT \l_tmpa_int { \skip_horizontal:n { \l__jiazhu_unit_dim } } \group_end: \tex_ignorespaces:D }
Reacted by Explorer确认这个问题仍然存在,并实测比较了本 issue 里已提出的两个方案。环境:
xelatex(TeX Live 2026),jiazhu.sty由l3build unpack生成;本机无 SimSun,用Noto Serif CJK SC替代(\l__jiazhu_unit_dim只取\f@size pt而不查字体度量,所以换字体不影响机制)。(灰线为 20pt 正文字网格,粗竖线标出夹注结束、正文恢复处。复现文件
jiazhu714-mwe.tex、盒宽扫描探针
jiazhu714-sweep-width.tex,
连同上图源码见issues/714/。)复现与根因
跑报告者「附图左边情形」的完整代码,用
pdftotext -bbox提字形坐标后按 20pt 正文网格归一:col= 0.000 startGrid= 0.000 n=6 康誥曰克明德 col= 0.000 startGrid= 8.000 n=9 大甲曰顧諟天之明命 col= 1.209 startGrid= 10.500 n=7 帝典曰克明峻德 <-- 偏离网格 0.5 格第三段正文起点偏离整数网格恰好半个正文字宽(10pt),与报告者描述一致。
根因是夹注最终盒宽总是夹注字宽的整数倍,而不是正文字宽的整数倍。
\__jiazhu_dim_normalize:N只用\l__jiazhu_unit_dim(= 夹注字宽)做取整基准,正文字宽\l__jiazhu_outer_unit_dim完全没参与。扫描夹注字数 n=1..12(ratio=0.5、正文 20pt、夹注 10pt、lines=2):n 1 2 3 4 5 6 7 8 9 10 11 12 盒宽/夹注字宽 1 1 2 2 3 3 4 4 5 5 6 6 即
ceil(n/2)个夹注字宽。ratio=0.5时一个正文字宽 = 两个夹注字宽,所以该值为奇数即错位半个正文字,也就是 n mod 4 ∈ {1,2}——与报告者「夹注字数除以 4 余数为 1、2」逐字吻合。两个方案的实测比较
qinglee 的 patch(向上取偶) 对齐确实解决了,但代价是行长分配不均:
n 1 2 3 4 5 6 7 8 9 10 11 12 盒宽/夹注字宽 2 2 2 2 4 4 4 4 6 6 6 6 \jiazhu{一二三四五}(n=5)实测分行:基线是首行 3 字 + 次行 2 字,patch 后变成首行 4 字 + 次行 1 字。因为它改的是排版宽度,放宽到偶数倍后 TeX 把多出的位置全给首行,所以必然影响分行——正是报告者说的「第一行比第二行多出 2 个或 3 个字符」。报告者自己的第二方案(末尾补一个夹注字宽) 盒宽序列和分行都与基线完全一致(n=5 仍是 3+2),因为它改的是盒子之后的间距而非排版宽度。这是它优于 qinglee 方案的地方。
但它会在正文行接近排满时把行推到溢出。扫描
\hsize(夹注 5 字,盒宽 3 个夹注字宽,判为奇数所以补 10pt):\hsize基线 Overfull补白后 200pt 无 无 205pt 5.0pt 15.0pt 210pt 无 10.0pt \hsize=210pt是干净的反例:基线不溢出,补白使它溢出正好一个夹注字宽。补的是固定长度\skip_horizontal:,不可收缩,且位于\__jiazhu_good_break:的\penalty -100之后——断在那个 penalty 时它被丢弃、对齐失效;不断在那里时又强行占位、可能顶出溢出。要落地必须处理这两端。该方案的判别式本身是可靠的:它用
\l__jiazhu_max_dim而非最终盒宽判奇偶,我扫了 30 组(\textwidth200pt/120pt,夹注 1..57 字,含大量跨行),两者奇偶始终一致,0 例不符。跨行情形下补白也照样生效。两个坑,两个方案都没绕过
括弧盒子使两者都失效。 括弧不进入
\l__jiazhu_max_dim,但进入正文行总宽度:设置 opening宽closing宽closing=】0.0pt 20.0pt opening=【,closing=】7.6pt 20.0pt 】是 20.0pt(两个夹注字宽,不改变奇偶,所以无害),但【是 7.6pt,既不是夹注字宽也不是正文字宽的整数倍(bracketratio=2使括弧字号为 20pt,但【在该字体里 advance 不是全角)。实测正文行偏离网格:情形 行宽/正文字宽 偏离 报告者方案,无括弧 9.000 0.000 ✓ 报告者方案, closing=】10.001 +0.001 ✓ 报告者方案, opening=【,closing=】10.379 +0.379 ✗ 所以两个方案都只在不用
opening括弧时成立。本 issue 的样例没用括弧,#713 用的是shortcut=【】(走 shortcut 分隔符而非opening/closing键),所以两位报告者都没撞上。要通用地修,判别式得按「夹注排出后正文行的累计宽度」取余,而不是按夹注内容盒宽。两者都硬编码「偶数」,隐含假设
ratio=0.5。ratio=2/3时一个正文字宽 = 1.5 个夹注字宽,「偶数倍夹注字宽」不等于「整数倍正文字宽」,判别式失效。一般化的写法是直接按正文字宽取余补齐:\dim_set:Nn \l_tmpa_dim { \l__jiazhu_outer_unit_dim - \dim_eval:n { <盒宽> - \l__jiazhu_outer_unit_dim * \int_div_truncate:nn { <盒宽> } { \l__jiazhu_outer_unit_dim } } } \dim_compare:nNnF \l_tmpa_dim = \l__jiazhu_outer_unit_dim { \skip_horizontal:N \l_tmpa_dim }
实测
ratio=0.5时补 10pt/20pt,与报告者方案等效;ratio=2/3(夹注字宽 13.33333pt)时补 6.66667pt/13.33334pt/0.00002pt,把盒宽补齐到 20pt 整数倍。注意ratio=2/3下夹注字宽本身不是 pt 整数,取余会残留 0.00002pt 量级舍入,需判断是否可接受。未验证的部分
- 未在真实 SimSun 上验证(机制与字体度量无关,但没实测)。
- 上面的
ceil(n/2)是lines=2的特例,未验证lines>=3的规律。 - 括弧影响已实测,但没有试出一个能同时处理括弧与跨行的判别式写法。
- 「补白被行尾断点丢弃」只做了推理(补白在
\penalty -100之后,TeX 断在 penalty 处会丢弃其后 glue),没构造出实际断在那里的样例实测。溢出那一侧已实测。 - 还有个前置问题想请维护者定:这该做成默认行为还是新选项?传统直排需要它,横排夹注不需要。另外
jiazhu目前没有任何自动回归测试(无testfiles/、无test/,build.lua未设testfiledir,dtx 末尾%<*test>只是目视样例,没有.tlg基线),上面任何改动都会改变排版输出,没有基线难以判断回归。
- added a commit that references this issue
on Aug 3, 2026



中文传统直排要求正文垂直方向对齐。在ratio=0.5,夹注字符尺寸是正文的一半,当夹注字数除以 4 余数为 1、2 的时候,夹注本身的宽度是夹注字体尺寸的奇数倍,就会造成正文在垂直对齐错位半个正文字符。(附图左边情形)我最初解决这个问题的方法是在夹注末尾手动插入全角空格,强行使得夹注宽度是夹注字符尺寸的偶数倍。
感谢 @qinglee 给出的一个 Patch,保证了夹注的宽度总是夹注字体尺寸的偶数倍,解决了正文垂直对齐的问题。(附图中间情形)这个方案会出现夹注的第一行比第二行多出 2 个或 3 个字符的情形。但是总是感觉不够美观。
我现在的临时手工解决方案是不再采用这个 patch,手动在夹注末尾和正文之间插入
\hspace{0.5em}增加空白来对齐。(附图右边的情形)是否可以通过判断,夹注本身宽度为夹注字符尺寸奇数倍的时候,自动在夹注末尾和正文之间自动插入一个夹注字符高度的空白?本身水平有限,虽然读了夹注包的源代码,没有读懂而无从下手。请求帮助。感谢。