2

We publish a similar booklet every year, and currently layout the document by hand, to reach a page count divisible by 4, while having headings at the top of the pages and avoiding white space on the pages. This often includes adding or removing images or replacing paragraphs with shorter or longer versions of the same text. Most text is contained within a multicols environment, while most images aren't.

Is there some way to make LaTeX automatically choose one of these text variants with different length, filling the available space best while avoiding almost completely blank pages before a \pagebreak caused by too long contents?

Edit MWE:

\documentclass[10pt,a4paper]{article}
\usepackage[latin1]{inputenc}
\usepackage{multicol}
\usepackage[english]{babel}
\usepackage{blindtext}
\begin{document}
    \begin{multicols}{2}
        \blindtext

        \blindtext

        \blindtext

        \blindtext

        \blindtext

        Hello, here is some text without a
        meaning. This text should show what
        a printed text will look like at this
        place. If you read this text, you will
        get no information. Really? Is there
        no information? Is there a di erence
        between this text and some nonsense
        like Huardest gefburn? Kjift  not at
        all! A blind text like this gives you in-
        formation about the selected font, how
        the letters are written and an impres-
        sion of the look. This text should con-
        tain all letters of the alphabet and it
        should be written in of the original lan-
        guage. 

        This is a variant, that should not be used in this case, because it really breaks the layout, and I don't want that. Word Word Word Word.
%       This is a second variant, to avoid the almost empty second page in variant one.
    \end{multicols}

\end{document}
  • 1
    Sounds like a tough problem but you are more likely to get help if you provide a minimal working example for people to work with, In this instance, it should be a mock document that needs some adjustment. Also by "shorter or longer versions of the same text" are you talking adding extra text or just adjusting the font size? Clearly latex can helkp only with the latter – Andrew Sep 15 '17 at 6:48
  • See edit -- I am talking about adding predefined text, not to be generated by LaTeX. The text variants already are there from previous years, but currently they have to be commented in and out by hand, to choose the combination that fits better. – phi1010 Sep 15 '17 at 14:59
1

This works on the whole document, trying to get the contents to a multiple of 4 pages. It requires multiple runs of LaTeX.

How it works:

If the remaining space to fill a multiple of 4 pages is more the two pages, it will try to shorten the contents more and more with each run, until it is just below a 4-page limit. Otherwise it will try extend the contents. Here it will go above the 4-page limit and then go one step back.

It will stop, if the goal is reached or it can't shorten or extend the contents any more.

A warning is given out, telling you, if an additional run is required or not.

The main command is \variant{<level>}{<short contents>}{<normal contents>}{<long contents>}. <level> must be an integer greater then 0. For each run a counter will be increased (starting at 0) and the short or long contents will be used, if the level is less or equal than that counter. <short contents> may be empty. If <long contents> is empty, will be used instead.

With \dontchangetrue the level counter will not be changed. This is useful, as long as contents is still added to the document.

And \allowedspace is the remaining space allowed at the end (default: 0pt).

The macro produces an auxiliary file with the ending .vrt. If you want to restart, this file must be erased.

\documentclass[10pt,a4paper]{article}
\usepackage[latin1]{inputenc}
\usepackage{multicol}
\usepackage[english]{babel}
\usepackage{graphicx}         % for test with picture
\usepackage{xcolor}           % just for testing (color text to easy see what version is used)
\usepackage{blindtext}

\makeatletter
\newcounter{VariantLevel}
\newcounter{MaxVariantLevel}
% tristate flag, 0: less contents needed, 1: ok, 2 more contents needed
\newcounter{ContentsNeeded}
\newcounter{PrevContentsNeeded}
\newcounter{InitialContentsNeeded}
\newdimen\RemainingOnPage
\newdimen\PrevRemainingOnPage
\newdimen\InitialRemainingOnPage
\newcounter{RemainingPages}
\newcounter{PrevRemainingPages}
\newcounter{InitialRemainingPages}
\newif\iffirstrun
\newif\ifstopchange
\newif\ifdontchange
\newif\ifadditionalrunneeded
\newdimen\allowedspace

% args:
% #1: level
% #2: short contents, may be empty
% #3: normal contents
% #4: long contents, if empty, normal contents is used
\newcommand{\variant}[4]{%
    \ifnum#1>\c@MaxVariantLevel
        \global\c@MaxVariantLevel=#1\relax
    \fi
    % VariantLevel >= #1
    \@tempcnta#1\advance\@tempcnta\m@ne
    \ifnum\c@VariantLevel>\@tempcnta
        \ifcase\c@PrevContentsNeeded
            #2\relax
        \or
            #3\relax
        \or
            \def\@tempa{#4}\ifx\@tempa\@empty
                #3\relax
            \else
                #4\relax
            \fi
        \fi
    \else
        #3\relax
    \fi
}

\newcommand{\calc@remaining@pages}[1]{%
    \@tempcnta\c@page
    \advance\@tempcnta#1\relax
    \@tempcntb\@tempcnta
    \divide\@tempcnta4\relax
    \multiply\@tempcnta4\relax
    \advance\@tempcntb-\@tempcnta
    \ifnum\@tempcntb=0\else
        \advance\@tempcntb-4\fi
    \c@RemainingPages-\@tempcntb
}

\newcommand*{\checkremaining}{%
    \ifdim\pagetotal=\z@
        \calc@remaining@pages\m@one
        \RemainingOnPage\z@
    \else
        \calc@remaining@pages\z@
        \RemainingOnPage\pagegoal
        \advance\RemainingOnPage-\pagetotal
    \fi

    \@tempdima\c@RemainingPages\textheight
    \advance\@tempdima\RemainingOnPage
    \ifdim\@tempdima<\allowedspace
        \stopchangetrue
    \fi
    \advance\@tempdima-2\textheight
    \ifdim\@tempdima<\z@
        \setcounter{ContentsNeeded}{2}% more contents needed
    \else
        \ifdim\@tempdima=\z@
            \setcounter{ContentsNeeded}{1}% fits
        \else
            \setcounter{ContentsNeeded}{0}% less contents needed
        \fi
    \fi

    \ifdontchange\else    
    \ifstopchange\else
        \ifcase\c@ContentsNeeded
            \ifnum\c@PrevContentsNeeded<2\relax
                \stepcounter{VariantLevel}
                \additionalrunneededtrue
                \ifnum\c@VariantLevel>\c@MaxVariantLevel
                    \stopchangetrue
                    \additionalrunneededfalse
                \fi
            \else
                \stopchangetrue
                \additionalrunneededtrue
                \addtocounter{VariantLevel}{-1}%
                \setcounter{ContentsNeeded}{2}%
            \fi
        \or
            \stopchangetrue
            \additionalrunneededfalse
            \setcounter{ContentsNeeded}{\the\c@PrevContentsNeeded}%
        \or
            \ifnum\c@PrevContentsNeeded>\z@
                \stepcounter{VariantLevel}
                \additionalrunneededtrue
                \ifnum\c@VariantLevel>\c@MaxVariantLevel
                    \stopchangetrue
                    \additionalrunneededfalse
                \fi
            \else
                \stopchangetrue
                \additionalrunneededfalse
                \setcounter{ContentsNeeded}{0}%
            \fi
        \fi
    \fi\fi

    \iffirstrun
        \c@InitialContentsNeeded\c@ContentsNeeded
        \InitialRemainingOnPage\RemainingOnPage
        \c@InitialRemainingPages\c@RemainingPages
    \fi

    % statistics in log file
    \typeout{----------------------------------------}%
    \typeout{variants of contents}%
    \typeout{next variant level: \the\c@VariantLevel\space (max: \the\c@MaxVariantLevel)}%
    \typeout{initial run}%
    \typeout{remaining space: \the\InitialRemainingOnPage; remaining pages: \the\c@InitialRemainingPages}%
    \typeout{contents needed: \ifcase\c@InitialContentsNeeded less\or neither\or more\fi}%
    \typeout{previous run}%
    \typeout{remaining space: \the\PrevRemainingOnPage; remaining pages: \the\c@PrevRemainingPages}%
    \typeout{contents needed: \ifcase\c@PrevContentsNeeded less\or neither\or more\fi}%
    \typeout{current run}%
    \typeout{remaining space: \the\RemainingOnPage; remaining pages: \the\c@RemainingPages}%
    \typeout{contents needed: \ifcase\c@ContentsNeeded less\or neither\or more\fi}%
    % show if additional LaTeX run is needed
    \ifadditionalrunneeded
        \@latex@warning{please run LaTeX once more}%
    \else
        \@latex@warning{that's it, more can't be done}%
    \fi
    \typeout{----------------------------------------}%

    % write information needed to auxiliary file    
    \ifstopchange
        \protected@write\@auxout{}%
            {\string\@writefile{vrt}{\string\stopchangetrue}}%
    \else
        \protected@write\@auxout{}%
            {\string\@writefile{vrt}{\string\stopchangefalse}}%
    \fi
    \protected@write\@auxout{}%
        {\string\@writefile{vrt}{\string\firstrunfalse}}%
    \protected@write\@auxout{}%
        {\string\@writefile{vrt}{\string\setcounter{InitialContentsNeeded}{\the\c@InitialContentsNeeded}}}%
    \protected@write\@auxout{}%
        {\string\@writefile{vrt}{\string\InitialRemainingOnPage\the\InitialRemainingOnPage}}%
    \protected@write\@auxout{}%
        {\string\@writefile{vrt}{\string\setcounter{InitialRemainingPages}{\the\c@InitialRemainingPages}}}%
    \protected@write\@auxout{}%
        {\string\@writefile{vrt}{\string\setcounter{VariantLevel}{\the\c@VariantLevel}}}%
    \protected@write\@auxout{}%
        {\string\@writefile{vrt}{\string\setcounter{PrevContentsNeeded}{\the\c@ContentsNeeded}}}%
    \protected@write\@auxout{}%
        {\string\@writefile{vrt}{\string\PrevRemainingOnPage\the\RemainingOnPage}}%
    \protected@write\@auxout{}%
        {\string\@writefile{vrt}{\string\setcounter{PrevRemainingPages}{\the\c@RemainingPages}}}%
}

% read .vrt file
\AtBeginDocument{%
    \InputIfFileExists{\jobname.vrt}{}{}%
}

% check for remaining space and open .vrt file for LaTeX to write
\AtEndDocument{%
    \checkremaining
    \if@filesw\newwrite\tf@vrt
        \immediate\openout\tf@vrt\jobname.vrt\fi
}

% initialize
\setcounter{VariantLevel}{0}
\setcounter{MaxVariantLevel}{0}
\setcounter{ContentsNeeded}{1}
\setcounter{PrevContentsNeeded}{1}
\setcounter{InitialContentsNeeded}{1}
\RemainingOnPage\z@
\PrevRemainingOnPage\z@
\InitialRemainingOnPage\z@
\setcounter{RemainingPages}{0}
\setcounter{PrevRemainingPages}{0}
\setcounter{InitialRemainingPages}{0}
\firstruntrue
\stopchangefalse
\dontchangefalse
\allowedspace0pt

\makeatother

% uncomment until contents is completely added
%\dontchangetrue
% set to a length greater 0pt to allow for some empty space on the last page
%\allowedspace36pt


\begin{document}
\begin{multicols}{2}
\Blindtext

\variant{1}{}{{\color{blue}\blindtext}}{{\color{green}\Blindtext[2]}}
\end{multicols}

\begin{center}
\variant{2}{}{\includegraphics[height=3cm]{example-image-a.pdf}}{\includegraphics[height=6cm]{example-image-b.pdf}}
\end{center}

\begin{multicols}{2}
\Blindtext

\variant{3}{}{{\color{blue}\blindtext}}{{\color{green}\Blindtext[2]}}
\end{multicols}

\begin{center}
\variant{4}{}{\includegraphics[height=3cm]{example-image-a.pdf}}{\includegraphics[height=6cm]{example-image-b.pdf}}
\end{center}

\begin{multicols}{2}
\Blindtext

\variant{5}{}{{\color{blue}\blindtext}}{{\color{green}\Blindtext[2]}}
\end{multicols}

% uncomment this to test with too much contents
%\begin{center}
%\variant{6}{}{\includegraphics[height=3cm]{example-image-a.pdf}}{\includegraphics[height=6cm]{example-image-b.pdf}}
%\end{center}
%
%\begin{multicols}{2}
%\Blindtext
%
%\variant{7}{}{{\color{blue}\blindtext}}{{\color{green}\Blindtext[2]}}
%\end{multicols}

\end{document}

There is a lot, which could be improved, but this would be a lot of work. And without the booklet itself, it would also require a lot of guessing. But I think, it's at least a start.

You may also look into the savetrees package, which provides methods to squeeze more text on a page.

  • Thanks, I only thought about tweaking TeXes layout passes; but compiling multiple times will work as well. It'll probably take me some time to fully understand how the code actually works and tweak it to have multiple counters (some chapters/sections enforce a pagebreak, also causing empty pages), but it's a good point to start from. – phi1010 Sep 20 '17 at 4:39

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

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