Sometimes I don't need to have a specific picture size, but for layout reasons I would like to scale the image so that the rest of the page is filled. How can I achieve that?
-
3I remember asking the very same question several years ago, because of a figure in my Dipl.Ing. thesis which started an interesting discussion but didn't ended in a solution. This was before TikZ came up. – Martin Scharrer Mar 29 '11 at 16:05
-
I need it for the same reasons (diplomarbeit) ;) – RoflcoptrException Mar 29 '11 at 16:07
3 Answers
In ConTeXt, one can measure the space remaining on a page using
\definemeasure[page][\dimexpr\pagegoal-\pagetotal-\lineheight\relax]
and then use this as
\externalfigure[name][height=\measure{page}]
To scale the dimension of the figure until the width equals the text width or the height equals the remaining space, use:
\externalfigure[name][height=\measure{page}, width=\textwidth, factor=max]
As Martin Scharrer pointed out, the same idea also works with LaTeX:
Define a measure for the space remaining on the page
\newcommand\measurepage{\dimexpr\pagegoal-\pagetotal-\baselineskip\relax}
and then set is as the figure height:
\includegraphics[height=\measurepage,width=\textwidth,keepaspectratio]{cow}
- 62,301
-
@Bruno Le Floch Yes it does not need to be that complicated. Both
pagegoalas well aspagetotalare TeX primitives so one wouldn't have a problem using this code with any of the engines. But really ConTeXt is three steps ahead of LaTeX at the moment. – yannisl May 11 '11 at 03:51 -
@Yiannis: Are
\pagegoaland\pagetotalreally reliable? I once got told that in certain situations TeX can accumulate several pages worth of material before deciding about the page breaking. Therefore always the need to go through the.auxfile. Or do this primitives do this? – Martin Scharrer May 11 '11 at 06:59 -
6Works indeed with LaTeX as well as long
\lineheightis replaced:\includegraphics[height=\dimexpr\pagegoal-\pagetotal-\baselineskip\relax,width=\textwidth,keepaspectratio]{image}– Martin Scharrer May 11 '11 at 07:19 -
1@Martin They are very reliable and there is no need to go through an auxiliary file. The
\pagegoalis set based on the page geometry, penalties and tolerances. Every time TeX calls the output routine to see if a page break is needed it will recalculate the \pagetotal. – yannisl May 11 '11 at 07:36 -
@Yiannis: Is there some equivalent primitive for the linegoal? This would be useful for http://tex.stackexchange.com/questions/17349/is-there-a-way-to-measure-the-remaining-space-of-a-line-of-text – Martin Scharrer May 11 '11 at 11:01
-
1@Martin no there is no primitive for line goal, as TeX optimizes paragraphs and not lines based on demerits. pdfTEX's has
\pdfsavepos. A TeX solution would be to place box the paragraph and then unbox it line by line and compare against a marker or with the textwidth values. Here I think a TikZ solution is more appropriate (another way is to use LaTeX pic commands and do some calculations). – yannisl May 11 '11 at 13:06 -
@Martin ... sorry forgot about the Dirty Tricks in the TeXbook, have a look how Knuth defined a point(x,y), which is essentially what Lamport used to define
\put. – yannisl May 11 '11 at 13:27 -
1@MartinScharrer, that technique doesn't seem to work for me in beamer. I'm getting the following error: "Dimension too large". – user545424 May 31 '18 at 16:19
-
@user545424: Well
beamerredefines several length registers which might break this solution. Sorry, can't help you here without analyzing it. – Martin Scharrer Jun 02 '18 at 07:03
Update 2011/09/16:
I now created a package tikzpagenodes (CTAN) which provides a special node for the text area. This simplifies my original answer as follows:
\documentclass{scrartcl}
\usepackage{tikzpagenodes}
\usepackage{caption}
\usetikzlibrary{calc}
\usepackage{lipsum}
\begin{document}
\lipsum[1]% dummy text
\par\noindent
\begin{tikzpicture}[overlay,remember picture]
% Caption
\node [anchor=south west,outer sep=0pt,inner sep=0pt,text width=\textwidth] (caption) at (current page text area.south west) {%
\captionof{figure}{Text}%
};
% Image
\path let \p0 = (0,0), \p1 = (caption.north) in
node [inner sep=0pt,outer sep=0pt,anchor=south] at (\x1,\y1) {%
\pgfmathsetmacro\imgheight{\y0-\y1-\abovecaptionskip}%
\includegraphics[height=\imgheight pt,width=\textwidth,keepaspectratio]{image}%
};
\end{tikzpicture}%
\newpage
\end{document}
However, it is also possible without this special node by using two tikzpicture (one to set the start coordinate) as demonstrated by Herbert for PSTricks in Stretching a framebox over the whole page. In TikZ and for a figure this looks like:
\documentclass{scrartcl}
\usepackage{capt-of}
\usepackage{tikz}
\usetikzlibrary{calc}
\usepackage{lipsum}
\begin{document}
\lipsum[1]% dummy text
\par\noindent
\tikz[overlay,remember picture]\coordinate (image-start);
\par
\vfill
\null\hfill
\begin{tikzpicture}[overlay,remember picture]
\path let \p0 = (0,0), \p1 = (image-start) in
% Caption
node [anchor=south,outer sep=0pt,inner sep=0pt,text width=\textwidth] (caption) at (\x0/2+\x1/2,0) {%
\captionof{figure}{Text}%
}
% Image
node [inner sep=0pt,outer sep=0pt,anchor=south] at (caption.north) {%
\pgfmathsetmacro\imgheight{\y1-\y0-\abovecaptionskip}%
\includegraphics[height=\imgheight pt,width=\textwidth,keepaspectratio]{image}%
};
\end{tikzpicture}%
\newpage
\end{document}
You can use TikZ for that. In allows you to calculate the amount of the rest of the page. Note that this needs two compiler runs to work. Here my solution for Stretching a framebox over the whole page adapted to include an image instead of a frame-box. Because this isn't 100% trivial for beginners (of LaTeX and/or TikZ) I think this isn't a duplicate.
\documentclass{scrartcl}
\usepackage{tikz}
\usetikzlibrary{calc}
\usepackage{lipsum}
\newcommand{\currentsidemargin}{%
\ifodd\value{page}%
\oddsidemargin%
\else%
\evensidemargin%
\fi%
}
\begin{document}
\lipsum[1]% dummy text
\par\bigskip \noindent
\begin{tikzpicture}[overlay,remember picture]
% Helper nodes
\path (current page.north west) ++(\hoffset, -\voffset)
node[anchor=north west, shape=rectangle, inner sep=0, minimum width=\paperwidth, minimum height=\paperheight]
(pagearea) {};
\path (pagearea.north west) ++(1in+\currentsidemargin,-1in-\topmargin-\headheight-\headsep)
node[anchor=north west, shape=rectangle, inner sep=0, minimum width=\textwidth, minimum height=\textheight]
(textarea) {};
% Image
\path let \p0 = (0,0), \p1 = (textarea.south) in
node [inner sep=0pt,outer sep=0pt,anchor=north west] {%
\pgfmathsetmacro\imgheight{\y0-\y1}%
\includegraphics[height=\imgheight pt]{image}%
};
\end{tikzpicture}
\newpage
\end{document}
You might add width=\textwidth,keepaspectratio to the \includegraphics options to ensure that the image isn't scaled wider than the text width.
Note that above code doesn't work inside a float because the tikzpicture is kind of anchored to the page and the float isn't by definition. The use of a non-floating environment is therefore required. In this specific case the figure should not float anyway. The best solution I can image is to place the caption as part of the tikzpicture using \captionof{float}{...} of the caption environment. Otherwise the tikpicture will overlay the caption.
The following code places the caption at the very end of the page (i.e. text area of the page) and scales the image so that it goes from the current position to the top of the caption plus \abovecaptionskip:
\documentclass{scrartcl}
\usepackage{tikz}
\usepackage{caption}
\usetikzlibrary{calc}
\usepackage{lipsum}
\newcommand{\currentsidemargin}{%
\ifodd\value{page}%
\oddsidemargin%
\else%
\evensidemargin%
\fi%
}
\begin{document}
\lipsum[1]% dummy text
\begin{center}
\begin{tikzpicture}[overlay,remember picture]
% Helper nodes
\path (current page.north west) ++(\hoffset, -\voffset)
node[anchor=north west, shape=rectangle, inner sep=0, minimum width=\paperwidth, minimum height=\paperheight]
(pagearea) {};
\path (pagearea.north west) ++(1in+\currentsidemargin,-1in-\topmargin-\headheight-\headsep)
node[anchor=north west, shape=rectangle, inner sep=0, minimum width=\textwidth, minimum height=\textheight]
(textarea) {};
% Image
\node [anchor=south west,outer sep=0pt,inner sep=0pt,text width=\textwidth] (caption) at (textarea.south west) {%
\captionof{figure}{Text}%
};
\path let \p0 = (0,0), \p1 = (caption.north) in
node [inner sep=0pt,outer sep=0pt,anchor=north] {%
\pgfmathsetmacro\imgheight{\y0-\y1-\abovecaptionskip}%
\includegraphics[height=\imgheight pt]{image}%
}
[use as bounding box](\x0,\y0) rectangle (\x1,\y1);
\end{tikzpicture}%
\end{center}
\newpage
\end{document}
- 262,582
-
This is a very long and roundabout way. Aditya's code below, although mentioned as used by ConTeXt can be used with pdflatex as well. Both the
\pagegoalas well as\pagetotalare TeX primitives. Second problem will be that if you only scale the image usingheight, it is quite likely that it will exceed the width of the page and produce a bad box. One needs to check both the new width as well as the new height of the image and rather stretch the in between spaces a bit if necessary. – yannisl May 11 '11 at 03:49 -
@Yiannis: The code can be easily put into a macro (in the preamble or a package) and then is much more compact to use. I kept the trivial adjustment of the width as an exercise to the reader ;-) Also I like the flexibility of this TikZ code which I already used for a couple of other answers. – Martin Scharrer May 11 '11 at 06:56
tcolorbox package also offers a solution for this problem.
With option height fill, the height of the tcolorbox is set to the rest of the available vertical space of the current page.
And if we need to know tha available space inside the tcolorbox, it's possible to declare a macro to store this value: space to=\myspace.
Following code shows the results with a default tcolorbox and a second one with blanker option for those who want to hide all tcolorbox effects.
\documentclass{article}
\usepackage{caption}
\usepackage{duckuments}
\usepackage[most]{tcolorbox}
\newtcolorbox{myfigure}[1][]{height fill, space to=\myspace,#1}
\begin{document}
\blindduck[1-2]% dummy text
\begin{myfigure}
\includegraphics[width=\linewidth, height=\myspace]{example-image-duck}\\
\captionof{figure}{text}
\end{myfigure}
\blindduck[3]% dummy text
\begin{myfigure}[blanker]
\includegraphics[width=\linewidth, height=\myspace]{example-image-duck}\\
\captionof{figure}{text}
\end{myfigure}
\end{document}
- 136,588
