6

I use the following layout where the \sec{} headers are placed in the margin and \subsec{} headers and text paragraphs are position in the main column next to it.

However, when a \subsec{} immediately follows \sec{}, I would like both to start on the same line without vertical space. The issue is that I can't just include negative space in one of the commands because \subsec{} should still create spacing when it follows anything other than \sec{}. How can I achieve this?

Here is a small example that shows the unwanted space, where I would want SECTION and Subsection to start on the same line and keep all other spacing as it is:

unwanted space between sec and subsec

\documentclass{article}
\usepackage{calc}
\usepackage{changepage}
\usepackage{lipsum}

\setlength{\parskip}{.5em}
\setlength{\parindent}{0em}

\newcommand{\titlecol}{1.45in}
\newcommand{\titlesep}{0.5em}
\newcommand{\secskip}{1.6em}
\newcommand{\subsecskip}{0.6em}

\renewcommand{\sec}[1]{%
  \vspace{\secskip}\hspace{-\titlecol}%
  \parbox[t][0pt][t]{\titlecol-\titlesep}{#1\raggedright}}

\newcommand{\subsec}[1]{\vspace{\subsecskip}\textbf{#1}\par}

\begin{document}
\begin{adjustwidth}{\titlecol}{0em}

\sec{SECTION}

\subsec{Subsection}
\lipsum[1][1-2] \par
\lipsum[1][3]

\subsec{Subsection}
\lipsum[1][1-3]

\sec{ANOTHER SECTION}
\lipsum[1][1-3]

\end{adjustwidth}
\end{document}

2
  • 2
    you are dropping everything which allows to control the behaviour, e.g. your sectioning commands will allow page breaks between the heading and the text. Better use e.g. a package like titlesec to setup the sectioning commands. The documentation shows on page 22 and example of a margin heading. Sep 4, 2022 at 16:32
  • 1
    @UlrikeFischer I spent a whole day trying to use \titlesec but leftmargin didn't work and drop had a similar issue to the one described in my question here. I opened a question about it but gave up this route because it was too complex and hard to find help. You can see the solution below, which uses \nopagebreak to prevent the issue you described.
    – danijar
    Sep 5, 2022 at 11:42

4 Answers 4

4

The following plain TeX macro defines \sec in such a way that the section heading is typeset as part of the first paragraph in the section (so no page break can intervene):

\def\sec#1{\vskip\secskip
  \noindent\llap{
  \setbox0=\vtop{\hsize\titlecol\advance\hsize by-\titlesep\raggedright#1} 
  \dp0=0pt\box0\hskip\titlesep}\ignorespaces}

This allows you to omit the vertical skip before a subsection if you are already in a paragraph, that is, contribute a vertical skip only in vertical mode:

\def\subsec#1{\ifvmode\vskip\subsecskip\fi\textbf{#1}\par}

But you must then avoid a blank line between \sec and \subsec, because that would end the paragraph started for the section so that the subsection contributes a vertical skip:

\sec{SECTION}
\subsec{Subsection}
4
  • That looks promising! Could the blank line between \sec and \subsec be allowed through something like \ifvmode\vspace*{-\baselineskip}\fi or \@ifnextchar\par\@gobble\relax?
    – danijar
    Sep 4, 2022 at 20:24
  • Adding \@ifnextchar\par\@gobble\relax after \ignorespaces might work, please try it out. Sep 5, 2022 at 5:55
  • (A line with a single % on it does not count as blank.) Sep 5, 2022 at 6:10
  • Fantastic, that works! I wrote up the complete solution here, based on your approach (which turned out to be the simplest of the answers): tex.stackexchange.com/a/656233/61219
    – danijar
    Sep 5, 2022 at 11:39
7

You can set the title text, then return to the vertical mode, then correct the vertical position by a set of \vskips and then put a \penalty with a special (non-breakable) number (10013 in the example).

If the \subsec follows then you can read this number by \lastskip and put (or not) a \vskip above the \subsec:

\parskip=.5em
\parindent=0em

\def\titlecol{1.3in}
\def\titlesep{0.5em}
\def\secskip{1.6em}
\def\subsecskip{0.6em}
\hoffset=6cm
\hsize=12cm

\def\sec#1{%
  \vskip\secskip
  \hskip-\titlecol
  \vtop {\hsize=\dimexpr\titlecol-\titlesep\relax \rightskip=0pt plus1fil\relax #1\vss}
  \par
  \nobreak \vskip-\prevdepth \prevdepth=0pt \vskip-\baselineskip \vskip-\parskip
  \penalty10013
}

\def\subsec#1{\ifnum\lastpenalty=10013 \else \vskip\subsecskip\fi {\bf#1}\par\nobreak}


\sec{SECTION}

\subsec{Subsection}
\lipsum[1]\par
\lipsum[2]

\subsec{Subsection}
\lipsum[1]

\sec{ANOTHER SECTION}
\lipsum[1]

Edit: I try to explain the code in more detail. The \sec macro puts a \vtop on the left side (with one or more lines) as a part of single-line paragraph finalized by \par. The second and more lines in the \vtop are accumulated in the depth of it. If we put a next material after the \par, the material will be after it, i.e. after the last line of \vtop. The \prevdepth is TeX register (can be used only in vertical mode) where we can read or set the depth of the last box put to the vertical mode, i.e. this \vtop in our example. \vtop-\prevdepth returns the current typesetting point at the baseline of the first line of the \vtop. We set it to zero after using its because placing of next box in vertical mode is calculated from \baselineskip or \lineskip depending on \prevdepth. We want to use \baselineskip and we return the typesetting point by -\baselineskip in order the baseline of the next box will be the same as the baselines of \vtop. But next paragraph adds \parskip too, so we must to put the negative \parskip too.

The \rightskip=0pt plus1fil\relax is similar to \raggedright but the glue at the right side is infinitely stretchable so it is excluded that the words hyphenation is started (because we don't want such hyphenation in titles).

2
  • Great, that works! The idea of setting the penalty in \sec and reading it in \subsec is intuitive to me, can this not be integrated into my example more easily? For example, what is the \prevdepth and the \rightskip=0pt plus1fil\relax for? I'm sure your rewrite is better than my initial version, I'd just like to make sure I understand it so that I can learn something and also to make modifications to it in the future.
    – danijar
    Sep 4, 2022 at 20:22
  • 1
    OK, I added an explanation of the code.
    – wipet
    Sep 5, 2022 at 6:07
4

This assumes you never start the section text with {.

\documentclass{article}
\usepackage{calc}
\usepackage{changepage}
\usepackage{lipsum}

\setlength{\parskip}{.5em}
\setlength{\parindent}{0em}

\newcommand{\titlecol}{1.45in}
\newcommand{\titlesep}{0.5em}
\newcommand{\secskip}{1.6em}
\newcommand{\subsecskip}{0.6em}

\renewcommand{\sec}[1]{%
  \par
  \addvspace{\secskip}\hspace{-\titlecol}%
  \makebox[\titlecol][l]{\parbox[t][0pt][t]{\titlecol-\titlesep}{#1\raggedright}}%
  \subsecfalse
  \checksubsec
}

\makeatletter
\newcommand{\checksubsec}[1]{%
  \ifx#1\par
    \expandafter\@firstoftwo
  \else
    \expandafter\@secondoftwo
  \fi
  {\checksubsec}% found \par, repeat
  {\checksubsec@#1}% check for \subsec
}
\newcommand{\checksubsec@}[1]{%
  \ifx#1\subsec
    \expandafter\@firstoftwo
  \else
    \expandafter\@secondoftwo
  \fi
  {\subsectrue\subsec}% found \subsec set the conditional to true
  {#1}% reinsert the token
}
\makeatother
\newif\ifsubsec
\newcommand{\subsec}[1]{%
  \ifsubsec\else\par\addvspace{\subsecskip}\fi
  \textbf{#1}\par\nobreak
}

\begin{document}
\begin{adjustwidth}{\titlecol}{0em}

\sec{SECTION}

\subsec{Subsection}
\lipsum[1][1-2] \par
\lipsum[1][3]

\subsec{Subsection}
\lipsum[1][1-3]

\sec{ANOTHER SECTION}
\lipsum[1][1-3]

\end{adjustwidth}
\end{document}

The first token is looked at (ignoring spaces). If it's \par we discard it and repeat; if it's \subsec we set the conditional to true and issue \subsec again. Otherwise the token is reinserted and typesetting goes on.

I made a few changes: the section title is set in a box of the appropriate dimension and the \parbox with a reduced width (by \titlesep) is set inside it; I added \nobreak so sections or subsections are not detached from the following text.

You should better use geometry and actually set the section titles in the margin, rather than faking this with a global adjustwidth.

enter image description here

1
  • Thanks! I'm trying out all the answers now and will accept the one that works best soon. The tip about \nobreak is much appreciated. I tried using geometry initially but I didn't figure out how to change the margins mid-way into a page, which is needed in my case for an intro paragraph without title that should span the full with.
    – danijar
    Sep 5, 2022 at 10:35
0

Thanks a lot to Heiko Theißen for suggesting to typeset the \sec inline as part of the paragraph and then checking in \subsec whether we're already in a paragraph to only otherwise (\ifvmode) insert the vertical space.

I also used \@ifnextchar\par\@gobble\relax} to prevent empty lines after \sec to start a new paragraph, which requires wrapping the \sec definition in \makeatletter and \makeatother. The \ignorespaces didn't seem to be necessary anymore since we're already ignoring paragraph breaks.

In addition, I switched from the pure TeX solution to LaTex \parbox that I'm more familiar with, making it easier to understand and modify the solution for me. Thanks also to egreg for suggesting the \nobreak/\nopagebreak to prevent sections from detaching from following paragraphs.

\makeatletter
\def\sec#1{%
  \vskip\secskip%
  \noindent\llap{\parbox[t][0pt][t]{\titlecol-\titlesep}{\raggedright#1}}%
  \nopagebreak\@ifnextchar\par\@gobble\relax}
\makeatother
 
\def\subsec#1{%
  \ifvmode\vskip\subsecskip\fi%
  \textbf{#1}\par\nopagebreak}

Full example:

\documentclass{article}
\usepackage{calc}
\usepackage{changepage}
\usepackage{lipsum}

\setlength{\parskip}{.5em}
\setlength{\parindent}{0em}

\newcommand{\titlecol}{1.45in}
\newcommand{\titlesep}{0.5em}
\newcommand{\secskip}{1.6em}
\newcommand{\subsecskip}{0.6em}
 
\makeatletter
\def\sec#1{%
  \vskip\secskip%
  \noindent\llap{\parbox[t][0pt][t]{\titlecol-\titlesep}{\raggedright#1}}%
  \nobreak\@ifnextchar\par\@gobble\relax}
\makeatother
 
\def\subsec#1{%
  \ifvmode\vskip\subsecskip\fi%
  \textbf{#1}\par}

\begin{document}
\begin{adjustwidth}{\titlecol}{0em}

\sec{SECTION}

\subsec{Subsection}
\lipsum[1][1-2]\par
\lipsum[2][1-3]

\subsec{Subsection}
\lipsum[1][1-2]

\sec{ANOTHER SECTION}
\lipsum[1][1-3]

\end{adjustwidth}
\end{document}

Result:

enter image description here

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .