1#' Convert a document with pandoc
2#'
3#' Convert documents to and from various formats using the pandoc utility.
4#'
5#' Supported input and output formats are described in the
6#' \href{https://pandoc.org/MANUAL.html}{pandoc user guide}.
7#'
8#' The system path as well as the version of pandoc shipped with RStudio (if
9#' running under RStudio) are scanned for pandoc and the highest version
10#' available is used.
11#' @param input Character vector containing paths to input files
12#'   (files must be UTF-8 encoded)
13#' @param to Format to convert to (if not specified, you must specify
14#'   \code{output})
15#' @param from Format to convert from (if not specified then the format is
16#'   determined based on the file extension of \code{input}).
17#' @param output Output file (if not specified then determined based on format
18#'   being converted to).
19#' @param citeproc \code{TRUE} to run the pandoc-citeproc filter (for processing
20#'   citations) as part of the conversion.
21#' @param options Character vector of command line options to pass to pandoc.
22#' @param verbose \code{TRUE} to show the pandoc command line which was executed
23#' @param wd Working directory in which code will be executed. If not
24#'   supplied, defaults to the common base directory of \code{input}.
25#' @examples
26#' \dontrun{
27#' library(rmarkdown)
28#'
29#' # convert markdown to various formats
30#' pandoc_convert("input.md", to = "html")
31#' pandoc_convert("input.md", to = "latex")
32#'
33#' # process citations
34#' pandoc_convert("input.md", to = "html", citeproc = TRUE)
35#'
36#' # add some pandoc options
37#' pandoc_convert("input.md", to = "latex", options = c("--listings"))
38#' }
39#' @export
40pandoc_convert <- function(input,
41                           to = NULL,
42                           from = NULL,
43                           output = NULL,
44                           citeproc = FALSE,
45                           options = NULL,
46                           verbose = FALSE,
47                           wd = NULL) {
48
49  # ensure we've scanned for pandoc
50  find_pandoc()
51
52  # evaluate path arguments before changing working directory
53  force(output)
54
55  # execute in specified working directory
56  if (is.null(wd)) {
57    wd <- base_dir(input)
58  }
59
60  oldwd <- setwd(wd)
61  on.exit(setwd(oldwd), add = TRUE)
62
63  # input file and formats
64  args <- c(input)
65  if (!is.null(to)) {
66    if (to == 'html') to <- 'html4'
67    if (to == 'pdf') to <- 'latex'
68    args <- c(args, "--to", to)
69  }
70  if (!is.null(from))
71    args <- c(args, "--from", from)
72
73  # output file
74  if (!is.null(output))
75    args <- c(args, "--output", output)
76
77  # set pandoc stack size
78  args <- prepend_pandoc_stack_size(args)
79
80  # additional command line options
81  args <- c(args, options)
82
83  # citeproc filter if requested
84  if (citeproc) {
85    # --natbib/--biblatex conflicts with pandoc own citation processing,
86    # '--filter pandoc-citeproc' or '--citeproc'
87    args <- c(args[!args %in% c("--natbib", "--biblatex")],
88              pandoc_citeproc_args())
89  }
90
91  # build the conversion command
92  command <- paste(quoted(pandoc()), paste(quoted(args), collapse = " "))
93
94  # show it in verbose mode
95  if (verbose)
96    cat(command, "\n")
97
98  # run the conversion
99  with_pandoc_safe_environment({
100    result <- system(command)
101  })
102  if (result != 0)
103    stop2("pandoc document conversion failed with error ", result)
104
105  invisible(NULL)
106}
107
108
109#' Convert a bibliograpy file
110#'
111#' Convert a bibliography file (e.g. a BibTeX file) to an R list, JSON text,
112#'   or YAML text
113#'
114#' @param file Bibliography file
115#' @param type Conversion type
116#'
117#' @return For `type = "list"`, and R list. For `type = "json"` or `type = "yaml"`,
118#'   a character vector with the specified format.
119#'
120#' @export
121pandoc_citeproc_convert <- function(file, type = c("list", "json", "yaml")) {
122
123  # ensure we've scanned for pandoc
124  find_pandoc()
125
126  # resolve type
127  type <- match.arg(type)
128
129  if (pandoc_available("2.11")) {
130    bin <- pandoc()
131    to <- switch(type,
132                 list = "csljson",
133                 json = "csljson",
134                 yaml = "markdown"
135    )
136    args <- c(file, "-s", "-t", to)
137  } else {
138    bin <- pandoc_citeproc()
139    conversion <- switch(type,
140                         list = "--bib2json",
141                         json = "--bib2json",
142                         yaml = "--bib2yaml"
143    )
144    args <- c(conversion, file)
145  }
146
147  # build the conversion command
148  command <- paste(quoted(bin), paste(quoted(args), collapse = " "))
149
150  # run the conversion
151  with_pandoc_safe_environment({
152    result <- system(command, intern = TRUE)
153  })
154  status <- attr(result, "status")
155  if (!is.null(status)) {
156    cat(result, sep = "\n")
157    stop("Error ", status, " occurred building shared library.")
158  }
159
160  Encoding(result) <- "UTF-8"
161
162  # convert the output if requested
163  if (type == "list") {
164    jsonlite::fromJSON(result, simplifyVector = FALSE)
165  } else {
166    result
167  }
168}
169
170#' Check pandoc availability and version
171#'
172#' Determine whether pandoc is currently available on the system (optionally
173#' checking for a specific version or greater). Determine the specific version
174#' of pandoc available.
175#'
176#' The system environment variable \samp{PATH} as well as the version of pandoc
177#' shipped with RStudio (its location is set via the environment variable
178#' \samp{RSTUDIO_PANDOC} by RStudio products like the RStudio IDE, RStudio
179#' Server, Shiny Server, and RStudio Connect, etc) are scanned for pandoc and
180#' the highest version available is used. Please do not modify the environment
181#' variable \samp{RSTUDIO_PANDOC} unless you know what it means.
182#' @param version Required version of pandoc
183#' @param error Whether to signal an error if pandoc with the required version
184#'   is not found
185#' @return \code{pandoc_available} returns a logical indicating whether the
186#'   required version of pandoc is available. \code{pandoc_version} returns a
187#'   \code{\link[base]{numeric_version}} with the version of pandoc found.
188#' @examples
189#' \dontrun{
190#' library(rmarkdown)
191#'
192#' if (pandoc_available())
193#'   cat("pandoc", as.character(pandoc_version()), "is available!\n")
194#'
195#' if (pandoc_available("1.12.3"))
196#'   cat("required version of pandoc is available!\n")
197#' }
198#' @export
199pandoc_available <- function(version = NULL,
200                             error = FALSE) {
201
202  # ensure we've scanned for pandoc
203  find_pandoc()
204
205  # check availability
206  found <- !is.null(.pandoc$dir) && (is.null(version) || .pandoc$version >= version)
207
208  msg <- c(
209    "pandoc", if (!is.null(version)) c("version", version, "or higher"),
210    "is required and was not found (see the help page ?rmarkdown::pandoc_available)."
211  )
212  if (error && !found) stop2(paste(msg, collapse = " "))
213
214  found
215}
216
217
218#' @rdname pandoc_available
219#' @export
220pandoc_version <- function() {
221  find_pandoc()
222  .pandoc$version
223}
224
225#' Functions for generating pandoc command line arguments
226#'
227#' Functions that assist in creating various types of pandoc command line
228#' arguments (e.g. for templates, table of contents, highlighting, and content
229#' includes).
230#'
231#' Non-absolute paths for resources referenced from the
232#' \code{in_header}, \code{before_body}, and \code{after_body}
233#' parameters are resolved relative to the directory of the input document.
234#' @inheritParams includes
235#' @param name Name of template variable to set.
236#' @param value Value of template variable (defaults to \code{true} if missing).
237#' @param toc \code{TRUE} to include a table of contents in the output.
238#' @param toc_depth Depth of headers to include in table of contents.
239#' @param highlight The name of a pandoc syntax highlighting theme.
240#' @param latex_engine LaTeX engine for producing PDF output. Options are
241#'   "pdflatex", "lualatex", "xelatex", and "tectonic".
242#' @param default The highlighting theme to use if "default"
243#'   is specified.
244#' @return A character vector with pandoc command line arguments.
245#' @examples
246#' \dontrun{
247#' library(rmarkdown)
248#'
249#' pandoc_include_args(before_body = "header.htm")
250#' pandoc_include_args(before_body = "header.tex")
251#'
252#' pandoc_highlight_args("kate")
253#'
254#' pandoc_latex_engine_args("pdflatex")
255#'
256#' pandoc_toc_args(toc = TRUE, toc_depth = 2)
257#' }
258#' @name pandoc_args
259NULL
260
261#' @rdname pandoc_args
262#' @export
263pandoc_variable_arg <- function(name,
264                                value) {
265
266  c("--variable", if (missing(value)) name else paste(name, "=", value, sep = ""))
267}
268
269#' @rdname pandoc_args
270#' @export
271pandoc_metadata_arg <- function(name,
272                                value) {
273
274  c("--metadata", if (missing(value)) name else paste(name, "=", value, sep = ""))
275}
276
277
278#' @rdname pandoc_args
279#' @export
280pandoc_include_args <- function(in_header = NULL,
281                                before_body = NULL,
282                                after_body = NULL) {
283  args <- c()
284
285  for (file in in_header)
286    args <- c(args, "--include-in-header", pandoc_path_arg(file))
287
288  for (file in before_body)
289    args <- c(args, "--include-before-body", pandoc_path_arg(file))
290
291  for (file in after_body)
292    args <- c(args, "--include-after-body", pandoc_path_arg(file))
293
294  args
295}
296
297#' @rdname pandoc_args
298#' @export
299pandoc_highlight_args <- function(highlight,
300                                  default = "tango") {
301
302  args <- c()
303
304  if (is.null(highlight))
305    args <- c(args, "--no-highlight")
306  else {
307    if (identical(highlight, "default"))
308      highlight <- default
309    args <- c(args, "--highlight-style", highlight)
310  }
311
312  args
313}
314
315#' @rdname pandoc_args
316#' @export
317pandoc_latex_engine_args <- function(latex_engine) {
318
319  c(if (pandoc2.0()) "--pdf-engine" else "--latex-engine",
320    find_latex_engine(latex_engine))
321}
322
323# For macOS, use a full path to the latex engine since the stripping
324# of the PATH environment variable by OSX 10.10 Yosemite prevents
325# pandoc from finding the engine in e.g. /usr/texbin
326find_latex_engine <- function(latex_engine) {
327
328  # do not need full path if latex_engine is available from PATH
329  if (!is_osx() || nzchar(Sys.which(latex_engine))) return(latex_engine)
330  # resolve path if it's not already an absolute path
331  if (!grepl("/", latex_engine) && nzchar(path <- find_program(latex_engine)))
332    latex_engine <- path
333  latex_engine
334}
335
336#' @rdname pandoc_args
337#' @export
338pandoc_toc_args <- function(toc,
339                            toc_depth = 3) {
340
341  args <- c()
342
343  if (toc) {
344    args <- c(args, "--table-of-contents")
345    args <- c(args, "--toc-depth", toc_depth)
346  }
347
348  args
349}
350
351#' @section About Pandoc citeproc:
352#' For Pandoc version before 2.11, a pandoc filter \samp{pandoc-citeproc} is
353#' used. Since Pandoc 2.11, the feature is built-in and activated using
354#' \samp{--citeproc} flag. \samp{pandoc_citeproc_arg} will return the correct
355#' switches depending on the Pandoc version in use.
356#' @rdname pandoc_args
357#' @export
358pandoc_citeproc_args <- function() {
359  if (pandoc_available("2.11"))
360    "--citeproc"
361  else
362    c("--filter", pandoc_citeproc())
363}
364
365
366#' Transform path for passing to pandoc
367#'
368#' Transform a path for passing to pandoc on the command line. Calls
369#' \code{\link[base:path.expand]{path.expand}} on all platforms. On Windows,
370#' transform it to a short path name if it contains spaces, and then convert
371#' forward slashes to back slashes (as required by pandoc for some path
372#' references).
373#' @param path Path to transform
374#' @param backslash Whether to replace forward slashes in \code{path} with
375#'   backslashes on Windows.
376#' @return Transformed path that can be passed to pandoc on the command line.
377#' @export
378pandoc_path_arg <- function(path, backslash = TRUE) {
379
380  path <- path.expand(path)
381
382  # remove redundant ./ prefix if present
383  path <- sub('^[.]/', '', path)
384
385  if (is_windows()) {
386    i <- grep(' ', path)
387    if (length(i))
388      path[i] <- utils::shortPathName(path[i])
389    if (backslash) path <- gsub('/', '\\\\', path)
390  }
391
392  path
393}
394
395
396#' Render a pandoc template.
397#'
398#' Use the pandoc templating engine to render a text file. Substitutions are
399#' done using the \code{metadata} list passed to the function.
400#' @param metadata A named list containing metadata to pass to template.
401#' @param template Path to a pandoc template.
402#' @param output Path to save output.
403#' @param verbose \code{TRUE} to show the pandoc command line which was
404#'   executed.
405#' @return (Invisibly) The path of the generated file.
406#' @export
407pandoc_template <- function(metadata, template, output, verbose = FALSE) {
408
409  tmp <- tempfile(fileext = ".md")
410  on.exit(unlink(tmp))
411
412  cat("---\n", file = tmp)
413  cat(yaml::as.yaml(metadata), file = tmp, append = TRUE)
414  cat("---\n", file = tmp, append = TRUE)
415  cat("\n", file = tmp, append = TRUE)
416
417  pandoc_convert(tmp, "markdown", output = output,
418                 options = paste0("--template=", template),
419                 verbose = verbose)
420
421  invisible(output)
422}
423
424#' Create a self-contained HTML document using pandoc.
425#'
426#' Create a self-contained HTML document by base64 encoding images,
427#' scripts, and stylesheets referred by the input document.
428#' @param input Input html file to create self-contained version of.
429#' @param output Path to save output.
430#' @return (Invisibly) The path of the generated file.
431#' @export
432pandoc_self_contained_html <- function(input, output) {
433
434  # make input file path absolute
435  input <- normalizePath(input)
436
437  # ensure output file exists and make it's path absolute
438  if (!file.exists(output))
439    file.create(output)
440  output <- normalizePath(output)
441
442  # create a simple body-only template
443  template <- tempfile(fileext = ".html")
444  on.exit(unlink(template), add = TRUE)
445  write_utf8("$body$", template)
446
447  # convert from markdown to html to get base64 encoding
448  # (note there is no markdown in the source document but
449  # we still need to do this "conversion" to get the
450  # base64 encoding)
451
452  # determine from (there are bugs in pandoc < 1.17 that
453  # cause markdown_strict to hang on very large script
454  # elements)
455  from <- if (pandoc_available("1.17"))
456            "markdown_strict"
457          else
458            "markdown"
459
460  # do the conversion
461  pandoc_convert(
462    input = input,
463    from = from,
464    output = output,
465    options = c(
466      "--self-contained",
467      "--template", template
468    )
469  )
470
471  invisible(output)
472}
473
474
475validate_self_contained <- function(mathjax) {
476
477  if (identical(mathjax, "local"))
478    stop2("Local MathJax isn't compatible with self_contained\n",
479         "(you should set self_contained to FALSE)")
480}
481
482pandoc_mathjax_args <- function(mathjax,
483                                template,
484                                self_contained,
485                                files_dir,
486                                output_dir) {
487  args <- c()
488
489  if (!is.null(mathjax)) {
490
491    if (identical(mathjax, "default")) {
492      if (identical(template, "default"))
493        mathjax <- default_mathjax()
494      else
495        mathjax <- NULL
496    }
497    else if (identical(mathjax, "local")) {
498      mathjax_path <- pandoc_mathjax_local_path()
499      mathjax_path <- render_supporting_files(mathjax_path,
500                                              files_dir,
501                                              "mathjax-local")
502      mathjax <- paste(normalized_relative_to(output_dir, mathjax_path), "/",
503                       mathjax_config(), sep = "")
504    }
505
506    if (identical(template, "default")) {
507      args <- c(args, "--mathjax")
508      args <- c(args, "--variable", paste0("mathjax-url:", mathjax))
509    } else if (!self_contained) {
510      args <- c(args, paste(c("--mathjax", mathjax), collapse = "="))
511    } else {
512      warning("MathJax doesn't work with self_contained when not ",
513              "using the rmarkdown \"default\" template.", call. = FALSE)
514    }
515
516  }
517
518  args
519}
520
521
522pandoc_mathjax_local_path <- function() {
523
524  local_path <- Sys.getenv("RMARKDOWN_MATHJAX_PATH", unset = NA)
525  if (is.na(local_path)) {
526    local_path <- unix_mathjax_path()
527    if (is.na(local_path)) {
528      stop("For mathjax = \"local\", please set the RMARKDOWN_MATHJAX_PATH ",
529           "environment variable to the location of MathJax. ",
530           "On Linux systems you can also install MathJax using your ",
531           "system package manager.")
532    } else {
533      local_path
534    }
535  } else {
536    local_path
537  }
538}
539
540
541unix_mathjax_path <- function() {
542
543  if (identical(.Platform$OS.type, "unix")) {
544    mathjax_path <- "/usr/share/javascript/mathjax"
545    if (file.exists(file.path(mathjax_path, "MathJax.js")))
546      mathjax_path
547    else
548      NA
549  } else {
550    NA
551  }
552}
553
554
555pandoc_html_highlight_args <- function(template,
556                                       highlight) {
557
558  args <- c()
559
560  if (is.null(highlight)) {
561    args <- c(args, "--no-highlight")
562  }
563  else if (!identical(template, "default")) {
564    if (identical(highlight, "default"))
565      highlight <- "pygments"
566    args <- c(args, "--highlight-style", highlight)
567  }
568  else {
569    highlight <- match.arg(highlight, html_highlighters())
570    if (is_highlightjs(highlight)) {
571      args <- c(args, "--no-highlight")
572      args <- c(args, "--variable", "highlightjs=1")
573    }
574    else {
575      args <- c(args, "--highlight-style", highlight)
576    }
577  }
578
579  args
580}
581
582is_highlightjs <- function(highlight) {
583  !is.null(highlight) && (highlight %in% c("default", "textmate"))
584}
585
586#' Find the \command{pandoc} executable
587#'
588#' Searches for the \command{pandoc} executable in a few places and use the
589#' highest version found, unless a specific version is requested.
590#' @param cache Whether to search for \command{pandoc} again if a Pandoc
591#'   directory containing the \command{pandoc} executable of the expected
592#'   version (if provided) has been found previously. Search again if
593#'   \code{cache = FALSE}.
594#' @param dir A character vector of potential directory paths under which
595#'   \command{pandoc} may be found. If not provided, this function searches for
596#'   \command{pandoc} from the environment variable \var{RSTUDIO_PANDOC} (the
597#'   RStudio IDE will set this variable to the directory of Pandoc bundled with
598#'   the IDE), the environment variable \var{PATH}, and the directory
599#'   \file{~/opt/pandoc/}.
600#' @param version The version of Pandoc to look for (e.g., \code{"2.9.2.1"}). If
601#'   not provided, this function searches for the highest version under the
602#'   potential directories.
603#' @note Usually you do not need to install Pandoc if you use the RStudio IDE,
604#'   because the IDE has bundled a version of Pandoc. If you have installed a
605#'   version of Pandoc by yourself and want to use this version instead, you may
606#'   use the \code{dir} argument of this function.
607#' @return A list containing the directory and version of Pandoc (if found).
608#' @export
609#' @examples rmarkdown::find_pandoc()
610#' rmarkdown::find_pandoc(dir = '~/Downloads/Pandoc')
611#' rmarkdown::find_pandoc(version = '2.7.3')
612find_pandoc <- function(cache = TRUE, dir = NULL, version = NULL) {
613
614  if (!cache) set_pandoc_info(NULL)  # clear previously found pandoc path
615  if (!is.null(.pandoc$dir) && (is.null(version) || version == .pandoc$version))
616    return(as.list(.pandoc))
617
618  # look up pandoc in potential sources unless user has supplied `dir`
619  sources <- if (length(dir) == 0) c(
620    Sys.getenv("RSTUDIO_PANDOC"),
621    dirname(find_program("pandoc")),
622    "~/opt/pandoc"
623  ) else dir
624  sources <- path.expand(sources)
625
626  # determine the versions of the sources
627  versions <- lapply(sources, function(src) {
628    if (dir_exists(src)) get_pandoc_version(src) else numeric_version("0")
629  })
630
631  # find the maximum version
632  found_src <- NULL
633  found_ver <- numeric_version("0")
634  for (i in seq_along(sources)) {
635    ver <- versions[[i]]
636    if ((!is.null(version) && ver == version) || (is.null(version) && ver > found_ver)) {
637      found_ver <- ver
638      found_src <- sources[[i]]
639    }
640  }
641
642  set_pandoc_info(found_src, found_ver)
643  as.list(.pandoc)
644}
645
646# Get an S3 numeric_version for the pandoc utility at the specified path
647get_pandoc_version <- function(pandoc_dir) {
648  path <- file.path(pandoc_dir, "pandoc")
649  if (is_windows()) path <- paste0(path, ".exe")
650  if (!utils::file_test("-x", path)) return(numeric_version("0"))
651  info <- with_pandoc_safe_environment(
652    system(paste(shQuote(path), "--version"), intern = TRUE)
653  )
654  version <- strsplit(info, "\n")[[1]][1]
655  version <- strsplit(version, " ")[[1]][2]
656  numeric_version(version)
657}
658
659set_pandoc_info <- function(dir, version = if (!is.null(dir)) get_pandoc_version(dir)) {
660  .pandoc$dir <- dir
661  .pandoc$version <- version
662}
663
664# prepend pandoc stack size arguments
665prepend_pandoc_stack_size <- function(args) {
666  stack_size <- getOption("pandoc.stack.size", default = "512m")
667  c(c("+RTS", paste0("-K", stack_size), "-RTS"), args)
668}
669
670# wrap a system call to pandoc so that LC_ALL is not set
671# see: https://github.com/rstudio/rmarkdown/issues/31
672# see: https://ghc.haskell.org/trac/ghc/ticket/7344
673with_pandoc_safe_environment <- function(code) {
674
675  lc_all <- Sys.getenv("LC_ALL", unset = NA)
676
677  if (!is.na(lc_all)) {
678    Sys.unsetenv("LC_ALL")
679    on.exit(Sys.setenv(LC_ALL = lc_all), add = TRUE)
680  }
681
682  lc_ctype <- Sys.getenv("LC_CTYPE", unset = NA)
683
684  if (!is.na(lc_ctype)) {
685    Sys.unsetenv("LC_CTYPE")
686    on.exit(Sys.setenv(LC_CTYPE = lc_ctype), add = TRUE)
687  }
688
689  if (Sys.info()['sysname'] == "Linux" &&
690        is.na(Sys.getenv("HOME", unset = NA))) {
691    stop("The 'HOME' environment variable must be set before running Pandoc.")
692  }
693
694  if (Sys.info()['sysname'] == "Linux" &&
695        is.na(Sys.getenv("LANG", unset = NA))) {
696    # fill in a the LANG environment variable if it doesn't exist
697    Sys.setenv(LANG = detect_generic_lang())
698    on.exit(Sys.unsetenv("LANG"), add = TRUE)
699  }
700
701  if (Sys.info()['sysname'] == "Linux" &&
702    identical(Sys.getenv("LANG"), "en_US")) {
703    Sys.setenv(LANG = "en_US.UTF-8")
704    on.exit(Sys.setenv(LANG = "en_US"), add = TRUE)
705  }
706
707  force(code)
708}
709
710# if there is no LANG environment variable set pandoc is going to hang so
711# we need to specify a "generic" lang setting. With glibc >= 2.13 you can
712# specify C.UTF-8 so we prefer that. If we can't find that then we fall back
713# to en_US.UTF-8.
714detect_generic_lang <- function() {
715
716  locale_util <- Sys.which("locale")
717
718  if (nzchar(locale_util)) {
719    locales <- system(paste(locale_util, "-a"), intern = TRUE)
720    locales <- suppressWarnings(
721        strsplit(locales, split = "\n", fixed = TRUE)
722    )
723    if ("C.UTF-8" %in% locales)
724      return("C.UTF-8")
725  }
726
727  # default to en_US.UTF-8
728  "en_US.UTF-8"
729}
730
731
732# get the path to the pandoc binary
733pandoc <- function() {
734  find_pandoc()
735  file.path(.pandoc$dir, "pandoc")
736}
737
738
739# get the path to the pandoc-citeproc binary
740pandoc_citeproc <- function() {
741  find_pandoc()
742  bin <- "pandoc-citeproc"
743  p <- file.path(.pandoc$dir, bin)
744  if (xfun::is_windows()) p <- xfun::with_ext(p, "exe")
745  if (file.exists(p)) p else bin
746}
747
748#' @rdname pandoc_args
749#' @param lua_files Character vector of file paths to Lua filter files. Paths
750#'   will be transformed by \code{\link{pandoc_path_arg}}.
751#' @export
752pandoc_lua_filter_args <- function(lua_files) {
753  # Lua filters was introduced in pandoc 2.0
754  if (pandoc2.0()) c(rbind("--lua-filter", pandoc_path_arg(lua_files)))
755}
756
757
758# quote args if they need it
759quoted <- function(args) {
760
761  # some characters are legal in filenames but without quoting are likely to be
762  # interpreted by the shell (e.g. redirection, wildcard expansion, etc.) --
763  # wrap arguments containing these characters in quotes.
764  shell_chars <- grepl(.shell_chars_regex, args)
765  args[shell_chars] <- shQuote(args[shell_chars])
766  args
767}
768
769find_pandoc_theme_variable <- function(args) {
770
771  range <- length(args) - 1
772
773  for (i in 1:range) {
774    if (args[[i]] == "--variable" && grepl("^theme:", args[[i + 1]])) {
775      return(substring(args[[i + 1]], nchar("theme:") + 1))
776    }
777  }
778
779  # none found, return NULL
780  NULL
781}
782
783
784# Environment used to cache the current pandoc directory and version
785.pandoc <- new.env()
786.pandoc$dir <- NULL
787.pandoc$version <- NULL
788
789pandoc2.0 <- function() {
790  pandoc_available("2.0")
791}
792
793#' Get the path of the pandoc executable
794#'
795#' Returns the path of the pandoc executable used by functions in the the
796#' \pkg{rmarkdown} package. This is the most recent version of pandoc found in
797#' either the system path or shipped with RStudio.
798#'
799#' See the
800#' \href{https://pandoc.org/MANUAL.html}{pandoc manual}
801#' for pandoc commands.
802#'
803#' @export
804pandoc_exec <- pandoc
805