8

I needed something (a package, a macro, whatever) that would take n images of arbitrary size and calculate their height so that all the images side by side filled a column exactly. As far as I know, there is no such package. The nearest thing I found was this: How to define a macro that uses image width and height to define a variable for later use. I modified the solution offered there to accept any number of images. I put it here in the hope that someone may find it useful:

\documentclass{article}

\usepackage{graphicx}
\usepackage{calc}
\usepackage{pgf}
\usepackage{pgffor}
\usepackage{subfig}
\usepackage{lipsum}

\newlength\myheight%
\newlength\mywidth%
\newlength\effwidth%
\newlength\finalheight%
\newlength\gapspace%

\newcommand{\manyimages}[2]{

\setlength{\gapspace}{#2}%
\pgfmathsetlength{\effwidth}{0.99\textwidth - \gapspace}%

\def\sumratio{0}

\foreach \im [remember=\sumratio] in {#1} {%

    \def\myimage{\includegraphics{\im}}%
    \setlength{\myheight}{\heightof{\myimage}}%
    \setlength{\mywidth}{\widthof{\myimage}}%
    \pgfmathsetmacro{\myratio}{\mywidth / \myheight}%

    \pgfmathsetmacro{\sumratio}{\sumratio+\myratio}
}%

\pgfmathsetlength{\finalheight}{\effwidth / \sumratio}% 
}

\begin{document}

\begin{figure}
\begin{centering}
\manyimages{image1, image2, image3, image4}{0.06\textwidth}
\subfloat{\includegraphics[height=\finalheight]{image1}}
\hfill
\subfloat{\includegraphics[height=\finalheight]{image2}}
\hfill
\subfloat{\includegraphics[height=\finalheight]{image3}}
\hfill
\subfloat{\includegraphics[height=\finalheight]{image4}}
\end{centering}
\end{figure}

\lipsum

\end{document}

Notes:

  1. \manyimages has two arguments. The first argument is a comma-separated list of images. The second argument is the gap space between them (the total gap, to be split in n-1 gaps).
  2. The code needs real images with arbitrary sizes, otherwise the problem is trivial. Therefore I don't put the graphics in demo mode.
  3. I'm new to LaTeX and StackExchange, I appreciate input about best practices, etc.
6

Surely I misunderstood the question due to lack of sleep... but what you want is not simply this?:

MWE

\documentclass{article}
\usepackage{graphicx}
\usepackage{lipsum} % dummy text
\begin{document}

\lipsum[2]\bigskip

\noindent\resizebox{\textwidth}{!}{
\includegraphics[height=\textheight]{example-image}
\includegraphics[height=\textheight]{example-image-10x16}
\includegraphics[height=\textheight]{example-image-golden}
\includegraphics[height=\textheight]{example-image-golden-upright}}

\bigskip\lipsum[5]

\end{document}

Note: I forget to put % after each image, so that a space is added between the images. When the fixed height is enough big, the result is roughly the same, but with very low values (try for instance 1 mm in this example) the space is upscaled giving the appearance of \hfill commands between the images. Funnily, \hfill here just prevented this spacing (please, do not consider this a recommendation of alternative usage).

  • Thank you! You didn't misunderstand. It is indeed that. A little background: I'm new to TeX and LaTeX. I needed this feature for my first project, a few months ago. I found my question had been asked on a different forum, and the answers boiled down to "trial and error". That didn't seem too clever because, as @egreg pointed out, mathematically it is straightforward. At the time, I settled for manual calculations. Now I am teaching myself LaTeX. I'm happy to learn that this problem is very simple, and that several different approaches all work. I hope other beginners will find it. – olga.saucedo Jan 22 '14 at 12:55
  • You're welcome. In my experience, this is largely the best Q&A site about TeX\LaTeX. – Fran Jan 22 '14 at 13:29
9

Some linear algebra shows that the final height is given by the formula

enter image description here

where w is the desired global width, bi and hi are the width and height of the i-th image (i = 1,2,…,n); *h$ is the final height.

An implementation using expl3 is almost straightforward. I guess that it's easy also with pgfmath. The macro \manyimages has as first argument the desired width (w in the preceding notation) which can be whatever one wants. In the following example I chose 0.8\textwidth to give the images some room.

\documentclass{article}
\usepackage{graphicx}
\usepackage{xparse}

\ExplSyntaxOn
\NewDocumentCommand{\manyimages}{ m m }
 {
  % #1 is the required width
  % #2 is a comma separated list of image names
  \manyimages_main:nn { #1 } { #2 }
 }

\fp_new:N \l_manyimages_temp_fp
\box_new:N \l_manyimages_temp_box
\dim_new:N \l_manyimages_finalheight_dim

\cs_set_eq:NN \finalheight \l_manyimages_finalheight_dim

\cs_new_protected:Npn \manyimages_main:nn #1 #2
 {
  % clear the storage bin
  \fp_zero:N \l_manyimages_temp_fp
  \clist_map_inline:nn { #2 }
   {
    % set a temporary box for measuring the current image
    \hbox_set:Nn \l_manyimages_temp_box { \includegraphics{##1} }
    % compute the ratio width/height and add it to the storage bin
    \fp_add:Nn \l_manyimages_temp_fp 
     {
      \dim_to_fp:n { \box_wd:N \l_manyimages_temp_box }
      /
      \dim_to_fp:n { \box_ht:N \l_manyimages_temp_box } 
     }
   }
  % now set \finalheight to the ratio w/(computed sum)
  \dim_set:Nn \l_manyimages_finalheight_dim
   {
    \fp_to_dim:n { \dim_to_fp:n { #1 } / \l_manyimages_temp_fp }
   }
 }
\ExplSyntaxOff

\begin{document}

\begin{flushleft}
\manyimages{0.8\textwidth}{
  example-image,
  example-image-10x16,
  example-image-golden,
  example-image-golden-upright
}

\includegraphics[height=\finalheight]{example-image}\hfill
\includegraphics[height=\finalheight]{example-image-10x16}\hfill
\includegraphics[height=\finalheight]{example-image-golden}\hfill
\includegraphics[height=\finalheight]{example-image-golden-upright}

\bigskip

For control

\bigskip

\resizebox{\textwidth}{!}{%
  \includegraphics{example-image}
  \includegraphics{example-image-10x16}
  \includegraphics{example-image-golden}
  \includegraphics{example-image-golden-upright}%
}


\end{flushleft}

\end{document}

I have used some images in the mwe suite. The second row is just for control and shows the images at their (uniformly scaled) original proportions.

enter image description here

Here is a full featured solution for subfig. For typesetting a row of images we use the manysubfloats environment, which contains one or more \subimage commands. The mandatory argument to manysubfloats is a dimension; use whatever you want, but recall that using \textwidth would push all the images together.

You can have more than one manysubfloats environments, each one will print a row of subfloats.

Each \subimage command has two arguments: the first is an optional list of key-value pairs, the second is the file name. In the key-value pairs one can specify

  • file= for the file name;
  • path= for a non standard path;
  • caption= for setting the subcaption;
  • listcaption= for a possibly different subcaption in the list of figures;
  • label= for a label referring to the subfloat.

Why a key file? If the file name has special characters or is too long, a symbolic name for the mandatory argument is to be preferred. The mandatory argument is used for internal purposes only. The file= key is probably not necessary, as most of the time the file name can be safely used.

\documentclass{article}
\usepackage{graphicx,subfig}
\usepackage{xparse}

\ExplSyntaxOn
\NewDocumentEnvironment{manysubfloats}{ m }
 { \clist_clear:N \l_manyimages_row_clist }
 { \manyimages_display:n { #1 } }
\NewDocumentCommand{\subimage}{ O{} m }
 {% #1 is a key-value list, #2 is a symbolic name or the file name
  \manyimages_subimage:nn { #2 } { #1 }
 }

\keys_define:nn { manyimages }
 {
  file .tl_set:N = \l_manyimages_file_tl,
  path .tl_set:N = \l_manyimages_path_tl,
  caption .tl_set:N = \l_manyimages_caption_tl,
  listcaption .tl_set:N = \l_manyimages_listcaption_tl,
  label .tl_set:N = \l_manyimages_label_tl
 }


\fp_new:N \l_manyimages_temp_fp
\box_new:N \l_manyimages_temp_box
\dim_new:N \l_manyimages_finalheight_dim
\clist_new:N \l_manyimages_row_clist
\tl_new:N \l_manyimages_output_tl
\tl_new:N \l_manyimages_tmpa_tl
\tl_new:N \l_manyimages_tmpb_tl

% a constant property list
\prop_new:N \c_manyimages_proto_prop
\prop_gput:Nnn \c_manyimages_proto_prop { file } { }
\prop_gput:Nnn \c_manyimages_proto_prop { path } { }
\prop_gput:Nnn \c_manyimages_proto_prop { caption } { }
\prop_gput:Nnn \c_manyimages_proto_prop { listcaption } { }
\prop_gput:Nnn \c_manyimages_proto_prop { label } { }

\cs_new_protected:Npn \manyimages_main:n #1
 {
  % clear the storage bin
  \fp_zero:N \l_manyimages_temp_fp
  \clist_map_inline:Nn \l_manyimages_row_clist
   {
    % set a temporary box for measuring the current image
    \__manyimages_prop_get:nnN { ##1 } { path } \l_manyimages_tmpa_tl
    \__manyimages_prop_get:nnN { ##1 } { file } \l_manyimages_tmpb_tl
    \hbox_set:Nn \l_manyimages_temp_box
     {
      \includegraphics
       {
        \tl_if_empty:NF \l_manyimages_tmpa_tl { \l_manyimages_tmpa_tl / }
        \l_manyimages_tmpb_tl
       }
     }
    % compute the ratio width/height and add it to the storage bin
    \fp_add:Nn \l_manyimages_temp_fp 
     {
      \dim_to_fp:n { \box_wd:N \l_manyimages_temp_box }
      /
      \dim_to_fp:n { \box_ht:N \l_manyimages_temp_box } 
     }
   }
  % now set \finalheight to the ratio w/(computed sum)
  \dim_set:Nn \l_manyimages_finalheight_dim
   {
    \fp_to_dim:n { \dim_to_fp:n { #1 } / \l_manyimages_temp_fp }
   }
 }

\cs_new_protected:Npn \manyimages_subimage:nn #1 #2
 {% add the item to the clist
  \clist_put_right:Nn \l_manyimages_row_clist { #1 }
  % clear the property list for #1 (symbolic name or file name)
  \prop_clear_new:c { l_manyimages_\tl_to_str:n { #1 }_prop }
  % fill in the property list with empty values
  \prop_set_eq:cN { l_manyimages_\tl_to_str:n { #1 }_prop } \c_manyimages_proto_prop
  \keys_set:nn { manyimages }
   {% empty the variables and evaluate the given ones
    file = {},
    path = {},
    caption = {},
    listcaption = {},
    label = {},
    #2
   }
  \tl_if_empty:NTF \l_manyimages_file_tl
   {
    \__manyimages_prop_put:nnn { #1 } { file } { #1 }
   }
   {
    \__manyimages_prop_put:nnV { #1 } { file } \l_manyimages_file_tl
   }
  \__manyimages_prop_put:nnV { #1 } { path } \l_manyimages_path_tl
  \__manyimages_prop_put:nnV { #1 } { caption } \l_manyimages_caption_tl
  \__manyimages_prop_put:nnV { #1 } { listcaption } \l_manyimages_listcaption_tl
  \__manyimages_prop_put:nnV { #1 } { label } \l_manyimages_label_tl
 }

%%% syntactic sugar
% a personal version of \prop_put:cnn
\cs_new_protected:Npn \__manyimages_prop_put:nnn #1 #2 #3
 {
  \prop_put:cnn { l_manyimages_\tl_to_str:n { #1 }_prop } { #2 } { #3 }
 }
\cs_generate_variant:Nn \__manyimages_prop_put:nnn { nnV }

% a personal version of \prop_get:cnN
\cs_new_protected:Npn \__manyimages_prop_get:nnN #1 #2 #3
 {
  \prop_get:cnN { l_manyimages_\tl_to_str:n { #1 }_prop } { #2 } #3
 }

%%% use \subfloat
\cs_new_protected:Npn \manyimages_display:n #1
 {% compute the final height
  \manyimages_main:n { #1 }
  % output the subfloats
  \clist_map_inline:Nn \l_manyimages_row_clist
   {
    \tl_set:Nn \l_manyimages_output_tl { \subfloat }
    \__manyimages_prop_get:nnN { ##1 } { listcaption } \l_tmpa_tl
    \tl_if_empty:NF \l_tmpa_tl
     {
      \tl_put_right:Nn \l_manyimages_output_tl { [ }
      \tl_put_right:NV \l_manyimages_output_tl \l_tmpa_tl
      \tl_put_right:Nn \l_manyimages_output_tl { ] }
     }
    \__manyimages_prop_get:nnN { ##1 } { caption } \l_tmpa_tl
    \tl_put_right:Nn \l_manyimages_output_tl { [ }
    \tl_if_empty:NF \l_tmpa_tl
     {
      \tl_put_right:NV \l_manyimages_output_tl \l_tmpa_tl
     }
    \tl_put_right:Nn \l_manyimages_output_tl { ] }
    \__manyimages_prop_get:nnN { ##1 } { label } \l_tmpa_tl
    \__manyimages_prop_get:nnN { ##1 } { path } \l_manyimages_tmpa_tl
    \__manyimages_prop_get:nnN { ##1 } { file } \l_manyimages_tmpb_tl
    \tl_put_right:Nx \l_manyimages_output_tl
     {
      {
       \exp_not:N \includegraphics
         [height=\l_manyimages_finalheight_dim]
         {
          \tl_if_empty:NF \l_manyimages_tmpa_tl { \l_manyimages_tmpa_tl / }
          \l_manyimages_tmpb_tl
         }
       \tl_if_empty:NF \l_tmpa_tl { \exp_not:N \label { \l_tmpa_tl } }
      }
     }
    \tl_use:N \l_manyimages_output_tl\hfill
   }
  \unskip
 }
\ExplSyntaxOff

\setcounter{lofdepth}{2}

\begin{document}
\listoffigures

\begin{figure}[htp]
\centering
\begin{manysubfloats}{.9\textwidth}
\subimage{example-image}

\subimage[
  file=example-image-10x16
]{10x16}

\subimage[
  caption=Yikes,
  listcaption=Ouch,
  path=images
]{example-image-golden}

\subimage[
  label=subfig:upright
]{example-image-golden-upright}
\end{manysubfloats}
\caption{Cumulative caption}
\end{figure}

A reference: \subref{subfig:upright}

\end{document}

enter image description here

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.