如何实现符合中文字距的下划线和下划波浪线?

发布于 2025-08-19 21:38:03

我尝试使用ulem包,发现在相邻的文本加上连续的下划直线或波浪线,会出现直线或波浪线粘连在一起的问题,而手动加上空格,虽然会避免粘连的问题,但是又改变了字距。有没有符合中文字距的下划线和波浪线的方案,要求连续的下划线不能粘连,需要断开。

\documentclass{article}

\usepackage[normalem]{ulem}
\usepackage{xeCJK}

\setlength{\ULdepth}{2.4pt}

\begin{document}
    

    \noindent
    史记 赵世家
    
    \noindent
    \uwave{史记}\uwave{赵世家}
    
    \noindent
    \uline{史记}\uline{赵世家}
    
    \noindent
    \uwave{史记} \uwave{赵世家}
    
    \noindent
    \uline{史记} \uline{赵世家}
    
\end{document}

image.png

查看更多

关注者
0
被浏览
270
3 个回答
U156235
U156235 2025-08-20
这家伙很懒,什么也没写!

我调教Gemini,使用tikz给相邻两段中文绘制下划线或波浪线,下划线/波浪线不粘连了,也没有错误的中文字距的问题,但是产生一些新的问题,问得Gemini无解:

\documentclass{article}
\usepackage{tikz}
\usetikzlibrary{decorations.pathmorphing, calc}
\usepackage{xcolor}
\usepackage{xeCJK}

\begin{document}
    
    % 测量各个文本部分的宽度
    \newlength{\widthA}
    \newlength{\widthB}
    \newlength{\widthC}
    \newlength{\widthD}
    
    \settowidth{\widthA}{史}
    \settowidth{\widthB}{管子君臣}
    \settowidth{\widthC}{中庸}
    \settowidth{\widthD}{及}
    
    % 定义一个新命令,用于绘制整行并精确对齐
    \newcommand{\myWaveLine}[4]{
        \begin{tikzpicture}[baseline=(c.base)]
            % 将所有文本放在一个节点中
            \node (c) [inner sep=0pt, at={(0,0)}, anchor=west] {#1#2#3#4};
            
            % 绘制第一段波浪线
            \draw[decorate, decoration={snake, amplitude=0.5pt, segment length=1.5pt}, line width=0.3pt]
            ($(c.south west)+(\widthA,-2pt)$) -- ($(c.south west)+(\widthA+\widthB,-2pt)$);
            
            % 绘制第二段波浪线
            \draw[decorate, decoration={snake, amplitude=0.5pt, segment length=1.5pt}, line width=0.3pt]
            ($(c.south west)+(\widthA+\widthB,-2pt)$) -- ($(c.south west)+(\widthA+\widthB+\widthC,-2pt)$);
            
            % 使用白色矩形创建间隙,并遮盖不完美的波形
            \fill [white, opacity=1.0] ($(c.south west)+(\widthA-0.2pt, -10pt)$) rectangle ($(c.south west)+(\widthA+0.2pt, 10pt)$);
            \fill [white, opacity=1.0] ($(c.south west)+(\widthA+\widthB-0.4pt, -10pt)$) rectangle ($(c.south west)+(\widthA+\widthB+0.4pt, 10pt)$);
        \end{tikzpicture}
    }
    
    \noindent
    管子君臣中庸
        
    \noindent
    史管子君臣中庸及
    
    \noindent
    \myWaveLine{史}{管子君臣}{中庸}{及}
    
    % 移除 tikzpicture 引入的额外间距
    \vspace{-5pt}
    \noindent
    史管子君臣中庸及
    
\end{document}

st1.jpg

这段代码中,在使用noindent,绘制波浪线之后,无法左对齐;

\documentclass{article}
\usepackage{tikz}
\usetikzlibrary{decorations.pathmorphing, calc}
\usepackage{xcolor}
\usepackage{xeCJK}

\begin{document}
    
    % 测量各个文本部分的宽度
    \newlength{\widthA}
    \newlength{\widthB}
    \newlength{\widthC}
    \newlength{\widthD}
    
    \settowidth{\widthA}{史}
    \settowidth{\widthB}{管子君臣}
    \settowidth{\widthC}{中庸}
    \settowidth{\widthD}{及}
    
    % 定义一个新命令,用于绘制整行并精确对齐
    \newcommand{\myWaveLine}[4]{
        \begin{tikzpicture}[baseline=(c.base)]
            % 将所有文本放在一个节点中
            \node (c) [inner sep=0pt, at={(0,0)}, anchor=west] {#1#2#3#4};
            
            % 绘制第一段直线
            \draw[line width=0.3pt]
            ($(c.south west)+(\widthA,-2pt)$) -- ($(c.south west)+(\widthA+\widthB,-2pt)$);
            
            % 绘制第二段直线
            \draw[line width=0.3pt]
            ($(c.south west)+(\widthA+\widthB,-2pt)$) -- ($(c.south west)+(\widthA+\widthB+\widthC,-2pt)$);
            
            % 使用白色矩形创建间隙,并遮盖不完美的直线
            \fill [white, opacity=1.0] ($(c.south west)+(\widthA-0.2pt, -10pt)$) rectangle ($(c.south west)+(\widthA+0.2pt, 10pt)$);
            \fill [white, opacity=1.0] ($(c.south west)+(\widthA+\widthB-0.4pt, -10pt)$) rectangle ($(c.south west)+(\widthA+\widthB+0.4pt, 10pt)$);
        \end{tikzpicture}
    }
    
    \noindent
    管子君臣中庸
    
    \noindent
    史管子君臣中庸及
    
    \noindent
    \myWaveLine{史}{管子君臣}{中庸}{及}
    
    % 移除 tikzpicture 引入的额外间距
    \vspace{-5pt}
    \noindent
    史管子君臣中庸及
    
\end{document}

st2.jpg

这段代码中,在使用noindent,绘制下划线之后,无法左对齐。

\documentclass{article}
\usepackage{tikz}
\usetikzlibrary{decorations.pathmorphing, calc, positioning}
\usepackage{xcolor}
\usepackage{xeCJK}

\begin{document}
    
    % 您原始的、能够工作的波浪线宏
    \def\mywaveunderline#1{%
        \tikz[baseline=(node.south)]{
            \node[inner sep=0pt](node){#1};
            \draw[decorate, decoration={snake, amplitude=0.5pt, segment length=1.5pt}, line width=0.3pt, overlay, shorten >=0pt, shorten <=0pt] ($(node.south west)+(0pt,-2pt)$) -- ($(node.south east)+(0pt,-2pt)$);
        }%
    }
    
    % 测量各个文本部分的宽度
    \settowidth{\dimen0}{史}
    \settowidth{\dimen1}{管子君臣}
    \settowidth{\dimen2}{中庸}
    \settowidth{\dimen3}{及}
    
    % --- 最终的、可靠的排版方法 ---
    \noindent
    管子君臣中庸
    
    \noindent
    史管子君臣中庸及
    
    \noindent
    史管子君臣中庸及
    
    \noindent
    % 使用一个 TikZ 环境来精确排版第二行
    \begin{tikzpicture}[baseline=(c.base)]
        % 放置所有文字,但使用 \hphantom 确保它们不占空间,以便我们可以手动定位波浪线
        \node[inner sep=0pt, anchor=west] (c) at (0,0) {\hphantom{史}\mywaveunderline{管子君臣}\mywaveunderline{中庸}\hphantom{及}};
        
        % 绘制“史”和“及”两个字,并确保它们的对齐
        \node[anchor=west, inner sep=0pt] at (c.west) {史};
        \node[anchor=west, inner sep=0pt, right=0pt of c.east]{及};
    \end{tikzpicture}
    

    \noindent
    史管子君臣中庸及
    
\end{document}

st3.jpg

这段代码,尝试解决左对齐的问题,但是两段波浪线又连在一起,庸和及两字之间出现了不该有的空白,此后,就问得Gemini胡说了。有大佬基于此三段代码实现一个完美的解决方法吗?先在此谢谢了。

Sagittarius Rover
Sagittarius Rover 2025-08-20
这家伙很懒,什么也没写!

首先,在对「CJK文字」进行下划线时,建议使用xeCJKfntef宏包来实现,否则会出现无法正确换行的情况。

原生的xeCJKfntef的功能

image.png

\documentclass[fontset=fandol,12pt]{ctexart}
\usepackage{xeCJKfntef}
\setlength{\parindent}{0pt}
\begin{document}

\CJKunderline{史记赵世家}

\CJKunderline*{史记赵世家}
    
\CJKunderwave{史记赵世家}

\CJKunderwave*{史记赵世家}

\CJKunderline{史记,赵世家}

\CJKunderline*{史记,赵世家}

\CJKunderline-{史}%
\CJKunderline-{记}%
,%
\CJKunderline-{赵}%
\CJKunderline-{世}%
\CJKunderline-{家}%

\CJKunderwave{史记,赵世家}

\CJKunderwave*{史记,赵世家}

\CJKunderwave-{史}%
\CJKunderwave-{记}%
,%
\CJKunderwave-{赵}%
\CJKunderwave-{世}%
\CJKunderwave-{家}%

\end{document}

image.png

另外,这种「逐字」需要下划线和下划波浪线的实际用途是什么(?)一般波浪线和下划线很少需要逐字添加绘制(?)

一个不成功的探索

最后,要想调好不借助现有功能是比较困难的:

  • 标点压缩
  • 标点禁则
  • 字符压缩

每个CJK字符最终呈现的宽度是并不统一的,我在xeCJkfntef的框架下做了如下修改,也有瑕疵...

\documentclass[fontset=fandol,12pt]{ctexart}
\usepackage{xeCJKfntef}
\usepackage{zhlipsum}
\setlength{\parindent}{0pt}
\ExplSyntaxOn
%Line 5716~5733
% \NewDocumentCommand \CJKunderline { s t- s o }
% {
%     \xeCJK_ulem_group_begin:
%     \xeCJK_fntef_boot:nnNNNn { underline } { uline } #1#2#3 {#4} 
%     \xeCJK_fntef_initial:nnn
%         { \l__xeCJK_uline_depth_tl }
%         { \l__xeCJK_uline_sep_tl }
%         {
%             \l__xeCJK_uline_format_tl
%             \tex_vrule:D
%             height \dim_eval:n { \l__xeCJK_uline_thickness_tl } 
%             depth \c_zero_dim
%             width .2em
%         }
%     \xeCJK_ulem_on:n
% }
\RenewDocumentCommand \CJKunderline { s t- s o }
{
    \xeCJK_ulem_group_begin:
    \xeCJK_fntef_boot:nnNNNn { underline } { uline } #1#2#3 {#4} 
    \xeCJK_fntef_initial:nnn{ \l__xeCJK_uline_depth_tl }{ \l__xeCJK_uline_sep_tl }{%
        \l__xeCJK_uline_format_tl
        \tex_vrule:D height \dim_eval:n { \l__xeCJK_uline_thickness_tl }  depth \c_zero_dim width .95em\hskip1pt}%
 \xeCJK_ulem_on:n
}
\ExplSyntaxOff

\xeCJKsetup{ underwave = { symbol = \sixly \hskip1pt plus 1fill minus 1fill\char 58\hskip0pt plus 1fill minus 1fill\char 58\hskip1pt plus 1fill minus 1fill\relax } }
\begin{document}

\CJKunderline{史记赵世家}

\CJKunderline*{史记赵世家}
    
\CJKunderwave{史记赵世家}

\CJKunderwave*{史记赵世家}

\CJKunderline{史记,赵世家}

\CJKunderline*{史记,赵世家}
    
\CJKunderwave{史记,赵世家}

\CJKunderwave*{史记,赵世家}

\end{document}

image.png

这里的刚性宽度控制在字更多的时候很容易「歪」,同时如果遇到压缩的标点符号/换行等特殊情况,也是难以正常工作的....

U156235
U156235 2025-08-20
这家伙很懒,什么也没写!

测试了一下xeCJKfntef包的使用,能用,但是画线的位置不够准确。

\documentclass{article}

\parindent=0pt

\usepackage{xeCJK}
\usepackage{xeCJKfntef}

\begin{document}
    天地玄黄宇宙洪荒日月盈昃辰宿列张史记赵世家寒来暑往秋收冬藏闰余成岁律吕调阳云腾致雨露结为霜金生丽水玉出昆冈
    \medskip

    天地玄黄宇宙洪荒日月盈昃辰宿列张\CJKunderwave-{史记}\CJKunderwave-{赵世家}寒来暑往秋收冬藏闰余成岁律吕调阳云腾致雨露结为霜金生丽水玉出昆冈
    \medskip
    
    天地玄黄宇宙洪荒日月盈昃辰宿列张\CJKunderline-{史记}\CJKunderline-{赵世家}寒来暑往秋收冬藏闰余成岁律吕调阳云腾致雨露结为霜金生丽水玉出昆冈
    \medskip
    
    天地玄黄宇宙洪荒日月盈昃辰宿列张\CJKunderdot{史记}\CJKunderline-{\CJKunderdot{赵世家}}寒来暑往秋收冬藏闰余成岁律吕调阳云腾致雨露结为霜金生丽水玉出昆冈
    \medskip
    
    张\CJKunderwave-{史记}\CJKunderwave-{赵世家}寒
    \medskip
    
    张\CJKunderline-{史记}\CJKunderline-{赵世家}寒
    \medskip
    
    张\CJKunderdot{史记}\CJKunderline-{\CJKunderdot{赵世家}}寒
\end{document}

st5.jpg

撰写答案

请登录后再发布答案,点击登录

发布
问题

分享
好友

手机
浏览

扫码手机浏览