# Send (and stack) \marginpar to the top or the bottom of the page

• I'm looking to define something like \topmarginpar{<text>}, which would essentially create a \marginpar{<text>} and align its top with the top of the current page.

• I would also like multiple \topmarginpar calls to stack these \marginpar objects (as opposed to having them overlap).

• And finally I'd like to create a similar \bottommargin command.

Is there some existing package providing this functionality, or am I on my own here (I'm LaTeX fluent but worthless around raw TeX)?

Thanks for any suggestions...

-
–  Werner Aug 30 '12 at 22:18
spec questions: a) I assume that the text from \topmarginpar is supposed to show on the page that also contains the position the command was found. b) what should happen if the stacked \topmarginpars are bigger than the page, start a new page, or shift the marginpar to the next? c) I presume you do not want real marginpars at the same time, or do you? –  Frank Mittelbach Aug 31 '12 at 7:37
The memoir class has has \sidebar which looks like your \topmarginpar plus it has \sidefootnote which looks like a numbered version of your \bottommargin macro –  daleif Aug 31 '12 at 8:19

This solution deals with the asynchronous output routine:

• The pages are identified by absolute page numbers and the correct page numbers are available in the second LaTeX run using labels.

• Each page get two collector boxes that collect the marginal notes for the top and the bottom. At shipout time the absolute page number is known and the collector boxes for the page are output in the "marginpar" area of the page.

• Box registers are allocated dynamically and managed in a pool. After a page shipout, its collector box registers are freed and put back in the pool.

• \topskip and \maxdepth are respected for the "marginpar" area.

• Too many marginal notes on a page are reported as overfull \vbox warnings.

• LaTeX's \marginpar is not supported, the marginal notes of different kinds do not know each other and will happily overprint each other.

The example document:

\documentclass{article}

\usepackage{atbegshi}
\usepackage{zref-abspage}
\usepackage{picture}

\makeatletter
\providecommand*{\c@zabspage}{\c@abspage}

% * User macros for configuring
%
% \tbmparItemSep is inserted between marginal notes
% \tbmparMiddleSep is inserted between top and bottom marginal notes.

\newcommand*{\tbmparItemSep}{%
\vspace{1ex minus .5ex}%
\hrule
\vspace{1ex minus .5ex}%
}
\newcommand*{\tbmparMiddleSep}{%
\vspace*{0pt plus 1fil}%
}

% * Debug messages
%
\newcommand*{\tbmparDebug}[1]{%
\typeout{[tbmpar] #1}%
}

% * Label management to remember absolute page number
%
% \tbmpar@PageByLabel stores and loads absolute page number from
% label and defines \tbmpar@page with absolute page number or
% zero if the label is not yet available.

\newcount\c@tbmpar@item
\c@tbmpar@item\z@

\newcommand*{\tbmpar@PageByLabel}{%
\zref@labelbyprops{tbmpar\the\c@tbmpar@item}{abspage}%
\edef\tbmpar@page{%
\zref@extractdefault{tbmpar\the\c@tbmpar@item}{abspage}{0}%
}%
\zref@refused{tbmpar\the\c@tbmpar@item}%
\tbmparDebug{Item \the\c@tbmpar@item\space on page \tbmpar@page}%
}

% * Box register management

\newcount\c@tbmpar@box
\c@tbmpar@box\z@

\let\tbmpar@boxfreelist\@empty

% Get a new free box register either from the free list or,
% if the free list is empty, allocate a new box register.
\newcommand*{\tbmpar@NextBox}[1]{%
\@next#1\tbmpar@boxfreelist{%
\tbmparDebug{Reused box: #1}%
}{%
\expandafter\newbox\csname tbmpar@box\the\c@tbmpar@box\endcsname
\edef#1{\csname tbmpar@box\the\c@tbmpar@box\endcsname}%
\tbmparDebug{New box: #1}%
}%
}
% Put free box in free list.
\newcommand*{\tbmpar@FreeBox}[1]{%
\begingroup
\let\@elt\relax
\xdef\tbmpar@boxfreelist{%
\tbmpar@boxfreelist
\@elt#1%
}%
\tbmparDebug{Free box: #1}%
\endgroup
}

\newsavebox{\tbmpar@box}

% Each marginpar is put in a box that is initialized as
% parbox/minipage.
\newcommand*{\tbmparBoxSetup}{}
\newcommand{\tbmpar@VBox}[1]{%
\vbox{%
\color@begingroup
\hsize\marginparwidth
\edef\tbmpar@restore@ifminipage{%
\if@minipage
\noexpand\@minipagetrue
\else
\noexpand\@minipagefalse
\fi
}%
\@parboxrestore
\@marginparreset
\tbmparBoxSetup
#1%
\tbmpar@restore@ifminipage
\color@endgroup
}%
}

% Macro \tbmpar@marginpar looks for the page, where the margin note
% belongs to, stores the note in a box and appends the box to the
% note collector register of the page.
% Each page is assigned a box collector registers that collect
% the top notes and a register that collect the bottom notes.
% The name of the box register is \tbmpar@<top|bot>box<page>.
\newcommand{\tbmpar@marginpar}[4]{%
\ifhmode
\@bsphack
\fi
\tbmpar@PageByLabel
\ifnum\tbmpar@page>\z@
\setbox\tbmpar@box\tbmpar@VBox{#4}%
\@ifundefined{tbmpar@#1box\tbmpar@page}{%
\tbmpar@NextBox\tbmpar@currbox
\global\expandafter\let
\csname tbmpar@#1box\tbmpar@page\endcsname
\tbmpar@currbox
\global\setbox\tbmpar@currbox=\vbox{%
\unvbox\tbmpar@box
}%
}{%
\tbmparDebug{Use box: \tbmpar@currbox}%
\expandafter\let\expandafter\tbmpar@currbox
\csname tbmpar@#1box\tbmpar@page\endcsname
\global\setbox\tbmpar@currbox\tbmpar@VBox{%
\unvbox#2%
\par
\begingroup
\tbmparItemSep
\endgroup
\unvbox#3%
}%
}%
\fi
\ifhmode
\@esphack
\fi
}
\newcommand*{\topmarginpar}{%
\tbmpar@marginpar{top}\tbmpar@currbox\tbmpar@box
}
\newcommand*{\botmarginpar}{%
\tbmpar@marginpar{bot}\tbmpar@box\tbmpar@currbox
}

% At shipout time we look for the box collector registers of this
% page and set these boxes in the marginpar box with respecting
% \topskip and \maxdepth.
\AtBeginShipout{%
\AtBeginShipoutUpperLeft{%
\put(%
\dimexpr 1in+\oddsidemargin+\textwidth+\marginparsep\relax,%
){%
\begingroup
\global\let\tbmpar@inuse=N%
\setbox\tbmpar@box=\tbmpar@VBox{%
\penalty-\@M
\edef\tbmpar@tmp{tbmpar@topbox\the\value{zabspage}}%
\@ifundefined{\tbmpar@tmp}{%
}{%
\expandafter\let\expandafter\tbmpar@currbox
\csname\tbmpar@tmp\endcsname
\unvbox\tbmpar@currbox
\tbmpar@FreeBox\tbmpar@currbox
\global\let\tbmpar@inuse=Y%
}%
\endgraf
\tbmparMiddleSep
\edef\tbmpar@tmp{tbmpar@botbox\the\value{zabspage}}%
\@ifundefined{\tbmpar@tmp}{%
}{%
\expandafter\let\expandafter\tbmpar@currbox
\csname\tbmpar@tmp\endcsname
\unvbox\tbmpar@currbox
\tbmpar@FreeBox\tbmpar@currbox
\global\let\tbmpar@inuse=Y%
}%
}%
\ifx\tbmpar@inuse Y%
\splittopskip=\topskip
\setbox0=\vsplit\tbmpar@box to\z@
\boxmaxdepth=\maxdepth
\setbox\tbmpar@box=\vbox to\textheight{%
\unvbox\tbmpar@box
}%
\box\tbmpar@box
\fi
\endgroup
}%
}%
}

\makeatother

% Testing

\usepackage[
a5paper,
left=10mm,
right=10mm,
marginparwidth=40mm,
includemp,
]{geometry}
\usepackage{microtype}
\usepackage[T1]{fontenc}
\usepackage{lmodern}

\clubpenalty=10000
\flushbottom
\settodepth\maxdepth{g}
\setlength{\fboxsep}{1ex}
\usepackage{lipsum}
\usepackage{color}

\newcommand*{\shortlipsum}[1]{%
\begingroup
\long\def\y##1. ##2\@nil{##1.}%
\edef\x{\csname lipsum@\romannumeral#1\endcsname}%
\expandafter\y\x. \@nil
\endgroup
}

\begin{document}
\topmarginpar{\color{blue}\shortlipsum{1}}
\lipsum[1]
\botmarginpar{%
Show effect of \texttt{\textbackslash maxdepth}:
$\displaystyle\sum_{\textstyle i=\frac{a}{g}}^\infty i = x$}
\lipsum[2]
\topmarginpar{\shortlipsum{3}}%
\botmarginpar{Second bottom marginal note}%
\lipsum[3-4]
\noindent a\botmarginpar{a} b\botmarginpar{b} c \botmarginpar{c} d\\
e\\f\par
\topmarginpar{\fbox{\shortlipsum{5}}}
\lipsum[5]
Text with footnote and marginal note\footnote{Marginal note X}.
\topmarginpar{This is marginal note X}
\lipsum[6]
\botmarginpar{\shortlipsum{7}}
\lipsum[7]
\end{document}


-
running your example with pdflatex and TL2012 I get: ! You can't use \relax' after \the. <recently read> \c@zabspage l.237 \lipsum[3-4] –  Herbert Sep 18 '12 at 12:21
@Herbert Probably the two occurrences of zabspage should be abspage. –  egreg Sep 18 '12 at 12:34
@Herbert Sorry, I had renamed counter abspage to zabspage in the not yet released version of zref to avoid name clash with package perpage. Add \providecommand*{\c@zabspage}{\c@abspage}. –  Heiko Oberdiek Sep 18 '12 at 12:52
I feel that this is worth it to become another package... –  Daniel Sep 18 '12 at 13:30
I'm way out of the loop. What is \z@? –  Simon Kuang Aug 4 at 23:43

Here's an attempt using Martin Scharrer's package tikzpagenodes. As it uses the remember picture, overlay options of TikZ, you'll need to compile twice to get it right. It provides the two commands \marpartop and \marparbot which both take two arguments: the content and the text color, this could easily be expanded to be more customizable.

## Code

\documentclass{scrartcl}
\usepackage[left=15mm,top=40mm,bottom=40mm,right=50mm,a4paper]{geometry}
\usepackage{tikzpagenodes}
\usepackage{xifthen}

\usepackage{lipsum}

\setlength{\marginparwidth}{40mm}
\pagestyle{empty}

\def\myyshifttop{0}
\def\mypagetop{0}

\newcommand{\marpartop}[2]% content, color
{   \begin{tikzpicture}[remember picture, overlay]
\ifthenelse{\thepage=\mypagetop}{}{\xdef\myyshifttop{0}}
\xdef\mypagetop{\thepage}
\node[below right, yshift=\myyshifttop, text width=\marginparwidth-4pt, inner sep=2pt, #2] (tempnode) at (current page marginpar area.north west) {#1};
\path (current page marginpar area.north west);
\pgfgetlastxy{\tempxone}{\tempyone}
\path (tempnode.south west);
\pgfgetlastxy{\tempxtwo}{\tempytwo}
\pgfmathsetmacro{\diffy}{(\tempytwo-\tempyone)}
\xdef\myyshifttop{\diffy}
\end{tikzpicture}
}

\def\myyshiftbot{0}
\def\mypagebot{0}

\newcommand{\marparbot}[2]% content, color
{   \begin{tikzpicture}[remember picture, overlay]
\ifthenelse{\thepage=\mypagebot}{}{\xdef\myyshiftbot{0}}
\xdef\mypagebot{\thepage}
\node[above right, yshift=\myyshiftbot, text width=\marginparwidth-4pt, inner sep=2pt, #2] (tempnode) at (current page marginpar area.south west) {#1};
\path (current page marginpar area.south west);
\pgfgetlastxy{\tempxone}{\tempyone}
\path (tempnode.north west);
\pgfgetlastxy{\tempxtwo}{\tempytwo}
\pgfmathsetmacro{\diffy}{(\tempytwo-\tempyone)}
\xdef\myyshiftbot{\diffy}
\end{tikzpicture}
}

\begin{document}
\marpartop{On a journey to find the cure for a Tatarigami's curse, Ashitaka finds himself in the middle of a war between the forest gods and Tatara, a mining colony.}{red}
\marparbot{What begins as an open and shut case of murder soon becomes a mini-drama of each of the jurors' prejudices and preconceptions about the trial, the accused, and each other.}{orange!50!gray}
\lipsum[1-3]
\marparbot{The defense and the prosecution have rested and the jury is filing into the jury room to decide if a young Spanish-American is guilty or innocent of murdering his father.}{green!50!gray}
\marpartop{In this quest he also meets San, the Mononoke Hime.}{blue}
\lipsum[4-7]
\marpartop{On a journey to find the cure for a Tatarigami's curse, Ashitaka finds himself in the middle of a war between the forest gods and Tatara, a mining colony.}{red}
\marparbot{What begins as an open and shut case of murder soon becomes a mini-drama of each of the jurors' prejudices and preconceptions about the trial, the accused, and each other.}{orange!50!gray}
\lipsum[8]
\marpartop{In this quest he also meets San, the Mononoke Hime.}{blue}
\marparbot{The defense and the prosecution have rested and the jury is filing into the jury room to decide if a young Spanish-American is guilty or innocent of murdering his father.}{green!50!gray}
\lipsum[9-12]
\end{document}


## Result

-
+1 I discard my solution... Too late and too many similarities. –  Paul Gaborit Sep 16 '12 at 0:31
Have you found the bug of tikzpagenodes with the twoside option? –  Paul Gaborit Sep 16 '12 at 0:34
Martin Scharer is very reactive: a new version of tikzpagenodes with bug fixed is coming to CTAN. –  Paul Gaborit Sep 16 '12 at 16:18
Because of the asynchronous output routine you need a label/ref system to get the correct page number. \thepage might be different from an arabic number. –  Heiko Oberdiek Sep 18 '12 at 7:25
Again because of the asynchronous output routine \marpartop or \marparbot can process a margin par that will not be set on the current page, but to a following page. –  Heiko Oberdiek Sep 18 '12 at 7:30

(Note: it's a community answer because original idea is from Tom Bombadil.)

Here is a variant of Tom Bombadil's answer with a few improvements:

• The two macros are named \topmarginpar and \botmarginpar as requested.

• Their first argument (optional and empty by default) is some options for the TikZ node (to allow filling, drawing a border, etc.). Their second argument is the content of the paragraph.

• There are some calculations to correct a bug of tikzpagenodes with even page (edit: Martin Scharer is very reactive: a version with bug fixed is coming to CTAN.).

• The code uses let operations (and calc TikZ library). I think it is more readable than PGF calls.

Limits:

• \botmarginpar should stack paragraphs in reverse order!
• If a stack is full, it does not continue to the next page. It overflows up or down the page.
• If \topmarginpar (or \bormarginpar) is called in the last paragraph of a page, the margin paragraph can appear on the next page and not in the right order.

Here are two pages (odd then even):

The code (with my comments):

\documentclass[twoside]{report}
\usepackage{tikzpagenodes}
\usepackage{xifthen}
\usetikzlibrary{calc}

\def\myyshifttop{0}
\def\mypagetop{0}
\newcommand{\topmarginpar}[2][]{% tikz options of node, content
\begin{tikzpicture}[remember picture, overlay]
% reset position on new page
\ifthenelse{\thepage=\mypagetop}{}{\xdef\myyshifttop{0}\xdef\mypagetop{\thepage}}
% a big path with many actions
\path let
% patch for bug in tikzpagenodes with even pages
\p1=(current page marginpar area.north west),
\p2=(current page marginpar area.north east)
in \pgfextra{
\pgfmathsetmacro{\xw}{\x1<\x2?\x1:\x2}
\pgfmathsetmacro{\yw}{\y1<\y2?\y1:\y2}
\edef\coord{\xw pt,\yw pt}
}
% draw topmarginpar
node[below right, yshift=\myyshifttop, text width=\marginparwidth-4pt, inner sep=2pt, #1]
(tempnode) at (\coord) {#2}
% next position
let \p1=(\coord), \p2=(tempnode.south west) in \pgfextra{
\pgfmathsetmacro{\diffy}{(\y2-\y1)}
\xdef\myyshifttop{\diffy}
};
\end{tikzpicture}%
}

\def\myyshiftbot{0}
\def\mypagebot{0}
\newcommand{\botmarginpar}[2][]{% tikz options of node, content
\begin{tikzpicture}[remember picture, overlay]
% reset position on new page
\ifthenelse{\thepage=\mypagebot}{}{\xdef\myyshiftbot{0}\xdef\mypagebot{\thepage}}
% a big path with many actions
\path let
% patch for bug in tikzpagenodes with even pages
\p1=(current page marginpar area.south west),
\p2=(current page marginpar area.south east)
in \pgfextra{
\pgfmathsetmacro{\xw}{\x1<\x2?\x1:\x2}
\pgfmathsetmacro{\yw}{\y1<\y2?\y1:\y2}
\edef\coord{\xw pt,\yw pt}
}
% draw botmarginpar
node[above right, yshift=\myyshiftbot, text width=\marginparwidth-4pt, inner sep=2pt, #1]
(tempnode) at (\coord) {#2}
% next position
let \p1=(\coord), \p2=(tempnode.north west) in \pgfextra{
\pgfmathsetmacro{\diffy}{(\y2-\y1)}
\xdef\myyshiftbot{\diffy}
};
\end{tikzpicture}%
}

\usepackage{lipsum}

\begin{document}
\topmarginpar[red,fill=yellow!30]{On a journey to find the cure for a
Tatarigami's curse, Ashitaka finds himself in the middle of a war
between the forest gods and Tatara, a mining colony.}%
\botmarginpar{What begins as an open and shut case of murder soon
becomes a mini-drama of each of the jurors' prejudices and
preconceptions about the trial, the accused, and each other.}%
\lipsum[1-3]%
\botmarginpar[font=\itshape\footnotesize,text=green!50!black]{The
defense and the prosecution have rested and the jury is filing into
the jury room to decide if a young Spanish-American is guilty or
innocent of murdering his father.}%
\topmarginpar[blue]{In this quest he also meets San, the Mononoke
Hime.}%
\lipsum[4-7]%
\topmarginpar[red]{On a journey to find the cure for a Tatarigami's
curse, Ashitaka finds himself in the middle of a war between the
forest gods and Tatara, a mining colony.}%
\botmarginpar[orange!50!gray]{What begins as an open and shut case of
murder soon becomes a mini-drama of each of the jurors' prejudices and
preconceptions about the trial, the accused, and each other.}%
\lipsum[8]%
\topmarginpar[blue]{In this quest he also meets San, the Mononoke
Hime.}%
\botmarginpar[green!50!gray]{The defense and the prosecution have rested
and the jury is filing into the jury room to decide if a young
Spanish-American is guilty or innocent of murdering his father.}%
\lipsum[9-12]
\end{document}

-
Thanks for the code which works fine, except for one detail: the bottom marginal notes do not align perfectly with the bottom of the main page. This is quite annoying, is there some way to fix it? –  yannis Feb 14 at 20:07
I found a solution for that problem. But now there is another one: The notes are placed in inverse order: the first note of the page is placed on the bottom and the second above it, and so on. How can I get them in the same order as in the text? –  yannis Feb 14 at 22:55

Thanks for all the feedback so far. Here is the code I managed to piece together. It provides commands \topmarginnote and \footmarginnote which create "floating" margin notes which will be placed according to the following (arbitrary) rules:

• topmarginnotes are printed at the top of the margin, in the order they were defined (oldest on top);
• footmarginnotes are printed at the bottom of the margin, in the order they were defined (oldest on top);
• when there isn't enough vertical space, footmarginnotes take precedence over topmarginnotes (because I plan to use the former for citation references, and the latter for small figures);
• Unprinted notes carry over to the next page.

It's still far from perfect:

• I'm not using box registers because I was too lazy to learn how to create and use them.
• silent failure when a single note is taller than the margin;
• notes still unprinted after the last text page is shipped out are lost.

Criticism welcome!

The code:

\documentclass{article}

\usepackage[%
a4paper,
left=2cm,
textheight=24cm,
textwidth=10cm,
marginparsep=2cm,
marginparwidth=5cm
]{geometry}

\usepackage{forarray,etoolbox,xifthen,,graphicx,picture,calc,atbegshi,lipsum,color}

\newlength{\vincrement}

\newlength{\fmvorigin}
\newlength{\fmhorigin}
\newlength{\fmvoffset}
\newlength{\nextfmvoffset}
\newlength{\fmvskip}

\makeatletter
\ifthenelse{\isundefined{\Gm@lmargin}}%
{\setlength{\fmhorigin}{1in+\hoffset+\oddsidemargin+\textwidth+\marginparsep}}%
{\setlength{\fmhorigin}{\Gm@lmargin+\textwidth+\marginparsep}}
\makeatother
\setlength{\fmvoffset}{0pt}
\setlength{\fmvskip}{3ex}

\newlength{\tmvorigin}
\newlength{\tmhorigin}
\newlength{\tmvoffset}
\newlength{\nexttmvoffset}
\newlength{\tmvskip}
\newlength{\maxtmnheight}

\setlength{\tmhorigin}{\fmhorigin}
\setlength{\tmvoffset}{0pt}
\setlength{\tmvskip}{6ex}

\newcounter{fmn}
\newcounter{tmn}

\newboolean{marginisfull}

\newcommand{\fmnbuffer}{}
\newcommand{\currentfmnqueue}{}
\newcommand{\nextfmnqueue}{}

\newcommand{\tmnbuffer}{}
\newcommand{\currenttmnqueue}{}
\newcommand{\nexttmnqueue}{}

\newcommand{\append}[2]{%
\ifdefempty{#1}{}{\eappto{#1}{,}}%
\eappto{#1}{#2}%
}

\newcommand{\prepend}[2]{%
\ifdefempty{#1}{}{\epreto{#1}{,}}%
\epreto{#1}{#2}%
}

\newcommand{\footmarginnote}[1]{%
\expandafter\newsavebox\csname fmnbox\roman{fmn}\endcsname%
\expandafter\savebox\csname fmnbox\roman{fmn}\endcsname{\parbox[b]{\marginparwidth}{#1}}%
\append{\currentfmnqueue}{\roman{fmn}}%
}

\newcommand{\topmarginnote}[1]{%
\expandafter\newsavebox\csname tmnbox\roman{tmn}\endcsname%
\expandafter\savebox\csname tmnbox\roman{tmn}\endcsname{\parbox[t]{\marginparwidth}{\vskip-0.75\baselineskip#1}}%
\append{\currenttmnqueue}{\roman{tmn}}%
}

\newcommand{\printfmn}{%
\setlength{\fmvoffset}{0pt}%
\setboolean{marginisfull}{false}%
\ifdefempty{\currentfmnqueue}{}{%
\gdef\nextfmnqueue{}%
\gdef\fmnbuffer{}%
\ForEachX{,}{%
\ifthenelse{\boolean{marginisfull}}%
{\append{\nextfmnqueue}{\thislevelitem}}%
{%
\setlength{\vincrement}%
{\fmvskip+\expandafter\ht\csname fmnbox\thislevelitem\endcsname+\dp\csname fmnbox\thislevelitem\endcsname}%
\setlength{\nextfmvoffset}{\fmvoffset+\vincrement}%
\ifthenelse{\lengthtest{\nextfmvoffset>\textheight}}{%
\setboolean{marginisfull}{true}%
\append{\nextfmnqueue}{\thislevelitem}%
}{%
\prepend{\fmnbuffer}{\thislevelitem}%
}%
}%
}%
{\currentfmnqueue}%
\setlength{\fmvoffset}{0pt}%
\ForEachX{,}{%
\put(\fmhorigin,\fmvorigin+\fmvoffset){\expandafter\usebox\csname fmnbox\thislevelitem\endcsname}%
\setlength{\vincrement}%
{\fmvskip+\expandafter\ht\csname fmnbox\thislevelitem\endcsname+\dp\csname fmnbox\thislevelitem\endcsname}%
}%
{\fmnbuffer}%
\global\let\currentfmnqueue\nextfmnqueue%
}%
}

\newcommand{\printtmn}{%
\setlength{\maxtmnheight}{\textheight-\fmvoffset}%
\setlength{\tmvoffset}{0pt}%
\setboolean{marginisfull}{false}%
\ifdefempty{\currenttmnqueue}{}{%
\gdef\nexttmnqueue{}%
\gdef\tmnbuffer{}%
\ForEachX{,}{%
\ifthenelse{\boolean{marginisfull}}%
{\append{\nexttmnqueue}{\thislevelitem}}%
{%
\setlength{\vincrement}%
{\tmvskip+\expandafter\ht\csname tmnbox\thislevelitem\endcsname+\dp\csname tmnbox\thislevelitem\endcsname}%
\setlength{\nexttmvoffset}{\tmvoffset+\vincrement}%
\ifthenelse{\lengthtest{\nexttmvoffset>\maxtmnheight}}{%
\setboolean{marginisfull}{true}%
\append{\nexttmnqueue}{\thislevelitem}%
}{%
\append{\tmnbuffer}{\thislevelitem}%
}%
}%
}%
{\currenttmnqueue}%
\setlength{\tmvoffset}{0pt}%
\ifdefempty{\tmnbuffer}{}{%
\ForEachX{,}{%
\put(\tmhorigin,\tmvorigin-\tmvoffset){\expandafter\usebox\csname tmnbox\thislevelitem\endcsname}%
\setlength{\vincrement}%
{\tmvskip+\expandafter\ht\csname tmnbox\thislevelitem\endcsname+\dp\csname tmnbox\thislevelitem\endcsname}%
}%
{\tmnbuffer}%
}%
\global\let\currenttmnqueue\nexttmnqueue%
}%
}

\AtBeginShipout{\AtBeginShipoutUpperLeft{\printfmn\printtmn}}

\begin{document}

{\color{red}(t1)}\topmarginnote{\footnotesize\color{red}[t1] \lipsum[1]}
{\color{green}(t2)}\topmarginnote{\footnotesize\color{green}[t2] \lipsum[2]}
{\color{blue}(t3)}\topmarginnote{\footnotesize\color{blue}[t3] \lipsum[3]}
{\color{green}(f1)}\footmarginnote{\footnotesize\color{green}[f1] \lipsum[4]}
{\color{blue}(f2)}\footmarginnote{\footnotesize\color{blue}[f2] \lipsum[5]}
\lipsum[1-10]
{\color{red}(f3)}\footmarginnote{\footnotesize\color{red}[f3] \lipsum[6]}

\end{document}
`

The output:

-