Search code examples
knitrbeamerxelatex

Drop shadow in knitr


I'm mixing knitr, beamer, and xelatex to make some slides to a lecture. I'd like to see my figures dropping shadows in order to make my presentation more fancy.

I found this great way to drop shadows from regular figures, but I couldn't make it work inside knitr chunks.

Do you guys know how can I solve this issue?


Solution

  • With the chunk option fig.show = "hide" you can prevent knitr from including generated figures into your document. This allows you to manually include the figure, using whatever markup you like.

    Note that by default figures are stored in the directory figure and the file name follows the pattern chunkname-figurenumber, e.g. the second figure from mychunk has the filename mychunk-1.pdf. (See fig.path in the chunk options list linked above.)

    This leaves us with one last pitfall: The background color of your plot.

    Depending on the graphics packages and devices you use, the background of plots might be transparent (e.g. tikz for base graphics) or white (e.g. ggplot2) by default. If you definitely want the background color to be always white for base graphics, you can set up a hook for the white option like this:

    <<white-background,eval=FALSE>>=    
    knit_hooks$set(white = function(before, options, envir) {if (before) par(bg = 'white')}) 
    

    (The quote is from an old version of the knitr graphics manual; I cannot find it in the current version.)

    If you need a chunk hook or if setting par(bg = "white") is sufficient for you depends on the number of graphs you are generating. If you don't know how to use chunk hooks, take a look at the end of this posting.

    Not using a chunk hook and adapting the TEX code from the answer that was linked in the question, the following code uses knitr to generate a figure with a shadow:

    \documentclass{beamer}
    \usepackage{tikz}
    \usetikzlibrary{shadows,calc}
    
    % code adapted from https://tex.stackexchange.com/a/11483/3954
    % code then adapted from https://tex.stackexchange.com/a/81847/37118
    %
    % some parameters for customization
    \def\shadowshift{3pt,-3pt}
    \def\shadowradius{6pt}
    
    \colorlet{innercolor}{black!60}
    \colorlet{outercolor}{gray!05}
    
    % this draws a shadow under a rectangle node
    \newcommand\drawshadow[1]{
        \begin{pgfonlayer}{shadow}
            \shade[outercolor,inner color=innercolor,outer color=outercolor] ($(#1.south west)+(\shadowshift)+(\shadowradius/2,\shadowradius/2)$) circle (\shadowradius);
            \shade[outercolor,inner color=innercolor,outer color=outercolor] ($(#1.north west)+(\shadowshift)+(\shadowradius/2,-\shadowradius/2)$) circle (\shadowradius);
            \shade[outercolor,inner color=innercolor,outer color=outercolor] ($(#1.south east)+(\shadowshift)+(-\shadowradius/2,\shadowradius/2)$) circle (\shadowradius);
            \shade[outercolor,inner color=innercolor,outer color=outercolor] ($(#1.north east)+(\shadowshift)+(-\shadowradius/2,-\shadowradius/2)$) circle (\shadowradius);
            \shade[top color=innercolor,bottom color=outercolor] ($(#1.south west)+(\shadowshift)+(\shadowradius/2,-\shadowradius/2)$) rectangle ($(#1.south east)+(\shadowshift)+(-\shadowradius/2,\shadowradius/2)$);
            \shade[left color=innercolor,right color=outercolor] ($(#1.south east)+(\shadowshift)+(-\shadowradius/2,\shadowradius/2)$) rectangle ($(#1.north east)+(\shadowshift)+(\shadowradius/2,-\shadowradius/2)$);
            \shade[bottom color=innercolor,top color=outercolor] ($(#1.north west)+(\shadowshift)+(\shadowradius/2,-\shadowradius/2)$) rectangle ($(#1.north east)+(\shadowshift)+(-\shadowradius/2,\shadowradius/2)$);
            \shade[outercolor,right color=innercolor,left color=outercolor] ($(#1.south west)+(\shadowshift)+(-\shadowradius/2,\shadowradius/2)$) rectangle ($(#1.north west)+(\shadowshift)+(\shadowradius/2,-\shadowradius/2)$);
            \filldraw ($(#1.south west)+(\shadowshift)+(\shadowradius/2,\shadowradius/2)$) rectangle ($(#1.north east)+(\shadowshift)-(\shadowradius/2,\shadowradius/2)$);
        \end{pgfonlayer}
    }
    
    % create a shadow layer, so that we don't need to worry about overdrawing other things
    \pgfdeclarelayer{shadow}
    \pgfsetlayers{shadow,main}
    
    \newsavebox\mybox
    \newlength\mylen
    
    \newcommand\shadowimage[2][]{%
    \setbox0=\hbox{\includegraphics[#1]{#2}}
    \setlength\mylen{\wd0}
    \ifnum\mylen<\ht0
    \setlength\mylen{\ht0}
    \fi
    \divide \mylen by 120
    \def\shadowshift{\mylen,-\mylen}
    \def\shadowradius{\the\dimexpr\mylen+\mylen+\mylen\relax}
    \begin{tikzpicture}
    \node[anchor=south west,inner sep=0] (image) at (0,0) {\includegraphics[#1]{#2}};
    \drawshadow{image}
    \end{tikzpicture}}
    
    \begin{document}
    
    
    <<myfigure, echo = FALSE, fig.show = "hide">>=
    par(bg = "white")
    plot(1)
    @
    
    \begin{frame}
    \shadowimage[width=7cm]{figure/myfigure-1}
    \end{frame}
    
    \end{document}
    

    Figure with shadow.