1

I thought I'd solved the footnotes-in-tables problem with \makesavenoteenv but it interacts badly with the minted package so I thought, since my problem is really "make footnotes work in my box environment", maybe I can create a new footnote command to queue up footnotes which can be added to the page when the environment ends.

\documentclass{article}

% let ASIDE_FOOTNOTES be an empty list

\newcommand\asidefootnote[1]%
{%
    \footnotemark{}%
    % add #1 to ASIDE_FOOTNOTES list
}

\newcommand\emitasidefootnotes%
{
    % where SIZE is the length of the ASIDE_FOOTNOTES list
    % \addtocounter{footnote}{-SIZE}

    % for each NOTE in ASIDE_FOOTNOTES
    % \footnotetext{NOTE}

    % let ASIDE_FOOTNOTES be an empty list
}

\newenvironment{aside}[1]
{
    \begin{minipage}{0.5\textwidth}
    ASIDE (#1) \par
}
{
    \end{minipage}
    \emitasidefootnotes
}

\begin{document}

Foo\footnote{foonote}

\begin{aside}{advanced}
    aaa\asidefootnote{foo aaa} bbb\asidefootnote{foo bbb}
\end{aside}

Bar\footnote{barnote}

\begin{aside}{advanced}
    xxx\asidefootnote{foo xxx} zzz\asidefootnote{foo zzz}
\end{aside}

Baz\footnote{baznote}

\end{document}

Basically, I need to turn the above comments into actual code.

  1. The argument to \asidefootnote can be arbitray tex.
  2. It needs to not interfere with other, normal footnotes. In the above example, footnotes 1 through 7 should appear together, in the natural order, at the bottom of the page.
  3. It doesn't need to work recursively (an aside environment will never contain another aside environment, directly or indirectly, nor will it contain any other environment which uses this trick).

How do I implement this? (In other words, how do I add a chunk of tex to a list, pass the size of that list to \addtocounter, iterate over that list, and empty that list?)

1

This gets the job done, but it's a bit of a hack. I'll hold off on accepting my own answer in case someone posts a better, pure tex solution (but this should be considered a reference implementation).

I had \asidefootnote write its footnote text to a file, then \emitasidefootnotes calls a Perl script which reads that file and generates the footnotes with correct numbering.

\documentclass{article}
\usepackage{mdframed}
\usepackage{xcolor}

% Taken from https://gist.github.com/w495/7328b76e76aee49657e0bd7a3b46c870
% This runs a bash command and prits the first line of its output into the
% document
\makeatletter
    \newcommand{\bashline@file@name}[1]{%
        /tmp/${USER}-${HOSTNAME}-\jobname-#1.tex%
    }
    \newread\bashline@file
    \newcommand{\bashline@command@one}[2][tmp]{%
        \immediate\write18{#2 > \bashline@file@name{#1}}
        \openin\bashline@file=\bashline@file@name{#1}
        % The group localizes the change to \endlinechar
        \bgroup
            \endlinechar=-1
            \read\bashline@file to \localline
            % Since everything in the group is local, 
            % we have to explicitly make the assignment global
            \global\let\bashline@result\localline
        \egroup
        \closein\bashline@file
        % Clean up after ourselves
        \immediate\write18{rm \bashline@file@name{#1}}
        \bashline@result
    }
    \newcommand{\bashline@command@many}[2][tmp]{%
        \immediate\write18{#2 > \bashline@file@name{#1}}
        \openin\bashline@file=\bashline@file@name{#1}
        % The group localizes the change to \endlinechar
        \newcount\linecnt
        \bgroup
            \endlinechar=-1
            \loop\unless\ifeof\bashline@file 
                \read\bashline@file to \localline%
                \localline
                \newline
            \repeat
        \egroup
        \closein\bashline@file
        % Clean up after ourselves
        \immediate\write18{rm \bashline@file@name{#1}}
    }
    \newcommand{\bashline}[2][tmp]{%
        \bashline@command@one[#1]{#2}%
    }
    \newcommand{\bashlines}[2][tmp]{%
        \bashline@command@many[#1]{#2}%
    }
\makeatother

% The \asidefootnote and \emitasidefootnotes commands
\def\asideinpfilename{aside.inp}
\def\asidetexfilename{aside.out}

\newwrite\asidefile
\immediate\openout\asidefile=\asideinpfilename
%\immediate\write\asidefile{ASIDES-START-HERE}

\newcommand{\asidefootnote}[1]%
{%
    \footnotemark{}%
    \immediate\write\asidefile{\unexpanded{#1}}%
}

\newcommand{\emitasidefootnotes}%
{%
    \immediate\closeout\asidefile%
    \bashline{perl bin/emit-aside-foonotes.pl '\asideinpfilename' '\asidetexfilename'}%
    \input{\asidetexfilename}%
    \immediate\openout\asidefile=\asideinpfilename%
}

\newenvironment{aside}[1]
{
    \mdfsetup{%
       middlelinecolor=red,
       middlelinewidth=2pt,
       backgroundcolor=red!5}
    \begin{mdframed}
    ASIDE (#1) \par
}
{
    \end{mdframed}
    \emitasidefootnotes
}

\begin{document}

Foo\footnote{foonote}

\begin{aside}{intermediate}
    aaa\asidefootnote{foo

    \begin{tabular}{|l|l|}
            \hline
            a & b \\ \hline
            c & d \\ \hline
    \end{tabular}}
    bbb\asidefootnote{foo bbb}
\end{aside}

Bar\footnote{barnote}

\begin{aside}{advanced}
    xxx\asidefootnote{foo xxx} zzz\asidefootnote{foo zzz}
\end{aside}

Baz\footnote{baznote}

\end{document}

The Perl script bin/emit-aside-footnotes.pl looks like this:

#!/usr/bin/perl
use strict;

die "Needs arguments: infile outfile" if (2 != @ARGV);

my $in_name  = $ARGV[0];
my $out_name = $ARGV[1];

open (my $in,  "<", $in_name)  or die $!;
open (my $out, ">", $out_name) or die $!;

my @lines = <$in>;

my $n = @lines;

exit if (0 == $n);

print $out "\\addtocounter{footnote}{-$n}";

chomp and print $out "\\stepcounter{footnote}\\footnotetext{$_}"
foreach (@lines);

Result:

enter image description here

Note that \immediate\write\asidefile{\unexpanded{#1}} automatically writes a single line to the file even if #1 contains multiple lines of tex, which makes the multi-line footnote 2 possible.

Your Answer

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

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