6

The following MWE processes groups of pictures, placing them on a slide with a caption below the picture. The slides are made using the pdfscreen package, and the picture and caption placement is done by creating a tikz node.

Lists of pictures and their captions are created with, for example: \newcommand*{SetA}{pic1/caption 1,pic2/caption 2}. The sets of pictures are in \newcommand*{\MySets}{SetA,SetB}.

I use a foreach loop to process each set of pictures. My question is: How can I wrap a foreach loop around the inner loop to process all the picture sets?

\documentclass[a4paper,11pt]{article}
\usepackage{etoolbox}
\usepackage[dvipsnames,svgnames,x11names]{xcolor}
\usepackage{tikz}
\usepackage[T1]{fontenc}
\usepackage[nopanel,screen]{pdfscreen}
\margins{1cm}{1cm}{1cm}{1cm}
\screensize{15.875cm}{20.32cm}

\newcommand*{\SetA}{example-image-a/caption A1,example-image-a/caption A2}
\newcommand*{\SetB}{example-image-b/caption B1,example-image-b/caption B2}
\newcommand*{\MySets}{\SetA,\SetB}

\begin{document}
%\foreach \Set in {\MySets}{
\foreach \myphoto/\mycaption in \SetA {% How to change \SetA to \Set to process all the pictures in each set
\centering
\begin{tikzpicture}
\node (a) at (0,0) {\includegraphics[width=0.9\linewidth,height=0.9\textheight]{\myphoto}};
\node[below=3mm] (b) at (a.south) {\mycaption};
\end{tikzpicture}
\clearpage
}
%}

\end{document} 
5

For this particular application, where the outer \foreach uses a list of lists, you can expand the macro containing the inner list before starting the inner loop.

\documentclass[a4paper,11pt]{article}
\usepackage{etoolbox}
\usepackage[dvipsnames,svgnames,x11names]{xcolor}
\usepackage{tikz}
\usepackage[T1]{fontenc}
\usepackage[nopanel,screen]{pdfscreen}
\margins{1cm}{1cm}{1cm}{1cm}
\screensize{15.875cm}{20.32cm}

\newcommand*{\SetA}{example-image-a/caption A1,example-image-a/caption A2}
\newcommand*{\SetB}{example-image-b/caption B1,example-image-b/caption B2}
\newcommand*{\MySets}{\SetA,\SetB}

\newcommand{\eforeach}[2]{\expandafter\doeforeach\expandafter{#1}{#2}}
\newcommand{\doeforeach}[2]{\foreach #2 in #1}

\begin{document}

\centering
\foreach \Set in \MySets {
  \eforeach{\Set}{\myphoto/\mycaption} {%
    \begin{tikzpicture}
    \node (a) at (0,0) {\includegraphics[width=0.9\linewidth,height=0.9\textheight]{\myphoto}};
    \node[below=3mm] (b) at (a.south) {\mycaption};
    \end{tikzpicture}
    \clearpage
  }
}

\end{document} 
|improve this answer|||||
4

If you are consistent in naming your sets, you can loop over the characters (in this case A and B) and build a macro with \csname and \endcsname. MWE:

\documentclass[a4paper,11pt]{article}
\usepackage{etoolbox}
\usepackage[dvipsnames,svgnames,x11names]{xcolor}
\usepackage{tikz}
\usepackage[T1]{fontenc}
\usepackage[nopanel,screen]{pdfscreen}
\margins{1cm}{1cm}{1cm}{1cm}
\screensize{15.875cm}{20.32cm}

\newcommand*{\SetA}{example-image-a/caption A1,example-image-a/caption A2}
\newcommand*{\SetB}{example-image-b/caption B1,example-image-b/caption B2}
\newcommand*{\MySets}{\SetA,\SetB}

\begin{document}
  \foreach \id in {A,B}{
    \edef\Set{\csname Set\id\endcsname}
    \foreach \myphoto/\mycaption in \Set {
      \centering
      \begin{tikzpicture}
          \node (a) at (0,0) {\includegraphics[width=0.9\linewidth,height=0.9\textheight]{\myphoto}};
        \node[below=3mm] (b) at (a.south) {\mycaption};
      \end{tikzpicture}
      \clearpage
    }
  }
\end{document} 
|improve this answer|||||
4

There are a two things here:

Firstly, you need to omit the braces around \MySets in the outer \foreach.

Secondly, \foreach is written in such a way that the inner loop is the equivalent of

\foreach \myphoto/\mycaption in {\SetA} {

when you try to loop the way you want to. In your case we can remedy this by expanding everything before the first \foreach:

\documentclass[a4paper,11pt]{article}
\usepackage{etoolbox}
\usepackage[dvipsnames,svgnames,x11names]{xcolor}
\usepackage{tikz}
\usepackage[T1]{fontenc}
\usepackage[nopanel,screen]{pdfscreen}
\margins{1cm}{1cm}{1cm}{1cm}
\screensize{15.875cm}{20.32cm}

\newcommand*{\SetA}{example-image-a/caption A1,example-image-a/caption A2}
\newcommand*{\SetB}{example-image-b/caption B1,example-image-b/caption B2}
\edef\MySets{{\SetA},{\SetB}}

\begin{document}

\foreach \Set in \MySets {
    \foreach \myphoto/\mycaption in \Set {
        \centering
        \begin{tikzpicture}
            \node (a) at (0,0) {\includegraphics[width=0.9\linewidth,height=0.9\textheight]{\myphoto}};
            \node [below=3mm] (b) at (a.south) {\mycaption};
        \end{tikzpicture}
        \clearpage
    }
}

\end{document}

If your use case is not more complex than this, you could also just avoid the nested macro definition in the first place:

\newcommand*\MySets{%
    {   example-image-a/caption A1,
        example-image-a/caption A2},
    {   example-image-b/caption B1,
        example-image-b/caption B2}%
}
|improve this answer|||||
2

A listofitems approach. Note that, since a comma is used to separate elements of the individual sets, the caption may need to be embraced, lest any commas appearing in the caption mess with the parsing. Alternatively, a different parsing separator could be used.

Note also with listofitems parsing, there is no computational need to have regularity in the set names. For example, one could have \newcommand*{\MySets}{\Foo,\Bar}, and as long as the sets \Foo and \Bar were defined appropriately, the parsing would work.

\documentclass[a4paper,11pt]{article}
\usepackage{etoolbox}
\usepackage[dvipsnames,svgnames,x11names]{xcolor}
\usepackage{tikz}
\usepackage[T1]{fontenc}
\usepackage[nopanel,screen]{pdfscreen}
\margins{1cm}{1cm}{1cm}{1cm}
\screensize{15.875cm}{20.32cm}
\newcommand*{\SetA}{example-image-a/caption A1,example-image-a/caption A2}
\newcommand*{\SetB}{example-image-b/caption B1,example-image-b/caption B2}
\newcommand*{\MySets}{\SetA,\SetB}
\usepackage{listofitems}
\begin{document}
\setsepchar{,}%
\readlist*\SetList{\MySets}%
\foreachitem\z\in\SetList[]{%
  \setsepchar[!]{,!/caption}%
  \expandafter\readlist\expandafter\ThisSet\expandafter{\z}%
  \foreachitem\zz\in\ThisSet[]{%
    \begin{tikzpicture}
    \node (a) at (0,0) {\includegraphics%
    [width=0.9\linewidth,height=0.9\textheight]{\ThisSet[\zzcnt,1]}};
    \node[below=3mm] (b) at (a.south) {\ThisSet[\zzcnt,2]};
    \end{tikzpicture}%
    \clearpage
  }
}
\end{document} 
|improve this answer|||||
2

Half for fun, here is a solution using LaTeX3. It takes care of expanding your \SetA, \SetB and \MySets macros:

  • early enough to make the \foreach happy, and;

  • exactly once, so that in case your inner lists (\SetA and \SetB) contain things that should not be expanded, or should not be expanded too early, this solution could be usable as is (as opposed to solutions that use \edef\MySets{{\SetA},{\SetB}} or something similar, which fully expands \SetA and \SetB).

\documentclass{article}
\usepackage[T1]{fontenc}
\usepackage{tikz}
\usepackage{xparse}
\usepackage[nopanel,screen]{pdfscreen}
\margins{1cm}{1cm}{1cm}{1cm}
\screensize{15.875cm}{20.32cm}

\ExplSyntaxOn

\clist_new:N \SetA
\clist_new:N \SetB
\clist_new:N \MySets

\clist_gset:Nn \SetA {example-image-a/caption~A1, example-image-a/caption~A2}
\clist_gset:Nn \SetB {example-image-b/caption~B1, example-image-b/caption~B2}
\clist_gset:Nn \MySets {\SetA, \SetB}

\cs_new_protected:Npn \ross_iterate_on_sets:NN #1#2
  {
    \clist_map_variable:NNn #2 #1
  }

\NewDocumentCommand \RossIterateOnSets { m m }
  {
    \ross_iterate_on_sets:NN #1 #2
  }

\cs_new_protected:Npn \ross_iterate_on_pics:nn #1#2
  {
     \foreach #1 ~in~ {#2}
  }

\cs_generate_variant:Nn \ross_iterate_on_pics:nn { nV }

\NewDocumentCommand \RossIterateOnPics { m m }
  {
    % First expansion of #2 (e.g. \Set -> \SetA or \SetB)
    \exp_args:Nno
    % Second expansion: use the *value* of \SetA or \SetB for the second
    % argument of \ross_iterate_on_pics:nn
    \ross_iterate_on_pics:nV {#1} {#2}
  }

\ExplSyntaxOff

\begin{document}

\RossIterateOnSets{\Set}{\MySets} {%
  \RossIterateOnPics{\myphoto/\mycaption}{\Set} {%
    \centering
    \begin{tikzpicture}
      \node (a) at (0,0) {%
        \includegraphics[width=0.9\linewidth,height=0.9\textheight]{\myphoto}};
      \node[below=3mm] (b) at (a.south) {\mycaption};
    \end{tikzpicture}
    \clearpage
  }%
}
\end{document}

If one of the elements of a comma-list should contain a comma, just wrap the whole element in braces.

In order to see for yourself that things are expanded as advertised before being fed to \foreach (i.e., each of \SetA, \SetB and \MySets is expanded exactly once before arriving in the hands of \foreach), please consider this slightly modified version, where we print to the terminal and log file precisely what the inner loop does:

\documentclass{article}
\usepackage{xparse}

\newcommand*{\dontExpandMe}{If this gets printed, it is GAME OVER.}

\ExplSyntaxOn

\clist_new:N \SetA
\clist_new:N \SetB
\clist_new:N \MySets

\clist_gset:Nn \SetA {example-image-a/caption~\dontExpandMe A1,
                      example-image-a/caption~A2}
\clist_gset:Nn \SetB {example-image-b/caption~B1,
                      example-image-b/caption~\dontExpandMe B2}
\clist_gset:Nn \MySets {\SetA, \SetB}

\cs_new_protected:Npn \ross_iterate_on_sets:NN #1#2
  {
    \clist_map_variable:NNn #2 #1
  }

\NewDocumentCommand \RossIterateOnSets { m m }
  {
    \ross_iterate_on_sets:NN #1 #2
  }

\cs_new_protected:Npn \ross_iterate_on_pics:nn #1#2
  {
     \typeout { \string\foreach \unexpanded {#1} ~in~ \unexpanded {{#2}} }
  }

\cs_generate_variant:Nn \ross_iterate_on_pics:nn { nV }

\NewDocumentCommand \RossIterateOnPics { m m }
  {
    % First expansion of #2 (e.g. \Set -> \SetA or \SetB)
    \exp_args:Nno
    % Second expansion: use the *value* of \SetA or \SetB for the second
    % argument of \ross_iterate_on_pics:nn
    \ross_iterate_on_pics:nV {#1} {#2}
  }

\ExplSyntaxOff

\begin{document}

\RossIterateOnSets{\Set}{\MySets} {%
  \RossIterateOnPics{\myphoto/\mycaption}{\Set}
}
\end{document}

Here is the output (on the terminal and in the log file) of the two \typeout commands of this example:

\foreach\myphoto /\mycaption  in {example-image-a/caption \dontExpandMe A1,example-image-a/caption A2}
\foreach\myphoto /\mycaption  in {example-image-b/caption B1,example-image-b/caption \dontExpandMe B2}

As you can see, the \dontExpandMe macro is not expanded. In the full example, this means it would not be expanded before the \foreach is executed.

Note: \mycaption is followed by two spaces in this output:

  • the first one is from \typeout printing a control word (i.e., a control sequence composed of letters only);

  • the second one is because we put an explicit space token (~ in expl3 syntax) before the word in.

Similarly, \dontExpandMe is followed by exactly one space in the output because it is a control word printed by \typeout.

Note 2: using \unexpanded is necessary to see the tokens as is. This is because \typeout expands everything inside its argument (like \message, but \typeout is also \protect-aware as explained in this answer).

|improve this answer|||||
  • 1
    I added explanations and a modified example to illustrate how expansion of the clist variables is done in my solution (one of its strong points). – frougon May 3 '19 at 15:56

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.