Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Custom highlighting style in rmarkdown

Is there a way to use a custom highlighting style in rmarkdown?

Manual is a bit silent regarding that and the closest to that is to make a full blown custom css file for everything, that would however work only for html_document and not for pdf_document (see https://bookdown.org/yihui/rmarkdown/html-document.html#appearance-and-style )

The newer versions of Pandoc support this: http://pandoc.org/MANUAL.html#syntax-highlighting

but when anything else but one of the default pandoc styles is specified, rmarkdown throws an error.

For example, when I download zenburn.css from highlight.js library, modify it and want to use it:

```
title: Some title
output:
    html_document:
        theme: readable
        highlight: zenburn.css
```

I get:

Error in match.arg(highlight, html_highlighters()) : 'arg' should be one of “default”, “tango”, “pygments”, “kate”, “monochrome”, “espresso”, “zenburn”, “haddock”, “textmate” Calls: ... -> pandoc_html_highlight_args -> match.arg Execution halted

like image 521
Colombo Avatar asked Jan 02 '23 13:01

Colombo


2 Answers

For posterity, since this took more time than it should:

The problem

Answers from @martin_schmelzer is correct (didn't test @tarleb's solution, since rmarkdown doesn't supposedly play well with Pandoc > 2.0 see: https://github.com/rstudio/rmarkdown/issues/1471 ). However, when you write echo=TRUE on chunk, the output is code is not tagged as R code and because of that different rules apply for it. In HTML it means that it has white background and with PDF it is formatted through verbatim environment only. For example, the markdown of following:

```{r, echo=TRUE}
foo = "bar"
foo
```

will be:

```r
foo = "bar"
foo
```

```
## [1] "foo"
```

And while the first chunk will be highlighted, the second will follow only in text color, but background will always be white. This is very problematic with darker themes, since they often have very light text color and this does not play nicely with white background. See: https://eranraviv.com/syntax-highlighting-style-in-rmarkdown/ for overview of rmarkdown highlighting styles.

HTML solution

This is made more complicated with switch between highlight.js and pandoc highlighting. If highlighting is not specified, highlight.js is used with associated tags. There the highlighting is done through external css and .js library, which are then (I presume) hashed into the HTML to make it stand-alone. So no luck there.

If some highlighting style is used however, then pandoc highlighting is used. I.e.,:

---
title = "Foo"
output:
  html_document:
    theme: readable
    highlight: zenburn
---

In this case, there IS solution. Looking at the HTML output, there is this structure:

<style typetext/css">
  pre:not([class]) {
    background-color: white;
  }
</style>

This means that whenever there is no style in specific code chunk (applies only to the "echo" chunks since by default rmarkdown assumes R), the background is white. This behaviour can be changed just by including following chunk in the .Rmd file:

```{css, echo = FALSE}
  pre:not([class]) {
    color: #333333;
    background-color: #cccccc;
  }
```

and their behaviour can be fully specified accordingly. Here the color and background as reverse of the zenburn style. The output looks like this:

Before: Zenburn before

After: Zenburn after

PDF solution

With PDF, it is a little bit easier to find the problem, but little bit more complicated to solve it. If one looks at the .tex file, you can see that while all the chunks with an actual code have a lot of going on around them, the echo chunks are wrapped only in a simple verbatim environment. The result looks like this:

Zenburn PDF before

While it is more readable than the HTML output, since it does not share the text color defined by highlighting style, it kind of blends into the text and creates and breaks the feeling of uniform style across outputs. The solution is, as mentioned in previous answer, to use:

---
title: "Foo"
output:
  pdf_document:
  highlight: zenburn
  includes:
    in_header: highlight_echo.tex
---

And a following construct that utilize package framed which is already included:

\usepackage{xcolor}
\definecolor{backgroundecho}{HTML}{cccccc}
\definecolor{textecho}{HTML}{333333}

\let\oldverbatim=\verbatim
\let\oldendverbatim=\endverbatim

\makeatletter
\renewenvironment{verbatim}{
    \def\FrameCommand{
        \hskip-\fboxsep
        \color{textecho}
        \colorbox{backgroundecho}
        }
    \MakeFramed{\@setminipage}
    \oldverbatim
}
{
    \oldendverbatim
    \vskip-2em\@minipagefalse % The size required for this negative space is probably in some variable
    \endMakeFramed
}
\makeatother

This redefine the verbatim environment to have colored background and colored text. The result is unified formatting of the "echo" chunks:

Zenburn PDF after

So thanks again @tarleb and @martin_schmelzer, I wouldn't be able to solve it without you!

like image 155
Colombo Avatar answered Jan 04 '23 01:01

Colombo


At least for HTML documents, you can simply include your customized styles using the css YAML option:

---
title: Some title
output:
    html_document:
        theme: readable
        css: zenburn.css
---

Concerning PDF documents, you could check the intermediate TeX file. There you will find a block of commands that look like

\newcommand{\CommentTok}[1]{\textcolor[rgb]{0.96,0.35,0.01}{\textit{#1}}}
\newcommand{\KeywordTok}[1]{\textcolor[rgb]{0.93,0.29,0.53}{\textbf{#1}}}

These are the lines that define the code highlighting. The first one for example defines the color for comments. You could write a header.tex in which you redefine these commands using \renewcommand

\renewcommand{\CommentTok}[1]{\textcolor[rgb]{0.56,0.35,0.01}{\textit{#1}}}
\renewcommand{\KeywordTok}[1]{\textcolor[rgb]{0.13,0.29,0.53}{\textbf{#1}}} 

and include it in your document right before the body.

Here is an example in which we alter the highlighting of comments and keywords within the body:

---
title: Some title
output: 
  pdf_document:
    keep_tex: true
---

```{r}
# This is a test
head(mtcars)
```
\renewcommand{\CommentTok}[1]{\textcolor[rgb]{0.96,0.35,0.01}{\textit{#1}}}
\renewcommand{\KeywordTok}[1]{\textcolor[rgb]{0.93,0.29,0.53}{\textbf{#1}}}
```{r}
# This is a test
head(mtcars)
```

enter image description here

like image 35
Martin Schmelzer Avatar answered Jan 04 '23 01:01

Martin Schmelzer