12

This question led to a new package:
chkfloat

Making small tweaks to my (two-column) document can result in my floats (both single and double columns) moving all over the place. I know that my document is more likely to look good if float F is on page P say, than if it is on page P+1. However, I have a long document and it is tiresome to check every page after each build to see if my floats have moved.

Therefore, I would like LaTeX to raise an error (or at least alert me in some way) if float F(n) is not on page P(n) where F(n) and P(n) are specified for some subset of my floats (I'd be happy with an answer that can deal with tracking one float to begin with though!). However, it is not possible to put checking code inside the floats themselves, because this code is executed when the floats are "read" rather than when they are "placed".

Even if I can tweak the float parameters so that the floats appear in locations I prefer, this would still be a useful automated test, so the focus of my question is not intended to be about float parameters, unless there are some magic parameters that force LaTeX to put the floats in approximate places that I specify (that is, floating places! --- not just 'here', which I assume is not possible for two column floats anyway).

Clearly, LaTeX does keep track of which floats appear on which page, as the \listoffigures can show this information. But I am not sure what's the best way of accessing that information myself to do the testing; is there a hook that gets called when a float is placed? Preferably the test would work in one run (note that the list of figures takes two runs to update), but this is not essential.

I've included a single-column MWE with a single float to play with. Changing the \lipsum range changes where the float gets placed. (If the double column nature makes a big difference, I can try to supply a modified MWE.)

\documentclass{article}
\usepackage[a4paper]{geometry}%don't trust MikTeX's defaults!
\usepackage{lipsum}
\begin{document}
    % choose which of the following two lines to use to configure where the float appears.
    \lipsum[1-5]% figure appears on page 2 if this line is used
    %\lipsum[1-4]% figure appears on page 1 if this line is used
    \begin{figure}
        \centering
        Hello world. I was typeset on page \thepage, but that may not be where I appear!

        \rule{4cm}{4cm}

        \caption{fig}
    \end{figure}
    ***Figure appeared in the text just above here.***

    \lipsum[5-8]
    \listoffigures
\end{document}
5
  • I believe it would be possible to envoke a warning when a float gets moved by more than X pages (where X is arbitrary number, best probably X=1). Would that be sufficient to you?
    – yo'
    Aug 19, 2012 at 17:46
  • Yes, thanks, that would probably do what I want. But, I think the case X=0 is best, as "float gets moved more than X pages" is the same as "float has moved". (Also, I'm not sure if we are talking about "a float" as in a particular float F? But I'm sure the general case that just detects that "there exists a float in the document that has moved" could be adapted to focus on a specific float.) Aug 19, 2012 at 18:01
  • I'll be able to say exactly which float got moved of course, I'll look into it, just give me some time.
    – yo'
    Aug 19, 2012 at 18:04
  • If you specify \begin{figure}[!h] then LaTeX gives warnings in the .log file if it has changed it; in your example, it gives LaTeX Warning: !h' float specifier changed to !ht'. You could then grep the .log file accordingly
    – cmhughes
    Aug 19, 2012 at 18:17
  • @cmhughes I would worry that the placement parameters would be changed with that approach; would LaTeX then avoid placing the figure at the bottom of any page? Aug 19, 2012 at 18:27

2 Answers 2

11

Different approach from JLDIaz. Just put the code between START HERE and END HERE by the end of yout preamble. The code is self-commented.

Remark: I made this into a package. Don't use this code directly, better get and use the package.

\documentclass{article}

% START HERE
\makeatletter
% tolerances
\PassOptionsToPackage{patch}{kvoptions}
\RequirePackage{kvoptions}
\DeclareStringOption{tolerance}
\def\chkfloat@tolerance{1}
\ProcessKeyvalOptions*
% store original macros \@float and \@caption
\let\chkfloat@float\@float
\let\chkfloat@caption\@caption
% make \@float remember the page where the float should be
\def\@float{\edef\chkfloat@page{\thepage}\chkfloat@float}
% make \@caption write to a file .fof the information about final page, original page and float caption
\def\@caption#1[#2]#3{\chkfloat@caption{#1}[#2]{#3}%
  \addtocontents{fof}{\protect\chkfloat@{\thepage}{\chkfloat@page}{\csname fnum@#1\endcsname: #2}}%
}
% checking macro
\def\chkfloat@#1#2#3{\ifnum#1>\numexpr#2+\chkfloat@tolerance\relax
  \begingroup\let\on@line\@gobble\def\nobreakspace{ }\GenericWarning{}{Float misplaced on pages #2->#1, #3}\endgroup
\fi
}
% process the file .fof
\@starttoc{fof}

\makeatother
% END HERE

\begin{document}

Hello world!

\begin{figure}[b]
FIGURE BOTTOM
\caption{Figure at bottom}
\end{figure}

\begin{figure}[p]
FIGURE PAGE
\caption{Figure at its page}
\end{figure}

\end{document}
3
  • This isn't quite the problem I had in mind, apologies again for the misunderstanding, but it is more general and more useful. Thanks! I have learnt a few things in the last couple of days and will try to rephrase my question when I get a chance to target my remaining issues. Aug 22, 2012 at 7:11
  • Perhaps you'd like to add the equivalent version of the code using your package also, especially as it's only one line (it's nice to see everything all in one place!) Aug 22, 2012 at 7:17
  • 1
    @cyberSingularity I'd suggest you to start a new question where you might state the problem clearlier.
    – yo'
    Aug 22, 2012 at 12:59
8

Here is an idea. When you use the command \label next to a caption, LaTeX writes into the .aux file the figure number and the page number (the actual page number in which the figure appears). In addition it defines a macro which stores that page number, which can be retrieved with command \pageref.

The idea is to add to each figure a \label which specifies the page at which the figure should appear, as for example: \label{ShouldBeOnPage1}. An external script could read the -aux file and compare the name of the label with the value of its page number.

Even better, LaTeX can do this check at the end of the document. The following MWE provides the basic idea, but it only checks for a single figure. A proper solution would include a loop to check all the figures, but I lack the knowledge to implement the complete solution. You can manually specify each check as I did with the first figure:

\documentclass{article}
\usepackage[a4paper]{geometry}%don't trust MikTeX's defaults!
\usepackage{lipsum}

\def\CheckFloat#1{%  The macro which does the check for a single figure
% The parameter is the page number at which the figure should appear
\expandafter\ifx#1\pageref{ShouldBeOnPage#1}%
\relax
\else\GenericWarning{(Floats)}{Warning: A float which should appear on page #1 is misplaced (to page \pageref{ShouldBeOnPage#1})}
\fi
}


\AtEndDocument{% Manually do all checks
 \CheckFloat{1}
}

\begin{document}
% choose which of the following two lines to use to configure where the float appears.
\lipsum[1-5]% figure appears on page 2 if this line is used
%\lipsum[1-4]% figure appears on page 1 if this line is used
\begin{figure}
\centering
Hello world. I was typeset on page \thepage, but that may not be where I appear!

\rule{4cm}{4cm}

\caption{fig}\label{ShouldBeOnPage1}  % <----- Add this to the figures
\end{figure}
***Figure appeared in the text just above here.***

\lipsum[5-8]
\listoffigures
\end{document}

Running this example you get the warning:

Warning: A float which should appear on page 1 is misplaced (to page 2\hbox {})
 on input line 33.

Note that my code assumes a single figure per page. A proper solution would define a macro for each figure, based on the figure number, which stores the page at which the figure should appear, and create a label based also on the figure number and the expected page, which will provide the actual page via \pageref.

I know this is not a complete solution, but I hope this idea is worth and someone more skilled than me completes it.

3
  • Thanks, that could be the start of a great idea! I'm currently thinking about extensions. Perhaps wrapping the \label in a \thisshouldappearonpage{1} type command and getting that to sequentially number the labels. By the end of the document, the checks could be done for all labels between 1 and the current value of the sequential counter. Aug 19, 2012 at 18:31
  • Yes! That was indeed my idea, but when I started to code it, I realized it will take me too much time, so I first posted the simple version while I think more deeply in the complete one.
    – JLDiaz
    Aug 19, 2012 at 18:52
  • Defining so many unnecessary labels eats quite a lot of TeX capacity!
    – yo'
    Aug 19, 2012 at 19:07

You must log in to answer this question.

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