3

Is there a way in (plain) (La)TeX to determine whether a subsection directly follows a section?

E.g. in the case

\section{Main level} 
\subsection{Sublevel} 

I want draw a line under the subsection, and in

\section{Main level}
some text
\subsection{Sublevel}

I want to draw a line under the section (not underlining the text, but a simple hsize-wide rule between heading and text).

To add some spice: I cannot include any external packages for they may interfer with my existing layout, therefore I need a rather low level solution to this problem. Is this even possible, at all?


Thanks to Ulrike Fischer's answer i ended up with something like

\documentclass{article}

\makeatletter
\newif\if@dolinesec\@dolinesecfalse

\def\section{\@dolinesectrue\@startsection{section}{1}{\z@}
  {-2em\@plus-1ex\@minus-.2ex}
  {1em\@plus.2ex}
  {\normalsize\bfseries}}
\def\subsection{\@startsection{subsection}{2}{\z@}
  {-1em\@plus-0.25ex\@minus-.2ex}
  {1em\@plus.2ex}
  {\normalsize\itshape\bfseries}}
\def\subsubsection{\@startsection{subsubsection}{3}{\z@}
  {-3.25ex\@plus-1ex\@minus-0.2ex}
  {1.5ex\@plus0.2ex}
  {\normalfont\sffamily}}


\def\@afterheading{%
  \@nobreaktrue
  \everypar{%
    \if@nobreak
      \@nobreakfalse
      \clubpenalty \@M
      \if@afterindent \else
        {\setbox\z@\lastbox
          \if@dolinesec\rlap{\raise\baselineskip\hbox{\smash{\vrule width\hsize height.4\p@\relax}}}\fi%
        }%
      \fi
      \@dolinesecfalse
    \else
      \clubpenalty \@clubpenalty
      \everypar{}%
    \fi}}

\makeatother


\begin{document}

\section{Test}
\subsection{Sub Test}
text text text text text text text text text text text text
text text text text text text text text text text text text

\subsubsection{Sub Test}\label{blabla}
text text text text text text text text text text text text
text text text text text text text text text text text text

\section{Test}
text text text text text text text text text text text text
text text text text text text text text text text text text
\subsection{Sub Test}
text text text text text text text text text text text text
text text text text text text text text text text text text
\subsubsection{Sub Test}\label{blabla}
text text text text text text text text text text text text
text text text text text text text text text text text text

\section{Test}\label{bla}

\subsection{Sub Test}\label{blabla}
\subsubsection{Sub Test}\label{blabla}
text text text text text text text text text text text text
text text text text text text text text text text text text

text text text text text text text text text text text text
text text text text text text text text text text text text

\end{document}

which gives me:

Result: Line after lowest adjacent heading.

Lupino
  • 2,772
  • Related (in an offhand way): https://tex.stackexchange.com/questions/101545/different-vertical-spacing-parskip-between-concurrent-section-titles-and-parag – Steven B. Segletes Nov 06 '17 at 11:01

2 Answers2

3

As several have noted, this approach only works if there are no extra tokens (e.g., \label or even a blank line) between the \section and \subsection.

\documentclass{article}
\usepackage{lipsum}
\let\svsection\section
\let\svsubsection\subsection
\renewcommand\section[3][]{%
  \ifx\relax#1\relax\svsection{#2}\else\svsection[#1]{#2}\fi%
  \ifx\subsection#3%
    \def\next{\xsubsection}%
  \else%
    \noindent\rule[\ht\strutbox]{\linewidth}{.4pt}\par%
    \def\next{#3}%
  \fi%
  \next
}
\newcommand\xsubsection[2][]{%
  \ifx\relax#1\relax\svsubsection{#2}\else\svsubsection[#1]{#2}\fi%
  \noindent\rule[.5\ht\strutbox]{\linewidth}{.4pt}\par%
}
\begin{document}
\section{Test}
\subsection{Sub Test}
\lipsum[4]

\section{Test}
\lipsum[4]
\subsection{Sub Test}

\section[optional]{Test}
\subsection[optional]{Sub Test}
\lipsum[4]

\section[optional]{Test}
\lipsum[4]
\subsection[optional]{Sub Test}
\end{document}

enter image description here

  • 1
    Interesting idea. Unfortunaltely, this only works if there is nothing (like a blank line) between the section headings. Since i need to convert the tex to xml and render both through the same stylesheets, i cannot use this solution. But thanks, anyways. – Lupino Nov 06 '17 at 11:31
  • 1
    You should probably mention that this only works if there is really nothing between the section and subsection (e.g. no \label{} and more importantly no empty line). – Ulrike Fischer Nov 06 '17 at 11:32
2

Adding the rule to \@afterheading actually already gives more or less the wanted result. It needs some tuning (e.g. to avoid that it affects also \subsubsections etc) but I don't have the time now:

\documentclass{article}
\usepackage[utf8]{inputenc}
\makeatletter
\def\@afterheading{%
  \@nobreaktrue
  \everypar{%
    \if@nobreak
      \@nobreakfalse
      \clubpenalty \@M
      \if@afterindent \else
        {\setbox\z@\lastbox 
         \hrule\vspace{1ex}%<---
        }% 
      \fi
    \else
      \clubpenalty \@clubpenalty
      \everypar{}%
    \fi}}
\makeatother
\begin{document}
\section{Test} 

\subsection{Sub Test}
text

\subsection{Sub Test}
text


\section{Test}

\subsection{Sub Test}
text


\section{Test}
text
\subsection{Sub Test}

\section[optional]{Test}
\subsection[optional]{Sub Test}
text

\section{blblblb}
vlvlv

\end{document}
Ulrike Fischer
  • 327,261
  • Thanks very much, this helps me enough. One question, thou: would it be possible, if i have section-subsection-subsubsection to get the line between the subsection and subsubsection? I guess then @afterheading is the wrong place to look at, isn't it? – Lupino Nov 06 '17 at 12:22
  • @afterheading can imho be used, but you should put the "after subsection" code outside the \everypar as this code is only inserted when a "real" paragraph starts. You can test the subsection counter to check if you are behind a subsection. Btw: better test what happens if the text starts with a list or a display math. – Ulrike Fischer Nov 06 '17 at 12:49
  • You are right, when a block element follows the headings, the line vanishes. But i think those rare cases can (and should, speaking of style) be caught by hand. – Lupino Nov 06 '17 at 14:41