# Dynamically hide LaTeX beamer frame based on tags

I guess I'm in a configuration that will ring the bell to many: in the context of a project that run for many years, I developed a very large Beamer presentation featuring a lot of slides. The LaTeX sources are split in many files and as I have to often redo the presentation of the project, I include (or nor) these files in the main document depending on the time / audience.

Now I would like to somehow automate the generation of the presentation by controlling the frame I wish to include. It seems to be possible statically with the <handout:0|beamer:0> parameters upon frame definition:

\begin{frame}
Hello.
\end{frame}

% this frame won't be included
\begin{frame}<handout:0|beamer:0>
Hidden
\end{frame}


I would like to render the approach more generic. Thus i was thinking about the possibility to

• associate a [set of] tag upon frame definition
• decide later (in some way) about the set of tags to use when compiling the presentation such that only the frame having this tag are considered (and the other are thus hidden).

Does anybody knows a way to do that?

-

## migrated from stackoverflow.comMar 23 '14 at 2:18

This question came from our site for professional and enthusiast programmers.

This is a really nice question!

This is a start of a solution highly inspired by Werner's comment and with few limitations. They are due to the fact that the solution is on top of Beamer. For such a reason I mark it community wiki and every body can improve it (and there's lot of room).

It provides a simple user interface with the aim of

• creating tags for the differentiating version(s);
• deciding which version(s) output.

The first task is provided by the keys

1. create new version
2. create multiple versions

and the names are quite clear. To decide which frames output, exploit:

1. compile version
2. compile frames to select a subset of frames within one version
3. compile generic frames to select frames from different versions.

As said, the mechanism is pretty much described in Werner's comment: creating a new version simply means that a new counter (or more) is instantiated and updated each time a frame is tagged that way. Compiling versions is traduced behind the scenes with the creation of a list that is passed to \includeonlyframes. So far so good.

Let us see now limits and week points of the approach. Tagging frames in the aforementioned way without going really underneath Beamer, requires a bit of trickery, namely a new environment. For the purpose, a sebframe environment has been defined which accepts an optional argument with the following syntax

<tag>-<list of beamer options>


The character - is used to split the tag from other Beamer options; incidentally, this misses up with fragile.

The mwe. It is commented and take it as a proof of concept: a few things can be improved as comments point out.

\documentclass{beamer}
\usepackage{lmodern} % http://tex.stackexchange.com/a/58088/13304
\usepackage{mwe} % for dummy image

\newcommand{\enlarge}{\centering\scalebox{13}}% Just for this example

% = = = = = = = = = = = = = = = = = = = = = = = =
% core of the solution

\usepackage{pgffor,pgfkeys,xstring}

\newcommand{\deflistframes}[1]{
\gdef\listframes{}% global list
\foreach \x[count=\xi] in {#1}{\global\let\maxitems\xi}% count the max number of items
\foreach \x[count=\xi] in {#1}{
\ifnum\xi=\maxitems
\xdef\listframes{\listframes \vers\x}
\else
\xdef\listframes{\listframes \vers\x,}
\fi
}
}

\newcommand{\defcustomlistframes}[1]{
\gdef\listframes{}% global list
\foreach \x[count=\xi] in {#1}{\global\let\maxitems\xi}% count the max number of items
\foreach \x[count=\xi] in {#1}{
\ifnum\xi=\maxitems
\xdef\listframes{\listframes \x}
\else
\xdef\listframes{\listframes \x,}
\fi
}
}

\makeatletter
% http://tex.stackexchange.com/a/74890/13304
\newcommand{\defversion}[1]{\@namedef{vers#1}{}}
\makeatother

% test for multiple versions compilation
\newif\ifmultipleversion
\multipleversionfalse

\pgfkeys{/seb-beamer/.cd,
create new version/.code={\gdef\vers{#1}\newcounter{#1}},
create multiple versions/.code={%
\gdef\listofversions{}
\foreach \v in {#1}{%
\defversion{\v}
\newcounter{\v}
\xdef\listofversions{\listofversions \v,}
}%
\multipleversiontrue
},
compile version/.code={% version-based: need to store in .aux a
% "total frame" counter per version (incremented in custom enviroment)
% and use it as final value (hard-coded here for simplicity)
\deflistframes{1,...,1000}
% list of frames in output
\expandafter\includeonlyframes\expandafter{\listframes}%
},
compile frames/.code={% this should be version-based -> second argument (TBA)
\deflistframes{#1}
% for the list
\expandafter\includeonlyframes\expandafter{\listframes}%
},
compile generic frames/.code={% generic for multi version compilations
\defcustomlistframes{#1}
% list of frames in output
\expandafter\includeonlyframes\expandafter{\listframes}%
},
}

\makeatletter

% see: http://tex.stackexchange.com/a/132411/13304
\newenvironment{sebframe}[1][]{
% cut the string to separate version from other options
\StrCut{#1}{-}\compvers\opt
\edef\seboptions{\opt}
\def\mylabel{}
\ifmultipleversion
\@ifundefined{c@\compvers}{}{% test if the counter is undefined
\stepcounter{\compvers}
% increment here the counter total frames per version
\edef\mylabel{label=\compvers\the\value{\compvers}}
}
\else
\IfStrEq{\vers}{\compvers}{% true
\stepcounter{\compvers}
% increment here the counter total frames per version
\edef\mylabel{label=\compvers\the\value{\compvers}}
}{% false
\edef\mylabel{}
}
\fi
\begingroup\edef\x{\endgroup\noexpand\begin{frame}[\mylabel,\seboptions]}\x}{\end{frame}}

\makeatother

\newcommand{\SebCompileOptions}[1]{%
\pgfkeys{/seb-beamer/.cd,#1}
}

% = = = = = = = = = = = = = = = = = = = = = = = =
% user interface

%\SebCompileOptions{create new version=A, compile version=A}
%\SebCompileOptions{%
% create multiple versions={A,B},
% compile generic frames={A1,A3,B1,B2,C3}
%}
\SebCompileOptions{create new version=C, compile frames={1,3}}

\begin{document}

\begin{sebframe}[A-t,allowframebreaks]
\frametitle{Version A}
\enlarge{A1}
\end{sebframe}

\begin{sebframe}[B]
\frametitle{Version B}
\enlarge{B1}
\end{sebframe}

\begin{sebframe}[C]
\frametitle{Version C}
\enlarge{C1}
\end{sebframe}

\begin{sebframe}[A]
\frametitle{Version A}
\enlarge{A2}
\end{sebframe}

\begin{sebframe}[B]
\frametitle{Version B}
\enlarge{B2}
\end{sebframe}

\begin{sebframe}[C]
\frametitle{Version C}
\enlarge{C2}
\end{sebframe}

\begin{frame}
\frametitle{Version B or C}
\enlarge{BC}
\end{frame}

\begin{sebframe}[A-t,b,containsverbatim]% just to point out that passing multiple beamer options works (unless fragile)
\frametitle{Version A}
\enlarge{A3}
\end{sebframe}

\begin{sebframe}[C]
\frametitle{Version C}
\enlarge{C3}
\vskip1ex
\includegraphics<2>[scale=0.5]{example-image}
\end{sebframe}

\begin{sebframe}[A]
\frametitle{Version A}
\enlarge{A4}
\end{sebframe}

\begin{frame}[fragile,label=A5]% add manually a label: displayed only in A version though
\frametitle{Version A \& B \& C}
\enlarge{ABC}
\end{frame}

\end{document}


As we selected:

\SebCompileOptions{create new version=A, compile version=A}


the output is:

By selecting

\SebCompileOptions{create new version=C, compile frames={1,3}}


Notice that overleyed-aware commands still work and frame C2 is not displayed. Let us try

\SebCompileOptions{%
create multiple versions={A,B},
compile generic frames={A1,A3,B1,B2,C3}
}


which produces:

As expected, since version C is not defined, the frame C3 is not displayed. The order in which one provides the list does not affect the order of appaearance in the PDF: only page numbers counts for that.

-
Impressive! I'll test that – Sebastien Varrette Apr 2 '14 at 15:38
@SebastienVarrette: take it really as a proof of concept. It can work under some assumptions that depend on the way in which you built your frames: for example, \begin{sebframe}[options]<something> up to now will just break because of <something>. I will come back on this, but not on the short term. – Claudio Fiandrino Apr 2 '14 at 18:22

I would create a bunch of conditionals, one for each type of presentation you want to give. Let's call these types "versions". So, perhaps there is VersionA, VersionB and VersionC.

For each version, create a conditional using \newif (they are set to false by default):

\newif\VersionA
\newif\VersionB
\newif\VersionC


Then, wrap your slides/frames associated with a specific version. For example, a VersionA slide would resemble

\ifVersionA
\begin{frame}
...
\end{frame}
\fi


If you have a slide that should be associated with (say) VersionB OR VersionC, then you can do

\makeatletter
\let\ifCombined\ifVersionB
\ifVersionC
\expandafter\@firstoftwo
\else
\expandafter\@secondoftwo
\fi
{\let\ifCombined\iftrue}{}
\makeatother

\ifCombined% Version B or C
\begin{frame}
..
\end{frame}
\fi


Those slides that are visible in all version don't need any of the conditional wrapping. Here's a minimal example of the setup:

\documentclass{beamer}% http://ctan.org/pkg/beamer
\let\Tiny=\tiny% http://tex.stackexchange.com/q/58087/5764
\newcommand{\enlarge}{\centering\scalebox{13}}% Just for this example

\newif\ifVersionA
\newif\ifVersionB
\newif\ifVersionC

%\VersionAtrue% Show Version A
\VersionBtrue% Show Version B
%\VersionCtrue% Show Version C

\begin{document}

\ifVersionA
\begin{frame}
\frametitle{Version A}
\enlarge{A1}
\end{frame}
\fi

\ifVersionB
\begin{frame}
\frametitle{Version B}
\enlarge{B1}
\end{frame}
\fi

\ifVersionC
\begin{frame}
\frametitle{Version C}
\enlarge{C1}
\end{frame}
\fi

\ifVersionA
\begin{frame}
\frametitle{Version A}
\enlarge{A2}
\end{frame}
\fi

\ifVersionB
\begin{frame}
\frametitle{Version B}
\enlarge{B2}
\end{frame}
\fi

\makeatletter
\let\ifCombined\ifVersionB
\ifVersionC
\expandafter\@firstoftwo
\else
\expandafter\@secondoftwo
\fi
{\let\ifCombined\iftrue}{}
\makeatother

\ifCombined% Version B or C
\begin{frame}
\frametitle{Version B or C}
\enlarge{BC}
\end{frame}
\fi

\ifVersionA
\begin{frame}
\frametitle{Version A}
\enlarge{A3}
\end{frame}
\fi

\ifVersionC
\begin{frame}
\frametitle{Version C}
\enlarge{C3}
\end{frame}
\fi

\ifVersionA
\begin{frame}
\frametitle{Version A}
\enlarge{A4}
\end{frame}
\fi

\begin{frame}
\frametitle{Version A \& B \& C}
\enlarge{ABC}
\end{frame}

\end{document}


You can do something similar by using the comment package. However, combining conditions is not that easy. A work-around, of course, would be to duplicate slides containing information for multiple versions.

Another viable option would be to create a label for each frame using

\begin{frame}[label=ConceptX]
...
\end{frame}


or using the \frame command. And then add ConceptX to the versions you're interested in including using

\includeonlyframes{...,ConceptX,...}% Version A


This requires you to provide each frame with a conceptual (unique) label though.

-
I like the last approach (with labels) and the \includeonlyframes: I guess there is absolutely no way to have the same label to multiple frame and further display all the corresponding frames, not only the last one? – Sebastien Varrette Mar 24 '14 at 20:30
@SebastienVarrette: At the moment, no. label most likely translates to a TeX macro, which is overwritten with a new call using the same name. Perhaps one could create a new key-value interface, say labelgroup. Every similarly-named labelgroup could increment a counter labelling the first frame using labelgroup=groupA as groupA1, the next groupA2, the next groupA3..., and then provide a new macro \includeonlyframesingroup{groupA} (say) that expands to \includeonlyframes{groupA1,groupA2,...}. File a feature request. – Werner Mar 24 '14 at 20:43
Fyi, I just added the feature request. – Sebastien Varrette Mar 25 '14 at 19:00
Note that the \newif approach does not work if use are in \mode* mode or use the ignorenonframetext option. In that case, you should use \mode<all>\ifVersionA and \fi\mode* – Yannick Loiseau Jan 23 at 11:16