6

A common struggle I run into when writing papers is getting figures to land on the correct page. In practice this is often fixed by cutting the \begin{figure} ... \end{figure} and pasting it higher up in the document.

However, the figure is semantically tied to a certain paragraph in the paper and it makes more sense to keep its definition in the tex near that paragraph. (Then someone editing the caption would be more likely to review the related paragraphs for parallel changes.)

What I want is the ability to define a figure in one location, but call its placement somewhere else, often somewhere earlier in the tex. Does anyone have a good solution to this problem?


My attempt:

As an attempt to enable this, I can create a \newcommand that contains the figure definition, but I cannot place that command earlier in the document as it wouldn't be defined yet.

This question provides a method for using a macro before creation. However, that method fails when a figure definition is inside the macro with a response of ! TeX capacity exceeded, sorry [text input levels=15]. which seems to be signalling an infinite loop problem, but frankly I'm well out of my tex depth here.

  • Like in http://tex.stackexchange.com/a/360385/128553? – CampanIgnis Apr 06 '17 at 23:43
  • @CampanIgnis, similar but not quite. I do want to keep the code of the figures next to its logical place in the .tex files, but I don't want all the figures moved to the end of the document. I want to be able to place the figures throughout the document, like on the page before for instance. – Branden Ghena Apr 06 '17 at 23:59
  • 2
    This will clearly require (at least) two passes. The first pass treats the placement call as a no-op while writing the figure definition to a file. The second pass inputs the file and treats the figure definition as a no-op (or inefficiently lazily overwrites the file). There are various tools available to implement this strategy. I don't have the time to work out details now; I may later. – Ethan Bolker Apr 07 '17 at 13:03
  • @EthanBolker I agree that seems like the correct strategy. – Branden Ghena Apr 07 '17 at 19:10

2 Answers2

5

You can use a two-pass system.

\documentclass{article}
\usepackage{environ}
\usepackage{graphicx}

\usepackage{lipsum}

\makeatletter
\newwrite\remember@figures
\AtBeginDocument{%
  \InputIfFileExists{\jobname.dft}{}{}%
  \immediate\openout\remember@figures=\jobname.dft
}
\AtEndDocument{\immediate\closeout\remember@figures}

\NewEnviron{dfigure}[1]{%
  \immediate\write\remember@figures{%
    \noexpand\rememberfigure{#1}{\unexpanded\expandafter{\BODY}}%
  }%
}
\newcommand{\placefigure}[2][tp]{%
    \csname remembered@figure@#2\endcsname{#1}
}
\newcommand{\rememberfigure}[2]{%
  \global\@namedef{remembered@figure@#1}##1{%
    \begin{figure}[##1]#2\end{figure}%
  }%
}
\makeatother

\begin{document}

\placefigure[!htp]{first}

\lipsum[1]

\begin{dfigure}{first}
\centering
\includegraphics[width=4cm]{example-image-a}
\caption{Example image A}
\end{dfigure}

\begin{dfigure}{second}
\centering
\includegraphics[width=4cm]{example-image-b}
\caption{Example image B}
\end{dfigure}

\lipsum[3-5]

\placefigure[b]{second}

\end{document}

The dfigure environment saves the contents and the key it is defined with in an auxiliary file which is read at begin document. With \placefigure you put the correspondent figure at the position you like, with the suitable positioning options.

enter image description here

egreg
  • 1,121,712
  • I was about to post a hack proof of concept along these lines and ask someone (essentiailly, you) to clean it up. No need now! – Ethan Bolker Apr 07 '17 at 23:39
  • This is exactly what I was looking for. And it's pretty easy to add additional rules to provide figure*s and tables too. – Branden Ghena Apr 08 '17 at 02:14
0
  • I think you mean something like this -- at least here you have the full control of the placement.
  • I use the float package.
  • It offers the H placement option which means exactly here :).

\documentclass{article}

\usepackage{float}
\usepackage{graphicx}
\usepackage{blindtext}

\begin{document}

\blindtext

\begin{figure}[H]
\includegraphics[width=\textwidth]{example-image-a}
\caption{1st picture}
\end{figure}

\blindtext

\begin{figure}[H]
\includegraphics[width=\textwidth]{example-image-b}
\caption{2nd picture}
\end{figure}

\end{document}

enter image description here

enter image description here

  • Thanks for the suggestion, but not quite what I'm looking for. I am happy to let the figure float (rather than being inserted in between paragraphs). I just want it to start it's float algorithm from a different place in the document. – Branden Ghena Apr 07 '17 at 00:11