1

For a book project, examples, theorems, etc. are all styled using tcolorbox. (This part is not really negotiable -- the LaTeX code is generated by PreTeXt.)

I've been trying to figure out how to get some figures, as well as asides, into the margin.

My most recent attempt used eso-pic, but I ran into a problem: if the figure I want to be in the margin is part of an example (which is a tcolorbox environment) that spans 3 pages, I can't get a figure on the second page. (The \AddToShipoutPicture* command takes its page reference from the top of the tcolorbox.)

I found this example, which creates a tcolorbox in the margin for the figure/aside.

This does almost exactly what I want. But the example tcolorbox uses the option parbox=false, because we want paragraphs in an example. With this option present, the figure in the margin gets indented by 30pt.

Is there a way to avoid this, other than using separate environments for figures inside/outside an example? Here is an example to illustrate:

\documentclass[twoside]{article}
\usepackage[breakable, theorems, skins]{tcolorbox}
%\tcbset{enhanced}
\usepackage[showframe,letterpaper]{geometry}
\usepackage{ifthen}
\usepackage{lipsum}
\geometry{inner=1in,textheight=9in,textwidth=320pt,marginparwidth=150pt,marginparsep=20pt,bottom=1in,footskip=29pt}


\newtcbtheorem[number within = section]{myexample}{Example}%
    {breakable,
    colback=white, colbacktitle=white, coltitle=black,
        parbox=false,}{ex}

\newtcbtheorem[number within=section]{myfig}{My Theorem}%
    {colback=white, colbacktitle=white,coltitle=black,
     fonttitle=\bfseries,
     }{th}    

\newsavebox{\mymargbox}
\newcommand{\marginfig}[1]{%
\sbox{\mymargbox}{\vbox{%
       \linewidth=\marginparwidth%
       #1%
   }}
   \ifthenelse{\isodd{\thepage}}{
   \leftskip -325pt%
   \usebox{\mymargbox}
   \leftskip 325pt%
   }
   {\leftskip 186pt%
   \usebox{\mymargbox}
   \leftskip -186pt%
   }
  \vspace*{-\ht\mymargbox}
  }
\begin{document}
\begin{myexample}{title}{label}
{
\lipsum[1]

\par

\lipsum[2]

{

\marginfig{
\begin{myfig}{title2}{label2}
\begin{itemize}
\item An item
\item Another item
\item A somewhat longer item that is a bit longer than the margin width
\end{itemize}
\end{myfig}
}

}

\lipsum[5]
}
\end{myexample}

{

\marginfig{
\begin{myfig}{title3}{label3}
\begin{itemize}
\item An item
\item Another item
\item A somewhat longer item that is a bit longer than the margin width
\end{itemize}
\end{myfig}
}

}


\lipsum[1-2]

{

\marginfig{
\begin{myfig}{title4}{label4}
\begin{itemize}
\item An item
\item Another item
\item A somewhat longer item that is a bit longer than the margin width
\end{itemize}
\end{myfig}
}

}

\lipsum[3]

\par

\begin{myexample}{Title5}{label5}
\lipsum[2]


{

\marginfig{
\begin{myfig}{title6}{label6}
\begin{itemize}
\item An item
\item Another item
\item A somewhat longer item that is a bit longer than the margin width
\end{itemize}
\end{myfig}
}

}

\end{myexample}
\end{document}

output of MWE

7
  • Not a global solution, but if I drop \hskip0pt in front of the \marginfig command (or just inside), it removes the indentation. (Not global because if I put it in the macro definition, the boxes that weren't affected before get shifted out of the margin.) I also wish I could do away with the braces and whitespace wrapping around each \marginfig but without them, all the text following the \marginfig gets pulled into the margin. (Which is to say that I don't fully understand this macro...) Jul 22, 2021 at 15:14
  • On the plus side, my LaTeX is written via XSL transform from PreTeXt XML source. So I can use an <xsl:if> that looks up the tree to see if a figure is a descendent of example. If yes, it writes in the \hskip. Jul 22, 2021 at 15:16
  • I updated the code to correct some minor issues. Please try it. (1) Please check that everything works changing the page geometry: textwidth, marginparwidth,marginparsep. (2) Still missing: automatic horizontal alignment from inside the tcolorbox (the Example enviroment), if boxsep or the inner margins change. Thank you for your feedback. It is always a pleasure to contribute to a real world project. Jul 23, 2021 at 19:47
  • New update to version 3. I think I calculate everything in the correct way. No more hardwired dimensions. Try the new v3 code by changing the boxsep. Jul 23, 2021 at 21:09
  • This is great! Thank you very much. I got v2 working properly, so I'm happy with that. I will try v3 though to see if that works once I'm in a more complicated setting. With v2, only issue I saw was sometimes a margin box at the bottom of one page gets dropped to the next page, but on the wrong side. Not unexpected. Some adjusting with \pagebreak or moving the box up a paragraph will take care of that. Jul 23, 2021 at 21:14

1 Answer 1

1

I propose another approach (much easier to understand) to put the theorem boxes on the margin.

Using the package xcoffin you can put any material anywhere on the page. The reference point is the current insertion point.

Because there are some theorems that come from inside a tcolorbox and others from the text area, it takes two macros to account for the different margins involved.

(1) \insertTheoBox{<content>}{<vertical shift>}

(2) \insertTheoPara{<content>}{<vertical shift>}

The vertical shift allows for a vertical displacement inside the margin area.

Two inner "boxes" are used in the macros: \Framex is a zero depth box that allows for the \Theox box to be joined at a proper position.

The result of the assembly is then typeset into the margin area.

x

UPDATED Minor issues corrected. No hardwired dimensions.

%%%% version 3

\documentclass[twoside]{article}
\usepackage[breakable, theorems, skins]{tcolorbox}
%\tcbset{enhanced}
\usepackage[showframe,letterpaper]{geometry}
\usepackage{ifthen}
\usepackage{lipsum}
\geometry{inner=1in,textheight=9in,textwidth=320pt,marginparwidth=150pt,marginparsep=20pt,bottom=1in,footskip=29pt}

\newtcbtheorem[number within = section]{myexample}{Example}%
{breakable,
    colback=white,
    colbacktitle=white,
    coltitle=black,
    parbox=false,
 %  boxsep=14mm,  % for testing
}{ex}

\newtcbtheorem[number within=section]{myfig}{My Theorem}%
{colback=white, colbacktitle=white,coltitle=black,
    fonttitle=\bfseries,
}{th}  


%%*************************************************************** v3
\usepackage{xcoffins} %added
\NewCoffin\Framex
\NewCoffin\Theox

\usepackage{changepage} %added
\strictpagecheck

\newlength{\Textw} % save textwidth outside the boxes
\setlength{\Textw}{\textwidth}
\newlength{\Hshift}
\newlength{\Mshift}

\newcommand*{\calculateMshift}{%
    \checkoddpage
    \ifoddpage
        \setlength{\Mshift}{\marginparsep}
    \else
        \setlength{\Mshift}{\dimexpr-\marginparsep-\textwidth-\marginparwidth\relax}
    \fi}

    \newcommand*{\calculateHshift}{%
    \checkoddpage
    \ifoddpage
        \setlength{\Hshift}{\dimexpr\Textw/2-\tcbtextwidth/2\relax}
    \else
        \setlength{\Hshift}{\dimexpr-\Textw/2+\tcbtextwidth/2\relax}
    \fi}
    
\newcommand{\insertTheoBox}[2]{% v2 updated         
    \par %start a new line
    \calculateMshift
    \calculateHshift    
    \SetHorizontalCoffin\Framex{\color{blue}\rule{\tcbtextwidth}{0pt}} %clear box Framex
    \SetVerticalCoffin\Theox{\marginparwidth}{#1}% fill box \Theox
    \JoinCoffins*\Framex[r,vc]\Theox[l,vc](\dimexpr\Mshift+\Hshift\relax,#2)%join boxes
    \noindent\TypesetCoffin\Framex\\[-2\baselineskip] %typeset assembly
}

\newcommand{\insertTheoPara}[2]{% v2 updated
    \par
    \calculateMshift    
    \SetHorizontalCoffin\Framex{\color{red}\rule{\textwidth}{0pt}}
    \SetVerticalCoffin\Theox{\marginparwidth}{#1}
    \JoinCoffins*\Framex[r,vc]\Theox[l,vc](\Mshift,#2)  
    \noindent\TypesetCoffin\Framex\\[-2\baselineskip]
}
%%***************************************************************

\begin{document}    

    \begin{myexample}{title}{label}
        1. \lipsum[1]           
        \insertTheoBox{%
            \begin{myfig}{title1}{label1}
                \begin{itemize}
                    \item An item
                    \item Another item
                    \item A somewhat longer item that is a bit longer than the margin width
                \end{itemize}
            \end{myfig}
        }{70pt}         
                
    2.  \lipsum[2]          
    \insertTheoBox{%
        \begin{myfig}{title2}{label2}
            \begin{itemize}
                \item An item
                \item Another item
                \item A somewhat longer item that is a bit longer than the margin width
            \end{itemize}
        \end{myfig}
    }{50pt}
            
    3.  \lipsum[5]      
    \end{myexample}

    \insertTheoPara{%       
    \begin{myfig}{title3}{label3}
        \begin{itemize}
            \item An item
            \item Another item
            \item A somewhat longer item that is a bit longer than the margin width
        \end{itemize}
    \end{myfig}
        }{-40pt}    

    
    4.  \lipsum[1-2]                    
    \insertTheoPara{%
    \begin{myfig}{title4}{label4}
        \begin{itemize}
            \item An item
            \item Another item
            \item A somewhat longer item that is a bit longer than the margin width
        \end{itemize}
    \end{myfig}
    }{-20pt}
    
    5.  \lipsum[3]  
    
        \insertTheoPara{%
        \begin{myfig}{title5}{label7}
            \begin{itemize}
                \item An item
                \item Another item
                \item A somewhat longer item that is a bit longer than the margin width
            \end{itemize}
        \end{myfig}
    }{-250pt}
    
    \begin{myexample}{Title5}{label5}
    6.  \lipsum[2]  
    
        \insertTheoBox{%
        \begin{myfig}{title6}{label6}
            \begin{itemize}
                \item An item
                \item Another item
                \item A somewhat longer item that is a bit longer than the margin width
            \end{itemize}
        \end{myfig}
    }{60pt}     
            
    \end{myexample}
\end{document}

The margin boxes and their positions will automatically adjust to changes in the page geometry or the internal geometry of the boxes in the text area.

For the next figure

\geometry{inner=1in,textheight=9in,
    textwidth=220pt, % changed <<<<<
    marginparwidth=220pt,  % changed <<<<<
    marginparsep=40pt,  % changed <<<<<
    bottom=1in,footskip=29pt}

    \newtcbtheorem[number within = section]{myexample}{Example}%
{breakable,
    colback=white,
    colbacktitle=white,
    coltitle=black,
    parbox=false,
    boxsep=14mm,  % added <<<<<
}{ex}

y

7
  • Thanks! I'll have a look at that package. Main concern with adding packages is making sure it plays nicely with everything else. (This is a large book, with PreTeXt-generated .tex source: about 100,000 lines of LaTeX, and 1000 of that is preamble.) The vertical shift is a nice feature though: there's always a bunch of hand-editing needed to adjust page breaks and margin figures. Jul 23, 2021 at 3:42
  • Updating my XSL now to write this in. I'll rebuild my LaTeX overnight and if I don't see any weirdness when I compile in the morning, I'll mark this as answered. Jul 23, 2021 at 3:52
  • Everything is working beautifully in a test article, with everything I've thrown at it so far. When I try to do it in my book, the first block is placed correctly, but the next one is placed to the right of it, instead of below it! Horizontal placement is sort of all over the place. Jul 23, 2021 at 17:08
  • @Sean Fitzpatrick Hard to tell without a complete code to test myself. The two blocks are inside a tcolobox or in pure text? Jul 23, 2021 at 17:49
  • The problem seems to be that the marco was getting inserted without leading whitespace. If I put a blank line above the macro call, I can avoid that problem. The macro within the example environment has some horizontal alignment issues but hopefully I can fix that with some tinkering. Jul 23, 2021 at 18:56

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .