I would like to make a presentation with beamer, and have a few frames (at the start or the end) containing thumbnails of the other frames, hyperlinked to them, with something like 4x3 or 5x4 thumbnails per slide.
The aim would be to allow easy and quick navigation (just press the home
or end
key of the keyboard, and click on the frame you want to go to).
Result of take 3
The whole document
The thumbnails slide
Note that if there are too many thumbnails, they nicely spill across several slides.
thumbs-3.tex
Compile with:
rm -f thumbs-3-copy.pdf
cp thumbs-3.pdf thumbs-3-copy.pdf
pdflatex thumbs-3.tex
Source:
\documentclass{beamer}
\usepackage{thumbs-3}
\begin{document}
\showthumbs[Welcome]
\begin{frame}\Huge 1\end{frame}
\begin{frame}{With frame title}\Huge 2\end{frame}
\section{Example slides}
\begin{frame}\Huge 3\end{frame}
\subsection{Subsection slides}
\begin{frame}\Huge 4\only<2>{.1}\end{frame}
\subsubsection{Subsubsection slides}
\begin{frame}\Huge 5\end{frame}
\begin{frame}{Subsubsection slide with frame title}\Huge 6\end{frame}
\def\thumbframetitle{Title just for thumbnail}
\begin{frame}{Subsubsection slide with frame title and thumbnail title}\Huge 6\end{frame}
\def\thumbframetitle{}% Stop using a special frame title for thumbnails.
\showthumbs
\section{Random slides}
% Generate some random slides
\begin{onethumb}[2]
\foreach \i in {7,...,10} {
\begin{frame}
\begin{center}
\begin{tikzpicture}
\foreach \c in {0,...,3} {
\pgfmathsetmacro\r{0.6*rnd+0.3}
\pgfmathsetmacro\g{0.6*rnd+0.3}
\pgfmathsetmacro\b{0.6*rnd+0.3}
\definecolor{CircleColor}{rgb}{\r,\g,\b}
\node[circle, fill=CircleColor, minimum size=rnd*2cm] at (rnd*5cm-2.5cm, rnd*4cm-2cm) {};
}
\node at (0,0) {\Huge\i};
\path[use as bounding box] (-3.5cm,-3cm) rectangle (3.5cm,3cm);
\end{tikzpicture}
\end{center}
\end{frame}
}
\end{onethumb}
\foreach \i in {11,...,18} {
\begin{frame}{Random slide \i}\Huge\i\end{frame}
}
\begin{frame}[allowframebreaks]{Testing allowframebreaks}
\begin{itemize}
\item A
\foreach \i in {1,...,20} {
\item Very \i
}
\item Long
\item List
\end{itemize}
\end{frame}
\showthumbs[Any questions?]
\end{document}
thumbs-3.sty
\usepackage{morewrites}
\usepackage{graphicx}
\usepackage{tikz}
\usepackage{etextools}
% https://tex.stackexchange.com/q/40297/5699
\newcommand\addtofrml[1]{%
\write\@auxout{\noexpand\@writefile{frml}{\noexpand #1}}%
}
\newcommand{\loadframelist}{\ifx\thumbtitles\undefined\xdef\thumbtitles{}\fi\@starttoc{frml}}
\newcounter{thumbid}
\setcounter{thumbid}{0}
\newif\ifaddthumb
\newif\ifaddcomma\addcommafalse
\def\testaddthumb{\addthumbtrue}
% Hook at the end of each frame (could work if hooked at the beginning I guess).
\let\thumbframetitle\empty
\setbeamertemplate{background}{%
\addtocounter{thumbid}{1}%
\testaddthumb%
\ifaddthumb%
% Get either the frametitle, current (sub)(sub)section title, or just "Frame 42"
\xdef\thumbtitle{}%
\ifx\thumbtitle\empty\ifx\thumbframetitle\empty\else\xdef\thumbtitle{\thumbframetitle}\fi\fi%
\ifx\thumbtitle\empty\ifx\beamer@frametitle\empty\else\xdef\thumbtitle{\beamer@frametitle}\fi\fi%
\ifx\thumbtitle\empty\ifx\subsubsecname\undefined\else\xdef\thumbtitle{\subsubsecname}\fi\fi%
\ifx\thumbtitle\empty\ifx\subsecname\undefined\else\xdef\thumbtitle{\subsecname}\fi\fi%
\ifx\thumbtitle\empty\ifx\secname\undefined\else\xdef\thumbtitle{\secname}\fi\fi%
\ifx\thumbtitle\empty \xdef\thumbtitle{Frame \arabic{thumbid}}\fi%
% Append ",42/Frame Title" to \thumbtitles
\ifaddcomma%
\addtofrml{%
\xdef\noexpand\thumbtitles{\noexpand\thumbtitles,\arabic{thumbid}/\thumbtitle}%
}%
\else%
\addtofrml{%
\xdef\noexpand\thumbtitles{\arabic{thumbid}/\thumbtitle}%
}%
\global\addcommatrue%
\fi%
\fi%
}
% Should take an overlay specification, but I don't know how to handle these.
\newenvironment{onethumb}[1][1]{%
\gdef\onethumbcount{0}%
\gdef\onethumbgoal{#1}%
\gdef\testaddthumb{%
\count@=\onethumbcount%
\advance\count@ by 1%
\xdef\onethumbcount{\the\count@}%
\ifnum\onethumbcount=\onethumbgoal\relax\addthumbtrue\else\addthumbfalse\fi%
}%
}{%
\gdef\testaddthumb{\addthumbtrue}%
}
\newenvironment{nothumbs}{
\gdef\testaddthumb{\addthumbfalse}
}{
\gdef\testaddthumb{\addthumbtrue}
}
\newcommand\showthumbs[1][]{
{
\setbeamertemplate{frametitle continuation}{}
\begin{nothumbs}
\begin{frame}[allowframebreaks]{#1}
\pgfmathsetmacro\thumbwidth{0.2*\linewidth}
\pgfmathsetmacro\thumbheight{\thumbwidth/\paperwidth*\paperheight}
\loadframelist
\begin{center}
\foreach \thid/\thtitle in \thumbtitles {%
\hyperlink{page.\thid}{%
% https://tex.stackexchange.com/a/39601/5699
\begin{tikzpicture}[baseline={([yshift={-\ht\strutbox}]current bounding box.north)}]
\node[draw, inner sep=0pt] (thumb) {
\IfFileExists{\jobname-copy.pdf}{%
\includegraphics[width=\thumbwidth pt, height=\thumbheight pt, page=\thid] {\jobname-copy.pdf}
}{%
\begin{minipage}[t][\thumbheight pt]{\thumbwidth pt}\vfill No thumbnail\vfill\end{minipage}
}%
};
\node[anchor=north, font=\tiny, inner xsep=0pt, inner ysep=0.1cm, yshift=-0.05cm, text width=\thumbwidth pt, align=center] at (thumb.south) {\thtitle};
\end{tikzpicture}%
} % space here, outside of hyperlink
}%
\end{center}
\end{frame}
\end{nothumbs}
}
}
Take 1
Here's a first (a posteriori, added long after the question was asked) attempt :
- Use tikz's
\foreach
loop to produce a series of\includegraphics
including pages of the PDF generated by the previous compilation. The(edit: Instead I wrote the titles and frame numbers of each frame that has a thumbnail.)totcount
package should help knowing the range of frames (pages) to include.\hyperlink{page.42}{\includegraphics...}
should turn those thumbnails to hyperlinks (from this TeX.SX answer).
thumbs-1.tex
:
\documentclass{beamer}
\usepackage{thumbs-1}
\begin{document}
\showthumbs
inserts one or more frames containing thumbnails of the other frames, hyperlinked to the actual frame. It can be called at the beginning of the document:
\showthumbs
Some slides with and without \frametitle
s, inside and outside of various levels of (sub)(sub)sections:
\begin{frame}\Huge 1\end{frame}
\begin{frame}{With frame title}\Huge 2\end{frame}
\section{Example slides}
\begin{frame}\Huge 3\end{frame}
\subsection{Subsection slides}
A frame with an ovelay:
\begin{frame}\Huge 4\only<2>{.1}\end{frame}
\subsubsection{Subsubsection slides}
\begin{frame}\Huge 5\end{frame}
\begin{frame}{Subsubsection slide with frame title}\Huge 6\end{frame}
\section{Random slides}
\showthumbs
can be called multiple times, and in the middle of the document:
\showthumbs
Some random slides, with a single thumbnail (for the second in the group):
\begin{onethumb}[2]
\foreach \i in {7,...,10} {
\begin{frame}
\begin{center}
\begin{tikzpicture}
\foreach \c in {0,...,3} {
\pgfmathsetmacro\r{0.6*rnd+0.3}
\pgfmathsetmacro\g{0.6*rnd+0.3}
\pgfmathsetmacro\b{0.6*rnd+0.3}
\definecolor{CircleColor}{rgb}{\r,\g,\b}
\node[circle, fill=CircleColor, minimum size=rnd*2cm] at (rnd*5cm-2.5cm, rnd*4cm-2cm) {};
}
\node at (0,0) {\Huge\i};
\path[use as bounding box] (-3.5cm,-3cm) rectangle (3.5cm,3cm);
\end{tikzpicture}
\end{center}
\end{frame}
}
\end{onethumb}
Other dummy slides, with one thumbnail each:
\foreach \i in {11,...,20} {
\begin{frame}{Dummy slide \i}\Huge\i\end{frame}
}
\showthumbs
can be called multiple times, and at the end of the document:
\showthumbs
\end{document}
thumbs-1.sty
:
\usepackage{graphicx}
\usepackage{tikz}
\usepackage{etextools}
Using information from this question, write a list of frame numbers and frame titles to the aux file:
\newcommand\addtofrml[1]{%
\write\@auxout{\noexpand\@writefile{frml}{\noexpand #1}}%
}
\newcommand{\loadframelist}{\ifx\thumbtitles\undefined\xdef\thumbtitles{}\fi\@starttoc{frml}}
\newcounter{thumbid}
\setcounter{thumbid}{0}
\newif\ifaddthumb
\def\testaddthumb{\addthumbtrue}
Hook at the end of each frame (could work if hooked at the beginning I guess):
\setbeamertemplate{background}{%
\addtocounter{thumbid}{1}%
\testaddthumb%
\ifaddthumb%
The thumbails are labeled with the \frametitle
, susubsection
title, subsection
title, section
title or "Frame ", in that order of preference:
\xdef\thumbtitle{%
\ifx\beamer@frametitle\empty%
\ifx\insertsubsubsectionhead\empty%
\ifx\insertsubsectionhead\empty%
\ifx\insertsectionhead\empty%
Frame \arabic{thumbid}%
\else\insertsectionhead\fi%
\else\insertsubsectionhead\fi%
\else\insertsubsubsectionhead\fi%
\else\beamer@frametitle\fi%
}%
Append ,42/Frame Title
to the \thumbtitles
macro:
\ifaddcomma
\addtofrml{%
\xdef\noexpand\thumbtitles{\noexpand\thumbtitles,\arabic{thumbid}/\thumbtitle}%
}%
\else
\addtofrml{%
\xdef\noexpand\thumbtitles{\arabic{thumbid}/\thumbtitle}%
}%
\global\addcommatrue
\fi
\fi%
}
\begin{onethumb}[42]…\end{onethumb}
allows one to have just one thumbnail (of the 42th frame in the group, one by default) for a group of frames. This should really use beamer's overlay specification, to allow having a thumbnail for frames 2 to 4 and 6 by typing \begin{onethumb}<2-4,6>…\end{onethumb}
, but I haven't implemented that yet.
\newenvironment{onethumb}[1][5]{%
\gdef\onethumbcount{0}%
\gdef\onethumbgoal{#1}%
\gdef\testaddthumb{%
\count@=\onethumbcount%
\advance\count@ by 1%
\xdef\onethumbcount{\the\count@}%
\ifnum\onethumbcount=\onethumbgoal\relax\addthumbtrue\else\addthumbfalse\fi%
}%
}{%
\def\testaddthumb{\addthumbtrue}%
}
\begin{nothumbs}…\end{nothumbs}
disables the creation of thumbnails for the enclosed frames.
\newenvironment{nothumbs}{
\def\testaddthumb{\addthumbfalse}
}{
\def\testaddthumb{\addthumbtrue}
}
\def\showthumbs{
The frames containig the thumbnails, generated by \showthumbs
, are enclosed in \begin{nothumbs}…\end{nothumbs}
, otherwise after the second compilation, they would contain a copy of themselves, and more recursively at each compilation, so the PDF would grow larger and larger.
\begin{nothumbs}
\begin{frame}[allowframebreaks]
\pgfmathsetmacro\thumbwidth{0.2*\linewidth}
\pgfmathsetmacro\thumbheight{\thumbwidth/\paperwidth*\paperheight}
\loadframelist
\begin{center}
Loop over the \thumbtitles
macro defined in the .aux
/.frml
file.
\foreach \thid/\thtitle in \thumbtitles {%
\hyperlink{page.\thid}{%
Adjust vertical alignment of the tikzpicture containing the thumbnail and its label using this answer:
\begin{tikzpicture}[baseline={([yshift={-\ht\strutbox}]current bounding box.north)}]
The framed and scaled thumbnail:
\node[draw, inner sep=0pt] (thumb) {%
\includegraphics[width=\thumbwidth pt, height=\thumbheight pt, page=\thid] {\jobname.pdf}%
};
The label, below the thumbnail:
\node[anchor=north, font=\tiny, inner xsep=0pt, inner ysep=0.1cm, yshift=-0.05cm, text width=\thumbwidth pt, align=center] at (thumb.south) {\thtitle};
\end{tikzpicture}%
Insert a space between each thumbnail, but only one (the rest of the lines that matter swpace-wise end with a %
). The space must be outside the \hyperlink
, as explaind in this answer to my follow-up question.
} % space here, outside of hyperlink
}%
\end{center}
\end{frame}
\end{nothumbs}
}
What I don't know is how to run the \includegraphics
commands only if the PDF exists, like during first compilation. Running the \includegraphics
commands then would cause an error, and no PDF would be generated because of the error… Chicken and egg problem :) .
I'd appreciate a solution to this last problem (running \includegraphics
commands only if the PDF exists), or any other approach to automatically generating those slides.
Take 2
With this answer, I can run \includegraphics commands conditionnally.
I simply replace this code (at the end of the .sty
):
\node[draw, inner sep=0pt] (thumb) {%
\includegraphics[width=\thumbwidth pt, height=\thumbheight pt, page=\thid] {\jobname.pdf}%
};
By this one:
\node[draw, inner sep=0pt] (thumb) {%
\IfFileExists{\jobname.pdf}{%
\includegraphics[width=\thumbwidth pt, height=\thumbheight pt, page=\thid] {\jobname.pdf}%
}{%
\begin{minipage}[t][\thumbheight pt]{\thumbwidth pt}\vfill No thumbnail\vfill\end{minipage}%
}%
};
I now have another problem: When pdflatex
is generating the document, it overwrites the pdf from which \includegraphics
will try to read.
MWE:
\documentclass{beamer}
\usepackage{graphicx}
\usepackage{tikz}
\begin{document}
\begin{frame}\Huge 1\end{frame}
\begin{frame}\Huge 2\end{frame}
\begin{frame}\Huge 3\end{frame}
\begin{frame}\Huge 4\end{frame}
\begin{frame}
\foreach \i in {1,...,4} {
\IfFileExists{\jobname.pdf}{%
\fbox{\includegraphics[width=0.2\linewidth, page=\i] {\jobname.pdf}}%
}{No thumbnail}
}
\end{frame}
\end{document}
When I run the above example, I get the following error, since \jobname.pdf
has already been opend for writing and truncated by pdflatex
:
Error: PDF file is damaged - attempting to reconstruct xref table...
Error: Couldn't find trailer dictionary
Error: Couldn't read xref table
!pdfTeX error: pdflatex (file ./thumbs.pdf): xpdf: reading PDF image failed
Take 3
If instead, I manually copy \jobname.pdf
to \jobname-copy.pdf
(edit: changed .copy.pdf
to -copy.pdf
because it faild for me sometimes) after each compilation, and use the following code in the .sty
, it works fine:
\node[draw, inner sep=0pt] (thumb) {%
\IfFileExists{\jobname-copy.pdf}{%
\includegraphics[width=\thumbwidth pt, height=\thumbheight pt, page=\thid] {\jobname-copy.pdf}%
}{%
\begin{minipage}[t][\thumbheight pt]{\thumbwidth pt}\vfill No thumbnail\vfill\end{minipage}%
}%
};
Compilation needs to be done this way:
rm -f thumbs-3-copy.pdf
cp thumbs-3.pdf thumbs-3-copy.pdf
pdflatex thumbs-3.tex
When the number of frames in the document decreases, during the next (second) compilation, the \includegraphics
shall fail, preventing the generation of the PDF. Then, the thumbs-3-copy.pdf
file is removed, and a new copy fails to be created (because the PDF generation failed). So at the next (third) compilation, since the thumbs-3-copy.pdf
file is absent, no\includegraphics
is attempted, and the generation succeeds. At the next (fourth) compilation, the thumbnails are finally created.
If you don't understand the above, try reducing the number of frames from 5 to 2 (for example), without running the rm
command: compilation will fail permanently. With the rm
, everything is settled after something like 4 compilations.
Take 4
I tried to automatically copy the pdf (using this TeX.SX question's answers), but even if I run the copy just after \documentclass{beamer}
, the copy is still a truncated file (the code breaks if I move the copy code before the documentclass
, but I don't think it will make a difference). Here's the code doing the copy:
\documentclass{beamer}
\newread\in%
\openin\in=\jobname.pdf%
\newwrite\out%
\immediate\openout\out\jobname-copy.pdf%
\endlinechar-1%
\loop \unless\ifeof\in%
\readline\in to\l%
\immediate\write\out{\l}%
\repeat%
\immediate\closeout\out%
\closein\in%
\usepackage{graphicx}
\usepackage{tikz}
\begin{document}
\begin{frame}\Huge 1\end{frame}
\begin{frame}\Huge 2\end{frame}
\begin{frame}\Huge 3\end{frame}
\begin{frame}\Huge 4\end{frame}
\begin{frame}
\foreach \i in {1,...,4} {
\IfFileExists{\jobname-copy.pdf}{\fbox{\includegraphics[width=0.2\linewidth, page=\i] {\jobname-copy.pdf}}}{No thumbnail}
}
\end{frame}
\end{document}
So I'd like to know if it's possible to tell (La)TeX to backup the old PDF file before generating a new one, using only (La)TeX code.
Take 5
We thought this wasn't possible, as pdflatex
had already opened and truncated the file by the time it is copied, which results in a copy containing a single newline byte.
We were wrong.
pdflatex
doesn't open the output file until it has something to do with it, and it won't open it before the \documentclass{beamer}
(actually, it doesn't even open it after a \documentclass{article}
).
So, by moving the copy code above the \documentclass{beamer}
, and by adding \endlinechar13
to prevent beamer from breaking, one can copy the pdf file.
\newread\in%
\openin\in=\jobname.pdf%
\newwrite\out%
\immediate\openout\out\jobname-copy.pdf%
\endlinechar-1%
\loop \unless\ifeof\in%
\readline\in to\l%
\immediate\write\out{\l}%
\repeat%
\immediate\closeout\out%
\closein\in%
\endlinechar13%
\documentclass{beamer}
\usepackage{graphicx}
\usepackage{tikz}
\begin{document}
\begin{frame}\Huge 1\end{frame}
\end{document}
However, the copied PDF is damaged, because when \read
reads a line, it drops any trailing spaces, which are important in this case.
Take 6
I asked how to read from a file including trailing spaces, but the answers are disapointing~:
- No hope with plain TeX.
- Pdf(La)Tex provides
\pdffiledump
, which solves the reading problem, but the way non-ASCII bytes are written (when you write to the copy) is implementation-dependant. - One can perform the copy in lua, when using Lua(La)Tex, but not everybody uses Lua(La)Tex.
Conclusion
So it seems the best solution when using \includegraphics
is either to copy the PDF manually, or use Lua(La)Tex (wich is the future, but not as widespread as it "should" I guess, see this very interesting interview of Andrew Stacey on TeX.SX's [http://tex.blogoverflow.com/](community blog)).
I have provided as an answer below a proof of concept that re-\input
s the tex file, and generates the thumbnails without relying on the PDF from a previous compilation, but it's much more fragile than the \includegraphics
version.
References
(Non-exhaustive) list of stuff I used/didn't use while working on this:
- Overlay specifications:
- Generating the list of slides:
- Create a list of beamer slides
- How to get the current (sub)(sub)section name: What variable current section name? on latex-community.org
- Choose between the frame title, (sub)(sub)section and default string: What is the difference between \empty and @empty?
- Use a loop to generate a list for another (foreach) loop
- Alignment of the thumbnails (with their text): https://tex.stackexchange.com/a/39601/5699
- PDF hyperlinks to a given page
- File copy:
- File copy without expansion
- Flush latex file io buffers
- Include binary data
- How to copy contents of one file to another?
- My own question: \read: trailing spaces are removed, with two excellent answers
\write
non-printable ASCII characters to a file- In a desperate attempt, I wanted to change the
\jobname
half of the time, but it won't affect the PDF file name: How to influence the name of the pdf file created with pdfLaTeX (from within the source code)?
- Andrew Stacey's interview
\tracingall
(I keep forgetting the name of that one !)
\insertsectionhead
insert hyperlinks instead of just the text, which can't be written to the aux file, so I had to use\secname
instead). I also included a few improvements and bugfixes. Note it'll crash badly (you'll have to erase the.aux
and friends to recover from that) if you insert any "advanced punctuation", accents or fragile commands in the frame title. To circumvent that, you can provide a\thumbframetitle
without weird stuff (and leave weird stuff in the regular frame title). I hope to fix it someday.