Table of Contents – How to Get a Flexible Left Margin in the Table of Contents

marginstable of contentstitletoc

I'm aware that using package titletoc one can easily change the format of ToC. But, I'm wondering if there exist some solutions to make the left margin in ToC flexible. By "flexible", I mean that the left margin of certian level will change according to the current label number. For example, if there exist a \chapter, but no \section, \subsection and \subsubsection, then an immediate \paragraph will have the same margin in TC as the normal \section. I know that the margin of a section title is basically controlled by the <left> option in titletoc package. So my naive idea is to combine this with some \ifthenelse.

\documentclass{book}
\usepackage{xifthen}
\usepackage{titletoc}

\setcounter{tocdepth}{4}
\setcounter{secnumdepth}{4}

\newlength{\TOCparagraphskip}
\setlength{\TOCparagraphskip}{9em}

\titlecontents*
    {paragraph}
    [\ifnum\pdfmatch{.[.].[.].[.]0[.].}{\thecontentslabel}=1
        \setlength{\TOCparagraphskip}{7em}%
        \ifnum\pdfmatch{.[.].[.]0[.]0[.].}{\thecontentslabel}=1
            \setlength{\TOCparagraphskip}{3.8em}%
            \ifnum\pdfmatch{.[.]0[.]0[.]0[.].}{\thecontentslabel}=1
                \setlength{\TOCparagraphskip}{1.5em}%
                \ifnum\pdfmatch{0[.]0[.]0[.]0[.].}{\thecontentslabel}=1
                    \setlength{\TOCparagraphskip}{0em}%
                \fi
            \fi
        \fi
    \else
        \setlength{\TOCparagraphskip}{9em}%
    \fi    
    \TOCparagraphskip]{}{\textbullet{}\;}{}{}[\quad]



\begin{document}
    \tableofcontents
    \paragraph{paragraph}


    \chapter{chapter one}

   
\end{document}

But, unfortunately, my naive try give error message.

 (./test.toc
./test.toc:1: Missing number, treated as zero.
<to be read again> 
                   \protect 
l.1 ...aph}{\numberline {0.0.0.0.1}paragraph}{1}{}
                                                  %
A number should have been here; I inserted `0'.
(If you can't figure out why I needed to see a number,
look up `weird error' in the index to The TeXbook.)

./test.toc:1: Illegal unit of measure (pt inserted).
<to be read again> 
                   \protect 
l.1 ...aph}{\numberline {0.0.0.0.1}paragraph}{1}{}
                                                  %
Dimensions can be in units of em, ex, in, pt, pc,
cm, mm, dd, cc, nd, nc, bp, or sp; but yours is a new one!
I'll assume that you meant to say pt, for printer's points.
To recover gracefully from this error, it's best to
delete the erroneous units; e.g., type `2' to delete
two letters. (See Chapter 27 of The TeXbook.)


./test.toc:1: Package calc Error: `\let ' invalid at this point.

See the calc package documentation for explanation.
Type  H <return>  for immediate help.
 ...                                              
                                                  
l.1 ...aph}{\numberline {0.0.0.0.1}paragraph}{1}{}
                                                  %
I expected to see one of: + - * / )

But I've checked that with or without my personal hack, the .toc file are in fact the same.

\contentsline {paragraph}{\numberline {0.0.0.0.1}paragraph}{1}{}%
\contentsline {chapter}{\numberline {1}chapter one}{3}{}%
\contentsfinish 

So, I don't know where is the problem. Or, maybe someone else has some better idea to implement this functionality?

Best Answer

Here's how I would implement what you want instead.

Basic example:

\documentclass{book}
\usepackage{titletoc}

\setcounter{tocdepth}{4}
\setcounter{secnumdepth}{4}

\newlength{\TOCparagraphskip}
\setlength{\TOCparagraphskip}{9em}

\titlecontents*
    {paragraph}
    [\TOCparagraphskip]{}{\textbullet{}\;}{}{}[\quad]



\begin{document}
    \tableofcontents
    \paragraph{paragraph}

    \chapter{chapter one}
    \addtocontents{toc}{\setlength{\TOCparagraphskip}{1em}}

    \paragraph{paragraph 2}
   
\end{document}

You see that but inserting various \setlength into the toc file you can change the left indent on the fly.

Now let's try to automate it. Unfortunately the <after code> in titlesec doesn't work because it results in the contents line being printed too early. However, trying to use etoolbox to append code to run after sectioning commands is prone to unexpected errors.

In the solution below, what we do is the following:

  • We patch the \paragraph command so that before the normal \paragraph is called, we check to see if the indentation needs changing. If it does, then we issue an \addtocontents to do so.
  • The checking is done with a boolean toggle that we defined. After we issue the \addtocontents, we reset the toggle.
  • Patching before the command is less problematic, since you don't have to worry about * forms or other shenanigans. I did it by hand below, but I think using the \pretocmd macro from etoolbox would work just as well.
  • We patch each sectioning command (\chapter, \section, \subsection, and so on; I got tired of typing so you have to add the additional levels yourself) to indicate that the indentation level has changed, and store the appropriate lengths.
  • The sectioning commands now set the boolean toggle to true, and then store the appropriate lengths so that when \paragraph is called the right length is placed in the .toc file.
  • The boolean toggle is not strictly necessary: it helps keep the size of the .toc file down (otherwise you print one \setlength command for each \paragraph). In the example below it would be 7 times without the toggle, and 5 times with the toggle.
\documentclass{book}
\usepackage{titletoc}

\setcounter{tocdepth}{4}
\setcounter{secnumdepth}{4}

\newlength{\TOCparagraphskip}
\setlength{\TOCparagraphskip}{9em}


\titlecontents*
    {paragraph}
    [\TOCparagraphskip]{}{\textbullet{}\;}{}{}[\quad]

\makeatletter
\newif\ifqt@tocskipchanged
\qt@tocskipchangedfalse

% Some helper macros to simplify typing
\newcommand\qt@tocchangepskip[1]{\addtocontents{toc}{\setlength{\TOCparagraphskip}{#1}}}

% Patch the sectioning commands
\let\qt@chap\chapter
\def\chapter{\qt@tocskipchangedtrue\gdef\qt@prepare{\qt@tocchangepskip{1.5em}}\qt@chap}
\let\qt@sec\section
\def\section{\qt@tocskipchangedtrue\gdef\qt@prepare{\qt@tocchangepskip{4em}}\qt@sec}
\let\qt@ssec\subsection
\def\subsection{\qt@tocskipchangedtrue\gdef\qt@prepare{\qt@tocchangepskip{7em}}\qt@ssec}
%% insert additional definitions here for subsubsections etc.
% Patch the paragraph command
\let\qt@para\paragraph
\def\paragraph{\ifqt@tocskipchanged\qt@prepare\fi\qt@tocskipchangedfalse\qt@para}
% In case there are paragraphs outside of traditional chapters. Call this after \tableofcontents
\def\setdefaultparaskip{\qt@tocskipchangedtrue\gdef\qt@prepare{\qt@tocchangepskip{0em}}}
\makeatother

\begin{document}
    \tableofcontents

    \setdefaultparaskip % Only necessary if there is a paragraph before any chapters
    \paragraph{paragraph}

    \chapter{chapter one} 
    \paragraph{paragraph 2}

    \section{Section one}

    \paragraph{paragraph 3}
    
    \section{Section two}

    \paragraph{paragraph 4}

    \paragraph{paragraph 4.5}

    \paragraph{paragraph 4.6}

    \chapter{chapter two}
    \paragraph{paragraph 5}
  
\end{document}

The output TOC from the code above:

enter image description here