% \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} % % %\NeedsTeXFormat{LaTeX2e} %\ProvidesPackage{marginfix}% [2010/08/28 v0.9.1 Fix Margin Paragraphs] %<*driver> \documentclass{ltxdoc} \CheckSum{546} %\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} % % \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.} % % \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. % % \section{Options} % There are currently no options that do anything yet. % % \section{Commands} % For the most part, this is a drop-in replacement. Simply add % |\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 \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 margins are in the wrong place, the callout % positions can be adjusted 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. % % \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 Margin phantoms. % \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 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} % \begin{macros}{\@ight,\f@ur,\@C} % Make the |@|-sign into a letter for use in macro names. We also % define a few other (hopefully) obvious and unambiguous macros that % other packages won't destructively clobber. % \begin{macrocode} %<*package> \makeatletter \chardef\@ight=8 \chardef\f@ur=4 \chardef\@c=100 % \end{macrocode} % \end{macros} % % \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. % The newline can be suppressed with a |*|. We'll also ask for more % error context in the debug mode. % \begin{macrocode} %\def\MFX@debug{\@ifstar\message{\message{^^JMFX:}\message}} %\errorcontextlines=20 % \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 (using the \pkg{inlinedef} package % whenever we can), 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. % \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} % % 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@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@marginboxspace,\Mfx@marginpos} % While we build up the margin box, we need to keep track of both % where we are in the margin (\cs{Mfx@marginpos}) and how % much of that space is incompressible (\cs{Mfx@marginboxspace}). % \begin{macrocode} \newdimen\Mfx@marginboxspace \newdimen\Mfx@marginpos % \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. % \begin{macrocode} \newdimen\Mfx@marginheight % \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 (of \@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. % \begin{macrocode} \def\@addmarginpar{% \@next\@marbox\@currlist{}\MFX@bug %\MFX@debug{addmarginpar (running insert) \@marbox/\@currbox at % \the\c@page:\the\@pageht, marginlist=\meaning\mfx@marginlist}% \MFX@getypos \MFX@cons\mfx@marginlist{% TODO: later this will be a run@marginlist \noexpand\mfx@margin@note\@marbox\@currbox{\mfx@ypos}% (^i.e. for phantoms) \noexpand\mfx@margin@skip{\the\marginparpush}% }% %\MFX@debug{addmarginpar (exit): marginlist=\meaning\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 % corectly 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@getypos,\mfx@ypos} % We now need to settle on a way to determine the vertical position. % Ultimately this will 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 will ultimately be only a fallback for when % \cs{pdflastypos} isn't available, it should be good enough. % NOTE: we might be able to use a \cs{vadjust} instead here? % \begin{macrocode} \def\MFX@getypos{% \edef\mfx@ypos{% \the\dimexpr\@pageht - \Mfx@strutheight + \marginposadjustment + \Mfx@mparshift\relax}% \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,\MFX@combinefloats@before} % 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} % % \cs{MFX@combinefloats@before} is then responsible for picking the % needed notes from \cs{mfx@marginlist}, building them into a box, and % attaching said box onto the correct side of \cs{@outputbox}. This is % also a convenient place to reset \cs{Mfx@marginheight} to zero (since % we reuse the register for one-time extensions). % \begin{macrocode} \def\MFX@combinefloats@before{% \MFX@buildmargin \MFX@attachmargin \Mfx@marginheight\marginheightadjustment } % \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{% %\MFX@debug{attachmargin}% % \end{macrocode} % First, we need to make sure that the boxes we're combining are the % same size. % \begin{macrocode} \ifdim\ht\@outputbox<\ht\Mfx@marginbox \setbox\@outputbox\vbox to \ht\Mfx@marginbox{% \box\@outputbox \vfill }% \else \setbox\Mfx@marginbox\vbox to \ht\@outputbox{% \unvbox\Mfx@marginbox \vfill }% \fi % \end{macrocode} % Next we need to figure out which side of \cs{@outputbox} to attach % the \cs{Mfx@marginbox} on. % \begin{macrocode} \setbox\@outputbox\vbox to \ht\@outputbox{% \hbox to \wd\@outputbox{% \if\MFX@leftmargin \llap{\box\Mfx@marginbox\hskip\marginparsep}% \box\@outputbox \else \box\@outputbox \rlap{\hskip\marginparsep\box\Mfx@marginbox}% \fi }}% } % \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. After it's done % working, \cs{mfx@marginlist} should have the first $n>0$ of these % removed, and leaving only notes that were deferred to the next % page. Additionally, \cs{Mfx@marginbox} must contain a % box the same height as \cs{@outputbox}. We do this in several % steps. We start with a height of \cs{@colroom} rather than % \cs{textheight} because \cs{@colroom} has already subtracted % off the top/bottom floats that have been set. Eventually we % will want to encroach on these floats, particularly if they % don't encroach on the margin, but that will be tricky... % \begin{macrocode} \def\MFX@buildmargin{% \advance\Mfx@marginheight\@colroom %\MFX@debug{buildmargin: marginheight=\the\Mfx@marginheight}% \MFX@buildmargin@down \MFX@buildmargin@up } % \end{macrocode} % \end{macros} % % \subsection{First pass} % \begin{macros}{\MFX@buildmargin@down} % The first step is the ``down'' step, in which we move the notes % that will go on the current page into \cs{mfx@marginout} in reverse order, % and anything that will be deferred gets put back in % \cs{mfx@marginlist}. This behavior is configured by changing % the meaning of \cs{mfx@margin@note}, \cs{mfx@margin@skip}, and % \cs{mfx@margin@clear}. Note that during the course of processing % \cs{mfx@marginlist}, these meanings will continue to change. % \begin{macrocode} \def\MFX@buildmargin@down{% %\MFX@debug{buildmargin@down: ENTRY}% %\MFX@debug{marginlist=\meaning\mfx@marginlist}% \let\mfx@margin@note\MFX@margin@note@down \let\mfx@margin@skip\@gobble \let\mfx@margin@clear\MFX@margin@clear@down \let\mfx@marginout\@empty % \end{macrocode} % We do a little bit of \cs{expandafter} trickery here to first expand % the current meaning of \cs{mfx@marginlist}, then clear it before % actually executing anything. % \begin{macrocode} \expandafter\global\expandafter\let \expandafter\mfx@marginlist\expandafter\@empty \mfx@marginlist %\MFX@debug{buildmargin@down: RETURN}% %\MFX@debug{marginlist=\meaning\mfx@marginlist}% } % \end{macrocode} % \end{macros} % % We must now define the different meanings for the % |\mfx@margin@...| macros. % \begin{macros}{\MFX@margin@note@down,\MFX@whichbox,% % \MFX@margin@skip@down,\MFX@margin@clear@down} % We'll dive right into the notes first. When we see a note in the % \cs{marginlist}, we need to do several things. % \begin{enumerate} % \item optionally add a \emph{compressible} skip before it, of length % $|#3| - \cs{Mfx@marginpos}$, to line it up with the callout % location % \item figure out which box we need for the current page, not yet freeing % the allocated boxes, since we may still end up deferring % \item add the height of the correct box to \cs{Mfx@marginboxspace} and % \cs{Mfx@marginpos} % \item if \cs{Mfx@marginboxspace} exceeds \cs{Mfx@marginheight}, then % we defer this note and change the meaning of \cs{mfx@margin@note} % to defer all remaining notes % \item otherwise, we add tokens to construct the box to \cs{toks@} and % free the allocated inserts. % \item redefine \cs{mfx@margin@skip} to \cs{MFX@margin@skip@down}, now % that we've gotten a real note (it starts out as \cs{@gobble} since % we don't want to apply skips at the very beginning). % \end{enumerate} % \begin{macrocode} \def\MFX@margin@note@down#1#2#3{% %\MFX@debug{note@down: ENTRY: #1/#2 at #3}% \ifdim#3>\Mfx@marginpos \dimen@\dimexpr#3-\Mfx@marginpos\relax %\MFX@debug{note@down: adding compressible \the\dimen@}% \MFX@snoc\mfx@marginout{\noexpand\mfx@margin@compressible{\the\dimen@}}% \advance\Mfx@marginpos\the\dimen@ \fi \MFX@whichbox#1#2% \advance\Mfx@marginboxspace\dimexpr\ht\@marbox+\dp\@marbox\relax \ifdim\Mfx@marginboxspace>\Mfx@marginheight % \end{macrocode} % We've run out of margin space, so we now defer every following box, % which means appending them back to \cs{mfx@marginlist}, except with % a callout position of zero, since we want them as high up as possible % (also, the position should be monotonic). % \begin{macrocode} %\MFX@debug{note@down: out of space: % \the\Mfx@marginboxspace>\the\Mfx@marginheight}% \advance\Mfx@marginboxspace\dimexpr-\ht\@marbox-\dp\@marbox\relax \mfx@margin@clear \mfx@margin@note#1#2{#3}% \else % \end{macrocode} % At this point, the box is definitely going onto this page, so we can % arrange for the boxes to be freed and then add the correct box to the % output list. % \begin{macrocode} %\MFX@debug{note@down: adding \@marbox % ht \the\ht\@marbox dp \the\dp\@marbox}% \advance\Mfx@marginpos\dimexpr\ht\@marbox+\dp\@marbox\relax \MFX@snoc\mfx@marginout{% \noexpand\mfx@margin@note\@marbox \noexpand\@cons\noexpand\@freelist#1% \noexpand\@cons\noexpand\@freelist#2% }% \fi \let\mfx@margin@skip\MFX@margin@skip@down %\MFX@debug{note@down: RETURN space=\the\Mfx@marginboxspace, % pos=\the\Mfx@marginpos}% } % \end{macrocode} % % We need to figure out which box to set. This macro calls % \cs{MFX@leftmargin} and then sets the correct box into % \cs{@marbox} for use by \cs{MFX@margin@box@down}. % \begin{macrocode} \def\MFX@whichbox#1#2{% \if\MFX@leftmargin \def\@marbox{#1}% \else \def\@marbox{#2}% \fi %\MFX@debug{whichbox: \@marbox}% } % \end{macrocode} % % The next macro we'll write is the skip. This one just has to save itself % onto whichever list we're working on (output or deferred) and in the case % of output, update \cs{@Mfx@marginpos}. % \begin{macrocode} \def\MFX@margin@skip@down#1{% %\MFX@debug{skip@down #1}% \advance\Mfx@marginpos#1\relax \advance\Mfx@marginboxspace#1\relax \MFX@snoc\mfx@marginout{\noexpand\mfx@margin@skip{#1}}% } % \end{macrocode} % % Finally, \cs{MFX@margin@clear@down} simply signals the end of this % margin. All further material will be deferred, so we redefine % \cs{mfx@margin@note}, \cs{mfx@margin@skip}, and \cs{mfx@margin@clear} % to defer. % \begin{macrocode} \def\MFX@margin@clear@down{% %\MFX@debug{clear@down}% \def\mfx@margin@note##1##2##3{% \MFX@cons\mfx@marginlist{\noexpand\mfx@margin@note##1##2{\MFX@minus@inf}}}% \def\mfx@margin@skip##1{% \MFX@cons\mfx@marginlist{\noexpand\mfx@margin@skip{##1}}}% \def\mfx@margin@clear{% \MFX@cons\mfx@marginlist{\noexpand\mfx@margin@clear}}% } % \end{macrocode} % \end{macros} % % \begin{macros}{\MFX@minus@inf} % Note that when we added deferred boxes to the list, we put them % at \cs{MFX@minus@inf}. We'll define that to be a large negative % dimension. % \begin{macrocode} \def\MFX@minus@inf{-4000pt} % \end{macrocode} % \end{macros} % % \begin{macros}{\MFX@leftmargin} % Here we figure out which box to use based on the page number and % other flags. This is a conditional that should be used after % \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. % \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 %\MFX@debug{margin on \ifnum\@tempcnta<\z@ left\else right\fi}% \ifnum\@tempcnta<\z@ % start a new \if } % \end{macrocode} % \end{macros} % % \subsection{Second pass} % \begin{macros}{\MFX@buildmargin@up} % Next is the ``up'' step. Here we simply take the reversed % list in \cs{mfx@marginout} and prepend each item in turn to % \cs{Mfx@marginbox}. We start by automatically discarding any % skips, though we can't do this with a simple \cs{@gobble} anymore % since we need to deduct them from \cs{Mfx@marginpos}. Once we % hit a note, we'll change the skips to be their normal meanings. % Note that there are no clears in \cs{mfx@marginout}. % \begin{macrocode} \def\MFX@buildmargin@up{% %\MFX@debug{buildmargin@up: excess=\the\dimexpr % \Mfx@marginpos-\Mfx@marginheight\relax}% %\MFX@debug{marginout=\meaning\mfx@marginout} \let\mfx@margin@note\MFX@margin@note@up \let\mfx@margin@compressible\MFX@margin@skip@gobble@up \let\mfx@margin@skip\MFX@margin@skip@gobble@up \mfx@marginout } % \end{macrocode} % \end{macros} % % \begin{macros}{\MFX@margin@skip@gobble@up} % In case we have any skips at the beginning of \cs{mfx@marginout}, % we'll gobble them and deduct their lenghts from \cs{Mfx@marginpos}. % \begin{macrocode} \def\MFX@margin@skip@gobble@up#1{% %\MFX@debug{skip@gobble@up: #1}% \advance\Mfx@marginpos-#1\relax } % \end{macrocode} % \end{macros} % % \begin{macros}{\MFX@margin@note@up} % Once we actually do hit a note, we need to set it in \cs{Mfx@marginbox}. % We also redefine \cs{mfx@margin@skip} and \cs{mfx@margin@compressible} % here. Hopefully any skips aren't being dropped by our \cs{unvbox}ing. % \begin{macrocode} \def\MFX@margin@note@up#1{% %\MFX@debug{note@up: #1}% \setbox\Mfx@marginbox\vbox{\box#1\unvbox\Mfx@marginbox}% \let\mfx@margin@skip\MFX@margin@skip@up \let\mfx@margin@compressible\MFX@margin@compressible@up } % \end{macrocode} % \end{macros} % % \begin{macros}{\MFX@margin@skip@up} % This one is even easier---all we have to do is add a skip to the % margin output box. We also need to make sure it's not getting dropped, % which might entail adding some \cs{vbox}\lit\{\lit\}s. % \begin{macrocode} \def\MFX@margin@skip@up#1{% %\MFX@debug{skip@up: #1}% \setbox\Mfx@marginbox\vbox{\vskip#1\relax\unvbox\Mfx@marginbox}% } % \end{macrocode} % \end{macros} % % \begin{macros}{\MFX@margin@compressible@up} % This token gets put into \cs{mfx@marginout} during the first pass. % When it executes, it inserts a \cs{vskip} depending on how % much extra margin space (stored in \cs{Mfx@marginpos}) % we need to excise. If also updates \cs{Mfx@marginpos}. % \begin{macrocode} \def\MFX@margin@compressible@up#1{% %\MFX@debug{compressible@up: #1, excess=\the\dimexpr % \Mfx@marginpos-\Mfx@marginheight\relax}% \dimen@#1\relax \ifdim\Mfx@marginpos>\Mfx@marginheight \advance\dimen@\dimexpr\Mfx@marginheight-\Mfx@marginpos\relax \ifdim\dimen@<\z@ \dimen@\z@ \fi \advance\Mfx@marginpos\dimexpr\dimen@-#1\relax \fi \ifdim\dimen@>\z@ \MFX@margin@skip@up\dimen@ \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{% %\MFX@debug{dumpmargins}% \loop \unless\ifx\mfx@marginlist\@empty \let\temp@\mfx@marginlist \vbox{}\clearpage \ifx\temp@\mfx@marginlist \PackageError{marginfix}{lost some margin notes% %: \meaning\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@margin@skip} to \cs{mfx@marginlist}. % \begin{macrocode} \def\marginskip#1{% \MFX@cons\mfx@marginlist{\noexpand\mfx@margin@skip{#1}}% } % \end{macrocode} % \end{macros} % % \begin{macros}{\clearmargin,\softclearmargin} % Likewise, \cs{clearmargin} is easy too. % \begin{macrocode} \def\clearmargin{% \MFX@cons\mfx@marginlist{\noexpand\mfx@margin@clear}% } % \end{macrocode} % 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} % % \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 % % \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 \~} %%