Take the 2-minute tour ×
TeX - LaTeX Stack Exchange is a question and answer site for users of TeX, LaTeX, ConTeXt, and related typesetting systems. It's 100% free, no registration required.

I have sort of a strange formatting requirement. For the situation where a figure and its caption are too large to fit on a single page, the caption needs to appear by itself on the page directly preceding the caption. What's more, the formatting for this page is very specific (and involves geometry changes which are a lot like even pages in a two-sided printing even though the document is single sided, cf. this question—blame whoever designs dissertation formatting requirements for this, not me).

It's easy enough to construct the page with the caption and the page with the figure. What I need is a way to insert this material into whatever queue LaTeX uses for outputting figures and further to ensure that the two pages stay together.

Note that I don't want to use the \FloatBarrier macro since the goal isn't to start outputting floats now, I simply need to add two new floats and for them to stay together on successive pages.

It looks a bit like I could use \@freelist to grab an insertion number, construct a box for the page and then cause the output routine to be called, but the kernel source is far from clear to me.

share|improve this question
    
Is it really impossible to make the figure slightly smaller in order to squeeze the caption in? From your question I believe you that it is, but somehow I can't picture why this is the case. –  Hendrik Vogt Jan 3 '11 at 13:27
    
@Hendrik: Yeah, I was kind of wondering the same thing. I'm trying to implement the guidelines for the dissertation into a simple to use class file. I'm quite positive I won't be needing this, but the guidelines expend a healthy number of pages on this, so I wanted it to work. –  TH. Jan 3 '11 at 18:49
1  
@TH.: Somehow I still think you'd better put that effort into changing the guidelines (or even better into writing your dissertation ;-)). –  Hendrik Vogt Jan 3 '11 at 18:59
1  
@Hendrik: Yeah, maybe, but what fun would that be? –  TH. Jan 3 '11 at 19:26
    
@TH How about a simpler algo? First define a macro to check-size of figure, if >limit set caption page ->set image page ->eject Since you know that these two pages are full, why float them? Alternatively, image height can be reduced slightly to fit caption. –  Yiannis Lazarides Jan 7 '11 at 4:21
show 1 more comment

2 Answers

up vote 2 down vote accepted

Here is how I would do something like this in ConTeXt. Maybe that will give you some ideas about a LaTeX solution.

The key to the solution are two properties

  1. The ability to have just one float on a page. In ConTeXt, you can do this using

    \placefigure[...][page]{....}{....}
    

    I do not know if the p specifier for floats in LaTeX works the same way or not.

  2. The ability to insert arbitrary text on the next page. In ConTeXt, you can do this using

    \startpostponing[+1] .... \stoppostponing
    

    In fact, instead of +1, you can use +2 to insert something after two pages, and so on. I don't know if there is a LaTeX package that provides this functionality.

Then, the solution is just a matter of combining these two features:

\startpostponing[+1]
\placefigure[page]{Caption of the figure}{\strut}
\page
\placefigure[page,nonumber]{}
    {\externalfigure[...][width=\textwidth,height=\textheight]}
\stoppostponing

This appears to be working in the few examples that I tried. Here is a complete working example:

\useMPlibrary[dum] %For dummmy figures

% Arbitrary text
\startbuffer
\dorecurse{5}{
\input knuth \endgraf
\dorecurse{4}
{
\placefigure[here,top,bottom]
  {An arbitrary floating figure}
  {\externalfigure[dummy][width=0.4\textwidth,height=0.3\textheight]}
}
\input knuth \endgraf
}
\stopbuffer

\starttext

\getbuffer
\startpostponing[+1]
\placefigure[page]{Caption of the figure}{\strut} \page
\placefigure[page,nonumber]{}{\externalfigure[dummy][width=\textwidth,height=\textheight]}
\stoppostponing
\getbuffer

\stoptext
share|improve this answer
    
This was my first ConTeXt document compiled! Unfortunately, this doesn't work. Figure 21 appears before figure 20. –  TH. Jan 6 '11 at 1:52
    
In my output (I am using MkIV, ver 2010.12.02). Fig 21 appears after Fig 20, but also after Fig 23 and 24. In some sense, the code is doing exactly what it is told. It places the Fig 21 on the next page, irrespective of the current status of the float list. I agree that it would have been better if floats 22 and 23 (which ended up on the current page) were renumbered. –  Aditya Jan 6 '11 at 16:10
    
@Aditya: It's easy enough to tell LaTeX to just insert material on the next page using the afterpage package, but that doesn't solve my problem. I need the floats to be in order. –  TH. Jan 6 '11 at 16:28
    
Did you try the equivalent code in LaTeX? It is possible that LaTeX's float management is more intelligent than ConTeXt's and does renumber the floats. –  Aditya Jan 6 '11 at 16:57
    
@Aditya: Ha! LaTeX's float management is (in my opinion) one of its weakest points. Even using \begin{figure}[H] can put floats in the wrong order since the figure isn't inserted into the floating queues. –  TH. Jan 7 '11 at 0:32
show 2 more comments

The following code seems to work under extremely minimal testing (i.e., onesided, single column, and without colors only). It's a pretty huge hack though. I'm definitely open to better ways to do this or improvements on this code.

There are two parts to the code. The first is a new floating environment, sort of.

\newbox\my@captionbox
\newenvironment{facingcaption}[1]{%
    \let\my@saved@caption\@caption
    \global\setbox\my@captionbox\box\voidb@x
    \long\def\@caption##1[##2]##3{%
        \global\setbox\my@captionbox\color@vbox\vbox{%
            \my@saved@caption##1[##2]{##3}%
        }\color@endbox
        \def\caption{\ClassError{mydissertation}{%
            Only one \noexpand\caption\space per
            facingcaption}{}}%
    }%
    \@xfloat{#1}[p]%
}{%
    \@endfloatbox
    \let\@endfloatbox\relax
    \ifvoid\my@captionbox\else
        \ht\my@captionbox\z@
        \dp\my@captionbox1sp
        \global\setbox\@currbox\vbox{%
            \box\@currbox
            \nointerlineskip
            \box\my@captionbox
            \vskip-1sp
        }%
        \ht\@currbox\textheight
    \fi
    \end@float
}

That code redefines \@caption so that it typesets the caption inside a \vbox. (I'm not sure if the color stuff is needed or not. It doesn't seem to hurt, at least when colors aren't used.)

Then, rather than ending the float with \end@float, it ends the box started by \@xfloat—which is usually done by \end@float, hence setting it to \relax. If the caption box is not void, the current float box \@currbox is redefined to be a \vbox consisting of 3 items: the floating material in the original \@currbox, the caption box which has height 0 and depth 1sp and a skip of -1sp. Finally, we set the height of the box to the \textheight so that the output routine will stick the float on a page by itself when it is actually building pages.

Finally, \end@float will actually call the output routine twice which will cause the float to go onto the defer list, but no pages will actually be built and output here.

The second part of the code is a modification to the output routine. When actually producing pages of output, the lists of floats will be processed, one at a time. Since the box has the maximum height to fit on a page, it will be placed by itself. In particular, \@wtryfc will be executed for each box in the list of boxes that will fit on the page. Normally, it just adds its argument (the box) and some space to the \@outputbox.

This code hijacks that and calls my own routine if the height of the output box is 0 (this is just an optimization).

\let\my@saved@wtryfc\@wtryfc
\def\@wtryfc#1{%
    \ifdim\dimexpr\ht\@outputbox + \dp\@outputbox=\z@
        \my@wtryfc{#1}%
    \else
        \my@saved@wtryfc{#1}%
    \fi
}

\my@wtryfc needs to check that its argument is one of these special boxes. It does this by unboxing the box, checking that the last skip is -1sp and that the box before that has depth 1sp. If it does, then it is unboxed and saved. Finally, the only remaining box (the first box in the argument to \@wtryfc) is the original figure box, so save that.

\newbox\my@outputcaptionbox
\newbox\my@outputfigurebox
\def\my@wtryfc#1{%
    \begingroup
    \global\setbox\my@outputcaptionbox\box\voidb@x
    \setbox\z@\vbox{%
        \unvcopy#1%
        \ifdim\lastskip=-1sp
            \unskip
            \setbox\z@\lastbox
            \ifdim\dp\z@=1sp
                \global\setbox\my@outputcaptionbox\vbox{%
                    \unvbox\z@
                }%
                \global\setbox\my@outputfigurebox\lastbox
            \fi
        \fi
    }%
    \ifvoid\my@outputcaptionbox
        \my@saved@wtryfc{#1}%
    \else
        \if\dim\dimexpr\ht\z@+\dp\z@>\z@
            \message{Output box not torn apart!}%
        \fi
        \global\setbox\@outputbox\vbox to\textheight{%
            \vfill
            \box\my@outputcaptionbox
            \vfill
            \vfill
        }%
        \my@savespecial
        \ps@facingcaption
            \@specialpagefalse
        \@outputpage
        \my@restorespecial
        \global\setbox\@outputbox\vbox{}%
        \my@saved@wtryfc{\my@outputfigurebox}%
    \fi
    \endgroup
}

\def\my@savespecial{%
    \let\my@specialpage\if@specialpage
}
\def\my@restorespecial{%
    \global\let\if@specialpage\my@specialpage
}

After looking for the \vbox{figure}\vbox{caption}\vskip-1sp in the \setbox, if no caption box was discovered, then it uses LaTeX's normal \@wtryfc to add the box to \@outputbox. Otherwise, a box corresponding to the facing caption page is constructed in \@outputbox. The special page style conditional is saved (this might be unnecessary, I'm not sure) and then the page style commands for the facingcaption page style are executed (\thispagestyle{facingcaption} doesn't work with \AtBeginShipout used by the showframes package since the margins need to change). Next, the page it actually output using \@outputpage. The \@outputbox is cleared and the normal \@wtryfc is executed for the figure box.

share|improve this answer
add comment

Your Answer

 
discard

By posting your answer, you agree to the privacy policy and terms of service.

Not the answer you're looking for? Browse other questions tagged or ask your own question.