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:
- Related packages and TeX.SE questions,
- Successful workaround for
marginfigure
usingcapt-of
package,- Trying to emulate environments out of
tufte
classes source-code:
- Successful (but unwieldy) for
marginfigure
;- Unsuccessful for
figure
andfigure*
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.
-
figure
andfigure*
: I then looked around in TeX.SE as I was blocked, and found several related threads:- Floats that span the text width and the margin: Don't handle caption in the margin
- Using \MarginFigure command:
- How to make sure the images properly float on the marginpar?: on the good way.
- I finally found Margin Figures/Captions that suggest to use
tufte
classes.
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 copyingtufte-book
classe'smarginfigure
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
andfigure*
: I tried to do the same forfigure
andfigure*
.
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}%
}
Best Answer
You could use the
sidenotes
-package: