# text, margin, and text + margin width floats without using tufte class

I want to use the marginfigure, figure*, and figure environment as defined in tufte-book and tufte-handout classes (see respectively Figure 1, 2 and 3 in the template example).

It means being able to use following environments:

• marginfigure: A figure in the margin that has \marginparwidth width, with its caption below in the margin;
• figure: A figure in the main text that has \textwidth with, with its caption besides in the margin (and bottom aligned);
• figure*: A figure over main text and margin that has \textwidth + \marginparsep + \marginparwidth width, with caption below in the margin.

However, since most of my source-code is intended for KOMAscript classes, I don't want to switch to one of the tufte classes just because of that.

Question: How to emulate those environment outside tufte classes?

This question have been solved by the accepted answer, i.e., using the sidenotes-package. However, for keeping someone else from following tortuous paths/culs-de-sac I took, I let herein below my attempts to solve my issue. It gather the previous versions of my question together.

Contents:

1. Related packages and TeX.SE questions,
2. Successful workaround for marginfigure using capt-of package,
3. Trying to emulate environments out of tufte classes source-code:
• Successful (but unwieldy) for marginfigure;
• Unsuccessful for figure and figure*

## 1. Related packages and TeX.SE questions

I am aware of the floatrow package, but even if it seems great for figure and figure*, I couldn't find how to handle marginfigure.

I also found Implementing marginfigure that solves the problem in an other way.

## 2. Successful workaround for marginfigure using capt-of package

I achieved a workaround for marginfigure using capt-of package, as described here.

\documentclass{scrartcl}
\usepackage{graphicx}
\usepackage{showframe}
\usepackage{capt-of}
\begin{document}
Some text.
\marginpar{%
\includegraphics[width=\marginparwidth]{example-image}%
\captionof{figure}{My caption}%
}%
More text.

Another paragraph
\end{document}


It's great, but doesn't solve the issue for figure and figure*

## 3. Trying to emulate environments out of tufte classes source-code

I've read through the sources, and since it all has been implemented (+ managing floats, etc.), my idea was so to re-implement the commands of tufte-common.def available in CTAN to reproduces those environment.

I am thus trying to adapt macros and environments defined in tufte-common.def in order to let them standalone.

I succeeded for marginfigure, but compilation fails when I redefine figure and figure*.

• marginfigure: I succeeded in copying tufte-book classe's marginfigure environment.

See MWE below.

\documentclass[twoside]{scrartcl}
\usepackage{showframe}
\usepackage{graphicx}
\usepackage{placeins}

\makeatletter
\input{my-tufte-marginfigure}
\makeatother

\begin{document}
\noindent
Say what?!

\begin{marginfigure}%
\includegraphics[width=\marginparwidth]{example-image-a}
\caption{My caption}
\end{marginfigure}
\end{document}


where my-tufte-marginfigure is following excerpt of tufte-common.def

\usepackage{ragged2e}

\newcommand{\@tufte@marginfont}{\normalfont\footnotesize\sffamily}
\newcommand*{\@tufte@caption@font}{\@tufte@marginfont}
\newcommand*{\@tufte@caption@justification}{\justifying} %% or \RaggedLeft or \RaggedRight

% Paragraph indentation and separation for marginal text
\newcommand{\@tufte@margin@par}{%
\setlength{\RaggedRightParindent}{0.5pc}%
\setlength{\JustifyingParindent}{0.5pc}%
\setlength{\parindent}{0.5pc}%
\setlength{\parskip}{0pt}%
}

\newsavebox{\@tufte@margin@floatbox}

\newenvironment{@tufte@margin@float}[2][-1.2ex]%
{\FloatBarrier% process all floats before this point so the figure/table numbers stay in order.
\begin{lrbox}{\@tufte@margin@floatbox}%
\begin{minipage}{\marginparwidth}%
\@tufte@caption@font% %%some font definition%%
\def\@captype{#2}%
\hbox{}\vspace*{#1}%
\@tufte@caption@justification%
\@tufte@margin@par%
\noindent%
}
{\end{minipage}%
\end{lrbox}%
\marginpar{\usebox{\@tufte@margin@floatbox}}%
}

%%
% Margin figure environment

\newenvironment{marginfigure}[1][-1.2ex]%
{\begin{@tufte@margin@float}[#1]{figure}}%
{\end{@tufte@margin@float}}

%%
% Margin table environment

\newenvironment{margintable}[1][-1.2ex]%
{\begin{@tufte@margin@float}[#1]{table}}
{\end{@tufte@margin@float}}

• figure and figure*: I tried to do the same for figure and figure*.

However, the compilation fails.

\documentclass[twoside]{scrartcl}
\usepackage{showframe}
\usepackage{graphicx}
\usepackage{placeins}
\usepackage{lipsum}

\makeatletter
%\input{my-tufte-marginfigure} %% cf. hereinabove
\input{my-tufte-figure}
\makeatother

\begin{document}

\begin{figure}
\includegraphics[width=\linewidth]{example-image-b}
\caption{My long caption is not \emph{that} long!}
\setfloatalignment{b}
\end{figure}

%   \begin{figure*}[h]
%       \includegraphics[width=\linewidth]{example-image-c}%
%       \caption{Some caption!}%
%   \end{figure*}

\end{document}


Where my-tufte-figure is following excerpt of tufte-common.def. (I know it's very long, but I'm sure it's pretty close to the minimal form!)

\usepackage{ifthen}
\usepackage{optparams}
\usepackage{fullwidth}
\usepackage{changepage}

%%
% symmetric' option -- puts marginpar space to the outside edge of the page
%   Note: this option forces the twoside option (see the .cls files)
\newboolean{@tufte@symmetric}
%%\DeclareOptionX[tufte]<common>{symmetric}{
\setboolean{@tufte@symmetric}{true}
%%%\@tufte@info@noline{The symmetric' option implies twoside'}
%%%\ExecuteOptionsX[tufte]<common>{twoside}
%%}

%DEBUT
%%
% A collection of macros to be used with the new Tufte-style float environments.
% \setfloatalignment forces the caption placement to be treated as top, bottom, etc.
% \forcerectofloat forces the float to be treated as if it were appearing on a recto page.
% \forceversofloat does the same, but for verso pages.

\newcommand{\@tufte@float@debug@info}{}% contains debug info generated as the float is processed
\newcommand{\@tufte@float@debug}[1]{% adds debug info to the queue for output
\ifthenelse{\equal{\@tufte@float@debug@info}{}}%
{\def\@tufte@float@debug@info{#1}}%
}

\newcommand{\floatalignment}{x}% holds the current float alignment (t, b, h, p)
\newcommand{\setfloatalignment}[1]{\global\def\floatalignment{#1}\@tufte@float@debug{Forcing position: [#1]}}% manually sets the float alignment
\newboolean{@tufte@float@recto}
\newcommand{\forcerectofloat}{\gsetboolean{@tufte@float@recto}{true}\@tufte@float@debug{Forcing page: [recto]}}
\newcommand{\forceversofloat}{\gsetboolean{@tufte@float@recto}{false}\@tufte@float@debug{Forcing page: [verso]}}

% Boxes to temporarily store our float and caption
\newsavebox{\@tufte@figure@box}
\newsavebox{\@tufte@caption@box}

%MANDATORY
% Save original LaTeX float environment
\let\@tufte@orig@float\@float
\let\@tufte@orig@endfloat\end@float
%MANDATORY/
% New length for tweaking float captions
\newlength{\@tufte@caption@vertical@offset}
\setlength{\@tufte@caption@vertical@offset}{0pt}

% Store the caption and label contents
\newcommand{\@tufte@stored@shortcaption}{}
\newcommand{\@tufte@stored@caption}{}
\newcommand{\@tufte@stored@label}{}

\long\def\@tufte@caption[#1][#2]#3{%
\ifthenelse{\isempty{#1}}%
{\gdef\@tufte@stored@shortcaption{#3}}%
{\gdef\@tufte@stored@shortcaption{#1}}%
\gsetlength{\@tufte@caption@vertical@offset}{-#2}% we want a positive offset to lower captions
\gdef\@tufte@stored@caption{#3}%
}

\newcommand{\@tufte@label}[1]{%
\gdef\@tufte@stored@label{#1}%
}

\newcommand{\@tufte@fps}{}

\newboolean{@tufte@float@star}
\newlength{\@tufte@float@contents@width}
%FIN

%%
% Compute lengths used for full-width displays

\newlength{\@tufte@overhang}% used by the fullwidth environment and the running heads
\newlength{\@tufte@fullwidth}
\newlength{\@tufte@caption@fill}

\newcommand{\TufteRecalculate}{%
\setlength{\@tufte@overhang}{\marginparwidth}

\setlength{\@tufte@fullwidth}{\textwidth}

\setlength{\@tufte@caption@fill}{\textwidth}
}

\AtBeginDocument{\TufteRecalculate}

%%
% Globally sets a boolean

\newcommand*{\gsetboolean}[2]%
{% based on code from ifthen pkg
\lowercase{\def\@tempa{#2}}%
\@ifundefined{@tempswa\@tempa}%
{\PackageError{ifthen}{You can only set a boolean to true' or false'}\@ehc}%
{\@ifundefined{#1\@tempa}%
{\PackageError{ifthen}{Boolean #1 undefined}\@ehc}%
{\global\csname#1\@tempa\endcsname}%
}%
}

%%
% Detect if the subfigure package has been loaded

\newboolean{@tufte@packages@subfigure}
\setboolean{@tufte@packages@subfigure}{false}
\AtBeginDocument{%
{\gsetboolean{@tufte@packages@subfigure}{true}}
{\gsetboolean{@tufte@packages@subfigure}{false}}%
}

% Write our own aliases for the \checkoddpage and \ifoddpage or \ifcpoddpage commands
\newboolean{@tufte@odd@page}
\setboolean{@tufte@odd@page}{true}
\newcommand*{\@tufte@checkoddpage}%
{%
\checkoddpage%
\ifthenelse{\boolean{@tufte@changepage}}%
{%
\ifoddpage%
\setboolean{@tufte@odd@page}{true}%
\else%
\setboolean{@tufte@odd@page}{false}%
\fi%
}{%
\ifcpoddpage%
\setboolean{@tufte@odd@page}{true}%
\else%
\setboolean{@tufte@odd@page}{false}%
\fi%
}%
}

%%
% Define a float environment to place the captions in the margin space

\newenvironment{@tufte@float}[3][htbp]%
{% begin @tufte@float
% Should this float be full-width or just text-width?
\ifthenelse{\equal{#3}{star}}%
{\gsetboolean{@tufte@float@star}{true}}%
{\gsetboolean{@tufte@float@star}{false}}%
%
% Check page side (recto/verso) and store detected value -- can be overriden in environment contents
\@tufte@checkoddpage%
\ifthenelse{\boolean{@tufte@odd@page}}%
{\gsetboolean{@tufte@float@recto}{true}%%%\@tufte@float@debug{Detected page: [recto/odd]}
}%
{\gsetboolean{@tufte@float@recto}{false}%%%\@tufte@float@debug{Detected page: [verso/even]}
}%
% If the float placement specifier is 'b' and only 'b', then bottom-align the mini-pages, otherwise top-align them.
\renewcommand{\@tufte@fps}{#1}%
%%%\@tufte@float@debug{Allowed positions: [#1]}%
\ifthenelse{\equal{#1}{b}\OR\equal{#1}{B}}%
{\renewcommand{\floatalignment}{b}%%%\@tufte@float@debug{Presumed position: [bottom]}
}%
{\renewcommand{\floatalignment}{t}%%%\@tufte@float@debug{Presumed position: [top]}
}%
% Capture the contents of the \caption and \label commands to use later
\global\let\@tufte@orig@caption\caption%
\global\let\@tufte@orig@label\label%
\renewcommand{\caption}{\optparams{\@tufte@caption}{[][0pt]}}%
%QUESTION: is ##1 ok?
\renewcommand{\label}[1]{\@tufte@label{##1}}%
% Handle subfigure package compatibility
\ifthenelse{\boolean{@tufte@packages@subfigure}}%
{% don't move the label while inside a \subfigure or \subtable command
\global\let\label\@tufte@orig@label%
}{%
}% subfigure package is not loaded
\@tufte@orig@float{#2}[#1]%
\ifthenelse{\boolean{@tufte@float@star}}%
{\setlength{\@tufte@float@contents@width}{\@tufte@fullwidth}}%
{\setlength{\@tufte@float@contents@width}{\textwidth}}%
\begin{lrbox}{\@tufte@figure@box}%
\begin{minipage}[\floatalignment]{\@tufte@float@contents@width}\hbox{}%
}
{% end @tufte@float
\par\hbox{}\vspace{-\baselineskip}\ifthenelse{\prevdepth>0}{\vspace{-\prevdepth}}{}% align baselines of boxes
\end{minipage}%
\end{lrbox}%
% build the caption box
\begin{lrbox}{\@tufte@caption@box}%
\begin{minipage}[\floatalignment]{\marginparwidth}\hbox{}%
\ifthenelse{\NOT\equal{\@tufte@stored@caption}{}}%
{\@tufte@orig@caption[\@tufte@stored@shortcaption]{\@tufte@stored@caption}}%
{}%
\ifthenelse{\NOT\equal{\@tufte@stored@label}{}}%
{\@tufte@orig@label{\@tufte@stored@label}}%
{}%
\par\vspace{-\prevdepth}%% TODO: DOUBLE-CHECK FOR SAFETY
\end{minipage}%
\end{lrbox}%
% now typeset the stored boxes
\begin{fullwidth}%
\begin{minipage}[\floatalignment]{\linewidth}%
\ifthenelse{\boolean{@tufte@float@star}}%
{\@tufte@float@fullwidth[\@tufte@caption@vertical@offset]{\@tufte@figure@box}{\@tufte@caption@box}}%
{\@tufte@float@textwidth[\@tufte@caption@vertical@offset]{\@tufte@figure@box}{\@tufte@caption@box}}%
\end{minipage}%
\end{fullwidth}%
\@tufte@orig@endfloat% end original LaTeX float environment
% output debug info
%   \ifthenelse{\boolean{@tufte@debug}}%
%       {%
%           \typeout{^^J^^J----------- Tufte-LaTeX float information ----------}%
%           \ifthenelse{\equal{\@tufte@stored@label}{}}%
%           {\typeout{Warning: Float unlabeled!}}%
%           {\typeout{Float label: [\@tufte@stored@label]}}%
%           \typeout{Page number: [\thepage]}%
%           \def\MessageBreak{^^J}%
%           \typeout{\@tufte@float@debug@info}%
%           \ifthenelse{\boolean{@tufte@symmetric}}%
%           {\typeout{Symmetric: [true]}}%
%           {\typeout{Symmetric: [false]}}%
%           \typeout{----------------------------------------------------^^J^^J}%
%       }{%
%       }%
% reset commands and temp boxes and captions
%%\gdef\@tufte@float@debug@info{}%
\let\caption\@tufte@orig@caption%
\let\label\@tufte@orig@label%
\begin{lrbox}{\@tufte@figure@box}\hbox{}\end{lrbox}%
\begin{lrbox}{\@tufte@caption@box}\hbox{}\end{lrbox}%
\gdef\@tufte@stored@shortcaption{}%
\gdef\@tufte@stored@caption{}%
\gdef\@tufte@stored@label{}%
\gsetlength{\@tufte@caption@vertical@offset}{0pt}% reset caption offset
}

\newcommand{\@tufte@float@textwidth}[3][0pt]%
{%
\ifthenelse{\NOT\boolean{@tufte@symmetric}\OR\boolean{@tufte@float@recto}}
{% asymmetric or page is odd, so caption is on the right
\hbox{%
\usebox{#2}%
\hspace{\marginparsep}%
\smash{\raisebox{#1}{\usebox{#3}}}%
}%
\@tufte@float@debug{Caption position: [right]}%
}{% symmetric pages and page is even, so caption is on the left
\hbox{%
\smash{\raisebox{#1}{\usebox{#3}}}%
\hspace{\marginparsep}%
\usebox{#2}%
}%
\@tufte@float@debug{Caption position: [left]}%
}%
}

\newcommand{\@tufte@float@fullwidth}[3][0pt]%
{%
\ifthenelse{\equal{\floatalignment}{b}}%
{% place caption above figure
\ifthenelse{\NOT\boolean{@tufte@symmetric}\OR\boolean{@tufte@float@recto}}%
{\hfill\smash{\raisebox{#1}{\usebox{#3}}}\par\usebox{#2}\@tufte@float@debug{Caption position: [above right]}}% caption on the right
{\smash{\raisebox{#1}{\usebox{#3}}}\hfill\par\usebox{#2}\@tufte@float@debug{Caption position: [above left]}}% caption on the left
}{% place caption below figure
\ifthenelse{\NOT\boolean{@tufte@symmetric}\OR\boolean{@tufte@float@recto}}%
{\usebox{#2}\par\hfill\smash{\raisebox{#1}{\usebox{#3}}}\@tufte@float@debug{Caption position: [below right]}}% caption on the right
{\usebox{#2}\par\smash{\raisebox{#1}{\usebox{#3}}}\hfill\@tufte@float@debug{Caption position: [below left]}}% caption on the left
}%
}

%%
% Redefine the figure environment to place the captions in the margin space

\renewenvironment{figure}[1][htbp]%
{%
\begin{@tufte@float}[#1]{figure}{}%
}{%
\end{@tufte@float}%
}

%%
% Redefine the table environment to place the captions in the margin space

\renewenvironment{table}[1][htbp]%
{%
\begin{@tufte@float}[#1]{table}{}%
}{%
\end{@tufte@float}%
}

%%
% Full-width figure

\renewenvironment{figure*}[1][htbp]%
{%
\begin{@tufte@float}[#1]{figure}{star}%
}{%
\end{@tufte@float}%
}

%%
% Full-width table

\renewenvironment{table*}[1][htbp]%
{%
\begin{@tufte@float}[#1]{table}{star}%
}{%
\end{@tufte@float}%
}


You could use the sidenotes-package:

\documentclass[twoside]{scrartcl}
\usepackage{showframe}
\usepackage{graphicx}
\usepackage{placeins}
\usepackage{sidenotes}
\usepackage{kantlipsum}

\begin{document}
\begin{marginfigure}%
\caption{My caption}
\includegraphics[width=\marginparwidth]{example-image-a}
\end{marginfigure}

\kant[1]
\begin{figure}[h]
\sidecaption{My caption}
\includegraphics[width=\textwidth]{example-image-a}
\end{figure}

\begin{figure*}
\sidecaption{My caption}
\includegraphics[width=\linewidth]{example-image-a}
\end{figure*}

\end{document}


• I knew it! Someone had to have wanted similar to be! But I couldn't find the relevant package... Thank you for not having let me fiddle with tufte-classes any longer! Apr 1 '16 at 13:06
• Thank you so much for this answer @DG' Dec 7 '20 at 8:31
• I second these comments. I really wanted something like this, preferably with KOMA script, and the tufte class has been giving me nothing but grief. Apr 10 at 7:20

I am rather late to the party, but there might be another recent option worth checking out, namely the kaobook class. The style is a bit different, but it picks up many ideas from tufte` and is under active development (as of 2021).