% \begin{meta-comment} % % $Id: footnote.dtx,v 1.13 1997/01/28 19:45:16 mdw Exp $ % % Save footnotes around boxing environments and things % % (c) 1996 Mark Wooding % %----- Revision history ----------------------------------------------------- % % $Log: footnote.dtx,v $ % Revision 1.13 1997/01/28 19:45:16 mdw % Fixed stupid bug in AMS environment handling which stops the thing from % working properly if you haven't included amsmath. Doh. % % Revision 1.12 1997/01/18 00:45:37 mdw % Fix problems with duplicated footnotes in broken AMS environments which % typeset things multiple times. This is a nasty kludge. % % Revision 1.11 1996/11/19 20:50:05 mdw % Entered into RCS % % % \end{meta-comment} % % \begin{meta-comment} %% %% footnote package -- Save footnotes around boxing environments %% Copyright (c) 1996 Mark Wooding %<*package> %% %% 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., 675 Mass Ave, Cambridge, MA 02139, USA. % %% % \end{meta-comment} % % \begin{meta-comment} %<+package>\NeedsTeXFormat{LaTeX2e} %<+package>\ProvidesPackage{footnote} %<+package> [1997/01/28 1.13 Save footnotes around boxes] % \end{meta-comment} % % \CheckSum{327} %\iffalse %<*package> %\fi %% \CharacterTable %% {Upper-case \A\B\C\D\E\F\G\H\I\J\K\L\M\N\O\P\Q\R\S\T\U\V\W\X\Y\Z %% Lower-case \a\b\c\d\e\f\g\h\i\j\k\l\m\n\o\p\q\r\s\t\u\v\w\x\y\z %% Digits \0\1\2\3\4\5\6\7\8\9 %% Exclamation \! Double quote \" Hash (number) \# %% Dollar \$ Percent \% Ampersand \& %% Acute accent \' Left paren \( Right paren \) %% Asterisk \* Plus \+ Comma \, %% Minus \- Point \. Solidus \/ %% Colon \: Semicolon \; Less than \< %% Equals \= Greater than \> Question mark \? %% Commercial at \@ Left bracket \[ Backslash \\ %% Right bracket \] Circumflex \^ Underscore \_ %% Grave accent \` Left brace \{ Vertical bar \| %% Right brace \} Tilde \~} %% %\iffalse % %\fi % % \begin{meta-comment} % %<*driver> \input{mdwtools} \describespackage{footnote} \mdwdoc % % % \end{meta-comment} % % \section{User guide} % % This package provides some commands for handling footnotes slightly % better than \LaTeX\ usually does; there are several commands and % environments (notably |\parbox|, \env{minipage} and \env{tabular} % \begin{footnote} % The \package{mdwtab} package, provided in this distribution, handles % footnotes correctly anyway; it uses an internal version of this package % to do so. % \end{footnote}) % which `trap' footnotes so that they can't escape and appear at the bottom % of the page. % % \DescribeEnv{savenotes} % The \env{savenotes} environment saves up any footnotes encountered within % it, and performs them all at the end. % % \DescribeMacro{\savenotes} % \DescribeMacro{\spewnotes} % If you're defining a command or environment, you can use the |\savenotes| % command to start saving up footnotes, and the |\spewnotes| command to % execute them all at the end. Note that |\savenotes| and |\spewnotes| % enclose a group, so watch out. You can safely nest the commands and % environments -- they work out if they're already working and behave % appropriately. % % \DescribeEnv{minipage*} % To help things along a bit, the package provides a $*$-version of the % \env{minipage} environment, which doesn't trap footnotes for itself (and % in fact sends any footnotes it contains to the bottom of the page, where % they belong). % % \DescribeMacro{\makesavenoteenv} % The new \env{minipage$*$} environment was created with a magic command % called |\makesavenoteenv|. It has a fairly simple syntax: % % \begin{grammar} % ::= \[[ % "\\makesavenoteenv" % \begin{stack} \\ "[" "]" \end{stack} % "{" "}" % \]] % \end{grammar} % % Without the optional argument, it redefines the named environment so that % it handles footnotes correctly. With the optional argument, it makes % the new environment named by \ into a footnote-friendly % version of the \ environment. % % \DescribeMacro{\parbox} % The package also redefines the |\parbox| command so that it works properly % with footnotes. % % \DescribeEnv{footnote} % The other problem which people tend to experience with footnotes is that % you can't put verbatim text (with the |\verb| comamnd or the \env{verbatim} % environment) into the |\footnote| command's argument. This package % provides a \env{footnote} \emph{environment}, which \emph{does} allow % verbatim things. You use the environment just like you do the command. % It's really easy. It even has an optional argument, which works the same % way. % % \DescribeEnv{footnotetext} % To go with the \env{footnote} environment, there's a \env{footnotetext} % environment, which just puts the text in the bottom of the page, like % |\footnotetext| does. % % There's a snag with these environments, though. Some other nonstandard % environments, like \env{tabularx}, try to handle footnotes their own % way, because they won't work otherwise. The way they do this is not % compatible with the way that the \env{footnote} and \env{footnotetext} % environments work, and you will get strange results if you try (there'll % be odd vertical spacing, and the footnote text may well be incorrect). % \begin{footnote} % The solution to this problem is to send mail to David Carlisle persuading % him to use this package to handle footnotes, rather than doing it his % way. % \end{footnote} % % \implementation % % \section{Implementation} % % Most implementations of footnote-saving (in particular, that used in % the \package{tabularx} and \package{longtable} packages) use a token % list register to store the footnote text, and then expand it when whatever % was preventing footnotes (usually a vbox) stops. This is no good at all % if the footnotes contain things which might not be there by the time the % expansion occurs. For example, references to things in temporary boxes % won't work. % % This implementation therefore stores the footnotes up in a box register. % This must be just as valid as using tokens, because all I'm going to do % at the end is unbox the box). % % \begin{macrocode} %<*macro|package> \ifx\fn@notes\@@undefined% \newbox\fn@notes% \fi % \end{macrocode} % % I'll need a length to tell me how wide the footnotes should be at the % moment. % % \begin{macrocode} \newdimen\fn@width % \end{macrocode} % % Of course, I can't set this up until I actually start saving footnotes. % Until then I'll use |\columnwidth| (which works in \package{multicol} % even though it doesn't have any right to). % % \begin{macrocode} \let\fn@colwidth\columnwidth % \end{macrocode} % % And now a switch to remember if we're already handling footnotes, % % \begin{macrocode} \newif\if@savingnotes % \end{macrocode} % % % \subsection{Building footnote text} % % I need to emulate \LaTeX's footnote handling when I'm putting the notes % into my box; this is also useful in the verbatim-in-footnotes stuff. % % \begin{macro}{\fn@startnote} % % Here's how a footnote gets started. Most of the code here is stolen % from |\@footnotetext|. % % \begin{macrocode} \def\fn@startnote{% \hsize\fn@colwidth% \interlinepenalty\interfootnotelinepenalty% \reset@font\footnotesize% \floatingpenalty\@MM% Is this right??? \@parboxrestore% \protected@edef\@currentlabel{\csname p@\@mpfn\endcsname\@thefnmark}% \color@begingroup% } % \end{macrocode} % % \end{macro} % % \begin{macro}{\fn@endnote} % % Footnotes are finished off by this macro. This is the easy bit. % % \begin{macrocode} \let\fn@endnote\color@endgroup % \end{macrocode} % % \end{macro} % % % \subsection{Footnote saving} % % \begin{macro}{\fn@fntext} % % Now to define how to actually do footnotes. I'll just add the notes to % the bottom of the footnote box I'm building. % % There's some hacking added here to handle the case that a footnote is % in an |\intertext| command within a broken \package{amsmath} alignment % environment -- otherwise the footnotes get duplicated due to the way that % that package measures equations. % \begin{footnote} % The correct solution of course is to % implement aligning environments in a sensible way, by building the table % and leaving penalties describing the intended format, and then pick that % apart in a postprocessing phase. If I get the time, I'll start working % on this again. I have a design worked out and the beginnings of an % implementation, but it's going to be a long time coming. % \end{footnote} % % \begin{macrocode} \def\fn@fntext#1{% \ifx\ifmeasuring@\@@undefined% \expandafter\@secondoftwo\else\expandafter\@iden% \fi% {\ifmeasuring@\expandafter\@gobble\else\expandafter\@iden\fi}% {% \global\setbox\fn@notes\vbox{% \unvbox\fn@notes% \fn@startnote% \@makefntext{% \rule\z@\footnotesep% \ignorespaces% #1% \@finalstrut\strutbox% }% \fn@endnote% }% }% } % \end{macrocode} % % \end{macro} % % \begin{macro}{\savenotes} % % The |\savenotes| declaration starts saving footnotes, to be spewed at a % later date. We'll also remember which counter we're meant to use, and % redefine the footnotes used by minipages. % % The idea here is that we'll gather up footnotes within the environment, % and output them in whatever format they were being typeset outside the % environment. % % I'll take this a bit at a time. The start is easy: we need a group in % which to keep our local definitions. % % \begin{macrocode} \def\savenotes{% \begingroup% % \end{macrocode} % % Now, if I'm already saving footnotes away, I won't bother doing anything % here. Otherwise I need to start hacking, and set the switch. % % \begin{macrocode} \if@savingnotes\else% \@savingnotestrue% % \end{macrocode} % % I redefine the |\@footnotetext| command, which is responsible for adding % a footnote to the appropriate insert. I'll redefine both the current % version, and \env{minipage}'s specific version, in case there's a nested % minipage. % % \begin{macrocode} \let\@footnotetext\fn@fntext% \let\@mpfootnotetext\fn@fntext% % \end{macrocode} % % I'd better make sure my box is empty before I start, and I must set up % the column width so that later changes (e.g., in \env{minipage}) don't % upset things too much. % % \begin{macrocode} \fn@width\columnwidth% \let\fn@colwidth\fn@width% \global\setbox\fn@notes\box\voidb@x% % \end{macrocode} % % Now for some yuckiness. I want to ensure that \env{minipage} doesn't % change how footnotes are handled once I've taken charge. I'll store the % current values of |\thempfn| (which typesets a footnote marker) and % |\@mpfn| (which contains the name of the current footnote counter). % % \begin{macrocode} \let\fn@thempfn\thempfn% \let\fn@mpfn\@mpfn% % \end{macrocode} % % The \env{minipage} environment provides a hook, called |\@minipagerestore|. % Initially it's set to |\relax|, which is unfortunately unexpandable, so if % I want to add code to it, I must check this possibility. I'll make it % |\@empty| (which expands to nothing) if it's still |\relax|. Then I'll % add my code to the hook, to override |\thempfn| and |\@mpfn| set up by % \env{minipage}. % % Note that I can't just force the |mpfootnote| counter to be equal to % the |footnote| one, because \env{minipage} clears |\c@mpfootnote| to zero % when it starts. This method will ensure that even so, the current counter % works OK. % % \begin{macrocode} \ifx\@minipagerestore\relax\let\@minipagerestore\@empty\fi% \expandafter\def\expandafter\@minipagerestore\expandafter{% \@minipagerestore% \let\thempfn\fn@thempfn% \let\@mpfn\fn@mpfn% }% \fi% } % \end{macrocode} % % \end{macro} % % \begin{macro}{\spewnotes} % % Now I can spew out the notes we saved. This is a bit messy, actually. % Since the standard |\@footnotetext| implementation tries to insert funny % struts and things, I must be a bit careful. I'll disable all this bits % which start paragraphs prematurely. % % \begin{macrocode} \def\spewnotes{% \endgroup% \if@savingnotes\else\ifvoid\fn@notes\else\begingroup% \let\@makefntext\@empty% \let\@finalstrut\@gobble% \let\rule\@gobbletwo% \@footnotetext{\unvbox\fn@notes}% \endgroup\fi\fi% } % \end{macrocode} % % \end{macro} % % Now make an environment, for users. % % \begin{macrocode} \let\endsavenotes\spewnotes % \end{macrocode} % % That's all that needs to be in the shared code section. % % \begin{macrocode} % %<*package> % \end{macrocode} % % % \subsection{The \env{footnote} environment} % % Since |\footnote| is a command with an argument, things like \env{verbatim} % are unwelcome in it. Every so often someone on |comp.text.tex| moans % about it and I post a nasty hack to make it work. However, as a more % permanent and `official' solution, here's an environment which does the % job rather better. Lots of this is based on code from my latest attempt % on the newsgroup. % % I'll work on this in a funny order, although I think it's easier to % understand. First, I'll do some macros for reading the optional argument % of footnote-related commands. % % \begin{macro}{\fn@getmark} % % Saying \syntax{"\\fn@getmark{""}{""}"} will read % an optional argument giving a value for the footnote counter; if the % argument isn't there, the \ is executed, and it's expected % to set up the appropriate counter to the current value. The footnote % marker text is stored in the macro |\@thefnmark|, as is conventional for % \LaTeX's footnote handling macros. Once this is done properly, the % \ is called to continue handling things. % % Since the handling of the optional argument plays with the footnote % counter locally, I'll start a group right now to save some code. Then I'll % decide what to do based on the presence of the argument. % % \begin{macrocode} \def\fn@getmark#1#2{% \begingroup% \@ifnextchar[% {\fn@getmark@i{#1}}% {#1\fn@getmark@ii{#2}}% } % \end{macrocode} % % There's an optional argument, so I need to read it and assign it to the % footnote counter. % % \begin{macrocode} \def\fn@getmark@i#1[#2]{% \csname c@\@mpfn\endcsname#2% \fn@getmark@ii% } % \end{macrocode} % % Finally, set up the macro properly, and end the group. % % \begin{macrocode} \def\fn@getmark@ii#1{% \unrestored@protected@xdef\@thefnmark{\thempfn}% \endgroup% #1% } % \end{macrocode} % % \end{macro} % % From argument reading, I'll move on to footnote typesetting. % % \begin{macro}{\fn@startfntext} % % The |\fn@startfntext| macro sets everything up for building the footnote % in a box register, ready for unboxing into the footnotes insert. The % |\fn@prefntext| macro is a style hook I'll set up later. % % \begin{macrocode} \def\fn@startfntext{% \setbox\z@\vbox\bgroup% \fn@startnote% \fn@prefntext% \rule\z@\footnotesep% \ignorespaces% } % \end{macrocode} % % \end{macro} % % \begin{macro}{\fn@endfntext} % % Now I'll end the vbox, and add it to the footnote insertion. Again, I % must be careful to prevent |\@footnotetext| from adding horizontal mode % things in bad places. % % \begin{macrocode} \def\fn@endfntext{% \@finalstrut\strutbox% \fn@postfntext% \egroup% \begingroup% \let\@makefntext\@empty% \let\@finalstrut\@gobble% \let\rule\@gobbletwo% \@footnotetext{\unvbox\z@}% \endgroup% } % \end{macrocode} % % \end{macro} % % \begin{environment}{footnote} % % I can now start on the environment proper. First I'll look for an % optional argument. % % \begin{listing} %\def\footnote{% % \end{listing} % % Oh. I've already come up against the first problem: that name's already % used. I'd better save the original version. % % \begin{macrocode} \let\fn@latex@@footnote\footnote % \end{macrocode} % % The best way I can think of for seeing if I'm in an environment is to % look at |\@currenvir|. I'll need something to compare with, then. % % \begin{macrocode} \def\fn@footnote{footnote} % \end{macrocode} % % Now to start properly. |;-)| % % \begin{macrocode} \def\footnote{% \ifx\@currenvir\fn@footnote% \expandafter\@firstoftwo% \else% \expandafter\@secondoftwo% \fi% {\fn@getmark{\stepcounter\@mpfn}% {\leavevmode\unskip\@footnotemark\fn@startfntext}}% {\fn@latex@@footnote}% } % \end{macrocode} % % Ending the environment is simple. % % \begin{macrocode} \let\endfootnote\fn@endfntext % \end{macrocode} % % \end{environment} % % \begin{environment}{footnotetext} % % I'll do the same magic as before for |\footnotetext|. % % \begin{macrocode} \def\fn@footnotetext{footnotetext} \let\fn@latex@@footnotetext\footnotetext \def\footnotetext{% \ifx\@currenvir\fn@footnotetext% \expandafter\@firstoftwo% \else% \expandafter\@secondoftwo% \fi% {\fn@getmark{}\fn@startfntext}% {\fn@latex@@footnotetext}% } \let\endfootnotetext\endfootnote % \end{macrocode} % % \end{environment} % % \begin{macro}{\fn@prefntext} % \begin{macro}{\fn@postfntext} % % Now for one final problem. The style hook for footnotes is the command % |\@makefntext|, which takes the footnote text as its argument. Clearly % this is utterly unsuitable, so I need to split it into two bits, where % the argument is. This is very tricky, and doesn't deserve to work, % although it appears to be a good deal more effective than it has any right % to be. % % \begin{macrocode} \long\def\@tempa#1\@@#2\@@@{\def\fn@prefntext{#1}\def\fn@postfntext{#2}} \expandafter\@tempa\@makefntext\@@\@@@ % \end{macrocode} % % \end{macro} % \end{macro} % % % \subsection{Hacking existing environments} % % Some existing \LaTeX\ environments ought to have footnote handling but % don't. Now's our chance. % % \begin{macro}{\makesavenoteenv} % % The |\makesavenoteenv| command makes an environment save footnotes around % itself. % % It would also be nice to make |\parbox| work with footnotes. I'll do this % later. % % \begin{macrocode} \def\makesavenoteenv{\@ifnextchar[\fn@msne@ii\fn@msne@i} % \end{macrocode} % % We're meant to redefine the environment. We'll copy it (using |\let|) to % a magic name, and then pass it on to stage~2. % % \begin{macrocode} \def\fn@msne@i#1{% \expandafter\let\csname msne$#1\expandafter\endcsname% \csname #1\endcsname% \expandafter\let\csname endmsne$#1\expandafter\endcsname% \csname end#1\endcsname% \fn@msne@ii[#1]{msne$#1}% } % \end{macrocode} % % Now we'll define the new environment. The start is really easy, since we % just need to insert a |\savenotes|. The end is more complex, since we % need to preserve the |\if@endpe| flag so that |\end| can pick it up. I % reckon that proper hooks should be added to |\begin| and |\end| so that % environments can define things to be done outside the main group as % well as within it; still, we can't all have what we want, can we? % % \begin{macrocode} \def\fn@msne@ii[#1]#2{% \expandafter\edef\csname#1\endcsname{% \noexpand\savenotes% \expandafter\noexpand\csname#2\endcsname% }% \expandafter\edef\csname end#1\endcsname{% \expandafter\noexpand\csname end#2\endcsname% \noexpand\expandafter% \noexpand\spewnotes% \noexpand\if@endpe\noexpand\@endpetrue\noexpand\fi% }% } % \end{macrocode} % % \end{macro} % % \begin{environment}{minipage*} % % Let's define a \env{minipage$*$} environment which handles footnotes % nicely. Really easy: % % \begin{macrocode} \makesavenoteenv[minipage*]{minipage} % \end{macrocode} % % \end{environment} % % \begin{macro}{\parbox} % % Now to alter |\parbox| slightly, so that it handles footnotes properly. % I'm going to do this fairly inefficiently, because I'm going to try and % change it as little as possible. % % First, I'll save the old |\parbox| command. If I don't find a \lit{*}, % I'll just call this command. % % \begin{macrocode} \let\fn@parbox\parbox % \end{macrocode} % % This is the clever bit: I don't know how many optional arguments % Mr~Mittelbach and his chums will add to |\parbox|, so I'll handle any % number. I'll store them all up in my first argument and call myself % every time I find a new one. If I run out of optional arguments, % I'll call the original |\parbox| command, surrounding it with |\savenotes| % and |\spewnotes|. % % \begin{macrocode} \def\parbox{\@ifnextchar[{\fn@parbox@i{}}{\fn@parbox@ii{}}} \def\fn@parbox@i#1[#2]{% \@ifnextchar[{\fn@parbox@i{#1[#2]}}{\fn@parbox@ii{#1[#2]}}% } \long\def\fn@parbox@ii#1#2#3{\savenotes\fn@parbox#1{#2}{#3}\spewnotes} % \end{macrocode} % % \end{macro} % % Done! % % \begin{macrocode} % % \end{macrocode} % % \hfill Mark Wooding, \today % % \Finale % \endinput