\ pgfmathsetlengthmacro incompatible avec une boucle?

Sep 06 2020

Le code suivant se compile parfaitement:

\documentclass[]{article}

\usepackage[most]{tcolorbox}

\newcommand\UseImageLeft[1]{%
\IfFileExists{#1\i.jpg}%
  {\includegraphics[scale=0.2]{#1\i.jpg}}%
    {\includegraphics[scale=0.2]{example-image.jpg}}%
}

\def\ImageLeft{\UseImageLeft{Image-}}

% Problem ===================================
% \pgfmathsetlengthmacro\LeftWidth{width("\ImageLeft")}
% =======================================

\begin{document}

\foreach \i in {1,...,2}{

% On any page (begin)
\begin{tcolorbox}[]
\begin{tcbitemize}[]
\tcbitem[] \ImageLeft
\end{tcbitemize}
\end{tcolorbox}
% On any page (end)

\newpage
}

\end{document}

Mon problème est que je veux décommenter \pgfmathsetlengthmacro\LeftWidth{width("\ImageLeft")}, mais le code ne se compile plus. Comment puis-je résoudre ce problème s'il vous plaît?

Réponses

2 UlrichDiez Sep 06 2020 at 14:31

Problème 1:

Vous utilisez en \pgfmathsetlengthmacro\LeftWidth{width("\ImageLeft")} dehors de la \foreachboucle. Mais avec \pgfmathsetlengthmacro\LeftWidth{width("\ImageLeft")}la macro \ImageLeftest utilisée qui à son tour utilise \UseImageLeftqui à son tour utilise \iwhile \iest défini pour donner le numéro d'une image uniquement dans la \foreachboucle.

Problème 2:

Vous avez \pgfmathsetlengthmacro\LeftWidth{width("\ImageLeft")}ce qui est de modèle

\pgfmathsetlengthmacro\LeftWidth{⟨expression⟩}
avec
⟨expression⟩ = width("\ImageLeft")
, alors
width("\ImageLeft")
qu'à son tour est de modèle avec = .
width(⟨expression⟩)

⟨expression⟩"\ImageLeft"

Cela signifie qu'il \ImageLeftest évalué dans le cadre d'un ⟨expression⟩double:

La première fois que la ⟨expression⟩formation de l'argument de \pgfmathsetlengthmacro\LeftWidthest évaluée.

La deuxième fois où la ⟨expression⟩formation de l'argument de width(...)est évaluée.

pgfmanual.pdf, section "95.2 Syntaxe pour les expressions mathématiques: opérateurs" dit dans l'explication de l' opérateur:"x"

... Cependant, comme chaque expression est développée avec \ edef avant d'être analysée, les macros (par exemple, les commandes de police comme \ tt ou \ Huge) peuvent avoir besoin d'être «protégées» de cette extension (par exemple, \ noexpand \ Huge). Idéalement, vous devriez éviter de telles macros de toute façon. De toute évidence, ces opérateurs doivent être utilisés avec beaucoup de précaution car il est peu probable que d'autres calculs soient possibles avec le résultat.

Ces informations ne concernent pas uniquement l' opérateur. Ces informations sont pertinentes lorsqu'il s'agit d'évaluer un fichier . C'est une règle générale qui est appliquée lors de l'évaluation de chaque . Par conséquent, je pense que ce n'est pas une bonne idée de transmettre cette information uniquement en expliquant un opérateur spécial."x"⟨expression⟩⟨expression⟩

Quoi qu'il en soit:

En rassemblant les informations, vous constatez que cela \ImageLeftest évalué \edefdeux fois. Comme la chaîne d'expansion est \ImageLeft→ \UseImageLeft→ ...\includegraphics..., cela implique que deux tentatives d'évaluation \includegraphicsvia \edefont lieu.
Mais dans le cas où vous n'utilisez pas l'une des distributions TeX les plus récentes \includegraphicsne fonctionne pas dans \edef-expansion-contextts. C'est parce que dans les anciennes distributions TeX \includegraphicsn'est pas "protégé" - "protégé" fait référence à un mécanisme interne à LaTeX pour empêcher l'expansion des commandes dans des situations où cette expansion ne fonctionnerait pas - et "repose sur" l'exécution d'affectations temporaires, par exemple, en termes de \def, alors que de telles missions ne sont pas effectuées pendant que l' \edefévaluation est en cours. \edefdéclenche l'expansion des jetons extensibles alors qu'il \defs'agit d'une primitive non extensible .

Donc, si vous n'utilisez pas l'une des distributions TeX les plus récentes, vous devez prendre des mesures de précaution pour empêcher l'expansion / l'évaluation de la commande \ImageLeft(et ainsi empêcher l'expansion / l'évaluation de \includegraphics) avec chacune de ces \edefévaluations. (Sur les distributions TeX les plus récentes, ces mesures de précaution ne sont ni une nuisance ni nécessaires.)

Vous pouvez empêcher l'expansion / l'évaluation en préfixant \ImageLeftavec \noexpand\noexpand\noexpand.

La première \edefévaluation, appliquée sur <code>\noexpand\noexpand\noexpand\ImageLeft</code>,
donne des rendements
<code>\noexpand\ImageLeft</code>.
La deuxième \edef-évaluation sera appliquée à ce sujet , ce qui donne à son tour:
<code>\ImageLeft</code>.

Problème 3:

egreg a souligné que \ic'est déjà défini dans LaTeX et que vous ne voulez probablement pas remplacer cette définition. Par conséquent, dans l'exemple ci-dessous, j'ai utilisé à la \NiceForEachElementplace de \i.

\documentclass[]{article}

\usepackage[most]{tcolorbox}

\newcommand\UseImageLeft[1]{%
\IfFileExists{#1\NiceForEachElement.jpg}%
  {\includegraphics[scale=0.2]{#1\NiceForEachElement.jpg}}%
    {\includegraphics[scale=0.2]{example-image.jpg}}%
}

\def\ImageLeft{\UseImageLeft{Image-}}

% Problem ===================================
%\pgfmathsetlengthmacro\LeftWidth{width("\ImageLeft")}
% =======================================

\begin{document}

\foreach \NiceForEachElement in {1,...,2}{%
\pgfmathsetlengthmacro\LeftWidth{width("\noexpand\noexpand\noexpand\ImageLeft")}%
%Activate the following line in case you wish to see on screen/console what the definition of \LeftWidth looks like now:
%\show\LeftWidth
% On any page (begin)
\begin{tcolorbox}[]%
\begin{tcbitemize}[]%
\tcbitem[] \ImageLeft
\end{tcbitemize}%
\end{tcolorbox}%
% On any page (end)
\newpage
}

\end{document}

Si vous souhaitez également pouvoir utiliser en \ImageLeftdehors de la boucle, définissez \ImageLeftetc pour traiter un argument au lieu de traiter \i/ \NiceForEachElement.
Dans la boucle, vous pouvez passer \i/ \NiceForEachElementcomme argument.
En dehors de la boucle, vous pouvez passer directement le numéro de l'image comme argument.

\documentclass[]{article}

\usepackage[most]{tcolorbox}

% Define the command \NiceForEachElement to ensure error-message in case it is already defined.
% This way you can ensure to a certain degree that using `\niceelement` as Foreach-variable
% does not override something that alerady exists.
\newcommand\NiceForEachElement{}%

\newcommand\UseImageLeft[2]{%
% #1 preceding phrase "image-"
% #2 number of image
\IfFileExists{#1#2.jpg}%
  {\includegraphics[scale=0.2]{#1#2.jpg}}%
    {\includegraphics[scale=0.2]{example-image.jpg}}%
}

\newcommand*\ImageLeft[1]{\UseImageLeft{Image-}{#1}}

% Problem ===================================
%\pgfmathsetlengthmacro\LeftWidth{width("\ImageLeft")}
% =======================================

\begin{document}

% outside the loop the width of Image-7.jpg or example-image.jpg:

\pgfmathsetlengthmacro\LeftWidth{width("\noexpand\noexpand\noexpand\ImageLeft{7}")}%
%Activate the following line in case you wish to see on screen/console what the definition of \LeftWidth looks like now:
%\show\LeftWidth

% inside the loop:

\foreach \NiceForEachElement in {1,...,2}{%
  \pgfmathsetlengthmacro\LeftWidth{width("\noexpand\noexpand\noexpand\ImageLeft{\NiceForEachElement}")}%
  %Activate the following line in case you wish to see on screen/console  what the definition of \LeftWidth looks like now:
  %\show\LeftWidth
  % On any page (begin)
  \begin{tcolorbox}[]%
  \begin{tcbitemize}[]%
  \tcbitem[] \ImageLeft{\NiceForEachElement}%
  \end{tcbitemize}%
  \end{tcolorbox}%
  % On any page (end)
  \newpage
}

\end{document}
2 UlrikeFischer Sep 06 2020 at 13:59

N'utilisez pas de variables de boucle en dehors d'une boucle, ni dans le code exécuté, ni dans les définitions.

\documentclass[]{article}

\usepackage[most]{tcolorbox}

\newcommand\UseImageLeft[1]{%
\IfFileExists{#1.jpg}%
  {\includegraphics[scale=0.2]{#1.jpg}}%
  {\includegraphics[scale=0.2]{example-image.jpg}}%
}


\begin{document}

\foreach \i in {1,...,2}{%
\pgfmathsetlengthmacro\LeftWidth{width("\UseImageLeft{image\i}")}%
% On any page (begin)
\begin{tcolorbox}[]
\begin{tcbitemize}[]
\tcbitem[] \UseImageLeft{image\i}
\end{tcbitemize}
\end{tcolorbox}
% On any page (end)

\newpage
}^

\end{document}