1#' Convert to a PDF/LaTeX document
2#'
3#' Formats for converting from R Markdown to a PDF or LaTeX document.
4#'
5#' See the \href{https://bookdown.org/yihui/rmarkdown/pdf-document.html}{online
6#' documentation} for additional details on using the \code{pdf_document}
7#' format.
8#'
9#' Creating PDF output from R Markdown requires that LaTeX be installed.
10#'
11#' R Markdown documents can have optional metadata that is used to generate a
12#' document header that includes the title, author, and date. For more details
13#' see the documentation on R Markdown \link[=rmd_metadata]{metadata}.
14#'
15#' R Markdown documents also support citations. You can find more information on
16#' the markdown syntax for citations in the
17#' \href{https://pandoc.org/MANUAL.html#citations}{Bibliographies
18#' and Citations} article in the online documentation.
19#'
20#' Many aspects of the LaTeX template used to create PDF documents can be
21#' customized using metadata. For example:
22#'
23#' \tabular{l}{
24#' \code{---} \cr
25#' \code{title: "Crop Analysis Q3 2013"} \cr
26#' \code{fontsize: 11pt} \cr
27#' \code{geometry: margin=1in} \cr
28#' \code{---}
29#' }
30#'
31#' Available metadata variables include:
32#'
33#' \describe{
34#'    \item{\code{lang}}{Document language code (e.g. "es", "fr", "pt-BR")}
35#'    \item{\code{fontsize}}{Font size (e.g. 10pt, 11pt, 12pt)}
36#'    \item{\code{documentclass}}{LaTeX document class (e.g. article)}
37#'    \item{\code{classoption}}{Option for \code{documentclass} (e.g. oneside); may be repeated}
38#'    \item{\code{geometry}}{Options for geometry class (e.g. margin=1in); may be repeated}
39#'    \item{\code{mainfont, sansfont, monofont, mathfont}}{Document fonts (works only with xelatex and lualatex, see the \code{latex_engine} option)}
40#'    \item{\code{linkcolor, urlcolor, citecolor}}{Color for internal, external, and citation links (red, green, magenta, cyan, blue, black)}
41#'    \item{\code{linestretch}}{Options for line spacing (e.g. 1, 1.5, 3)}
42#' }
43#' @inheritParams html_document
44#' @param fig_crop Whether to crop PDF figures with the command
45#'   \command{pdfcrop}. This requires the tools \command{pdfcrop} and
46#'   \command{ghostscript} to be installed. By default, \code{fig_crop = TRUE}
47#'   if these two tools are available.
48#' @param dev Graphics device to use for figure output (defaults to pdf)
49#' @param highlight Syntax highlighting style. Supported styles include
50#'   "default", "tango", "pygments", "kate", "monochrome", "espresso",
51#'   "zenburn", and "haddock". Pass \code{NULL} to prevent syntax highlighting.
52#' @param keep_tex Keep the intermediate tex file used in the conversion to PDF
53#' @param latex_engine LaTeX engine for producing PDF output. Options are
54#'   "pdflatex", "lualatex", "xelatex" and "tectonic".
55#' @param citation_package The LaTeX package to process citations, \code{natbib}
56#'   or \code{biblatex}. Use \code{default} if neither package is to be used,
57#'   which means citations will be processed via the command
58#'   \command{pandoc-citeproc}.
59#' @param template Pandoc template to use for rendering. Pass "default" to use
60#'   the rmarkdown package default template; pass \code{NULL} to use pandoc's
61#'   built-in template; pass a path to use a custom template that you've
62#'   created.  See the documentation on
63#'   \href{https://pandoc.org/MANUAL.html}{pandoc online documentation} for
64#'   details on creating custom templates.
65#' @param output_extensions Pandoc extensions to be added or removed from the
66#'   output format, e.g., \code{"-smart"} means the output format will be
67#'   \code{latex-smart}.
68#' @param extra_dependencies A LaTeX dependency \code{latex_dependency()}, a
69#'   list of LaTeX dependencies, a character vector of LaTeX package names (e.g.
70#'   \code{c("framed", "hyperref")}), or a named list of LaTeX package options
71#'   with the names being package names (e.g. \code{list(hyperef =
72#'   c("unicode=true", "breaklinks=true"), lmodern = NULL)}). It can be used to
73#'   add custom LaTeX packages to the .tex header.
74#' @return R Markdown output format to pass to \code{\link{render}}
75#' @examples
76#' \dontrun{
77#' library(rmarkdown)
78#'
79#' # simple invocation
80#' render("input.Rmd", pdf_document())
81#'
82#' # specify an option for latex engine
83#' render("input.Rmd", pdf_document(latex_engine = "lualatex"))
84#'
85#' # add a table of contents and pass an option to pandoc
86#' render("input.Rmd", pdf_document(toc = TRUE, "--listings"))
87#' }
88#' @export
89pdf_document <- function(toc = FALSE,
90                         toc_depth = 2,
91                         number_sections = FALSE,
92                         fig_width = 6.5,
93                         fig_height = 4.5,
94                         fig_crop = 'auto',
95                         fig_caption = TRUE,
96                         dev = 'pdf',
97                         df_print = "default",
98                         highlight = "default",
99                         template = "default",
100                         keep_tex = FALSE,
101                         keep_md = FALSE,
102                         latex_engine = "pdflatex",
103                         citation_package = c("default", "natbib", "biblatex"),
104                         includes = NULL,
105                         md_extensions = NULL,
106                         output_extensions = NULL,
107                         pandoc_args = NULL,
108                         extra_dependencies = NULL) {
109
110  # base pandoc options for all PDF output
111  args <- c("--self-contained")
112
113  # table of contents
114  args <- c(args, pandoc_toc_args(toc, toc_depth))
115
116  append_in_header <- function(text, file = as_tmpfile(text)) {
117    includes_to_pandoc_args(includes(in_header = file))
118  }
119
120  # template path and assets
121  if (!is.null(template) && !identical(template, "default")) {
122    args <- c(args, "--template", pandoc_path_arg(template))
123  }
124
125  # numbered sections
126  if (number_sections)
127    args <- c(args, "--number-sections")
128
129  # highlighting
130  if (!is.null(highlight))
131    highlight <- match.arg(highlight, highlighters())
132  args <- c(args, pandoc_highlight_args(highlight))
133
134  # latex engine
135  latex_engine <- match.arg(latex_engine, c("pdflatex", "lualatex", "xelatex", "tectonic"))
136  args <- c(args, pandoc_latex_engine_args(latex_engine))
137
138  # citation package
139  args <- c(args, citation_package_arg(citation_package))
140
141  # content includes
142  args <- c(args, includes_to_pandoc_args(includes))
143
144  # make sure the graphics package is always loaded
145  if (identical(template, "default")) args <- c(args, "--variable", "graphics")
146
147  # args args
148  args <- c(args, pandoc_args)
149
150  saved_files_dir <- NULL
151
152  # Use filter to set pdf geometry defaults (while making sure we don't override
153  # any geometry settings already specified by the user)
154  pdf_pre_processor <- function(metadata, input_file, runtime, knit_meta, files_dir,
155                                output_dir) {
156
157    # make sure --include-in-header from command line will not completely
158    # override header-includes in metadata but give the latter lower precedence:
159    # https://github.com/rstudio/rmarkdown/issues/1359
160    args <- append_in_header(process_header_includes(metadata))
161
162    # use a geometry filter when we are using the "default" template
163    if (identical(template, "default")) {
164      # set the margin to 1 inch if no geometry options or document class specified
165      if (default_geometry(names(metadata), pandoc_args))
166        args <- c(args, "--variable", "geometry:margin=1in")
167      # support subtitle for Pandoc < 2.6
168      if (("subtitle" %in% names(metadata)) && !pandoc_available("2.6")) args <- c(
169        args, append_in_header(file = pkg_file("rmd/latex/subtitle.tex"))
170      )
171    }
172
173    if (length(extra_dependencies) || has_latex_dependencies(knit_meta)) {
174      extra_dependencies <- latex_dependencies(extra_dependencies)
175      all_dependencies <- append(extra_dependencies, flatten_latex_dependencies(knit_meta))
176      args <- c(args, append_in_header(latex_dependencies_as_string(all_dependencies)))
177    }
178    args
179  }
180
181
182  pre_processor <- function(metadata, input_file, runtime, knit_meta,
183                                files_dir, output_dir) {
184    # save files dir (for generating intermediates)
185    saved_files_dir <<- files_dir
186
187    pdf_pre_processor(metadata, input_file, runtime, knit_meta, files_dir,
188                      output_dir)
189  }
190
191  intermediates_generator <- function(...) {
192    general_intermediates_generator(saved_files_dir, ...)
193  }
194
195  # return format
196  output_format(
197    knitr = knitr_options_pdf(fig_width, fig_height, fig_crop, dev),
198    pandoc = pandoc_options(
199      to = paste(c("latex", output_extensions), collapse = ""),
200      from = from_rmarkdown(fig_caption, md_extensions),
201      args = args,
202      latex_engine = latex_engine,
203      keep_tex = keep_tex,
204      lua_filters = pkg_file_lua(c("pagebreak.lua", "latex-div.lua"))
205    ),
206    clean_supporting = !keep_tex,
207    keep_md = keep_md,
208    df_print = df_print,
209    pre_processor = pre_processor,
210    intermediates_generator = intermediates_generator
211  )
212}
213
214general_intermediates_generator <- function(
215  saved_files_dir, original_input, intermediates_dir
216) {
217
218  # copy all intermediates (pandoc will need to bundle them in the PDF)
219  intermediates <- copy_render_intermediates(original_input, intermediates_dir, FALSE)
220
221  # we need figures from the supporting files dir to be available during
222  # render as well; if we have a files directory, copy its contents
223  if (!is.null(saved_files_dir) && dir_exists(saved_files_dir)) {
224    file.copy(saved_files_dir, intermediates_dir, recursive = TRUE)
225    intermediates <- c(intermediates, list.files(
226      path = file.path(intermediates_dir, basename(saved_files_dir)),
227      all.files = TRUE, recursive = TRUE, full.names = TRUE))
228  }
229
230  intermediates
231}
232
233patch_tex_output <- function(file) {
234  x <- read_utf8(file)
235  if (length(i <- which(x == '\\begin{document}')) == 0) return()
236  if (length(i <- grep('^\\\\date\\{', head(x, i[1]))) == 0) return()
237
238  i <- i[1]
239  # add \author{} if missing: https://github.com/jgm/pandoc/pull/5961
240  if (length(grep('^\\\\author\\{', head(x, i))) == 0) {
241    x <- append(x, '\\author{}', i - 1)
242    i <- i + 1
243  }
244  # reduce the vertical spacing in \date{} if no author is given
245  if (any(head(x, i) == '\\author{}')) {
246    x[i] <- paste0('\\date{\\vspace{-2.5em}', sub('^\\\\date\\{', '', x[i]))
247  }
248  write_utf8(x, file)
249}
250
251# patch output from Pandoc < 2.8: https://github.com/jgm/pandoc/issues/5801
252fix_horiz_rule <- function(file) {
253  if (pandoc_available('2.8')) return()
254  x <- read_utf8(file)
255  i <- x == '\\begin{center}\\rule{0.5\\linewidth}{\\linethickness}\\end{center}'
256  if (any(i)) {
257    x[i] <- '\\begin{center}\\rule{0.5\\linewidth}{0.5pt}\\end{center}'
258    write_utf8(x, file)
259  }
260}
261
262process_header_includes <- function(x) {
263  x <- unlist(x[["header-includes"]])
264  gsub('(^|\n)\\s*```\\{=latex\\}\n(.+?\n)```\\s*(\n|$)', '\\1\\2\\3', x)
265}
266
267citation_package_arg <- function(value) {
268  value <- value[1]
269  if (value == "none") {
270    warning("citation_package = 'none' was deprecated; please use 'default' instead.")
271    value <- "default"
272  }
273  value <- match.arg(value, c("default", "natbib", "biblatex"))
274  if (value != "default") paste0("--", value)
275}
276
277default_geometry <- function(meta_names, pandoc_args = NULL) {
278  !any(c('geometry', 'documentclass') %in% meta_names) &&
279    length(grep('^(--(variable|metadata)=)?documentclass:', pandoc_args)) == 0
280}
281
282#' @param ... Arguments passed to \code{pdf_document()}.
283#' @rdname pdf_document
284#' @export
285latex_document <- function(...) {
286  merge_lists(pdf_document(..., keep_tex = TRUE), list(pandoc = list(ext = ".tex")))
287}
288
289#' @rdname pdf_document
290#' @export
291latex_fragment <- function(...) {
292  latex_document(..., template = pkg_file("rmd/fragment/default.tex"))
293}
294