5

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.

3 Answers 3

5

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
}
2

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}}%
}
2

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}

enter image description here

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

enter image description here

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}}}%
}
1
  • 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? Commented May 22, 2021 at 22:28

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .