3

I have an image foo.jpg. I would like to scale it into a box of an exact size, say 100pt wide by 200pt high. I may want to scale the axes independently, or I may want to preserve the aspect ratio and fill the shorter dimension with spaces.

However, if I use \includegraphics[width=100pt,height=200pt]{foo.jpg}, the resulting box is not exactly 100pt by 200pt—it's 99.9974pt by 199.99854pt.

How can I include the graphics into a box of exactly a prescribed width and height?

Is there a better way than creating a vbox and hbox around the includegraphics surrounded by vfil/hfil?

Or, is it possible that \includegraphics will exceed the prescribed width and height, requiring the use of vss/hss?

\documentclass{standalone}

\usepackage{graphicx}

\newsavebox{\badbox}
\newsavebox{\goodbox}
\newlength{\mywidth} \setlength{\mywidth}{100pt}
\newlength{\myheight} \setlength{\myheight}{200pt}

\begin{document}
\typeout{mywidth=\the\mywidth, myheight=\the\myheight}%
\sbox{\badbox}{%
  \includegraphics[width=\mywidth,height=\myheight]
    {foo.jpg}}%
\sbox{\goodbox}{%
  \vbox to\myheight{%
    \vfil%
    \hbox to\mywidth{\hfil\usebox{\badbox}\hfil}%
    \vfil}}%
\typeout{badbox wd=\the\wd\badbox, ht=\the\ht\badbox, dp=\the\dp\badbox}%
\typeout{goodbox wd=\the\wd\goodbox, ht=\the\ht\goodbox, dp=\the\dp\goodbox}%
\end{document}

Output:

mywidth=100.0pt, myheight=200.0pt
badbox wd=99.9974pt, ht=199.99854pt, dp=0.0pt
goodbox wd=100.0pt, ht=200.0pt, dp=0.0pt

Output with keepaspectratio:

mywidth=100.0pt, myheight=200.0pt
badbox wd=99.9974pt, ht=115.57187pt, dp=0.0pt
goodbox wd=100.0pt, ht=200.0pt, dp=0.0pt
6
  • Welcome. TeX arithmetic is not entirely accurate. Is there some particular reason the image must be scaled so precisely? Surely there will be no visible difference between 99.98...pt and 100pt?
    – cfr
    Oct 31, 2023 at 1:02
  • 1
    Can you reproduce using an image other people have? e.g. example-image? (See the mwe package for a selection.) If that's not possible, can you replace foo.jpg by a box? Or provide the image somewhere?
    – cfr
    Oct 31, 2023 at 1:07
  • @cfr Why scale exactly: The difference may not be visually perceptible in the typeset output (which is why I think the vfil/hfil or vss/hss approach will suffice), but it may affect the sizing of surrounding boxes leading to spurious underfull or overfull box warnings, or it may incorrectly push some box onto another line. Example image: Changed the MWE to use the first JPEG I found on Wikipedia. Oct 31, 2023 at 1:21
  • 1
    You can literally use example-image and people won't have to download anything. I admit the image may be considered a trifle dull, but it works.
    – cfr
    Oct 31, 2023 at 1:28
  • 2
    The only precise measurements in TeX are in units of sp and there are 65536 sp in 1 pt. Oct 31, 2023 at 3:08

2 Answers 2

6

You don't need to have all the nested box and glue combination you can just assert the box size

\documentclass{article}

\usepackage{graphicx}

\newsavebox{\badbox}

\newlength{\mywidth} \setlength{\mywidth}{100pt}
\newlength{\myheight} \setlength{\myheight}{200pt}

\begin{document}
\typeout{mywidth=\the\mywidth, myheight=\the\myheight}%
\sbox{\badbox}{%
  \includegraphics[width=\mywidth,height=\myheight]
    {example-image}}%

\typeout{badbox wd=\the\wd\badbox, ht=\the\ht\badbox, dp=\the\dp\badbox}%
\ht\badbox=\myheight \wd\badbox=\mywidth
\typeout{badbox wd=\the\wd\badbox, ht=\the\ht\badbox, dp=\the\dp\badbox}%




\end{document}

produces

badbox wd=99.99736pt, ht=200.00575pt, dp=0.0pt
badbox wd=100.0pt, ht=200.0pt, dp=0.0pt

I did consider (a lifetime ago) making that the default behaviour but it seemed marginally better for height= (and the similar \resizebox) to return the calculated size with the rounding error rather than the requested size.

It's done that way as most back ends can not actually set content to a specified size, only to scale. Requested size is a convenience in the user interface but it means tex has to calculate the required scale factor.

graphicx uses TeX dimension arithmetic which is only capable of around 5 decimal places in stored values and loses some of that in calculation, so here you see difference in the third decimal place, but 0.00264pt really isn't very big.

These days it could use more accurate (and memory intensive) calculation methods such as l3fp functions which use decimal floating point to much higher accuracy, around 18 significant figures, but even then you can never guarantee that actual x (requested / actual) exactly equals requested so if you type out the value as above you could get 100.0000000000001pt or some such, so you may still want to force the box to be the requested size.


Note if you do use glue you should use \vss not \vfil so it can shrink to negative size, note with example-image as above, you need to shrink the box in the vertical direction not pad it out.


See also https://github.com/latex3/latex2e/issues/112

7
  • 1
    Note if you do use glue you should use \vss not \vfil so it can shrink to negative size, note with example-image you need to shrink the box in the vertical direction not pad it out. Oct 31, 2023 at 16:37
  • I think l3graphics would be on-the-money here ...
    – Joseph Wright
    Oct 31, 2023 at 16:52
  • Aside from tiny potential sub-point differences in where the image might be rendered, won't assigning the width and height directly instead of surrounding with glue also affect the centering (whether vertical or horizontal) of the image in the keepaspectratio case? It looks like your approach will leave the image at the bottom of the box, whereas mine will add it to the center. (Not saying one or the other is correct—I didn't specify where I wanted it in my question—but it makes a difference.) Oct 31, 2023 at 18:19
  • @TaylorRCampbell forcing both sizes when using keepaspectratio is just weird, I would never even think of offering that, it would make sense to force the direction that is used to the requested size, but then the other direction should scale by the same amount, that's the whole point. I will add first two comments to the answer Oct 31, 2023 at 18:54
1

Taking David's answer and re-coding using l3graphics, we get something like

\documentclass{article}

\usepackage{l3graphics}
\ExplSyntaxOn
\keys_define:nn { graphics }
  {
    height .dim_set:N = \l__graphics_height_dim ,
    width .dim_set:N  = \l__graphics_width_dim ,
  }
\box_new:N \l__graphics_tmp_box
\NewDocumentCommand \includegraphics { O{} m }
  {
    \mode_leave_vertical:
    \group_begin:
       \keys_set_known:nn { graphics } {#1}
       \hbox_set:Nn \l__graphics_tmp_box
         { \graphics_include:nn {#1} {#2} }
       \box_resize_to_wd_and_ht_plus_dp:Nnn \l__graphics_tmp_box
         \l__graphics_width_dim \l__graphics_height_dim
       \box_use:N \l__graphics_tmp_box
    \group_end:
  }
\ExplSyntaxOff


\newsavebox{\badbox}

\newlength{\mywidth} \setlength{\mywidth}{100pt}
\newlength{\myheight} \setlength{\myheight}{200pt}

\begin{document}
\typeout{mywidth=\the\mywidth, myheight=\the\myheight}%
\sbox{\badbox}{%
  \includegraphics[width=\mywidth,height=\myheight]
    {example-image.pdf}}%

\typeout{badbox wd=\the\wd\badbox, ht=\the\ht\badbox, dp=\the\dp\badbox}%
\ht\badbox=\myheight \wd\badbox=\mywidth
\typeout{badbox wd=\the\wd\badbox, ht=\the\ht\badbox, dp=\the\dp\badbox}%




\end{document}

Note that at present l3graphics is for low-level graphics inclusion and doesn't cover resizing directly: a better interface would be needed to set up a complete replacement for \includegraphics.

You must log in to answer this question.

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