8

In my LaTeX document, I want to make it so that sections that can fit entirely on one page are automatically forced to start on the next page so that the section does not get widowed/orphaned. The document has images. If the section is capable of fitting on one page, then I want the equivalent effect of having "\clearpage" before that section. I need this process to be automated because, in the "real" document, the data is auto-generated. Here is a working example:

% test.tex
% For mypic.jpg I used this example image: http://media.istockphoto.com/photos/yellow-flowers-picture-id506321010

\documentclass[letterpaper,12pt,twoside,onecolumn,notitlepage]{report}
\usepackage{float}
\usepackage{lipsum}
\usepackage{graphicx}
\usepackage[margin=0.7in]{geometry}
\raggedbottom
\begin{document}
\chapter{First chapter}


% Want LaTeX to automatically put the equivalent of a \clearpage here.
\section{First section}
The image in this section is going to be widowed onto the next page.  If I hypothetically put ``{\textbackslash}clearpage" before this section, then the entire section including the image would fit on one page.  I'd like to make LaTeX detect that and automatically force this section to start on the next page so that the image doesn't get widowed.

\lipsum[1-2]
\includegraphics[height=3.4in]{mypic.jpg}


% Don't want LaTeX to automatically put the equivalent of a \clearpage here.
\section{Second section}
This section has two images and cannot fit entirely on one page (putting ``{\textbackslash}clearpage" before the section would still result in the section spanning two pages anyway), so I'm fine having this section start here and having the images flow onto the next page -- i.e., I don't want to force this section to start on the next page.

\lipsum[1]

\includegraphics[height=3.4in]{mypic.jpg}

\vspace{\baselineskip}

\includegraphics[height=3.4in]{mypic.jpg}


% Don't want LaTeX to automatically put the equivalent of a \clearpage here.
\section{Third section}
This section has no images and fits on the same page after the second section, so I'm fine having it stay here and don't want to force it to start on the next page.


% Want LaTeX to automatically put the equivalent of a \clearpage here.
\section{Fourth section}
This is also a section that could fit entirely on one page and gets orphaned or widowed, so I'd like it to be automatically forced to start on the next page.

\lipsum[1-2]

\includegraphics[height=3.4in]{mypic.jpg}


\end{document}

Can this be done in LaTeX?

17
  • Have you tried \usepackage[all]{nowidow}? Maybe it suits your needs...
    – Flinston
    Commented Jan 12, 2017 at 8:50
  • 1
    That didn't do anything. If you found a solution with it then can you please post your working example?
    – user553702
    Commented Jan 12, 2017 at 15:56
  • Can't you auto-generate different code? I don't understand the concept of widowed or orphaned sections. How are you extrapolating it to sections exactly?
    – cfr
    Commented Jan 15, 2017 at 2:49
  • No; the generating code would have to know that the section is able to fit on a page and is being widowed/orphaned by default, in order to know when to insert a "\clearpage". Sure it can be applied to sections; try running my example.
    – user553702
    Commented Jan 15, 2017 at 4:10
  • 1
    I haven't worked with (La)TeX for a while now, but just thinking about it, it should theoretically be possible with TeX's box-glue model to typeset each "section" into a box, check whether the height of the box is greater than a page's height, and if so unbox it (so that it is allowed to span multiple pages), else typeset the box without unboxing (so that it will not be broken). This definitely seems like it should be possible; in the LaTeX world there is probably even a package for it. Commented Jan 15, 2017 at 20:17

3 Answers 3

8
+50

Here is one suggestion.

  • Change the auto-generation of the code so that

    \section{<title>}
    ...
    

    is replaced by

    \begin{chunk}{<title>}
      ...
    \end{chunk}
    
  • Use environ to define the environment chunk so that you can capture its contents and test its total height.

  • Use calc so that you can easily calculate the total height (i.e. height + depth) and to make it easier to manage dimensions.
  • Use needspace to reserve sufficient vertical space for the total height of the section if, and only if, that total height is not greater than the height of the text block.

The upshot of this will be to force a page break before the section unless either the section fits on the current page or the section needs more than a single page anyway.

chunked document

Code:

\documentclass[letterpaper,12pt,twoside,onecolumn,notitlepage]{report}
\usepackage{float}
\usepackage{lipsum}
\usepackage{graphicx}
\usepackage[margin=0.7in]{geometry}
\usepackage{environ,calc,needspace}
\raggedbottom
\newlength\chunktotheight
\NewEnviron{chunk}[1]{%
  \settototalheight{\chunktotheight}{%
    \begin{minipage}{\textwidth}
      \section{#1}
      \BODY
    \end{minipage}%
  }%
  \ifdim\chunktotheight>\textheight
  \else
  \needspace{\chunktotheight}%
  \fi
  \addtocounter{section}{-1}%
  \section{#1}
  \BODY
}{}
\begin{document}
\chapter{First chapter}

% Want LaTeX to automatically put the equivalent of a \clearpage here.
\begin{chunk}{First section}
  The image in this section is going to be widowed onto the next page.  If I hypothetically put ``{\textbackslash}clearpage'' before this section, then the entire section including the image would fit on one page.  I'd like to make \LaTeX{} detect that and automatically force this section to start on the next page so that the image doesn't get widowed.

  \lipsum[1-2]
  \includegraphics[height=3.4in]{mypic}% better not to specify the extension ; may not look quite the same as the original
\end{chunk}

% Don't want LaTeX to automatically put the equivalent of a \clearpage here.
\begin{chunk}{Second section}
  This section has two images and cannot fit entirely on one page (putting ``{\textbackslash}clearpage'' before the section would still result in the section spanning two pages anyway), so I'm fine having this section start here and having the images flow onto the next page -- i.e., I don't want to force this section to start on the next page.

  \lipsum[1]

  \includegraphics[height=3.4in]{mypic}

  \vspace{\baselineskip}

  \includegraphics[height=3.4in]{mypic}
\end{chunk}

% Don't want LaTeX to automatically put the equivalent of a \clearpage here.
\begin{chunk}{Third section}
  This section has no images and fits on the same page after the second section, so I'm fine having it stay here and don't want to force it to start on the next page.
\end{chunk}

% Want LaTeX to automatically put the equivalent of a \clearpage here.
\begin{chunk}{Fourth section}
  This is also a section that could fit entirely on one page and gets orphaned or widowed, so I'd like it to be automatically forced to start on the next page.

  \lipsum[1-2]

  \includegraphics[height=3.4in]{mypic}
\end{chunk}

\end{document}

EDIT

This is a response to the comment concerning difficulties including PDFs. Without an example, it is hard to say whether I've worked on the correct issue or not. However, I did fine a problem with use of \includepdf[]{} within a chunk, but not outside them.

The following code should solve this particular problem. The idea is that, since PDF inclusion always starts a new page, any section which includes \includepdf[]{} is necessarily going to require more than one page to typeset. Hence, we check for \includepdf[]{} and do nothing in that case.

To do the check, I simply use a toggle and temporarily redefine \includepdf[]{} to set the toggle true. The original definition is replaced after the check is complete.

\documentclass[letterpaper,12pt,twoside,onecolumn,notitlepage]{report}
\usepackage{float}
\usepackage{lipsum}
\usepackage{graphicx,pdfpages}
\usepackage[margin=0.7in]{geometry}
\usepackage{environ,calc,needspace}
\raggedbottom
\newlength\chunktotheight
\NewEnviron{chunk}[1]{%
  \let\savedincludepdf\includepdf
  \let\includepdf\chunkincludepdf
  \chunkincludepdffalse
  \settototalheight{\chunktotheight}{%
    \begin{minipage}{\textwidth}
      \section{#1}
      \BODY
    \end{minipage}%
  }%
  \ifdim\chunktotheight>\textheight
  \else
    \ifchunkincludepdf
    \else
    \needspace{\chunktotheight}%
    \fi
  \fi
  \let\includepdf\savedincludepdf
  \addtocounter{section}{-1}%
  \section{#1}
  \BODY
}{}
\newcommand*\chunkincludepdf[1][]{\chunkincludepdftrue}
\newif\ifchunkincludepdf
\chunkincludepdffalse
\begin{document}
\chapter{First chapter}

\includepdf{example-image-letter}

% Want LaTeX to automatically put the equivalent of a \clearpage here.
\begin{chunk}{First section}
  The image in this section is going to be widowed onto the next page.  If I hypothetically put ``{\textbackslash}clearpage'' before this section, then the entire section including the image would fit on one page.  I'd like to make \LaTeX{} detect that and automatically force this section to start on the next page so that the image doesn't get widowed.

  \lipsum[1-2]
  \includegraphics[height=3.4in]{mypic}% better not to specify the extension ; may not look quite the same as the original
\end{chunk}

% Don't want LaTeX to automatically put the equivalent of a \clearpage here.
\begin{chunk}{Second section}
  This section has two images and cannot fit entirely on one page (putting ``{\textbackslash}clearpage'' before the section would still result in the section spanning two pages anyway), so I'm fine having this section start here and having the images flow onto the next page -- i.e., I don't want to force this section to start on the next page.

  \lipsum[1]

  \includegraphics[height=3.4in]{mypic}

  \vspace{\baselineskip}

  \includegraphics[height=3.4in]{mypic}
\end{chunk}

% Don't want LaTeX to automatically put the equivalent of a \clearpage here.
\begin{chunk}{Third section}
  This section has no images and fits on the same page after the second section, so I'm fine having it stay here and don't want to force it to start on the next page.
\end{chunk}

\includepdf{example-image-letter}

% Want LaTeX to automatically put the equivalent of a \clearpage here.
\begin{chunk}{Fourth section}
  This is also a section that could fit entirely on one page and gets orphaned or widowed, so I'd like it to be automatically forced to start on the next page.

  \lipsum[1-2]

  \includegraphics[height=3.4in]{mypic}
\end{chunk}

\begin{chunk}{Fifth section}
  This include a full-page PDF, so obviously won't fit on one page.
  Hence, we don't expect a page break prior to this section unless there would be one anyhow.

  \lipsum[1]

  \includepdf{example-image-letter}

  \lipsum[2]
\end{chunk}


\end{document}

chunked document with include PDFs

Without the fix, the final PDF inclusion does not work correctly: the PDF is included twice, with the first inclusion overlaying the previous section. This happens since this inclusion is within a chunk, whereas the first two inclusions were not.

10
  • (+1) i think you understand it ;-) and yes it will be better if you used media.istockphoto.com/photos/yellow-flowers-picture-id506321010 :-)
    – touhami
    Commented Jan 16, 2017 at 19:24
  • @touhami Thanks :-). But where did the OP reference that picture?
    – cfr
    Commented Jan 16, 2017 at 20:22
  • it is (as comment) in second line befor \documentclass
    – touhami
    Commented Jan 16, 2017 at 21:10
  • @touhami I must be blind. Thank you. I've read the source and the comments at least twice trying to find the link :(.
    – cfr
    Commented Jan 16, 2017 at 22:39
  • @touhami Is this better? I'm not sure about the legality of using the original, but this is part of a free software distribution, so should be OK.
    – cfr
    Commented Jan 16, 2017 at 22:52
7

I understand your goal as keeping sections on one page if possible, the unconventional usage of "widow" and "orphan" terminology notwithstanding.

Expanding my comment above into an answer: the idea is to put the entire section into a box, and measure the height of the box. If the height of the box is small enough that it would fit in a page, and if it is large enough that it wouldn't fit on the current page, then we insert a \clearpage. I believe that is exactly what you wanted.

To implement this idea,

  • It will help to change your auto-generated code to use something like \begin{mysection}{Name of section} and \end{mysection}, instead of just \section{Name of section}.
  • You can use the following code to define the mysection environment accordingly:

    \newbox\mysectionbox
    \newenvironment{mysection}[1]{%
    \setbox\mysectionbox=\vbox\bgroup%
    \section{#1}
    }{\egroup
    % Calculate remaining space on the page
    \newdimen\pageremaining
    \pageremaining=\pagegoal
    \advance\pageremaining by -\pagetotal
    % If \mysectionbox fits on a page, but not on current page, then insert a clearpage.
    \ifdim\ht\mysectionbox>\pagegoal
    \else
      \ifdim\ht\mysectionbox>\pageremaining
        \clearpage
      \fi
    \fi
    % Unbox the section box and typeset it.
    \unvbox\mysectionbox
    }
    

(This requires no additional packages and uses only things available in plain TeX, apart from of course \clearpage which you wanted, and \newenvironment… more on this below.)

Here is output produced by your example, modified accordingly:

Output from OP's example with <code>mysection</code>

% test.tex
% For mypic.jpg I used this example image: http://media.istockphoto.com/photos/yellow-flowers-picture-id506321010

\documentclass[letterpaper,12pt,twoside,onecolumn,notitlepage]{report}
\usepackage{float}
\usepackage{lipsum}
\usepackage{graphicx}
\usepackage[margin=0.7in]{geometry}
\raggedbottom

\newbox\mysectionbox
\newenvironment{mysection}[1]{
\setbox\mysectionbox=\vbox\bgroup
\section{#1}
}{\egroup
% Calculate remaining space on the page
\newdimen\pageremaining
\pageremaining=\pagegoal
\advance\pageremaining by -\pagetotal
% If \mysectionbox fits on a page, but not on current page, then insert a clearpage.
\ifdim\ht\mysectionbox>\pagegoal
\else
  \ifdim\ht\mysectionbox>\pageremaining
    \clearpage
  \fi
\fi
% Unbox the section box and typeset it.
\unvbox\mysectionbox
}

\begin{document}
\chapter{First chapter}


% Want LaTeX to automatically put the equivalent of a \clearpage here.
\begin{mysection}{First section}
The image in this section is going to be widowed onto the next page.  If I hypothetically put ``{\textbackslash}clearpage" before this section, then the entire section including the image would fit on one page.  I'd like to make LaTeX detect that and automatically force this section to start on the next page so that the image doesn't get widowed.

\lipsum[1-2]
\includegraphics[height=3.4in]{mypic.jpg}
\end{mysection}

% Don't want LaTeX to automatically put the equivalent of a \clearpage here.
\begin{mysection}{Second section}
This section has two images and cannot fit entirely on one page (putting ``{\textbackslash}clearpage" before the section would still result in the section spanning two pages anyway), so I'm fine having this section start here and having the images flow onto the next page -- i.e., I don't want to force this section to start on the next page.

\lipsum[1]

\includegraphics[height=3.4in]{mypic.jpg}

\vspace{\baselineskip}

\includegraphics[height=3.4in]{mypic.jpg}
\end{mysection}

% Don't want LaTeX to automatically put the equivalent of a \clearpage here.
\begin{mysection}{Third section}
This section has no images and fits on the same page after the second section, so I'm fine having it stay here and don't want to force it to start on the next page.
\end{mysection}

% Want LaTeX to automatically put the equivalent of a \clearpage here.
\begin{mysection}{Fourth section}
This is also a section that could fit entirely on one page and gets orphaned or widowed, so I'd like it to be automatically forced to start on the next page.

\lipsum[1-2]

\includegraphics[height=3.4in]{mypic.jpg}
\end{mysection}

\end{document}

(Note: I haven't thought carefully about whether it's necessary to consider the depth (\dp) of the vbox in addition to its height. Probably.)

To make a further recommendation that would be controversial on this site: IMO for auto-generated documents, there isn't a strong reason to use LaTeX instead of plain TeX; at any rate, you would be better off if you took a few hours to learn TeX's boxes-and-glue model of typesetting (e.g. from A Beginner's Book of TeX or of course The TeXbook), and used (La)TeX solely as a typesetter and did your programming externally (as much as possible). LaTeX is a profound idea of a "document-preparation system" that uses "logical structure" (as advocated by Leslie Lamport (reprinted in TUGboat)), keeping the author concerned more with the content than the appearance (thinking of their "document" as a collection of "chapters", "sections", "subsections", etc.), but if you're already managing your content elsewhere and using (La)TeX only for producing pages, and care very much about the appearance, then it makes sense to understand exactly what's happening under the hood so that you can influence it appropriately.

Edit: To go with my suggestion of keeping programming out of (La)TeX as much as possible to preserve your sanity, here's an equivalent with some of the "programming" part (getting the height and comparing it) done in Lua: my first lines of LuaTeX code :-) (You'll need to compile the file with lualatex instead of pdflatex.)

\newbox\mysectionbox
\newenvironment{mysection}[1]{
  \setbox\mysectionbox=\vbox\bgroup
  \section{#1}
}{\egroup
  \directlua{
    local remaining = tex.get('pagegoal') - tex.get('pagetotal')
    local myboxheight = tex.getbox('mysectionbox').height
    -- If the box can fit on a page *and* cannot fit on current page, then clear page.
    if myboxheight <= tex.get('pagegoal') and myboxheight > remaining then
      tex.print([[\noexpand\clearpage]])
    end
  }
  % Unbox the section box and typeset it.
  \unvbox\mysectionbox
}
5
  • (+1) Much better pictures than mine ;).
    – cfr
    Commented Jan 16, 2017 at 1:19
  • Really? How so?
    – cfr
    Commented Jan 16, 2017 at 3:37
  • (+1) very good :-) by the way 2 remarks: we don't realy need \clearpage and it will be better if \newdimen\pageremaining is used outside mysection definition. any way that's very good.
    – touhami
    Commented Jan 16, 2017 at 19:21
  • Try taking a copy of my fifth section (chunk) and change it to mysection. Assuming the OP is asking about the problem I found and not another one, and that the problem is the same with your first solution as with my original code, I guess that you will see the problem. (I haven't tested to check this, though, so it is possible that the problem differs. However, that seems unlikely as our solutions are basically the same.)
    – cfr
    Commented Jan 16, 2017 at 23:24
  • Yes I can completely believe that if one attempts to put an included page into this vbox, then it will not work. To which my answer would be, as the doctor said, "well, stop doing it then". :-) It is possible to make TeX recognize when a \includepdf has happened and not do anything, but it would be simpler in wherever the TeX code is being generated to simply generate the usual \section… stuff for such sections instead. Just because it can be done in TeX doesn't mean it should be. :-) Unless it's really unavoidable here for some reason… Commented Jan 17, 2017 at 1:15
6

I am not completely satisfied with the two answers that have already been posted, so I’ll try to propose a refinement of the same basic idea. Allow me to explain what I deem to be the relative weaknesses of the other answers.

@cfr’s answer swallows up the whole contents of a section as a macro argument and processes them twice as a token list, the first time in order to decide what to do, and the second time to do the actual typesetting. I think this is the only method that permits to cope with the problem of \includepdf, since obtaining the correct placement of the included pages entails executing one or more \shipout at the appropriate times, and \shipout is a TeX primitive command, so it can only be executed in TeX’s stomach, while digesting tokens (this is not the same way as, e.g., the way in which a \write is performed). So, in my opinion, @cfr’s answer should be regarded as the most general and powerful solution of the problem, and I suggest that you accept it and award the bounty to her. Notwithstanding this, storing the tokens of a whole section as a macro argument and processing them twice is (1) inefficient, and (2) prone to introducing undesired side effects, as it was the case with the section counters incremented twice; so, we see that it can be worthwhile to look for solutions based on different strategies.

Indeed, the contents of an environment can also be captured at a later point downstream in TeX’s processing line, that is, as a list of boxes; more precisely, contents that (potentially) span several paragraphs can only be collected, in practice, as a vertical list. This is exactly what @ShreevatsaR’s solution does: it measures the height of the collected list and, based on the result of this measurement, hands on to the page builder a suitably manipulated version of that list. Note that this manipulation takes place well downstream of TeX’s stomach in TeX’s processing line, when it is no longer possible to execute primitive commands, and this is the reason that makes it impossible for this solution to cope with the \includepdf problem: simply, there’s nothing you can put in a list of boxes that can make TeX perform a \shipout operation at a certain point of that list (again, contrast this with the behavior of \write).

However, the way in which @ShreevatsaR’s solution implements this general idea is, IMHO, not entirely correct: basically, the main downside that I find in it is that it only considers the natural height of the captured contents, and not their ability to shrink; but there are also a few more problems, that the comments included in the code given below will discuss. For this reason, I dare submit another answer based on the same general principle, but implemented via \vsplit; it works like this:

  • it captures the contents of an environment as a vertical list, exactly as @ShreevatsaR does;

  • it tries to \vsplit this list either to the available height remaining on the current page, or to the full \textheight (note that \vsplit will consider the available shrinkability);

  • it eventually delivers to the page builder a suitably manipulated version of the captured list, again exactly as in @ShreevatsaR’s solution.

Since it is based on the same principle, my solution doesn’t permit to use \includepdf inside the environment either. I consider it a significant enhancement, though, to have introduced proper management of the interline glue at the environment’s boundaries.

I’ve split the code into two files. The first is a package, named tryfittinginpage, that defines an environment by the same name, inside which you should put the material that you want to try to make fit in a single page:

\NeedsTeXFormat{LaTeX2e}[2004/06/01] % LaTeX2e is required!
\ProvidesPackage{tryfittinginpage}
                [2017/01/17 version information here]

% Diagnostic commands.
\newcommand*\@TFIP@Show[1]{%
  \begingroup
    \showboxbreadth \@m
    \showboxdepth \sixt@@n
    #1% what should be shown
  \endgroup
}
\newcommand*\ShowList{\@TFIP@Show\showlists}
% In the following command, "\relax" is not needed after "#1" since
% "\endgroup" will follow anyway:
\newcommand*\ShowBox[1]{\@TFIP@Show{\showbox #1}}
\newcommand*\@TFIP@debug@Show[2]{%
  \typeout{}%
  \typeout{================================================%
                ================}%
  \typeout{#2}%
  \typeout{}%
  \ShowBox{#1}%
}
\newcommand*\@TFIP@maybe@debug@Show[2]{}
\newcommand*\TFIPDebugInfoOn{%
  \let \@TFIP@maybe@debug@Show \@TFIP@debug@Show
}
\newcommand*\TFIPDebugInfoOff{%
  \let \@TFIP@maybe@debug@Show \@gobbletwo
}

\DeclareOption{debug}{\TFIPDebugInfoOn}
\DeclareOption{quiet}{\TFIPDebugInfoOff}
\ExecuteOptions{quiet}
\ProcessOptions\relax

\newenvironment*{tryfittinginpage}{%
  \par
  % The following assignment need not be "\global", bur we prefer to do
  % so in order to conserve save stack positions, given the fact that,
  % in any case, "\dimen@i" is going to be altered below.
  \global \dimen@i \prevdepth
  \setbox\z@ \vbox\bgroup % collect environment contents in "\box0"
    \prevdepth \dimen@i
}{%
    \par
    \global \dimen@i \prevdepth
  \egroup % end of "\vbox"
  \@TFIP@maybe@debug@Show \z@
    {Original contents of the environment:}%
  \dimen@ \pagegoal
  \advance \dimen@ -\pagetotal
  % Do not forget that the material accumulated in the page so far can
  % cooperate in making our content fit in the current page.
  \advance \dimen@ \pagestretch % now "\dimen@" = available space
  % Note that we cannot prevent the "\splittopskip" glue from being
  % added during a "\vsplit", so we make sure that at least it is zero.
  \splittopskip \z@skip
  % Use in "\vsplit" the same setting used by the page builder:
  \splitmaxdepth \maxdepth
  % We'll need to reinsert the discared items if we eventually find that
  % the material does not fit:
  \savingvdiscards \@ne
  % Finally, avoid warnings:
  \vbadness \@M
  \setbox\tw@ \vsplit\z@ to \dimen@
  % Convention: we'll leave in "\box0" the material that should be
  % returned to the main vertical list.
  \ifvoid\z@ % if everything fits in the current page...
    \setbox\z@ \vbox{% ...put it back in "\box0"
      \@TFIP@clever@unbox@two \dimen@
    }%
    \@TFIP@maybe@debug@Show \z@
      {It fits in the current page; will append:}%
  \else
    \@TFIP@reassemble@contents
    \@TFIP@maybe@debug@Show \z@
      {It does NOT fit in the current page; reassembled contents:}%
    % Note that it is wrong, in general, to use the current value of
    % "\pagegoal" as the measure of the height of the text.
    \setbox\tw@ \vsplit\z@ to \textheight
    \ifvoid\z@ % if the contents fit in a single page...
      \setbox\z@ \vbox{% ...package them in "\box0" with a page break
        % We insert here the equivalent of "\clearpage": the page
        % builder will take care of forwarding the "\penalty -\@Mi"
        % message to the output routine.
        \vfil\break
        \write\m@ne{}%
        \vbox{}%
        \penalty -\@Mi
        \@TFIP@clever@unbox@two \textheight
      }%
      \@TFIP@maybe@debug@Show \z@
        {It fits in the next page; will append:}%
    \else % if the oontents do not fit in a single page...
      \@TFIP@reassemble@contents % ...put them back in "\box0"
      \@TFIP@maybe@debug@Show \z@
        {It does NOT fit in a page by itself; will append:}%
    \fi
  \fi
  \unvbox\z@
  \prevdepth \dimen@i
}
\newcommand*\@TFIP@reassemble@contents{%
  % This macro reassembles in "\box0" the original contents of the
  % environment after they have been split.
  \setbox\z@ \vbox{% now be careful:
    \unvbox\tw@ % split-off material
    \splitdiscards % includes chosen breakpoint
    \nobreak % because the "\splittopskip" glue follows, and of course
             % it hadn't been considered as a possible breakpoint in
             % the previous steps...
    \unvbox\z@ % rest of the contents
  }%
}
\newcommand*\@TFIP@clever@unbox@two[1]{%
  % This macro unboxes "\box2" while taking care of the following
  % problem: if "\vsplit" shrank the contents in order to make them fit
  % in the given height (and note that "\vsplit" assumes an implicit
  % "\penalty -10000" at the end of the vertical list being split, see
  % "The TeXbook", p. 124, lines 6-7), we need to be sure that the page
  % builder will choose the same breakpoint; for this, we insert
  % "\break" at the end of the current list, but only if the natural
  % height of "\box2" exceeds the dimension passed as argument #1.
  \ifdim\ht\tw@>#1% assume #1 is a <dimen> token
    \unvbox\tw@
    \break
  \else
    \unvbox\tw@
  \fi
}

\endinput

Save this code as tryfittinginpage.sty and put it in a place where TeX looks for input files (e.g., in the same directory as the file you want to compile). Note that the code includes some diagnostic commands you could very well get rid of.

The second file is an ordinary LaTeX document that invokes the tryfittinginpage package and tests it:

% My standard header for TeX.SX answers:
\documentclass[a4paper]{article} % To avoid confusion, let us explicitly 
                                 % declare the paper format.

\usepackage[T1]{fontenc}         % Not always necessary, but recommended.
% End of standard header.  What follows pertains to the problem at hand.

\usepackage{tryfittinginpage}
\usepackage{caption}
\usepackage{pdfpages}
\usepackage{mwe}



\begin{document}

\tableofcontents
\listoffigures

\section{Some recipes}

\lipsum[1-2]

\begin{figure}[tbp]
    \centering
    \includegraphics{image}
    \caption{An insertion that makes things a little bit more difficult}
    \label{fig:image}
\end{figure}

The presence of figure~\ref{fig:image} explains why it is necessary to ensure
that dangling insertions are processed before the contents of our environment
get re-inserted.

% \showthe\prevdepth
% 
% \TFIPDebugInfoOn

\begin{tryfittinginpage}
    \subsection{Sachertorte} % :-)
    \lipsum[3]
    \begin{center}
        \includegraphics{image-a}
        \captionof{figure}{Is this a Sachertorte?}
    \end{center}
    \lipsum[4]
\end{tryfittinginpage}

Note that some ordinary text may follow on the same page.
\par\lipsum[5-7]

\begin{tryfittinginpage}
    \subsection{Gnocchi alla bolognese}
    This is expected to fit in the same page.
    \par\lipsum[8]
\end{tryfittinginpage}

% \TFIPDebugInfoOff

Again, ordinary text may follow (of course, it's always possible to prevent this
from happening by inserting an explicit \verb|\pagebreak| immediately after the
end of the environment.

\begin{tryfittinginpage}
    Now, a section that will not fit in a single page.
    \subsection{Canard \`{a} l'orange}
    \lipsum[9-16]
    \begin{center}
        \includegraphics{image-b}
        \captionof{figure}{And this?  Could it ever be a canard?}
    \end{center}
\end{tryfittinginpage}

\section{Technical trials}

Here we check some technical issues.

\begin{tryfittinginpage}
    \subsection{Page breaks are honored}
    Without the page breaks\ldots
    \newpage
    \ldots this section would almost certainly\ldots
    \newpage
    \ldots fit in a single page (but not at the end of the page on which the
    environments starts, because of the ``lipsum'' text).
    \par\lipsum[17-18]
\end{tryfittinginpage}

\newpage

Text between two sections.
\lipsum*[19]
This text starts on a new page and continues until it gets to the eighth line.

\begin{tryfittinginpage}
    \subsection{Without shrinkability}
    This section contains an unshrinkable vertical space that prevents it from
    fitting into a single page.  Lorem ipsum adipisci elit num.  This text takes
    up three lines, no more and no less.
    \par\vspace{.9\textheight}
    This text comes at the end of a section, after a big, unshrinkable vertical 
    space.  Lorem ipsum dolor sit amet.
\end{tryfittinginpage}

\newpage

Now we repeat the test under the same conditions, but with shrinkability.
\lipsum*[19]
This text gets to the eighth line too.

\begin{tryfittinginpage}
    \subsection{With shrinkability}
    This section contains a shrinkable vertical space with the same natural 
    length as the space contained in the previous section.  This text takes up 
    three lines, no more and no less.
    \par\vspace{.9\textheight minus .1\textheight}
    This text comes at the end of a section, after a big, but shrinkable
    vertical space.  Lorem ipsum dolor sit amet.
\end{tryfittinginpage}

\subsection{Including pages}
Unfortunately, it is possible to include a whole page with \verb|\includepdf|
only when you are outside the \texttt{tryfittinginpage} environment.

\includepdf{image-a4}

\lipsum[20]

\end{document}
6
  • Excellent! That was very informative, thank you. Commented Jan 18, 2017 at 0:51
  • (+1) Though I understand not one single line of it.
    – cfr
    Commented Jan 18, 2017 at 1:33
  • Could you elaborate on \pagegoal versus \textheight — which one is more appropriate when? Commented Jan 18, 2017 at 23:27
  • @ShreevatsaR: First, note that you are using the value of \pagegoal that is current when your environment starts; next, observe than \pagegoal is set to the value of \textheight when TeX adds the first box (or insertion) to an empty “current page” (cf. The TeXbook, p. 114), but it can subsequently change if insertions (e.g., figures, or footnotes) occur on the page (p. 113, lines 13–14). Now, think of what happens if your environment chances to start on a page that already contains a figure or a (possibly tall) footnote…
    – GuM
    Commented Jan 19, 2017 at 17:21
  • 1
    @ShreevatsaR: Be careful that “head and foot”, in that context, do not refer to places where floating insertions or footnotes will go, but to the “running” heads and foots that contain such things as page numbers and marks. Floating insertions and footnotes are placed within the boundaries of the text area. Having said that, your final remark is correct. If you have any doubts left, however, you can experiment directly with LaTeX, or you can ask a question on this specific topic.
    – GuM
    Commented Jan 20, 2017 at 18:59

You must log in to answer this question.

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