Intereting Posts

# Selective and bunched entries to list of figures

I have a complex document with a few thousand figures (most of which are slight variants, but a few are key). I would like to produce a list of figures (lof) which only includes individual figures selectively, bunches others, and omits some completely. Some of my captions are in floats and others use captionof from the caption package.

For example:

1.2 This is the king…………………….5

1.5 This is the queen……………………9

1.16-1.66 Various princesses……………12-88

Most of this is easy to do. I can omit the entries I want by sending a blank alternative argument to the caption. I can also manually send entries to the lof that includes a range of figure numbers (for excluded entries).

What is however difficult is to figure out a way to

a) punch a page range (corresponding to the figure range) into the page number part of the lof.
b) format the figure range so that it aligns with the rest of the figure numbers (I use titletoc)

MWE below with image of results so far

\documentclass[a4paper]{book}
\usepackage{caption}

\begin{document}
\listoffigures

\chapter{Bloo}

\begin{figure}[htb]
\caption[]{This is God and does not appear in lof}
\label{fig:first}
\end{figure}

\clearpage
\begin{figure}[htb]
\caption[This is the king]{King}
\label{fig:second}
\end{figure}

\clearpage
\begin{center}
\captionof{figure}[This is the queen]{Queen}
\label{fig:third}
\end{center}

\clearpage
\begin{center}
\captionof{figure}[]{Does not appear in lof}
\label{fig:fourth}
\end{center}

\clearpage
\begin{center}
\captionof{figure}[]{Princess 1}
\label{fig:fifth}
\end{center}

\clearpage
\begin{center}
\captionof{figure}[]{Princess 2}
\label{fig:sixth}
\end{center}

\addcontentsline{lof}{section}{\ref{fig:fifth}--\ref{fig:sixth} Various princesses page numbers wrong want a range}

\end{document}


Edit: Since posting I realised I can do

\addtocontents{lof}{\protect \contentsline {figure}{\numberline {\ref{fig:fifth}--\ref{fig:sixth}}{\ignorespaces Various princesses}}{\pageref{fig:fifth}--\pageref{fig:sixth}}}


which yields a great output.

I will leave the question up here for the moment in case it inspires any better solutions or helps anyone else.

#### Solutions Collecting From Web of "Selective and bunched entries to list of figures"

Here is a proof-of-concept that doesn’t require much fiddling around; you construct the “combined LoF caption entry” yourself using \refs and \pagerefs:

\documentclass{article}

\usepackage{lipsum}
\usepackage{caption,graphicx}
\usepackage{tocloft}
\cftsetpnumwidth{3em}

\begin{document}

\sloppy% Just for this example

\listoffigures

\section{Document body}
% Some image within the document body
\begin{figure}[ht]
\centering
\includegraphics[width=1cm]{example-image}
\caption{Example image}
\end{figure}

\appendix
\section{Appendix}
\captionsetup[figure]{list=no}
% First image in Appendix
\begin{figure}[ht]
\centering
\includegraphics[width=1cm]{example-image}
\caption{First image in Appendix}
\label{fig:first-image}
\end{figure}

\lipsum[1-50]% A whole bunch more images
\lipsum[1-50]% A whole bunch more images
\lipsum[1-50]% A whole bunch more images

% Last image in Appendix
\begin{figure}[ht]
\centering
\includegraphics[width=1cm]{example-image}
\caption{Last image in Appendix}
\label{fig:last-image}
\end{figure}

% Insert combined LoF entry
\protect\contentsline{figure}
{\protect\numberline{\protect\ref{fig:first-image}--\protect\ref{fig:last-image}}% Image range
A collection of images}% Collection title
{\protect\pageref{fig:first-image}--\protect\pageref{fig:last-image}}% Page range
{}% ...for hyperref
}

\end{document}


If the contents entries need to be indented naturally due to a large figure range, then simply drop the \protect\numberline entry and insert an appropriate space:

% Insert combined LoF entry
\protect\contentsline{figure}
{\protect\ref{fig:first-image}--\protect\ref{fig:last-image} % Image range
A collection of images}% Collection title
{\protect\pageref{fig:first-image}--\protect\pageref{fig:last-image}}% Page range
{}% ...for hyperref
}


The above is compatible with hyperref.

Since you are already manually outputting the \contentsline instruction, you can automate the process a bit. Add this to the preamble:

\makeatletter
\def\beginfigrange#1{%initiates the start of the figure range
\label{#1}
\edef\dummy{\noexpand\figrangestartpage{\thepage}}
\expandafter\gdef\dummy
\gdef\figrangestartlbl{#1}}
\def\endfigrange#1#2{%ends the figure range and writes the \contentsline to the aux
\label{#1}
\@ifundefined{r@#1}{}{%
\edef\dummylabel{\figrangestartlbl}
\immediate\write\@auxout{\noexpand\@writefile {lof}{\noexpand\contentsline{figure}{\noexpand\numberline {\noexpand\ref{\dummylabel}--\ref{#1}}\ignorespaces #2}{\figrangestartpage --\thepage}}}}
\gdef\figrangestartpage{\relax}
\gdef\figrangestartlbl{\relax}}
\renewcommand\l@figure{\@dottedtocline{1}{1.5em}{5em}}
\makeatother


Then, in place of \label{fig:XX} in the figure environment, call \beginfigrange{fig:XX} in the first figure environment and \endfigrange{fig:YY}{List of Figures description} in the last one.

For example:

\begin{center}
\captionof{figure}[]{Princess 1}
%\label{fig:fifth}
\beginfigrange{fig:fifth}
\end{center}


and

\begin{center}
\captionof{figure}[]{Princess 2}
%\label{fig:sixth}
\endfigrange{fig:sixth}{Various princesses page numbers wrong want a range}
\end{center}


The \l@figure macro is used to format the lines in the List of Figures, hence its redefinition.

Edit:

The \write for \label isn’t called immediately, so an error will result on the first run when \endfigrange is called. Subsequent runs should be fine. The \@ifundefined{r@#1}{}{% line prevents this initial error.

I don’t like hiding the \label{fig:XX}, which my first answer does. An alternative that follows convention is to use \thefigure to pull the requisite figure identifier, as opposed to using the label key. This allows the first macro to serve as a “flag” and removes the potential reference error in the second. Additionally, I’ve incorporated a check for hyperref so that, if it is loaded, the List of Figures entry will link to the first figure in the range.

\documentclass[a4paper]{book}
\usepackage{caption}
\usepackage{hyperref}

\makeatletter
\def\beginfigrange{%initiates the start of the figure range
\edef\dummy{\noexpand\figrangestartpage{\thepage}}
\expandafter\gdef\dummy
\edef\dummy{\noexpand\figrangestartlbl{\thefigure}}
\expandafter\gdef\dummy
%added to use the same reference key as hyperref; will work for \caption, \captionof, and with the hypertexnames=false option
\expandafter\ifx\expandafter\relax\usinghyperref\relax%saves the current hyperlink if hyperref is used
\gdef\hyperapp{\relax}
\else
\edef\dummy{\expandafter\noexpand\expandafter\hyperapp{\@currentHref}}
\expandafter\gdef\dummy
\fi}
\def\endfigrange#1{%ends the figure range and writes the \contentsline to the aux
\edef\dummylabel{\figrangestartlbl}
\edef\dummy{\thefigure}
\immediate\write\@auxout{\noexpand\@writefile{lof}{\noexpand\contentsline{figure}{\noexpand\numberline {\dummylabel--\dummy}{\ignorespaces #1}}{\figrangestartpage --\thepage}{\hyperapp}}}
\gdef\figrangestartpage{\relax}
\gdef\figrangestartlbl{\relax}
\gdef\hyperapp{\relax}}
\renewcommand\l@figure{\@dottedtocline{1}{1.5em}{5em}}
\AtBeginDocument{%Defined such that \usinghyperref = \relax if not using hyperref
{\gdef\usinghyperref{true}}
{\gdef\usinghyperref{\relax}}}
\makeatother


Use:

\begin{center}
\captionof{figure}[]{Princess 1}
\label{fig:fifth}
\beginfigrange
\end{center}


and:

\begin{center}
\captionof{figure}[]{Princess 2}
\label{fig:sixth}
\endfigrange{Various princesses page numbers wrong want a range}
\end{center}


Variant:

Added to the preamble (with \usepackage{keyval}),

\makeatletter
\AtBeginDocument{%
%keyvalues for the new second option of caption and captionof
\define@key{runfig}{beginrange}[true]{\def\runfig@beginrange{#1}}
\define@key{runfig}{endrange}[Figure range LOF entry]{\def\runfig@endrange{#1}}
\def\runfigdefaults{\setkeys{runfig}{beginrange={\relax}, endrange={\relax}}}
\gdef\figrangestartlbl{\relax}%use the label as a flag to indicate if the current figure is within a running fig; if so, it will not be in the list of figures.

%redefine \caption and \captionof
\let\@oldcaption=\caption
\let\@oldcaptionof=\captionof
\def\caption{\@ifstar{\@newcapstar}{\@newcapnostar}}
\def\@newcapnostar{\@ifnextchar[{\@newcapnostaropa}{\@newcapnostarnoop}}
\def\@newcapnostaropa[#1]{\@ifnextchar[{\@newcapnostaropaa[#1]}{\@newcapnostaropab[#1]}}
\def\@newcapnostaropab[#1]#2{\@newcapnostaropaa[#1][]{#2}}
\def\@newcapnostarnoop#1{\@newcapnostaropaa[][]{#1}}
\def\@newcapnostaropaa[#1][#2]#3{%
\runfigdefaults
\setkeys{runfig}{#2}
\expandafter\ifx\expandafter\relax\runfig@beginrange\relax%not beginfigrange
\expandafter\ifx\expandafter\relax\runfig@endrange\relax%not endfigrange
\expandafter\if\expandafter\relax\figrangestartlbl\relax%not running...default caption behavior
\@oldcaption[#1]{#3}
\else%is running, exclude from LOF
\@oldcaption[]{#3}
\fi
\else%is endfigrange
\@oldcaption[]{#3}
\endfigrange{\runfig@endrange}
\fi
\else%is beginfigrange
\@oldcaption[]{#3}
\beginfigrange
\fi
}
\def\@newcapstar{\@oldcaption}%the second option is not enabled for the starred variant

%redefine captionof
\def\captionof{\@ifstar{\@newcapofstar}{\@newcapofnostar}}
\def\@newcapofnostar#1{\@ifnextchar[{\@newcapofnostaropa#1}{\@newcapofnostarnoop#1}}
\def\@newcapofnostaropa#1[#2]{\@ifnextchar[{\@newcapofnostaropaa#1[#2]}{\@newcapofnostaropab#1[#2]}}
\def\@newcapofnostaropab#1[#2]#3{\@newcapofnostaropaa#1[#2][]#3}
\def\@newcapofnostarnoop#1#2{\@newcapofnostaropaa#1[][]#2}
\def\@newcapofnostaropaa#1[#2][#3]#4{%
\runfigdefaults
\setkeys{runfig}{#3}
\expandafter\ifx\expandafter\relax\runfig@beginrange\relax%not beginfigrange
\expandafter\ifx\expandafter\relax\runfig@endrange\relax%not endfigrange
\expandafter\if\expandafter\relax\figrangestartlbl\relax%not running...default caption behavior
\@oldcaptionof{#1}[#2]{#4}
\else%is running, exclude from LOF
\@oldcaptionof{#1}[]{#4}
\fi
\else%is endfigrange
{\@oldcaptionof{#1}[]{#4}}%\captionof calls caption, which resets keys (hence group)
\endfigrange{\runfig@endrange}
\fi
\else%is beginfigrange
\@oldcaptionof{#1}[]{#4}
\beginfigrange
\fi
}
\def\@newcapstar{\@oldcaptionof}%the second option is not enabled for the starred variant
}
\makeatother


This redefines \caption and \captionof to have a second optional parameter, which controls the running figure range. The advantage here is that all of the captions would be defined normally, then the second parameter could be added without having to redefine the captions of the figures between them. Use is demonstrated below:

\begin{center}
\captionof{figure}[][beginrange]{Princess 1}
\label{fig:fifth}
%\beginfigrange
\end{center}


and

\begin{center}
\captionof{figure}[][endrange=Various princess page numbers wrong want a range]{Princess 2}
\label{fig:sixth}
%\endfigrange{Various princesses page numbers wrong want a range}
\end{center}