18

I am looking for a method to put small side notes next to the text, by simultanously marking the amount of lines to which the note refers. So, something like this:

enter image description here

I was trying many methods like todonotes, sidenotes, marginpar with mdframed, etc. But all packages seem to limit the height depending on the amount of text in the note. And I can only point the note to a specific place in the main text. But I want something that I can point to a specific amount of text or paragraph which also determines the note height.

3
  • Do you need to allow page breaking within the marked region? Jun 11, 2013 at 14:16
  • Yes that would be great if possible. But in the case that there is no better solution, I would also be interested in a no-pagebreak solution.
    – Martin
    Jun 11, 2013 at 14:20
  • Welcome to TeX.SX!
    – mafp
    Jun 11, 2013 at 14:26

1 Answer 1

14

One option using \tikzmark (since some calculations are performed for the bar placement, the code needs three runs to stabilize). The bar admits (multiple) page breaks:

\documentclass{article}
\usepackage[a5paper,rmargin=4cm]{geometry}
\usepackage{atbegshi}
\usepackage{refcount}
\usepackage{setspace}
\usepackage{tikzpagenodes}
\usetikzlibrary{calc}
\usepackage{lipsum}

\newcounter{bordercntr}
\newcounter{borderpages}

\newcommand\tikzmark[1]{%
  \tikz[overlay,remember picture] \node[inner xsep=0pt] (#1) {};}

\newenvironment{tikzborder}[2][7pt]
{%
  \gdef\borderspacing{#1}
  \stepcounter{bordercntr}%
  \tikzmark{start-border}\label{start-border\thebordercntr}%
  \tikz[remember picture,overlay]
    \node[anchor=north west,inner sep=0pt,text=gray,font=\footnotesize] at 
  ([xshift=\marginparsep,yshift=0.65ex]current page text area.east|-start-border.north)
  {\parbox{\marginparwidth}{\begin{spacing}{0.8}\raggedright#2\end{spacing}}};%
  % if the marks are in the same page, nothing is done
  % otherwise, the decoration is drawn from the starting point to the page bottom
  % and, if necessary, intermediate pages will also receive the decoration
  \ifnum\getpagerefnumber{start-border\thebordercntr}=\getpagerefnumber{end-border\thebordercntr}\else
    \begin{tikzpicture}[overlay, remember picture]
    \draw [ultra thick,gray]
         ([xshift=\borderspacing,yshift=0.65ex]current page text area.east|-start-border.north) --  
         ([xshift=\borderspacing]current page text area.south east);
    \end{tikzpicture}%
\setcounter{borderpages}{\numexpr\getpagerefnumber{end-border\thebordercntr}-\getpagerefnumber{start-border\thebordercntr}}%
    \ifnum\value{borderpages}>1\relax
      \AtBeginShipoutNext{\tikzborderpage[#1]}%
    \fi%
  \fi%
\ignorespaces%
}
{\tikzmark{end-border}\label{end-border\thebordercntr}%
  % if the marks are in the same page, the decoration is drawn
  % otherwise, the decoration is drawn from the top of the page to the end mark
  \ifnum\getpagerefnumber{start-border\thebordercntr}=\getpagerefnumber{end-border\thebordercntr}%
    \begin{tikzpicture}[overlay, remember picture]
    \draw [ultra thick,gray]
         ([xshift=\borderspacing,yshift=0.65ex]current page text area.east|-start-border.north) --  
         ([xshift=\borderspacing,yshift=0.65ex]current page text area.east|-end-border.south);
    \end{tikzpicture}%
  \else
    \begin{tikzpicture}[overlay, remember picture]
    \draw [ultra thick,gray]
         ([xshift=\borderspacing]current page text area.north east) --  
         ([xshift=\borderspacing,yshift=0.65ex]current page text area.east|-end-border.south);
    \end{tikzpicture}%
  \fi%
}

% the command to draw the decoration in intermediate pages from the top
% to the bottom of the page
\newcommand\tikzborderpage[1][0pt]{%
  \begin{tikzpicture}[overlay, remember picture]
    \draw[ultra thick,gray]
         ([xshift=\borderspacing,yshift=-\baselineskip]current page text area.north east) --  
         ([xshift=\borderspacing,yshift=0.65ex]current page text area.south east);
  \end{tikzpicture}
  \addtocounter{borderpages}{-1}%
  \ifnum\value{borderpages}>1
    \AtBeginShipoutNext{\tikzborderpage[#1]}%
  \fi%
}

\begin{document}

\begin{tikzborder}{Suspendisse ut massa. Cras nec ante}
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.\end{tikzborder} 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. Pellentesque a nulla.

\begin{tikzborder}{Nam dui ligula, fringilla a, euismod sodales, sollicitudin vel, wisi.}\lipsum*[1-3]\end{tikzborder}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. Pellentesque a nulla.

\end{document}

enter image description here

The solution uses an environment with a mandatory argument:

\begin{tikzborder}{<note>}
<text>
\end{tikzborder}

There's also an optional argument to control the separation between the text and the bar.

Here's now a new variation placing the annotations on the outer margin; this is specially useful for two-sided documents (in fact, the code contains the necessary elements for an easy extension admitting outer, left or inner margins):

\documentclass[twoside]{scrreprt}
\usepackage{atbegshi}
\usepackage{refcount}
\usepackage{setspace}
\usepackage{tikzpagenodes}
\usetikzlibrary{calc}
\usepackage{lipsum}

\newcounter{bordercntr}
\newcounter{borderpages}

\newcommand\AnnAnchor{north west}
\newcommand\AnnMargin{east}
\newcommand\AnnAlign{\raggedright}
\newlength\AnnSep
\setlength\AnnSep{\marginparsep}

\newcommand\tikzmark[1]{%
  \tikz[overlay,remember picture] \node[inner xsep=0pt] (#1) {};}

\newenvironment{tikzborder}[1]
{%
\checkoddpage
\ifoddpage
\renewcommand\AnnAnchor{north west}%
\renewcommand\AnnMargin{east}%
\setlength\AnnSep{\marginparsep}%
\else
\renewcommand\AnnAnchor{north east}%
\renewcommand\AnnMargin{west}%
\setlength\AnnSep{-\marginparsep}%
\renewcommand\AnnAlign{\raggedleft}
\fi%
  \stepcounter{bordercntr}%
  \tikzmark{start-border}\label{start-border\thebordercntr}%
  \tikz[remember picture,overlay]
    \node[anchor=\AnnAnchor,inner sep=0pt,text=gray,font=\footnotesize] at 
  ([xshift=\AnnSep,yshift=0.65ex]current page text area.\AnnMargin|-start-border.north)
  {\parbox{\marginparwidth}{\begin{spacing}{0.8}\AnnAlign#1\end{spacing}}};%
  % if the marks are in the same page, nothing is done
  % otherwise, the decoration is drawn from the starting point to the page bottom
  % and, if necessary, intermediate pages will also receive the decoration
  \ifnum\getpagerefnumber{start-border\thebordercntr}=\getpagerefnumber{end-border\thebordercntr}\else
    \begin{tikzpicture}[overlay, remember picture]
    \draw [ultra thick,gray]
         ([xshift=0.5\AnnSep,yshift=0.65ex]current page text area.\AnnMargin|-start-border.north) --  
         ([xshift=0.5\AnnSep]current page text area.south \AnnMargin);
    \end{tikzpicture}%
\setcounter{borderpages}{\numexpr\getpagerefnumber{end-border\thebordercntr}-\getpagerefnumber{start-border\thebordercntr}}%
    \ifnum\value{borderpages}>1\relax
      \AtBeginShipoutNext{\tikzborderpage[#1]}%
    \fi%
  \fi%
\ignorespaces%
}
{\checkoddpage%
\ifoddpage
\setlength\AnnSep{\marginparsep}%
\renewcommand\AnnAnchor{north west}%
\renewcommand\AnnMargin{east}%
\else
\setlength\AnnSep{-\marginparsep}%
\renewcommand\AnnAnchor{north east}%
\renewcommand\AnnMargin{west}%
\fi%
\tikzmark{end-border}\label{end-border\thebordercntr}%
  % if the marks are in the same page, the decoration is drawn
  % otherwise, the decoration is drawn from the top of the page to the end mark
  \ifnum\getpagerefnumber{start-border\thebordercntr}=\getpagerefnumber{end-border\thebordercntr}%
    \begin{tikzpicture}[overlay, remember picture]
  \draw [ultra thick,gray]
         ([xshift=0.5\AnnSep,yshift=0.65ex]current page text area.\AnnMargin|-start-border.north) --  
         ([xshift=0.5\AnnSep,yshift=0.65ex]current page text area.\AnnMargin|-end-border.south);
    \end{tikzpicture}%
  \else
    \begin{tikzpicture}[overlay, remember picture]
    \draw [ultra thick,gray]
         ([xshift=0.5\AnnSep]current page text area.north \AnnMargin) --  
         ([xshift=0.5\AnnSep,yshift=0.65ex]current page text area.\AnnMargin|-end-border.south);
    \end{tikzpicture}%
  \fi%
}

% the command to draw the decoration in intermediate pages from the top
% to the bottom of the page
\newcommand\tikzborderpage[1][0pt]{%
\checkoddpage%
\ifoddpage
\setlength\AnnSep{\marginparsep}%
\renewcommand\AnnAnchor{north west}%
\renewcommand\AnnMargin{east}%
\else
\setlength\AnnSep{-\marginparsep}%
\renewcommand\AnnAnchor{north east}%
\renewcommand\AnnMargin{west}%
\fi%
  \begin{tikzpicture}[overlay, remember picture]
    \draw[ultra thick,gray]
         ([xshift=0.5\AnnSep,yshift=-\baselineskip]current page text area.north \AnnMargin) --  
         ([xshift=0.5\AnnSep,yshift=0.65ex]current page text area.south \AnnMargin);
  \end{tikzpicture}
  \addtocounter{borderpages}{-1}%
  \ifnum\value{borderpages}>1
    \AtBeginShipoutNext{\tikzborderpage[#1]}%
  \fi%
}

\title{Some Title}
\author{Some Author}

\begin{document}
.\clearpage%just for the example

\begin{tikzborder}{Suspendisse ut massa. Cras nec ante}
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.\end{tikzborder} 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. Pellentesque a nulla.

\begin{tikzborder}{Nam dui ligula, fringilla a, euismod sodales, sollicitudin vel, wisi.}\lipsum*[1-7]\end{tikzborder}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. Pellentesque a nulla.

\lipsum*[1-2]\begin{tikzborder}{Nam dui ligula, fringilla a, euismod sodales, sollicitudin vel, wisi.}\lipsum*[3-5]\end{tikzborder}\lipsum[2]

\lipsum*[3-4]\begin{tikzborder}{Morbi ac orci et nisl hendrerit mollis. Suspendisse ut massa. Cras nec ante. Pellentesque a nulla.}\lipsum*[4-8]\end{tikzborder}\lipsum[2]

\end{document}

enter image description here

10
  • Looks interesting but I don't have time to study the solution. Am I right in assuming that you assume a non-symmetric page design? If so, is there a way to generalise the solution and force the line in the outer (or inner) margin for symmetric page designs?
    – user10274
    Jun 11, 2013 at 15:16
  • @MarcvanDongen in it's current form, the text is always typeset in the left margin using a box of width equal to \marginparwidth; and yes, the solution can be generalized to use the outer (or inner) margin. Perhaps I'll do this. Jun 11, 2013 at 15:34
  • +1 (I should have put money on you or Andrew posting a tikzmark solution to this:-) Jun 11, 2013 at 17:30
  • 1
    @Martin I've added the code using the setspace package to reduce the inter-line spacing for the annotations. As soon as I have time, I'll tackle number (3) :-) Jun 15, 2013 at 0:21
  • 2
    @Martin I've made a new update; the last example code in my answer contains the settings for placement in the outer margins. Jun 16, 2013 at 0:24

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.