[Tex/LaTex] Separating definition and placement of a figure

content-replicationfloatsmacrospositioning

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.

Best Answer

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

Related Question