% Copyright 2004-2020, Alexander Shibakov % This file is part of SPLinT % % SPLinT 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 3 of the License, or % (at your option) any later version. % % SPLinT 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 SPLinT. If not, see . % multicolumn output: % the general layout of the page is similar to the following, where % the number of columns and the location and the number of `footnote' % columns can be adjusted. %%%%%%%%%%% %%%%%%%%%%% %%%%%%%%%%% %%column1%% %%column2%% %%column3%% %%%%%%%%%%% ----------- %%%%%%%%%%% %%%%%%%%%%% %footnotes% %%%%%%%%%%% \newskip\realfootins \newskip\footglue \newskip\adjskip % skip to improve \vboxes \newcount\linecount \newcount\noofcolumns \newcount\fnotesstart \newcount\fnotesspan \newdimen\columnwidth \newdimen\icgap \newdimen\pagewidth \newdimen\pageheight \newdimen\longvsize % the height of all the columns combined \newdimen\fnoteboxwidth \newdimen\footvboxht % holds the previous value of the height of the % box of footnotes \newbox\partialpage \newbox\currentcolumn \newbox\foothbox % holds the footnotes `in one line' \newbox\foothhbox % holds every footnote in a separate \hbox \newbox\migrantbox {\setbox\migrantbox=\box\migrantbox} \newtoks\fromword \newtoks\toword \newcount\remaindergaps % the number of gaps between columns after the % first `footnote column' \newcount\remaindernonfoots % the number of columns without footnotes % after the columns with footnotes \newif\ifoutputnow \newif\iflastpageup \maxdeadcycles=600 % this should be approximately half the \pageheight in points % to handle the case when \enddoublecolumns is called before the % last doublecolumn page is full % setup the page length and the column count \linecount=53 % number of lines in a page \fnotesstart=1 % start footnotes under the first column \fnotesspan=2 % footnotes span two columns \noofcolumns=2 \realfootins=12pt plus 1pt minus 1pt \footglue=1em plus.2em minus.1em \adjskip=0pt plus 6pt %\topskip=9pt %\normalbottom % one of these must accompany the use of the macros below % so that the columns are aligned % \def\setmcparams{% \columnwidth=\hsize \advance\columnwidth-\noofcolumns\icgap \advance\columnwidth\icgap \divide\columnwidth by\noofcolumns % \advance\linecount\m@ne % \vsize=\baselineskip % \multiply\vsize by \linecount % \advance\vsize by \topskip % \advance\linecount\@ne % \longvsize=\vsize \advance\longvsize by-\topskip \advance\longvsize by\baselineskip \divide\longvsize\baselineskip \multiply\longvsize\baselineskip \count\@cclv\noofcolumns \multiply\longvsize by \count\@cclv \advance\longvsize-\baselineskip \advance\longvsize\topskip } \setmcparams \skip\footins=\realfootins \count\footins=\@m % 1000 \pagewidth=\hsize \pageheight=\vsize % \def\footnote#1{% \edef\@sf{\spacefactor\the\spacefactor}\setfnmark \@sf \insert\footins{\floatingpenalty\@MM % 20000 \setbox\z@=\hbox{\fnfsize\strut\fnmark\localfn\nobreak \enskip{#1}\penalty-10\hskip\footglue}% \setfootnoteheight \box\z@ }% } \def\setfnoteparams{% \fnfsize % \emergencystretch=10pt \hbadness\@M % 10000 \hyphenpenalty\z@ \doublehyphendemerits\z@ \exhyphenpenalty\z@ \finalhyphendemerits\z@ \righthyphenmin\z@ \lefthyphenmin=\tw@ \leftskip\z@\rightskip\z@ \adjdemerits\z@ \uchyph=\@ne \pretolerance=-1 } \let\fnfsize\eightpoint \def\setfootnoteheight{ \global\setbox\foothbox=\hbox{\unhbox\foothbox\unhcopy\z@}% \setbox\@ne=\vbox{ \hsize=\fnoteboxwidth \setfnoteparams \noindent\unhcopy\foothbox \par }% \dimen\z@=\ht\@ne \advance\dimen\z@ by -\footvboxht \global\footvboxht=\ht\@ne \ht\z@=\dimen\z@ \dp\z@=\z@ \global\setbox\foothhbox=\hbox{\copy\z@\unhbox\foothhbox}} \def\makefootnoteparagraph{ \count\@cclv=\z@ \unvbox\footins \makehboxofhboxes \cleanupfootbuffer\readjustfoots \setbox\z@=\hbox{\unhbox\z@ \removehboxes} \setfnoteparams \noindent\unhbox\z@\par } \def\makehboxofhboxes{% \setbox\z@=\hbox{}% \loop \setbox\tw@=\lastbox \ifhbox\tw@ \advance\count\@cclv\@ne \setbox\z@=\hbox{\box\tw@\unhbox\z@}% \repeat } % the macro below admittedly stretches \TeX's stack capacity but it is rather efficient % at removing the \hbox packaging in the correct order. \def\removehboxes{\setbox\z@=\lastbox \ifhbox\z@{\removehboxes}\unhbox\z@ \fi} \def\cleanupfootbuffer{% remove the footnotes that made it to the page % form the buffer \global\setbox\foothhbox=\hbox{\unhbox\foothhbox\cleanupf@otbuffer}} \def\cleanupf@otbuffer{% \ifnum\count\@cclv>\z@ \setbox\z@=\lastbox \advance\count\@cclv\m@ne \let\next=\cleanupf@otbuffer \else \let\next=\relax \fi \next} \def\readjustfoots{% \setbox\@ne=\copy\foothhbox % keep the footnotes that did not make it % in the buffer \setbox3=\vbox{}% \global\setbox\foothbox=\hbox{}% \setbox\@ne=\hbox{\unhbox\@ne\readjustf@ots}% } \def\readjustf@ots{% \setbox\tw@=\lastbox \ifvoid\tw@ \let\next=\readjustf@@ts \else \global\setbox\foothbox=\hbox{\unhbox\foothbox\unhcopy\tw@}% \setbox3=\vbox{\box2\unvbox3}% \let\next=\readjustf@ots \fi \next } \def\readjustf@@ts{% \setbox\tw@=\vbox{ \hsize=\fnoteboxwidth \setfnoteparams \noindent\unhcopy\foothbox\par } \global\footvboxht=\ht\tw@ \dimen\z@=\ht\tw@ \advance\dimen\tw@ by -\ht3 \ifnum\dimen\z@=\z@ \else \global\setbox\footins=\vbox{}% \advance\dimen\z@ by \m@ne sp \ht\footins=\dimen\z@ \dp\footins=\@ne sp % to indicate that this is an % adjustment box for inner (i.e. the ones inserted by \footblock rathar than % as part of the main output routine) footnotes (looked at by the output routine); % if the height of the already contributed footnotes needs to be % adjusted, adjust the height of the \box\footins by that amount \fi } \fnoteboxwidth=\pagewidth % footnotes will spread across the whole page \newskip\dsskip \dsskip\smallskipamount \def\begindoublecols{\begingroup\normalbottom\parindent=0pt \count\footins=\@m \multiply\count\footins by\fnotesspan \skip\footins=\fnotesspan\realfootins \remaindergaps=\noofcolumns \advance\remaindergaps by -\fnotesstart \remaindernonfoots=\remaindergaps \advance\remaindernonfoots by -\fnotesspan \advance\remaindernonfoots\@ne \ifnum\noofcolumns>\@ne % typesetting in one column is an exception, % otherwise ... % first, calculate the intercolumn skip ... \dimen\z@=\pagewidth \dimen\@ne=\columnwidth \multiply\dimen\@ne by\noofcolumns \advance\dimen\z@ by-\dimen\@ne \count\@cclv=\noofcolumns \advance\count\@cclv\m@ne \divide\dimen\z@ by\count\@cclv \count\@cclv=\fnotesspan \advance\count\@cclv\m@ne \dimen\@ne=\columnwidth \multiply\dimen\@ne by\fnotesspan \advance\dimen\@ne by \count\@cclv\dimen\z@ % ... to get the width of the footnote box \fnoteboxwidth=\dimen\@ne \fi % this final modification is needed due to the fact that the whole % page is put in \hbox to\hsize{...; without the modification the % width of the footnote box is not going to match the width of the % page, and when the footnotes span all the columns there will be no % `\hfill' to compensate for arithmetic imprecision \ifnum\fnotesspan=\noofcolumns \fnoteboxwidth=\pagewidth \fi % \output={ \ifvoid\partialpage \else % this is a border case: too much material has been contributed, this routine will be called twice % so output the full page now \normaloutput{\unvbox\partialpage}\lheader\rheader \fi \global\setbox\partialpage=\vbox{\unvbox\@cclv\vskip\dsskip}% \dimen\z@=\longvsize \advance\dimen\z@ by -\noofcolumns\ht\partialpage \ifnum\dimen\z@<\z@ % the amount of material contributed to the current page is more than the % required \vsize (so that \pageshrink would have to be involved); % output the page normally \normaloutput{\unvbox\partialpage}\lheader\rheader \else % move the footnotes into the buffer, recalculate their size \migratefootnotes \fi }% \eject \def\footnoterule{\kern -3\p@ \hrule width \fnoteboxwidth \kern 2.6\p@}% \outputnowfalse % not ready for immediate output, play with dimensions first \lastpageupfalse % not the final page yet \holdinginserts=\@ne \let\thepage\pagesofar \output={\multcolumnout} \hsize=\columnwidth \global\vsize=\pageheight% \global\advance\vsize by-\ht\partialpage% \global\divide\vsize\baselineskip \global\multiply\vsize\baselineskip \global\multiply\vsize\noofcolumns% %\message{starting with vsize at \the\vsize, with pp at \the\ht\partialpage} \global\advance\vsize by-\noofcolumns\topskip \global\advance\vsize by\topskip \global\advance\vsize by\noofcolumns\baselineskip \global\advance\vsize by-\baselineskip %\message{vsize set at \the\vsize} \ifvoid\migrantbox \else \insert\footins{\box\migrantbox}% \fi } \newif\ifpenultimatepage \newif\ifp@nultimatepage \def\enddoublecols{\par % fire the page buidling routine \ifnum\pagetotal>\pagegoal \global\penultimatepagetrue \eject % if the multicolumn output ended just barely after accumulating a full page \fi % but before the page is shipped out, output the page with the standard multicolumn output \dimen7 =\pagegoal \vsize=\longvsize % now recalculate all the dimensions assuming that the footnotes stretch across the whole page \ifnum\footvboxht>\z@ \advance\vsize by \fnotesspan\footvboxht \advance\dimen7 by \fnotesspan\footvboxht \advance\vsize by -\noofcolumns\realfootins \advance\vsize by \fnotesspan\realfootins \advance\dimen7 by -\noofcolumns\realfootins \advance\dimen7 by \fnotesspan\realfootins \fi \fnotesspan=\noofcolumns \fnotesstart=\@ne \remaindergaps=\noofcolumns \advance\remaindergaps by -\fnotesstart \remaindernonfoots=\z@ \fnoteboxwidth=\pagewidth \setbox\z@=\hbox{}\setfootnoteheight % calculate and adjust the height of the footnotes; this introduces an extra \hbox to \foothhbox % remember this when in the output routine for the last page! \advance\vsize by -\noofcolumns\footvboxht \advance\dimen7 by -\noofcolumns\footvboxht \pagegoal=\dimen7 % \lastpageuptrue \ifp@nultimatepage % everything has been output already \global\penultimatepagefalse \global\p@nultimatepagefalse \else \global\penultimatepagefalse \eject \fi \endgroup \holdinginserts=\z@ \global\pagegoal=\pageheight \global\vsize=\pageheight} \def\migratefootnotes{% \ifvoid\footins \else \ifdim\dp\footins=\z@ % it is not a correction \setbox\@ne=\vbox{\unvbox\footins % similar to \makehboxofhboxes \setbox\z@=\hbox{}% \loop \setbox\tw@=\lastbox \ifhbox\tw@ \setbox\z@=\hbox{\unhbox\tw@\unhbox\z@}% \repeat % ... \setbox\foothbox=\hbox{}% \setbox\foothhbox=\hbox{}% \footvboxht=\z@ \setfootnoteheight\global\setbox\migrantbox=\box\z@ } \else {\global\setbox\footins=\box\footins}% \fi \fi } \def\multcolumnout{% \splittopskip=\topskip \splitmaxdepth=\maxdepth \dimen\z@=\pageheight \advance\dimen\z@ by-\ht\partialpage \advance\dimen\z@ by-\dp\partialpage \ifnum\dimen\z@<\baselineskip % another extreme case: at most one line will fit, so % the adjustments to \vsize will not change the page layout % quickly enough resulting in an infinite \deadcycles loop \normaloutput{\unvbox\partialpage}\lheader\rheader \unvbox\@cclv \penalty\lastpenalty \fi \divide\dimen\z@\baselineskip \multiply\dimen\z@\baselineskip %\message{aiming for d0: \the\dimen0} \dimen\@ne=\pagegoal % we claculate the maximum allowable height of the % middle column next \advance\dimen\@ne by-\vsize \advance\dimen\@ne by \fnotesspan\dimen\z@ \divide\dimen\@ne by\fnotesspan %\message{d1: \the\dimen1, d0: \the\dimen0, pageheight: \the\pageheight }% %\message{(vsize: \the\vsize, pagegoal: \the\pagegoal\space ppage: \the\ht\partialpage)^^J^^J}% % \balanceallcolumns % \ifoutputnow \global\fromword=\expandafter{\firstmark} \global\toword=\expandafter{\botmark} \iflastpageup \ifnum\outputpenalty<-9999 % this break was forced (most likely by the \break in \enddouble...) \ifvoid\footins \else \ifdim\dp\footins=\z@ \else {\setbox\footins=\box\footins}% this was a correcion box after which no new real footnotes \fi % were cotributed \fi % a correction (\hbox{}) was added by the \enddouble... command; % this correction will affect \box\footins (since it will appear % as a box in \foothhbox, a box that is not a footnote); % this correction will be translated into an appropriate % adjustment to \box\footins (since the output routine thinks % it is a footnote); however, it does throw off the footnote count in \foothhbox % off by one; kill it here; the footnotes will be inserted from the footins box \setbox\z@=\vbox{\pagesofar}% \unvbox\z@ \penalty\lastpenalty % since we could not touch \foothhbox above (to keep the footnote height calculations correct), % the \footins box is messed up; fix it {\setbox\footins=\box\footins}% \global\setbox\foothhbox=\hbox{}% to keep the footnote counting \global\setbox\foothbox=\hbox{}% consistent \global\footvboxht=\z@ \else \normaloutput\page\lheader\rheader % \global\vsize=\longvsize \global\outputnowfalse \global\holdinginserts=\@ne \fi \else % this is not the last page, preform normal multicolumn output \normaloutput\page\lheader\rheader % \global\vsize=\longvsize \global\outputnowfalse \global\holdinginserts=\@ne \ifpenultimatepage % page broken at a penalty inserted by \enddoublecolumns \global\p@nultimatepagetrue % the next penalty should be removed \fi \fi {\setbox\@cclv=\box\@cclv}% use up \box255 ! \else % do not output yet, see if adjustments worked \ifnum\dimen\tw@>\dimen\@ne \global\advance\vsize by -\fnotesspan pt \else \global\outputnowtrue \global\holdinginserts=\z@ \fi \unvbox\@cclv \ifnum\outputpenalty=\@M % 10000 the break was not at a penalty item \else \ifp@nultimatepage % we already split the page at this penalty \global\p@nultimatepagefalse % let \enddoublecolumns insert the penalty \else \penalty\outputpenalty \fi \fi \fi } % we will change some of the plain TeX macros used by cwebmac \def\pagebody{% \vbox to \pageheight{ \boxmaxdepth=\maxdepth \pagecontents \the\footline \ifr@ggedbottom \vskip\adjskip \fi } } \def\normaloutput#1#2#3{\ifodd\pageno\hoffset=\pageshift\fi \shipout\vbox{ \vbox to\fullpageheight{ \iftitle\global\titlefalse \else\hbox to\pagewidth{\vbox to10pt{}\ifodd\pageno #3\else#2\fi}\fi \vfill#1}} % parameter #1 is the page itself \global\advance\pageno by1} \let\page\pagebody % this is a cwebmac command but it has to be executed here, after the \pagebody macro is changed \def\pagecontents{ \ifvoid\topins \else \unvbox\topins \fi \setbox\z@=\vbox{\thepage}\dimen\z@=\dp\z@ % for \ruggedbottom \unvbox\z@ \ifr@ggedbottom \kern-\dimen\z@\vfil \fi \ifvoid\footins \else \ifnum\dp\footins=\z@ % this is not an adjustment \vskip\realfootins\footnoterule \vbox{\hsize=\pagewidth\makefootnoteparagraph}% \fi \fi } \def\thepage{\unvbox\@cclv} \def\balanceallcolumns{ \tcthr=\vbadness \vbadness=1000000 % set \vbadness to the badness of a overfull box \count\@cclv\z@ % (does not really prevent TeX from reporting one) \setbox\z@=\vbox{\unvcopy\@cclv}% \setbox\tw@=\vbox{}%%% this box is changed globally \b@lanceallcolumns } \def\b@lanceallcolumns{ \advance\count\@cclv\@ne \whichcolumn{\count\@cclv}% \ifinfoots % if the footnotes go under these columns ... \dimen\tw@=\ht\z@ %\message{set d2(=ht0): \the\dimen2, d0: \the\dimen0; vs. ph: \the\pageheight}% \advance\dimen\tw@ by -\remaindernonfoots\dimen\z@ \advance\dimen\tw@ by -\remaindergaps\baselineskip \advance\dimen\tw@ by \remaindergaps\topskip \divide\dimen\tw@ by \fnotesspan %\message{set d2: \the\dimen2}% \let\next=\b@l@nceallcolumns \else % otherwise, split off the columns ... \ifnum\dimen\z@<\topskip \setbox\@ne=\vsplit\z@ to \topskip \else \setbox\@ne=\vsplit\z@ to \dimen\z@ % see a remark below on why this was necessary \fi \wd\@ne=\hsize \setbox\tw@=\vbox{\box\@ne \unvbox\tw@}%%% this box is changed globally \let\next=\b@lanceallcolumns \fi \next } \newcount\tcthr \def\b@l@nceallcolumns{% splitting off the columns after the footnotes start \count\@cclv=\fnotesstart \setbox3=\copy\z@ \setbox5=\vbox{}% \b@l@nc@allcolumns } \def\b@l@nc@allcolumns{ \whichcolumn{\count\@cclv}% \iflastcolumn %\message{box3 height: \the\ht3, box3 depth: \the\dp3, d0: \the\dimen0, d2: \the\dimen2}% \ifnum\ht3>\dimen\ifinfoots \tw@ \else \z@ \fi \advance\dimen\tw@ by \@ne pt \let\next=\b@l@nceallcolumns \else \ifinfoots \else \ifnum\dimen\z@<\topskip \setbox3=\vsplit3 to \topskip \else \setbox3=\vsplit3 to \dimen\z@ \fi \fi %\message{**box3 height: \the\ht3, d0: \the\dimen0, bs: \the\baselineskip, ts: \the\topskip}% \wd3=\hsize \setbox\tw@=\vbox{\box3 \unvbox5 \unvbox\tw@}% %%% \box2 is laterchanged globally \vbadness=\tcthr \let\next=\relax \fi \else \ifnum\dimen\ifinfoots \tw@ \else \z@ \fi<\topskip \setbox4=\vsplit3 to \topskip \else \setbox4=\vsplit3 to \dimen\ifinfoots \tw@ \else \z@ \fi \fi % the code above is a replacement for: % \setbox4=\vsplit3 to \dimen\ifinfoots \tw@ \else \z@ \fi % \ifnum\badness>\@MM \errmessage{\the\badness:\the\vbadness}\fi % the replacement was necessary to avoid `Overfull \vbox ... ' warnings % that (unlike those for underfull boxes) cannot be silenced by raising % \vbadness \wd4=\hsize \setbox5=\vbox{\box4 \unvbox5}% \advance\count\@cclv\@ne \let\next=\b@l@nc@allcolumns \fi \next } \def\pagesofar{% \unvbox\partialpage \count\@cclv\z@ \hbox to\pagewidth{\p@gesofar}% \kern-\prevdepth \vskip\adjskip } \def\p@gesofar{\advance\count\@cclv\@ne \whichcolumn{\count\@cclv}% \ifinfoots \let\next=\footblock \else \iflastcolumn \getnextcolumn\box\currentcolumn \let\next=\relax \else \getnextcolumn\box\currentcolumn\hfil \let\next=\p@gesofar \fi \fi \next } \def\footblock{% \hbox to \fnoteboxwidth{\f@otblock}% \count\@cclv=\fnotesstart \advance\count\@cclv by\fnotesspan \advance\count\@cclv\m@ne \whichcolumn{\count\@cclv}% \iflastcolumn \let\next=\relax \else \hfil \let\next=\p@gesofar \fi \next } \def\f@otblock{% \whichcolumn{\count\@cclv}% \ifinfoots \iffirstfootcol \setbox\z@=\rlap{\vbox{ \ifvoid\footins \else \ifdim\dp\footins=\z@ % footnote info is present \vskip\realfootins\footnoterule \vbox{\hsize=\fnoteboxwidth\makefootnoteparagraph} \fi \fi }}% \dimen5=\ht\z@ \dimen6=\dimen5 \advance\dimen6 by\dimen\tw@ \setbox\z@=\vbox{ \getnextcolumn\unvbox\currentcolumn\kern-\dimen3 \vskip\adjskip \box\z@ }% \ifnum\ht\z@>\baselineskip \setbox\z@=\vbox to \dimen\iflastpageup 6 \else \z@ \fi{\unvbox\z@}% \fi \wd\z@=\hsize \box\z@ \else \hfil\setbox\z@=\vbox{ \getnextcolumn\unvbox\currentcolumn\kern-\dimen3 \vskip\adjskip \hrule width\z@ height \dimen5 depth\z@ }% \ifnum\ht\z@>\baselineskip \setbox\z@=\vbox to \dimen\iflastpageup 6 \else \z@ \fi{\unvbox\z@}% \fi \wd\z@=\hsize \box\z@ \fi \iflastcolumn \let\next=\relax \else \let\next=\f@otblock \fi \else \let\next=\relax \fi \advance\count\@cclv\@ne \next } \def\getnextcolumn{% \global\setbox\tw@=\vbox{\unvcopy\tw@ \global\setbox\currentcolumn=\lastbox}% \dimen3=\dp\currentcolumn } \newif\ifinfoots \newif\iflastcolumn \newif\ifbeforefoots \newif\iffirstfootcol \def\whichcolumn#1{% \infootsfalse \lastcolumnfalse \beforefootsfalse \firstfootcolfalse \ifnum#1<\fnotesstart \beforefootstrue \else \ifnum#1=\fnotesstart \firstfootcoltrue \infootstrue \else \advance\fnotesstart by\fnotesspan \ifnum#1<\fnotesstart \infootstrue \fi \advance\fnotesstart by-\fnotesspan \fi \ifnum#1=\noofcolumns \lastcolumntrue \fi \fi } % footnote counting macros \let\footthenotes\empty \footline{% \footthenotes % so the footnotes know where to reset \ifchapterhead \vbox to 0pt{ \hbox to\pagewidth{\hfil \mainfont\vrule width 0pt height 2pc\oldstyle\the\pageno \hfil} \vss }% \global\chapterheadfalse \fi } \def\fnmark#1{{% \ifnum#1<\@M \setbox\z@=\hbox{\rm)}\vbox to \ht\z@{\hbox{$\scriptstyle\the#1$}\vss}\box\z@ \else \setbox\z@=\hbox{\rm)}\setbox2=\hbox{$\scriptstyle0$}% \setbox\@ne=\hbox{\vrule width\wd2 height\ht2}% \vbox to \ht\z@{\box\@ne\vss}\box\z@ \fi}} \def\addfnlabel{\expandafter\fnstream\expandafter{\the\fnstream*}} \def\addpgendlabel{\expandafter\fnstream\expandafter{\the\fnstream|}} % the commands above will be exectuted when the .ftn file is loaded below \newtoks\fnstream % locations of footnotes \fnstream={} % before processing the footnotes one by one we have to open an % auxiliary file (we have to read it in whole because it will be % rewritten as the document is being typeset); this opening has to be % done carefully for the file might not be there yet; check for it first. % as a side note, one should not use the same stream number (at least the % one allocated by a \new... command) for both the input and the output % streams, since one might accidentally close a different stream that % happened to have the same index but allocated by a \new... command as a % stream of a different type. \newread\tryfnlabels % to check if the file exists \newwrite\fnlabels \newcount\localfn % footnote count \newcount\fniteration \def\setupfootnotes{% this macro should be used at the beginning of the file, after a box (preferably) \let\footthenotes\markbottomofpage \openin\tryfnlabels=\jobname.ftn\relax \ifeof\tryfnlabels \closein\tryfnlabels\relax \fniteration\z@ \else \closein\tryfnlabels\relax \input \jobname.ftn\relax \fi \edef\savedfnstream{\the\fnstream}% % now we open the same file for writing ... \openout\fnlabels=\jobname.ftn\relax \advance\fniteration\@ne \write\fnlabels{\noexpand\fniteration\the\fniteration\space}% } \def\markbottomofpage{% \write\fnlabels{\noexpand\addpgendlabel}% } \def\unsetfootnotes{% \def\footthenotes{% \write\fnlabels{\noexpand\endinput}% \closeout\fnlabels \global\let\footthenotes\checkfootnotestream% % it is tempting to say \input \jobname.ftn % at this point to use in the test below but this is not % going to work: the \closeout will get invoked during the % \shipout, which happens after the page is set and these % macros are expanded. % note that using another `stream' sequence for this purpose % would not work either, since a footnote at the bottom of the % page might `fire' but would be put on the next page by % the epage builder. }% } \def\checkfootnotestream{% this needs at least one extra page to work correctly ... \fnstream{}% \input\jobname.ftn \edef\next{\the\fnstream}% \ifx\next\savedfnstream \message{Footnotes are stable ...}% \else % this test is too strict, a better way would be to check that % \next is a prefix in \savedfnstream and the remaining characters are all `|' \message{Footnotes may be set incorrectly ...}% \fi \global\let\footthenotes\empty } \def\setfnmark{{% \ifnum\localfn<\@M \getfntok \if*\nexttok \global\advance\localfn\@ne \else \global\localfn=\@M \fi \fi \removewhitespace\kern2pt\fnmark\localfn \write\fnlabels{\noexpand\addfnlabel}% }} \def\getfntok{\loop\splitfnstream\if|\nexttok\global\localfn=\z@ \repeat} \def\splitfnstream{\edef\tempdefone{\the\fnstream}% \ifx\tempdefone\empty \def\nexttok{?}\message{You may have to reposition the footnotes ...}% \else \expandafter\splitfnstre@m\tempdefone\end \fi } \def\splitfnstre@m#1#2\end{\def\nexttok{#1}\global\fnstream={#2}}