1
votes

I am creating an R package which is relying on Bookdown to create PDF books out of .Rmd files. I want to use a custom latex template. So, according to Bookdown doc section on templates and Pandoc's doc on templates, I have created, in my project, the following structure:

myproj/
├── inst/
│   ├── examples/
│   |   ├── firstbook/
│   |   |   ├── index.Rmd
│   |   |   ├── ...
│   ├── rmarkdown/
│   |   ├── templates/
│   |   |   ├── book_tex/
│   |   |   |   ├── resources/
│   |   |   |   |   ├── template.tex
│   |   |   |   ├── skeleton/
│   |   |   |   |   ├── monograph.cls
│   |   |   |   |   ├── bibliography.bib
│   |   |   |   |   ├── skeleton.Rmd
│   |   |   |   ├── template.yaml
├── R/
│   ├── format.R
│   └── render.R
└── DESCRIPTION

Template artifacts

The new template is called book_tex, I have created a folder with this name under inst/rmarkdown/templates.

template.tex is:

% !TeX program = pdfLaTeX
\documentclass{monograph}

\usepackage{hyperref}
\usepackage{newtxmath}       % Times Roman as basic font

\makeindex

\begin{document}

\author{ $for(authors)$ $authors.name$ \and $endfor$ }
\title{$title$}
$if(subtitle)$
    \subtitle{$subtitle$}
$endif$

\maketitle
\tableofcontents

$body$

\printindex

\end{document}

template.yaml is:

name: PDF Book
description: >
 Template for creating a TEX book
create_dir: true

skeleton.Rmd is:

---
title: Title here
subtitle: Do you have a subtitle? If so, write it here

thanks: | 
    Grants or other notes about the article that should go on the front 
    page should be placed here. General acknowledgments should be placed at the
    end of the article.
authors: 
- name: Author 1
  address: Department of YYY, University of XXX
  email: abc@def

- name: Author 2
  address: Department of ZZZ, University of WWW
  email: djf@wef

keywords:
- key
- dictionary
- word  

abstract: |
  The text of your abstract.  150 -- 250 words.

output: myprojpkg::book_tex
---

# Introduction {#intro}

Your text comes here. Separate text sections with

# Section title {#sec:1}

Some text.

## Subsection title {#sec:2}

Some other text.

Building

I have created a custom format which basically sets my custom template in file format.R:

book_tex_format <- function(...) {
  rmarkdown::pdf_document(..., template = "book_tex")
}

And then, in render.R, I invoke Bookdown from inside my package's example dir (working directory is myproj/inst/examples/firstbook) in RStudio:

bookdown::render_book(".", book_tex_format())

Which gives me error:

"C:/PROGRA~1/Pandoc/pandoc" +RTS -K512m -RTS _index_merged.utf8.md --to latex --from markdown+autolink_bare_uris+tex_math_single_backslash --output _index_merged.tex --self-contained --template book_tex --highlight-style tango --pdf-engine pdflatex --lua-filter "C:/Users/me/Documents/R/win-library/3.5/rmarkdown/rmd/lua/pagebreak.lua" --lua-filter "C:/Users/me/Documents/R/win-library/3.5/rmarkdown/rmd/lua/latex-div.lua" Could not find data file templates\book_tex.latex

I have then tried:

bookdown::render_book(".", rmarkdown::pdf_document(template="../../rmarkdown/templates/book_tex/resources/template.tex"))

Which seemed to work but then gave me error:

"C:/PROGRA~1/Pandoc/pandoc" +RTS -K512m -RTS _index_merged.utf8.md --to latex --from markdown+autolink_bare_uris+tex_math_single_backslash --output _index_merged.tex --self-contained --template "....\rmarkdown\templates\book_tex\resources\template.tex" --highlight-style tango --pdf-engine pdflatex --lua-filter "C:/Users/me/Documents/R/win-library/3.5/rmarkdown/rmd/lua/pagebreak.lua" --lua-filter "C:/Users/me/Documents/R/win-library/3.5/rmarkdown/rmd/lua/latex-div.lua" ! LaTeX Error: File `monograph.cls' not found.

I have mimicked the structure by looking at how RMarkdown and Bookdown are doing stuff. But it seems like it is not good enough. I am very frustrated, this part of the API is poorly documented and I don't get how I am supposed to make it work.

How should I invoke render_book in order to pass a template?

bookdown::render_book(".", !!HERE!!)

Another attempt

I have come across function rmarkdown::draft which seems to be the recommended way of using a template. So I did invoke (working directory: myproj/inst/examples):

rmarkdown::draft("mybook2", "book_tex", "myprojpkg", TRUE)

Note that my package is called myprojpkg (in DESCRIPTION file). This command works fine and actusally emits this folder:

myproj/
├── inst/
│   ├── examples/
│   |   ├── firstbook/
│   |   ├── mybook2/
│   |   |   |   ├── monograph.cls
│   |   |   |   ├── bibliography.bib
│   |   |   |   ├── mybook.Rmd
│   ├── rmarkdown/
│   |   ├── templates/
│   |   |   ├── book_tex/
│   |   |   |   ├── ...
├── R/
│   ├── ...
└── DESCRIPTION

Where mybook.Rmd is skeleton.Rmd renamed. So the command is correctly taking the template I defined in my package and creating the directory structure ready to be submitted to bookdown::render_book, however the problem is that calling it does not get me any error, but does not apply the correct template (template.tex inside book_tex). The command here relies on the content of skeleton.Rmd where it specifies the template to use in the yaml header, but that template is not found somehow and not applied :(


(friendly) Feedback on Bookdown documentation

I have to say that this part of custom templates needs better documenting because it is not immediate how a developer is supposed to structure the project to make things work with custom templates.

EDIT I have actually found some good documentation about these topics on the rmarkdown::draft function. My feedback would be to make it more visible in the help pages in the sections about templates. Now this chunk (quite important chunk) of doc is basically hidden.

1
The structure you set up is meant for a package containing your template. A more informative description can be found here bookdown.org/yihui/rmarkdown/document-templates.html and bookdown.org/yihui/rmarkdown/new-formats.html. But you are right. There is still room for improvement concerning the docs.(; - stefan
@stefan Thanks for your comment. I can see that basically I need to invoke rmarkdown::draft to deploy the template! Is that right? - Andry
Nop. First you have to put your template in a package. Then can you choose your template via rmarkdown::draft("my_article.Rmd", template = "NAME_OF_MY_TEMPLATE", package = "MY_TEMPLATE_PACKAGE"). - stefan
@stefan Can't I simply put the templates in the same package where I want to use them and invoke: rmarkdown::draft("my_article.Rmd", template = "NAME_OF_MY_TEMPLATE", package = ".")? - Andry
Really don't know whether this will work. In my case I simply followed the docs and set up a package with a html and a word template for our company reports. Works fine besides setting up a word template is a real pain. (; - stefan

1 Answers

1
votes

After analyzing the code and playing a little bit with Bookdown and Rmarkdown, and as it also shown in some comments I received (thanks @stefan), I came to the conclusion that the template, and all its resources, need to be in the same folder where your .Rmd files are. This is actually reflected by the presence of function draft in Rmarkdown which accomplishes this target. So my folder structure would eventually become:

myproj/
├── monograph.cls
├── bibliography.bib
├── template.tex
├── mybook.Rmd
├── index.Rmd
├── _bookdown.yaml

From here, I would open RStudio and change folder to myproj, or open an R shell and change working directory to myproj by issuing: setwd("path/to/myproj"). I would then invoke command:

bookdown::render_book("index.Rmd", bookdown::pdf_book(template = "template.tex"))

This will start the machine and get your PDF created.


Warning: theorems and other environments

If your template imports amsthm or other environments that define blocks like theorem or similar, Bookdown will fail. Bookdown will automatically append definitions for theorems in the Latex template your provide (a copy of it generated in the intermediate folder). You need to apply this fix to make things work (in your index.Rmd):

---
title: My book
author: Me
---

```{r, echo=FALSE}
options(bookdown.post.latex = function(x) {
  from <- grep("usepackage\\{amsthm\\}", x)
  to <- grep("newtheorem\\*\\{solution", x)
  x <- x[-c(from:to)]
})
```