% \iffalse
%
% Copyright 2010 Stephen Hicks, All rights reserved.
% marginfix.dtx - 29 Jul 2010
% originally floatprobsbegone, created 21 Mar 2008
%
% Run LaTeX on this document to produce documentation.
% Run LaTeX on marginfix.ins to produce the package.
%<*driver>
\ProvidesFile{marginfix.dtx}
%</driver>
%
%<package>\NeedsTeXFormat{LaTeX2e}
%<package>\ProvidesPackage{marginfix}%
           [2020/05/06 v1.2 Fix Margin Paragraphs]
%<*driver>
\documentclass{ltxdoc}
\CheckSum{1159}
%\OnlyDescription % (un)comment this line to show (hide) source code
\RecordChanges
\EnableCrossrefs
\CodelineIndex   % (un)comment this line to index source by page (line)
\begin{document}
  \newcommand*\Lopt[1]{\textsf {#1}}
  \newcommand*\lit[1]{\texttt{\char`#1}} %% literal character (funny catcodes)
  \parindent0pt
  \def\*#1{\texttt{\string#1}} %% sdh - |...| doesn't work in headings
  \makeatletter
  %
  \let\pkg\textsf
  \newcount\mac@depth\mac@depth\z@
  \newcommand\@macros{}\newcommand\@endmacros{}
  \catcode`&3  %% we use a funny catcode to ensure never used.
  \def\@macros#1,{\macro{#1}\global\advance\mac@depth\@ne\relax
    \@ifnextchar&\@gobble\@macros}
  \def\@endmacros{\let\mac@next\relax\ifnum\mac@depth>\z@
    \endmacro\let\mac@next\@endmacros
    \global\advance\mac@depth\m@ne\fi\mac@next}
  \newenvironment{macros}[1]{\@macros#1,&}{\@endmacros}
  \catcode`&4  %% put it back 
  \makeatother %% must be balanced for character table to work properly
  %
  \DocInput{marginfix.dtx}
  \setcounter{IndexColumns}{2}
  \PrintIndex
  \PrintChanges
\end{document}
%</driver>
% \fi
% \changes{v0.0}{2008/03/21}
%       {(SDH) Initial version of floatprobsbegone.}
% \changes{v0.9}{2010/08/18}
%       {(SDH) Initial CTAN version.}
% \changes{v0.9.1}{2010/08/28}
%       {(SDH) Fix bug where we alternated sides in article.}
% \changes{v1.0}{2013/09/01}
%       {(Dario Buttari) Alternate order of \cs{marginpar} arguments to be
%        consistent with the original macro.}
% \changes{v1.0}{2013/09/01}
%       {(Dario Buttari) Fix bug in deferred note spacing.}
% \changes{v1.0}{2013/09/01}
%       {(Dario Buttari) Fix issues with \cs{marginheightadjustment} and
%        \cs{extendmargin} not being applied and reset consistently.}
% \changes{v1.0}{2013/09/01}
%       {(SDH) Stop gobbling vertical stretch on page.  Margin notes will
%        not line up properly if page is stretched.}
% \changes{v1.0}{2013/09/01}
%       {(SDH) Add margin phantoms.}
% \changes{v1.1}{2013/09/08}
%       {(SDH) Globally calculate margin phantoms over 4 passes.}
% \changes{v1.1}{2013/09/08}
%       {(SDH) Add \cs{topskip} to notes at the top of the margin.}
% \changes{v1.2}{2020/05/06}
%       {(SDH) Fix long-standing bug where margin notes called out in
%        the last few points of a page were being entirely dropped.}
%
% \GetFileInfo{marginfix.dtx}
% \title{\Lopt{marginfix} package documentation}
% \author{Stephen Hicks\\%
%    \texttt{sdh33@cornell.edu}\\%
%    \texttt{http://shicks.github.com/marginfix}}
% \date{\fileversion{} -- \filedate}
% \maketitle
%
% \part*{Usage}
% \section{Overview}
% Authors using \LaTeX\ to typeset books with significant margin material
% often run into the problem of long notes running off the bottom of the
% page.  A typical workaround is to insert \cs{vshift}s by hand, but
% this is a tedious process that is invalidated when pagination changes.
% Another workaround is \pkg{memoir}'s \cs{sidebar} function, but this can be
% unsatisfying for short textual notes, and standard marginpars cannot
% be mixed with sidebars.  This package implements a solution to
% make marginpars "just work" by keeping a list of floating inserts and
% arranging them intelligently in the output routine.  The credit for the
% concept behind this algorithm goes to Prof. Andy Ruina, who employed me
% to work on some of his textbook macros in 2007--9.
%
% \section{Options}
% There are currently no options that do anything yet.
%
% \section{Commands}
% For the most part, this is a drop-in replacement.  Simply include
% a call to |\usepackage{marginfix}| to the preamble, use \cs{marginpar}
% normally and hope for the best.
% In the event, however, that it doesn't work exactly as hoped,
% there are a number of tweaks that the user can apply.
%
% \DescribeMacro{\marginskip}
% Calling \cs{marginskip}\marg{length} will insert an incompressible
% skip in the margin.  These skips will force neighboring notes on
% the same page to be separated, but will disappear at the top or
% bottom of a margin.
%
% \DescribeMacro{\clearmargin}\DescribeMacro{\softclearmargin}
% In an analog to \cs{clearpage}, \cs{clearmargin} prevents any further
% material from being added to the current margin.  These calls are
% cumulative, so that two \cs{clearmargin}s in a row will produce a
% completely empty margin on the next page as well.  If this is not
% the desired effect, use \cs{softclearmargin}, which is effectively
% idempotent: multiple calls have the same effect as one call to
% end the current margin.
%
% \DescribeMacro{\extendmargin}
% If a page has too much margin material to fit and an important note
% is floating to the next page, \cs{extendmargin}\marg{length} will
% extend the margin (for the current page only) by the given length.
% If the length is negative, the margin will shrink.  Multiple calls
% on the same page are cumulative.
%
% \DescribeMacro{\mparshift}
% To adjust the position of a single note, use \cs{mparshift}\marg{length}
% before a call to \cs{marginpar}.  Positive lengths move it down the page.
% This essentially shifts the call-out location, so the actual position of
% the note might not change if the margin is sufficiently crowded.
% Multiple calls before the same note are cumulative.
%
% \DescribeMacro{\marginheightadjustment}
% If all the margins are the wrong size, the height of the margin on every
% page can be adjusted by assigning a non-zero value to the dimension
% register \cs{marginheightadjustment} (as in 
% \cs{marginheightadjustment}\texttt{=}$\langle$\emph{length}$\rangle$).
% This is effectively the same as a call to
% \cs{extendmargin} on every page.
%
% \DescribeMacro{\marginposadjustment}
% Similarly, if all the margin notes are in the wrong place, the callout
% positions can be adjusted globally by assigning a non-zero value to the
% dimension register \cs{marginposadjustment}.  This is effectively the same
% as a call to \cs{mparshift} before every note.  This is particularly useful
% at present because the height of the line on which the margin note is called
% is currently only estimated, and appears to be off by a point or two.
% This may get fixed in the future, but until then, the adjustment is
% possibly the easiest workaround.
%
% \DescribeMacro{\blockmargin}\DescribeMacro{\unblockmargin}
% As of version 1.0, we now support ``margin phantoms'': sections of the
% margin in which no notes will be placed, which can be useful for large
% figures that jut into the margin (note: margins already move out of the
% way of floats, regardless of whether or not they extend into the margin;
% this is mainly for in-place figures or equations).  The easiest way to
% block off part of the margin is to call \cs{blockmargin} before the
% extended content and \cs{unblockmargin} afterwards.  No margin notes
% will be placed between these two points (though one must be careful:
% if one of these is called in horizontal mode, the toggle will occur at
% the \emph{top} of the current line).  Each of these commands takes an
% optional argument: \cs{blockmargin}\oarg{pos} will begin the margin
% block at a position \emph{pos} below the current position (or above
% if \emph{pos} is negative), and \cs{unblockmargin}\oarg{pos} will
% likewise end the block at a position \emph{pos} below the current
% position.
%
% \DescribeMacro{\marginphantom}
% Margin phantoms may also be called out in place with a known size
% using \cs{marginphantom}\oarg{pos}\marg{size}, which is essentially
% equivalent to \cs{blockmargin}\oarg{pos}\cs{unblockmargin}\oarg{pos$+$size}.
% Either argument may be negative to refer upward rather than downward.
%
% \section{Interaction with other packages}
% \subsection{memoir}
% There are no known issues with \Lopt{memoir} at present, provided that
% \cs{sidebar} is not used.
%
% \subsection{mparhack}
% \Lopt{mparhack} was designed to deal with the problem of margin notes
% showing up in the wrong margin because the left/right was decided before
% it was known exactly which page the note would be on.  Because we defer
% this decision to shipout time in this package, we are not susceptible
% to this problem, so \Lopt{mparhack} is no longer needed and should not
% be included (though I'm unaware whether it causes any actual problems).
%
% \subsection{Multiple columns}
% There is currently no support for multiple columns.
%
% \section{Coming attractions and known issues}
% Here is a list of things to possibly look forward to in a future version.
% If any of them are particularly important, please let me know.
% \begin{itemize}
% \item Use of pdf\TeX's \cs{pdfsavepos} and \cs{pdflastypos} for more
%   accurate margin placement.
% \item \cs{vadjust} to correct inconsistencies with \cs{@pageht}.
% \item Margin note placement is irrespective of vertical stretch.
%   Previously we gobbled any vertical stretch, but now that we have
%   fixed that bug, there's the possibility of wrong alignment since
%   we don't know where the positions will ultimately end up.  This
%   may be fixed by \cs{pdfsavepos} as well.
% \item Better interaction with floats.
%   (We can set a default one way
%   or the other and then allow a macro to override it (presumably with a
%   CS defined in terms of the box name/meaning, so as not to get in the
%   way of \LaTeX's use of the insert registers).  We would then add or
%   not add phantoms in the right spots.  We'd also need to shift all the
%   callout points by the size of the top figures (unless we're using
%   \cs{pdfsavepos}).)
% \end{itemize}
%
% \StopEventually{}
%
% \makeatletter
% \part*{Implementation}
% \section{Initial Setup}
% Make the |@|-sign into a letter for use in macro names.
%    \begin{macrocode}
%<*package>
\makeatletter
%    \end{macrocode}
%
% \begin{macros}{\MFX@debug}
% We have some optionally-included code for debugging.  \cs{MFX@debug}
% prints a new line followed by ``|MFX: |'' and then the message.
% We'll also ask for more error context in the debug mode.
%    \begin{macrocode}
%<*debug>
\def\MFX@debug{\message{^^JMFX:}\message}
\errorcontextlines=20
\def\MFX@mac#1{\expandafter\MFX@@mac\meaning#1>>>}
\def\MFX@@mac#1->{<<<}
\def\MFX@htdp#1{\ht#1=\the\ht#1, \dp#1=\the\dp#1}
%</debug>
%    \end{macrocode}
% \end{macros}
%
% The reader might begin to note at this point a convention we
% adopt throughout this package.  While we strive to avoid introducing
% new names as much as possible (with clever usages of \cs{expandafter}),
% any new names we do introduce will be prefixed
% by |\MFX@|, |\Mfx@|, or |\mfx@|, depending on the type of name.
% The all-capitol |\MFX@| is used for fully-constant macros.  The
% initial-caps |\Mfx@| is used for control sequences that are
% technically constant, but that refer to things that change, such
% as counters, token lists, dimension registers, etc.  Finally,
% the lowercase |\mfx@| is used for control sequences whose meaning
% changes dynamically (i.e. variable macros).
%
% \section{Options}
% Here we define the various package options.
% \iffalse - CURRENTLY UNIMPLEMENTED
% \begin{macros}{\ifmfx@ypos}
% The \Lopt{ypos} option signifies that we should use the pdf{\TeX}
% primitives \cs{pdfsavepos} and \cs{pdflastypos} to improve positioning
% of margin notes relative to their callouts.  This requires two
% passes to work, and the first time through, the margin notes
% will be positioned very na\"ively.
%    \begin{macrocode}
\newif\ifmfx@ypos
\DeclareOption{ypos}{\mfx@ypostrue}
%    \end{macrocode}
% \end{macros}
% END IFFALSE - \else There are no options yet. \fi
%
% Now we actually process the options.
%    \begin{macrocode}
\ProcessOptions\relax
%    \end{macrocode}
%
% \section{Variables}
% \begin{macros}{\mfx@marginlist}
% We need a place to store our list of marginal material.  We
% store material in this variable using insert registers and
% a variety of macros, to be explained later.
%    \begin{macrocode}
\let\mfx@marginlist\@empty
%    \end{macrocode}
% \end{macros}
%
% \begin{macros}{\mfx@inject,\Mfx@inject@insert}
% These are used to hijack \cs{marginpar} to inject arbitrary code
% into the output routine, rather than actually set a note.  To
% inject code, we unshift two copies of this dummy insert onto
% \cs{@freelist} for \cs{marginpar} to pull off.  We append
% whatever code we want to inject into \cs{mfx@inject} and then
% call \cs{marginpar}.  Then our custom \cs{@addmarginpar} will
% recognize the dummy inserts and run the code instead of setting
% a margin note.
%    \begin{macrocode}
\let\mfx@injected\@empty
\newinsert\Mfx@inject@insert
%    \end{macrocode}
% \end{macros}
%
% \begin{macros}{\Mfx@marginbox}
% While we're building the margin, we need to put it in a box
% before we can attach it to the main columm.
%    \begin{macrocode}
\newbox\Mfx@marginbox
%    \end{macrocode}
% \end{macros}
%
% \begin{macros}{\Mfx@marginpos@min,\Mfx@marginpos@max,\Mfx@marginspace}
% While we build up the margin piece boxes, we need to keep track of the
% possible range of positions.  The pair \cs{Mfx@marginpos@min} and
% \cs{Mfx@marginpos@max} are used to accumulate how much material has
% been added so far, with the difference that \cs{Mfx@marginpos@min} doesn't
% take into account compressible space, while \cs{Mfx@marginpos@max} does.
% Finally, \cs{Mfx@marginspace} is the amount of (incompressible) space
% since the last note, which allows skips to span margin phantoms.
%    \begin{macrocode}
\newdimen\Mfx@marginpos@min
\newdimen\Mfx@marginpos@max
\newdimen\Mfx@marginspace
%    \end{macrocode}
% \end{macros}
%
% \begin{macros}{\Mfx@marginheight}
% Because the margin height can be altered by, \cs{extendmargin},
% we must maintain a dimension for the height of the current margin.
% This dimension is reused in several different ways in the shipout-time
% margin building routines, keeping track of how much much space is left
% in (for the global passes) and the end position of the end of (for the
% piecewise passes) the current piece.
%    \begin{macrocode}
\newdimen\Mfx@marginheight
%    \end{macrocode}
% \end{macros}
%
% \begin{macros}{\mfx@marginstart,\mfx@marginpieces,
%     \Mfx@piece@content,\Mfx@piece@count,\ifmfx@in@phantom}
% These control sequences keep track of the margin phantoms.  When the
% margin is unblocked then \cs{mfx@marginstart} is not \cs{relax}.  When
% a phantom begins, the current \cs{mfx@marginstart} and page position
% are stored as a pair in \cs{mfx@marginpieces}, which is iterated over
% while building individual pieces of the margin.  We also define a box
% to keep the content of each margin piece, and a counter to keep track
% of how many pieces we have.  Finally, we define a switch for use in
% the second pass to indicate that we're inside a phantom.
%    \begin{macrocode}
\def\mfx@marginstart{0pt}
\let\mfx@marginpieces\@empty
\newbox\Mfx@piece@content
\newcount\Mfx@piece@count
\newif\ifmfx@in@phantom
%    \end{macrocode}
% \end{macros}
%
% \begin{macros}{\Mfx@mparshift}
% We store the current shift in a dimension register.
%    \begin{macrocode}
\newdimen\Mfx@mparshift
%    \end{macrocode}
% \end{macros}
%
% \section{User-configurable dimensions}
% We export a few dimensions that the user can redefine to tweak behavior.
% \begin{macros}{\marginheightadjustment}
% This length will be added to the total margin height of each page
% (the default is zero).
%    \begin{macrocode}
\newdimen\marginheightadjustment
%    \end{macrocode}
% \end{macros}
%
% \begin{macros}{\marginposadjustment}
% We will offset each margin note from its callout location by this length
% (the default is zero).
%    \begin{macrocode}
\newdimen\marginposadjustment
%    \end{macrocode}
% \end{macros}
%
% \section{Plan of attack}
% \subsection{\cs{marginpar}}
% The default sequence of events for a \cs{marginpar} is roughly the following
% (assuming no errors):
% \begin{verbatim}
% \marginpar:
%   let \@floatpenalty := (horizontal ? -10002 : -10003)
%   allocate inserts \@currbox and \@marbox from \@freelist
%   let \count\@marbox := -1 % signifies marginpar (not float)
%   if optional argument then \@xmpar else \@ympar
% \@xmpar:
%   \@savemarbox \@currbox := required argument
%   \@savemarbox \@marbox := optional argument
%   \@xympar
% \@ympar:
%   \@savemarbox \@currbox := required argument
%   copy \@marbox := \@currbox
%   \@xympar
% \@xympar:
%   append \@marbox to \@currlist
%   \end@float
% \end@float:
%   append \@currbox to \@currlist
%   if horizontal then following two lines are in \vadjust:
%     \penalty -10004
%     \penalty \@floatpenalty
% \end{verbatim}
%
% To get the rest of the picture, we need to peek into the
% output routine.  The pertinent parts are as follows (in
% vanilla \LaTeX):
% \begin{verbatim}
% \output:
%   if \outputpenalty < -10000 then
%     \@specialoutput
%   else
%     do regular output...
%   details for dealing with footnotes...
% \@specialoutput:
%   switch \outputpenalty:
%    case -10001: \@doclearpage
%    case -10004: set box \@holdpg := \vbox{\unvbox255}
%    case -10002 or -10003:
%     set box \@holdpg := \vbox{\unvbox\@holdpg \unvbox255}
%     let \@pageht := \ht\@holdpg, \@pagedp := \dp\@holdpg
%     \unvbox\@holdpg
%     pop \@currbox off of \@currlist
%     \@addmarginpar (assuming \count\@currbox <= 0)
% \@addmarginpar:
%   pop \@marbox off of \@currlist
%   free \@currbox and \@marbox back to \@freelist
%   if left-hand margin then let \@marbox := \@currbox
%   let \@tempdima := \@mparbottom - \@pageht + \ht\@marbox
%   if \@tempdima < 0 then let \@tempdima := 0
%   let \@mparbottom := \@pageht + \@tempdima + \dp\@marbox + \marginparpush
%   decrement \@tempdima := \@tempdima - \ht\@marbox
%   prepend \vskip\@tempdima to \@marbox
%   let \ht\@marbox := \dp\@marbox := 0
%   \kern -\@pagedp, \nointerlineskip
%   set an \hbox to \columnwidth (zero height/depth):
%     attach \@marbox to correct margin
%   set a \vbox with height 0 and depth \@pagedp
% \end{verbatim}
%
% We see from here that \cs{@addmarginpar} is the place where {\LaTeX}
% does the work of calculating the current page position and where the
% next note should go, and then actually puts it there.  We will need
% to completely replace this routine, but can leave everything else
% as is.
%
% \subsection{\cs{output}}
% While \LaTeX's margin routines end with \cs{@addmarginpar}, we must
% dig even deeper to apply our patch, since we need to insert some
% code to run during the \emph{main} output routine that ships out
% each page.  Thus, we'll expand ``\texttt{do regular output...}''
% from the previous \cs{output} listing.
% \begin{verbatim}
% do regular output...:
%   \@makecol
%   do { \@opcol \@startcolumn } while @fcolmade
% \@makecol:
%   set box \@outputbox := box255 (plus any footnotes)
%   let \@freelist := \@freelist + \@midlist, \@midlist := \@empty
%   \@combinefloats
%   add \@texttop and \@textbottom to \@outputbox (default no-op)
% \@opcol:
%   \@outputpage (or \@outputdblcol in twocolumn mode)
%   let \@mparbottom := \@textfloatsheight := 0
%   \@floatplacement
% \@startcolumn:
%   try to make a float column from \@deferlist, setting @fcolmade
%   if !@fcolmade then add floats from \@deferlist to next column
% \@combinefloats:
%   aggregate \@toplist floats into a box and prepend to \@outputbox
%   aggregate \@botlist floats into a box and append to \@coutputbox
%   free inserts from \@toplist and \@botlist
% \@outputpage:
%   ship out the page
%   reset a bunch of stuff
%   let \@colht := \textheight (in \@outputpage)
% \end{verbatim}
%
% We've seen two main times when action occurs: callout time and
% shipout time.  We proceed chronologically with our patches.
%
% \section{Callout-time patches}
% \begin{macros}{\@addmarginpar}
% The first thing we must modify is that at callout time, we need to
% get the inserts into \cs{mfx@marginlist}.  This should happen in the
% output routine so that we can get ahold of the current page
% position.  Even if we have a better idea of the page position
% (e.g. from pdf\TeX), we still might as well do this in the OR.
% In addition to actually setting the margin note, we also use this
% routine to inject arbitrary code into the OR (see \cs{MFX@inject}).
%    \begin{macrocode}
\def\@addmarginpar{%
  \@next\@marbox\@currlist{}\MFX@AssertionError
%<debug>\MFX@debug{addmarginpar (running insert) \@marbox/ \@currbox at
%<debug>    \the\c@page:\the\@pageht, marginlist=\MFX@mac\mfx@marginlist}%
%<debug>\MFX@debug{addmarginpar outputpenalty=\the\outputpenalty}%
  \MFX@getypos
  \expandafter\ifx\@marbox\Mfx@inject@insert
    \mfx@injected\global\let\mfx@injected\@empty
  \else
    \MFX@cons\mfx@marginlist{%
      \noexpand\mfx@build@note\@currbox\@marbox{\mfx@ypos}%
      \noexpand\mfx@build@skip{\the\marginparpush}%
    }%
  \fi
%<debug>\MFX@debug{addmarginpar (exit): marginlist=\MFX@mac\mfx@marginlist}%
}
%    \end{macrocode}
% \end{macros}
%
% \begin{macros}{\MFX@cons,\MFX@snoc}
% In passing we'll define the cons macro, which fully-expands
% its second argument, but makes sure to only expand the first
% one once, so that any fragile control sequences in it are
% correctly protected.  We also define snoc, which prepends.
% Note that we could put the \cs{temp@} definition into a group
% if it was really gonna matter\ldots
%    \begin{macrocode}
\def\MFX@cons#1#2{%
  \edef\temp@{#2}%
  \expandafter\expandafter\expandafter\gdef
  \expandafter\expandafter\expandafter#1%
  \expandafter\expandafter\expandafter{\expandafter#1\temp@}%
}

\def\MFX@snoc#1#2{%
  \edef\temp@{#2}%
  \expandafter\expandafter\expandafter\gdef
  \expandafter\expandafter\expandafter#1%
  \expandafter\expandafter\expandafter{\expandafter\temp@#1}%
}
%    \end{macrocode}
% \end{macros}
%
% \begin{macros}{\MFX@run@clear}
% Finally, \cs{MFX@run@clear} is a quick trick to expand the
% contents of a macro and then clear it (to \cs{@empty}) before
% any of its tokens are consumed.
%     \begin{macrocode}
\def\MFX@run@clear#1{%
  \expandafter\global\expandafter\let\expandafter#1\expandafter\@empty#1%
}
%    \end{macrocode}
% \end{macros}
%
% \begin{macros}{\MFX@inject}
% As mentioned earlier, \cs{@addmarginpar} is also a hook for injecting
% arbitrary code into the output routine, i.e. to get a vertical position
% for blocking the margin.  We define \cs{MFX@inject} to facilitate this.
%    \begin{macrocode}
\def\MFX@inject#1{
  \expandafter\def\expandafter\@freelist\expandafter{%
    \expandafter\@elt\expandafter\Mfx@inject@insert
    \expandafter\@elt\expandafter\Mfx@inject@insert
    \@freelist}%
  \expandafter\def\expandafter\mfx@injected\expandafter{\mfx@injected#1}%
  \marginpar{}%
}
%    \end{macrocode}
% \end{macros}
%
% \begin{macros}{\MFX@getypos,\mfx@ypos}
% We now need to settle on a way to determine the vertical position.
% Someday this may be an option, and will depend on a variety
% of factors.  But for starters, we define the simplest version.
% Note the subtraction of \cs{Mfx@strutheight}.  Ideally we would simply
% grab a copy of \cs{@holdpg} from the middle of \cs{@specialoutput}
% and then discard the last box to figure out what height we're really
% at, since \cs{@holdpg} includes the box from the line we're currently
% on, and we want to be level with the \emph{top} of that box, rather
% than the baseline.  But since \cs{@holdpg} is accessible only deep
% within \cs{@specialoutput}, and it's not worth the risky job of
% performing surgery on it (which is unfortunately brittle if anyone
% else has a similar idea), we instead resort to this approximation.
% And since this should ultimately be only a fallback for when
% \cs{pdflastypos} isn't available, it's good enough.
% (NOTE: we might be able to use a \cs{vadjust} instead here?)
%    \begin{macrocode}
\def\MFX@getypos{%
  \dimen@\dimexpr\@pageht+\@pagedp+\marginposadjustment+\Mfx@mparshift\relax
  \ifnum\outputpenalty=-10002\relax
    \advance\dimen@-\Mfx@strutheight
  \fi
  \edef\mfx@ypos{\the\dimen@}%
  \global\Mfx@mparshift\z@
}
%    \end{macrocode}
% \end{macros}
%
% \begin{macros}{\marginpar,\Mfx@strutheight}
% We need to make sure \cs{Mfx@strutheight} gets defined somewhere,
% and the best time is probably right before the \cs{marginpar} does
% its work, since that will most likely ensure we're using the right
% font for the line.
%    \begin{macrocode}
\newdimen\Mfx@strutheight
\edef\marginpar{%
  \unexpanded{\setbox\@tempboxa\hbox{\strut}\Mfx@strutheight\ht\@tempboxa}%
  \expandafter\unexpanded\expandafter{\marginpar}%
}
%    \end{macrocode}
% \end{macros}
%
% \section{Shipout-time patches}
% \begin{macros}{\@combinefloats}
% We need to patch in somewhere before \cs{@combinefloats} at the latest,
% so that any heights calculated from \cs{@pageht} are correct---otherwise
% the top figures will confuse us.  So we'll start by simply adding our
% own \cs{MFX@combinefloats@before} at the very beginning of \cs{@combinefloats}
%    \begin{macrocode}
\expandafter\def\expandafter\@combinefloats\expandafter{\expandafter
  \MFX@combinefloats@before\@combinefloats}
%    \end{macrocode}
% \end{macros}
%
% \begin{macros}{\MFX@combinefloats@before}
% \cs{MFX@combinefloats@before} is then responsible for picking the
% needed notes from \cs{mfx@marginlist}, building them into a box, and
% attaching that box onto the correct side of \cs{@outputbox}.  We also
% add any global \cs{marginheightadjustment} to \cs{Mfx@marginheight}
% before building the margin, and then reset it back to zero at the end.
% This allows any calls to \cs{extendmargin} during the page itself to
% work as expected.
%    \begin{macrocode}
\def\MFX@combinefloats@before{%
  \advance\Mfx@marginheight\marginheightadjustment
  \MFX@buildmargin
  \MFX@attachmargin
  \global\Mfx@marginheight\z@
}
%    \end{macrocode}
% \end{macros}
%
% \begin{macros}{\MFX@attachmargin}
% We'll start with the second half of \cs{MFX@combinefloats@before},
% since it's simpler.  We need to do several things here.
%    \begin{macrocode}
\def\MFX@attachmargin{%
%<debug>\MFX@debug{attachmargin}%
%    \end{macrocode}
% We start by moving the reference point of \cs{Mfx@marginbox} to the top.
%    \begin{macrocode}
%<debug>\MFX@debug{attachmargin: \MFX@htdp\@outputbox, \MFX@htdp\Mfx@marginbox}%
  \setbox\Mfx@marginbox\vtop{%
    \vskip\z@\unvbox\Mfx@marginbox}%
%    \end{macrocode}
% Next we need to figure out which side of \cs{@outputbox} to attach
% the \cs{Mfx@marginbox} on.  We now use \cs{columnwidth} instead of
% \cs{wd}\cs{@outputbox} to set the right-hand margins, since tufte-\LaTeX
% sometimes makes too-wide output boxes.  If this becomes a problem,
% we'll need to consider making this configurable elsewhere.  We should
% also pay attention to whether adding a box at the top of \cs{@outputbox}
% might have unintended consequences w.r.t. any glue being retained that
% should have been swallowed.  This will require further investigation.
%    \begin{macrocode}
  \setbox\@outputbox\vbox{%
    \begingroup
    \setbox\@tempboxa\vbox{%
      \hbox{%
        \if\MFX@leftmargin
          \llap{\box\Mfx@marginbox\hskip\marginparsep}%
        \else
          \hskip\columnwidth
          \rlap{\hskip\marginparsep\box\Mfx@marginbox}%
        \fi
      }}%
    \ht\@tempboxa\z@
    \dp\@tempboxa\z@
    \box\@tempboxa
    \endgroup
    \unskip
    \unvbox\@outputbox
  }%
}
%    \end{macrocode}
% \end{macros}
%
% \begin{macros}{\MFX@buildmargin}
% When \cs{MFX@buildmargin} is called, we have a list of tokens in
% \cs{mfx@marginlist} that need to be processed: combinations of
% \cs{mfx@build@note}, \cs{mfx@build@skip}, and \cs{mfx@build@clear},
% with various parameters to indicate what material still needs to be
% set in the margin.  This macro therefore must pull off the first $n>0$
% of these commands to set on the current page ($n$ must be positive to
% prevent infinite loops), and leave the rest to be deferred.  We do not
% currently support taking notes out of order, though that is a possible
% feature to allow in the future, on an opt-in basis.  The typeset
% material will be left in \cs{Mfx@marginbox}, which must have the same
% height as \cs{@outputbox} (although because we \cs{unvbox}\cs{@outputbox}
% in \cs{MFX@attachmargin}, we can't guarantee that this will correctly
% line up the notes with their callouts).  This procedure happens in
% four passes.  But first, we initialize \cs{Mfx@marginheight} to
% \cs{@colroom}, which is the height of the page minus any floats that
% have been added to the top or bottom (these floats may extend into the
% margins: in the future we may look into detecting this and using the
% whole page, with overwide floats blocked off as phantoms).  We add
% \cs{@colroom} rather than assigning it because any global or per-page
% adjustments have already been added to \cs{Mfx@marginheight}.  We
% can then close out any still-open margin pieces (this is the typical
% case, where the margin is not blocked across a page boundary, so that
% \cs{mfx@marginstart} will hold a position, rather than \cs{relax}).
% After this, \cs{Mfx@marginheight} is no longer necessary, so we reuse
% it for keeping track of available space in individual margin pieces.
%    \begin{macrocode}
\def\MFX@buildmargin{%
  \advance\Mfx@marginheight\@colroom
  \ifx\mfx@marginstart\relax
  \else
    \MFX@cons\mfx@marginpieces{%
      \noexpand\@elt{\mfx@marginstart}{\the\Mfx@marginheight}}%
    \gdef\mfx@marginstart{0pt}%
    \global\advance\Mfx@piece@count\@ne
  \fi
%<debug>\MFX@debug{buildmargin: marginheight=\the\Mfx@marginheight,
%<debug>           marginlist=\MFX@mac\mfx@marginlist,
%<debug>           marginpieces=\MFX@mac\mfx@marginpieces}%
%    \end{macrocode}
% We now execute the four passes.  First is a global downward pass,
% whose purpose is to determine the maximum number of notes (and other
% material) that can fit in the margin, taking any phantoms into
% consideration.  Every note identified by \cs{MFX@buildmargin@down}
% is guaranteed to show up on this page, so we free its inserts back
% to \cs{@freelist}.  The second pass is the global upward pass, in
% which we determine the lowest possible margin piece each note may
% go into without causing lower notes to fall off the bottom.  The
% third pass is the piecewise downward pass.  For each piece, we
% figure out where in the piece each note will go by inserting
% compressible spaces between the notes.  If a note is called out
% past the end of the piece and does not need to go into the piece
% (as determined by pass 2), it will be deferred to a later piece.
% The fourth pass is the piecewise upward pass, in which the compressible
% spaces are shrunk just enough to fit everything into the piece.  The
% last two (piecewise) passes both occur in each piece before the next
% piece is addressed.  The whole process is bypassed if there are
% no eligible margin pieces.
%    \begin{macrocode}
  \ifx\mfx@marginpieces\@empty\else
    \MFX@buildmargin@down
    \MFX@buildmargin@up
    \MFX@buildmargin@pieces
  \fi
}
%    \end{macrocode}
% \end{macros}
%
% \subsection{First pass: global downward}
% \begin{macros}{\MFX@buildmargin@down,\mfx@pieceheights}
% The first step is the global ``down'' step, in which we move the
% notes that will fit on the current page into \cs{mfx@marginout} in
% reverse order (to prepare for the second, upward, pass), and anything
% that doesn't fit is deferred back into \cs{mfx@marginlist}.  We do
% this by changing the meaning of \cs{mfx@build@note}, \cs{mfx@build@skip},
% and \cs{mfx@build@clear}, which delimit the different types of material
% in \cs{mfx@marginlist}.  Note that as we continue processing, these
% macros will change from time to time (i.e. changing \cs{mfx@build@skip}
% to actually doing something once we find a note, rather than gobbling
% so as to remove skips at page boundaries; or changing them to save
% material back onto \cs{mfx@marginlist} once the margin fills up).
% The first thing we need to do is iterate over the piece positions
% to get the list of heights.
%    \begin{macrocode}
\def\MFX@buildmargin@down{%
  \let\mfx@pieceheights\@empty
  \def\@elt##1##2{%
    \MFX@cons\mfx@pieceheights{\noexpand\@elt{\the\dimexpr##2-##1}}}%
  \mfx@marginpieces
  \MFX@popdimen\Mfx@marginheight\mfx@pieceheights
%    \end{macrocode}
% Now we run forwards over the \cs{mfx@marginlist} to actually
% operate on each thing in the margin.
%    \begin{macrocode}
  \let\mfx@build@note\MFX@margin@note@down
  \let\mfx@build@skip\@gobble
  \let\mfx@build@clear\MFX@build@clear@down
  \let\mfx@marginout\@empty
  \MFX@run@clear\mfx@marginlist
%<debug>\MFX@debug{buildmargin@down: RETURN
%<debug>           marginout=\MFX@mac\mfx@marginout,
%<debug>           marginlist=\MFX@mac\mfx@marginlist}%
}
%    \end{macrocode}
% \end{macros}
%
% We now define the various |\MFX@margin@...@down| macros.
% At this stage in the game, the only difference between  notes
% and skips is that we ignore skips before any notes by setting
% \cs{mfx@build@skip} initially to \cs{@gobble}.  Once we've
% seen the first note, skips are treated exactly the same: as
% fixed-height material.  If there is room in the current piece
% for the given height, then we prepend it to \cs{mfx@marginout},
% decrement the remaining height, and arrange for the boxes to
% be freed.  If not, we unshift the next piece height from
% \cs{mfx@pieceheights} and try again, until \cs{mfx@pieceheights}
% is empty and we simply defer everything to later pages.
%
% \begin{macros}{\MFX@margin@note@down}
% Upon seeing a note, we must do several things:
% \begin{enumerate}
% \item determine which box (left or right) is needed for the
%   current page, by calling \cs{MFX@whichbox}
% \item if the box fits, free both boxes, prepend \cs{mfx@marginout}
%   with a call to \cs{mfx@build@note}, and re-enable skips
% \item otherwise, defer the current note and all future notes
% \end{enumerate}
% The latter two steps are taken care of by \cs{MFX@margin@fit},
% which takes the height and two blocks of material: one to prepend
% to \cs{mfx@marginout} if it fits, the other to append to
% \cs{mfx@marginlist} if it doesn't.
%    \begin{macrocode}
\def\MFX@margin@note@down#1#2#3{%
%<debug>\MFX@debug{margin@note@down: ENTRY: #1/ #2 at #3}%
  \MFX@whichbox\@marbox#1#2%
  \if\MFX@check@fit{}{\ht\@marbox+\dp\@marbox}%
    \MFX@snoc\mfx@marginout{%
      \noexpand\@cons\noexpand\@freelist#1%
      \noexpand\@cons\noexpand\@freelist#2%
      \noexpand\mfx@build@note\@marbox{#3}}%
    \let\mfx@build@skip\MFX@margin@skip@down
  \else
    \mfx@build@clear
    \mfx@build@note{#1}{#2}{#3}%
  \fi
}
%    \end{macrocode}
% \end{macros}
%
% \begin{macros}{\MFX@margin@skip@down}
% Skips are similar.  A skip needs only to save itself back into
% \cs{mfx@marginout}, provided it fits.  If not, there is no need
% to defer it because it will just get gobbled at the top of the
% next page anyway.
%    \begin{macrocode}
\def\MFX@margin@skip@down#1{%
%<debug>\MFX@debug{margin@skip@down #1}%
  \if\MFX@check@fit{}{#1}%
    \MFX@snoc\mfx@marginout{\noexpand\mfx@build@skip{#1}}%
  \else
    \mfx@build@clear
  \fi
}
%    \end{macrocode}
% \end{macros}
%
% \begin{macros}{\MFX@margin@clear@down}
% Finally, \cs{MFX@margin@clear@down} is the only place we actually
% need to handle full-margin clears, since the downward pass does not
% ever push \cs{mfx@build@clear} onto \cs{mfx@marginout}.  When we
% see this, we simply redefine all three commands to append themselves
% back to \cs{mfx@marginlist}.
%    \begin{macrocode}
\def\MFX@build@clear@down{%
%<debug>\MFX@debug{clear@down}%
  \def\mfx@build@note##1##2##3{%
    \MFX@cons\mfx@marginlist{\noexpand\mfx@build@note##1##2{\MFX@minus@inf}}}%
  \def\mfx@build@skip##1{%
    \MFX@cons\mfx@marginlist{\noexpand\mfx@build@skip{##1}}}%
  \def\mfx@build@clear{%
    \MFX@cons\mfx@marginlist{\noexpand\mfx@build@clear}}%
}
%    \end{macrocode}
% \end{macros}
%
% \begin{macros}{\MFX@check@fit}
% We factored out some of the common functionality between the
% note and skip routines, so that must now be defined.  The
% \cs{MFX@check@fit} macro acts as a conditional and should be
% used as \cs{if}\cs{MFX@check@fit}\marg{piece-hook}\marg{size}.
% It takes care of iterating through the list of heights and
% accumulating the total size of material encountered so far.
% The \emph{piece-hook} is executed each time a new piece height
% is popped.
%    \begin{macrocode}
\def\MFX@check@fit#1#2{%
  00\fi % close out the \if
%<debug>\MFX@debug{check@fit{\unexpanded{#1}}{#2=\the\dimexpr#2} ENTRY:
%<debug>           marginheight=\the\Mfx@marginheight}%
  \@tempswafalse
  \ifdim\dimexpr#2<\Mfx@marginheight % it fits
    \advance\Mfx@marginheight-\dimexpr#2\relax % deduct the size
    \@tempswatrue
  \else % didn't fit: check the next piece
%<debug>\MFX@debug{check@fit overflow: pieceheights=\MFX@mac\mfx@pieceheights}%
    \ifx\mfx@pieceheights\@empty\else % make sure there's anything there
      #1%
      \MFX@popdimen\Mfx@marginheight\mfx@pieceheights
      \if\MFX@check@fit{#1}{#2}\fi
    \fi
  \fi
%<debug>\MFX@debug{check@fit RETURN \meaning\if@tempswa:
%<debug>           marginheight=\the\Mfx@marginheight,}%
  \if@tempswa % start a new \if
}
%    \end{macrocode}
% \end{macros}
% \begin{macros}{\MFX@popdimen}
% Here is a quick convenience routine.  \cs{MFX@popdimen}\marg{dimen}%
% \marg{list} removes the first dimension from \emph{list} and stores
% it into \emph{dimen}.
%    \begin{macrocode}
\def\MFX@popdimen#1#2{%
  \def\@elt##1{%
    #1##1\relax
    \def\@elt####1{%
      \MFX@cons#2{\noexpand\@elt{####1}}%
    }%
  }%
  \MFX@run@clear#2%
}
%    \end{macrocode}
% \end{macros}
%
% \begin{macros}{\MFX@whichbox}
% We also need to determine which box should be used, since they
% may have different heights.  The macro \cs{MFX@whichbox}%
% \marg{target-box}\marg{left-box}\marg{right-box} checks which margin
% we're setting and stores the correct box into \emph{target-box}.
% Note that \emph{target-box} must be a single control sequence.
%    \begin{macrocode}
\def\MFX@whichbox#1#2#3{%
  \if\MFX@leftmargin
    \def#1{#2}%
  \else
    \def#1{#3}%
  \fi
%<debug>\MFX@debug{whichbox: \@marbox (\the\dimexpr\ht#1+\dp#1)}%
}
%    \end{macrocode}
% \end{macros}
%
% \begin{macros}{\MFX@leftmargin}
% And here is the logic to figure out which margin we're in, based on
% the page number and other flags.  This is another conditional-like
% macro, and should be used after an \cs{if}, as in
% \cs{if}\cs{MFX@leftmargin}\ldots\cs{else}\ldots\cs{fi}.
%
% This is different from the corresponding code in the {\LaTeX}
% routines because we don't support double columns.  In addition, we
% would ideally allow \cs{if@reversemargin} to work on a per-note
% basis (i.e. at callout time) but we also need something working at
% shipout time so we can figure out which margin to use.  Thus, until
% we figure out how to use multiple margins, this will have to do.
%    \begin{macrocode}
\def\MFX@leftmargin{%
  00\fi % close out the \if
  \@tempcnta\@ne
  \if@mparswitch
    \unless\ifodd\c@page
      \@tempcnta\m@ne
    \fi
  \fi
  \if@reversemargin
    \@tempcnta-\@tempcnta
  \fi
%<debug>\MFX@debug{margin on \ifnum\@tempcnta<\z@ left\else right\fi}%
  \ifnum\@tempcnta<\z@ % start a new \if
}
%    \end{macrocode}
% \end{macros}
%
% \begin{macros}{\MFX@minus@inf}
% Finally, note that when deferring notes to the next page, we
% adjust their position to the top of the page, rather than the
% callout position.  This is a large negative dimension (near
% \TeX's maximum), but we may reconsider making this zero or even
% a small positive amount, since there seems to be a small amount
% of space before the first paragraph in normal text, though I'm
% not sure where that comes from.
%    \begin{macrocode}
\def\MFX@minus@inf{-4000\p@}
%    \end{macrocode}
% \end{macros}
%
% \subsection{Second pass: global upward}
% \begin{macros}{\MFX@buildmargin@up,\mfx@phantomheights}
% The next step is the global ``up'' step, in which we figure out the
% lowest piece a note can possibly occupy (without pushing later notes
% off the bottom) and add this information to the \cs{mfx@build@note} in
% \cs{mfx@marginout}.  We start similar to \cs{MFX@buildmargin@down},
% except we need the list of heights to be backwards.  We also need a
% list of phantom heights, in order to handle skips properly, which we
% intersperse as negative heights.
%    \begin{macrocode}
\def\MFX@buildmargin@up{%
%<debug>\MFX@debug{buildmargin@up: ENTRY
%<debug>           marginpieces=\MFX@mac\mfx@marginpieces,
%<debug>           marginout=\MFX@mac\mfx@marginout}%
  \let\mfx@pieceheights\@empty
  \let\mfx@phantomheights\@empty
  \let\temp@@\relax
  \def\@elt##1##2{%
%<debug>\MFX@debug{  -> piece (##1,##2), temp@@=\meaning\temp@@}%
    \MFX@snoc\mfx@pieceheights{\noexpand\@elt{\the\dimexpr##2-##1}}%
    \ifx\temp@@\relax\else
%<debug>\MFX@debug{  -> phantom (\temp@@,##1)}%
      \MFX@snoc\mfx@phantomheights{\noexpand\@elt{\the\dimexpr##1-\temp@@}}%
    \fi
    \def\temp@@{##2}%
  }%
  \mfx@in@phantomfalse
  \mfx@marginpieces
%    \end{macrocode}
% The piece counter, \cs{Mfx@piece@count} has not been touched yet,
% so now we will start decrementing it for each piece to keep track
% of where we are.  Note that \cs{mfx@marginout} will never contain
% \cs{mfx@build@clear} so we don't need to reassign it.  Since we
% don't do any deferrals here, we don't need to empty out a new target
% list.  Instead, we operate ``in-place'' in \cs{mfx@marginout}, after
% popping off the first height.
%    \begin{macrocode}
  \MFX@popdimen\Mfx@marginheight\mfx@pieceheights
  \let\mfx@build@note\MFX@margin@note@up
  \let\mfx@build@skip\@gobble
  \MFX@run@clear\mfx@marginout
%<debug>\MFX@debug{buildmargin@up: RETURN marginout=\MFX@mac\mfx@marginout}%
}
%    \end{macrocode}
% \end{macros}
%
% \begin{macros}{\MFX@margin@note@up}
% We must again define the specific behavior of each build command.
% These macros simply reuse \cs{MFX@check@fit}, but ask it to
% decrement the piece counter when a piece runs out of space.
% Aside from that, the only thing that actually happens here is
% that we append the current piece to each note, and also end
% up reversing the contents by again prepending everything.
% We are guaranteed that \cs{MFX@check@fit} will never fail.
% Since we cannot put notes in a phantom, we start by ensuring
% we're not in one.
%    \begin{macrocode}
\def\MFX@margin@note@up#1#2{%
%<debug>\MFX@debug{margin@note@up: #1at #2, marginheight=\the\Mfx@marginheight}%
  \ifmfx@in@phantom
    \MFX@popdimen\Mfx@marginheight\mfx@pieceheights
    \advance\Mfx@piece@count\m@ne
    \mfx@in@phantomfalse
  \fi
%    \end{macrocode}
% Now we're guaranteed to be in a piece, rather than a phantom, we
% look for a piece that can fit this note, making sure to decrement
% the piece count and pop off a phantom for each new piece we check.
% Once it's found, we add the note back to \cs{mfx@marginout} with
% the correct piece.
%    \begin{macrocode}
  \if\MFX@check@fit{\advance\Mfx@piece@count\m@ne
                    \MFX@popdimen\dimen@\mfx@phantomheights}{\ht#1+\dp#1}%
    \MFX@snoc\mfx@marginout{%
      \noexpand\mfx@build@note{#1}{#2}{\the\Mfx@piece@count}}%
    \let\mfx@build@skip\MFX@margin@skip@up
  \else\MFX@AssertionError\fi
}
%    \end{macrocode}
% \end{macros}
%
% \begin{macros}{\MFX@margin@skip@up}
% Skips are similar, but we have the added complication of handling
% margin phantoms.  When we cross between phantom and piece, we split
% the skip so that we can use the simplest recursion possible.
%    \begin{macrocode}
\def\MFX@margin@skip@up#1{%
%<debug>\MFX@debug{margin@skip@up: #1}%
  \dimen@#1\relax
  \advance\Mfx@marginheight-\dimen@
  \ifdim\Mfx@marginheight<\z@
%    \end{macrocode}
% This skip was bigger than the piece, so we need to split this skip,
% adding the overflow back, and try again.  Since we're done with this
% piece, we'll pop the next one and recurse on whatever's left.  This
% looks slightly different depending on whether or not we're in a phantom.
%    \begin{macrocode}
    \advance\dimen@\Mfx@marginheight
    \MFX@snoc\mfx@marginout{%
      \noexpand\mfx@build@skip{\the\dimen@}{\the\Mfx@piece@count}}%
    \dimen@-\Mfx@marginheight
    \ifmfx@in@phantom
      \MFX@popdimen\Mfx@marginheight\mfx@pieceheights
      \advance\Mfx@piece@count\m@ne
      \mfx@in@phantomfalse
    \else
      \MFX@popdimen\Mfx@marginheight\mfx@phantomheights
      \mfx@in@phantomtrue
    \fi
    \mfx@build@skip\dimen@
  \else
%    \end{macrocode}
% This skip fit entirely within the phantom, so we simply emit it.
%    \begin{macrocode}
    \MFX@snoc\mfx@marginout{%
      \noexpand\mfx@build@skip{\the\dimen@}{\the\Mfx@piece@count}}%
  \fi
}
%    \end{macrocode}
% \end{macros}
%
% \subsection{Margin pieces}
% \begin{macros}{\MFX@buildmargin@pieces}
% Before we can start the third and fourth passes, we need to set up
% a loop over the pieces so that each piece can do these passes at
% one time.  In case we didn't use up all the pieces in the second
% phase, we'll reset \cs{Mfx@piece@count} to zero.  We also reset
% \cs{Mfx@marginspace} and \cs{Mfx@marginbox}.
%    \begin{macrocode}
\def\MFX@buildmargin@pieces{%
  \Mfx@piece@count\z@
  \Mfx@marginspace\z@
  \setbox\Mfx@marginbox\vbox{\vskip\z@}%  TODO - do we need this?
%    \end{macrocode}
% Now we run over the individual margin pieces, clearing it out as we
% build up the contents of \cs{Mfx@marginbox}.  Once we're done with
% that, we need to do a bit of clean-up before finishing.
%    \begin{macrocode}
  \let\@elt\MFX@buildmargin@piece
  \MFX@run@clear\mfx@marginpieces
  \let\@elt\relax
  \global\Mfx@piece@count\z@
}
%    \end{macrocode}
% \end{macros}
%
% \begin{macros}{\MFX@buildmargin@piece}
% The \cs{MFX@buildmargin@piece} macro is called for each piece of
% the margin, and is passed the top and bottom positions of the piece.
% Here we need to do a few things.  First, if the output so far is
% smaller than the top of the piece (this is generally true, except
% sometimes for the first piece) then we need to insert padding into
% \cs{Mfx@marginbox} before continuing.  We accumulate this padding
% into \cs{Mfx@marginspace}, which allows skips to do double-duty
% across phantoms.
%    \begin{macrocode}
\def\MFX@buildmargin@piece#1#2{%
%<debug>\MFX@debug{buildmargin@piece: ENTRY (#1, #2)}%
  \ifdim\ht\Mfx@marginbox<#1\relax
    \dimen@\dimexpr#1-\ht\Mfx@marginbox\relax
%<debug>\MFX@debug{buildmargin@piece: padding \the\dimen@}%
    \setbox\Mfx@marginbox\vbox{%
      \unvbox\Mfx@marginbox
      \vskip\dimen@
    }%
    \advance\Mfx@marginspace\dimen@
  \fi
%    \end{macrocode}
% Now that \cs{Mfx@marginbox} has been padded, we proceed to set
% things up for our down and up passes, and then run them to build
% \cs{Mfx@piece@content}.
%    \begin{macrocode}
  \Mfx@marginpos@min#1\relax
  \Mfx@marginpos@max#1\relax
  \Mfx@marginheight#2\relax
  \advance\Mfx@piece@count\@ne
  \MFX@buildpiece@down
  \MFX@buildpiece@up
%    \end{macrocode}
% Once \cs{Mfx@piece@content} has been built, we append it to the
% \cs{Mfx@marginbox}.
%    \begin{macrocode}
  \setbox\Mfx@marginbox\vbox{%
    \unvbox\Mfx@marginbox
    \box\Mfx@piece@content
    \vskip\z@
  }%
}
%    \end{macrocode}
% \end{macros}
%
% \subsection{Third pass: piecewise downward}
% \begin{macros}{\MFX@buildpiece@down,\mfx@pieceout}
% The next pass is another downward pass.  This is very similar
% to the first (global downward) pass, except we're now moving
% the first several notes from \cs{mfx@marginout} into
% \cs{mfx@pieceout} (again, inverting the order) and deferring
% the rest back into \cs{mfx@marginout}.  
%    \begin{macrocode}
\def\MFX@buildpiece@down{%
%<debug>\MFX@debug{buildpiece@down: ENTRY piece=\the\Mfx@piece@count,
%<debug>    marginpos=(\the\Mfx@marginpos@min, \the\Mfx@marginpos@max),
%<debug>    marginspace=\the\Mfx@marginspace,
%<debug>    marginout=\MFX@mac\mfx@marginout}%
  \let\mfx@build@note\MFX@piece@note@down
  \let\mfx@build@skip\MFX@piece@skip@down
  \let\mfx@pieceout\@empty
  \MFX@run@clear\mfx@marginout
%<debug>\MFX@debug{buildpiece@down: RETURN pieceout=\MFX@mac\mfx@pieceout,
%<debug>    marginout=\MFX@mac\mfx@marginout}%
}
%    \end{macrocode}
% \end{macros}
%
% \begin{macros}{\MFX@piece@note@down}
% We again define each of our building macros.  First, the note builder.
% When we encounter a note, we first zero out \cs{Mfx@marginspace}.  Then
% we need to decide whether to put it in the current piece, or whether
% to defer it to the next piece, based on a few signals.  A note will only
% be deferred for one of two reasons.  In either case, we store the decision
% to defer in |@tempswa|, so we'll start by clearing it.
%    \begin{macrocode}
\def\MFX@piece@note@down#1#2#3{%
%<debug>\MFX@debug{piece@note@down: ENTRY: #1at #2, lowest=#3,
%<debug>           marginpos=(\the\Mfx@marginpos@min,\the\Mfx@marginpos@max,
%<debug>           marginspace=\the\Mfx@marginspace}%
  \Mfx@marginspace\z@
  \@tempswafalse
%    \end{macrocode}
% The first reason to defer is if its callout position (|#2|)
% is beneath the end of the current piece (\cs{Mfx@marginheight}) \emph{and}
% if its lowest-possible-piece (|#3|, as computed in the second pass) is
% \emph{after} the current one (\cs{Mfx@piece@count}).
%    \begin{macrocode}
  \ifdim#2>\Mfx@marginheight
    \ifnum#3>\Mfx@piece@count
      \@tempswatrue
    \fi
  \fi
%    \end{macrocode}
% The second possibility is that we've run out of room in this piece.
% In this case, there's no need to check |#3|, since that number was
% computed assuming everything that fit was as low as possible.
%    \begin{macrocode}
  \ifdim\dimexpr\ht#1+\dp#1+\Mfx@marginpos@min>\Mfx@marginheight
    \@tempswatrue
  \fi
%    \end{macrocode}
% If a note is deferred, then we push it and everything after it
% back onto \cs{mfx@marginout}.
%    \begin{macrocode}
  \if@tempswa
%<debug>\MFX@debug{piece@note@down: clearing margin}%
    \MFX@piece@clear
    \mfx@build@note{#1}{#2}{#3}%
  \else
%    \end{macrocode}
% Otherwise, we have decided the note is going into this piece.  In this
% case, we now need to check if any compressible space is needed above it.
% In order to get better alignment for deferred notes, we check for the
% case that the current position is zero and the note's callout position
% is \cs{MFX@minus@inf}.  In this case, we change the callout position
% to instead be the greater of 0 or $\cs{topskip}-\cs{ht}\#1$.
%    \begin{macrocode}
    \dimen@#2\relax
    \ifdim\dimen@=\MFX@minus@inf
      \ifdim\Mfx@marginpos@max=\z@
        \dimen@\topskip
        \advance\dimen@-\ht#1\relax
        \ifdim\dimen@<\z@ \dimen@\z@ \fi
      \fi
    \fi
    \advance\dimen@-\Mfx@marginpos@max
    \ifdim\dimen@>\z@
%<debug>\MFX@debug{piece@note@down: adding compressible \the\dimen@}%
      \MFX@snoc\mfx@pieceout{\noexpand\mfx@build@compressible{\the\dimen@}}%
      \advance\Mfx@marginpos@max\dimen@
    \fi
%    \end{macrocode}
% After (maybe) adding the compressible space, we now add the
% (incompressible) box itself, and accumulate its height in both position
% registers.  We no longer need the piece index or the position, so we
% only store the box itself here.
%    \begin{macrocode}
    \MFX@snoc\mfx@pieceout{\noexpand\mfx@build@note{#1}}%
    \advance\Mfx@marginpos@min\dimexpr\ht#1+\dp#1\relax
    \advance\Mfx@marginpos@max\dimexpr\ht#1+\dp#1\relax
  \fi    
}
%    \end{macrocode}
% \end{macros}
%
% \begin{macros}{\MFX@piece@skip@down}
% Skips are a bit more complicated now.  We no longer gobble the
% initial skips (since the skips at the top and bottom of the page
% have already been eaten).  Instead, we need to look at
% \cs{Mfx@marginspace}: if it's nonzero, we substract it from the
% skip length before adding it incompressibly (if there's any left).
% While we have piece number information here, we just pass it on
% to the upward pass, which can choose to ``late-defer'' initial
% (i.e. the bottom-most) skips in a piece.
%    \begin{macrocode}
\def\MFX@piece@skip@down#1#2{%
  \dimen@#1\relax
  \ifdim\Mfx@marginspace>\z@
    \advance\dimen@-\Mfx@marginspace
    \ifdim\dimen@<\z@ \dimen@\z@ \fi
    \advance\Mfx@marginspace-\dimen@
  \fi
%    \end{macrocode}
% At this point, \cs{dimen@} now stores any further incompressible
% skip we need to add, which is now relative to the top of this piece.
% In that case (note that \cs{Mfx@marginspace} is now necessarily zero),
% we need to check that it actually fits in the current piece, and if
% not, defer it.
%    \begin{macrocode}
  \ifdim\dimen@>\z@
    \ifdim\dimexpr#1+\Mfx@marginpos@min>\Mfx@marginheight
      \MFX@piece@clear
      \mfx@build@skip{\the\dimen@}{#2}%
    \else
%    \end{macrocode}
% If the skip \emph{does} fit, we need to add it to \cs{mfx@pieceout}
% and advance the position registers.
%    \begin{macrocode}
      \MFX@snoc\mfx@pieceout{\noexpand\mfx@build@skip{\the\dimen@}{#2}}%
      \advance\Mfx@marginpos@min\dimen@
      \advance\Mfx@marginpos@max\dimen@
    \fi
  \fi
}
%    \end{macrocode}
% \end{macros}
%
% \begin{macros}{\MFX@piece@clear}
% Finally, we need to handle the case of deferring material.  By
% analogy with the previous two passes, we'll continue to refer to
% this as clearing.  In this case, we need to redefine the note and
% skip macros to save themselves back to \cs{mfx@marginout}.
%    \begin{macrocode}
\def\MFX@piece@clear{%
%<debug>\MFX@debug{piece@clear}%
  \def\mfx@build@note##1##2##3{%
    \MFX@cons\mfx@marginout{\noexpand\mfx@build@note##1{##2}{##3}}}%
  \def\mfx@build@skip##1##2{%
    \MFX@cons\mfx@marginout{\noexpand\mfx@build@skip{##1}{##2}}}%
}
%    \end{macrocode}
% \end{macros}
%
% \subsection{Fourth pass: piecewise upward}
% \begin{macros}{\MFX@buildpiece@up}
% We are now ready for the final pass, where we take the reversed
% list of notes for this piece and stack them up, compressing as much
% compressible space as necessary to make them all fit.  Since this
% is the last pass, we can finally prepend all the boxes into
% \cs{Mfx@piece@content}.  Because it is still possible for pieces
% to have skips at the bottom, we need to worry about which piece
% these skips belong in.  Since we've passed forward the lowest-piece
% information from pass 2, we can choose at the last possible moment
% to defer the bottom-most skips of a piece to the next piece (by
% prepending it to \cs{mfx@marginout}.  Note that compressible spaces
% will \emph{never} be at the beginning of \cs{mfx@pieceout}.  We
% store the excess space in \cs{Mfx@marginheight}.
%    \begin{macrocode}
\def\MFX@buildpiece@up{%
  \Mfx@marginheight\dimexpr\Mfx@marginpos@max-\Mfx@marginheight\relax
  \ifdim\Mfx@marginheight<\z@\Mfx@marginheight\z@\fi
%<debug>\MFX@debug{buildpiece@up: excess=\the\Mfx@marginheight}%
  \let\mfx@build@note\MFX@piece@note@up
  \let\mfx@build@compressible\MFX@piece@compressible@up
  \let\mfx@build@skip\MFX@piece@skip@maybedefer
  \MFX@run@clear\mfx@pieceout\relax
}
%    \end{macrocode}
% \end{macros}
%
% \begin{macros}{\MFX@piece@skip@maybedefer}
% As mentioned earlier, the initial skips in a piece may be deferred
% to later pieces, provided they can possibly go there.  Here we
% check the |#2| argument against \cs{Mfx@piece@count} and defer if
% it is larger.  Otherwise, we switch back to the standard behavior
% defined in \cs{MFX@piece@skip@up}.  Deferred skips do not need to
% be subtracted from the excess because they were never compressible
% in the first place.
%    \begin{macrocode}
\def\MFX@piece@skip@maybedefer#1#2{%
  \ifnum#2>\Mfx@piece@count
%<debug>\MFX@debug{piece@skip deferring: #1, #2 (\the\Mfx@piece@count)}%
    \MFX@snoc\mfx@marginout{\noexpand\mfx@build@skip{#1}{#2}}%
  \else
    \let\mfx@build@skip\MFX@piece@skip@up
    \mfx@build@skip{#1}{#2}%
  \fi
}
%    \end{macrocode}
% \end{macros}
%
% \begin{macros}{\MFX@piece@note@up}
% Now that we've taken care of late deferrals, we can define the
% standard behavior without worrying as much about that.
% These macros finally set their contents into \cs{Mfx@piece@content},
% as well as reset \cs{mfx@build@skip} back to its standard value.
%    \begin{macrocode}
\def\MFX@piece@note@up#1{%
%<debug>\MFX@debug{piece@note@up: #1}%
  \setbox\Mfx@piece@content\vbox{%
    \box#1%
    \unvbox\Mfx@piece@content}%
  \let\mfx@build@skip\MFX@piece@skip@up
}
%    \end{macrocode}
% \end{macros}
% \begin{macros}{\MFX@piece@skip@up}
% Skips are also straightforward.
%    \begin{macrocode}
\def\MFX@piece@skip@up#1#2{%
%<debug>\MFX@debug{skip@up: #1=\the\dimexpr#1\relax (#2)}%
  \setbox\Mfx@piece@content\vbox{%
    \vskip#1\relax
    \unvbox\Mfx@piece@content}%
}
%    \end{macrocode}
% \end{macros}
%
% \begin{macros}{\MFX@piece@compressible@up}
% Finally we come to the compressible space.  Here, as long as
% there's excess space (\cs{Mfx@marginheight}) we drop the
% compressible space.  Once the excess is exhausted, we insert
% them as normal skips.
%    \begin{macrocode}
\def\MFX@piece@compressible@up#1{%
%<debug>\MFX@debug{compressible@up: #1, excess=\the\Mfx@marginheight}%
  \advance\Mfx@marginheight-#1\relax
  \ifdim\Mfx@marginheight<\z@
    \MFX@piece@skip@up{-\Mfx@marginheight}\relax
    \Mfx@marginheight\z@
  \fi
}
%    \end{macrocode}
% \end{macros}
%
% \section{Cleaning up}
% We need to worry about a few more things.  First, what happens
% if we reach the end of the document and there are still deferred
% margin notes?  We need to be able to dump all the margin notes
% whenever the user wants (i.e. before a new chapter), so we'll
% make a macro \cs{dumpmargins} to do this, and then make sure it
% gets called \cs{AtEndDocument}.  Since we're looping to do this,
% we need to make darned sure that every \cs{newpage} shrinks the
% marginlist.
% \begin{macros}{\dumpmargins}
%    \begin{macrocode}
\def\dumpmargins{%
%<debug>\MFX@debug{dumpmargins}%
  \loop
  \unless\ifx\mfx@marginlist\@empty
%<debug>\MFX@debug{dumpmargins: marginlist=\MFX@mac\mfx@marginlist}%
    \let\temp@\mfx@marginlist
    \vbox{}\clearpage
    \ifx\temp@\mfx@marginlist
      \PackageError{marginfix}{lost some margin notes%
      \ifx\mfx@marginstart\relax\ (missing \noexpand\unblockmargin)\fi
%<debug>: \MFX@mac\mfx@marginlist
      }\@eha
      \let\mfx@marginlist\@empty % be nicer by just dropping one?
      % TODO: also, set an emergency mode to allow oversized notes
    \fi
  \repeat
}
\AtEndDocument{\dumpmargins}
%    \end{macrocode}
% \end{macros}
%
% \section{User macros}
%
% \begin{macros}{\marginskip}
% Inserting a skip in the margin list is simple.  We need only
% append \cs{mfx@build@skip} to \cs{mfx@marginlist}.
%    \begin{macrocode}
\def\marginskip#1{%
  \MFX@cons\mfx@marginlist{\noexpand\mfx@build@skip{#1}}%
}
%    \end{macrocode}
% \end{macros}
%
% \begin{macros}{\clearmargin}
% Likewise, \cs{clearmargin} is easy too.
%    \begin{macrocode}
\def\clearmargin{%
  \MFX@cons\mfx@marginlist{\noexpand\mfx@build@clear}%
}
%    \end{macrocode}
% \end{macros}
% \begin{macros}{\softclearmargin}
% While we call \cs{softclearmargin} a ``clear margin'', it's
% actually just a big \cs{marginskip}.  This allows us to stack
% multiple copies without backing them all up.
%    \begin{macrocode}
\def\softclearmargin{%
  \marginskip{\the\textheight}%
}
%    \end{macrocode}
% \end{macros}
%
% \begin{macros}{\extendmargin}
% We overload \cs{Mfx@marginheight} to be the amount of extension
% at all times except shipout-time.
%    \begin{macrocode}
\def\extendmargin#1{%
  \advance\Mfx@marginheight#1\relax
}
%    \end{macrocode}
% \end{macros}
%
% \begin{macros}{\mparshift}
% This is as simple as setting the dimen register.  We advance so that
% the shifts are cumulative, but there's not really any point either way.
%    \begin{macrocode}
\def\mparshift#1{%
  \advance\Mfx@mparshift#1\relax
}
%    \end{macrocode}
% \end{macros}
%
% \begin{macros}{\blockmargin,\MFX@blockmargin}
% We need two macros to process the optional bracket argument.
% Essentially, \cs{blockmargin} just checks that
% \cs{mfx@marginstart} is defined and then appends a new item to
% the \cs{mfx@marginpieces} list to indicate a piece that starts
% at \cs{mfx@marginstart} and ends at the current position, potentially
% modified by the argument.  It then resets \cs{mfx@marginstart} to
% \cs{relax} and optionally adds a \cs{softclearmargin} if there was no
% star, to keep margin material separated on either side of the phantom.
%    \begin{macrocode}
\def\blockmargin{%
  \@ifnextchar[%]
    \MFX@blockmargin
    {\MFX@blockmargin[0\p@]}%
}
\def\MFX@blockmargin[#1]{%
  \MFX@inject{%
    \ifx\mfx@marginstart\relax
      \PackageError{marginfix}{two \\blockmargin with no \\unblockmargin}\@eha
    \else
      \MFX@cons\mfx@marginpieces{\noexpand
        \@elt{\mfx@marginstart}{\expandafter\dimexpr\mfx@ypos+#1\relax}}%
      \global\let\mfx@marginstart\relax
      \global\advance\Mfx@piece@count\@ne
    \fi
  }%
}
%    \end{macrocode}
% \end{macros}
%
% \begin{macros}{\unblockmargin,\MFX@unblockmargin}
% This is the other half of \cs{blockmargin}.  It ensures that the
% margin is currently blocked, and if so, sets \cs{mfx@marginstart}
% back to a real dimension (the current page position, plus the optional
% modifier).
%    \begin{macrocode}
\def\unblockmargin{%
  \@ifnextchar[%]
    \MFX@unblockmargin
    {\MFX@unblockmargin[0\p@]}%
}
\def\MFX@unblockmargin[#1]{%
  \MFX@inject{%
    \ifx\mfx@marginstart\relax
      \xdef\mfx@marginstart{\dimexpr\mfx@ypos+#1\relax}%
    \else
      \PackageError{marginfix}{\\unblockmargin with no \\blockmargin}\@eha
    \fi
  }%
}
%    \end{macrocode}
% \end{macros}
%
% \begin{macros}{\marginphantom,\MFX@marginphantom}
% The \cs{marginphantom} command is basically just a concatenation of
% \cs{blockmargin} and \cs{unblockmargin}.  We reimplement it from the
% ground up mainly so that the error messages make more sense.
%    \begin{macrocode}
\def\marginphantom{%
  \@ifnextchar[%]
    \MFX@marginphantom
    {\MFX@marginphantom[0\p@]}%
}
\def\MFX@marginphantom[#1]#2{%
  \ifdim#2<\z@\MFX@marginphantom[#1+#2]{-#2}\else
  \MFX@inject{%
    \ifx\mfx@marginstart\relax
      \PackageError{marginfix}{\\marginphantom while margin blocked}\@eha
    \else
      \MFX@cons\mfx@marginpieces{\noexpand
        \@elt{\mfx@marginstart}{\expandafter\dimexpr\mfx@ypos+#1\relax}}%
      \xdef\mfx@marginstart{\dimexpr\mfx@ypos+#1+#2\relax}%
      \global\advance\Mfx@piece@count\@ne
    \fi
  }%
  \fi
}
%    \end{macrocode}
% \end{macros}
%
% \section{Random scribbles}
%
% Later we'll get fancier with putting notes next to top/bottom
% figures but for now, not so much.
%
% In the future we will support the use of \cs{pdfsavepos} and
% \cs{pdflastypos} for more accurately determining where the callouts
% actually were, which will end up going right around here.  But in
% order to work with older versions of \LaTeX, we still need to
% support the old style of using \cs{@pageht} to figure that out, so for
% now that's all we'll do.
%
% \section{Parting words}
% Finish it up.
%    \begin{macrocode}
\makeatother
%</package>
%    \end{macrocode}
% \makeatother
% \Finale
%
%
% \iffalse
%
% The next line of code prevents DocStrip from adding the
% character table to the generated files(s).
\endinput
% \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         \~}
%%
