1

I am trying to populate a tikz matrix dynamically using foreach to loop over a list-argument and breaking it further apart using xstring operations.

I am hitting an issue where, as far as I can tell, the temporary macro produced by \StrBefore gets lost / is unreadable further down the macro chain.

The compiler tells me that the file icons/.png could not be found - which boils down to the macro expansion not working as far as I understand it (and LaTeX terminology):

LaTeX Warning: File `icons/.png' not found on input line 63.

! Unable to load picture or PDF file 'icons/.png'.
<to be read again> 
                   }
l.63 ...{(0,0)}{Fish}{1}{Farmers:800,Workers:1200}

Further I get an actual error telling me about an undefined control sequence. This is the temporary macro generated by \StrBefore:

! Undefined control sequence.
<recently read> \templeft 

l.63 ...{(0,0)}{Fish}{1}{Farmers:800,Workers:1200}

Here's a MWE:

\documentclass{article}

\usepackage{calc}
\usepackage{geometry}
\usepackage{tikz}
\usepackage{etoolbox}
\usepackage{xparse}
\usepackage{xstring}

% PAGE GEOMETRY
\geometry{a4paper,
          landscape,
          top=1cm,
          bottom=1cm,
          left=1cm,
          right=1cm,
          includehead,
          includefoot}
\usetikzlibrary{calc, matrix}
% END PAGE GEOMETRY

% META

% sizes
\newcommand{\unitsize}{1cm}

% #1 > (x,y)
% #2 > Object_Name (filename without .png)
% #3 Amount
% #4 > Comma Separated list: <stratum>:<amount>,<stratum>:<amount>, ...
% >{\SplitList{,}}
\NewDocumentCommand{\consnode}{m m m m}{
    \node (#2) at #1 {
        \let\matrixcontents\empty
        \foreach \c in {#4}{
            \StrBefore{\c}{:}[\templeft]
            \StrBehind{\c}{:}[\tempright]

            \expandafter\gappto\expandafter\matrixcontents{\expandafter\consimg{\expandafter\templeft} \&}
            \expandafter\gappto\expandafter\matrixcontents\expandafter{\tempright \\}
        }

        \begin{tikzpicture}
            \node[label={[label distance=-(0.35*\unitsize)]105:#3×}] at (0,0) {
                \includegraphics[width=\unitsize, height=\unitsize]{icons/#2}};
            \matrix[row sep=-2mm, matrix of nodes, ampersand replacement=\&] (output) at (0,-1) {
                \matrixcontents
                %\includegraphics[width=\unitsize/2, height=\unitsize/2]{icons/example-image-b.png} \& 800 \\
                %\includegraphics[width=\unitsize/2, height=\unitsize/2]{icons/example-image-c.png} \& 1200 \\
                };
        \end{tikzpicture}};
}

\newcommand{\consimg}[1]{
    \includegraphics[width=\unitsize/2, height=\unitsize/2]{icons/#1.png}
}

% END META

\begin{document}

\begin{tikzpicture}
    \consnode{(0,0)}{example-image-a}{1}{example-image-b:800,example-image-c:1200}
\end{tikzpicture}

\end{document}

(The commented out parts in the matrix definition produce the desired output, see also the img below, and are what I am trying to replace
!Please note that I replaced the image-names with example names as recommended in comments, the image names in my command serve double-duty as the identifier-names for tikz nodes)


This is the desired result I want to achieve (which I've achieved by hardcoding the matrix): a graphic of fish above two icons stylizing two different peoples, each with the number 800 next to it


I am using Overleaf (compiling with XeLaTeX) and would prefer to keep working with it.

I've cobbled the code together with help from various documentations, but in this case mainly the following two questions:

Tikz foreach inside matrix

Foreach loop with command inside Tikz matrix

  • 3
    Welcome to TeX-SE! Please consider making your example more accessible to everyone. Most of us do not have, say, a graphics Farmers in their TeX path. So please replace these by something like example-image-a. – marmot Apr 20 at 15:50
  • Is the file extension the same as you wrote it on the MWE? – manooooh Apr 20 at 15:51
  • @marmot thanks for the tip, is there any list of generally accepted sample images? – dot_Sp0T Apr 20 at 16:14
  • 1
    Most users will have example-image, example-image-duck, example-image-a, example-image-b and example-image-c in their TeX path. – marmot Apr 20 at 16:15
  • @marmot I'll go with the boring ones for extra safety.. – dot_Sp0T Apr 20 at 16:18
4

as posted the first error is

! LaTeX Error: File `icons/example-image-a' not found.

I deleted icons/

You then get

! Undefined control sequence.
<recently read> templeft 

because you are just defining that token locally but then adding it outside the group.

   \expandafter\gappto\expandafter\matrixcontents\expandafter{\tempright \\}

adds the expansion of \tempright followed by \\to \matrixcontents

but

 \expandafter\gappto\expandafter\matrixcontents{\expandafter 
    \consimg{\expandafter\templeft} \&}

The first two \expandafter apply expansion to { which is a non expandable token so it is the same as

       \gappto\matrixcontents{\expandafter\consimg{\expandafter\templeft} \&}

so adds \expandafter\consimg{\expandafter\templeft} \& to \matrixcontents but (a) \templeft will not be defined by then and (b) when this is used, the first \expandafter again applies to { so does nothing.

You want to expand \templeft as in the \tempright case, so:

        \expandafter\gappto\expandafter\matrixcontents\expandafter
                    {\expandafter\consimg\expandafter{\templeft} \&}

enter image description here

This seems vastly over-complicated to just set three images with three labels, it isn't clear why you need xstring or tikz at all but perhaps your real document uses these features and it is just an artefact of the small example.

\documentclass{article}

\usepackage{calc}
\usepackage{geometry}
\usepackage{tikz}
\usepackage{etoolbox}
\usepackage{xparse}
\usepackage{xstring}

% PAGE GEOMETRY
\geometry{a4paper,
          landscape,
          top=1cm,
          bottom=1cm,
          left=1cm,
          right=1cm,
          includehead,
          includefoot}
\usetikzlibrary{calc, matrix}
% END PAGE GEOMETRY

% META

% sizes
\newcommand{\unitsize}{1cm}

% #1 > (x,y)
% #2 > Object_Name (filename without .png)
% #3 Amount
% #4 > Comma Separated list: <stratum>:<amount>,<stratum>:<amount>, ...
% >{\SplitList{,}}
\NewDocumentCommand{\consnode}{m m m m}{
    \node (#2) at #1 {
        \let\matrixcontents\empty
        \foreach \c in {#4}{
            \StrBefore{\c}{:}[\templeft]
            \StrBehind{\c}{:}[\tempright]

            \expandafter\gappto\expandafter\matrixcontents\expandafter
                        {\expandafter\consimg\expandafter{\templeft} \&}
            \expandafter\gappto\expandafter\matrixcontents\expandafter{\tempright \\}
        }

        \begin{tikzpicture}
            \node[label={[label distance=-(0.35*\unitsize)]105:#3×}] at (0,0) {
                \includegraphics[width=\unitsize, height=\unitsize]{#2}};
            \matrix[row sep=-2mm, matrix of nodes, ampersand replacement=\&] (output) at (0,-1) {
                \matrixcontents
                %\includegraphics[width=\unitsize/2, height=\unitsize/2]{icons/example-image-b.png} \& 800 \\
                %\includegraphics[width=\unitsize/2, height=\unitsize/2]{icons/example-image-c.png} \& 1200 \\
                };
        \end{tikzpicture}};
}

\newcommand{\consimg}[1]{%
    \includegraphics[width=\unitsize/2, height=\unitsize/2]{#1}%
}

% END META

\begin{document}

\begin{tikzpicture}
    \consnode{(0,0)}{example-image-a}{1}{example-image-b:800,example-image-c:1200}
\end{tikzpicture}

\end{document}

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.