6

Problem

Often I have the use case that I want to layout several images in a row inside one figure. These images may have different aspect ratios, but for the layout to look good I want them to have the same height and also fill the complete width, with possibly some white space between the images. For example something like this:

Example of what I want, same height, maximum width

Very often I find myself storing the height of these figures in a variable (using \newcommand), and tweaking it such that the width of all the images spans the intended width, typically the width of the surrounding figure-environment. I think this process is tedious, and not generic. If I change the width or the margins of my document, I'd have to go through all the images again. This is code of how I usually do it:

\newcommand{\figh}{3.94cm}

\begin{figure*}[t!]
  \centering
  \subfigure[]{\includegraphics[height=\figh]{fig/ratio1.pdf}}
  \subfigure[]{\includegraphics[height=\figh]{fig/ratio2.pdf}}
  \subfigure[]{\includegraphics[height=\figh]{fig/ratio3.pdf}}
  \caption{Three images with different aspect ratio's that fill the whole page.}
\end{figure*}

Alternatively, I could settle with these two examples, to decrease my latex reporting efforts. But I don't find them acceptable.

Left: the images are smaller and hence less clear or readable then possible, moreover I find this (visually) ugly. Right: the images have different height, I think this looks messy.

Suggestion

I assume the figure environment has at least a maximum width, and maybe a fixed width, as adding more figure will eventually put them on a new 'line', instead of just overflowing them over the page. This behavior indicates that there is width information during the image layout process.

Given a set of images with dimensions (h1,w1), ..., (hn,wn), the intended amount of white space between them ws, and the total width of the row wr, it should be trivial to calculate at which factors these images need to be scaled to layout in the suggested manner. First we correct the row width wr by removing the intended white space, wr becomes wr - (n-1) * ws. This way we don't have to think about the white space anymore, we could also remove margins, if we want them. Easy peasy :)

Then we can calculate the total width of all the images next to each other, given that they have height 1:

wt = sum { wi / hi | for i=1..n}

This gives us a hint off the difference between the width at height 1 and the width we actually have. Then we can determine how each image needs to be scaled to fill the whole row:

fi = ws / (wt * hi) for i=1..n

So if we scale the images to have width wi' = fi * wi and height hi' = fi * hi (for i=1...n), and put the intended (horizontal) white space between the images, the row is completely filled and all figures have the same height ws / wt.

In my opinion this should be trivial to implement for a latex code expert and also is a very useful feature, allowing us to

\newcommand{\figh}{3.94cm}

\begin{figure*}[t!]
  \fillwidthsameheight[width=\textwidth]{
    \includegraphics{fig/ratio1.pdf},
    \includegraphics{fig/ratio2.pdf},
    \includegraphics{fig/ratio3.pdf},
  }{caption 1, caption 2, caption 3}
  \caption{Three images with different aspect ratio's that fill the whole page.}
\end{figure*}

Of course, one can argue about the syntax, and I'm not a latex expert, so my suggestion can probably be improved.

Question

To the best of my knowledge, such a feature does not exist in latex, is that true? Is there something like this?

If there is not, why isn't there? Is this approach somehow against the workings or philosophy of latex?

Please don't suggest the use of \hfill, such that one only needs to approximately get the heights right, and stretch the white space to fill the width. This still needs manual (tedious) tweaking and gives a lesser result.

Personal solution

(based on suggestions of @DavidCarlisle and @HarishKumar)

I created a package that does this, including options for subcaptions and a in-between-figures margin. Please note that I'm not a latex programmer, and my code may have unintended side-effects or might be plain ugly. So if you use it, be prepared to be disappointed. Nevertheless, I consider lack of latex understanding a sign of sanity :P

The package, elasticrow.sty :

% Author: Herbert Kruitbosch , Copyright: be nice.
\NeedsTeXFormat{LaTeX2e}[1994/06/01]
\ProvidesPackage{elasticrow}[2015/02/13 Friday 13th code.]

\RequirePackage{graphicx}
\RequirePackage{subfigure}

\newcommand{\elasticfigure}[2][\empty]{%
  \argumenttoks=\expandafter{\the\argumenttoks \elasticfun[#1]{#2}}%
}

\newlength{\elasticheight}
\newlength{\elasticwidth}
\newsavebox{\elasticbox}
\newtoks{\argumenttoks}

\newenvironment{elasticrow}[1][0.01em]{
   \def\elasticwhitespace{#1}
   \argumenttoks={}
}{
  \setlength{\elasticwidth}{\textwidth}
  \addtolength{\elasticwidth}{\elasticwhitespace}
  \let\elasticfun=\elasticcalculatewidth
  \the\argumenttoks
  \let\elasticfun=\elasticpreprocess
  \let\elasticsep=\empty 
  \sbox\elasticbox{
    \resizebox{\elasticwidth}{!}{\the\argumenttoks}
  }
  \setlength{\elasticheight}{\ht\elasticbox}
  \let\elasticfun=\elasticprocess
  \let\elasticsep=\empty 
  \the\argumenttoks
}
\newcommand{\elasticcalculatewidth}[2][\empty]{\addtolength{\elasticwidth}{-\elasticwhitespace}}
\newcommand{\elasticpreprocess}[2][\empty]{\includegraphics[height=1cm]{#2}}

\newcommand{\elasticprocess}[2][\empty]{\elasticsep\subfigure[#1]{\includegraphics[height=\elasticheight]{#2}}\let\elasticsep=\hfill}

\endinput

Example usage:

\documentclass{article}

\usepackage{elasticrow}

\begin{document}

\begin{figure}
    \begin{elasticrow}[1em]
      \elasticfigure[caption]{example-image-16x9}
      \elasticfigure{example-image-a}
      \elasticfigure{example-image-16x9}
    \end{elasticrow}
    \begin{elasticrow}[1em]
      \elasticfigure{example-image-a}
      \elasticfigure{example-image-16x9}
      \elasticfigure{example-image-16x9}
      \elasticfigure[powder]{example-image-a}
    \end{elasticrow}
    \caption{Ultra caption}
    \hrule
\end{figure}

\end{document}

Output:

Example output

  • This is I think a duplicate of tex.stackexchange.com/questions/218378/… – David Carlisle Feb 13 '15 at 11:13
  • I see, resizebox may indeed be what I want :) Let me see if that works, I'm still wondering if this also allows us to have different rows in the same figure environment. Thank you for your efforts! – Herbert Feb 13 '15 at 11:16
  • 1
    @DavidCarlisle No, I'm sorry, but the suggested use of resizebox also resizes the caption of a subfigure. I don't mind not using subfigure, but I'm not sure how I could add a caption to the 3 images, without resizing that caption. – Herbert Feb 13 '15 at 11:21
  • Load \usepackage[export]{adjustbox} and you will have max width and max height as the arguments of \includegraphics. – user11232 Feb 13 '15 at 11:24
  • 1
    Please don't put the answer in the question it makes things very confusing for later readers. You can make a self answer. (You are missing a lot of % eg \sbox\elasticbox{ \resizebox{\elasticwidth}{!}{\the\argumenttoks} } has a word space either side of the scaled box making the saved elasticbox box two spaces wider than \elasticwidth. – David Carlisle Feb 14 '15 at 11:14
2

You can use adjustbox with its export option which will make keys like max width, available inside \includegraphics. Also, it offers keys width, height etc to be used for adjustbox environment.

Here is a sample.

\documentclass{article}
\usepackage[export]{adjustbox}
\usepackage{showframe}   %% for demo
\setkeys{Gin}{height=2cm}
\begin{document}
\noindent
 \begin{adjustbox}{width=\textwidth}
   \includegraphics{helen19}
   \includegraphics{helen18}
   \includegraphics{helen20}
   \includegraphics{helen22}
   \includegraphics{helen19}
   \includegraphics{helen18}
 \end{adjustbox}

 \bigskip
 \noindent
 \begin{adjustbox}{max width=0.8\textwidth}  %% change to \textwidth and see
   \includegraphics[height=1.5cm]{helen19}
   \includegraphics[height=1.5cm]{helen18}
   \includegraphics[height=1.5cm]{helen20}
   \includegraphics[height=1.5cm]{helen22}
   \includegraphics[height=1.5cm]{helen19}
   \includegraphics[height=1.5cm]{helen18}
   \includegraphics[height=1.5cm]{helen20}
   \includegraphics[height=1.5cm]{helen22}
 \end{adjustbox}
\end{document}

enter image description here

  • This is basically the same solution as in the duplicate question, but using adjustbox rather than resizebox – David Carlisle Feb 13 '15 at 13:17

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.