# Store and reproduce effective graphics scaling

When an image's length is scaled to a variable length, say \textwidth, how can I determine the effective scaling factor?

\includegraphics[width=\textwidth]{image.pdf}


I would like to use it to apply the same scaling to another image:

\includegraphics[scale=\myscale]{image2.pdf}


As a workaround, it would be sufficient for me to print the effective scaling factor and manually insert it in the second command. This could be done by determining the original image's lengths as seen by pdflatex and dividing \the\textwidth by it. Is this possible? How does trimming affect this?

• Note that original and final size information is written in the .log file, so one would hope one could access the information in the program too. May 31 '16 at 6:48
• @Andrew Swann: That is a very nice idea, I was not aware of that. Since it consitutes a workaround, why not make it an answer? May 31 '16 at 19:53
• I am sure there are experts here that know how to get their hands on this information. I'll post something on chat. If that gives nothing, then I'll expand my comment in to a partial answer. Jun 1 '16 at 7:09
• Thank you all for your good answers! I will leave the bounty open for a few days longer, but at this time, I think Steven B. Segletes provided the most versatile and elegant solution. Jun 7 '16 at 19:45

Here I introduce \SetIncludegraphics[]{} for the reference image whose purpose is to compute the \myscale while setting the image.

What it does (in temporary boxes) is compare the results of the \includegraphics with and without the application of the optional argument. The \myscale is computed as the ratio of the widths of two images with vs. without the application of [#1]. Thereafter, \myscale is available for use.

This uses the trick of storing the image widths in a \newcount variable, so that it is intrinsically without physical units (i.e., it has "machine" units) and is stored as a large integer. In so doing, a simple division of the two counts using the fp package produces the scale.

\documentclass{article}
\usepackage[margin=1cm]{geometry}
\usepackage{graphicx,fp}
\newcount\scaledwidth
\newcount\unscaledwidth
\newcommand\SetIncludegraphics[2][]{%
\setbox0=\hbox{\includegraphics{#2}}%
\unscaledwidth=\wd0\relax%
\setbox0=\hbox{\includegraphics[#1]{#2}}%
\scaledwidth=\wd0\relax%
\FPdiv\myscale{\the\scaledwidth}{\the\unscaledwidth}%
\includegraphics[#1]{#2}%
}
\parindent 0pt\parskip 2em
\begin{document}
\includegraphics{example-image-1x1}\\
\includegraphics{example-image-16x10}

\SetIncludegraphics[width=.2\textwidth]{example-image-1x1}%
\includegraphics[scale=\myscale]{example-image-16x10}
\end{document}


• Thank you, Steven, for your elegant and versatile solution. Jun 9 '16 at 6:30

Here is my try:

The three images have different widths (big, medium and small), but the star has the same size on each. I used \settowidth to get the original image width, and \pgfmathsetmacro to divide the dimentions (answere by Matthew Leingang here helped me).

\documentclass{article}
\usepackage{graphicx}
\usepackage{pgf}

\begin{document}
\newlength{\imagewidth}
\settowidth{\imagewidth}{\includegraphics{img_big}}
\pgfmathsetmacro{\myscale}{\the\textwidth/\the\imagewidth}

\includegraphics[width=\textwidth]{img_big}

\includegraphics[scale=\myscale]{img_medium}

\includegraphics[scale=\myscale]{img_small}

\end{document}


You can then add a \newcommand in the preambule:

\newlength{\imagewidth}
\newcommand\getscale[1]{%
\settowidth{\imagewidth}{\includegraphics{#1}}
\pgfmathsetmacro{\myscale}{\the\textwidth/\the\imagewidth}
}


And get the scaling factor for an image using:

\getscale{img_big}
\includegraphics[scale=\myscale]{img_medium}


The following example defines \setscalewidth[<options>]{<image} (and \setscaleheight) to set the scale used by LaTeX when including an image using \includegraphics[<options>]{<image>}:

\documentclass{article}

\usepackage{xparse,graphicx}
\ExplSyntaxOn
\cs_new_eq:NN \calc \fp_eval:n
\ExplSyntaxOff

\makeatletter
\newcommand{\scalefactor}{1}% Default scale factor
\newcommand{\setscalewidth}[2][]{% \setscalewidth[<options>]{<image>}
\settowidth{\@tempdima}{\includegraphics{#2}}% Original width
\settowidth{\@tempdimb}{\includegraphics[#1]{#2}}% Modified width
\edef\scalefactor{\calc{\strip@pt\@tempdimb/\strip@pt\@tempdima}}% modified/original = scale factor
}
\newcommand{\setscaleheight}[2][]{% \setscaleheight[<options>]{<image>}
\settoheight{\@tempdima}{\includegraphics{#2}}% Original height
\settoheight{\@tempdimb}{\includegraphics[#1]{#2}}% Modified height
\edef\scalefactor{\calc{\strip@pt\@tempdimb/\strip@pt\@tempdima}}% modified/original = scale factor
}

\begin{document}

\setscalewidth[width=0.2\textwidth]{example-image}% Define scale width

\includegraphics[width=0.2\textwidth]{example-image} \quad % Print original image using width= specification
\includegraphics[scale=\scalefactor]{example-image} % Print original image using scale= specification

\end{document}


Once set, the scaling factor is stored in \scalefactor.

With expl3 facilities, you can easily get the scaling factor:

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

\ExplSyntaxOn
\NewDocumentCommand{\setscale}{mmm}
{% #1 is the object to scale,
% #2 the width to scale to,
% #3 the name for the scale factor
\hbox_set:Nn \l_tmpa_box { #1 }
\tl_clear_new:c {l_douba_scale_#3_tl}
\tl_set:cx {l_douba_scale_#3_tl}
{
\fp_eval:n
{
round
(
\dim_to_fp:n { #2 }
/
\dim_to_fp:n { \box_wd:N \l_tmpa_box }
, 5
)
}
}
}
\DeclareExpandableDocumentCommand{\usescale}{m}
{
\tl_use:c {l_douba_scale_#1_tl}
}
\ExplSyntaxOff

\begin{document}
\raggedright

\setscale{\includegraphics{example-image}}{\textwidth}{myscale}

\texttt{\usescale{myscale}}

\includegraphics[width=\textwidth]{example-image}

\includegraphics[scale=\usescale{myscale}]{example-image-1x1}

\end{document}


In addition to Steven B. Segletes' answer, I have made a modification that offers to separate scaling-relevant from scaling-irrelevant arguments, so that, for example, a trimmed image can be used as the scaling base:

\usepackage{graphicx,fp}
\newcount\scaledwidth
\newcount\unscaledwidth
\newcommand{\SetTrimIncludegraphics}[3]{%
% #1: scaling, #2: trimming and other, #3: name
\setbox0=\hbox{\includegraphics[#2]{#3}}%
\unscaledwidth=\wd0\relax%
\setbox0=\hbox{\includegraphics[#1,#2]{#3}}%
\scaledwidth=\wd0\relax%
\FPdiv\myscale{\the\scaledwidth}{\the\unscaledwidth}%
\includegraphics[#1,#2]{#3}%
}


This way, the following code will produce two identical images side to side:

\SetTrimIncludegraphics{width=0.48\textwidth}{page=4,trim=52 595 321 61,clip}{mypic}
\includegraphics[scale=\myscale,page=4,trim=52 595 321 61,clip]{mypic}


Edit:

I just realized that \myscale is only locally defined. To get a global assignment, one would replace:

  \FPdiv\myscale{\the\scaledwidth}{\the\unscaledwidth}%


with:

  \FPdiv\0{\the\scaledwidth}{\the\unscaledwidth}%
\global\let\myscale\0%