# Multiple \marginnote commands on the same line in math environment

If multiple \marginnote commands are triggered on the same line, by default they are typeset on top of one another, making them unreadable. I would like them to stack vertically, which is the default behaviour of \marginpar. It doesn't matter that some of them will therefore not be rendered immediately adjacent to the line in which they were generated.

I need this to work both in math environments and non-math environments, so I can't use \marginpar itself. And I have this all over the place, so it has to be automatic like marginpar; I can't manually adjust the vertical spacing of each one.

• \marginnote has an optional argument for vertical offset: \marginnote[<left>]{<right>}[<voffset>] – clemens Dec 4 '14 at 11:18
• Thanks. I have this all over the place so it has to be automatic, just like marginpar. – Jamie Vicary Dec 4 '14 at 11:21
• Do you want it to expand downward or in both directions? BTW, a MWE would be nice. – John Kormylo Dec 4 '14 at 14:49
• Can you add an example of what you're trying to achieve? – egreg Dec 4 '14 at 16:06
• I think can do this, but it will take some time. In addition to not overlapping, I assume you don't want it going off the bottom of the page either. – John Kormylo Dec 5 '14 at 20:57

It takes two runs for the margin notes to show up. I put the content as well as the location into the aux file so that I didn't have to synch two different sources.

I used \marginparpush to separate consecutive margin notes vertically. I added \marginfillstyle to set the font etc.

\documentclass{article}
\usepackage{everypage}
\usepackage{amsmath}

\newcounter{abspage}

\pdfpageheight\paperheight
\pdfpagewidth\paperwidth

% user modifiable formatting
\newcommand{\marginfillstyle}{\raggedright\normalsize\normalfont\hspace{0pt}}

\makeatletter
\newcount\MF@index% global variables
\newcount\MF@total
\newlength{\MF@x}% marginpar offset
\newlength{\MF@y}% pagehook cursor location
\newlength{\MF@top}% marginpar area
\newlength{\MF@bottom}
\newlength{\MF@width}% \marginparwidth might get clobbered
\newlength{\MF@push}% \marginparpush might get clobbered
\newcount\MF@min% needed for nested loops
\newcount\MF@max
\newif\ifMF@repeat
\newif\ifMF@main

\newcommand{\marginfill}[1]% #1 = text for margin note
{\pdfsavepos
\protected@write\@auxout{\let\theabspage=\relax}{\string\newmarginfill{\theabspage}%
{\noexpand\number\pdflastypos}{#1}}%
}%

\newcommand{\newmarginfill}[3]% #1 = abspage, #2 = location, #3 = text
\expandafter\gdef\csname MF@page\the\MF@total\endcsname{#1}%
\expandafter\gdef\csname MF@location\the\MF@total\endcsname{#2}%
\expandafter\gdef\csname MF@note\the\MF@total\endcsname{#3}%
}%

\newcommand{\marginfillpage}% cursor 1in from top left corner
{\stepcounter{abspage}%
\ifodd\c@page
\setlength{\MF@x}{\oddsidemargin}%
\else
\setlength{\MF@x}{\evensidemargin}%
\fi
\setlength{\MF@y}{\paperheight}%
\setlength{\MF@top}{\MF@y}%
\setlength{\MF@bottom}{\MF@top}%
\setlength{\MF@width}{\marginparwidth}%
\setlength{\MF@push}{\marginparpush}%
% This algorithm overlays a lot of dimen registers, depending on how many notes are on the page.
% These registers will be restored by the \endgroup, but many will not be available until then.
% The \MF@...  dimens defined above should be safe.
\begingroup
\countdef\index=1
\index=0
\MF@repeattrue
\ifnum\MF@index<\MF@total
\edef\temp{\the\MF@index}%
\@ifundefined{MF@page\temp}{}{%
\ifnum\value{abspage}=\csname MF@page\temp\endcsname\relax
\dimendef\yloc=\index
\yloc=\csname MF@location\temp\endcsname sp\relax
\savebox{\index}{\parbox[c]{\MF@width}%
{\marginfillstyle \csname MF@note\temp\endcsname}}%
\else
\MF@repeatfalse
\fi}%
\else\MF@repeatfalse
\fi
\ifMF@repeat \repeat
\ifnum\index>0 \MF@process\fi
\endgroup}

%***** moduals used by \marginfillpage *****

\newcommand{\MF@process}%
{\countdef\total=2
\countdef\min=3
\countdef\max=4
\total=\index
\dimendef\tempdima=\index
\dimendef\tempdimb=\index
\dimendef\tempdimc=\index
% resolve overlaps
\MF@freespace{\tempdimb}%
\ifdim\tempdimb<1pt \MF@cram{\tempdimb}%
\else \MF@findsolution
\fi
% output notes to page
\index=0
\loop
\dimendef\yloc=\index
\MF@adjust=\yloc% in case \rlap or \raisebox clobbers \yloc
\ifnum\index<\total \repeat}

{\let\freespace=#1%
\freespace=\MF@top
\index=0
\loop% check for no solution
\settoheight{\tempdima}{\usebox{\index}}%
\settodepth{\tempdima}{\usebox{\index}}%
\ifnum\index<\total\relax \repeat

\newcommand{\MF@cram}[1]% #1 = free space
{\let\top=#1%
\top=-0.5\top% center excess
\index=0
\loop% overlap top and bottom
\settoheight{\tempdima}{\usebox{\index}}%
\dimendef\yloc=\index
\yloc=\top
\settodepth{\tempdima}{\usebox{\index}}%
\ifnum\index<\total\relax \repeat}

\newcommand{\MF@findsolution}% main loop
{\min=0% start of middle
\max=\total% end of middle+1
\loop
\MF@mainfalse
\ifMF@main\relax\else% wait until top and bottom done
\MF@min=\min
\MF@max=\max
\ifnum\MF@min<\MF@max \MF@locate\fi% find overlapping notes in middle
\ifnum\MF@min<\MF@max
\MF@maintrue
\MF@apply% cannot use nested loop
\ifnum\MF@min<\MF@max \MF@apply\fi
\ifnum\MF@min<\MF@max \MF@apply\fi
\ifnum\MF@min<\MF@max \MF@apply\fi
\ifnum\MF@min<\MF@max \MF@apply\fi
\ifnum\MF@min<\MF@max \MF@apply\fi
\ifnum\MF@min<\MF@max
\errormessage{Exceeded \string\marginfill algorithm capacity.}
\fi
\MF@apply
\fi
\fi
\ifMF@main \repeat}

{\let\top=\tempdimb
\ifnum\min=0 \top=\MF@top
\else
\index=\min
\dimendef\yloc=\index
\top=\yloc
\settodepth{\tempdima}{\usebox{\index}}%
\fi
\settoheight{\tempdima}{\usebox{\min}}%
\dimendef\yloc=\min
\ifdim\top<\yloc
\yloc=\top
\MF@maintrue
\fi}

{\let\bottom=\tempdimb
\ifnum\max=\total \bottom=\MF@bottom
\else
\dimendef\yloc=\max
\bottom=\yloc
\settoheight{\tempdima}{\usebox{\max}}%
\fi
\index=\max
\settodepth{\tempdima}{\usebox{\index}}%
\dimendef\yloc=\index
\ifdim\bottom>\yloc
\yloc=\bottom
\max=\index
\MF@maintrue
\fi}

\newcommand{\MF@locate}% returns \MF@min, \MF@max and \MF@adjust
{\begingroup% needed for nested loop
\let\top=\tempdimb
\let\test=\tempdimc
\dimendef\yloc=\min
\top=\yloc
\settodepth{\tempdima}{\usebox{\min}}%
%
\size=0pt
\index=\min
\loop% find largest overlap
\settoheight{\tempdima}{\usebox{\index}}%
\dimendef\yloc=\index
\test=\yloc
\ifdim\test>\size \size=\test
\MF@min=\index
\fi
\top=\yloc
\settodepth{\tempdima}{\usebox{\index}}%
\ifnum\index<\MF@max \repeat% \MF@max = \max-1
\MF@max=\MF@min
\ifdim\size>1pt
\MF@expand% find all contiguous overlaps
\fi
\global\MF@min=\MF@min
\global\MF@max=\MF@max
\endgroup}

\newcommand{\MF@expand}% locate all contiguous margin notes
{\let\test=\tempdimb
\dimendef\yloc=\MF@min
\test=\yloc
\loop
\MF@repeatfalse
\ifnum\MF@min > \min
\settoheight{\tempdima}{\usebox{\MF@min}}%
\settodepth{\tempdima}{\usebox{\MF@min}}%
\dimendef\yloc=\MF@min
\else\MF@repeattrue
\fi
\fi
\ifMF@repeat \repeat
%
\dimendef\yloc=\MF@max
\test=\yloc
\loop
\MF@repeatfalse
\ifnum\MF@max>\max\relax\else
\settodepth{\tempdima}{\usebox{\MF@max}}%
\settoheight{\tempdima}{\usebox{\MF@max}}%
\dimendef\yloc=\MF@max
\else\MF@repeattrue
\fi
\fi
\ifMF@repeat \repeat}

{\let\offset=\tempdimb
\offset=0pt
\index=\MF@min
\loop
\settoheight{\tempdima}{\usebox{\index}}%
\dimendef\yloc=\index
\settodepth{\tempdima}{\usebox{\index}}%
\ifnum\index>\MF@max\relax\else \repeat

\newcommand{\MF@apply}% corrects one margin note at a time
{\settoheight{\tempdima}{\usebox{\MF@min}}%
\dimendef\yloc=\MF@min
\settodepth{\tempdima}{\usebox{\MF@min}}%
\makeatother

\begin{document}
\noindent\rule{\textwidth}{1pt}
First line \marginfill{First note.}%
with not enough room. \marginfill{Second note}

\vfill
Isolated group in display style
\begin{align}
x &= a &\marginfill{Third note is a longer note.}\\
y &= b &\marginfill{Fourth note is a longer note.}
\end{align}

\vfill
Last line \marginfill{Fifth note.}%
with not enough room. \marginfill{Sixth note.}

\noindent\rule{\textwidth}{1pt}
\newpage
First line in text area \marginfill{text area}
\begin{figure}[t]
\marginfill{float}
\centering\rule{.5in}{.5in}
\end{figure}
\end{document}


• Thanks for this nice analysis! I typically have 2 or 3 margin notes declared on the same line, so the unconstrained approach would definitely not be sufficient (and anyway that's what \marginnote allows already.) – Jamie Vicary Dec 8 '14 at 18:04
• I am still debugging my code, but am very close. However, I have been spending way too much time on this and will not return to the project until after Xmas. – John Kormylo Dec 18 '14 at 22:51
• Okay, it's done. I had an epiphany and had to try it out. – John Kormylo Dec 20 '14 at 4:59
• Wow, this looks amazing (+1). Are you aware of any bugs/limitations? If this is reliable it could be a package (and I would use it)! – Circumscribe Jan 29 at 19:30
• No bugs that I know of, but I keep wanting to redo the whole thing using global macros instead of local registers. – John Kormylo Jan 30 at 14:02