16

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.

enter image description here

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}

    \usepackage[a4paper,left=24.8mm,top=27.4mm,headsep=2\baselineskip,textwidth=107mm,marginparsep=8.2mm,marginparwidth=49.4mm,textheight=49\baselineskip,headheight=\baselineskip]{geometry} % tufte-handout definitions

\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}

    \usepackage[a4paper,left=24.8mm,top=27.4mm,headsep=2\baselineskip,textwidth=107mm,marginparsep=8.2mm,marginparwidth=49.4mm,textheight=49\baselineskip,headheight=\baselineskip]{geometry}

\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}}%
    {\g@addto@macro\@tufte@float@debug@info{\MessageBreak#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}
    \addtolength{\@tufte@overhang}{\marginparsep}

    \setlength{\@tufte@fullwidth}{\textwidth}
    \addtolength{\@tufte@fullwidth}{\marginparsep}
    \addtolength{\@tufte@fullwidth}{\marginparwidth}

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

\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{%
    \@ifpackageloaded{subfigure}
    {\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}%
    }
18

You could use the sidenotes-package:

\documentclass[twoside]{scrartcl}
    \usepackage{showframe}
    \usepackage{graphicx}
    \usepackage{placeins}
    \usepackage{sidenotes}
    \usepackage{kantlipsum}
    \usepackage[a4paper,left=24.8mm,top=27.4mm,headsep=2\baselineskip,textwidth=107mm,marginparsep=8.2mm,marginparwidth=49.4mm,textheight=49\baselineskip,headheight=\baselineskip]{geometry} % tufte-handout definitions


\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}

enter image description here enter image description here

  • 1
    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! – ebosi Apr 1 '16 at 13:06

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.