Thomas
Thomas

Reputation: 10055

How to extend Pandoc

I am using bookdown for a documentation which is outputted with bookdown::gitbook and bookdown::pdf_book.

In my Rmd files, I am using a div to wrap around notes and warnings styled with a css file. For example:

<div class="note">
This is a note.
</div>

Obviously, HTML and CSS is ignored when generating the PDF file. I was wondering if there is a way to "inject" a small script that would replace the div with, for example, a simple prefix text.

Or, is there another way to have it formatted in HTML and in the PDF without littering my file by adding something lengthy every time like:

if (knitr::is_html_output(excludes='epub')) {
  cat('
<div class="note">
This is a note.
</div>
  ')
} else {
  cat('Note: This is a note.')
}

I could also style blockquotes as described here but it is not an option as I still need blockquotes.

Upvotes: 1

Views: 462

Answers (2)

jtbayly
jtbayly

Reputation: 303

The appropriate way to do this is to use a fenced div rather than inserting HTML directly into your markdown and trying to parse it later with LUA. Pandoc already allows you to insert custom styles and process them to both file types. In other words, it will take care of creating the appropriate HTML and LaTeX for you, and then you just need to style each of them. The Bookdown documentation references this here, but it simply points to further documentation here, and here.

This method will create both your custom classed div in html and apply the same style name in the LaTeX code.

So, for your example, it would look like this:

::: {.note data-latex=""}
This is a note.
:::

The output in HTML will be identical to yours:

<div class="note">
<p>This is a note.</p>
</div>

And you've already got the CSS you want to style that.

The LaTeX code will be as follows:

\begin{note}
This is a note.
\end{note}

To style that you'll need to add some code to your preamble.tex file, which you've already figured out as well. Here's a very simple example of some LaTeX that would simply indent the text from both the left and right sides:

\newenvironment{note}[0]{\par\leftskip=2em\rightskip=2em}{\par\medskip}

Upvotes: 1

Thomas
Thomas

Reputation: 10055

I found this answer on tex.stackexchange.com which brought me on the right track to solve my problem.


Here is what I am doing.

  1. Create boxes.lua with following function:
function Div(element)
-- function based on https://tex.stackexchange.com/a/526036

    if 
        element.classes[1] == "note" 
        or element.classes[1] == "side-note" 
        or element.classes[1] == "warning" 
        or element.classes[1] == "info" 
        or element.classes[1] == "reading" 
        or element.classes[1] == "exercise" 
    then

        -- get latex environment name from class name
        div = element.classes[1]:gsub("-", " ")
        div = div:gsub("(%l)(%w*)", function(a, b) return string.upper(a)..b end)
        div = "Div"..div:gsub(" ", "")
        
        -- insert element in front
        table.insert(
            element.content, 1,
            pandoc.RawBlock("latex", "\\begin{"..div.."}"))

        -- insert element at the back
        table.insert(
            element.content,
            pandoc.RawBlock("latex", "\\end{"..div.."}"))

    end
    return element
end
  1. Add pandoc_args to _output.yml:
bookdown::pdf_book:
  includes:
    in_header: latex/preamble.tex
  pandoc_args: 
    - --lua-filter=latex/boxes.lua 
  extra_dependencies: ["float"]
  1. Create environments in preamble.tex (which is also configured in _output.yml):
    • I am using tcolorbox instead of mdframed
\usepackage{xcolor}
\usepackage{tcolorbox}

\definecolor{notecolor}{RGB}{253, 196, 0}
\definecolor{warncolor}{RGB}{253, 70, 0}
\definecolor{infocolor}{RGB}{0, 183, 253}
\definecolor{readcolor}{RGB}{106, 50, 253}
\definecolor{taskcolor}{RGB}{128, 252, 219}

\newtcolorbox{DivNote}{colback=notecolor!5!white,colframe=notecolor!75!black}
\newtcolorbox{DivSideNote}{colback=notecolor!5!white,colframe=notecolor!75!black}
\newtcolorbox{DivWarning}{colback=warncolor!5!white,colframe=warncolor!75!black}
\newtcolorbox{DivInfo}{colback=infocolor!5!white,colframe=infocolor!75!black}
\newtcolorbox{DivReading}{colback=readcolor!5!white,colframe=readcolor!75!black}
\newtcolorbox{DivExercise}{colback=taskcolor!5!white,colframe=taskcolor!75!black}
  1. Because I have also images and tables within the boxes, I run into LaTeX Error: Not in outer par mode.. I was able to solve this issue by adding following command to my Rmd file:
```{r, echo = F}
knitr::opts_chunk$set(fig.pos = "H", out.extra = "")
```

Upvotes: 2

Related Questions