From d9fe66749d8072f377522910c593df4705a9dc66 Mon Sep 17 00:00:00 2001 From: Karl Berry Date: Mon, 17 Jul 2006 22:56:22 +0000 Subject: bigfoot update (18jul06) git-svn-id: svn://tug.org/texlive/trunk@1872 c570f23f-e606-0410-a88d-b1316a301751 --- Master/texmf-dist/source/latex/bigfoot/bigfoot.dtx | 3292 ++++++++++++++++++++ 1 file changed, 3292 insertions(+) create mode 100644 Master/texmf-dist/source/latex/bigfoot/bigfoot.dtx (limited to 'Master/texmf-dist/source/latex/bigfoot/bigfoot.dtx') diff --git a/Master/texmf-dist/source/latex/bigfoot/bigfoot.dtx b/Master/texmf-dist/source/latex/bigfoot/bigfoot.dtx new file mode 100644 index 00000000000..feaddf19929 --- /dev/null +++ b/Master/texmf-dist/source/latex/bigfoot/bigfoot.dtx @@ -0,0 +1,3292 @@ +% \iffalse +%% +%% bigfoot is part of the bigfoot bundle for critical typesetting +%% Copyright 2004, 05, 06 David Kastrup +%% +% This program is free software; you can redistribute it and/or modify +% it under the terms of the GNU General Public License as published by +% the Free Software Foundation; either version 2 of the License, or +% (at your option) any later version. +% +% This program is distributed in the hope that it will be useful, +% but WITHOUT ANY WARRANTY; without even the implied warranty of +% MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +% GNU General Public License for more details. +% +% You should have received a copy of the GNU General Public License +% along with this program; if not, write to the Free Software +% Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +% \fi +% \CheckSum{4157} +% \GetFileInfo{bigfoot.sty} +% \title{The \texttt{bigfoot} package\\version \fileversion} +% \date{\filedate} +% \author{David Kastrup\thanks{dak@gnu.org}} +% \maketitle +% Purpose of this package is to provide a one-stop solution to almost +% all problems related to footnotes. You can use it as a drop-in +% replacement of the \texttt{manyfoot} package, but without many of +% its shortcomings, and quite a few features of its own. It uses the +% existing document class layouts for footnotes, so you can usually +% use it without having to worry about the looks. +% +% Features are: +% \begin{itemize} +% \item You can specify and use multiple footnote apparatus. +% Footnotes for an apparatus lower on the +% page\footnoteB{like this one} can be anchored in an +% apparatus\footnote{The plural of ``apparatus'' is actually +% ``apparatus''\footnoteB{Well, actually ``apparat\=us'' with a +% long ``u'', but that's just obvious in spoken Latin.}} +% that is higher on the page. +% \item The last footnote in each apparatus may be broken to the next +% page\footnote{This will probably be interesting for footnotes +% that contain stuff like math equations\footnoteB+{Like +% \begin{equation} +% \label{eq:1} +% \sum_{k=1}^\infty \frac{1}{k^2} = \frac{\pi^2}{6} +% \end{equation}} or lists\footnoteB+{Like +% \begin{itemize} +% \item This, or +% \item this. +% \end{itemize}}.} +% Any subordinate footnote anchors that get moved to the next page +% will take the corresponding footnote with them. +% \item The order of footnotes in an apparatus is `natural': it starts +% with any footnote that may have been broken from the next page, +% followed by footnotes from the current page in the order of the +% appearance of their footnote +% marks.\footnoteB{This footnote appears above notes on notes.} +% Where the order of appearance in the document differs from the +% order in the source code, you will usually want to use the +% \cmd{\MakeSorted} command from the |perpage| package to get the +% numbering fixed appropriately. +% \item Footnotes can be formatted in separate paragraphs, or be run +% into a single paragraph. The choice is made per footnote +% apparatus, but can be overridden for single +% footnotes.\footnote{Footnotes containing material like display +% math\footnoteB{We had this already, right?} or list +% environments\footnoteB{And this looks familiar, too.} have to be +% done in vertical mode.} +% \item If footnotes are run into one paragraph, a variety of criteria +% makes sure that this formatting is only chosen when it saves +% noticeable space and delivers visually attractive results. +% \item Parameters for footnote formatting can be specified globally, +% or separately for each footnote. +% \item The material in footnotes can contain |\verb|-like material +% without problems.\footnote{We wrote \verb+|\verb|-like+ above in +% the main text.\footnoteB{Well, this is not so impressive. But we +% wrote \verb/\verb+|\verb|-like+/ in the footnote then.}} +% \item You can use color in footnotes. If a footnote gets broken +% across pages, the color at the point of the break will get resumed +% on the next page. Actually, the whole color stack will get +% reinstated. +% \end{itemize} +% As an example of how simple the usage can be, here is the +% documentation driver for this document: +% \begin{macrocode} +%<*driver> +\documentclass{ltxdoc} +\usepackage{bigfoot} +\usepackage{tabularx} +\usepackage{hyperref} +% \end{macrocode} +% After loading the packages, we declare two footnote blocks. One is +% the default footnote block, another block is called~|B| and is +% numbered with letters. The letters start new on each page. Both +% footnote blocks default to in-paragraph footnotes. Since the +% block~|B| can get entries from both the main text as well as the +% default footnote block, the entries are not necessarily generated in +% page order. So we need to use a sorted counter to fix this (feel +% free to try what happens when using an unsorted counter). +% \begin{macrocode} +\DeclareNewFootnote[para]{default} +\DeclareNewFootnote[para]{B}[alph] +\MakeSortedPerPage{footnoteB} +% \end{macrocode} +% In addition, we add an alternate footnote sequence that can be +% interspersed with the normal footnotes by use of the +% \cmd{\footnote'} command which we effectively define here. +% \begin{macrocode} +\newcounter{footalt} +\def\thefootalt{\fnsymbol{footalt}} +\MakeSortedPerPage[2]{footalt} +\WithSuffix\def\footnotedefault'{\refstepcounter{footalt}% + \Footnotedefault{\thefootalt}} +% \end{macrocode} +% Actually, that already was all. We can now start the document. The +% following makes sure that we get the full documentation only by +% compiling the separate driver file: +% \begin{macrocode} +\begin{document} +\OnlyDescription +% \AlsoImplementation +\DocInput{bigfoot.dtx} +\end{document} +% +% \end{macrocode} +% In order to be useful without additional hassle, the normal footnote +% level will be called |default|. If no such style has been defined +% at the start of the document, it will get defined and used for +% ordinary footnotes, fixing quite a few problems of \LaTeX's own +% footnote placement algorithms. +% +% Apart from that, usage is very much like that of |manyfoot|, so for +% the customization possibilities of |bigfoot| with regards to +% multiple footnote blocks and rules between them, refer to +% |manyfoot|'s documentation. +% +% |bigfoot| contains a lot of bells and whistles for defining your +% footnote formats and can use different formats for different +% footnote blocks. Those expert options are not documented separately +% yet: look through the code sections to see them explained. +% \StopEventually{} +% \section{The implementation} +% \subsection{Startup code} +% We declare the package and several compatibility options supposed to +% make |bigfoot| a drop-in replacement for |manyfoot|. +% \begin{macrocode} +%<*style> +\NeedsTeXFormat{LaTeX2e} +\def\FN@next $#1: #2 #3${#2} +\ProvidesPackage{bigfoot}[\FN@next $Date: 2006/07/15 21:24:56 $ + \FN@next $Revision: 1.25 $ makes footnotes work]%} + +\DeclareOption{para}{\PackageInfo{bigfoot}{Compatibility option `para' + has no effect:^^J% + Spacing will be guessed from `\string\@makefntext' unless^^J% + `\string\@preparefnhtext' is redefined}} + +\DeclareOption{para*}{\PackageInfo{bigfoot}{Compatibility option + `para*':^^J% + Redefining `\string\@preparefnhtext'}% + \def\@preparefnhtext{\ifx\@thefnmark\@empty + \else\@makefnmark\nobreak\fi}} + +\DeclareOption{ruled}{\PassOptionsToPackage{ruled}{manyfoot}} +% \end{macrocode} +% The |verbose| option talks about changed labels at the end of a +% run. It is for debugging instable configurations that fail to +% converge after a number of \TeX\ runs. The output is probably +% obscure. +% \begin{macrocode} +\DeclareOption{verbose}{\AtBeginDocument{% + \def\@testdef #1#2#3{% + \def\reserved@a{#3}% + \expandafter \ifx \csname #1@#2\endcsname + \reserved@a + \else \@tempswatrue + \typeout{Changed label #1/#2: + \csname #1@#2\endcsname->#3}% + \fi}}} +% \end{macrocode} +% The |trace| option is only available if you used |docstrip| while +% explicitly requesting |trace| functionality. If you set the |trace| +% option, the next option specifies a bit map of trace bits. +% +% The following bits can be set in tracing: +% \begin{tabular}{rl} +% 1&break decisions\\ +% 2&horizontal box building\\ +% 4&allocation stuff\\ +% 8&output routine stuff\\ +% 16&retained and kept boxes +% \end{tabular} +% \begin{macrocode} +%\DeclareOption{trace}{% +%\DeclareOption*{\ftflags=\CurrentOption\relax +% \DeclareOption*{\OptionNotUsed}}% +% \AtEndOfPackage{\RequirePackage{trace}\relax +% \errorcontextlines\maxdimen +% \showboxdepth4 +% \showboxbreadth100 +% \tracingonline=\@ne}% +%} +% \end{macrocode} +% The |tracepage| option is followed by another option specifying the +% page to be traced. If you use it, tracing happens only on the +% specified page. Only a single page can be specified. +% \begin{macrocode} +%\def\FN@tracepage{\c@page} +%\DeclareOption{tracepage}{% +% \DeclareOption*{\edef\FN@tracepage{\CurrentOption}% +% \DeclareOption*{\OptionNotUsed}}} +%\newcount\ftflags +%\def\foottrace#1{\ifnum\numexpr(\ftflags+(#1))/(2*#1)*(2*#1)% +% =\numexpr(\ftflags+3*#1/2)/#1*#1\relax +% f\else t\fi\ifnum\FN@tracepage=\c@page t\else n\fi} +\ProcessOptions* +% \end{macrocode} +% |hyperref|'s footnote support will just cause trouble. So if +% |hyperref| was already loaded or is going to be loaded, we turn off +% its footnote support. If you think you know what you are doing, you +% can use \cmd{\hypersetup} to turn it on again before the start of +% the document. Unfortunately, it appears like \cmd{\hypersetup} +% refuses to be called more than once, so this actually does not work +% unless you load |hyperref| last. +% \begin{macrocode} +\ifx\hypersetup\@undefined + \PassOptionsToPackage{hyperfootnotes=false}{hyperref} +\else + \hypersetup{hyperfootnotes=false} +\fi +% \end{macrocode} +% We require the |etex| package because +% \begin{enumerate} +% \item We need the facilities of the \eTeX\ engine; and where they +% are not available, the error messages from not finding the |etex| +% package or from loading it into the wrong engine make much more +% sense than what would happen otherwise. +% \item We allocate quite a few registers, and the danger of running +% out of them is smaller when the extra registers of \eTeX\ are +% taken into account. +% \end{enumerate} +% We need the |manyfoot| package to build on. The |suffix| and +% |perpage| package are needed for some small stuff. +% \begin{macrocode} +\RequirePackage{etex} +\RequirePackage{manyfoot} +\RequirePackage{suffix} +\RequirePackage{perpage} +% \end{macrocode} +% \subsection{Fixes to the \texttt{manyfoot} package} +% While those fixes have been submitted once to the author of +% |manyfoot|, they have not made it into its distribution at the +% current point of time. In the interest of stability, it would +% probably be best just to incorporate the parts from |manyfoot| that +% get used by |bigfoot|. This has not yet been done. +% +% \begin{macro}{\MFL@reinsout} +% We need the appropriate splitting parameters set for the footnote +% again. \cmd{\MFL@realinsert} does that, but it has the disadvantage +% that it uses \cmd{\strutbox}, and that may be set to arbitrary +% values at the time the output routine is invoked. |manyfoot| +% already has this problem with minipages: the split sizes will be +% those of the font at the end of the minipage instead of those at the +% time the footnote body was set up. So we do this here, and see +% later for more info about how to do this right: +% \begin{macrocode} +\def\MFL@reinsout#1#2{\ifvoid#2\else + \ifnum\count\@currbox>\z@ + \advance\@pageht \ht#2% + \advance\@pageht \skip#2% + \advance\@pageht \dp#2% + \fi + \MFL@realinsert{#2}{\unvbox#2}% + \fi +} +% \end{macrocode} +% \end{macro} +% \begin{macro}{\MFL@reins} +% Actually, I don't get the purpose of the following line in the +% first place. But if we do need it for some reason, it is rather +% certain that we don't want this empty insert to float. Use +% \cmd{\MFL@realinsert}, or set the floatingpenalty the hard way. +% \begin{macrocode} +\def\MFL@reins#1#2{\ifvoid#2\else\insert#2{\floatingpenalty\@MM}\fi} +% \end{macrocode} +% \end{macro} +% \begin{macro}{\MFL@mpinsert} +% \begin{macro}{\MFL@minipage} +% The structure of the \cmd{\MFL@mpinsert} box is overly +% complicated, and it is a bad idea to unpack the boxes put into it +% too early: the \cmd{\lastbox} command is pretty inefficient when +% the list before it is long due to unpacking. So we just leave +% everything packed in its own boxes, and unpack only at the moment +% when we are reinserting. +% \begin{macrocode} +\long\def\MFL@mpinsert#1#2{% + \global\setbox#1\vbox{% + \unvbox#1% + \nointerlineskip + \vbox{#2}% + }% +} + +\def\MFL@minipage{% + \ifinner\else + \let\MFL@mpinsertsave\MFL@insert + \MFL@reinsert \let\MFL@insert\MFL@mpinsert + \fi +} +% \end{macrocode} +% \end{macro} +% \end{macro} +% \begin{macro}{\MFL@mpreinsert} +% When reinserting, we put all but the last insertion into one +% humongous blob. This is so that the last insertion can be split +% by \TeX's paragraph splitting routines. The footnote types that +% |bigfoot| supports will never get split by \TeX, anyhow, but it is +% conceivable that other extension packages for manyfoot work +% differently. There is one difference, though: we let a slave mark +% escape into the main vertical list. +% \begin{macrocode} +\def\MFL@mpreinsert#1#2{% + \ifvoid#2\else + \setbox\@tempboxa\vbox\bgroup\unvbox#2% + \global\setbox#2\lastbox + \setbox\z@\lastbox + \ifvoid\z@ + \egroup + \setbox\z@\box#2% + \else + \MFL@removevboxes \unvbox\z@ + \egroup + \setbox\z@\box#2% + \MFL@mpinsertsave#2{\unvbox\@tempboxa}% + \fi + \ifvoid\z@\else + \MFL@mpinsertsave#2{\unvbox\z@}% + \fi + \marks\FN@slave{\number\FN@id}% + \fi} +% \end{macrocode} +% \end{macro} +% \begin{macro}{\MFL@removevboxes} +% The trick is like with \cmd{\removehboxes} in the \TeX book's +% appendix~D. +% \begin{macrocode} +\def\MFL@removevboxes{{\setbox\z@\lastbox + \ifvbox\z@ \MFL@removevboxes \unvbox\z@\fi}} +% \end{macrocode} +% \end{macro} +% \begin{macro}{\NCC@makefnmark} +% This provides the command in case it is not present (some versions +% did not have it). +% \begin{macrocode} +\ifx\NCC@makemark\@undefined + \ifx\NCC@makefnmark\@undefined \else + \def\NCC@makemark{\NCC@makefnmark} + \fi +\fi +% \end{macrocode} +% \end{macro} +% +% While the above operations actually were fixes to |manyfoot|, now we +% actually patch it for our own purposes. When allocating a new +% footnote, we set its maximum dimension to \cmd{\maxdimen} (since no +% hard limit makes sense, given that we recalculate all respective +% sizes at output time) and allocate a cache box to go with it. We +% also add the insertion to the list of insertions in +% \cmd{\FN@nestlist}. +% \begin{macro}{\MFL@startplain} +% \begin{macro}{\MFL@startpara} +% \begin{macrocode} +\def\MFL@startplain#1{\global\dimen#1\maxdimen + \@cons\FN@nestlist{{}#1}% + \expandafter\expandafter\expandafter\newbox\FN@cache#1} + +\let\MFL@startpara\MFL@startplain +% \end{macrocode} +% \end{macro} +% \end{macro} +% \begin{macro}{\RestyleFootnote} +% This macro gets two arguments: a footnote \meta{type}, and the +% style to be used for it. It works by redefining the corresponding +% |Footnotetext|\meta{type} macro. +% \begin{macrocode} +\def\RestyleFootnote#1#2{\expandafter\xdef + \csname Footnotetext#1\endcsname{\expandafter + \noexpand\csname MFL@fnote#2\endcsname{\csname footins#1\endcsname}}} +% \end{macrocode} +% \end{macro} +% \begin{macro}{\FN@stripfootins} +% \begin{macro}{\FN@restylefootnote} +% We need the same kind of functionality for a footnote specified by +% its footnote insertion. So we strip the footnote \meta{type} from +% the insertion macro name. Kind of ugly. +% \begin{macrocode} +\expandafter\def\expandafter\FN@stripfootins\string\footins{} + +\def\FN@restylefootnote#1#2{{\edef\next{% + \noexpand\RestyleFootnote{\expandafter\FN@stripfootins + \string#1}{#2}}\next}} +% \end{macrocode} +% \end{macro} +% \end{macro} +% +% \subsection{Dealing with footnote-specific code} +% The formatting of footnotes is determined by macros such as +% \cmd{\@makefntext}. For several blocks of footnotes, we might want +% to have several different ways for formatting them. Whenever this +% is the case, we call them with +% \begin{quote} +% |\FN@specific|\marg{insert\#}\marg{macroname} +% \end{quote} +% This will use the default \meta{macroname} unless a special macro has +% been defined with something like +% \begin{quote} +% |\FootnoteSpecific\marg{type}|\def|\meta{macroname}\dots +% \end{quote} +% A number of other defining commands and constructs are available: +% those are pretty much like the ones for the \cmd{\WithSuffix} command +% implemented by the |suffix| package. +% \begin{macro}{\FN@specific} +% We use |\romannumeral| here just for the purpose of sustaining +% expansion. It expands to nothing when followed by |\z@| +% eventually. Thus expanding the expansion of \cmd{\FN@specific} +% again delivers the (unexpanded) final token to use. +% \begin{macrocode} +\def\FN@specific#1#2{\romannumeral + \ifcsname FN\string#2\number#1\endcsname + \expandafter + \z@\csname FN\string#2\number#1\expandafter\endcsname + \else\expandafter\z@ + \expandafter#2\fi} +% \end{macrocode} +% \end{macro} +% \begin{macro}{\FootnoteSpecific} +% \begin{macro}{\FN@specific@ii} +% This is all a bit muddy, but quite similar to what the |suffix| +% package does, so you might want to look there for the explanation. +% \begin{macrocode} +\def\FootnoteSpecific#1{\count@\csname footins#1\endcsname\toks@{}% + \FN@specific@ii} + +\long\def\FN@specific@ii#1#2{\toks@\expandafter{\the\toks@#1}% + \the\expandafter\toks@ + \csname FN\string#2\number\count@\endcsname} + +\WithSuffix\def\FN@specific@ii\long{\toks@\expandafter + {\the\toks@\long}\FN@specific@ii} + +\WithSuffix\def\FN@specific@ii\global{\toks@\expandafter + {\the\toks@\global}\FN@specific@ii} + +\WithSuffix\def\FN@specific@ii\expandafter{\expandafter + \FN@specific@ii\expandafter} +% \end{macrocode} +% \end{macro} +% \end{macro} +% +% \subsection{Putting footnotes into insertions} +% \subsubsection{Dealing with Ids} +% Since we have to store additional information for each footnote as +% long as it is not yet typeset, we allocate and deallocate numeric +% `id's on an as-needed base, since we do not want to store this +% sort of information indefinitely, with a large toll on hash space. +% So we work with indirect ids, where the unique ids are just +% referenced indirectly. We do this with `slots'. +% \begin{macro}{\FN@slotxdef} +% \begin{macro}{\FN@slotget} +% New slots are assigned values with \cmd{\FN@slotxdef}, which can +% be retrieved again with \cmd{\FN@slotget}. +% \begin{macrocode} +\def\FN@slotxdef#1{% + \global\expandafter\xdef\csname FN@slot#1\endcsname} + +\def\FN@slotget#1{\csname FN@slot#1\endcsname} +%\def\FN@slotget#1{% +% \expandafter\FN@slotgetii\expandafter +% \FN@slotfreelist\expandafter +% {\number\number#1}} +%\def\FN@slotgetii#1#2{% +% \ifx#1\@empty \csname FN@slot#2\endcsname\else +% \ifnum#1=#2 \errmessage{Use after freed: #1}\else +% \expandafter\FN@slotgetii +% \csname FN@slot#1\endcsname{#2}\fi\fi} +% \end{macrocode} +% \end{macro} +% \end{macro} +% \begin{macro}{\FN@slotfreelist} +% \begin{macro}{\FN@nextslot} +% \cmd{\FN@slotfreelist} point to the first already allocated +% available id to be reused. If it is empty, none exist. In that +% case, \cmd{FN@nextslot} contains the next slot number to use. +% \begin{macrocode} +\def\FN@slotfreelist{} +\def\FN@nextslot{1} +% \end{macrocode} +% \end{macro} +% \end{macro} +% \begin{macro}{\FN@newslot} +% This allocates a new slot by setting the given macro to a +% currently unused slot number in decimal form. If there is +% something left in the freelist, it is assigned, otherwise a new +% slot gets allocated. +% \begin{macrocode} +\def\FN@newslot#1{% + \ifx\FN@slotfreelist\@empty + \edef#1{\FN@nextslot}% + \xdef\FN@nextslot{\number\numexpr \FN@nextslot+\@ne}% + \else + \let#1\FN@slotfreelist + \xdef\FN@slotfreelist{\csname FN@slot\FN@slotfreelist\endcsname}% + \fi +% \if\foottrace4\message{^^JAllocated #1^^J}\fi +} +% \end{macrocode} +% \end{macro} +% \begin{macro}{\FN@freeslot} +% This frees a given slot (by number) again by adding it to the +% freelist. +% \begin{macrocode} +\def\FN@freeslot#1{% +% \if\foottrace4\message{^^JFreeing #1^^J}\fi + \global\expandafter\let\csname FN@slot#1\endcsname=\FN@slotfreelist + \xdef\FN@slotfreelist{#1}} +% \end{macrocode} +% \end{macro} +% +% \subsubsection{Dealing with footnote stacks} +% Footnote stacks are used for paired footnotes that refer to a text +% range instead of a single text point. For example, you can use +% something like +% \begin{verbatim} +% Text \var<{was there}is here\var> +% \end{verbatim} +% To have a text variant ``was there'' for the original passage ``is +% here'', and mark it, say, as ``\textsuperscript{a}is +% here\textsuperscript{\textsl{a}}'' by employing the |suffix| package +% suitably. This would anchor the footnote at the start of the +% passage. It would also be imaginable to implement the syntax +% \begin{verbatim} +% Text \var{was there} +% \end{verbatim} +% for anchoring it at the end of the given passage. +% \begin{macrocode} +\global\let\FN@stacklist\@empty +% \end{macrocode} +% \begin{macro}{\DefineFootnoteStack} +% This command is used for defining a footnote stack. It gets a +% single argument which is the name of the stack and should consist +% just of ordinary character tokens. +% \begin{macrocode} +\def\DefineFootnoteStack#1{% + \global\expandafter\let\csname FN@stack@#1\endcsname\@empty + \@cons\FN@stacklist{{#1}}% +} +% \end{macrocode} +% \end{macro} +% At the end of the document, all stacks are checked to make sure they +% have been used up completely. +% \begin{macrocode} +\AtEndDocument{\FN@checkstacklist} + +\def\FN@checkstacklist{{\let\@elt\FN@checkstack + \FN@stacklist}} + +\def\FN@checkstack#1{{\let\@elt\FN@checkstackentry + \csname FN@stack#1@\endcsname}} + +\def\FN@checkstackentry#1#2#3{% + \PackageError{bigfoot}{Unfinished #1 #2 from line #3}% + {The specified footnote range is uncomplete}} +% \end{macrocode} +% \begin{macro}{\PushFootnoteMark} +% This gets one argument, the name of the footnote stack. It pushes +% the current footnote mark name stored in \cmd{\@thefnmark} onto +% the footnote stack. +% \begin{macrocode} +\def\PushFootnoteMark#1{{\let\@elt\relax + \expandafter\unrestored@protected@xdef \csname FN@stack@#1\endcsname + {\@elt{#1}{\@thefnmark}{\number\inputlineno}\csname + FN@stack@#1\endcsname}}} +% \end{macrocode} +% \end{macro} +% \begin{macro}{\PopFootnoteMark} +% This gets one argument, the name of the footnote stack. It pops +% the value of \cmd{\@thefnmark} from the named footnote stack. +% \begin{macrocode} +\def\PopFootnoteMark#1{\expandafter + \ifx\csname FN@stack@#1\endcsname\@empty + \PackageError{bigfoot}{Empty footnote stack #1}% + {The specified footnote type has no uncompleted range}% + \else + {\let\@elt\FN@firstpop + \iffalse{\fi\csname FN@stack@#1\endcsname}}\fi} +% \end{macrocode} +% \end{macro} +% \begin{macrocode} +\def\FN@firstpop#1#2#3{\protected@xdef\@thefnmark{#2}% + \let\@elt\relax + \expandafter\protected@xdef\csname FN@stack@#1\endcsname{% + \iffalse}\fi} +% \end{macrocode} +% +% +%\subsubsection{The works} +% +% \begin{macro}{\FN@cache} +% Cacheboxes cache the typeset forms of the insertion boxes for a +% certain configuration of footnotes. +% \begin{macrocode} +\def\FN@cache#1{\csname FN@cache\number#1\endcsname} +% \end{macrocode} +% \end{macro} +% \cmd{\FN@sortlist} takes the current vertical list and sorts the +% contained boxes according to their width (which is supposed to +% contain the sort key). +% +% The algorithm is a pretty straightforward insertion sort with +% $O(n^2)$~steps. This is the best one can hope for without +% comparisons across non-adjacent list elements. For presorted lists, +% the performance will be $O(n)$, and that's what we expect to see for +% simple cases (and when there are no sortkeys yet). Any negative +% width will certainly hang the algorithm. +% +% It also happens that \TeX\ has a hardwired limit for grouping levels +% that hits at 255. Oops. We better not have a few hundred footnotes +% in a single block on one page\dots +% \begin{macrocode} +\def\FN@sortlist{{% + \setbox\z@\lastbox + \ifvoid\z@ \else \FN@sortlist\FN@sortlistii \fi}} + +\def\FN@sortlistii{% + \setbox\tw@\lastbox + \ifvoid\tw@\else + \ifdim\wd\tw@<\wd\z@ {\FN@sortlistii}% + \fi\nointerlineskip\box\tw@\fi\nointerlineskip\box\z@} +% \end{macrocode} +% \begin{macro}{\FN@sortinsert} +% This function is an \cmd{\@elt} function that will sort the given +% insertion if it is non-empty and if there is no cache box present +% (which would imply that the insertion had already been sorted +% previously). +% \begin{macrocode} +\def\FN@sortinsert#1#2{\ifvoid\FN@cache#2% + \ifvoid#2\else\global\setbox#2\vbox{\unvbox#2% + \FN@sortlist}\fi\fi} +% \end{macrocode} +% \end{macro} +% \begin{macro}{\FN@maybeinvalidatecache} +% This is called after pulling in additional material from the +% page. If the material added an insertion, the cache is junk and +% must be regenerated. +% \begin{macrocode} +\def\FN@maybeinvalidatecache#1#2{% + \ifvoid#2\else\global\setbox\FN@cache#2=\box\voidb@x\fi} +% \end{macrocode} +% \end{macro} +% \begin{macro}{\FN@regeneratecache} +% This unconditionally regenerates one cache box. The structure of +% a cache box is basically a list of vertical boxes. All but the +% last such box are packed into a single vertical box which is then +% followed by the last vertical box. +% \begin{macrocode} +\def\FN@regeneratecache#1#2{% + \global\setbox\FN@cache#2=% + \ifvoid#2% + \box\voidb@x + \else + \vbox{\vbox{\unvcopy#2% + \setbox\z@\lastbox + \def\FN@masterinsert{#2}% + \FN@assembleboxes + \global\setbox\FN@cache#2\box\z@}% + \nointerlineskip \box\FN@cache#2}% + \fi} +% \end{macrocode} +% \end{macro} +% \begin{macro}{\FN@mayberegeneratecache} +% This regenerates the cache in case the cache box has been voided +% in order to mark it as invalid. +% \begin{macrocode} +\def\FN@mayberegeneratecache#1#2{% + \ifvoid\FN@cache#2% + \FN@regeneratecache{}#2% + \fi} +% \end{macrocode} +% \end{macro} +% \begin{macro}{\FN@cachesize} +% This calculates the size impact of a cache box on the current page +% as a term to be added into a \cmd{\glueexpr}-type of expression. +% \begin{macrocode} +\def\FN@cachesize#1#2{% + \ifvoid\FN@cache#2% + \else + +\skip#2+(\ht\FN@cache#2+\dp\FN@cache#2)*\count#2/\@m + \fi} +% \end{macrocode} +% \end{macro} +% \begin{macro}{\FN@clearcache} +% This just completely voids a cache register. +% \begin{macrocode} +\def\FN@clearcache#1#2{% + \global\setbox\FN@cache#2=\box\voidb@x} +% \end{macrocode} +% \end{macro} +% +% \begin{macro}{\@makefnvtext} +% Ok, this is one of the parts putting together footnotes in para +% mode. The footnotes themselves have already been formatted into +% hboxes (placed there with \cmd{\@preparefnhtext} in order to cater +% for proper indentation). \cmd{\@makefnvtext} then formats a +% single footnote block from horizontal mode pieces (vertical mode +% pieces are kept as-is). +% This takes text and typesets it in a single block. To get correct +% indentation, it breaks before the first footnote and adjusts the +% clubpenalties to move them to one line lower effectively. +% \cmd{\@makefnvtext} is called in vertical mode, and its argument +% is typeset in horizontal mode right after a break, inside of +% \cmd{\@makefntext}. +% \begin{macrocode} +\def\@makefnvtext#1{% + \FN@specific\FN@masterinsert\@makefntext{% + \clubpenalties\tw@\@MM\clubpenalty + \vadjust{\nobreak\vskip-\baselineskip}\nobreak\hfill\break#1}} +% \end{macrocode} +% \end{macro} +% \begin{macro}{\@preparefnhtext} +% This creates appropriate skips to be put before the horizontal +% material to make the indentation correct with a breakpoint before +% the footnote as well as when in run-in text. This is run once at +% the start of each horizontal mode footnote when it is first being +% typeset, in horizontal mode. +% \begin{macrocode} +\ifx\@preparefnhtext\@undefined +\def\@preparefnhtext{{% + \setbox\z@\vbox{\FN@specific\FN@masterinsert\@makefntext{% + \unskip\unpenalty\setbox\z@\lastbox + \dimen@ + \ifnum\parshape>\z@ + \dimexpr\parshapeindent\tw@-\parshapeindent\@ne\relax + \else \ifnum\hangafter=\@ne\hangindent \else + \ifnum\hangafter=\m@ne -\hangindent + \else \z@ \fi\fi\fi + \dimen@ii\dimen@ + \ifhbox\z@ \advance\dimen@-\wd\z@ + \setbox\z@\hbox{\unhbox\z@}% + \advance\dimen@\wd\z@ + \fi + \xdef\FN@tempinfo{\hskip\the\dimen@ + \vadjust{}\nobreak\hskip-\the\dimen@ii\relax}}}}% + \FN@tempinfo} +\fi +% \end{macrocode} +% Now we have in |\FN@tempinfo| the excess width of the label we don't +% want to preserve when doing in-paragraph footnote setting. A +% sequence of glue before a label now has to consist of stuff that +% vanishes at a breakpoint, followed by stuff that remains. We have +% to have two behaviors for the contents: behavior one is +% justification at the start of a line, behavior two is justification +% in the line. When we are at the start of the line, preceding +% interword space disappears swallowed and so the natural criterion +% for distinguishing those cases is this initial line break. This +% means that we can't avoid articifially adding a line break at the +% start of such a box. We will back up its height again. Some +% packages specify a |\hangindent| (I~know of no examples where they +% would actually set |\hangafter| to a value different from its +% default of~1, or set |\hangindent| to a negative value which would +% affect the right margin): due to our artifical line at the top, the +% indent will actually be active for the first line already. We back +% it out of the actual labels happening at the start of the line. +% Two-line parshapes have the same effect: the first line is not +% actually used, and we put the relevant info for the first line into +% the label. Different right indentation for the first line is +% something we can't simulate, but again, it should occur rarely. +% When |\parshape| is active, |\hangindent| is ignored. +% \end{macro} +% \begin{macrocode} +\def\@makefnstartbox{% + \reset@font\footnotesize + \hsize\MFL@columnwidth \@parboxrestore + \interlinepenalty\FN@specific\FN@masterinsert\interfootnotelinepenalty + \widowpenalty\FN@specific\FN@masterinsert\footnotewidowpenalty + \clubpenalty\FN@specific\FN@masterinsert\footnoteclubpenalty + \advance\linepenalty500\relax} + +\def\@makefnendbox{% + \widowpenalty\FN@specific\FN@masterinsert\finalfootnotewidowpenalty} + +\newcount\footnotewidowpenalty +\footnotewidowpenalty=250 +\newcount\footnoteclubpenalty +\footnoteclubpenalty=250 +\newcount\finalfootnotewidowpenalty +\finalfootnotewidowpenalty=4000 +% \end{macrocode} +% \begin{macro}{\@makefnvbox} +% This is the formatting code for a vertical mode footnote box. It +% uses \cmd{\@makefnstartbox} for setting up the initial widow/club +% penalties, and \cmd{\@makefnendbox} for preparing the final end. It +% results in a vbox, but its argument, a stream of horizontal and +% vertical material, is typeset in +% \begin{macrocode} +\ifx\@makefnvbox\@undefined + \def\@makefnvbox#1{\vbox{% + \@makefnstartbox + \clubpenalties\tw@\@MM\clubpenalty + \let\@thefnmark\@empty + \FN@specific\FN@masterinsert\@makefntext{\rule\z@\footnotesep + \nobreak + #1\@finalstrut\strutbox + \@makefnendbox}}} +\fi +% \end{macrocode} +% \end{macro} +% \begin{macro}{\hfootfraction} +% \begin{macro}{\vtypefraction} +% Those parameters govern when a footnote block is going to be set +% completely in vertical mode. If a footnote block does not shrink +% to less than \cmd{\hfootfraction} its size when using in-paragraph +% notes and, has at least \cmd{\vtypefraction} of forcedly vertical +% footnotes (specified as purely vertical, or vertical because of +% being large), it is set entirely in vertical mode. +% +% In addition, the ratio of purely vertical footnotes must not fall +% below \cmd{\vtypefraction}. +% \begin{macrocode} +\def\hfootfraction{0.9} +\def\vtypefraction{0.6} +% \end{macrocode} +% \end{macro} +% \end{macro} +% \begin{macro}{\FN@assembleboxes} +% This will produce the finished product, by generating all boxes +% and concatenating them except for the last vbox. It is assumed +% that have already set |\box\z@| to |\lastbox| before calling this +% routine (or, more likely, have already assembled and split the +% last box). The last, not yet unpacked |\vbox| is left in +% |\box\z@| on return. The original id of the last box of a block +% is properly transferred to it. +% +% The last box might have come about by joining several horizontal +% boxes, so splitting it might separate footnotes. We deal with +% that problem at a different point of time by checking the +% respective Ids when breaking a vbox into pieces: if the split +% piece does not contain the last footnote beginning, we switch to a +% slow motion decomposal. \cmd{\FN@assembleboxes} is supposed to be +% entered and exited in vertical mode. +% \begin{macrocode} +\def\FN@assembleboxes{% +% \ifhmode \PackageError{bigfoot}{Unexpected hmode}{}\fi + \ifhbox\z@ + \dimen@\dp\z@ +% \MFL@checksinglebox\z@\z@{}{}% + \dimen@ii\z@ + \setbox\tw@\box\voidb@x + \loop \advance\dimen@ii\dimexpr\ht\z@+\dp\z@\relax + \setbox\tw@\hbox{\box\z@\unhbox\tw@}% + \setbox\z@\lastbox + \ifhbox\z@ + \repeat + {\FN@assembleboxes\nointerlineskip\unvbox\z@}% +% \end{macrocode} +% At this point of time, |\box\tw@| contains a plain hbox with nothing +% but the unadorned hboxes in horizontal mode to be joined into one +% footnote block. All preceding footnote blocks have been emptied +% into the current vertical list. We put the |\unvbox| operations in +% a group so that the paragraph shapes will not get reset over the +% break. +% \begin{macrocode} + \global\setbox\FN@tempbox\copy\tw@ + \setbox\z@\@makefnvbox{% + {\unhbox\FN@tempbox}% + \setbox\z@\lastbox\FN@joinhboxes}% + \ifdim\FN@vfound>\dimexpr\vtypefraction\p@*\FN@found\relax + \ifdim\dimexpr \ht\z@+\dp\z@>\hfootfraction\dimen@ii + \global\setbox\FN@tempbox\box\tw@ + \setbox\z@\@makefnvbox{\let\@makefnbreak\FN@pseudofillbreak + {\unhbox\FN@tempbox}\setbox\z@\lastbox\FN@joinhboxes}% + \fi + \fi + \setbox\tw@\box\voidb@x + \ht\z@\dimexpr \ht\z@+\dp\z@-\dimen@\relax + \dp\z@\dimen@ +% \MFL@checksinglebox\z@\z@{}{}% + \else + \ifvbox\z@ +% \MFL@checksinglebox\z@\z@{}{}% + {\setbox\z@\lastbox + \FN@assembleboxes\nointerlineskip\unvbox\z@}% + \fi + \fi} +% \end{macrocode} +% \end{macro} +% Ok, now follow a lot of fuzzy calculation routines. When we are +% considering truth values, |\p@| (1pt) corresponds to a value of +% ``true'', and |\z@| corresponds to ``false''. +% \begin{macro}{\FN@fuzzyeval} +% This calculates a ratio, something with which you multiply. The +% first two arguments of the function define an interval, and the +% third argument is a value in that interval. If |#3| is equal to +% |#1|, the resulting ratio is~0, if the |#3| is equal to |#2|, the +% resulting ratio is~1. Values in between are linearly +% interpolated. Values outside of the interval are mapped to 0 +% and~1. If all three values are equal (hardly useful), 1 is +% returned. +% \begin{macrocode} +\def\FN@fuzzyeval#1#2#3{% + \ifdim\dimexpr(#3)<\dimexpr(#2)\relax + \ifdim\dimexpr(#3)>\dimexpr(#1)\relax + *(\dimexpr(#3)-(#1))% + /(\dimexpr(#2)-(#1))% + \else *\z@ + \fi + \fi} +% \end{macrocode} +% \end{macro} +% \begin{macro}{\FN@fuzzyor} +% This returns probabilistic \textsc{or}: +% \[(|#1|+|#2|-|#1|\cdot|#2|)\] +% \begin{macrocode} +\def\FN@fuzzyor#1#2{(\p@-(\p@-(#1))*(\dimexpr\p@-(#2))/\p@)} +% \end{macrocode} +% \end{macro} +% \begin{macro}{\FN@magicclue} +% Ok, so here is the magic glue calculator. |#1| and~|#2| give the +% range over which the preceding line changes from `short' to `long'. +% |#3| and |#4| give the range over which the current line changes +% from `short' to `long'. Both are combined with a probabilistic or +% function, and then a penalty is chosen which ranges from |#5| +% to~|#6| for short to long. +% \begin{macrocode} +\def\FN@magicglue#1#2#3#4#5#6{% +% \if\foottrace2\traceon\fi + \dimen@\dimexpr\p@\FN@fuzzyeval{#1}{#2}\FN@lasthsize\relax + \dimen@ii\dimexpr\p@\FN@fuzzyeval{#3}{#4}{\ht\z@+\dp\z@}\relax + \dimen@\dimexpr\FN@fuzzyor\dimen@\dimen@ii + \count@\numexpr((#6)-(#5))*\dimen@/\p@+(#5)\relax + \xdef\FN@vfound{\the\dimexpr\FN@vfound+\dimen@}% + \ifnum\count@>-\@M + \penalty\count@ + \hskip\glueexpr -\parfillskip+1em minus 0.5em\relax + \else + \FN@pseudobreak + \fi + \xdef\FN@found{\number\numexpr\FN@found+\@ne}% +} +% \end{macrocode} +% \end{macro} +% \begin{macro}{\FN@pseudobreak} +% This ends a line, but without introducing parskip and similar. It +% also `breaks in' the next line to get proper indentation. The +% main difference with regard to \cmd{\break} is that this restarts +% the reckoning of line numbers for the sake of \cmd{\clubpenalty} +% calculation. +% \begin{macrocode} +\def\FN@pseudobreak{% + {\parskip\z@skip\parfillskip\z@skip\parindent\z@\vadjust{}\par\noindent + \vadjust{\nobreak\vskip-\baselineskip}\nobreak\hfill\break}} +% \end{macrocode} +% \end{macro} +% \begin{macro}{\FN@pseudofillbreak} +% This is basically just for separating paragraphs by force. +% \begin{macrocode} +\def\FN@pseudofillbreak{\nobreak\hskip\parfillskip\FN@pseudobreak} +% \end{macrocode} +% +% \end{macro} +% \begin{macro}{\@makefnbreak} +% This calculates the glue for the standard horizontal footnotes. +% \begin{macrocode} +\def\@makefnbreak{\FN@magicglue {\footnotesep+\dp\strutbox}% + {\footnotesep+\dp\strutbox+\baselineskip}% + {\footnotesep+\dp\strutbox+0.5\baselineskip}% + {\footnotesep+\dp\strutbox+2\baselineskip}{-200}{-12000}} +% \end{macrocode} +% \end{macro} +% \cmd{\FN@joinhboxes} is called with box~0 set to the next box to be +% appended to the current list (all preceding hboxes on the current +% vertical list will have to go in front). \cmd{\FN@joinhboxes} is +% entered in vertical mode, and will be exited in horizontal mode. +% \begin{macrocode} +\def\FN@joinhboxes{% +% \ifvmode \errmessage{Unexpected vertical mode.}\fi + \begingroup\setbox\z@\lastbox + \ifhbox\z@ \FN@joinhboxes +% \ifvmode \errmessage{Unexpected vertical mode.}\fi + \endgroup + \nobreak\hskip\parfillskip + \@makefnbreak + \else +% \ifvbox\z@ \errmessage{Unexpected vbox.}\fi + \endgroup + \vadjust{\nobreak\vskip-\baselineskip}\nobreak\hfill\break + \xdef\FN@vfound{\z@}% + \xdef\FN@found{\z@}% + \fi + \xdef\FN@lasthsize{\the\dimexpr \ht\z@ +\dp\z@}% + \unhbox\z@} +% \end{macrocode} +% \begin{macro}{\FN@par} +% \begin{macro}{\FN@noindent} +% \begin{macro}{\FN@indent} +% In-paragraph footnotes are collected in horizontal mode. So +% |\par|, \cmd{\noindent} and \cmd{\indent} simply don't work. We +% replace them with something having the same effect when the boxes +% get unhboxed. Note that this does not admit the tracking of +% club/widow penalties: in a later version, it should get replaced +% by something that actually allows for separate paragraphs. One +% possibility would be to replace the current single hbox for an +% in-paragraph footnote by an hbox of hboxes and unbox all of them +% in separate paragraphs. But that glosses over the fact that a +% multi-paragraph footnote does not make sense in anything but +% vertical mode. So a saner way would probably be to close off the +% hbox altogether and reinsert it into a vbox, restarting the whole +% footnote in vertical mode. Both of those approaches would require +% that no groups have been opened since the start of the footnote by +% the time |\par| gets called. The below pseudosolution at least +% has the advantage of not depending on the grouping structure at +% all. +% \begin{macrocode} +\def\FN@par{\unskip\nobreak\hskip\parfillskip + \vadjust{\vskip\parskip}\break\null\kern\parindent\ignorespaces} +\def\FN@noindent{\unkern} +\def\FN@indent{\unkern{\setbox\z@\null\wd\z@\parindent\box\z@}} +% \end{macrocode} +% \end{macro} +% \end{macro} +% \end{macro} +% \begin{macro}{\MFL@fnoteplain} +% \begin{macro}{\MFL@fnotepara} +% We redefine |manyfoot|'s basic footnote calls to use our own, +% versatile variant. +% \begin{macrocode} +\def\MFL@fnoteplain{\FN@fnotenested{plain}} +\def\MFL@fnotepara{\FN@fnotenested{para}} +% \end{macrocode} +% \end{macro} +% \end{macro} +% \begin{macro}{\FN@fnotenested} +% This is somewhat contorted: we want |\footnote+| to be in |plain| +% style and |\footnote-| in |para| style regardless of the current +% footnote style. Adding a second |+| or |-| after the first will +% actually restyle all footnotes coming afterwards appropriately. +% This should work for all footnote commands getting footnote text. +% \begin{macrocode} +\def\FN@fnotenested#1#2#3{% + \edef\reserved@d{#1}% + \FN@checkvariant{\edef\reserved@d}{% + \FN@checkvariant{\FN@restylefootnote{#2}}% + {\csname FN@fnote\reserved@d\endcsname{#2}{#3}}}} + +\def\FN@checkvariant#1#2{\def\reserved@a{#1}% + \def\reserved@b{#2}% + \futurelet\reserved@c\FN@checkvariantii} + +\def\FN@checkvariantii{% + \ifx\reserved@c+% + \reserved@a{plain}\expandafter\@firstoftwo + \else\ifx\reserved@c-% + \reserved@a{para}\expandafter\expandafter\expandafter + \@firstoftwo + \fi\fi + \reserved@b} +% \end{macrocode} +% \end{macro} +% In order to be able to sort footnotes according to the order of +% their reference points, we use a sorted counter. +% \begin{macrocode} +\newcounter{FN@totalid} +\MakeSorted{FN@totalid} +% \end{macrocode} +% \begin{macro}{\FN@fnoteplain} +% \begin{macro}{\FN@fnotepara} +% The actual commands are easy enough: +% \begin{macrocode} +\def\FN@fnoteplain{\FN@fnotecommon\vbox} +\def\FN@fnotepara{\FN@fnotecommon\hbox} +% \end{macrocode} +% \end{macro} +% \end{macro} +% \begin{macro}{\FN@masterinsert} +% This contains the insert number of the insert where the footnote +% mark appears. If it appears in the main text, 255~will be used. +% \begin{macrocode} +\def\FN@masterinsert{\@cclv} +% \end{macrocode} +% \end{macro} +% \begin{macro}{\FN@id} +% \begin{macro}{\FN@master} +% \begin{macro}{\FN@slave} +% Here is the deal with master and slave ids: each footnote has a +% unique master id. This master id is larger by one than the last +% id of its subordinate footnotes. It is recorded in the mark +% \cmd{\FN@master} in the footnote box at the start itself, although +% with an indirection through the \cmd{\FN@newslot} mechanism since +% the actual id can only become known after all subfootnotes have +% been typeset. The same id is recorded in \cmd{\FN@slave} at the +% ultimate end of the footnote. +% +% At the point where a \cmd{\FN@master} mark is placed, a default +% \cmd{\FN@slave} mark is placed also with an id that is one less +% than the smallest id generated from a footnote that is a +% `descendent' of the current one. This makes it possible to +% distinguish any split off subordinate footnotes. It must be noted +% that this sentinel slave id will be the valid id of a completely +% unrelated footnote! Since the value is only used for determining +% one end of an \emph{open} interval of excluded ids, this is no +% problem. All subordinate footnotes are numbered sequentially in +% the order of \emph{completion}, so that any subordinate footnotes +% have lower ids than their master. +% \begin{macrocode} +\newcount\FN@id +\FN@id\@ne +\newmarks\FN@master +\newmarks\FN@slave +% \end{macrocode} +% \end{macro} +% \end{macro} +% \end{macro} +% \begin{macro}{\FN@errorstack} +% This records the history of nested footnotes in order to deliver +% more useful error messages. +% \begin{macrocode} +\let\FN@errorstack\@empty +% \end{macrocode} +% \end{macro} +% \begin{macro}{\FN@fnotecommon} +% Well, this is the work horse if the footnote macro. Really bad +% thing. We start off by stepping our absolute counter and making a +% mark. \cmd{\leavevmode} is required so that the action of +% |perpage.sty| is done smoothly. +% \begin{macrocode} +\def\FN@fnotecommon#1#2#3{% + \leavevmode + \stepcounter{FN@totalid}% + \NCC@makemark{#3}% +% \end{macrocode} +% It is an error if the footnote insert number of the current +% footnote does not correspond to a block below the current +% insertion level. +% \begin{macrocode} + \ifnum#2<\FN@masterinsert + \FN@colorstackbgroup\MFL@minipage + \FN@newslot\FN@masterslot + \count@\FN@id +% \end{macrocode} +% \cmd{\dimen@} is here set to a sorting criterion. This is +% designed to make the conversion of footnote blocks as reliable as +% possible. If we could guarantee convergence, just using +% \cmd{\c@FN@totalid} would be sufficient for sorting. It turns out +% that this is too sensitive to footnotes of different blocks +% changing pages, so the number of the superior footnote block is +% allowed to take precedence by multiplying it with 4194304 which is +% unlikely to get exceeded by \cmd{\c@FN@totalid}. +% \begin{macrocode} + \dimen@=\dimexpr64\p@*\FN@masterinsert-\c@FN@totalid sp\relax + \def\FN@masterinsert{#2}% + \edef\FN@errorstack{\FN@errorstack^^J% +\FN@masterinsert\space entered in line \number\inputlineno}% + \let\FN@boxtype=#1% + \setbox\z@#1\bgroup +% \end{macrocode} +% The following is for the likes of PDF\TeX\ which has its own idea +% about how to restore a color stack. +% \begin{macrocode} + \let\current@color\default@color + \FN@@color@begingroup + \let\MFL@minipage\relax + \let\MFL@endminipage\relax + \@makefnstartbox +% \end{macrocode} +% We reset the list parameters in footnotes. Strictly speaking, +% this is interfering with \LaTeX's standard operation, but the +% standard operation does not make sense. +% \begin{macrocode} + \let\@listdepth\@mplistdepth \@mplistdepth\z@ + \@itemdepth\z@ \@enumdepth\z@ + \protected@edef\@currentlabel{\csname p@footnote% + \expandafter\FN@stripfootins\string#2\endcsname\@thefnmark}% + \ifx\FN@boxtype\vbox \normalcolor\nobreak + \else \FN@specific{#2}\@preparefnhtext \normalcolor + \fi +% \end{macrocode} +% After preparation, we now do the big bad trick for making +% footnotes cooperate with \cmd{\verb} and other catcode changing +% things: we call \cmd{\@makefntext} with an argument of +% |\iffalse|. This kills off its expansion right at the point +% where it would choose to place its argument. +% \begin{macrocode} + \FN@specific{#2}\@makefntext + \iffalse\fi +% \end{macrocode} +% Record the footnote specific dimensions. It is assumed that they +% don't change in the document, at least not before the footnote +% gets actually placed. +% \begin{macrocode} + \expandafter\xdef\csname FN@ht\number#2\endcsname{\the\footnotesep}% + \expandafter\xdef\csname FN@dp\number#2\endcsname{\the\dp\strutbox}% +% \end{macrocode} +% The footnote gets markers for identifying it and its starting block. +% \begin{macrocode} + \marks\FN@master{\FN@masterslot}% + \marks\FN@slave{\number\FN@id}% + \nobreak + \bgroup + \aftergroup\FN@commonending +% \end{macrocode} +% \cmd{\FN@commonending} will intervene before any tokens that are +% shifted in due to switching back the color stack. Those will only +% be executed once we completely relinquish control. +% \begin{macrocode} + \ifx\FN@boxtype\vbox + \rule\z@\footnotesep + \else + \ifx\FN@par\par\else + \let\FN@@par\par + \let\FN@@noindent\noindent + \let\FN@@indent\indent + \fi + \everyvbox\expandafter{\expandafter\everyvbox + \expandafter{\the\everyvbox}% + \let\par\FN@@par + \let\noindent\FN@@noindent + \let\indent\FN@@indent + \the\everyvbox}% + \let\par\FN@par + \let\noindent\FN@noindent + \let\indent\FN@indent + \fi + \afterassignment\ignorespaces + \expandafter\FN@fnotecommonii + \else +% \end{macrocode} +% We still needed to cater for the error of badly anchored footnotes: +% \begin{macrocode} + \PackageError{bigfoot}{#2 forbidden in \FN@masterinsert.}% + {Higher-placed footnotes can't be anchored in inferior ones.^^J% + I am not putting this text in a footnote. History:% +\FN@errorstack}% + \rule{1em}{\ht\strutbox}% +\fi} +% \end{macrocode} +% \end{macro} +% \begin{macro}{\FN@fnotecommonii} +% This just swallows the opening brace of the footnote text and then +% lets the footnote text progress. The closing group will then +% trigger the processing via \cmd{\aftergroup}. +% \begin{macrocode} +\def\FN@fnotecommonii#{\let\next} +% \end{macrocode} +% \end{macro} +% % Ok, color handling is a nuisance, to say the least. Split +% footnotes need to close their color stack on the old page, and reopen +% it on the new one. So we record the color stack state at each time +% it changes in a marks register. +% \begin{macrocode} +\newmarks\FN@color +\def\FN@colorstackbgroup{\let\FN@savecolorstack\FN@colorstack + \global\let\FN@colorstack\@empty + \bgroup + \ifdefined\FN@savecolorstack\else + \let\FN@@set@color\set@color + \let\FN@@reset@color\reset@color + \let\FN@@color@begingroup\color@begingroup + \fi + \let\set@color\FN@set@color + \let\reset@color\FN@reset@color + \let\color@begingroup\FN@color@begingroup} + +\def\FN@colorstackegroup{\egroup\ifx\FN@colorstack\@empty + \global\let\FN@colorstack\FN@savecolorstack \fi} + +\def\FN@colorstackfinish{\def\@elt##1##2{\FN@@reset@color##2}% + \FN@colorstack + \def\@elt##1##2{\noexpand\@elt{}{##2}}% + \xdef\FN@colorstack{\FN@colorstack}% + \let\@elt\relax + \marks\FN@color{}} + +\def\FN@reset@color{% + \bgroup\def\@elt##1##2{\def\FN@next{##1}{\gdef\FN@colorstack{##2}}}% + \let\FN@next\@empty + \FN@colorstack + \ifx\FN@next\@empty + \FN@colorstackegroup + \else \egroup + \FN@@reset@color + \marks\FN@color{\FN@colorstack}% + \fi} + +\def\FN@color@begingroup{% + \let\reset@color\FN@@reset@color + \let\color@begingroup\FN@@color@begingroup + \let\set@color\FN@@set@color + \color@begingroup} + +\def\FN@set@color{\FN@@set@color + \xdef\FN@colorstack{\@elt{\current@color}{\FN@colorstack}}% + \marks\FN@color{\FN@colorstack}} + +\def\FN@coloraftersplit#1{% + \def\@elt##1##2{##2\def\current@color{##1}\set@color}% + #1% + \let\@elt\relax} +% \end{macrocode} +% \begin{macro}{\FN@commonending} +% We'll eventually arrive here at the end of the footnote. Now we +% again call \cmd{\@makefntext}, but this time pass it |\fi| as +% its argument, and place |\iffalse| before its expansion. This +% cuts away the start of the macro. If this start changes the tail +% of the macro when executed, the whole trickery will not work. It +% turns out that a large sampling of document classes (including the +% standard ones) happens to work. +% \begin{macrocode} +\def\FN@commonending{% + \expandafter\expandafter\expandafter + \expandafter\expandafter\expandafter\expandafter + \iffalse \FN@specific\FN@masterinsert\@makefntext\fi + \@makefnendbox + \ifx\FN@boxtype\vbox\@finalstrut\strutbox \else \unskip \fi + \FN@colorstackfinish + \color@endgroup + \egroup + \global\advance\FN@id\@ne + \FN@slotxdef\FN@masterslot{\number\FN@id}% +% \end{macrocode} +% Now we want to get an upper estimate of the size. In case of a +% horizontal box, we do this by creating a vertical box of it all +% alone, and measuring that. Measuring the hbox itself is plain +% out: \TeX's maximal dimension of something like 5\,m is already +% busted with about two pages of material. We put the master slot +% identification into the depth of the box, and arrange for the +% total of depth and height of the box to still give the total depth +% and height of its size on the page. +% \begin{macrocode} + \ifhbox\z@ + \global\setbox\FN@tempbox\copy\z@ + \setbox\tw@\@makefnvbox{\unhbox\FN@tempbox}% + \ht\z@\dimexpr\ht\tw@+\dp\tw@-\FN@masterslot sp\relax + \else + \ht\z@\dimexpr\ht\z@+\dp\z@-\FN@masterslot sp\relax + \fi +% \end{macrocode} +% Now we put the sorting criterion into the width of the box, and +% then put the masterslot id into the depth. +% \begin{macrocode} + \wd\z@\dimen@ + \dp\z@\FN@masterslot sp\relax +% \ifnum\z@<0\FN@slotget{\FN@masterslot} % +% \else \errmessage{Inconsistent +% \string\FN@masterslot=\FN@masterslot}\fi +% \end{macrocode} +% Now we just need to place the stuff into an insertion and record +% the possibly changed slave id in order to know what subordinate +% footnotes belong to this one. +% \begin{macrocode} + \MFL@insert\FN@masterinsert{\nointerlineskip\box\z@}% + \marks\FN@slave{\number\FN@id}% + \MFL@endminipage + \FN@colorstackegroup +} +% \end{macrocode} +% \end{macro} +% A lot of stuff follows. This should really be cleaned up and +% documented. +% \begin{macrocode} +\dimen\footins\maxdimen +\gdef\FN@nestlist{} + +\newdimen\FN@outervsize +\newskip\FN@vsize + +\newbox\FN@insertions + +%\def\MFL@showone#1#2{\message{Box #2:}\showbox#2% +% \MFL@checkconsistency{#2}% +% \message{Cachebox #2:}\showbox\FN@cache#2} +% +%\def\MFL@checkconsistency#1{{% +% \setbox\z@\vbox{\unvcopy#1% +% \MFL@checkconsistencyi{#1}}}} +% \end{macrocode} +% \begin{macro}{\MFL@checksinglebox} +% Check box~|#1| for consistency. If it is bad, output box~|#2|. +% Execute |#3| if it was good, |#4| if it was bad. +% \begin{macrocode} +%\def\MFL@checksinglebox#1#2#3#4{% +% \ifvoid#1\else +% \ifnum\z@<0\FN@slotget{\number\dp#1} % +% #3% +% \else \errmessage{Inconsistent box #2}% +% \showboxdepth4\showboxbreadth100 +% \showbox#2\relax +% #4% +% \fi\fi} +% \end{macrocode} +% \end{macro} +% \begin{macrocode} +%\def\MFL@checkconsistencyi#1{% +% \unpenalty\unskip\unkern +% \setbox\z@\lastbox +% \MFL@checksinglebox\z@{#1}{{\MFL@checkconsistencyi{#1}}}{}} +% +%\def\MFL@showall{{% +% \showboxbreadth=\maxdimen +% \showboxdepth=4 +% \tracingonline=\@ne +% \FN@nest@iterate\MFL@showone}} +% \end{macrocode} +% \begin{macro}{\FN@retaindelayed} +% This is a complex macro that removes all boxes from the current +% list that are not to be kept for the next page. It works on the +% material from the original insertions, not the cache boxes. +% The slot specified by \cmd{\count@} is not freed when encountered, +% all others are freed upon removing the box. The last box is +% returned in box~0 if any is retained. The vertical list might +% have an unchecked part locked off in front by placing a +% \cmd{\nobreak} penalty there. This penalty is removed, and the +% list before it not touched. +% \begin{macrocode} +\def\FN@retaindelayed{% + \setbox\z@\lastbox + \ifcase + \ifvoid\z@\m@ne\fi \FN@config\z@ +% \if\foottrace8\message{^^J\string\FN@retaindelayed: +% dropping box \FN@slotget{\number\dp\z@}}\fi +% \if\foottrace{16}{\showboxdepth4 \showboxbreadth400 +% \tracingonline=\@ne\showbox\z@}\fi + \ifnum\dp\z@=\count@\else \FN@freeslot{\number\dp\z@}\fi +% \ifnum\dp\z@<\@ne \errmessage{Unidentified box}\fi + \FN@retaindelayed + \or +% \if\foottrace8\message{^^J\string\FN@retaindelayed: +% retaining box \FN@slotget{\number\dp\z@}}\fi +% \if\foottrace{16}{\showboxdepth4 \showboxbreadth400 +% \tracingonline=\@ne\showbox\z@}\fi + {\FN@retaindelayed \nointerlineskip \box\z@}% + \else \unpenalty \setbox\z@\lastbox +% \ifnum\lastnodetype>\m@ne +% \errmessage{Unexpected node \number\lastnodetype}\fi +% \ifvoid\z@ \else +% \if\foottrace8\message{^^J\string\FN@retaindelayed: +% carrying split box \FN@slotget{\number\dp\z@}}\fi +% \if\foottrace{16}{\showboxdepth4 \showboxbreadth400 +% \tracingonline=\@ne\showbox\z@}\fi\fi + \fi} +% \end{macrocode} +% \end{macro} +% \begin{macro}{\MFL@processplain} +% This gets called for actually inserting the processed material +% into the footnote box. The current state of affairs is that +% \cmd{\FN@config} contains all footnotes that should get +% transferred to the next page completely. The cache boxes contain +% the collected and typeset footnotes for typesetting on the current +% page. +% +% The structure of a cachebox is currently as follows: it is filled +% with vboxes containing the arranged material, optionally followed +% by another box to be carried over to the next page flagged with a +% \cmd{\nobreak} penalty. +% \begin{macrocode} +\def\MFL@processplain#1{% +% \MFL@checkconsistency#1% + \ifvoid\FN@cache#1% +% \end{macrocode} +% Now if the cache box is void, nothing gets typeset on the current +% page. What we do, however, is to collect all boxes from the +% original insertion that did not make it on this page and reinsert +% them. \cmd{\count@} is cleared to zero to retain nothing special. +% \begin{macrocode} + \global\setbox\FN@tempbox\vbox\bgroup + \unvbox#1% + \count@\z@ + \let\@elt\FN@removecheck \FN@retaindelayed + \ifvoid\z@ \egroup + \else \nointerlineskip \box\z@ \egroup + \MFL@realinsert{#1}{\unvbox\FN@tempbox}% + \fi +% \end{macrocode} +% The following stops in the insertion process within the |manyfoot| +% package. +% \begin{macrocode} + \expandafter\expandafter + \fi\iffalse\fi +% \end{macrocode} +% Ok, this is the case when we have a nonvoid cache box. +% \begin{macrocode} + \global\setbox#1\vbox\bgroup% + \unvbox\FN@cache#1% + \ifnum\lastpenalty>\z@ + \unpenalty + \setbox\z@\lastbox + \else + \setbox\z@\box\voidb@x + \fi +% \end{macrocode} +% Ok, now box zero contains carryover material (if any). We +% initialize \cmd{\count@} to this so that we will keep this carryover +% material just once. +% \begin{macrocode} + \count@\dp\z@ + \global\setbox\FN@tempbox\vbox\bgroup + \box\z@ + \nobreak + \unvbox#1% + \let\@elt\FN@removecheck \FN@retaindelayed + \ifvoid\z@ \egroup \MFL@removevboxes\egroup + \else \nointerlineskip \box\z@ \egroup + \MFL@removevboxes \egroup + \MFL@realinsert{#1}{\unvbox\FN@tempbox}% + \fi} + +\let\MFL@processpara\MFL@processplain +% \end{macrocode} +% \end{macro} +% Ok, here is the bit about the caches: whenever we encounter a new +% configuration, we have to first update the caches since we don't +% know the sizes we are dealing with regarding the new configuration +% until we do so. The caches are kept up to date globally. When we +% are working at several levels in the recursion, we have a bottom +% active level where we may are looking for a way to find a best break +% and configuration. We will return at most one configuration once we +% are finished. While we are working with a returned configuration, +% adding more material on the current list will not require another +% recursion as long as the totals stay underfull: the penalty +% difference between underfull configurations becomes smaller while +% the underfullness decreases, which means that smaller breaks that +% have not been chosen before might become eligible if the penalties +% allow for that. Only when the badness of underfullness remains +% infinite can't we have any improvement. +% +% Ok, after we recurse for removing an underfull condition, the +% resulting configuration can't actually be used further for breaks +% with less remaining space. It is, however, clear that if less space +% remains, there is no better break with the same configuration +% leaving \emph{more} space: if there were, it would already have been +% taken. That means that our goal height for the next break will be +% chosen in order to reach the exact size met on the last recursion. +% No break before that can be chosen on the next try, but a break +% after it might then be taken. +% +% available, or an overfull one. If a deeper level at any point of +% time returns an overfull configuration, we are finished. The best +% configuration to be returned is the least underfull. If there is +% none, the least overfull. The case of no underfull at all can only +% happen if even splitting this and every subordinate level to minimal +% height and recursing does not yield an underfull. At every level, +% we need to maintain just a current split, and the previous best +% split at most. +% +% When we change a configuration on recursing, we have to remember the +% configurations for the previous best split. We can manage that by +% sweeping the current cache values into a local box register +% before recursing with a different configuration: we have to rebuild +% the box registers for a different configuration, anyway. We don't +% save the configuration from an overfull setting: when we rework the +% list in slow motion mode, we can't help stopping the recursion by +% reaching an overfull setting that is at least as good as the +% initial one. +% +% When we return to a caller, we leave the cache in the configuration +% of the best choice up to now: either we are returning an overfull +% configuration and if it is not the best so far, the caller can +% restore his better choice from his copy, or we are returning an +% underfull configuration in which case the caller might still want to +% improve upon it before returning to its caller in turn. New: If we +% return an underfull configuration, we also return an ``optimal +% penalty estimate'' that gives the best break point penalty under the +% assumption that additional stretchability is present on the page. +% +% The purpose of this is to offer the possibility of avoiding widows +% and similar by moving more material in some footnotes to the next +% page in exchange for other material. +% +% At the current grouping level we empty out our current cache and +% keep it for working purposes on the vertical list until we return +% (nobody references it while we are working on it). We always enter +% with an overfull configuration, meaning that \cmd{\FN@vsize} is +% negative. It is calculated with the current cache\slash config +% setting. +% +% There is a danger of overflow involved with that: if we keep a swept +% complete configuration at each level of recursion, we need $O(n^2)$ +% of space here. The alternative would be to keep the history of how +% the configuration came about. Since that might involve some +% slow-motion splitting, this is also a speed issue. Since deep +% recursion with pending best data at each level is not really +% likely, and since we are not going to have that many footnote +% levels to go around, anyway, we just rely on \LaTeX\ having been +% started with sufficient memory. +% +% A workable compromise would be to just store the split boxes +% from a configuration together with the configuration data for +% reconstructing the rest. After all, we don't need to reconsider +% such a configuration before actually typesetting anything. And +% whenever we find an acceptable fit (neither underfull\slash +% overfull), we could cut through all the hierarchy without having to +% restore anything. This has not been implemented yet: at the moment +% we go for the less complicated variation. +% +% +% The algorithm we use here is a bit complicated. Whenever we +% recurse, we have one of the following situations: +% \begin{enumerate} +% \item An overfull/underfull dilemma: including a minimal amount of +% material at the current level will cause the page to become +% overfull. This can be the case in connection with zero (in +% case of interline penalties for larger blocks), one or more +% subordinate footnotes and related footnotes. +% \item A pure overfull dilemma: the page was overfull to start with, +% we need to reduce it. +% \item an underfull dilemma: some operation in the next level made +% the page become underfull, only too much so. We can't make it +% fuller on the current level, but we can make it even emptier, +% and let the next level fill it up again. +% \end{enumerate} +% +% In the current implementation, we just ignore the slight probability +% that the optimum choice might lie with case~3. We don't +% recurse for making the page fuller again. If we have an +% overfull/underfull dilemma, the recursion will either give us a +% less awful overfull box, or an underfull one. An overfull box that +% occurs at the highest level of recursion can't be improved on any +% lower level. So we never need to locally return an overfull box: we +% can compare it to the best overfull box seen before, and if we turn +% out better than that, we overwrite the global best overfull value +% and return the best local underfull if there is such a one. The +% best local underfull will then be refilled as much as possible on +% the next level without changing the configuration. Actually, if we +% need to change the configuration, this would also be fine as long as +% we arrive at a better underfull eventually. But since a change of +% configuration renders our previous split completely useless, as the +% broken paragraph could look disastrously different under a changed +% configuration, we would need to recurse again. We repeat this +% recursing operation until we don't get an underfull solution +% returned anymore. We then return the best underfull, if any. The +% best overfull is stored globally, as mention before. +% +% Does this sound complicated? Unfortunately, it does. It also +% sounds somewhat slow. For that reason, we do a few assumptions that +% will facilitate a good average-case behavior. The first assumption +% is that we will usually do fine by just splitting in the current +% level (if at all) and not at all in subordinate levels. +% +% We do this assumption on the first pass used for gathering the size +% information and collect the corresponding boxes in nested lists. +% When the recursion tops out, it does so either with an overfull +% page, or an underfull page. If it does with an underfull page, we +% cache the current configuration for the next pass through the output +% routine, so that we won't need to retypeset and measure assembled +% boxes that have not gathered any new material. If we top out with an +% overfull page, the previous underfull configuration is still worth +% keeping as well, as it might become the material actually chosen to +% be typeset. +% +% Ok, the current best configuration of the next recursion level is +% gathered on the current vertical list, in a separate box. We use +% box~2 for this purpose. A saved configuration consists of the +% complete contents for the current cache box without the trailing +% penalty indicating material from a single split box carried over to +% the next page (boxes that are carried over completely to the next +% page are not maintained here but rather reinserted by +% \cmd{\MFL@processnested}). This penalty is added in case the box +% is actually disassembled and returned: there is no possibility for +% confusion since we only save such a configuration if indeed there is +% a split present. +% \begin{macrocode} +\newtoks\FN@output +\FN@output\output + +\newbox\FN@tempbox +\newinsert\FN@savebox +\count\FN@savebox\@m +\dimen\FN@savebox\maxdimen +\skip\FN@savebox\z@skip +\global\setbox\FN@savebox\box\voidb@x + +\expandafter\expandafter\expandafter\let\FN@cache\FN@savebox=\@cclv + +\expandafter\def\csname FN@ht\number\FN@savebox\endcsname{\topskip}% +\expandafter\def\csname FN@dp\number\FN@savebox\endcsname{\maxdepth} + +\def\FN@list{\MFL@list\@elt{}\footins} + +\def\FN@sweepbox#1#2{\ifvoid#2\else + \nointerlineskip\box#2\penalty#2\fi} + +\def\FN@sweepcachebox#1#2{\nointerlineskip + \box\FN@cache#2% + \penalty\FN@cache#2} + +\def\FN@copycachebox#1#2{\nointerlineskip + \copy\FN@cache#2% + \penalty\FN@cache#2} + +\def\FN@restoreboxes{\count@\lastpenalty \unpenalty + \ifnum\count@>\z@ + \global\setbox\count@\lastbox + \expandafter\FN@restoreboxes + \fi} +% \end{macrocode} +% \begin{macro}{\FN@removecheck} +% This returns |\@ne| if and only if the current slot master is strictly +% inside of the specified interval. +% \begin{macrocode} +\def\FN@removecheck#1#2{% + \ifnum#1<\FN@slotget{\number\dp\z@} % + \ifnum#2>\FN@slotget{\number\dp\z@} % + \@ne\fi\fi} +% \end{macrocode} +% \end{macro} +% Parameter recording merely records the relevant value of the +% skip register and sets it to zero. The purpose is to avoid changes +% of the reserved page space when we collect additional material from +% a page where an insertion of the appropriate kind had already been +% encountered. This is used for filling up underfull pages. +% \begin{macrocode} +\def\FN@recordinsertparam#1#2{\ifvoid#2\else + \global\skip\number#2=\the\skip#2\relax\fi} + +\def\FN@clearinsertparam#1#2{\ifvoid#2\else + \global\skip#2=\z@skip\fi} +% \end{macrocode} +% \cmd{\FN@insertouterspace} will sum the size of the inserts manually. +% \begin{macrocode} +\def\FN@insertouterspace#1#2{\ifvoid#2\else + +\skip#2+(\ht#2+\dp#2)*\count#2/\@m\fi} + +\def\FN@list@iterate#1{\let\FN@eltsave\@elt + \let\@elt#1% + \FN@list + \let\@elt\FN@eltsave} + +\def\FN@nest@iterate#1{\let\FN@eltsave\@elt + \let\@elt#1% + \FN@nestlist + \let\@elt\FN@eltsave} +% \end{macrocode} +% +% \subsection{The output routine stuff} +% +% \paragraph{Marks} +% This is used for sweeping all marks up for reinsertion. +% \begin{macro}{\FN@allmarks} +% \begin{macrocode} +\def\FN@allmarks#1{\@elt{#1}% + \ifnum#1<\count266 + \expandafter\FN@allmarks\expandafter{\number\numexpr#1+\@ne}% + \fi} +% \end{macrocode} +% \end{macro} +% \begin{macro}{\FN@sweeptopmarks} +% \begin{macro}{\FN@topmarkbox} +% This sweeps the current topmarks and places them into the global box +% \cmd{\FN@topmarkbox}. +% \begin{macrocode} +\def\FN@sweeptopmarks{\global\setbox\FN@topmarkbox\vbox{% + \def\@elt##1{\marks##1{\unexpanded\expandafter{\topmarks##1}}}% + \FN@allmarks0}} +\newbox\FN@topmarkbox +% \end{macrocode} +% \end{macro} +% \end{macro} +% \begin{macro}{\FN@establishmarks} +% This sets marks from a marks sweep. The first argument is the +% mark number, the second is from the first mark on the first scan, +% the third argument from the bottom mark on the first scan, and the +% fourth argument from the bottom mark on the second scan (with +% additional mark entries). If second and third arguments don't +% match, no mark gets placed. +% \begin{macrocode} +\long\def\FN@establishmarks#1#2{\edef\reserved@a{\unexpanded{#2}}% + \edef\reserved@b{\unexpanded\expandafter{\splitbotmarks#1}}% + \ifx\reserved@a\reserved@b + \marks#1{\unexpanded\expandafter{\splitfirstmarks#1}}% + \marks#1{\unexpanded\expandafter{\reserved@b}}% + \fi} +% \end{macrocode} +% \end{macro} +% \begin{macro}{\FN@markspassone} +% This constitutes the first pass for mark collection. We do this +% just to check whether there are any marks in the list. +% \begin{macrocode} +\def\FN@markspassone#1{\noexpand\FN@establishmarks{#1}% + {\unexpanded\expandafter{\splitbotmarks#1}}} +% \end{macrocode} +% \end{macro} +% \begin{macro}{\FN@insertmarks} +% This routine transfers first and bottom marks from the current +% |\box255| to the vertical list in order to get the marks right. +% This is quite a bother, since we must detect the special case +% where there are no marks at all in the list, and since we might +% require the use of several \cmd{\vsplit} commands in a row, since +% infinite stretch might make the optimal breakpoint lie before the +% end of the box in spite of its large size. +% +% So we need to do the splitting in a loop, and do it twice, once +% with artificial marks at the start. If those artificial marks +% make it to \cmd{\splitbotmarks}, we don't place any actual marks. +% \begin{macrocode} +\def\FN@pseudomarks#1{\marks#1{X}} +\def\FN@insertmarks{% + {\setbox\z@\copy\@cclv + \splittopskip-\maxdimen\relax + \vbadness=\maxdimen + \loop + \ifvoid\z@\else + {\let\@elt\FN@pseudomarks + \setbox\z@\vbox{\FN@allmarks0\nobreak\unvcopy\z@}% + \setbox\z@\vsplit\z@ to\maxdimen}% + \let\@elt\FN@markspassone + \edef\next{\FN@allmarks0}% + \setbox\z@\vbox{\nobreak\unvbox\z@}% + {\setbox\z@\vsplit\z@ to\maxdimen}% + \next + \repeat}} +% \end{macrocode} +% \end{macro} +% \paragraph{Some stuff} +% +% \begin{macro}{\FootnoteMainMinimum} +% This specifies the minimum amount of main text. You can make this +% a complicated expression if you want to, for example by checking +% the presence of particular footnotes. +% \begin{macrocode} +\def\FootnoteMainMinimum{0pt} +% \end{macrocode} +% \end{macro} +% +%\paragraph{The output routine itself} +% +% This is our own output routine that does all the balancing stuff. +% If we receive a forced penalty here, we must not do any of our +% output processing on our own unless this is the choice of the +% underlying output routine. We do want to have the `real' output +% routine to have a correct idea about the size that the insertions +% will take up. So the steps that we \emph{will} actually perform in +% any case are sorting the insertions and calculating their real +% size. If we have not had a forced penalty, we are free to exit the +% output routine for gathering further material as there are no +% expections of the underlying output routine when it should get +% called. If we encountered a forced penalty, things are getting more +% complicated. If the current page happens to be overfull after +% adding the current material, we first need to ship out the material +% for a regular page (after splitting off the necessary material for +% the next page). We then reinsert the remaining split insertions, +% any possibly split off page material and the penalty. +% +% \TeX is rather monotonous in its page break processing. Increase +% the available page size, and the available page material will also +% increase. There is a singular exception to that rule, and that are +% split and floating insertions. However, we notice their presence by +% a non-zero setting of \cmd{\insertpenalties}, and we can just +% measure the material that they have taken up in a forced pass of the +% output routine, adding that much to our request size. However, this +% operation will change the penalties associated with the page breaks. +% +% Unfortunately, this is not sufficient: the penalty might have been +% inserted with a box immediately preceding it. In that case the +% penalty would have been guaranteed to eventually turn up in the +% output routine. If we now reinsert merely all of the above stuff, +% the penalty will just disappear. If we protect the penalty by +% placing an empty box before it when none of it had been before it +% before, we will get an empty page. Since we don't know whether the +% penalty was supposed to disappear at the start of an empty page or +% not, we will do the following: if the rest of |\box255| is nonvoid, +% we just reinsert the split insertions followed by the rest of +% \box255 and the penalty and return. If it is void, we call the +% regular output routine, capturing its output in a |\vbox| of its +% own. If the regular output routine failed to ship out the prepared +% insertions, we just keep the original data either in their boxes or +% in a reinserted insertion. +% +% It hides the relevant information from the `real' output routine +% until such a time that we have enough material gathered to produce a +% full page. The exception to this is when we have a special penalty +% that gets passed through to the regular output routine. +% +% If we are on a material collecting spree, \cmd{\FN@savebox} contains +% all boxes from the last output call time. At the point where we +% enter the output routine, \cmd{\FN@vsize} contains the amount of +% space available for mounting footnotes, after subtracting all +% insertions of footnote variety. At most times in our output +% routine, the variable will contain the amount of space left after +% everything is put to the page including footnotes. +% \begin{macrocode} +\savingvdiscards=\@ne +% \end{macrocode} +% We have the following situations that can cause us to enter the +% output routine: +% \begin{enumerate} +% \item The page has just filled up. +% \item A magic output penalty has been encountered. +% \item We are filling up a previously underfull page. +% \item We are looking for missing insertions that may have floated. +% \end{enumerate} +% +% In the situation where the page break has already been determined +% (magic output penalty, looking for missing insertions), we are not +% interested in `foreign' insertions like floating figures. The +% current code does not cater for them. +% +% If the output routine is invoked with a penalty of $-13750$, then +% the page content is merely used for setting the \cmd{\topmarks} +% array. In that case, we just clear out the output box and resume. +% We don't fiddle with \cmd{\deadcycles} in order to catch foulups. +% Also we don't touch insertion boxes. There is a particular +% situation where there \emph{are} insertions, namely if we are +% collecting insertions after the last output routine has ended up +% with a non-zero value of \cmd{\insertpenalties}. In this case, all +% insertions we \emph{do} get are floating insertions, meaning that +% the had a preceding insertion of the same class already on the last +% page, and thus we have zeroed its skip register already. We are +% assuming that a single pass with such a large \cmd{\vsize} is +% sufficient for pulling all insertions. If that happens to be +% incorrect, insertions need to get pulled in piecewise, but then we +% are probably in big dodo with regard to page size accounting, +% anyway. +% \begin{macrocode} +\output{% +% \if\foottrace8% +% \message{entering output with +% \outputpenalty=\the\outputpenalty:}% +% \showbox\@cclv\fi +% \ifnum\c@page=10 \traceon\fi + \ifnum\outputpenalty=-13750 +% \if\foottrace8% +% \message{Discarding box 255.}% +% \fi + \ifnum\insertpenalties>\z@ + \PackageError{bigfoot}{Too much insertion material}{% + This error means that the output routine was not able to^^J% + gather all floating insertions in a single pass.^^J% + Complain to the author if you consider this a bug}% + \fi + \global\advance\FN@outervsize\dimexpr\ht\@cclv-\vsize\relax + \global\setbox\@cclv\box\voidb@x + \else +% \end{macrocode} +% Note that a potential \cmd{\FN@vsadjustlist} will restore the +% previous value of \cmd{\outputpenalty}. So we need to save it. +% \begin{macrocode} + \edef\FN@outputpenalty{\number\outputpenalty}% + \ifvoid\FN@savebox + \ifvoid\@holdpg + \FN@sweeptopmarks + \fi + \ifvoid\@cclv + \global\FN@outervsize\z@ + \else + \global\FN@outervsize\ht\@cclv + \global\setbox\@cclv\vbox{\unvbox\@cclv\boxmaxdepth\maxdepth}% + \global\advance\FN@outervsize-\ht\@cclv\relax + \fi + \global\let\FN@vsadjustlist\@empty + \FN@normaloutput +% \end{macrocode} +% \cmd{\FN@outervsize} now contains the amount of stretch that was put +% into the output box in order to reach the goal size. So it is the +% space we still have left available to fill given the current +% \cmd{\vsize} and contents of insertions. Note that this can be +% negative if shrinkability has been involved in getting at the value. +% \begin{macrocode} + \else +% \end{macrocode} +% We now are in the situation that we already have collected material +% previously. \cmd{\FN@outervsize} contains the overshoot according +% to the difference of demanded and achieved space again. It remains +% unchanged if we have inserted a special penalty ourselves: in this +% case no page material has possibly been added. However, we can't be +% sure that adding a special penalty does not take more than one +% output routine call before delivery. For that reason, we don't rely +% on special outputs being special and always subtract any +% additionally demanded \cmd{\vspace} from \cmd{\FN@outervsize} before +% calling a special output, so that we can afterwards compensate for +% it. +% \begin{macrocode} + \ifvbox\@cclv + \dimen@\ht\@cclv + \global\setbox\@cclv\vbox{\unvbox\@cclv\boxmaxdepth\maxdepth}% + \global\advance\FN@outervsize\dimexpr\dimen@-\ht\@cclv\relax + \fi +% \end{macrocode} +% Now we invalidate the cache boxes for all insertions that had +% changed due to the recent additions to the page. +% \begin{macrocode} + \FN@nest@iterate\FN@maybeinvalidatecache +% \end{macrocode} +% We now update all boxes by inserting the previously collected +% material in front of the boxes. +% \begin{macrocode} + \vskip\z@skip + \unvbox\FN@savebox + \loop + \count@\lastpenalty + \ifnum\count@>\z@ + \unpenalty + \setbox\z@\lastbox + \global\setbox\count@\vbox{\unvbox\z@\unvbox\count@}% + \repeat + \ifcase + \ifnum\FN@outputpenalty=-13749 \@ne\fi + \ifnum\FN@outputpenalty=-13751 \@ne\fi \tw@ + \or + \FN@vsadjustlist +% \if\foottrace8\message{receiving special penalty +% \FN@outputpenalty, dissing box 255:}% +% {\showboxdepth4 \showboxbreadth400 +% \tracingonline=\@ne\showbox\@cclv}\fi +% \end{macrocode} +% We don't need to insert either discards or outputpenalties anywhere: +% those are exclusively derived from our own inserted code. +% Since the \cmd{\pagediscards} derive from our own insertion, we +% clear them out. \cmd{\outputpenalty} is restored to the original +% value before the total page is glued together. +% \begin{macrocode} + \global\setbox\@cclv\vbox{\pagediscards}% +% \end{macrocode} +% This special penalty means that we have been collecting floated +% insertions right now. So |\box255| is actually empty except for +% filler material. We restore the old box into it. +% \begin{macrocode} + \global\setbox\@cclv\lastbox + \unskip +% \ifnum\lastnodetype>\m@ne +% \errmessage{Unexpected node \number\lastnodetype}\fi + \ifnum\FN@outputpenalty=-13749 + \FN@normaloutput + \else + \the\FN@output + \fi + \else + \dimen@\topskip + \FN@vsadjustlist + \setbox\z@\lastbox + \unskip +% \ifnum\lastnodetype>\m@ne +% \errmessage{Unexpected node \number\lastnodetype}\fi +% \end{macrocode} +% Ok, now we reconstruct the box from its parts. We add the material +% together, taking the previous output penalty and the current page +% discards for glueing the stuff together. The previous output +% penalty then is irrelevant for further purposes and we replace it +% again. \cmd{\FN@outervsize} has been +% adjusted by the accumulated contributions of insertions to the page +% size. Fiddling with it would not appear necessary or even prudent. +% \begin{macrocode} + \global\setbox\@cclv\vbox{\unvbox\z@ + \ifnum\outputpenalty<\@M \penalty\outputpenalty\fi + \pagediscards +% \end{macrocode} +% Now we might have had a \cmd{\topskip} value designed for requesting +% a given number of lines. We need to remove anything of that kind. +% Splitting again achieves that. +% \begin{macrocode} + \ifvbox\@cclv + \global\setbox\@cclv\vbox{\break\unvbox\@cclv}% + {\splittopskip-\maxdimen \setbox\z@\vsplit\@cclv to\z@}% + \unvbox\@cclv + \fi + \boxmaxdepth\maxdepth}% + \global\outputpenalty\FN@outputpenalty\relax +% \end{macrocode} +% Ok, now if \cmd{\topskip} is actually \emph{positive}, we have been +% collecting material tentatively without having proper marks. We +% then need to fill in the marks into the list and try again. +% \begin{macrocode} + \ifdim\dimen@>\z@ +% \if\foottrace8 +% \message{recycling special penalty} +% \fi + \hrule\@height\z@\@depth\z@ + \unvcopy\FN@topmarkbox + \penalty-13750 + \hrule\@height\z@\@depth\z@ + \FN@insertmarks + \penalty-13749 + \FN@prepareoutput + \global\topskip-\maxdimen + \global\vsize0.5\maxdimen + \global\advance\FN@outervsize-\vsize + \else + \FN@normaloutput + \fi + \fi + \fi + \fi} +% \end{macrocode} +% \begin{macro}{\FN@normaloutput} +% This is the normal output routine we use. +% Now we have recovered a sensible state and glued everything together +% that has been necessary. All insertion parameters are at their +% standard values, and any insertions have been collected in the +% respective boxes. If the whole exercise has been +% \begin{macrocode} +\def\FN@normaloutput{% +% \end{macrocode} +% \cmd{\FN@vsize} is now being set to the vertical size taken up by +% the insertions, according to \TeX. Note that this does not include +% flexibility. This much amount of space gets available on the +% current page if we remove all insertions. This figures into +% \cmd{\FN@vsize} as a \emph{positive} quantity since the insertion +% size was taken from \cmd{\pagegoal}, and we reconstitute it in this +% manner. +% \begin{macrocode} + \FN@nest@iterate{\FN@insertouterspace\global\FN@vsize\dimexpr\z@}% +% \end{macrocode} +% Now we sort the inserts and regenerate the cache. +% \begin{macrocode} + \FN@nest@iterate\FN@sortinsert + \FN@nest@iterate\FN@clearcache + \xdef\FN@config{\noexpand\@elt{\number0\botmarks\FN@slave}% + {\number\maxdimen}}% + \FN@nest@iterate\FN@reconfig +% \end{macrocode} +% Note that \cmd{\FN@reconfig} subtracts the \emph{actual} size of all +% insertions (after paragraphs have been combined and too early +% insertions moved to the next page) and also subtracts the flexible +% glues associated with the insertions' skip registers, so this +% flexibility is typically negative. +% \begin{macrocode} + \global\advance\FN@vsize\FN@outervsize\relax + \ifcase + \ifnum\insertpenalties>\z@ \@ne\fi +% \end{macrocode} +% If we have floating insertions, we need to catch up with them. This +% is done in case~1 which just places an immediate penalty and +% recurses. +% +% Now here are a few cases that are only checked when we don't have a +% special penalty: +% \begin{macrocode} + \ifnum\outputpenalty>-\@M +% \end{macrocode} +% The first case is if the page is underfull. We need more material then. +% \begin{macrocode} + \ifdim\FN@vsize>-\gluestretch\FN@vsize \tw@ \fi +% \end{macrocode} +% Second case is when there is not enough vertical minimum material. +% \begin{macrocode} + \ifdim\FootnoteMainMinimum>\ht\@cclv \tw@ \fi + \fi +% \end{macrocode} +% Case 3 means page is overfull. If there are no missing insertions, +% try to split. +% \begin{macrocode} + \ifdim\FN@vsize<\glueshrink\FN@vsize \thr@@ \fi +% \end{macrocode} +% +% page has appropriate size or we have special penalty. If we have +% come here not the first time, we might have arrived at a non-optimal +% break. So we attempt a split. +% \begin{macrocode} + \ifx\FN@vsadjustlist\@empty \else \thr@@\fi\z@ + \ifvbox\@cclv + {\vbadness\maxdimen + \global\setbox\@cclv\vbox + spread\FN@vsize{\unvbox\@cclv\boxmaxdepth\maxdepth}}% + \fi + \the\FN@output + \FN@nest@iterate\FN@clearcache + \or +% \end{macrocode} +% Case 1: We just pull in remaining insertions and are done. +% \begin{macrocode} + \FN@restartoutput + \penalty -13749 +% \end{macrocode} +% Now we want to gather additional material. This is somewhat weird. +% We first gather our material with a `normal' setting of topskip, and +% then we'll have another go at the material using proper marks. We +% can't actually insert anything right now in order not to introduce a +% premature breakpoint. +% \begin{macrocode} + \or + \dimen@=\dimexpr\FN@vsize-\glueshrink\FN@vsize\relax + \FN@prepareoutput + \global\topskip \normalbaselineskip + \global\vsize \dimen@ + \global\advance\FN@outervsize-\vsize + \global\deadcycles\z@ + \else +% \end{macrocode} +% Fake our output box into something looking like a cache box +% \begin{macrocode} + \edef\FN@masterid{\number\maxdimen}% + \def\FN@masterslot{-1}% + \global\setbox\@cclv\vbox{% + \box\@cclv}% + \xdef\FN@config{\noexpand\@elt{\number0\botmarks\FN@slave}% + {\number\maxdimen}}% +% \ifvoid\FN@savebox \else \PackageError{bigfoot}{\FN@savebox +% \space should be void!}{}\fi + \global\setbox\FN@savebox\vbox{}% + \gdef\FN@badness{0}% + \edef\FN@defaultpenalty{\ifnum\outputpenalty<\@M + \number\outputpenalty + \else + 0\fi}% + \let\@elt\FN@newlevel + \@elt{}\FN@savebox\FN@nestlist\FN@mainsplitreturn + \let\@elt\relax +% \if\foottrace8{\showboxdepth4\showboxbreadth100\showbox\@cclv}\fi + \global\setbox\FN@savebox\box\voidb@x + \count@\vbadness + \vbadness\maxdimen + \global\setbox\@cclv\vbox spread\FN@vsize\bgroup + \unvbox\@cclv + \ifnum\lastpenalty>\z@ + \unpenalty + \global\setbox\FN@tempbox\lastbox + \else + \global\setbox\FN@tempbox\box\voidb@x + \fi + \setbox\z@\lastbox + \ifvoid\z@ + \ifvbox\FN@tempbox + \ifnum\outputpenalty<\@M + \global\setbox\FN@tempbox{\unvbox\FN@tempbox + \penalty\outputpenalty}% + \fi + \global\outputpenalty=\@M + \fi + \egroup \global\setbox\@cclv\box\voidb@x + \else + \MFL@removevboxes + \unvbox\z@ + \let\FN@defaultpenalty\@M + \FN@getbreakpenalty + \setbox\z@\lastbox + \ifcase + \ifvbox\FN@tempbox \@ne\fi + \ifvoid\z@ \else \@ne\fi \z@ + \or + \global\setbox\FN@tempbox\vbox{\unvbox\z@ + \unvbox\FN@tempbox + \ifnum\outputpenalty<\@M + \penalty\outputpenalty + \fi}% + \global\outputpenalty\FN@breakpenalty + \fi + \boxmaxdepth\maxdepth\egroup + \fi + \vbadness\count@ + \setbox\z@\box\FN@tempbox + \let\@elt\relax +% \end{macrocode} +% Ok, now we have in box~255 the split off stuff for the current +% output routine, and in box~0 stuff that is going to follow +% afterwards. If box~0 is not void, we were not able to make use of +% all of box~255. There is a slight probability that by taking even +% \emph{more} material from the main list, we might get a better +% result (by being able to move footnote material to the next page +% instead), but we don't make use of this possibility here. In +% general, we assume that if box~0 is nonvoid, we take the resulting +% split. Otherwise, if the page appears underfull, we pull in more +% material. If the page is not underfull, we can pass it to the +% output routine. +% \begin{macrocode} + \dimen@=\dimexpr\FN@vsize-\glueshrink\FN@vsize\relax + \ifcase + \ifvoid\z@ \else\thr@@\fi + \ifdim\ht\@cclv<\normalbaselineskip \@ne\fi + \ifdim\dimen@<\normalbaselineskip \tw@\fi \@ne + \or + \FN@prepareoutput + \global\topskip \normalbaselineskip + \ifdim\dimen@<\normalbaselineskip \dimen@=2\baselineskip\fi + \global\vsize \dimen@ + \global\advance\FN@outervsize-\vsize + \global\deadcycles\z@ + \or +% \if\foottrace8% +% \message{^^JOutput: config is \FN@config...}\fi + \setbox\tw@\vbox{% + \the\FN@output +% \if\foottrace8% +% \ifnum\lastnodetype=\m@ne +% \message{^^JOutput: end without carryover^^J}% +% \else +% \message{^^JOutput: end with carryover}}% +% {\showboxdepth5 \showboxbreadth400 +% \tracingonline=\@ne\showbox\tw@ +% \fi +% \fi + }% + \unvbox\tw@ + \unvbox\z@ + \FN@nest@iterate\FN@clearcache + \or + \FN@restartoutput + \penalty -13751 + \unvbox\z@ + \fi + \fi} +% \end{macrocode} +% \end{macro} +% \begin{macro}{\FN@prepareoutput} +% This is a preparation for gathering more material. First sweep up +% all the information about \cmd{\vsize}, \cmd{\topskip} and +% \cmd{\outputpenalty}. After that, record the insertion skip +% parameter of all insertions that have already been started, and +% reset them to zero so that no additional space gets reserved for +% them in case more material accumulates. We don't reset +% \cmd{\topskip} here since the amount of newly requested material +% will typically be in total lines, and \cmd{\topskip} might be the +% only way to figure out the proper request size. If the current +% depth and following height would make for a non-standard line +% distance, we might have a problem here. There is no obvious way +% to avoid it, though. +% \begin{macrocode} +\def\FN@prepareoutput{% + {\let\@elt\FN@recordinsertparam + \xdef\FN@vsadjustlist{% + \global\vsize=\the\vsize + \global\topskip=\the\topskip + \global\outputpenalty=\the\outputpenalty\relax + \FN@list}% + \let\@elt\FN@clearinsertparam + \FN@list}% +% \end{macrocode} +% Now we collect all boxes in the save box. +% \begin{macrocode} +% \ifvoid\FN@savebox \else \PackageError{bigfoot}{\FN@savebox +% \space should be void in \string\FN@prepareoutput}{}\fi + \global\setbox\FN@savebox\vbox{% + \box\@cclv + \FN@list@iterate\FN@sweepbox}} +% \end{macrocode} +% \end{macro} +% \begin{macro}{\FN@restartoutput} +% This is for the case where we are requesting additional material +% and have to cater for sizes. +% \begin{macrocode} +\def\FN@restartoutput{% +% \end{macrocode} +% Calculate the remaining size on this page: +% \begin{macrocode} + \dimen@=\dimexpr\FN@vsize-\glueshrink\FN@vsize\relax +% \end{macrocode} +% We just pull in remaining insertions and are done. +% \begin{macrocode} +% \if\foottrace8\message{sending special penalty}\fi + \hrule\@height\z@\@depth\z@ + \unvcopy\FN@topmarkbox + \penalty -13750 + \hrule\@height\z@\@depth\z@ + \FN@insertmarks + \FN@prepareoutput + \global\topskip-\maxdimen\relax + \global\vsize 0.5\maxdimen + \global\advance\FN@outervsize-\vsize + \global\deadcycles\z@ +} +% \end{macrocode} +% \end{macro} +% +% Ok, here is the deal. If the \cmd\FN@truevsize is negative, we have +% an overfull vbox at our hand. We then start the splitting action. +% We take the first non-split lowest footnote block and split it to +% size, removing subordinate footnotes that we would not be able to +% maintain. We do this recursively starting by the top footnote +% block. It must be noted that it would be even better to start with +% the highest-numbered footnote (which corresponds to the latest +% finished footnote in \emph{logical} order, that in the source code), +% but then we get the problem that we might have to remove boxes from +% a footnote block that has already been split, and that is +% troublesome (to put it mildly) in case where the footnote block is +% set in paragraph mode. It's bad enough backtracking in a fixed +% order across footnote blocks, going back and forward would be pretty +% tough. +% +% So our recursion just walks the footnote blocks once top to bottom, +% splitting and removing boxes that are not needed. When we recurse, +% we have a dichotomy between current overfull and underfull boxes. +% At each recursion level, we enter with an overfull configuration +% that establishes the breakable section for the footnote block in +% question. +% +% Suppose that we have already established a previous best +% configuration. When we are recursing, we can only increase the +% badness (a non-broken insertion box contributes nothing to the +% overall badness or penalties, breaking the box causes a badness of +% 10000, minus the break penalty, plus the break badness). So there +% is no point in recursing if entry badness and break penalty are as +% large as the previous best break penalty or more. +% +% Ok, so we construct the footnote block and try splitting it +% to size. If this +% gives us a \emph{good} underfull version, we return that (and break +% out of recursion altogether). Otherwise we remember the underfull +% version before the break and recurse on the overfull version. If +% this returns an overfull version again, we return the underfull +% version before the break. If it returns an underfull version, we +% fill up the underfull version as much as possible without a change +% of configuration, then select the best of the last underfull and +% this as new local underfull. We then take the first overfull +% combination (even allowing a change of configuration), throw away +% the previous split in the next recursion level and recurse on the +% now thoroughly overfull combination again. +% +% When recursion tops out, we compare the current overfull with the +% previous one and record the best. We prefer keeping an older +% overfull, all other things being equal. +% +% Ok, so what are the data structures we maintain when going through +% all this folderol? +% +% We let the insertion boxes themselves remain untouched: that makes +% it only a bit more complicated to maintain and access the relevant +% boxes, but it might come handy at one time when somebody wants to +% implement recursion that is not strictly top-to-bottom. +% +% Instead we return the relevant information in the cache boxes. The +% total size of the cache boxes may not correspond to their actual +% contents: in case a split box intended for the next page is stored +% within them, its height is deducted from the total height of the +% cache box (and, consequentially, from \cmd{\FN@vsize}). +% +% \cmd{\FN@vsize}, the amount of free space on the current page, is +% only updated when changing levels of recursion. Instead we maintain +% score of the accumulated size in the current insertion in +% \cmd{\FN@myvsize}. +% +% What about the penalties and badness we collect? An unsplit +% footnote block carries a penalty of~0 (so we need not take into +% account unsplit footnote blocks at all during our bookkeeping, as +% they are neutral), a split footnote block is prepenalized with a +% penalty of~10000, plus the badness of the split, plus any penalties +% associated with the split (limited to the $[-10000\ldots10000]$ +% range). This means that no operation on other footnote blocks can +% lower an already accumulated score. This in turn means that we can +% prune any operations leading to a worse score than the preceding +% best score without having to actually recurse. +% +% This strategy will usually buy us a minimum number of split +% footnotes (since the penalty of~10000 is not easy to compensate) +% and corresponds rather closely to \TeX's own idea of footnote +% splitting. +% +% The following routine will analyze the last box where the results +% from \cmd{\splitdiscards} are stored and return the penalty +% associated with the breakpoint in the macro +% \cmd{\FN@breakpenalty}. +% \begin{macro}{\FN@getbreakpenalty} +% \begin{macrocode} +\def\FN@getbreakpenalty{{\setbox\z@\lastbox + \nointerlineskip\copy\z@ + \setbox\z@ + \vbox{\unvbox\z@ + \count@\FN@defaultpenalty\relax + \FN@getbreakpenaltyii + \xdef\FN@tempinfo{\edef\noexpand\FN@breakpenalty{% + \number\ifnum\count@>-\@M \count@ + \else -\@M \fi}}}}% + \FN@tempinfo} + +\def\FN@getbreakpenaltyii{% + \ifcase + \ifnum\lastnodetype<\z@ \m@ne\fi + \ifnum\lastnodetype<11 \@ne\fi + \ifnum\lastnodetype>13 \@ne\fi + \numexpr\lastnodetype-9\relax + \or + \PackageError{bigfoot}{Illegal node type}{This can't happen}% + \or + \count@\z@ \unskip \expandafter\FN@getbreakpenaltyii + \or + \count@\z@ \unkern \expandafter\FN@getbreakpenaltyii + \or + \count@\lastpenalty + \unpenalty \expandafter\FN@getbreakpenaltyii + \fi} +% \end{macrocode} +% \end{macro} +% \begin{macro}{\FN@mainsplitreturn} +% This is merely an argument delimiting control sequence to make it +% possible to figure out which recursion levels still need visiting. +% \begin{macrocode} +\def\FN@mainsplitreturn{} +% \end{macrocode} +% \end{macro} +% \begin{macro}{\FN@myvsize} +% This is the size currently taken by this insertion. +% \begin{macrocode} +\newdimen\FN@myvsize +% \end{macrocode} +% \end{macro} +% \begin{macro}{\bigfoottolerance} +% This specifies what footnote arrangement penalty will be accepted +% without looking for a better solution. +% \begin{macrocode} +\newcount\bigfoottolerance +\bigfoottolerance=100 +% \end{macrocode} +% \end{macro} +% \begin{macro}{\FN@getbadness} +% This takes a skip value of remaining space and negative +% stretchability and shrinkability, and then calculates +% \cmd{\badness} depending on how good the stretching accommodates +% the remaining space. +% \begin{macrocode} +\def\FN@getbadness#1{% + {\hfuzz\maxdimen\hbadness\maxdimen\setbox\z@\hbox to\z@{\hskip-#1}}} +% \end{macrocode} +% \end{macro} +% \begin{macro}{\FN@newlevel} +% This is the main workhorse of |bigfoot|. It splits a particular +% footnote level, recursing if necessary. The level list is +% delimited with \cmd{\FN@mainsplitreturn}. The whole thing is +% looped through while the splits are being optimized. While +% recursing, \cmd{\FN@badness} contains the badness of the current +% split configuration: a penalty of $10000$ for any split, plus the +% penalty at the split points. \cmd{\FN@ebadness} is the same, but +% also counting in the stretch badness for one particular +% configuration. Ebadness does not make sense to evaluate more than +% temporarily: it is not passed through the levels. Since +% \cmd{\FN@badness} is globally tampered with, its value at entry is +% saved in \cmd{\FN@entrybadness}. +% \begin{macrocode} +\def\FN@newlevel#1#2#3\FN@mainsplitreturn{% + \count@\FN@cache#2% + \ifvoid\count@ +% \if\foottrace1\message{Page=\thepage #2 is empty, recursing with +% \the\FN@vsize^^J}% +% \message{Config=\FN@config^^J}\fi + #3\FN@mainsplitreturn +% \if\foottrace1% +% \message{Page=\thepage #2 was empty, returning with \the\FN@vsize^^J}% +% \message{Config=\FN@config^^J}\fi + \else +% \if\foottrace1\message{Entering #2 with \FN@badness}\fi + {\def\FN@currentinsertion{#2}% + \def\FN@currentrecursion{#3}% + \let\FN@entryconfig\FN@config + \let\FN@entrybadness\FN@badness + \splittopskip\csname FN@ht\number#2\endcsname\relax + \splitmaxdepth\csname FN@dp\number#2\endcsname\relax + \vbadness=\maxdimen + \vfuzz\maxdimen + \let\@elt\relax + \expandafter\FN@newleveli\expandafter}% +% \if\foottrace1\message{Exiting #2 with \FN@badness}\fi + \fi} +% \end{macrocode} +% \end{macro} +% \begin{macro}{\FN@newleveli} +% \begin{macrocode} +\def\FN@newleveli{% +% \end{macrocode} +% \cmd{\FN@vsize} already includes the size of the complete unsplit +% insertion. When we recurse, it has to reflect the correct size at +% the time of recursion. Rounding error problems don't permit us to +% accumulate any sizes in \cmd{\FN@vsize} from processing our current +% insertion, so we just subtract the whole insertion-related +% content. We'll add stuff into it when recursing. +% \begin{macrocode} + \dimen@\dimexpr\ht\count@+\dp\count@\relax + \global\advance\FN@vsize\dimexpr \dimen@ + *\count\FN@currentinsertion/\@m\relax\relax +% \end{macrocode} +% Ok, now we are typesetting and collecting the best box. Notice +% that we \emph{don't} exit this \cmd{\setbox} command until we have +% found the best possible split. What we \cmd{\unvbox} here, stays +% dormant except for the last box. When we collect configurations +% from cache boxes, we don't collect anything from our current box +% that is being assembled. So the whole action is confined within the +% current list that will replace the cache box after splitting. The +% meaning of boxes on the various levels are:\par\noindent +% \begin{tabularx}{\linewidth}{ll@{:\hskip\tabcolsep}X} +% 0&box0&where stuff gets collected as tentative material to be +% unboxed with |\FN@removevboxes| once the insertion gets readied +% for shipout\\ +% 0&box2&the previous best split that was found\\\hline +% 1&box0&the material that gets worked off, the tail of the split\\ +% 1&box2&where the current split is assigned +% \end{tabularx} +% The structure of 0/box2 is the head of the split, followed by +% |\break| penalty, followed by the tail of the split, followed by +% |\penalty\FN@tempbox|, followed by pairs of cache boxes and +% penalties indicating their box number. That way, the tail can get +% restored immediately into |\FN@tempbox| when using +% |\FN@restoreboxes|. +% \begin{macrocode} + \global\setbox\count@\vbox\bgroup\unvbox\count@ +% \end{macrocode} +% We calculate \cmd{\FN@myvsize} as the total space taken up by this +% insertion. The size of the last box is excluded since it will be +% split now. +% \begin{macrocode} + \ifnum\lastpenalty=\z@ + \setbox\tw@\box\voidb@x + \setbox\z@\lastbox + \FN@myvsize=\dimexpr\dimen@-\ht\z@-\dp\z@\relax + \else +% \end{macrocode} +% Now if the box has been split previously, we glue it back together +% again. +% \begin{macrocode} + \unpenalty + \setbox\tw@\lastbox + \setbox\z@\lastbox + \FN@myvsize=\dimexpr\dimen@-\ht\z@-\dp\z@\relax + \dimen@\dp\z@ + \setbox\z@{\unvbox\z@ + \setbox\z@\lastbox + \unvbox\z@ + \unvbox\tw@}% + \ht\z@=\dimexpr\ht\z@+\dp\z@-\dimen@\relax + \dp\z@\dimen@ + \fi +% \end{macrocode} +% Ok, size is all accounted for. Go on with optimization. Note that +% these definitions here are made in \emph{inner} level, so they can't +% make it outside as the result of the optimization. If we drop out +% of here without superceding them, something's completely rotten. +% \begin{macrocode} + \edef\FN@bestebadness{\number\maxdimen}% + \let\FN@bestconfig\@undefined + \let\FN@bestvsize\z@skip + \let\FN@splitcolors\@empty +% \end{macrocode} +% Ok, first attempt. One interesting feature is that we will never +% have to rewind the boxes from a split: we can always just glue the +% box together again. And apart from tentative splits which we might +% revert if they cause a configuration change, we will not have to +% bother about contributing too much. What we put in the box here +% can stay. +% +% First we split to the remaining size. After having done so, we +% continue splitting until we get the necessary mark of the last +% footnote into our grasp: we can't split before that. +% \begin{macrocode} + \ifnum\FN@currentinsertion=\FN@savebox + \else + \def\FN@defaultpenalty{-\@M}% + \edef\FN@masterslot{\number\dp\z@}% + \edef\FN@masterid{\FN@slotget\FN@masterslot}% + \fi +% \end{macrocode} +% Now we are building one tentative candidate for returning in +% |\box\z@|. It will get discarded in case that a better candidate +% was already found before this box completes. +% \begin{macrocode} + \setbox\z@ + \vbox\bgroup +% \end{macrocode} +% Ok, now stuff gets complicated: for the first, tentatively `optimal' +% split, we want to have all available page stretchability properly +% taken into account. So we take the box, and add the available page +% stretchability at the top. Note that the stretchability is +% registered negatively. +% \begin{macrocode} + \let\FN@splitcolors\@empty + \setbox\z@\vbox{\vskip-\glueexpr(\FN@vsize-\dimexpr\FN@vsize + \relax\@minus\glueshrink\FN@vsize)% + *\@m/\count\FN@currentinsertion + \unvbox\z@ + \ifnum\FN@defaultpenalty>-\@M + \penalty\FN@defaultpenalty\nointerlineskip + \vbox to\maxdimen{}% + \fi}% +% \end{macrocode} +% Ok, now we have pushed the additional available stretch onto the top +% of box~0. Now we do the actual split to minimal size. That means +% that we don't consider any of the shrinkability available on the +% page: it might still be better employed in some recursive level. +% \begin{macrocode} + \setbox\tw@\vsplit\z@ to% + \dimexpr\FN@vsize*\@m/\count\FN@currentinsertion + -\FN@myvsize + \relax + \ifnum\FN@defaultpenalty>-\@M + \setbox\z@\vbox\bgroup\unvbox\z@\setbox\z@\lastbox + \unpenalty + \ifnum\lastnodetype<\z@ + \egroup \setbox\z@\box\voidb@x + \else + \egroup + \fi + \fi +% \end{macrocode} +% Ok, now the top of box~2 contains unwanted additional +% stretchability. The easiest way to get rid of it is by adding its +% negation. +% \begin{macrocode} + \setbox\tw@\vbox{% + \vskip\glueexpr(\FN@vsize-\dimexpr\FN@vsize + \relax\@minus\glueshrink\FN@vsize)% + *\@m/\count\FN@currentinsertion + \unvbox\tw@\boxmaxdepth\splitmaxdepth}% +% \end{macrocode} +% Ok, now rinse and repeat if we don't have reached the last footnote +% in the block. Or, if we are in the main block and have not reached +% the necessary minimum size. +% \begin{macrocode} + \ifnum\FN@currentinsertion=\FN@savebox + \edef\FN@slaveid{\splitbotmarks\FN@slave}% + \FN@contribute@tw@ + \ifdim\FootnoteMainMinimum>\FN@myvsize + \loop + \setbox\tw@\vsplit\z@ to\z@ + \edef\FN@slaveid{\splitbotmarks\FN@slave}% + \FN@contribute@tw@ + \ifcase\ifdim\FootnoteMainMinimum>\FN@myvsize + \ifvoid\z@ + \@ne + \else + \tw@ + \fi\fi \z@ + \or + \PackageWarning{bigfoot}{too little main material}% + \else + \repeat + \fi + \else + \ifnum0\splitbotmarks\FN@master=\FN@masterslot \else + \loop + \FN@contribute@tw@ + \setbox\tw@\vsplit\z@ to\z@ + \ifnum0\splitbotmarks\FN@master=\FN@masterslot + \else + \repeat + \fi + \let\FN@splitcolors\@empty + \edef\FN@slaveid{\splitbotmarks\FN@slave}% + \FN@contribute@tw@ + \fi +% \end{macrocode} +% All of the above was necessary to ensure that we actually have the +% beginning of the relevant footnote in our material. From now on, +% we are dealing with legal splits. +% Ok, now we have to check whether the subordinate configuration has +% changed. +% \begin{macrocode} + \ifx\FN@slaveid\@empty +% \ifnum\FN@currentinsertion=\FN@savebox\else +% \errmessage{Missing slaveid in \FN@currentinsertion}\fi + \edef\FN@slaveid{\number0\topmarks\FN@slave}% + \fi + \ifnum\numexpr\FN@slaveid+\@ne<\FN@masterid + \let\FN@next\FN@slaveid + \else + \let\FN@next\@empty + \fi +% \end{macrocode} +% At this point of time, we have \cmd{\FN@masterid} set properly for +% our purposes. It is to be used for returning any \emph{tail} part +% of a box. \cmd{\FN@slaveid} is by necessity not empty. If any +% footnote +% has had its mark broken off, its id must be in the open range +% between \cmd{\FN@slaveid} and \cmd{\FN@masterid}. So a nonempty +% value of \cmd{\FN@next} at this point of time indicates that we have +% to cater for a different configuration rather than the currently +% cached one. +% \begin{macrocode} + \FN@splitfurther} +% \end{macrocode} +% \end{macro} +% \begin{macro}{\FN@vsizerecurse} +% This fixes the vertical size up and recurses once. +% \begin{macrocode} +\def\FN@vsizerecurse{% + \global\advance\FN@vsize + -\dimexpr\FN@myvsize*\count\FN@currentinsertion/\@m \relax\relax + \let\@elt\FN@newlevel + \FN@currentrecursion\FN@mainsplitreturn + \let\@elt\relax + \global\advance\FN@vsize + \dimexpr\FN@myvsize*\count\FN@currentinsertion/\@m \relax\relax} +% \end{macrocode} +% \end{macro} +% +% \subsubsection{Main label for reconsideration} +% When we are here, then there is not yet a split in the next footnote +% blocks scheduled. We might have to restitch stuff together here, +% though. +% \begin{macro}{\FN@splitfurther} +% \begin{macrocode} +\def\FN@splitfurther{% + \ifx\FN@next\@empty \else \let \FN@slaveid\FN@next \fi +% \end{macrocode} +% Ok, if our configuration now differs from the last one for which we +% have cache boxes set up, we have to reconfigure. It it doesn't, we +% just stitch the boxes together again in order to have correct size +% info. +% \begin{macrocode} + \let\FN@next\FN@config + \xdef\FN@config{% + \@elt{\FN@slaveid}% + {\FN@masterid}% + \FN@entryconfig}% + \ifx\FN@next\FN@config + \let\@elt\FN@rejoin + \else + \let\@elt\FN@reconfig + \fi + \FN@currentrecursion + \let\@elt\relax +% \end{macrocode} +% Ok, now we check whether the current configuration is a match for +% the best previous one. Also we calculate the badness of the current +% situation. +% \begin{macrocode} + \xdef\FN@badness{\number\FN@entrybadness}% + \FN@checkcurrent +% \end{macrocode} +% No point in recursing if we can't beat the current best one. +% However, if we find a forced break, this is considered perfect as +% long as we are not overfull. Note that recursion can only increase +% the badness if we are still underfull here. +% \begin{macrocode} + \ifnum + \ifdim\skip@>\z@ \FN@ebadness \else \FN@badness \fi >\FN@bestebadness + \else + \xdef\FN@badness{\number\FN@entrybadness}% + \FN@vsizerecurse + \FN@checkcurrent + \fi +% \end{macrocode} +% Don't look further if we had a forced break or are overfull or are +% at the end of the list. +% \begin{macrocode} + \ifcase + \ifnum\FN@breakpenalty>-\@M \else \@ne \fi + \ifvoid\z@ \@ne \fi + \ifnum\badness<\@MM \else \@ne \fi + \tw@ + \or + \expandafter \FN@returnbest + \else + \FN@mayberecordbest + \setbox\tw@\vsplit\z@ to\z@ + \edef\FN@next{\splitbotmarks\FN@slave}% + \FN@contribute@tw@ + \expandafter \FN@splitfurther + \fi} +% \end{macro} +% \begin{macro}{\FN@checkcurrent} +% Check out the badness of the current configuration. +% \begin{macrocode} +\def\FN@checkcurrent{% + \skip@\glueexpr\FN@vsize-\FN@myvsize + *\count\FN@currentinsertion/\@m\relax + \FN@getbreakpenalty + \FN@getbadness\skip@ + \xdef\FN@badness{\number\numexpr\FN@badness+% + \FN@breakpenalty+\@M}% + \ifnum\badness>\@M + \edef\FN@ebadness{\number\numexpr\maxdimen-\@ne}% + \else + \ifnum\badness=\@M + \edef\FN@ebadness{\number\numexpr\maxdimen-\tw@}% + \else + \edef\FN@ebadness{\number\numexpr + \FN@badness+\badness}% + \fi + \fi + \dimen@\glueexpr\FN@bestvsize\relax +} +% \end{macrocode} +% \end{macro} +% \begin{macro}{\FN@checkforbest} +% This generates 2 if the old stored best is better, 1~if the +% current variation is better. +% \begin{macrocode} +\def\FN@checkforbest{% + \ifnum\FN@breakpenalty>-\@M \else + \ifnum\badness>\@M \else + \@ne + \fi + \fi +% \end{macrocode} +% Ok, so the split was not as good as to cause us to return +% immediately, and it also was not the last opportunity for a split +% (which again would make us return immediately). +% So we check if it is at least better than the last one, in which +% case we need to replace the previous best. +% \begin{macrocode} + \ifnum\FN@bestebadness>\FN@ebadness \@ne\fi + \ifnum\FN@bestebadness<\FN@ebadness \tw@\fi + \ifdim\skip@<\z@ + \ifdim\dimen@<\skip@ \@ne \else \tw@\fi + \fi + \ifdim\dimen@>\skip@ \@ne\fi \tw@} +% \end{macrocode} +% \end{macro} +% \begin{macro}{\FN@mayberecordbest} +% This checks whether the current configuration is better than a +% previously saved one. If it is, the previous configuration gets +% replaced. The cache boxes itself are copied, not voided in the +% process for efficiency reasons. +% \begin{macrocode} +\def\FN@mayberecordbest{% +% \end{macrocode} +% If the current break is forced and the page is not overfull, we take +% the break. +% \begin{macrocode} + \ifcase + \FN@checkforbest + \or + \xdef\FN@tempinfo{\def\noexpand\FN@bestvsize{\the\skip@}% + \def\noexpand\FN@bestebadness{\FN@ebadness}% + \def\noexpand\FN@bestbadness{\number\FN@badness}% + \def\noexpand\FN@bestconfig{\FN@config}% + \def\noexpand\FN@bestslaveid{\FN@slaveid}% + \def\noexpand\FN@bestsplitcolors{\FN@splitcolors}% + \def\noexpand\FN@breakpenalty{\FN@breakpenalty}% + \FN@myvsize=\the\FN@myvsize\relax}% + \global\setbox\FN@tempbox\box\z@ + \egroup + \FN@tempinfo + \let\FN@splitcolors\FN@bestsplitcolors + \let\FN@slaveid\FN@bestslaveid +% \end{macrocode} +% Now all relevant info has been retrieved, and we collect the best +% box info in |\box\tw@|. The structure of the information is as +% follows: it starts with the current box in split form, first the +% tail, then the start of the current split box. +% This is then followed by a zero kern, and then by pairs of boxes and +% penalties indicating the swept box. +% \begin{macrocode} + \setbox\tw@\vbox{% +% \end{macrocode} +% We don't need to place master/slave marks here: the necessary +% information is available outside in the \cmd{\FN@masterslot} and +% \cmd{\FN@slaveid} info and gets attached afterwards. +% \begin{macrocode} + \copy\z@\break\nointerlineskip + \copy\FN@tempbox\penalty\FN@tempbox + \let\@elt\FN@copycachebox + \FN@currentrecursion + \xdef\FN@tempinfo{\FN@myvsize=\the\FN@myvsize\relax}}% + \FN@tempinfo + \setbox\z@ + \vbox\bgroup + \unvbox\z@ + \setbox\z@\box\FN@tempbox + \fi} +% \end{macrocode} +% \end{macro} + +% \begin{macro}{\FN@returnbest} +% \begin{macrocode} +\def\FN@returnbest{% + \ifcase\FN@checkforbest + \or + \xdef\FN@tempinfo{\def\noexpand\FN@bestvsize{\the\skip@}% + \def\noexpand\FN@bestebadness{\FN@ebadness}% + \def\noexpand\FN@bestbadness{\number\FN@badness}% + \def\noexpand\FN@bestconfig{\FN@config}% + \def\noexpand\FN@bestslaveid{\FN@slaveid}% + \def\noexpand\FN@bestsplitcolors{\FN@splitcolors}% + \def\noexpand\FN@breakpenalty{\FN@breakpenalty}% + \FN@myvsize=\the\FN@myvsize\relax}% + \global\setbox\FN@tempbox\box\z@ + \egroup + \FN@tempinfo + \let\FN@splitcolors\FN@bestsplitcolors + \let\FN@slaveid\FN@bestslaveid + \global\FN@vsize\FN@bestvsize\relax +% \end{macrocode} +% Now all relevant info has been retrieved, and we collect the best +% box info in |\box\tw@|. The structure of the information is as +% follows: it starts with the current box in split form, first the +% tail, then the start of the current split box. +% This is then followed by a zero kern, and then by pairs of boxes and +% penalties indicating the swept box. +% \begin{macrocode} + \or + \global\let\FN@config\FN@bestconfig + \global\FN@vsize\FN@bestvsize + \global\let\FN@badness\FN@bestbadness + \egroup +% \end{macrocode} +% Restore the saved configuration. +% \begin{macrocode} + \setbox\z@\vbox{\unvbox\tw@ \FN@restoreboxes}% + \let\FN@splitcolors\FN@bestsplitcolors + \let\FN@slaveid\FN@bestslaveid + \unvbox\z@ + \setbox\z@\lastbox + \fi +% \end{macrocode} +% \begin{macrocode} + \ifnum\FN@currentinsertion=\FN@savebox + \else + \setbox\z@\vbox{% + \dimen@\dp\z@ + \unvbox\z@ + \ifvoid\FN@tempbox + \else + \global\setbox\FN@tempbox\vbox{% + \marks\FN@master{\FN@masterslot}% + \marks\FN@slave{\FN@slaveid}% + \FN@coloraftersplit\FN@splitcolors + \nobreak + \unvbox\FN@tempbox}% + \ht\FN@tempbox + \dimexpr\ht\FN@tempbox+\dp\FN@tempbox-\FN@masterslot sp\relax + \dp\FN@tempbox\FN@masterslot sp\relax + \wd\FN@tempbox\maxdimen + \fi + \ifdim\dimen@<\splitmaxdepth + \hrule\@height-\dimen@ \@width\z@ + \@depth \splitmaxdepth \relax \fi}% + \ht\z@=\dimexpr\ht\z@+\dp\z@-\FN@masterslot sp\relax + \dp\z@=\FN@masterslot sp + \fi + \nointerlineskip \box\z@ +% \end{macrocode} +% If nothing is to be carried over, we just finish our assignment to +% the cache box and return. +% \begin{macrocode} + \ifvoid\FN@tempbox \egroup +% \end{macrocode} +% If not, we add the carried-over box to the list, flag it with a +% \cmd{\nobreak}, and subtract its size from the finished box. +% \begin{macrocode} + \else + \dimen@-\dimexpr\ht\FN@tempbox+\dp\FN@tempbox\relax + \nointerlineskip\box\FN@tempbox + \nobreak + \expandafter\egroup + \expandafter\ht\expandafter\count@\expandafter\dimexpr + \the\dimen@+\ht\count@\relax + \fi +} +% \end{macrocode} +% \end{macro} +% +% \begin{macro}{\FN@contribute@tw@} +% This will go from the state where we have the previous +% \cmd{\splitdiscards} struttified on the current list some material +% split off from box~0 in box~2 to a state where box~2 is +% contributed to the current list. +% \begin{macrocode} +\def\FN@contribute@tw@{% +% \end{macrocode} +% First we change the current colors if we have any in our group. Not +% sure if this is entirely correct. +% \begin{macrocode} + \begingroup\edef\FN@next{\splitbotmarks\FN@color}% + \ifx\FN@next\@empty \endgroup\else \endgroup + \edef\FN@splitcolors{\splitbotmarks\FN@color}\fi +% \end{macrocode} +% If the last box is void, there is no previous split to reconstitute. +% \begin{macrocode} + \setbox4\lastbox + \ifvoid4 \setbox4\vbox{\splitdiscards}% + \setbox\tw@\vbox{\unvbox\tw@\boxmaxdepth\splitmaxdepth}% + \else +% \end{macrocode} +% Now the last box is a strut. We remove its outer dimensions from +% the total account, and then add back its natural dimensions after +% which we pour it back into the current list. +% \begin{macrocode} + \advance\FN@myvsize-\dimexpr\ht4+\dp4\relax + \setbox4\vbox{\unvbox4}% + \advance\FN@myvsize\dimexpr\ht4+\dp4\relax + \unvbox4 +% \end{macrocode} +% We want to contribute box~2 back without any topskip glue, so we +% manually remove any such glue by splitting an empty box off. +% \begin{macrocode} + \setbox4\vbox{\splitdiscards}% + \setbox\tw@\vbox{\break\unvbox\tw@}% + {\splittopskip-\maxdimen \setbox\tw@\vsplit\tw@ to\z@}% +% \end{macrocode} +% Notice the effect of \TeX's special box scope rules: box~2 assigned +% just right now will be affected by the split. The result of the +% split will be an empty box that will temporarily overwrite box~2 +% within the group, but will be restored back to the split result on +% exit. In this manner, any topskip glue will have disappeared. +% After the split, box~2 is set to the natural depth and height of +% its contents. +% +% We now add a sort of strut by putting all the discarded material +% inside of a box that creates the proper size. +% If this split is taken, the box is adjusted to have a full depth of +% \cmd{\splitmaxdepth}, and we take this into account. +% \begin{macrocode} + \fi + \ht4-\dp\tw@ + \dp4\ifdim\dp\tw@<\splitmaxdepth \splitmaxdepth \else \dp\tw@ \fi + \advance\FN@myvsize\dimexpr \ht\tw@+\dp4\relax + \unvbox\tw@ + \nointerlineskip + \box4 } +% \end{macrocode} +% \end{macro} +% \begin{macro}{\FN@uncontribute@tw@} +% This is just the opposite: after a split, we revert its effects again. +% \begin{macrocode} +\def\FN@uncontribute@tw@{% + \ifvoid\tw@ \else + \setbox\tw@\vbox{\unvbox\tw@\splitdiscards}% + \setbox\z@\vbox{\break\unvbox\z@}% + {\splittopskip-\maxdimen \setbox\z@\vsplit\z@ to\z@}% + \setbox\z@\vbox{\unvbox\tw@\unvbox\z@}\fi} +% \end{macrocode} +% \end{macro} +% \begin{macro}{\FN@reconfig} +% This reconfigures the insertion cache to contain only the boxes +% that belong to this page. If the insertion box is empty, we can +% skip all the folderol. If it isn't, we empty the cache box (the +% number of which we place in \cmd{\count@}) and add its size back +% to \cmd{\FN@vsize}. +% \begin{macrocode} +\def\FN@reconfig#1#2{\ifvoid#2% +% \ifvoid\FN@cache#2\else +% \errmessage{\FN@cache#2 should be void}\fi + \else + \count@\FN@cache#2% + \ifvoid\count@\else + \global\advance\FN@vsize + \glueexpr(\ht\count@+\dp\count@)*\count#2/\@m+\skip#2\relax + \global\setbox\count@ \box\voidb@x + \fi +% \end{macrocode} +% Ok, now we have emptied the cache and readjusted the size. We now +% fill the cache by first copying the insertion into it. +% \begin{macrocode} + \global\setbox\count@\vbox\bgroup\vbox\bgroup\unvcopy#2% + \let\@elt\FN@removecheck + \FN@retainkept +% \end{macrocode} +% Now if nothing was retained, we void the cachebox. +% \begin{macrocode} + \ifvoid\z@ \egroup\egroup \global\setbox\count@ \box\voidb@x +% \end{macrocode} +% Otherwise, we combine all the boxes that remain on the page. +% \begin{macrocode} + \else \def\FN@masterinsert{#2}% + \FN@assembleboxes\global\setbox\count@\box\z@\egroup + \nointerlineskip\box\count@\egroup +% \end{macrocode} +% Note that now all footnote boxes are collected into a single vbox, +% followed by the last footnote box as another vbox. +% Now we just need to reduce the available size on the page by the +% height of the assembled material: +% \begin{macrocode} + \global\advance\FN@vsize + -\glueexpr(\ht\count@+\dp\count@)*\count#2/\@m+\skip#2\relax + \fi\fi} +% \end{macrocode} +% \end{macro} +% \begin{macro}{\FN@rejoin} +% This glues together cache boxes that have been split, without +% regenerating them. This saves a lot of time as compared to +% \cmd{\FN@reconfig}. +% \begin{macrocode} +\def\FN@rejoin#1#2{{% + \count@\FN@cache#2% + \ifvoid\count@\else + \global\advance\FN@vsize\dimexpr + (\ht\count@+\dp\count@)*\count#2/\@m\relax + \global\setbox\count@\vbox{% + \unvbox\count@ + \ifnum\lastpenalty>\z@ + \unpenalty + \setbox\tw@\lastbox + \setbox\z@\lastbox + \dimen@\dp\z@ + \setbox\z@\vbox{% + \unvbox\z@ + \setbox\z@\lastbox + \unvbox\z@ + \unvbox\tw@}% + \ht\z@=\dimexpr\ht\z@+\dp\z@-\dimen@\relax + \dp\z@=\dimen@ + \nointerlineskip + \box\z@ + \fi}% + \global\advance\FN@vsize-\dimexpr + (\ht\count@+\dp\count@)*\count#2/\@m\relax + \fi}} +% \end{macrocode} +% \end{macro} +% +% \begin{macro}{\FN@retainkept} +% This relies on \cmd{\@elt} being set to \cmd{\FN@removecheck} which +% expands to \cmd{\@ne} if |\box0| is strictly between the two values +% from an entry of \cmd{\FN@config}, which means that it is material +% that should get moved to the next page. In that case, we recurse +% while dropping the box in question. Otherwise we keep it. +% Recursion bottoms out when there are no boxes left. The function +% leaves the last retained box in box~0; if there are no boxes to be +% retained, this will be void. +% \begin{macrocode} +\def\FN@retainkept{% + \setbox\z@\lastbox + \ifcase + \ifvoid\z@\m@ne\fi \FN@config\z@ +% \if\foottrace8\message{^^J\string\FN@retainkept: +% retaining box \FN@slotget{\number\dp\z@}}\fi +% \if\foottrace{16}{\showboxdepth4 \showboxbreadth400 +% \tracingonline=\@ne\showbox\z@}\fi + {\FN@retainkept \nointerlineskip \box\z@}% + \or +% \if\foottrace8\message{^^J\string\FN@retainkept: +% dropping box \FN@slotget{\number\dp\z@}}\fi +% \if\foottrace{16}{\showboxdepth4 \showboxbreadth400 +% \tracingonline=\@ne\showbox\z@}\fi + \FN@retainkept +% \else +% \ifnum\lastnodetype>\m@ne +% \errmessage{Unexpected node \number\lastnodetype}\fi + \fi} +% \end{macrocode} +% \end{macro} +% Well, as the last measure, we change the output routine to our new +% routine. +% \begin{macrocode} +\let\output\FN@output +% \end{macrocode} +% Ok, here is debugging code intercepting all calls of the regular +% output routine and reporting its entry and exit states. +% \begin{macrocode} +% \newtoks\FN@tr@output +% \FN@tr@output\output +% \output{\if\foottrace8{% +% \setbox\z@\vbox{% +% \message{Calling regular output with +% \outputpenalty=\the\outputpenalty, box255 as}% +% \showbox\@cclv +% \the\FN@tr@output +% \message{Returning from regular output with +% \ifnum\lastnodetype<\z@ +% empty vertical list.\else vlist:}}% +% \showbox\z@ +% \unvbox\z@{{\fi}}}\else\the\FN@tr@output\fi} +% \let\output\FN@tr@output +% \end{macrocode} +% If the footnote type ``default'' has not been declared by the time +% the document starts, we do so at the start of the document. +% Unfortunately, by this time the initialization code in |manyfoot|'s +% own \cmd{\AtBeginDocument} hook has already run, so we manually run +% the initialization hook just for the command we inserted ourselves. +% \begin{macrocode} +\def\FN@maybestart#1#2#3{\ifx#3\relax + \csname MFL@start#1\endcsname{#2}\fi#3} +\@onlypreamble\FN@maybestart +\AtBeginDocument{\@ifundefined{footinsdefault}% + {\DeclareNewFootnote[plain]{default}% + {\let\@elt\FN@maybestart + \MFL@list\relax}}{}% + \let\c@footnote\c@footnotedefault + \let\thefootnote\thefootnotedefault + \def\thefootnotedefault{\thefootnote}% +} +% \end{macrocode} +% And since LaTeX's macros are inferior to our own (and would probably +% not match too well), we reroot them to the default footnote style. +% \begin{macrocode} +\def\footnote{\footnotedefault} +\def\footnotemark{\footnotemarkdefault} +\def\footnotetext{\footnotetextdefault} +% +% \end{macrocode} +% +% \section{Various driver files} +% The installer, in case it is missing. If it is to be used via +% |make|, we don't specify an installation path, since +% \begin{quote} +% |make install| +% \end{quote} +% is supposed to cater for the installation itself. +% \begin{macrocode} +% \input docstrip +% \askforoverwritefalse +% \generate{ +% \file{bigfoot.drv}{\from{bigfoot.dtx}{driver}} +% \file{perpage.drv}{\from{perpage.dtx}{driver}} +% \file{suffix.drv}{\from{suffix.dtx}{driver}} +% \usedir{tex/latex/bigfoot} +% \file{bigfoot.sty}{\from{bigfoot.dtx}{style}} +% \file{perpage.sty}{\from{perpage.dtx}{style}} +% \file{suffix.sty}{\from{suffix.dtx}{style}} +% } +% \endbatchfile +% \end{macrocode} +% \Finale +% \endinput +% Local Variables: +% mode: doctex +% TeX-master: "bigfoot.drv" +% End: -- cgit v1.2.3