# define sbox dynamical within a newcommand

I am trying to collect the widths of an arbitrary number of images within a macro with just the name of the image as input parameter. the results are stored in a global datatool list. unfortunately, the resulting list always contains the last width added to the list for all images/fields previously added.

this code

\documentclass{article}
\usepackage{datatool}
\usepackage{graphicx}

\def\dimtomm #1{\the\numexpr \dimexpr #1\relax*635/118407168\relax }

\newsavebox\imagebox

\newcommand{\regimg}[2]{%
\sbox{\imagebox}{\includegraphics[height=5cm]{#1}}
\DTLnewrow{list}
\DTLnewdbentry{list}{label}{#2}
\DTLnewdbentry{list}{width}{\dimtomm{\wd\imagebox}}
}

\begin{document}
\DTLnewdb{list}

\regimg{img1.jpg}{1}
\regimg{img2.jpg}{2}
\regimg{img3.jpg}{3}

\DTLsort{width}{list}
\DTLforeach*{list}{\theLabel=label,\theWidth=width}{\theLabel - \theWidth\\}
\end{document}


produces the following output:

1- 89
2- 89
2- 89


where 89 (mm) is the width of the last image. the ones before are/should be different.

when i extract the code of the macro and use a different savebox for each image the code works. so the problem is some "scope problem" where. i assume. i need some csname/expendafter, \@nameuse, global or etoolbox’ \csdef constructs. but could not figure out how to use them.

i also tried to define the savebox dynamical within the macro with a running number on the end of the name but also got lost there. any push in the right direction highly welcome.

You need to expand \the\wd (and not add lots of space tokens)

\newcommand{\regimg}[2]{%
\sbox{\imagebox}{\includegraphics[height=5cm]{#1}}%%%
\DTLnewrow{list}%%%
\DTLnewdbentry{list}{label}{#2}%%%
\edef\tmp{\noexpand\DTLnewdbentry{list}{width}{\noexpand\dimtomm{\the\wd\imagebox}}}%%%
\tmp
}


Just found out that \dtlexpandnewvalue would to the trick too:

\newcommand{\regimg}[2]{%
\sbox{\imagebox}{\includegraphics[height=5cm]{#1}}%
\DTLnewrow{list}%
\dtlexpandnewvalue%
\DTLnewdbentry{list}{label}{#2}%
\DTLnewdbentry{list}{width}{\dimtomm{\wd\imagebox}}%
}


You need to expand the value, but not in the way you do: \dtlexpandnewvalue is a declaration that will hold “forever” (with the usual scoping rules).

You can also get more accurate figures than just a rounding to millimeters.

\documentclass{article}
\usepackage{datatool}
\usepackage{graphicx}
\usepackage{xfp}

%\newcommand\dimtomm[1]{\the\numexpr\dimexpr#1\relax*635/118407168\relax}
% this can show two exact decimal digits (or more)
\newcommand\dimtomm[1]{\fpeval{round(#1/(1mm),2)}}

\newsavebox\imagebox

\newcommand{\regimg}[2]{%
\sbox{\imagebox}{\includegraphics[height=5cm]{#1}}%
\DTLnewrow{list}%
\DTLnewdbentry{list}{label}{#2}%
\DTLnewdbentry{list}{width}{\dimtomm{\wd\imagebox}}%
}

\begin{document}
\DTLnewdb{list}

\dtlexpandnewvalue
\regimg{example-image.jpg}{1}
\regimg{example-image-9x16.jpg}{2}
\regimg{example-image-1x1.jpg}{3}
\dtlnoexpandnewvalue

\DTLsort{width}{list}

\DTLforeach*{list}{\theLabel=label,\theWidth=width}{\theLabel\ - \theWidth\par}

\end{document}


This is what you'd get by switching the comments and using your definition.

Alternatively, without the “expand” declaration

\newcommand{\regimg}[2]{%
\sbox{\imagebox}{\includegraphics[height=5cm]{#1}}%
\DTLnewrow{list}%
\DTLnewdbentry{list}{label}{#2}%
\expanded{\noexpand\DTLnewdbentry{list}{width}{\dimtomm{\wd\imagebox}}}%
}

• your example works great. it also solves my follow-up question why my DTLsort does not work. Btw. your example also works fine (on the first view) without \dtlexpandnewvalue and \dtlnoexpandnewvalue. can this be the case? do i really need the two declarations? May 22 at 22:28