1#' Convert to a Beamer presentation
2#'
3#' Format for converting from R Markdown to a Beamer presentation.
4#'
5#' @inheritParams output_format
6#' @inheritParams pdf_document
7#' @inheritParams html_document
8#'
9#' @param toc \code{TRUE} to include a table of contents in the output (only
10#'   level 1 headers will be included in the table of contents).
11#' @param slide_level The heading level which defines individual slides. By
12#'   default this is the highest header level in the hierarchy that is followed
13#'   immediately by content, and not another header, somewhere in the document.
14#'   This default can be overridden by specifying an explicit
15#'   \code{slide_level}.
16#' @param incremental \code{TRUE} to render slide bullets incrementally. Note
17#'   that if you want to reverse the default incremental behavior for an
18#'   individual bullet you can precede it with \code{>}. For example:
19#'   \emph{\code{> - Bullet Text}}
20#' @param theme Beamer theme (e.g. "AnnArbor").
21#' @param colortheme Beamer color theme (e.g. "dolphin").
22#' @param fonttheme Beamer font theme (e.g. "structurebold").
23#' @param self_contained Whether to generate a full LaTeX document (\code{TRUE})
24#'   or just the body of a LaTeX document (\code{FALSE}). Note the LaTeX
25#'   document is an intermediate file unless \code{keep_tex = TRUE}.
26#'
27#' @return R Markdown output format to pass to \code{\link{render}}
28#'
29#' @details
30#'
31#' See the
32#' \href{http://rmarkdown.rstudio.com/beamer_presentation_format.html}{online
33#' documentation} for additional details on using the \code{beamer_presentation}
34#' format.
35#'
36#' Creating Beamer output from R Markdown requires that LaTeX be installed.
37#'
38#' R Markdown documents can have optional metadata that is used to generate a
39#' document header that includes the title, author, and date. For more details
40#' see the documentation on R Markdown \link[=rmd_metadata]{metadata}.
41#'
42#' R Markdown documents also support citations. You can find more information on
43#' the markdown syntax for citations in the
44#' \href{http://rmarkdown.rstudio.com/authoring_bibliographies_and_citations.html}{Bibliographies
45#' and Citations} article in the online documentation.
46#'
47#' @examples
48#' \dontrun{
49#'
50#' library(rmarkdown)
51#'
52#' # simple invocation
53#' render("pres.Rmd", beamer_presentation())
54#'
55#' # specify an option for incremental rendering
56#' render("pres.Rmd", beamer_presentation(incremental = TRUE))
57#' }
58#'
59#' @export
60beamer_presentation <- function(toc = FALSE,
61                                slide_level = NULL,
62                                incremental = FALSE,
63                                fig_width = 10,
64                                fig_height = 7,
65                                fig_crop = TRUE,
66                                fig_caption = TRUE,
67                                dev = 'pdf',
68                                df_print = "default",
69                                theme = "default",
70                                colortheme = "default",
71                                fonttheme = "default",
72                                highlight = "default",
73                                template = "default",
74                                keep_tex = FALSE,
75                                latex_engine = "pdflatex",
76                                citation_package = c("none", "natbib", "biblatex"),
77                                self_contained = TRUE,
78                                includes = NULL,
79                                md_extensions = NULL,
80                                pandoc_args = NULL) {
81
82  # base pandoc options for all beamer output
83  args <- c()
84
85  # template path and assets
86  if (!is.null(template)) {
87    if (identical(template, "default")) template <- patch_beamer_template()
88    if (!is.null(template))
89      args <- c(args, "--template", pandoc_path_arg(template))
90  }
91
92  # table of contents
93  if (toc)
94    args <- c(args, "--table-of-contents")
95
96  # slide level
97  if (!is.null(slide_level))
98    args <- c(args, "--slide-level", as.character(slide_level))
99
100  # incremental
101  if (incremental)
102    args <- c(args, "--incremental")
103
104  # themes
105  if (!identical(theme, "default"))
106    args <- c(args, pandoc_variable_arg("theme", theme))
107  if (!identical(colortheme, "default"))
108    args <- c(args, pandoc_variable_arg("colortheme", colortheme))
109  if (!identical(fonttheme, "default"))
110    args <- c(args, pandoc_variable_arg("fonttheme", fonttheme))
111
112  # highlighting
113  if (!is.null(highlight))
114    highlight <- match.arg(highlight, highlighters())
115  args <- c(args, pandoc_highlight_args(highlight))
116
117  # latex engine
118  latex_engine = match.arg(latex_engine, c("pdflatex", "lualatex", "xelatex"))
119  args <- c(args, pandoc_latex_engine_args(latex_engine))
120
121  # citation package
122  citation_package <- match.arg(citation_package)
123  if (citation_package != "none") args <- c(args, paste0("--", citation_package))
124
125  # generate a self-contained LaTeX document (including preamble)
126  if (self_contained) args <- c(args, "--self-contained")
127
128  # content includes
129  args <- c(args, includes_to_pandoc_args(includes))
130
131  # make sure the graphics package is always loaded
132  if (identical(template, "default")) args <- c(args, "--variable", "graphics=yes")
133
134  # custom args
135  args <- c(args, pandoc_args)
136
137  # initialize saved files dir
138  saved_files_dir <- NULL
139
140  pre_processor <- function(metadata, input_file, runtime, knit_meta,
141                                files_dir, output_dir) {
142    # save files dir (for generating intermediates)
143    saved_files_dir <<- files_dir
144
145    # no-op other than caching dir location
146    invisible(NULL)
147  }
148
149  # generate intermediates (required to make resources available for publish)
150  intermediates_generator <- function(original_input, encoding,
151                                      intermediates_dir) {
152    return(pdf_intermediates_generator(saved_files_dir, original_input,
153                                        encoding, intermediates_dir))
154  }
155
156  # return format
157  output_format(
158    knitr = knitr_options_pdf(fig_width, fig_height, fig_crop, dev),
159    pandoc = pandoc_options(to = "beamer",
160                            from = from_rmarkdown(fig_caption, md_extensions),
161                            args = args,
162                            latex_engine = latex_engine,
163                            keep_tex = keep_tex),
164    pre_processor = pre_processor,
165    intermediates_generator = intermediates_generator,
166    clean_supporting = !keep_tex,
167    df_print = df_print
168  )
169}
170
171
172patch_beamer_template_pagenumber <- function(template) {
173
174  patch <- paste(
175    "% Comment these out if you don't want a slide with just the",
176    "% part/section/subsection/subsubsection title:", "\\AtBeginPart{",
177    "  \\let\\insertpartnumber\\relax", "  \\let\\partname\\relax",
178    "  \\frame{\\partpage}", "}", "\\AtBeginSection{",
179    "  \\let\\insertsectionnumber\\relax", "  \\let\\sectionname\\relax",
180    "  \\frame{\\sectionpage}", "}", "\\AtBeginSubsection{",
181    "  \\let\\insertsubsectionnumber\\relax", "  \\let\\subsectionname\\relax",
182    "  \\frame{\\subsectionpage}", "}",
183    sep = "\n"
184  )
185
186  pasted <- paste(template, collapse = "\n")
187  patched <- sub(patch, "", pasted, fixed = TRUE)
188  strsplit(patched, "\n", fixed = TRUE)[[1]]
189}
190
191patch_beamer_template_paragraph_spacing <- function(template) {
192
193  patch <- c(
194    "\\setlength{\\parindent}{0pt}",
195    "\\setlength{\\parskip}{6pt plus 2pt minus 1pt}"
196  )
197
198  lines <- unlist(lapply(patch, function(line) {
199    index <- grep(line, template, fixed = TRUE)
200    if (length(index) == 1) index else -1
201  }))
202
203  # bail if we already have these lines in the document
204  if (all(lines >= 0) && lines[[1]] == lines[[2]] - 1)
205    return(template)
206
207  # find patch location -- we insert before this line
208  targetLine <- "\\setlength{\\emergencystretch}{3em}  % prevent overfull lines"
209  targetIdx <- grep(targetLine, template, fixed = TRUE)
210  if (!length(targetIdx))
211    return(template)
212
213  # insert patch
214  c(
215    utils::head(template, n = targetIdx - 1),
216    patch,
217    utils::tail(template, n = -(targetIdx - 1))
218  )
219}
220
221patch_beamer_template <- function() {
222  pandoc_available(error = TRUE)
223
224  # invoke pandoc to read default template
225  command <- paste(quoted(pandoc()), "-D beamer")
226  template <- with_pandoc_safe_environment({
227    tryCatch(
228      system(command, intern = TRUE),
229      error = function(e) NULL
230    )
231  })
232
233  # make failure to read template non-fatal
234  if (is.null(template))
235    return(NULL)
236
237  # trim whitespace
238  template <- gsub("^\\s+|\\s+$", "", template, perl = TRUE)
239
240  # apply patches (store original version of template so we can
241  # compare after applying patches)
242  original <- template
243  version <- pandoc_version()
244
245  if (version < "1.15.2")
246    template <- patch_beamer_template_pagenumber(template)
247
248  if (version > "1.15.2" && version < "1.17.3")
249    template <- patch_beamer_template_paragraph_spacing(template)
250
251  # if the template hasn't changed, return NULL (we don't need
252  # to apply a custom template)
253  if (identical(template, original))
254    return(NULL)
255
256  # write and return path to template
257  as_tmpfile(enc2utf8(template))
258}
259