% \iffalse %% %% bigfoot is part of the bigfoot bundle for critical typesetting %% Copyright 2004--2015 David Kastrup %% %% The license notice and corresponding source code for this file are %% contained in bigfoot.dtx. %% % 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{4580} % \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{I.e., footnotes with 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} \ProvidesPackage{bigfoot}[2015/08/30 2.1 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 normal processing makes footnote text macros allow verbatim and % similar. We call this |robust| processing though it is not totally % accurate. This is the default. There is also an option |fragile| % which will not allow this, but may be required for some definitions % of \cmd{\@makefntext}. It turns out that most document classes work % with the `robust' option, but there might be some that fail, and % there might be some footnote-modifying packages that also can cause % failure. % \begin{macrocode} \DeclareOption{robust}{\def\FN@makefncall{\FN@makefnrobust}} \DeclareOption{fragile}{\def\FN@makefncall{\FN@makefnfragile}} \ExecuteOptions{robust} % \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. Now unfortunately the LaTeX team has decided % in 2015 to do its own extended allocation scheme incompatible with % the |etex| package, so we need to guard against this load in case % the new LaTeX allocation scheme is detected. % \end{enumerate} % We need the |manyfoot| package to build on. The |suffix| and % |perpage| package are needed for some small stuff. % \begin{macrocode} \ifx\e@alloc\@undefined \RequirePackage{etex} \fi \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\FN@divert{% \let\MFL@mpinsertsave\MFL@insert \MFL@reinsert \let\MFL@insert\MFL@mpinsert} \def\FN@enddivert{{\let\@elt\MFL@mpreinsert \MFL@list}} \def\MFL@minipage{\ifinner\else \FN@divert\fi} \def\MFL@endminipage{\ifinner\else \FN@enddivert\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} % This trick works like \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{Continuation marks} % % We add a possibility of adding continuation marks. While the box is % assembled, immediately before the break, \cmd{\FN@beforebreak} gets % called, and \cmd{\FN@afterbreak} is called at the top of the % continuing box. % \begin{macrocode} \ifx\FN@beforebreak\@undefined \let\FN@beforebreak\@empty \fi \ifx\FN@afterbreak\@undefined \let\FN@afterbreak\@empty \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\thr@@\@MM\clubpenalty\z@ \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{% \ifdefined\setspace@singlespace \def\baselinestretch{\setspace@singlespace}% \fi \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 from % already set horizontal material. 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. % \begin{macrocode} \ifx\@makefnvbox\@undefined \def\@makefnvbox#1{\vbox{% \@makefnstartbox \clubpenalties\thr@@\@MM\clubpenalty\z@ \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 or 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. % \begin{macrocode} \def\hfootfraction{0.9} \def\vtypefraction{0.7} % \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}% \ifcase \ifdim\FN@vfound>\dimexpr\vtypefraction\p@*\FN@found\relax \@ne\fi \ifdim\dimexpr \ht\z@+\dp\z@>\hfootfraction\dimen@ii \@ne\fi \z@ \or \global\setbox\FN@tempbox\box\tw@ \setbox\z@\@makefnvbox{\let\@makefnbreak\FN@pseudofillbreak {\unhbox\FN@tempbox}\setbox\z@\lastbox\FN@joinhboxes}% \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\FN@divert \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} % Ok, now we do the call to \cmd{\@makefntext} which may occur in % one of several ways, depending on whether the `robust' or the % `fragile' package option got used. % \begin{macrocode} \expandafter \FN@makefncall \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@makefnstart} % This is called in the start of \cmd{\@makefntext}. % \begin{macrocode} \providecommand{\FN@seitenobreak}{\nobreak} \def\FN@makefnstart{% % \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\FN@masterinsert\endcsname {\the\footnotesep}% \expandafter\xdef\csname FN@dp\number\FN@masterinsert\endcsname {\the\dp\strutbox}% \expandafter\xdef\csname FN@wd\number\FN@masterinsert\endcsname {\the\hsize}% % \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 % \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 \FN@seitenobreak \afterassignment\ignorespaces} % \end{macrocode} % \end{macro} % \begin{macro}{\FN@makefnrobust} % 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. % % Furthermore, this 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@makefnrobust#{% \FN@specific\FN@masterinsert\@makefntext \iffalse\fi \bgroup \aftergroup\FN@robustending \FN@makefnstart \let\next} % \end{macrocode} % \end{macro} % \begin{macro}{\FN@robustending} % Here we put in the missing part of \cmd{\@makefntext}. % \begin{macrocode} \def\FN@robustending{% \expandafter\expandafter\expandafter \expandafter\expandafter\expandafter\expandafter \iffalse \FN@specific\FN@masterinsert\@makefntext\fi \FN@commonending} % \end{macrocode} % \end{macro} % \begin{macro}{\FN@makefnfragile} % This is the escape route when the robust variant does not work. % In that case, |\verb| and similar won't work in footnotes. % \begin{macrocode} \long\def\FN@makefnfragile#1{% \FN@specific\FN@masterinsert\@makefntext {\FN@makefnstart#1\FN@commonending}} % \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 \global\let\FN@colorstack\FN@savecolorstack} \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{% \@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@}% \ifdim\lastkern=\z@ \let\FN@next\@empty\else \edef\FN@next{\kern\the\lastkern\relax}\unkern \fi \marks\FN@slave{\number\FN@id}% \expandafter\FN@enddivert\expandafter\FN@colorstackegroup \FN@next } % \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 Id \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 \expandafter\FN@retaindelayed \or % \if\foottrace8\message{^^J\string\FN@retaindelayed: % retaining Id \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{\z@skip}% \expandafter\def\csname FN@dp\number\FN@savebox\endcsname{\maxdepth} \expandafter\def\csname FN@wd\number\FN@savebox\endcsname{\columnwidth} \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 open interval. In this case it is not to % appear on the current page. % \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=\@M \vfuzz=\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\FootnoteMinimum{1sp} \def\FootnoteMainMinimum{0pt} \expandafter\def\csname\string\FootnoteMinimum\number\FN@savebox \endcsname{\FootnoteMainMinimum} % \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} % % We are trying to do bookkeeping on the effects of page size for % insertions that fall into the footnote class. While we do basic % bookkeeping for other insertions as well, this can only be % incomplete since we don't reinsert material. In consequence, % multiple material ending up in the same insertion might cause the % corresponding skip register to be accounted for several times. % \LaTeX\ does not really reuse insertions in that manner except for % footnotes, so we are mostly ok here. % % |bigfoot| usually does some lookahead in the main list in order to % obtain optimal breakpoints. It explicitly undoes the effect this % has on marks, but insertions are a different matter here. So floats % may appear on an earlier page than expected. % % 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 % they 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} \newcount\FN@outputflag \FN@outputflag=3158345 \output{% \let\@elt\relax \ifvoid\@cclv \PackageError{bigfoot}{Empty box 255 in \output}\fi % \if\foottrace8% % \message{entering output with % \outputpenalty=\the\outputpenalty:}% % {\showboxdepth4\showboxbreadth\maxdimen\showbox\@cclv}\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 \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 \FN@nest@iterate{\FN@insertouterspace\global\FN@outervsize \dimexpr\z@}% \global\advance\FN@outervsize\ht\@cclv \global\setbox\@cclv\vbox{\unvbox\@cclv\boxmaxdepth\maxdepth}% \global\let\FN@vsadjustlist\@empty % \end{macrocode} % \cmd{\FN@outervsize} now contains the value of \cmd{\pagegoal} at % the time of output. It should be \cmd{\vsize} adjusted by the % natural size of insertions. Note that \cmd{\FN@normaloutput} is not % required to return with a sensible value of \cmd{\outputpenalty}. % \begin{macrocode} \FN@normaloutput \else % \end{macrocode} % We now are in the situation that we already have collected material % previously. 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} \FN@nest@iterate{\FN@insertouterspace\global\advance\FN@outervsize \dimexpr\ht\@cclv}% \global\setbox\@cclv\vbox{\unvbox\@cclv\boxmaxdepth\maxdepth}% % \end{macrocode} % Now we invalidate the cache boxes for all insertions that had % changed due to the recent additions to the page (this does % \emph{not} affect \cmd{\FN@vsize}). % \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 % \end{macrocode} % \cmd{\outputpenalty} is restored to the original value before the % total page is glued together. % \begin{macrocode} \FN@vsadjustlist % \if\foottrace8\message{receiving special penalty % \FN@outputpenalty, dissing box 255:}% % {\showboxdepth4 \showboxbreadth400 % \tracingonline=\@ne\showbox\@cclv}\fi % \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 \@pageht-\vsize \let\@currbox\footins \@reinserts \global\vsize-\@pageht \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 (if it belongs between those boxes, otherwise we leave it % on the list) 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} %\if\foottrace8 % \message{Box 255 before reglue % (outputpenalty=\the\outputpenalty):}% % {\showboxdepth4\showboxbreadth100\showbox\@cclv}\fi \global\setbox\@cclv\vbox{% % \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. If the current page was empty except % for insertions, this means that we gain a new breakpoint. But % insertions with discardable material before them would be unusual. % % The only exception to this may happen if the current page contained % only insertions: in this case \TeX\ has made a page break before the % actually inserted \cmd{\topskip} glue (which will then arrive one % page later). % % Note that the pagediscards contain material corresponding to the % last breakpoint chosen, so they will either start with a penalty % of~10000 (which is what an actual outputpenalty gets replaced with) % or will start with discardable material. We clean it for that % reason. % \begin{macrocode} \unvbox\z@ \global\setbox\@cclv\vbox{\break\unvbox\@cclv}% {\splittopskip-\maxdimen \setbox\z@\vsplit\@cclv to\z@}% \ifnum\outputpenalty=\@M \setbox\z@\vbox{\pagediscards \FN@cleanpagepenalty}% \unvbox\z@ \else \penalty\outputpenalty \pagediscards \fi \unvbox\@cclv \boxmaxdepth\maxdepth}% %\if\foottrace8 % \message{Box 255 reglued (outputpenalty=\FN@outputpenalty):}% % {\showboxdepth4\showboxbreadth100\showbox\@cclv}\fi \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. Note % that we are not reinserting anything in order to compensate for % \cmd{\outputpenalty} being replaced by a nobreak: this is the job of % \cmd{\FN@normaloutput} when it decides to place material back on the % page. That is: when code is written that will make use of % pagediscards, it has to cater for their proper structure. % \begin{macrocode} \ifdim\dimen@>\z@ % \if\foottrace8 % \message{recycling special penalty} % \fi \hrule\@height\z@\@depth\z@ \unvcopy\FN@topmarkbox \penalty-13750 \penalty\FN@outputflag \hrule\@height\z@\@depth\z@ \FN@insertmarks \penalty-13749 \penalty\FN@outputflag \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. % \begin{macrocode} \def\FN@normaloutput{% % \if\foottrace8\message{^^JEntering \string\FN@normaloutput:^^J}\fi % \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} \global\FN@vsize\FN@outervsize \global\advance\FN@vsize-\ht\@cclv\relax\relax % \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{\@elt{\number0\botmarks\FN@slave}% {\number\maxdimen}}% % \if\foottrace8% % \message{\noexpand\FN@normaloutput start config: \FN@config^^J}% % \fi \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. Since the cache registers have % been explicitly cleared, \cmd{\FN@reconfig} starts from the state % where indeed no insertions are present. % \begin{macrocode} \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@ % \end{macrocode} % Ok, now we get the default case in our big routine: case~0. We just % pass the result onto the output routine. % \begin{macrocode} {\vbadness\@M \vfuzz\maxdimen \global\setbox\@cclv\vbox spread\FN@vsize{\unvbox\@cclv\boxmaxdepth\maxdepth}}% \the\FN@output \let\@currbox\footins \@pageht-\vsize \@reinserts \global\vsize-\@pageht \FN@nest@iterate\FN@clearcache \or % \end{macrocode} % Case 1: We just pull in remaining insertions and are done. Note % that the special penalty here will get turned into an explicit % nobreak. So if we have no record of an actual outputpenalty, we % need to insert an artificial penalty of 0 here. % \begin{macrocode} \FN@restartoutput \penalty -13749 \penalty \FN@outputflag \or % \end{macrocode} % Case 2: 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} \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} % This is case 3: Fake our output box into something looking like a % cache box and do the optimal split routine. The output cache box % has a few deficiencies: its inner box is \emph{not} depth-extended % to some default measurement. That means that where page size % calculations are involved, one needs to \emph{disregard} its actual % depth and instead use \cmd{\maxdepth}. This is somewhat awkward and % prone to problems. One alternative might be to mark the box as % split, extend its depth in the split part and let it be followed by % nothing as lower part of the split. But we still would need to % account for the missing depth at the end. % \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@penalties{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 {% \vbadness\@M \vfuzz\maxdimen \global\setbox\@cclv\vbox spread\FN@vsize{% \unvbox\@cclv \ifnum\lastpenalty>\z@ \unpenalty \global\setbox\FN@tempbox\lastbox \else \global\setbox\FN@tempbox\box\voidb@x \fi \setbox\z@\lastbox % \end{macrocode} % Now if a split has been done, |\box\FN@tempbox| contains the lower % part of the split. In either case, |\box\z@| contains the upper % part of the split (in a prepared form with the splitdiscards in a % box of their own). This may be void if there is no main text but % only footnotes. If we have carryover material, we add the current % outputpenalty there and set outputpenalty to a value indicating that % we have no outputpenalty to add at the end of the current list. % \begin{macrocode} \ifvoid\z@ \ifvbox\FN@tempbox \ifnum\outputpenalty<\@M % The output penalty originally from below the split box gets appended % to the end of the split box. \global\setbox\FN@tempbox{\unvbox\FN@tempbox \penalty\outputpenalty}% \fi \global\outputpenalty=\@M \fi \else % \end{macrocode} % Ok, we have material to go to the next page. We unpack it and fish % out the break penalty from the last box. After checking it, put it in % box~0. A prospective current break penalty gets appended to the % carryover material. The fished-out break penalty becomes the new % value of outputpenalty. % \begin{macrocode} \MFL@removevboxes \unvbox\z@ \edef\FN@defaultpenalty{\number\@M}% \FN@getbreakpenalty \setbox\z@\lastbox \global\setbox\FN@tempbox\vbox\bgroup\unvbox\z@ \unvbox\FN@tempbox \ifnum\lastnodetype<\z@ \egroup\global\setbox\FN@tempbox\box\voidb@x \else \ifnum\outputpenalty<\@M \penalty\outputpenalty \fi \egroup \global\outputpenalty\FN@breakpenalty \fi \fi \boxmaxdepth\maxdepth}% }% \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. If box~0 is void, the break was chosen at the % ultimate end of the vertical list. If it was not a forced break, % and if it is not an overfull case already, we pull in more material % in order to avoid widows in the main text. % \begin{macrocode} \dimen@=\dimexpr\FN@vsize-\glueshrink\FN@vsize\relax \ifcase \ifvoid\z@ \ifnum\outputpenalty>-\@M \ifdim\dimen@<\z@ \else \@ne \fi \fi \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@ \let\@currbox\footins \@pageht-\vsize \@reinserts \global\vsize-\@pageht \FN@nest@iterate\FN@clearcache \or \FN@restartoutput \penalty -13751 \penalty\FN@outputflag \unvbox\z@ \ifnum\outputpenalty>\@M \else \penalty \ifnum\outputpenalty=\@M \z@ \else\outputpenalty\fi \fi \fi \fi % \if\foottrace8\message{^^JExiting \string\FN@normaloutput^^J}\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 \penalty\FN@outputflag \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@\@M \FN@getbreakpenaltyii \xdef\FN@tempinfo{\edef\noexpand\FN@breakpenalty{% \number\ifnum\count@=\@M \FN@defaultpenalty \else \count@\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@cleanpagepenalty} % This is used for removing initial infinite penalties from the % pagediscards: those are artifacts of the page break routine. % \begin{macrocode} \def\FN@cleanpagepenalty{% \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 \skip@=\lastskip \unskip \expandafter \FN@cleanpagepenalty \expandafter\vskip\the \expandafter\skip@ \or \dimen@=\lastkern \unkern \expandafter \FN@cleanpagepenalty \expandafter\kern\the \expandafter\dimen@ \or \count@\lastpenalty \unpenalty \ifnum\count@=\FN@outputflag \unpenalty\expandafter\expandafter\expandafter\FN@cleanpagepenalty \else \expandafter\FN@cleanpagepenalty\expandafter \penalty\the\expandafter\expandafter\expandafter\count@ \fi \fi \relax} % \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\@M\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@penalties} contains the accumulated penalities % of the current split configuration: a penalty of $10000$ for any % split (except the main list), plus the penalty at the split points % plus a `hangover' badness for the percentage of material carried % over to the following pages. If nothing is carried over, this % is~0, if more is carried over, we get a penalty according to the % proportion of carryover material, raised to the third power. % \begin{macro}{\footnotecarryratio} % The fractional variable \cmd{\footnotecarryratio} is used for % scaling the leftover material dimensions. After scaling with % \cmd{\footnotecarryratio}, the carried material is treated like % missing material in a glue calculation, while the stretchability % for this calculation is given by the total size of material before % breaking. So with a setting of~1, there should always be enough % stretchability, causing at most a penalty of~100. That's not very % effective, so we scale this up. % % The default value of~2 seems to provide a reasonable penalty for % leftover material. The actual purpose for this component of the % scoring is to penalize footnote blocks that seem to carry over % disproportionally much material to later pages. % \begin{macrocode} \providecommand\footnotecarryratio{2} % \end{macrocode} % \end{macro} % \cmd{\FN@ebadness} is an augmented value, 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@penalties} is globally % tampered with, its value at entry is saved in % \cmd{\FN@entrypenalties}. Whenever we recurse or return, % \cmd{\FN@vsize} contains the full information about the available % space on the page, even though locally we use \cmd{\FN@myvsize}, a % local value, to keep track of the locally reserved space. The % only time when we need to save \cmd{\FN@myvsize} should be when we % temporarily leave boxes in order to save the current % configuration. % \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=\unexpanded\expandafter{\FN@config}^^J}\fi #3\FN@mainsplitreturn % \if\foottrace1% % \message{Page=\thepage #2 was empty, % returning with \the\FN@vsize^^J}% % \message{Config=\unexpanded\expandafter{\FN@config}^^J}\fi \else % \if\foottrace1\message{Entering #2 with \FN@penalties, % \FN@vsize=\the\FN@vsize,^^J% % Config=\unexpanded\expandafter{\FN@config}^^J}\fi {\def\FN@currentinsertion{#2}% \def\FN@currentrecursion{#3}% \let\FN@entryconfig\FN@config \let\FN@entrypenalties\FN@penalties \splittopskip\csname FN@ht\number#2\endcsname\relax \splitmaxdepth\csname FN@dp\number#2\endcsname\relax \hsize\csname FN@wd\number#2\endcsname\relax \vbadness=\@M \vfuzz\maxdimen \let\@elt\relax \expandafter\FN@newleveli\expandafter}% % \if\foottrace1\message{Exiting #2 with \FN@penalties, % \FN@vsize=\the\FN@vsize,^^J% % Config=\unexpanded\expandafter{\FN@config}^^J}\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@ \ifnum\FN@currentinsertion=\FN@savebox +\maxdepth \else +\dp\count@ \fi\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 \cmd{\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=\ifnum\lastnodetype<\z@ \z@ \else \dimexpr\dimen@-\ht\z@-\dp\z@\relax \fi \else % \end{macrocode} % Now if the box has been split previously, we glue it back together % again. Since the lower part of the split has been \emph{subtracted} % from the total in \cmd{\dimen@}, we need to put it back into the % equation here. \cmd{\dimen@} contains the size of the box after % padding split material has been added. % \begin{macrocode} % \if\foottrace1\message{Regluing box 2}\fi \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. % ^^A Fixme: Alpha pruning could be implemented by calling with % ^^A \FN@bestcost already set at entry to the best badness at % ^^A the respective level. % \begin{macrocode} \edef\FN@bestcost{\number\maxdimen}% \let\FN@bestbadness\FN@bestcost \let\FN@bestconfig\@undefined \def\FN@bestvsize{-\maxdimen}% \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. Since we still have all % subordinate footnotes considered fully, we need at least this split % size (in case of a configuration change, we will need more). After % having done the initial split, 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 \edef\FN@defaultpenalty{\number-\@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. If we are on the main vertical list, an % empty page can be an acceptable option, so we add a penalty of zero % to account for that. Note that any prospective true penalties will % already have disappeared into the page break. % \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 \penalty\z@ \unvbox\z@ \ifnum\FN@defaultpenalty>-\@M \penalty\FN@defaultpenalty\relax\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-\splitmaxdepth \relax \ifnum\FN@defaultpenalty>-\@M \setbox\z@\vbox\bgroup\unvbox\z@\setbox\z@\lastbox \unskip \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 haven't reached the last footnote % in the block. % \begin{macrocode} \ifnum\FN@currentinsertion=\FN@savebox \edef\FN@slaveid{\splitbotmarks\FN@slave}% \FN@contribute@tw@ \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@penalties{\number\FN@entrypenalties}% \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, so there is no point in % using \cmd{\FN@penalties} as the deciding factor of whether there may % be a point in recursing: the current ebadness (which is never less % than the badness) already is minimal. % \begin{macrocode} \ifnum \ifdim\skip@>\z@ \FN@ebadness \else \FN@penalties \fi >\FN@bestcost\relax % \if\foottrace1% % \message{no recursion: \skip@=\the\skip@, % \noexpand\FN@bestvsize=\FN@bestvsize, % \noexpand\FN@ebadness=\FN@ebadness, % \noexpand\FN@penalties=\FN@penalties, % \noexpand\FN@bestcost=\FN@bestcost.}\fi \else % \if\foottrace1% % \message{before recursion: \skip@=\the\skip@, % \noexpand\FN@bestvsize=\FN@bestvsize, % \noexpand\FN@ebadness=\FN@ebadness, % \noexpand\FN@penalties=\FN@penalties, % \noexpand\FN@bestcost=\FN@bestcost.^^J % recurse with \noexpand\FN@penalties=\number\FN@entrypenalties.}\fi \xdef\FN@penalties{\number\FN@entrypenalties}% \FN@vsizerecurse \FN@checkcurrent % \if\foottrace1% % \message{after recursion: \skip@=\the\skip@, % \noexpand\FN@bestvsize=\FN@bestvsize, % \noexpand\FN@ebadness=\FN@ebadness, % \noexpand\FN@penalties=\FN@penalties, % \noexpand\FN@bestcost=\FN@bestcost.}\fi \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{macrocode} % \end{macro} % \begin{macro}{\FN@checkcurrent} % Check out the badness of the current configuration. The last box % on the list is the material constituting the discardable material % after a split. % \begin{macrocode} \def\FN@checkcurrent{% \FN@getbreakpenalty \ifnum\FN@breakpenalty<-\@M \edef\FN@breakpenalty{\number-\@M}% \fi \ifnum\FN@currentinsertion=\FN@savebox \else \ifdim\FN@specific\FN@currentinsertion\footnotecarryratio\p@>\z@ \skip@ \ifdim\FN@specific\FN@currentinsertion\footnotecarryratio\p@>\p@ \dimexpr\ht\z@+\dp\z@\relax \@plus-\dimexpr((\FN@myvsize+\ht\z@+\dp\z@) *\p@/\dimexpr \FN@specific\FN@currentinsertion \footnotecarryratio\p@)\relax \else \FN@specific\FN@currentinsertion \footnotecarryratio \dimexpr\ht\z@+\dp\z@\relax \@plus-\dimexpr\FN@myvsize+\ht\z@+\dp\z@\relax \fi \relax \FN@getbadness\skip@ \xdef\FN@penalties{\number\numexpr\FN@penalties+\badness}% \fi \fi \skip@\glueexpr\FN@vsize-\FN@myvsize *\count\FN@currentinsertion/\@m\relax \FN@getbadness\skip@ \xdef\FN@penalties{\number\numexpr\FN@penalties+% \FN@breakpenalty+\@M}% \ifnum\badness>\@M \edef\FN@ebadness{\number\numexpr\maxdimen-\@ne}% \else \ifnum\badness=\@M \ifdim\skip@<\vsize \edef\FN@ebadness{\number\numexpr\maxdimen-\tw@}% \else \edef\FN@ebadness{\number\numexpr\maxdimen}% \fi \else \edef\FN@ebadness{\number\numexpr \FN@penalties+\badness \ifdim\FN@specific\FN@currentinsertion\FootnoteMinimum>\FN@myvsize 1000000 \fi }% \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@bestcost>\FN@ebadness \@ne\fi \ifnum\FN@bestcost<\FN@ebadness \tw@\fi \ifdim\skip@<\z@ \ifdim\dimen@<\skip@ \@ne \fi \tw@ \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@bestcost{\FN@ebadness}% \def\noexpand\FN@bestbadness{\number\FN@penalties}% \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}% \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@bestcost{\FN@ebadness}% \def\noexpand\FN@bestbadness{\number\FN@penalties}% \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@penalties\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{% \prevdepth\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 \FN@specific\FN@currentinsertion\FN@afterbreak \nobreak \unvbox\FN@tempbox}% \FN@specific\FN@currentinsertion\FN@beforebreak \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\prevdepth<\splitmaxdepth \hrule\@height-\prevdepth \@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. Please % note that the \cmd{\expandafter} chain will expand just % |\cmd\dimen@|, but everything following it will be evaluated only % after \cmd{\egroup}, thus using the new height of the 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{{% % \if\foottrace1\message{^^JRejoining #2}\fi \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 Id \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 Id \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}% {\newfootnote[plain]{default}% {\let\@elt\FN@maybestart \MFL@list\relax}% }{}% % \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\@footnotetext{\Footnotetextdefault{}}% \def\p@footnotedefault{\p@footnote}% } % % \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 \nopreamble % \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: