4

As a follow up to Adding period/dot after subsubsection title, I need to find a way to conditionally add a period/dot after the subsection title when the title is directly followed by normal text. If it is followed by another heading or a float, there should be no period. In the example below, the title of Section 1.1 should not end in a period/dot because it is immediately followed by a subsection and not normal text.

Context: This is the strict style specification for an ANSI standard I am working on.

Can anyone help with this?

\documentclass{scrartcl}

% Body Section Definitions
\RedeclareSectionCommand[
  font=\sffamily\large,
  runin=false,
  beforeskip=10pt,
  afterskip=4pt,
  afterindent=false
]{section}
\RedeclareSectionCommand[
  font=\normalsize\normalfont\bfseries,
  runin=true,
  beforeskip=10pt
]{subsection}
\RedeclareSectionCommand[
  font=\normalsize\normalfont\bfseries,
  runin=true,
  beforeskip=10pt
]{subsubsection}

\renewcommand{\sectioncatchphraseformat}[4]{\hskip #2#3#4.}

\begin{document}

\section{Level 1 headings never end in a period}

\subsection{Level 2 headings should only end in a period if they are immediately followed by normal text}

\subsubsection{Level 3 is the same as level 2}

This section begins with normal text content, and its heading should end in a period. But the previous section should not end in a period because it is followed by another heading.

\end{document}

enter image description here

8
  • LaTeX is not very good at this kind of look-ahead functionality, basically a document is processed from top to bottom and content is put in the output document as soon as it is processed. Information from earlier in the document can be taken into account (because it is already seen) but information that comes later is not yet part of the processing. So here it would be the most practical solution to not use automatic periods and add them manually where needed. It is possible by storing information in the .aux file and reading it in future runs, but that would be rather complex.
    – Marijn
    Commented Sep 30, 2022 at 19:31
  • 1
    For the TOC and pdf bookmarks you can use the optional argument to \section etc., for example \section[Title without period]{Title with period.}. The optional (first) argument is used in the TOC and bookmarks, the mandatory (second) argument is used in the text. Depending on what you use to refer to the title in the document this would also work, for example nameref strips the period by default when printing a title reference.
    – Marijn
    Commented Oct 2, 2022 at 18:53
  • 1
    I started the bounty for you, I have a bit more reputation to spend after all.
    – Marijn
    Commented Oct 5, 2022 at 14:24
  • 1
    Much appreciated!
    – Neal Kruis
    Commented Oct 5, 2022 at 14:51
  • 1
    This is a different way of looking at the situation. The ams document classes (amsart, etc.) by default insert a period at the end of a run-in section heading, and provide the command \nopunct to suppress it. Originally created to get rid of a superfluous period when the heading ends with ?, it would be applicable also for the situations you describe. It's manual, but ... Commented Oct 5, 2022 at 18:00

2 Answers 2

1
+100

It is very hard to look forward to detect, if a heading is followed by another heading. But with some heuristic you can detect, of a heading is preceded by another heading. In the following example, I use the skip at the end of heading to detect is at the next level heading. To do so I use the do-hooks of KOMA-Script explained at Part II of the user manual:

\documentclass{scrartcl}

% Body Section Definitions
\RedeclareSectionCommand[
  font=\sffamily\large,
  runin=false,
  beforeskip=10pt,
  afterskip=4pt,
  afterindent=false
]{section}
\RedeclareSectionCommand[
  font=\normalsize\normalfont\bfseries,
  runin=true,
  beforeskip=10pt
]{subsection}
\RedeclareSectionCommand[
  font=\normalsize\normalfont\bfseries,
  runin=true,
  beforeskip=10pt
]{subsubsection}

\makeatletter
\newcommand*{\storesubsectiondot}[1]{%
  \ifdim\lastskip=4pt \else
    \immediate\write\@mainaux{\string\global\string\@namedef{subsection\thesubsection dot}{.}}%
  \fi
}
\AddtoDoHook{heading/preinit/subsubsection}{%
  \storesubsectiondot
}
\newcommand*{\addsubsectiondot}{%
  \@nameuse{subsection\thesubsection dot}%
}
\AtEndDocument{\storesubsectiondot{}}
\makeatother
\renewcommand{\sectioncatchphraseformat}[4]{\hskip #2#3#4\Ifstr{#1}{subsection}{\addsubsectiondot}{.}}

\begin{document}

\section{Level 1 headings never end in a period}

\subsection{Level 2 headings should only end in a period if they are
  immediately followed by normal text}

Here we have normal text.

\subsubsection{Level 3 is the same as level 2}

This section begins with normal text content, and its heading should end in a
period. The previous section should also end in a period because it is
followed by normal text.

\section{Level 1 headings never end in a period}

\subsection{Level 2 headings should only end in a period if they are
  immediately followed by normal text}

\subsubsection{Level 3 is the same as level 2}

This section begins with normal text content, and its heading should end in a period. But the previous section should not end in a period because it is followed by another heading.

\section{Level 1 headings never end in a period}

\subsection{Level 2 headings should only end in a period if they are
  immediately followed by normal text}

This section begins with normal text content and therefore should have a dot.
\end{document}

The \AtEndDocument is used to also detect that the last heading is followed by text. Maybe this could be done less heuristic, because the last heading should always be followed by text. You should also be able to do similar for lower levels if needed.

But as you see, it is really complicated. So a more manual suggestion would be:

\documentclass{scrartcl}

% Body Section Definitions
\RedeclareSectionCommand[
  font=\sffamily\large,
  runin=false,
  beforeskip=10pt,
  afterskip=4pt,
  afterindent=false
]{section}
\RedeclareSectionCommand[
  font=\normalsize\normalfont\bfseries,
  runin=true,
  beforeskip=10pt
]{subsection}
\RedeclareSectionCommand[
  font=\normalsize\normalfont\bfseries,
  runin=true,
  beforeskip=10pt
]{subsubsection}

\DeclareRobustCommand{\sentencedot}{.}
\BeforeStartingTOC[toc]{\RenewCommandCopy\sentencedot\relax}

\begin{document}

\tableofcontents
\section{Level 1 headings never end in a period}

\subsection{Level 2 headings should only end in a period if they are
  immediately followed by normal text\sentencedot}

Here we have normal text.

\subsubsection{Level 3 is the same as level 2\sentencedot}

This section begins with normal text content, and its heading should end in a
period. The previous section should also end in a period because it is
followed by normal text.

\section{Level 1 headings never end in a period}

\subsection{Level 2 headings should only end in a period if they are
  immediately followed by normal text}

\subsubsection{Level 3 is the same as level 2\sentencedot}

This section begins with normal text content, and its heading should end in a period. But the previous section should not end in a period because it is followed by another heading.

\section{Level 1 headings never end in a period}

\subsection{Level 2 headings should only end in a period if they are
  immediately followed by normal text\sentencedot}

This section begins with normal text content and therefore should have a dot.
\end{document}
2
  • @Marijn what do you think of this solution?
    – Neal Kruis
    Commented Oct 11, 2022 at 15:46
  • @NealKruis I think it is clever to use \lastskip here, so it is worth the bounty. However, the solution is (currently) not very generalized, for example it does not work for subsubsections and it depends on setting and knowing the afterskip value. So, as cabohah also states, doing this manually (cf. through Pandoc) is probably the preferred solution here.
    – Marijn
    Commented Oct 12, 2022 at 9:56
1

Without a clean way to do this directly in LaTeX, I ended up writing a panflute filter to automate this via Pandoc:

import panflute as pf

HEADING_NAMES = [
  "chapter",
  "section",
  "subsection",
  "subsubsection",
  "paragraph",
  "subparagraph"]

def action(elem, doc):
  if isinstance(elem, pf.Header):
    if isinstance(elem.next, pf.Para):
      if elem.level > 1:
        before = inlatex(f"\\hypertarget{{{elem.identifier}}}{{%\n\\{HEADING_NAMES[elem.level]}[")
        middle = inlatex(r"]{")
        after = inlatex(fr".}}\label{{{elem.identifier}}}}}")
        return [pf.Plain(before, *elem.content, middle, *elem.content, after)]

def main(doc=None):
  return pf.run_filter(action, doc=doc)

if __name__ == "__main__":
  main()

Note: This filter can be simplified once this feature is implemented.

You must log in to answer this question.

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