移动段落开头以避免与标题冲突

移动段落开头以避免与标题冲突

我有一份文档,其中段落级标题放在页边距中。我使用 KOMAscript 实现了此功能,如以下 MWE 所示:

\documentclass{scrartcl}

\RedeclareSectionCommand[afterskip=0pt]{paragraph}
\renewcommand{\sectioncatchphraseformat}[4]{%
  \ifstr{#1}{paragraph}
    {%
      \makebox[0pt][r]{\raisebox{0pt}[\height][0pt]{\parbox[t]{2cm}{%
        \raggedright\relax#4}%
      }\hskip.25em\relax}%
    }{%
      \hskip #2#3#4%
    }}

\usepackage{lipsum}

\begin{document}

\lipsum[2]

\paragraph{The name of this paragraph}
\lipsum[2]

\paragraph{Antidisestablishmentarianism}
\lipsum[2]

\end{document}

不幸的是,这会导致长单词与正文发生冲突:

正文中有很长的段落标题“反对政教分离主义”,干扰

由于在这种情况下连字符看起来很难看,我决定通过在段落与标题发生冲突时将段落向下推一行来解决这个问题。我目前为此使用以下宏:

\makeatletter
\newcommand{\overlongname}{%
  \rule[-.5\baselineskip]{0pt}{0pt}%
  \@afterindentfalse%
  \@afterheading}
\makeatother

\overlongname在任何有问题的标题之后发出\paragraph{…}就足以解决问题。

但是,我不喜欢这种方法的手动性质。有没有办法自动检测节标题是否水平溢出parbox,并在发生这种情况时移动段落开头?

答案1

似乎 LaTeX 从来不会\hbox对句子的第一个单词进行连字符处理,也不会生成过满警告。(它确实会抱怨\ifstr。)我找到了如何修复这个问题的方法,但现在它反而会进行连字符处理。

\documentclass{scrartcl}

\RedeclareSectionCommand[afterskip=0pt]{paragraph}
\renewcommand{\sectioncatchphraseformat}[4]{%
  \Ifstr{#1}{paragraph}
    {%
      \makebox[0pt][r]{\raisebox{0pt}[\height][0pt]{\parbox[t]{2cm}%
        {\raggedright\hspace*{-0.333em} #4}%
      }\hskip.25em\relax}%
    }{%
      \hskip #2#3#4%
    }}

\usepackage{lipsum}
\newsavebox{\test}


\begin{document}

\lipsum[2]

\paragraph{The name of this paragraph}
\lipsum[2]

\paragraph{Antidisestablishmentarianism}
\lipsum[2]

\end{document}

下面是一个测试句子第一个单词的代码片段。它使用了 xstring 包。

\sbox9{\StrBefore{#4 }{ }}%
\ifdim\wd9>2cm\relax \overlongname\fi

答案2

根据@JohnKormylo 的建议,这似乎基本有效:

\newsavebox{\overlongtestbox}
\newcommand{\IfOverlongWord}[4]{%
  \IfStrEq{#2}{}{% empty string, base case, use false arg
    #4%
    }{%
      \StrCut{#2}{ }\firsttestword\resttestword%
      \sbox{\overlongtestbox}{\firsttestword}%
      \ifdim\wd\overlongtestbox>#1\relax% overlong word, use true arg
        #3%
      \else% not overlong, now test next word
        \IfOverlongWord{#1}{\resttestword}{#3}{#4}%
      \fi%
    }}

\RedeclareSectionCommand[afterskip=0pt]{paragraph}
\renewcommand{\sectioncatchphraseformat}[4]{%
  \Ifstr{#1}{paragraph}
    {%
      \IfOverlongWord{2cm}{#4}{%
        \hspace{\dimexpr-2cm-.25em\relax}%
        #4%
        \rule[-.5\baselineskip]{0pt}{0pt}%
        \par%
        \noindent
      }{%
        \makebox[0pt][r]{\raisebox{0pt}[\height][0pt]{\parbox[t]{2cm}{%
          \raggedright\relax#4}%
        }\hskip.25em\relax}%
      }%
    }{%
      \hskip #2#3#4%
    }}

本质上,\IfOverlongWord依次测试每个单词,看其是否比阈值宽。然后将其用于\sectioncatchphraseformat,如果条件成立,则降低下一个段落,否则在边距中创建一个框。

不幸的是,它有一个严重的限制:如果我加载它,它就会崩溃hyperref!具体来说,我得到了很多错误,如下所示:

./latex-marginpar-mwe.tex:51: Undefined control sequence.
<argument> \begingroup \let \HyperRaiseLinkLength 
                                                  \@tempdima \setlength \Hyp...l.51 
     
./latex-marginpar-mwe.tex:51: Undefined control sequence.
<argument> \HyperRaiseLinkLength 
                                 
l.51 
     
./latex-marginpar-mwe.tex:51: Undefined control sequence.
<argument> ...r \raise \the \HyperRaiseLinkLength 
                                                  \hbox {\[email protected] 
     
./latex-marginpar-mwe.tex:51: You can't use `\hbox' after \the.
<argument> ...se \the \HyperRaiseLinkLength \hbox 
                                                  {\Hy@RestoreSpaceFactor \h...l.51 
     
./latex-marginpar-mwe.tex:51: Undefined control sequence.
\Hy@wrapper@babel ...set@display@protect \edef \x 
                                                  {#2}\@onelevel@sanitize \x...l.51 
     
./latex-marginpar-mwe.tex:51: Undefined control sequence.
<argument> \x 

这似乎表明我在这里做错了什么,所以我暂时不会接受这个答案。

相关内容