EDIT: I've updated the solution below to use the wrapfig environment, which simplified at least some parts of the code. This solved all the spacing issues I had before. I also changed the definition of the command to only require that it is put before each paragraph, and added functionality for putting long notes on new lines. The only issues remaining is that the solution still does not work well with page breaks, and also that it is quite cumbersome to but the same command before each paragraph, even if it is less cumbersome than the syntax of my previous solution.
I've now made a new solution (with the help of David Carlise) which I think is a bit closer to what you intended than my last attempt. It still has some problems though:
- Some unwanted spaces sometimes appear between paragraphs
- Notes are never put on separate lines
- No sidebreaks are allowed inside paragraphs
The result now look as in the image below:
And the source code.. :)
\documentclass[10pt]{article}
% Packages
\usepackage[newcommands]{ragged2e}
\usepackage{newfile}
\usepackage{color}
\usepackage{xparse}
\usepackage{graphics}
\usepackage[dvipsnames]{xcolor}
\usepackage{hyperref}
\usepackage{wrapfig}
\usepackage{ifthen}
% Settings
\colorlet{Pbardefault}{yellow!70} % Default color for the colored bar left to the notes
\colorlet{Plightdefault}{yellow!10}% Default color for background of the note
\def\Pmin{0.5} % ~minimal amount main text
\def\Pmax{0.95} % ~maximal amount main text / \Psf
\def\Psf{1.14} %
\newcounter{localsidenotecounter}
\newlength{\currentparskip}
\setlength{\currentparskip}{\parskip}
% The formatting of the note
\newdimen\Pparsep
\Pparsep=0.2em
\newdimen\Pcolorwidth
\Pcolorwidth=2mm
% The paragraph command
\makeatletter
\def\sidenotepi#1\par%
{
%
\immediate\openout\tempfile=sidenotes.tex %
%
\twopara%
{
#1 \immediate\closeout\tempfile
}
{\input{sidenotes.tex}}
}
\makeatother
% A command for finding the optimal text widths
\makeatletter
\long\def\twopara#1#2{%
\newdimen\columnsep
\columnsep=\dimexpr \Pparsep + \Pcolorwidth
\def\@elt##1{\global\value{##1}\the\value{##1}\relax}%
\edef\TY@ckpt{\cl@@ckpt}%
\@tempdima\dimexpr\textwidth-\columnsep\relax
{\hbadness\@M\raggedright\hsize.5\@tempdima \@tempdima\hsize
\setbox\z@\vbox{{#1\endgraf}}%
\setbox\tw@\vbox{{#2\endgraf}}%
\Gscale@div\tmp{\ht\z@}{\dimexpr\ht\z@+\ht\tw@\relax}%
\global\let\xtmp\tmp}%
\dimen@ \xtmp\@tempdima
\ifdim\dimen@<\Pmin\@tempdima \dimen@\Pmin\@tempdima\fi
\ifdim\dimen@>\Pmax\@tempdima \dimen@\Pmax\@tempdima\fi
\dimen@ii\dimexpr\@tempdima-\dimen@\relax
\TY@ckpt
\ifdim\dimen@ii<20pt
#1
\else
\ifdim \Psf\dimen@ii>\dimexpr\textwidth-\Pmin\textwidth
#1
\let\Pnotebox\Pnotebox@
\dimen@ii = \dimexpr \textwidth-4mm
\setlength{\intextsep}{2pt}%
\begin{figure}[h]
#2
\end{figure}
\else
\let\Pnotebox\Pnotebox@
\dimen@ii\dimexpr\Psf\dimen@ii
\setlength{\intextsep}{0pt}%
\begin{wrapfigure}{o}[-4mm]{\dimen@ii}
#2
\end{wrapfigure}
#1
\fi
\fi
\let\Pnotebox\Pnotebox@@
}
\makeatother
% The unformatted note text
\makeatletter
\long\def\Pnotebox@@#1#2#3{ %barcolor, backcolor, text
#3}
\makeatother
\makeatletter
\long\def\Pnotebox#1#2#3{ %barcolor, backcolor, text
#3}
\makeatother
% The formatting of the notes
\makeatletter
\newdimen\Pnoteheight
\long\def\Pnotebox@#1#2#3{ %barcolor, backcolor, text
\settoheight{\Pnoteheight}{\parbox{\dimen@ii}{#3}}%
\begin{minipage}{\dimexpr\dimen@ii+\Pcolorwidth}
\textcolor{#1}{\rule[\dimexpr-\Pnoteheight+1mm]{\Pcolorwidth}{\dimexpr 2\Pnoteheight}}%
\colorbox{#2}{\parbox{\dimen@ii}{#3}}
\end{minipage}%
}
\makeatother
% Commands for making the sidenote
\newwrite\tempfile
\makeatletter
\DeclareDocumentCommand \sidenote {m O{Pbardefault} O{Plightdefault}} {%
\stepcounter{localsidenotecounter}%
\hyperlink{sidenote\arabic{localsidenotecounter}}{${}^\textrm{\footnotesize\arabic{localsidenotecounter}}$}%
\immediate\write\tempfile%
{%
\noexpand\Pnotebox{#2}{#3}{%
\noexpand\footnotesize \noexpand{\noexpand\hypertarget{sidenote\arabic{localsidenotecounter}}{${}^{\arabic{localsidenotecounter}}$} \unexpanded{#1 }}%
}}}
\makeatother
\begin{document}
\sidenotepi
But I must explain to you how all this mistaken idea of denouncing pleasure and praising pain was born and I will give you a complete account of the system, and expound the actual teachings of the great explorer of the truth, the master-builder of human happiness. No one rejects, dislikes, or avoids pleasure itself, because it is pleasure, but because those who do not know how to pursue\sidenote{Nor again is there anyone who loves or pursues or desires to obtain pain of itself, because it is pain, but because occasionally circumstances occur in which toil and pain can procure him some great pleasure. Nor again is there anyone who loves or pursues or desires to obtain pain of itself, because it is pain, but because occasionally circumstances occur in which toil and pain can procure him some great pleasure. Nor again is there anyone who loves or pursues or desires to obtain pain of itself, because it is pain, but because occasionally circumstances occur in which toil and pain can procure him some great pleasure. Nor again is there anyone who loves or pursues or desires to obtain pain of itself, because it is pain, but because occasionally circumstances occur in which toil and pain can procure him some great pleasure} pleasure rationally encounter consequences that are extremely painful. Nor again is there anyone who loves or pursues or desires to obtain pain of itself, because it is pain, but because occasionally circumstances occur in which toil and pain can procure him some great pleasure
\sidenotepi
But I must explain to you how all this mistaken idea of denouncing pleasure and praising pain was born and I will give you a complete account of the system, and expound the actual teachings of the great explorer of the truth, the master-builder of human happiness. No one rejects, dislikes, or avoids pleasure itself, because it is pleasure, but because those who do not know how to pursue pleasure rationally encounter consequences that are extremely painful. Nor again is there anyone who loves or pursues or desires to obtain pain of itself, because it is pain, but because occasionally circumstances occur in which toil and pain can procure him some great pleasure.
\sidenotepi
But I must explain to you how all this mistaken idea of denouncing pleasure and praising pain was born and I will give you a complete account of the system, and expound the actual teachings of the great explorer of the truth, the master-builder of human happiness. No one rejects, dislikes, or avoids pleasure itself, because it is pleasure, but because those who do not know how to pursue\sidenote{Nor again is there anyone who loves or pursues or desires to obtain pain of itself, because it is pain, but because occasionally circumstances occur in which toil and pain can procure him some great pleasure} pleasure rationally encounter consequences that are extremely painful. Nor again is there anyone who loves or pursues or desires to obtain pain of itself, because it is pain, but because occasionally circumstances occur in which toil and pain can procure him some great pleasure.\sidenote{Nor again is there anyone who loves or pursues or desires to obtain pain of itself, because it is pain, but because occasionally circumstances occur in which toil and pain can procure him some great pleasure.}[blue][blue!5]
\end{document}
Here's one possibility using the mdframed
package and \marginnote
from the marginnote
package:
\documentclass{article}
\usepackage[lmargin=5cm,textwidth=15cm,marginparwidth=4cm]{geometry}
\usepackage[dvipsnames]{xcolor}
\usepackage{ragged2e}
\usepackage[framemethod=tikz]{mdframed}
\usepackage{marginnote}
\usetikzlibrary{calc}
\usepackage{lipsum}
\reversemarginpar
\newsavebox\mybox
\newlength\BoxHt
\newcommand\caution[2][-2.2\baselineskip]{%
\begin{lrbox}{\mybox}
\parbox{\marginparwidth}{#2}
\end{lrbox}%
\settoheight\BoxHt{\usebox\mybox}%
\raisebox{\BoxHt}[0pt][0pt]{\marginnote{%
\begin{mdframed}[
userdefinedwidth=\marginparwidth,
innerleftmargin=3pt,
innerrightmargin=3pt,
linecolor=BrickRed,
frametitle=\colorbox{white}{\space Caution\space},
frametitlefont=\color{BrickRed}\sffamily,
innertopmargin=10pt,
frametitleaboveskip=-\ht\strutbox,
frametitlebelowskip=-\ht\strutbox,
frametitlealignment=\raggedright,
singleextra={\fill[BrickRed] let \p1=(P), \p2=(O) in
( $ (P|-0,0.5*\y2+0.5*\y1) + (0,-4pt) $ ) -- +(4pt,4pt) -- +(0,8pt) -- cycle;}
]\RaggedRight\small#2\end{mdframed}}[#1]}}
\begin{document}
\lipsum*[3-4]\caution{\lipsum[2]}\lipsum[3-5]
Some test text\caution{Some text goes here just to illustrate the command}
\end{document}
A zoomed image:
The optional argument for \caution
allows you to control the vertical shifting if necessary.
And here's another simpler option (which I personally like more) using tikz
(and tikzpagenodes
):
\documentclass{article}
\usepackage[lmargin=5cm,textwidth=15cm,marginparwidth=4cm]{geometry}
\usepackage[dvipsnames]{xcolor}
\usepackage{ragged2e}
\usepackage[framemethod=tikz]{mdframed}
\usepackage{tikzpagenodes}
\usetikzlibrary{calc}
\usepackage{lipsum}
\newcounter{mycaution}
\newcommand\tikzmark[1]{%
\tikz[remember picture,overlay]\node[inner xsep=0pt,outer sep=0pt] (#1) {};}
\newcommand\caution[1]{%
\stepcounter{mycaution}%
\tikzmark{\themycaution}%
\begin{tikzpicture}[remember picture,overlay]
\node[draw=BrickRed,anchor=east,xshift=-\marginparsep]
(mybox\themycaution)
at ([yshift=3pt]current page text area.west|-\themycaution)
{\parbox{\marginparwidth}{\vskip10pt\RaggedRight\small#1}};
\node[fill=white,font=\color{BrickRed}\sffamily,anchor=west,xshift=7pt]
at (mybox\themycaution.north west) {\ Caution!\ };
\fill[BrickRed]
([yshift=3pt]mybox\themycaution.east) --
([xshift=3pt]mybox\themycaution.east) --
([yshift=-3pt]mybox\themycaution.east) -- cycle;
\end{tikzpicture}%
}
\begin{document}
\lipsum*[3-4]\caution{\lipsum[2]}\lipsum[3-5]
Some test text\caution{Some text goes here just to illustrate the command}
\end{document}
And a zoomed image:
An improved version; now there's an optional argument allowing you to change the position of the box and of the pointer; possible values are b
, t
, and c
, to place the pointer at the bottom, at the top, and at the center, respectively, of the box:
\documentclass{article}
\usepackage[lmargin=5cm,textwidth=15cm,marginparwidth=4cm]{geometry}
\usepackage[dvipsnames]{xcolor}
\usepackage{ragged2e}
\usepackage[framemethod=tikz]{mdframed}
\usepackage{tikzpagenodes}
\usetikzlibrary{calc}
\usepackage{lipsum}
\newcounter{mycaution}
\newcommand\pointeranchor{}
\newcommand\boxanchor{}
\newlength\boxvshift
\newlength\uppertrianglecorner
\newcommand\tikzmark[1]{%
\tikz[remember picture,overlay]\node[inner xsep=0pt,outer sep=0pt] (#1) {};}
\newcommand\caution[2][c]{%
\stepcounter{mycaution}%
\tikzmark{\themycaution}%
\if#1b\relax
\renewcommand\pointeranchor{mybox\themycaution.south east}%
\renewcommand\boxanchor{south east}%
\setlength\boxvshift{-10pt}%
\setlength\uppertrianglecorner{13pt}%
\else
\if#1t\relax
\renewcommand\pointeranchor{mybox\themycaution.north east}%
\renewcommand\boxanchor{north east}%
\setlength\boxvshift{10pt}%
\setlength\uppertrianglecorner{-7pt}%
\else
\if#1c\relax
\renewcommand\pointeranchor{mybox\themycaution.east}%
\renewcommand\boxanchor{east}%
\setlength\boxvshift{0pt}%
\setlength\uppertrianglecorner{3pt}%
\fi\fi\fi%
\begin{tikzpicture}[remember picture,overlay]
\node[draw=BrickRed,anchor=\boxanchor,xshift=-\marginparsep,yshift=\boxvshift]
(mybox\themycaution)
at ([yshift=3pt]current page text area.west|-\themycaution)
{\parbox{\marginparwidth}{\vskip10pt\RaggedRight\small#2}};
\node[fill=white,font=\color{BrickRed}\sffamily,anchor=west,xshift=7pt]
at (mybox\themycaution.north west) {\ Caution!\ };
\fill[BrickRed]
([yshift=\uppertrianglecorner]\pointeranchor) --
([yshift=\uppertrianglecorner-3pt,xshift=3pt]\pointeranchor) --
([yshift=\uppertrianglecorner-6pt]\pointeranchor) -- cycle;
\end{tikzpicture}%
}
\newcommand\Test{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.}
\begin{document}
\lipsum*[4]\caution[t]{\Test}\lipsum[3]\par\bigskip
\lipsum*[4]\caution{\Test}\lipsum[3]\par\bigskip
\lipsum*[4]\caution[b]{\Test}\lipsum[3]
\end{document}
The t
option is useful if the box will appear in the first lines of the text area; b
can be used in case the box will appear near the bottom of the text area.
A little variation required in a comment; now \caution has three optional arguments and a mandatory one:
\caution[<pos>][<color>][<title>]{<text>}
where ias an in the code immediately before, and can be b
, t
, or c
(default=c
); <color>
controls the color used for the frame and title (default=BrickRed
); <title>
changes the title used for the frame (defualt=Caption!
); <text>
is the content of the note.
\documentclass{article}
\usepackage[lmargin=5cm,textwidth=15cm,marginparwidth=4cm]{geometry}
\usepackage[dvipsnames]{xcolor}
\usepackage{ragged2e}
\usepackage{xparse}
\usepackage[framemethod=tikz]{mdframed}
\usepackage{tikzpagenodes}
\usetikzlibrary{calc}
\usepackage{lipsum}
\newcounter{mycaution}
\newcommand\pointeranchor{}
\newcommand\boxanchor{}
\newlength\boxvshift
\newlength\uppertrianglecorner
\newcommand\tikzmark[1]{%
\tikz[remember picture,overlay]\node[inner xsep=0pt,outer sep=0pt] (#1) {};}
\NewDocumentCommand{\caution}{O{c}O{BrickRed}O{Caution!}m}{%
\stepcounter{mycaution}%
\tikzmark{\themycaution}%
\if#1b\relax
\renewcommand\pointeranchor{mybox\themycaution.south east}%
\renewcommand\boxanchor{south east}%
\setlength\boxvshift{-10pt}%
\setlength\uppertrianglecorner{13pt}%
\else
\if#1t\relax
\renewcommand\pointeranchor{mybox\themycaution.north east}%
\renewcommand\boxanchor{north east}%
\setlength\boxvshift{10pt}%
\setlength\uppertrianglecorner{-7pt}%
\else
\if#1c\relax
\renewcommand\pointeranchor{mybox\themycaution.east}%
\renewcommand\boxanchor{east}%
\setlength\boxvshift{0pt}%
\setlength\uppertrianglecorner{3pt}%
\fi\fi\fi%
\begin{tikzpicture}[remember picture,overlay]
\node[draw=#2,anchor=\boxanchor,xshift=-\marginparsep,yshift=\boxvshift]
(mybox\themycaution)
at ([yshift=3pt]current page text area.west|-\themycaution)
{\parbox{\marginparwidth}{\vskip10pt\RaggedRight\small#4}};
\node[fill=white,font=\color{#2}\sffamily,anchor=west,xshift=7pt]
at (mybox\themycaution.north west) {\ #3\ };
\fill[#2]
([yshift=\uppertrianglecorner]\pointeranchor) --
([yshift=\uppertrianglecorner-3pt,xshift=3pt]\pointeranchor) --
([yshift=\uppertrianglecorner-6pt]\pointeranchor) -- cycle;
\end{tikzpicture}%
}
\newcommand\Test{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.}
\begin{document}
\lipsum*[4]\caution[t]{\Test}\lipsum[3]\par\bigskip
\lipsum*[4]\caution[c][cyan!80!black]{\Test}\lipsum[3]\par\bigskip
\lipsum*[4]\caution[b][orange][Defcon 5!]{\Test}\lipsum[3]
\end{document}
Best Answer
Update:
The notes have been requested to be inside the story box. Now I offer two options, both of them with automatic numbering for the notes and with the possibility to cross-reference them:
In this first option, the box for the story has a width equal to
\textwidth
, but the story text goes to the left of the box; the notes appear to the left of the text:In this second option, the notes are placed using
wrapfigure
from thewrapfig
package, so that the story text wraps around the note text; this approach, however, must be carefully used due to restrictions (imposed bywrapfig
); in particular, one must guarantee that a note won't appear near a page break, notes can't be used inside lists.Initial version:
Here's another option giving you automatic numbering for the notes and possibility to cross-reference them as required:
Remarks:
You write the text for the story inside the
story
environment (it has an optional argument allowing you to pass options (such as an eventual tile) to thetcolorbox
used.You write the notes using the
\StoryNote
command with syntaxwhere is the text for the note itself and is the string that can be used to produce cross-references (see example code above). The notes are automatically numbered.