New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
xeCJKfntef: 关于下划线中出现公式的一个「解决方案」 #614
Comments
关于 \ifx<token1><token2>% \ifx does not expand tokens; it compares meaning of <token1> and <token2>
...
\expandafter \peek_after:Nw \expandafter \__xeCJK_peek_catcode_ignore_spaces_branches:w
\romannumeral 0%
\else
...
\fi 首先就是这个写法完全不是 LaTeX3 的写法,这个是 low-level TeX 的写法。我猜是历史遗留问题。 吞掉空格的原因十有八九是那个 \ifx<token1><token2>%
...
\expandafter \peek_after:Nw \expandafter \__xeCJK_peek_catcode_ignore_spaces_branches:w
\else
...
\fi
正统的 LaTeX3 写法应该是: \cs_gset_protected:Npn \__xeCJK_peek_catcode_ignore_spaces_branches:w
{
\token_if_eq_meaning:NNTF \l_peek_token \c_space_token
{
\bool_set_true:N \l__xeCJK_peek_ignore_spaces_bool
\peek_after:Nw \__xeCJK_peek_catcode_ignore_spaces_branches:w
}
{
\token_if_eq_catcode:NNTF \l_peek_token \l__xeCJK_peek_search_token
{ \__xeCJK_peek_catcode_true:w }
{ \__xeCJK_peek_catcode_false:w }
}
} 其中 |
您的代码似乎无法在下例中编译: \documentclass{ctexart}
\usepackage{xeCJKfntef}
\makeatletter
\ExplSyntaxOn
\cs_set_protected:Npn \__xeCJK_peek_catcode_ignore_spaces_branches:w
{
\token_if_eq_meaning:NNTF \l_peek_token \c_space_token
{
\bool_set_true:N \l__xeCJK_peek_ignore_spaces_bool
\peek_after:Nw \__xeCJK_peek_catcode_ignore_spaces_branches:w
}
{
\token_if_eq_catcode:NNTF \l_peek_token \l__xeCJK_peek_search_token
{ \__xeCJK_peek_catcode_true:w }
{ \__xeCJK_peek_catcode_false:w }
}
}
\ExplSyntaxOff
\makeatother
\begin{document}
好 $x$
\ExplSyntaxOn
\xeCJK_peek_catcode_ignore_spaces:NTF \c_math_toggle_token {t} {f} ~~$x$ %%:: tx
\CJKunderline{ \xeCJK_peek_catcode_ignore_spaces:NTF \c_math_toggle_token {t} {f} ~~$x$ } %%:: fx
\ExplSyntaxOff
\end{document} 代码陷入到了死循环中。 \documentclass{ctexart}
\usepackage{xeCJKfntef}
\makeatletter
\ExplSyntaxOn
\cs_set_protected:Npn \__xeCJK_peek_catcode_ignore_spaces_branches:w
{
\token_if_eq_meaning:NNTF \l_peek_token \c_space_token
{
\bool_set_true:N \l__xeCJK_peek_ignore_spaces_bool
\exp_after:wN \peek_after:Nw
\exp_after:wN \__xeCJK_peek_catcode_ignore_spaces_branches:w
\exp:w \exp_end_continue_f:w
}
{
\token_if_eq_catcode:NNTF \l_peek_token \l__xeCJK_peek_search_token
{ \__xeCJK_peek_catcode_true:w }
{ \__xeCJK_peek_catcode_false:w }
}
}
\ExplSyntaxOff
\makeatother
\begin{document}
好 $x$
\ExplSyntaxOn
\xeCJK_peek_catcode_ignore_spaces:NTF \c_math_toggle_token {t} {f} ~~$x$ %%:: tx
\CJKunderline{ \xeCJK_peek_catcode_ignore_spaces:NTF \c_math_toggle_token {t} {f} ~~$x$ } %%:: fx
\ExplSyntaxOff
\end{document} 在 UL 中同样也匹配到的是 F 分支。 实际仍需要使用 ...
\exp:w \exp_end_continue_f:w
\exp:w 0
... 才能正常工作,但这样与使用 low-level 的写法并无多大区别。 |
啊,是的,我疏忽大意了,因为没有移除,所以死循环。
但是 |
是的,我也不理解,就是尝试这样写,然后居然就能正常工作了。 我又测试了一下,不使用两个
而使用两个
|
@Sophanatprime 我简单研究了一下 \long\def\UL@on#1{...
\UL@word\@empty#1\xdef\UL@spfactor{\the\spacefactor} \UL@end * } 当你做如下测试: \ExplSyntaxOn
\CJKunderline{ \xeCJK_peek_catcode_ignore_spaces:NTF \c_math_toggle_token {t} {f} ~~$x$ }
\ExplSyntaxOff 首先那两个连续的 \UL@word \@empty \xeCJK_peek_catcode_ignore_spaces:NTF \c_math_toggle_token {t}{f}␣$x$%
\xdef\UL@spfactor{\the\spacefactor}␣\UL@end *␣% 第一行的那个 space token 就暴露在 \long\def\UL@word#1␣{\expandafter\UL@start#1␣%
...\UL@word\@empty} 所以 \expandafter \UL@start \@empty \xeCJK_peek_catcode_ignore_spaces:NTF \c_math_toggle_token {t}{f}␣%
...\UL@word\@empty 再展开一步就是 \UL@start \xeCJK_peek_catcode_ignore_spaces:NTF \c_math_toggle_token {t}{f}␣%
...\UL@word\@empty 所以 你做 \UL@word \@empty
\use:nn {\xeCJK_peek_catcode_ignore_spaces:NTF \c_math_toggle_token {t}{f}}{␣}$x$%
\xdef\UL@spfactor{\the\spacefactor}␣\UL@end *␣% 第三行才出现第一个「暴露在外」的 space token,所以 \UL@start
\use:nn {\xeCJK_peek_catcode_ignore_spaces:NTF \c_math_toggle_token {t}{f}}{␣}$x$%
\xdef\UL@spfactor{\the\spacefactor}␣%
...\UL@word\@empty 之后的 |
但是这样还无法解释为何使用两次 使用 我的理解如下: 首先贴出几个重要的代码: \cs_new_protected:Npn \xeCJK_ulem_word:nw #1 ~
{
\exp_after:wN \UL@start #1 ~ %% 注意尾部的这个空格
\exp_after:wN \if_meaning:w \exp_after:wN \UL@end #1
\exp_after:wN \__xeCJK_ulem_end:
\else:
\exp_after:wN \__xeCJK_ulem_loop:nw
\fi:
}
\cs_new_protected:Npn \__xeCJK_ulem_loop:nw
{
\reverse_if:N \if_mode_math:
\reverse_if:N \if_dim:w \tex_lastskip:D = \c_zero_dim
\skip_gset_eq:NN \UL@skip \tex_lastskip:D
\tex_unskip:D
\UL@stop \UL@leaders
\fi:
\fi:
\xeCJK_ulem_word:nw \prg_do_nothing:
}
\cs_set_eq:NN \UL@word \xeCJK_ulem_word:nw 每个 chunk 都被 但是当有两个 所以反倒是使用两个 为此,请看下例:(需要之前的两个 patch) \documentclass{ctexart}
\usepackage{xeCJKfntef}
\makeatletter
\ExplSyntaxOn
%% 需要之前 patch 的结果
\cs_gset_protected:Npn \__xeCJK_ulem_CJK_and_Boundary:w
{
\xeCJK_if_ulem_patch:TF
{
\xeCJK_peek_catcode_ignore_spaces:NTF \c_math_toggle_token
{
\xeCJK_class_group_end: %\UL@stop %% remove
\CJKecglue
%\UL@start %% remove
}
{
\bool_if:NTF \l__xeCJK_peek_ignore_spaces_bool
{
\xeCJK_class_group_end: \UL@stop
\UL@start
{ \xeCJK_make_node:n { CJK-space } }
}
{
\xeCJK_class_group_end: \UL@stop
\UL@start { \xeCJK_make_node:n { CJK } }
}
\xeCJK_make_group_tag:
}
}
{ \__xeCJK_ulem_CJK_and_Boundary:w }
}
\cs_gset_protected:Npn \__xeCJK_peek_catcode_ignore_spaces_branches:w
{
\if_meaning:w \l_peek_token \c_space_token
\bool_set_true:N \l__xeCJK_peek_ignore_spaces_bool
\exp_after:wN \peek_after:Nw
\exp_after:wN \__xeCJK_peek_catcode_ignore_spaces_branches:w
\exp:w \exp_end_continue_f:w %% add
\exp:w \exp_end_continue_f:w %% \tex_romannumeral:D 0
\else:
\if_catcode:w
\exp_not:N \l_peek_token \exp_not:N \l__xeCJK_peek_search_token
\exp_after:wN \exp_after:wN
\exp_after:wN \__xeCJK_peek_catcode_true:w
\else:
\exp_after:wN \exp_after:wN
\exp_after:wN \__xeCJK_peek_catcode_false:w
\fi:
\fi:
}
\ExplSyntaxOff
\makeatother
\begin{document}
\def\test{\uline{好 $x$ 好 $y$}\par
\CJKunderline{好 $x$ 好 $y$}}
\test
\ExplSyntaxOn
\makeatletter
\cs_set_protected:Npn \xeCJK_ulem_word:nw #1 ~
{
% \exp_after:wN \UL@start #1 ~
\use:nnn { \exp_after:wN \UL@start #1 } {~} {~} %% 增加一个空格
\exp_after:wN \if_meaning:w \exp_after:wN \UL@end #1
\exp_after:wN \__xeCJK_ulem_end:
\else:
\exp_after:wN \__xeCJK_ulem_loop:nw
\fi:
}
\cs_set_eq:NN \UL@word \xeCJK_ulem_word:nw
\ExplSyntaxOff
\test
\end{document} 可以看到,增加一个空格后将不能正确输出。 |
这里的分析是正确的。不过问题是: |
正常文字下可以自动在公式两端断行(公式右端需有空格); \documentclass{ctexart}
\usepackage{xeCJKfntef}
%%% 两个 patch
\patch
\begin{document}
\hfuzz=1pt
\overfullrule=5pt
\lineskip=2.5pt
\def\test{好。好 $k$ 好$x$ 好。$x\displaystyle\int$ 好 \relax 好\relax 好\hbox{内}好 \hbox{内}好\parbox[t]{1\ccwd}{呐\par 讷}哦。我能吞下玻璃而不伤身 $E=mc^2$ 体。}
\CJKunderline{\test}
\CJKunderline*{\test}
\test
\def\test{好。好 $k$ 好$x$ 好。$x\displaystyle\int$ 好 \relax 好\relax 好\hbox{内}好 \hbox{内}好\parbox[t]{1\ccwd}{呐\par 讷}哦。我能吞下玻璃 $E=mc^2$ 而不伤身体。}
\textbf{展开,可以自动断行:}
\expandafter\CJKunderline\expandafter{\test}
\textbf{不展开,则不能自动断行:}
\CJKunderline*{\test}
\test
\def\test{好。好 $k$ 好$x$ 好。$x\displaystyle\int$ 好 \relax 好\relax 好\hbox{内}好 \hbox{内}好\parbox[t]{1\ccwd}{呐\par 讷}哦。我能吞下玻璃而不 $E^2=m^2c^4+c^2p^2$ 伤身体,我能吞下玻璃而不伤身体。}
\CJKunderline{\test}
\CJKunderline*{\test}
\test
\end{document}
\documentclass{article}
\usepackage{ulem}
\begin{document}
\overfullrule=5pt
\parskip=5pt
\def\test{I can eat glass, it doesn't hurt me. I can eat glass, it doesn't hurt $E^2=m^2c^4+c^2p^2$ me. I can eat glass, it doesn't hurt me. I can eat glass, it doesn't hurt me.}
\expandafter\uline\expandafter{\test}
\uline{\test}
\test
\end{document} |
这里需要注意的是 ulem 参数中的空格被作为宏参数的定界符。具体就 \xeCJK_ulem_word:nw 不好 ~ 继续展开后为(一些无关分析的 token 用 \exp_after:wN \UL@start xxxxxx 不好 ~
\exp_after:wN \if_meaning:w \exp_after:wN \UL@end #1
\exp_after:wN \__xeCJK_ulem_end:
\else:
\exp_after:wN \__xeCJK_ulem_loop:nw
\fi: 其中的
\cs_new_protected:Npn \__xeCJK_ulem_loop:nw
{
\reverse_if:N \if_mode_math:
\reverse_if:N \if_dim:w \tex_lastskip:D = \c_zero_dim
\skip_gset_eq:NN \UL@skip \tex_lastskip:D
\tex_unskip:D
\UL@stop \UL@leaders
\fi:
\fi:
\xeCJK_ulem_word:nw \prg_do_nothing:
} 这里 \prg_do_nothing: $n$ xxx 继续展开为 \exp_after:wN \UL@start \prg_do_nothing: $n$ xxx ~
\exp_after:wN \if_meaning:w \exp_after:wN \UL@end #1
\exp_after:wN \__xeCJK_ulem_end:
\else:
\exp_after:wN \__xeCJK_ulem_loop:nw
\fi: 注意这里的 % \UL@start: start of each chunk. It gives two levels of grouping.
% Each chunk is ended by \UL@stop. Local intermissions go like
% \UL@stop...\UL@start.
\def\UL@start{\setbox\UL@box\hbox\bgroup\everyhbox{\UL@hrest}%
% the following are to cope with stops (\ ,\- etc) within extra braces
\let\UL@start\@empty \def\UL@unegroup{\bgroup\bgroup}\let\UL@leadtype\@empty
\bgroup \kern-3sp\kern3sp % kerns so I can test for beginning of list
\if@ignore \global\@ignorefalse \ignorespaces \fi} 所以此时它展开为空,接下来,之前的 |
我安装了 ad44c66 的 \documentclass{article}
\usepackage{xeCJKfntef}
\begin{document}
\CJKunderline{张量 $A$ 的维度}
\CJKunderline{张量 \,$A$ 的维度}
\end{document} 文字和数学公式的间距并没有正常添加,是没完全解决还是? |
@xkwxdyy 你看一下 log,确保你的例子用的是开发版本。 |
我这里用开发版本测试的表现正常 \documentclass{article}
\usepackage{xeCJKfntef}
\listfiles
\begin{document}
\CJKunderline{张量 $A$ 的维度}
\CJKunderline{张量$A$ 的维度}
\end{document}
|
抱歉,我放在 Package: expl3 2022-07-15 L3 programming layer (loader)
(/usr/local/texlive/2022/texmf-dist/tex/latex/l3backend/l3backend-xetex.def
File: l3backend-xetex.def 2022-07-01 L3 backend support: XeTeX
\g__graphics_track_int=\count189
\l__pdf_internal_box=\box51
\g__pdf_backend_object_int=\count190
\g__pdf_backend_annotation_int=\count191
\g__pdf_backend_link_int=\count192
))
Package: xeCJKfntef 2022/07/28 v3.9.0 xeCJK font effect
(./xeCJK.sty
Package: xeCJK 2022/07/28 v3.9.0 Typesetting CJK scripts with XeLaTeX
(/usr/local/texlive/2022/texmf-dist/tex/latex/ctex/ctexhook.sty
Package: ctexhook 2022/07/14 v2.5.10 Document and package hooks (CTEX)
) (/usr/local/texlive/2022/texmf-dist/tex/latex/l3packages/xtemplate/xtemplate.sty
Package: xtemplate 2022-06-22 L3 Experimental prototype document functions |
xeCJK
版本 3.8.8,TeXLive 2022,expl3
:Released 2022-04-10
。如下代码在 XeLaTeX 下编译:
第一个
\uline
和第一个\CJKunderline
将报错。且第二个\uline
和\CJKunderline
中中文和公式之间的空格将被吃掉,本意不应如此。而且,当且仅当
$
前的字符类为CJK
时会出现错误,也就是CJK and Boundary
的情况,其它情况则不会出现此错误。比如西文字符或中文标点均不会有此错误。CJK
+空格(catcode=10)+Boundary
只是吸收掉了空白。此 issue 可见于 #530 。
涉及到如下三个宏:
\__xeCJK_ulem_CJK_and_Boundary:w
、\__xeCJK_ulem_glue:n
、\__xeCJK_peek_catcode_ignore_spaces_branches:w
,将第一个和第三个修改为如下:
移除
\UL@stop
和\UL@start
(位置见上方注释)。增加
\exp:w \exp_end_continue_f:w
,但不删除\tex_romannumeral:D 0
(位置见上方注释)。其它地方未作改动。
这样居然能正确编译上方的代码了。这是我没有想到的。本应不修改就可正常工作。
这里贴出上面提到的第二个宏的代码,以便随后的分析。
且在
UL
内部,\CJKecglue
为:我的分析是这样的(不使用上述 patch):对于
不好$n$
,直接就是CJK and Boundary
,匹配到了math toggle
,应该就插入\UL@stop \CJKecglue \UL@start
。但是\CJKecglue
此时会使用\__xeCJK_ulem_glue:n
的两个 T 分支,于是再次出现了\UL@stop
造成组不匹配。这是我理解的出错的原因。当然所谓的解决办法就是删掉
\UL@stop
和\UL@start
。经过我的测试这项修改并未引起其它错误,可能我没有考虑到所有情况。第二个是,
不好 $n$
,同样执行到了CJK and Boundary
,它和上述结果应该相同,因为 peek catcode 时忽略了空格。但实际并非如此,请看下列代码:其中第一个结果为 T 分支,第二个结果为 F 分支。这表明在 UL 中,原有的 peek catcode 宏并未正确处理,使用(第二个) patch 后,则可”正确“处理。
未 patch 时,经过我的 debug,发现在 UL 中, peek catcode 的最后一个
\peek_after:Nw
peek 到的是\expandafter
,在正常文本中则能正确 peek 到$
,这是第二个我不理解的地方。正因如此,执行到了错误的分支,于是
CJK
与公式之间并未插入空白。patch 仅仅是在
\romannumeral 0 \else ...
前增加了\exp:w \exp_stop_end_continue:w
,实际也就是增加了一个\romannumeral
,则在 UL 中和正常文本中都能正确 peek 到$
,我不理解为什么需要加上这个\exp:w
。总之,在我看来原来的代码应该能够正确处理,但是却没有达到想要的效果,使用修改后的代码则能够正确处理。
最后附上一个 work 的例子:
The text was updated successfully, but these errors were encountered: