%% Uncomment to enable debug output. Note that this also enable `\layout`
%% so the text will shift forward. So don't panic, just go to the next page.
%%
%\def\DebugMarginPar{}%
\documentclass[12pt]{scrartcl}
\usepackage{lmodern}
\usepackage{geometry}
\usepackage{etoolbox}
\usepackage{xcolor}
%\usepackage{marginfix}% Not used
\usepackage{etoolbox}
\usepackage{xparse}
\usepackage{xstring}
\usepackage{tikz}
\usetikzlibrary{calc}
\ifdefined\DebugMarginPar% For debug use only
% https://tex.stackexchange.com/questions/558/is-there-a-show-for-lengths
\usepackage{printlen}\uselengthunit{mm}
\usepackage{layout}
\fi
\geometry{left=2.5cm,textwidth=130mm}
\setlength{\marginparwidth}{4cm}
\setlength{\marginparsep}{2em}
\usepackage[noadjust]{marginnote}
% https://tex.stackexchange.com/questions/33703/extract-x-y-coordinate-of-an-arbitrary-point-in-tikz
\newdimen\XCoord
\newdimen\YCoord
\newcommand{\ExtractCoordinate}[1]{\path (#1); \pgfgetlastxy{\XCoord}{\YCoord};}%
%
\newcommand{\SetLengthToYCoordinate}[2]{%
\ExtractCoordinate{#2}%
\setlength{#1}{\YCoord}%
}%
% Default style for the margin par node.
\ifdefined\DebugMarginPar
\tikzset{Default Marginpar Style/.style={
shape=rectangle,inner sep=0, draw=blue,
rounded corners=2pt, blue, font=\itshape}
}
\else
\tikzset{Default Marginpar Style/.style={
shape=rectangle,inner sep=0, draw=none,
rounded corners=2pt, blue, font=\itshape}
}
\fi
\tikzset{Default Arrow Style/.style={red, ultra thick, -stealth, shorten <= 2pt, shorten >= 2pt}}
% Following determined via \layout:
\newlength{\EndOfTextOffsetFromWest}
\newlength{\StartOfMarginNotes}
\setlength{\EndOfTextOffsetFromWest}{\dimexpr1in+\hoffset+\textwidth\relax}
\setlength{\StartOfMarginNotes}{\dimexpr\EndOfTextOffsetFromWest+\marginparsep\relax}
% Change this to set the default style for the margin par.
\newcommand*{\MarginParStyle}[2][\marginparwidth]{%
% #1 = optional width of parbox
% #2 = content of margin par
%
% We don't want \everypar to have any effect on these paragraphs so
% disable that while we are in here.
\global\toggletrue{InMarginNote}%
\parbox[t]{#1}{%
\footnotesize\itshape\raggedright%
#2%
}%
\global\togglefalse{InMarginNote}%
}%
\newlength{\CurrentParHeight}
\newlength{\CurrentMargiparHeight}
\newlength{\CurrentPointToStartOfPara}
\newlength{\CurrentPointToEndOfPara}
\newlength{\VerticalShift}
\newlength{\VerticalDistanceToMarginParSouth}% for fancy arrow
\newcounter{NextPara}
\NewDocumentCommand{\MarginPar}{%
O{}% #1 = draw options for the marginpar
O{}% #2 = draw options for the arrow
m % #3 = margin par text
}{%
\setbox0=\vtop{\MarginParStyle{#3}}%
\setlength{\CurrentMargiparHeight}{\ht0}% height of this marginpar
\addtolength{\CurrentMargiparHeight}{\dp0}%
\begin{tikzpicture}[overlay,remember picture, thick]
\coordinate (EdgeOfText) at
($(current page.west |- 0,0)
+ (\EndOfTextOffsetFromWest,0.5ex)$);
\coordinate (MarginParWestSide) at ($(EdgeOfText)+(\marginparsep,0)$);
\setcounter{NextPara}{\arabic{ParaCount}};
\stepcounter{NextPara}
\path (pic cs:Para\arabic{ParaCount},{(0, \paperheight)}) +
(0, 0.7\baselineskip) coordinate (CurrentParStart);
\path (pic cs:Para\arabic{NextPara},{(0,-\paperheight)}) +
(0,-0.3\baselineskip) coordinate (NextParStart);
\ifdefined\DebugMarginPar
% Debug: Location of start of paragraphs with a marginpar
\draw [line width=0.5cm,green,opacity=.3]
(CurrentParStart) circle (2pt);
\fi
\coordinate (VectorParHeight) at ($(CurrentParStart)-(NextParStart)$);
\SetLengthToYCoordinate{\CurrentParHeight}{VectorParHeight}
% Now check that current para does not continue on to the next page.
% If it does we need to do me some tweakin' and set the end of this
% para to be the end of the text on this page
\pgfmathtruncatemacro{\ParaContinuesToNextPage}
{\CurrentParHeight > 0 ? 0 : 1}
\IfEq{\ParaContinuesToNextPage}{0}{}{%
\coordinate (NextParStart) at
($(current page.north) % top of page
- (0, 1.0in + \voffset) % (2) in \layout
- (0, \headsep) % (6) in \layout
- (0, \textheight) % (7) in \layout
- (0, 0.7\baselineskip)
$);
\coordinate (VectorParHeight) at
($(CurrentParStart)-(NextParStart)$);
\SetLengthToYCoordinate{\CurrentParHeight}{VectorParHeight}
}
\coordinate (VectorToStartOfPara) at
(MarginParWestSide |- CurrentParStart);
\SetLengthToYCoordinate{\CurrentPointToStartOfPara}{VectorToStartOfPara}
\ifdefined\DebugMarginPar
\draw [orange, ultra thick,-latex]
([xshift=-0.25\arabic{ParaCount} cm]MarginParWestSide) --
([xshift=-0.25\arabic{ParaCount} cm]VectorToStartOfPara);
\fi
% The \baselineskip adjustment is so that we are measuring to the end
% of the current para, as opposed to the start of the next para.
\coordinate (VectorToEndOfPara) at
($(MarginParWestSide |- NextParStart) +(0,\baselineskip)$);
\SetLengthToYCoordinate{\CurrentPointToEndOfPara}{VectorToEndOfPara}
\ifdefined\DebugMarginPar
% Debugging: line from arrow to end of para
\draw [red!40, dotted, ultra thick,-latex]%
([xshift=-0.35\arabic{ParaCount} cm]MarginParWestSide) --
([xshift=-0.35\arabic{ParaCount} cm]VectorToEndOfPara);
\fi
% Now we have:
%
% \CurrentParHeight = the height of the current paragraph saved as
% \CurrentMargiparHeight = height of the margin par
% (MarginParWestSide) = coordinate of start of arrow
% \CurrentPointToStartOfPara = distance from start of para to arrow
% \CurrentPointToEndOfPara = distance from end of para to arrow
%
% So it is just a matter of applying some algorithm to tweak the
% vertical placement. Here is one possible algorithm:
%
% Note: Distances are measured from the vertical location of the arrow.
%
% if (distance to end of para > 0.5*(height of margin par) then
% margin par can be centered about this point
% it may go above start of para but I think that is ok
% else
% move margin par so that bottom of the margin par is at end of para.
\pgfmathsetlength{\VerticalShift}{
-\CurrentPointToEndOfPara > 0.5*\CurrentMargiparHeight ? 0cm :
% First version aligns bottom of marginpar with arrow location.
%0.5*(\CurrentMargiparHeight - 0.5*\baselineskip)%
0.5*(\CurrentMargiparHeight - 0.0*\baselineskip) +
\CurrentPointToEndOfPara
}
\node [Default Marginpar Style, anchor=west, #1] (marginpar) at
($(MarginParWestSide) + (0,\VerticalShift)$)
{\MarginParStyle{#3}};
% Well, since this is a tikz solution we need to do
% something slightly fancy, so check if the margin par was
% moved above far enough that a simple horizontal arrow
% is not enough.
%
% The last coordinate below is tweak so that we don't end up
% with a vertical component of the arrow that is hardly visible.
\coordinate (VerticalVectorFromMarginParToArrow) at
($(marginpar.south) - (EdgeOfText) - (0,4.0pt)$);
\SetLengthToYCoordinate{\VerticalDistanceToMarginParSouth}{VerticalVectorFromMarginParToArrow}
\pgfmathtruncatemacro{\NeedFancyArrow}
{\VerticalDistanceToMarginParSouth > 0 ? 1 : 0}
\IfEq{\NeedFancyArrow}{0}{%
\draw [Default Arrow Style, #2] (MarginParWestSide) to (EdgeOfText);
}{%
\draw [Default Arrow Style, #2]
(marginpar.south) --
(marginpar.south |- EdgeOfText) --
(EdgeOfText);
}%
\ifdefined\DebugMarginPar
%% Debugging: Y = vertical location of arrow pointing to source
\node [
text width=8.5cm, font=\tiny, draw=red, anchor=south,
fill=white, fill opacity=1
]
at ($(EdgeOfText) - (0.75\textwidth,-0.2)$)
{\MarginParStyle[8.5cm]{%
ParaCount = \arabic{ParaCount}\\
CurrentParHeight = \printlength{\CurrentParHeight}\\
CurrentMargiparHeight = \printlength{\CurrentMargiparHeight}\\
CurrentPointToStartOfPara = \printlength{\CurrentPointToStartOfPara}\\
CurrentPointToEndOfPara = \printlength{\CurrentPointToEndOfPara}\\
ParaContinuesToNextPage = \ParaContinuesToNextPage\\
VerticalDistanceToMarginParSouth = \printlength{\VerticalDistanceToMarginParSouth}
}%
};
\fi
\end{tikzpicture}%
}
%--------------------------------------------
% https://tex.stackexchange.com/questions/50015/tikzmark-to-have-different-behaviour-if-first-run-and-mark-locations-not-yet-av
\makeatletter
\tikzset{%
remember picture with id/.style={%
remember picture,
overlay,
save picture id=#1,
},
save picture id/.code={%
\edef\pgf@temp{#1}%
\immediate\write\pgfutil@auxout{%
\noexpand\savepointas{\pgf@temp}{\pgfpictureid}}%
},
if picture id/.code args={#1#2#3}{%
@ifundefined{save@pt@#1}{%
\pgfkeysalso{#3}%
}{
\pgfkeysalso{#2}%
}
}
}
\def\savepointas#1#2{%
\expandafter\gdef\csname save@pt@#1\endcsname{#2}%
}
\def\tmk@labeldef#1,#2@nil{%
\def\tmk@label{#1}%
\def\tmk@def{#2}%
}
\tikzdeclarecoordinatesystem{pic}{%
\pgfutil@in@,{#1}%
\ifpgfutil@in@%
\tmk@labeldef#1@nil
\else
\tmk@labeldef#1,(0pt,0pt)@nil
\fi
@ifundefined{save@pt@\tmk@label}{%
\tikz@scan@one@point\pgfutil@firstofone\tmk@def
}{%
\pgfsys@getposition{\csname save@pt@\tmk@label\endcsname}\save@orig@pic%
\pgfsys@getposition{\pgfpictureid}\save@this@pic%
\pgf@process{\pgfpointorigin\save@this@pic}%
\pgf@xa=\pgf@x
\pgf@ya=\pgf@y
\pgf@process{\pgfpointorigin\save@orig@pic}%
\advance\pgf@x by -\pgf@xa
\advance\pgf@y by -\pgf@ya
}%
}
\newcommand\tikzmark[2][]{%
\tikz[remember picture with id=#2] #1;}
\makeatother
%--------------------------------------------
\newcounter{ParaCount}
\newtoggle{InMarginNote}
\newcommand*{\MarkParaWithTikzMark}{%
\iftoggle{InMarginNote}{%
% Don't want to count the margin note paragraphs.
}{%
\stepcounter{ParaCount}%
\tikzmark{Para\arabic{ParaCount}}%
}%
}%
\AtBeginDocument{%
\setcounter{ParaCount}{0}%
\global\togglefalse{InMarginNote}%
% https://tex.stackexchange.com/questions/33849/what-does-the-the-everypar-do
\everypar{\MarkParaWithTikzMark}%
}%
% Need this to handle the case where there is no aditional text following
% the last para.
\AtEndDocument{\MarkParaWithTikzMark}%
% Add highlighting to words before and after so that we know
% we are pointing to the correct place. These are really for
% debugging, but useful to leave them so we know that the
% arrows points to the correct place.
\newcommand{\Before}[1]{\textcolor{red}{#1}}%
\newcommand{\After}[1]{\textcolor{blue}{#1}}%
%
\begin{document}\ifdefined\DebugMarginPar\layout\fi
Lorem ipsum dolor sit amet, consectetuer adipiscing elit. Ut purus elit,
vestibulum ut, placerat ac, adipiscing vitae, felis. Curabitur dictum
gravida mauris. Nam arcu libero, nonummy eget, consectetuer id, vulpu-
tate a, magna. Donec vehicula augue eu neque. Pellentesque habitant morbi
tristique senectus et netus et malesuada fames ac turpis egestas. Mauris
ut leo. Cras viverra metus rhoncus sem. Nulla et lectus vestibu- lum urna
fringilla \Before{ultrices}.\MarginPar{\textbf{This note is centered because
there is enough space below and it is in the middle of a paragraph.} Erat
ligula aliquet magna, vitae ornare odio metus a mi. Morbi ac orci et nisl
hendrerit mollis.}
\After{Phasellus} eu tellus sit amet tortor gravida placerat.
Integer sapien est, iaculis in, pretium quis, viverra ac, nunc. Nam arcu
libero, nonummy eget, consectetuer id, vulpu- tate a, magna. Donec
vehicula augue eu neque. Pellentesque habitant morbi tristique senectus et
netus et malesuada fames ac turpis egestas.
Nam dui ligula, fringilla a, euismod sodales, sollicitudin vel, wisi.
Morbi auctor lorem non justo. Nam lacus libero, pretium at, lobortis
vitae, ultricies et, tellus. Donec aliquet, tortor sed accumsan bibendum,
erat ligula aliquet magna, vitae ornare odio metus a mi. Morbi ac orci et
nisl hendrerit mollis.Suspendisse ut massa. Cras nec ante. Pellen- tesque
a nulla. Cum sociis natoque penatibus et magnis dis parturient montes,
nascetur ridiculus mus. Aliquam tincidunt urna. Nulla ullam- corper
vestibulum turpis. Pellentesque cursus luctus
\Before{mauris}.\MarginPar{\textbf{This note is pushed upwards because it is at
the end of a paragraph}}
\After{Nulla} malesuada porttitor diam. Donec felis erat, congue non, volutpat at,
tincidunt tristique, libero. Vivamus viverra fermentum felis. Donec
nonummy pellentesque ante. Phasellus adipiscing semper elit. Proin
fermentum massa ac quam. Sed diam turpis, molestie vitae, placerat a,
molestie nec, leo. Maecenas lacinia. Nam ipsum ligula, eleifend at,
accumsan nec, suscipit a, ipsum. Morbi blandit ligula feugiat magna. Nunc
eleifend consequat lorem. Sed lacinia nulla vitae enim. Pellentesque
tincidunt purus vel magna. Integer non enim. Praesent euismod nunc eu
purus. Donec bibendum quam in tellus. Nullam cursus pulvinar lectus. Donec
et mi. Nam vulputate metus eu enim. Vestibulum pellentesque felis eu
massa.
Quisque ullamcorper placerat ipsum. Cras nibh. Morbi vel justo vi- tae
lacus tincidunt ultrices. Lorem ipsum dolor sit amet, consectetuer
adipiscing elit. In hac habitasse platea dictumst. Integer tempus con-
vallis augue. Etiam facilisis. Nunc elementum fermentum wisi. Aenean
placerat. Ut imperdiet, enim sed gravida sollicitudin, felis odio placerat
quam, ac pulvinar elit purus eget enim. Nunc vitae tortor. Proin tempus
nibh sit amet nisl. Vivamus quis tortor vitae risus porta
\Before{vehicula}.\MarginPar{\textbf{This note used to be automatically adjusted by
`marginfix'.} Phasellus adipiscing semper elit. Proin fermentum massa ac
quam. Sed diam turpis, molestie vitae, placerat a, molestie nec, leo.
Maecenas lacinia. Nam ipsum ligula, eleifend at, accumsan nec, suscipit a,
ipsum.} \After{Fusce} mauris.
Vestibulum luctus nibh at lectus. Sed bibendum,
nulla a vel justo vi- tae lacus tinci dunt ultrices. Lorem ipsum dolor sit
amet. Maecenas lacinia. Nam ipsum ligula, eleifend at, accumsan nec,
suscipit a, ipsum. Morbi blandit ligula feugiat magna. Nunc eleifend
consequat lorem. Sed lacinia nulla vitae enim. Pellentesque tincidunt
purus vel magna. Integer non enim. Praesent euismod nunc eu purus. Donec
bibendum quam in tellus. Nullam cursus pulvinar lectus. Donec et mi. Nam
vulputate metus eu enim. Vestibulum pellentesque felis eu massa.
Vestibulum luctus nibh at lectus. Sed bibendum,
nulla a vel justo vi- tae lacus tinci dunt ultrices. Lorem ipsum dolor sit
amet. Maecenas lacinia. Nam ipsum ligula, eleifend at, accumsan nec,
suscipit a, ipsum. Morbi blandit ligula feugiat magna. Nunc eleifend
consequat lorem. Sed lacinia nulla vitae enim. Pellentesque tincidunt
purus vel magna. Integer non enim. Praesent euismod nunc eu purus. Donec
bibendum quam in tellus. Nullam cursus pulvinar lectus. Donec et mi. Nam
vulputate metus eu enim. Vestibulum pellentesque felis eu massa.
Quisque ullamcorper placerat ipsum. Cras nibh. Morbi vel justo vi- tae
lacus tincidunt ultrices. Lorem ipsum dolor sit amet, consectetuer
adipiscing elit. In hac habitasse platea dictumst. Integer tempus con-
vallis augue. Etiam \Before{facilisis}.
\MarginPar[draw=brown, fill=yellow!20, inner sep=2pt, yshift=2cm][green]
{Here is an demo of a few of the tikz options and an illustration
of the auto arrow adjustments.%
}
\After{Nunc} elementum fermentum wisi. Aenean
placerat. Ut imperdiet, enim sed gravida sollicitudin, felis odio placerat
quam, ac pulvinar elit purus eget enim. Nunc vitae tortor. Proin tempus
nibh sit amet nisl. Vivamus quis tortor vitae risus porta
\end{document}
todonotesdoes exactly this, but don't ask me how. – yo' Nov 21 '12 at 12:20todonotesdoes, I just now that it places a sort-of arrow pointing to the place of reference (after 2nd run), which is something that is asked for here. I thought that the reference might be of interest ;) – yo' Nov 21 '12 at 22:26todonotesmakes a good job making the margin note point to the references, but it is much more liberal with regards to the position of the notes to what is required for my purposed. As David notes, it just uses standard marginpar. But for something like the book shown in the picture above you need some intelligent algorithm to place notes that are at the end of a paragraph or end of a chapter. – Jörg Nov 21 '12 at 22:45