1#' The YAML metadata of the current R Markdown document
2#'
3#' The object \code{metadata} stores the YAML metadata of the current R Markdown
4#' document as a list, which you may use in the R code chunks, e.g.
5#' \code{rmarkdown::metadata$title} (the title of the document),
6#' \code{rmarkdown::metadata$author}, and \code{rmarkdown::metadata$foo} (if you
7#' have a YAML field named \code{foo}), etc.
8#' @usage NULL
9#' @examples rmarkdown::metadata
10#' @export
11metadata <- list()
12
13#' @export
14render <- function(input,
15                   output_format = NULL,
16                   output_file = NULL,
17                   output_dir = NULL,
18                   output_options = NULL,
19                   intermediates_dir = NULL,
20                   knit_root_dir = NULL,
21                   runtime =  c("auto", "static", "shiny", "shiny_prerendered"),
22                   clean = TRUE,
23                   params = NULL,
24                   knit_meta = NULL,
25                   envir = parent.frame(),
26                   run_pandoc = TRUE,
27                   quiet = FALSE,
28                   encoding = getOption("encoding")) {
29
30  perf_timer_start("render")
31
32  init_render_context()
33  on.exit(clear_render_context(), add = TRUE)
34
35  on.exit(clean_tmpfiles(), add = TRUE)
36
37  # check for "all" output formats
38  if (identical(output_format, "all")) {
39    output_format <- enumerate_output_formats(input, envir, encoding)
40    if (is.null(output_format))
41      output_format <- "html_document"
42  }
43
44  # check for a list of output formats -- if there is more than one
45  # then recursively call this function with each format by name
46  if (is.character(output_format) && length(output_format) > 1) {
47    outputs <- character()
48    for (format in output_format) {
49      # the output_file argument is intentionally ignored (we can't give
50      # the same name to each rendered output); copy the rest by name
51      output <- render(input = input,
52                       output_format = format,
53                       output_file = NULL,
54                       output_dir = output_dir,
55                       output_options = output_options,
56                       intermediates_dir = intermediates_dir,
57                       knit_root_dir = knit_root_dir,
58                       runtime = runtime,
59                       clean = clean,
60                       params = params,
61                       knit_meta = knit_meta,
62                       envir = envir,
63                       run_pandoc = run_pandoc,
64                       quiet = quiet,
65                       encoding = encoding)
66      outputs <- c(outputs, output)
67    }
68    if (length(output_file) > 1) {
69      file.rename(outputs, output_file)
70      outputs <- output_file
71    }
72    return(invisible(outputs))
73  }
74
75  # check for required version of pandoc if we are running pandoc
76  if (run_pandoc) {
77    required_pandoc <- "1.12.3"
78    pandoc_available(required_pandoc, error = TRUE)
79  }
80
81  # setup a cleanup function for intermediate files
82  intermediates <- c()
83  on.exit(if (clean) unlink(intermediates, recursive = TRUE), add = TRUE)
84
85  # ensure we have a directory to store intermediates
86  if (!is.null(intermediates_dir)) {
87    if (!dir_exists(intermediates_dir))
88      dir.create(intermediates_dir, recursive = TRUE)
89    intermediates_dir <- normalize_path(intermediates_dir)
90  }
91  intermediates_loc <- function(file) {
92    if (is.null(intermediates_dir))
93      file
94    else
95      file.path(intermediates_dir, file)
96  }
97
98  # resolve output directory before we change the working directory in
99  # preparation for rendering the document
100  if (!is.null(output_dir)) {
101    if (!dir_exists(output_dir))
102      dir.create(output_dir, recursive = TRUE)
103    output_dir <- normalize_path(output_dir)
104  }
105
106  # check whether this document requires a knit
107  requires_knit <- tolower(tools::file_ext(input)) %in% c("r", "rmd", "rmarkdown")
108
109  # remember the name of the original input document (we overwrite 'input' once
110  # we've knitted)
111  original_input <- normalize_path(input)
112
113  # if the input file has shell characters in its name then make a copy that
114  # doesn't have shell characters
115  if (grepl(.shell_chars_regex, basename(input))) {
116    # form the name of the file w/o shell characters
117    input_no_shell_chars <- intermediates_loc(
118        file_name_without_shell_chars(basename(input)))
119
120    if (file.exists(input_no_shell_chars)) {
121      stop("The name of the input file cannot contain the special shell ",
122           "characters: ", .shell_chars_regex, " (attempted to copy to a ",
123           "version without those characters '", input_no_shell_chars, "' ",
124           "however that file already exists)", call. = FALSE)
125    }
126    file.copy(input, input_no_shell_chars, overwrite = TRUE)
127    intermediates <- c(intermediates, input_no_shell_chars)
128    input <- input_no_shell_chars
129
130    # if an intermediates directory wasn't explicit before, make it explicit now
131    if (is.null(intermediates_dir)) {
132      intermediates_dir <-
133        dirname(normalize_path(input_no_shell_chars))
134      # never use the original input directory as the intermediate directory,
135      # otherwise external resources discovered will be deleted as intermediate
136      # files later (because they are copied to the "intermediate" dir)
137      if (same_path(intermediates_dir, dirname(original_input)))
138        intermediates_dir <- NULL
139    }
140  }
141
142  # force evaluation of knitr root dir before we change directory context
143  force(knit_root_dir)
144
145  # execute within the input file's directory
146  oldwd <- setwd(dirname(tools::file_path_as_absolute(input)))
147  on.exit(setwd(oldwd), add = TRUE)
148
149  # reset the name of the input file to be relative and calculate variations
150  # on the filename for our various intermediate targets
151  input <- basename(input)
152  knit_input <- input
153  knit_output <- intermediates_loc(file_with_meta_ext(input, "knit", "md"))
154
155  intermediates <- c(intermediates, knit_output)
156  utf8_input <- intermediates_loc(file_with_meta_ext(input, "utf8", "md"))
157  intermediates <- c(intermediates, utf8_input)
158
159  # track whether this was straight markdown input (to prevent keep_md later)
160  md_input <- identical(tolower(tools::file_ext(input)), "md")
161
162  # if this is an R script then spin it first
163  if (identical(tolower(tools::file_ext(input)), "r")) {
164    # make a copy of the file to spin
165    spin_input <- intermediates_loc(file_with_meta_ext(input, "spin", "R"))
166    file.copy(input, spin_input, overwrite = TRUE)
167    intermediates <- c(intermediates, spin_input)
168    # spin it
169    spin_rmd <- knitr::spin(spin_input,
170                            knit = FALSE,
171                            envir = envir,
172                            format = "Rmd")
173    intermediates <- c(intermediates, spin_rmd)
174    knit_input <- spin_rmd
175    # append default metadata (this will be ignored if there is user
176    # metadata elsewhere in the file)
177    metadata <- paste('\n',
178      '---\n',
179      'title: "', input, '"\n',
180      'author: "', Sys.info()[["user"]], '"\n',
181      'date: "', date(), '"\n',
182      '---\n'
183    , sep = "")
184    if (!identical(encoding, "native.enc"))
185      metadata <- iconv(metadata, to = encoding)
186    cat(metadata, file = knit_input, append = TRUE)
187  }
188
189  # read the input file
190  input_lines <- read_lines_utf8(knit_input, encoding)
191
192  # read the yaml front matter
193  yaml_front_matter <- parse_yaml_front_matter(input_lines)
194
195  # if this is shiny_prerendered then modify the output format to
196  # be single-page and to output dependencies to the shiny.dep file
197  shiny_prerendered_dependencies <- list()
198  if (requires_knit && is_shiny_prerendered(yaml_front_matter$runtime)) {
199
200    # first validate that the user hasn't passed an already created output_format
201    if (is_output_format(output_format)) {
202      stop("You cannot pass a fully constructed output_format to render when ",
203           "using runtime: shiny_prerendered")
204    }
205
206    # require shiny for the knit
207    if (requireNamespace("shiny")) {
208      if (!"package:shiny" %in% search())
209        attachNamespace("shiny")
210    }
211    else
212      stop("The shiny package is required for 'shiny_prerendered' documents")
213
214    # force various output options
215    output_options$self_contained <- FALSE
216    output_options$dependency_resolver <- function(deps) {
217      shiny_prerendered_dependencies <<- deps
218      list()
219    }
220  }
221
222  # if we haven't been passed a fully formed output format then
223  # resolve it by looking at the yaml
224  if (!is_output_format(output_format)) {
225    output_format <- output_format_from_yaml_front_matter(input_lines,
226                                                          output_options,
227                                                          output_format,
228                                                          encoding = encoding)
229    output_format <- create_output_format(output_format$name,
230                                          output_format$options)
231  }
232  pandoc_to <- output_format$pandoc$to
233
234  # generate outpout file based on input filename
235  if (is.null(output_file))
236    output_file <- pandoc_output_file(input, output_format$pandoc)
237
238  # if an output_dir was specified then concatenate it with the output file
239  if (!is.null(output_dir)) {
240    output_file <- file.path(output_dir, basename(output_file))
241  }
242  output_dir <- dirname(output_file)
243
244  # use output filename based files dir
245  files_dir <- file.path(output_dir, knitr_files_dir(basename(output_file)))
246  files_dir <- pandoc_path_arg(files_dir)
247
248  # default to no cache_dir (may be generated by the knit)
249  cache_dir <- NULL
250
251  # call any intermediate files generator, if we have an intermediates directory
252  # (do this before knitting in case the knit requires intermediates)
253  if (!is.null(intermediates_dir) &&
254      !is.null(output_format$intermediates_generator)) {
255    intermediates <- c(intermediates,
256                       output_format$intermediates_generator(original_input,
257                                                             encoding,
258                                                             intermediates_dir))
259  }
260
261  # reset knit_meta (and ensure it's always reset before exiting render)
262  old_knit_meta <- knit_meta_reset()
263  on.exit({
264    knit_meta_reset()
265    if (length(old_knit_meta)) {
266      knitr::knit_meta_add(old_knit_meta, attr(old_knit_meta, 'knit_meta_id'))
267    }
268  }, add = TRUE)
269
270  # presume that we're rendering as a static document unless specified
271  # otherwise in the parameters
272  runtime <- match.arg(runtime)
273  if (identical(runtime, "auto")) {
274    if (!is.null(yaml_front_matter$runtime))
275      runtime <- yaml_front_matter$runtime
276    else
277      runtime <- "static"
278  }
279
280  # set df_print
281  context <- render_context()
282  context$df_print <- resolve_df_print(output_format$df_print)
283
284  # call any pre_knit handler
285  if (!is.null(output_format$pre_knit)) {
286    output_format$pre_knit(input = original_input)
287  }
288
289  # function used to call post_knit handler
290  call_post_knit_handler <- function() {
291    if (!is.null(output_format$post_knit)) {
292      post_knit_extra_args <- output_format$post_knit(yaml_front_matter,
293                                                      knit_input,
294                                                      runtime,
295                                                      encoding = encoding)
296    } else {
297      post_knit_extra_args <- NULL
298    }
299    c(output_format$pandoc$args, post_knit_extra_args)
300  }
301
302  # determine our id-prefix (add one if necessary for runtime: shiny)
303  id_prefix <- id_prefix_from_args(output_format$pandoc$args)
304  if (!nzchar(id_prefix) && is_shiny(runtime)) {
305    id_prefix <- "section-"
306    output_format$pandoc$args <- c(output_format$pandoc$args, rbind("--id-prefix", id_prefix))
307  }
308
309  # knit if necessary
310  if (requires_knit) {
311
312    # restore options and hooks after knit
313    optk <- knitr::opts_knit$get()
314    on.exit(knitr::opts_knit$restore(optk), add = TRUE)
315    optc <- knitr::opts_chunk$get()
316    on.exit(knitr::opts_chunk$restore(optc), add = TRUE)
317    hooks <- knitr::knit_hooks$get()
318    on.exit(knitr::knit_hooks$restore(hooks), add = TRUE)
319    ohooks <- knitr::opts_hooks$get()
320    on.exit(knitr::opts_hooks$restore(ohooks), add = TRUE)
321    templates <- knitr::opts_template$get()
322    on.exit(knitr::opts_template$restore(templates), add = TRUE)
323
324    # run render on_exit (run after the knit hooks are saved so that
325    # any hook restoration can take precedence)
326    if (is.function(output_format$on_exit))
327      on.exit(output_format$on_exit(), add = TRUE)
328
329    # default rendering and chunk options
330    knitr::render_markdown()
331    knitr::opts_chunk$set(tidy = FALSE, error = FALSE)
332
333    # store info about the final output format in opts_knit
334    knitr::opts_knit$set(
335      rmarkdown.pandoc.from = output_format$pandoc$from,
336      rmarkdown.pandoc.to = pandoc_to,
337      rmarkdown.pandoc.id_prefix = id_prefix,
338      rmarkdown.keep_md = output_format$keep_md,
339      rmarkdown.df_print = output_format$df_print,
340      rmarkdown.version = 2,
341      rmarkdown.runtime = runtime
342    )
343
344    # read root directory from argument (has precedence) or front matter
345    root_dir <- knit_root_dir
346    if (is.null(root_dir))
347      root_dir <- yaml_front_matter$knit_root_dir
348    if (!is.null(root_dir))
349      knitr::opts_knit$set(root.dir = root_dir)
350
351    # use filename based figure and cache directories
352    base_pandoc_to <- gsub('[-+].*', '', pandoc_to)
353    figures_dir <- paste(files_dir, "/figure-", base_pandoc_to, "/", sep = "")
354    knitr::opts_chunk$set(fig.path = figures_dir)
355    cache_dir <- knitr_cache_dir(input, base_pandoc_to)
356    knitr::opts_chunk$set(cache.path = cache_dir)
357
358    # strip the trailing slash from cache_dir so that file.exists() and unlink()
359    # check on it later works on windows
360    cache_dir <- gsub("/$", "", cache_dir)
361
362    # merge user options and hooks
363    if (!is.null(output_format$knitr)) {
364      knitr::opts_knit$set(as.list(output_format$knitr$opts_knit))
365      knitr::opts_chunk$set(adjust_dev(as.list(output_format$knitr$opts_chunk)))
366      knitr::opts_template$set(as.list(output_format$knitr$opts_template))
367      knitr::knit_hooks$set(as.list(output_format$knitr$knit_hooks))
368      knitr::opts_hooks$set(as.list(output_format$knitr$opts_hooks))
369    }
370
371    # setting the runtime (static/shiny) type
372    knitr::opts_knit$set(rmarkdown.runtime = runtime)
373
374    # install evaluate hook for shiny_prerendred
375    if (is_shiny_prerendered(runtime)) {
376
377      # remove uncached .RData (will be recreated from context="data" chunks)
378      shiny_prerendered_remove_uncached_data(original_input)
379
380      # set the cache option hook and evaluate hook
381      knitr::opts_hooks$set(label = shiny_prerendered_option_hook(original_input,encoding))
382      knitr::knit_hooks$set(evaluate = shiny_prerendered_evaluate_hook(original_input))
383    }
384
385    # install global chunk handling for runtime: shiny (evaluate the 'global'
386    # chunk only once, and in the global environment)
387    if (is_shiny_classic(runtime) && !is.null(shiny::getDefaultReactiveDomain())) {
388
389      # install evaluate hook to ensure that the 'global' chunk for this source
390      # file is evaluated only once and is run outside of a user reactive domain
391      knitr::knit_hooks$set(evaluate = function(code, envir, ...) {
392
393        # check for 'global' chunk label
394        if (identical(knitr::opts_current$get("label"), "global")) {
395
396          # check list of previously evaludated global chunks
397          code_string <- paste(code, collapse = '\n')
398          if (!code_string %in% .globals$evaluated_global_chunks) {
399
400            # save it in our list of evaluated global chunks
401            .globals$evaluated_global_chunks <-
402              c(.globals$evaluated_global_chunks, code_string)
403
404            # evaluate with no reactive domain to prevent any shiny code (e.g.
405            # a reactive timer) from attaching to the current user session
406            # (resulting in it's destruction when that session ends)
407            shiny::withReactiveDomain(NULL, {
408              evaluate::evaluate(code, envir = globalenv(), ...)
409            })
410
411          } else {
412            list()
413          }
414        # delegate to standard evaluate for everything else
415        } else {
416          evaluate::evaluate(code, envir, ...)
417        }
418      })
419    }
420
421    # make the params available within the knit environment
422    # (only do this if there are parameters in the front matter
423    # so we don't require recent knitr for all users)
424    if (!is.null(yaml_front_matter$params)) {
425
426      params <- knit_params_get(input_lines, params)
427
428      # bail if an object called 'params' exists in this environment,
429      # and it seems to be an unrelated user-created object. store
430      # references so we can restore them post-render
431      hasParams <- exists("params", envir = envir, inherits = FALSE)
432      envirParams <- NULL
433
434      if (hasParams) {
435        envirParams <- get("params", envir = envir, inherits = FALSE)
436        isKnownParamsObject <-
437          inherits(envirParams, "knit_param_list") ||
438          inherits(envirParams, "knit_param")
439
440        if (!isKnownParamsObject) {
441          stop("params object already exists in knit environment ",
442               "so can't be overwritten by render params", call. = FALSE)
443        }
444      }
445
446      # make the params available in the knit environment
447      assign("params", params, envir = envir)
448      lockBinding("params", envir)
449      on.exit({
450        do.call("unlockBinding", list("params", envir))
451        if (hasParams)
452          assign("params", envirParams, envir = envir)
453        else
454          remove("params", envir = envir)
455      }, add = TRUE)
456    }
457
458    # make the yaml_front_matter available as 'metadata' within the
459    # knit environment (unless it is already defined there in which case
460    # we emit a warning)
461    env <- environment(render)
462    metadata_this <- env$metadata
463    do.call("unlockBinding", list("metadata", env))
464    on.exit({
465      if (bindingIsLocked("metadata", env)) {
466        do.call("unlockBinding", list("metadata", env))
467      }
468      env$metadata <- metadata_this
469      lockBinding("metadata", env)
470    }, add = TRUE)
471    env$metadata <- yaml_front_matter
472
473    # call onKnit hooks (normalize to list)
474    sapply(as.list(getHook("rmarkdown.onKnit")), function(hook) {
475      tryCatch(hook(input = original_input), error = function(e) NULL)
476    })
477    on.exit({
478      sapply(as.list(getHook("rmarkdown.onKnitCompleted")), function(hook) {
479        tryCatch(hook(input = original_input), error = function(e) NULL)
480      })
481    }, add = TRUE)
482
483    perf_timer_start("knitr")
484
485    # perform the knit
486    input <- knitr::knit(knit_input,
487                         knit_output,
488                         envir = envir,
489                         quiet = quiet,
490                         encoding = encoding)
491
492    perf_timer_stop("knitr")
493
494    # call post_knit handler
495    output_format$pandoc$args <- call_post_knit_handler()
496
497    # pull any R Markdown warnings from knit_meta and emit
498    rmd_warnings <- knit_meta_reset(class = "rmd_warning")
499    for (rmd_warning in rmd_warnings) {
500      message("Warning: ", rmd_warning)
501    }
502
503    # pull out shiny_prerendered_contexts and append them as script tags
504    shiny_prerendered_append_contexts(runtime, input, encoding)
505
506    # collect remaining knit_meta
507    knit_meta <- knit_meta_reset()
508
509  } else {
510    output_format$pandoc$args <- call_post_knit_handler()
511  }
512
513  # if this isn't html and there are html dependencies then flag an error
514  if (!(is_pandoc_to_html(output_format$pandoc) ||
515        identical(tolower(tools::file_ext(output_file)), "html")))  {
516    if (has_html_dependencies(knit_meta)) {
517      if (!isTRUE(yaml_front_matter$always_allow_html)) {
518        stop("Functions that produce HTML output found in document targeting ",
519             pandoc_to, " output.\nPlease change the output type ",
520             "of this document to HTML. Alternatively, you can allow\n",
521             "HTML output in non-HTML formats by adding this option to the YAML front",
522             "-matter of\nyour rmarkdown file:\n\n",
523             "  always_allow_html: yes\n\n",
524             "Note however that the HTML output will not be visible in non-HTML formats.\n\n",
525             call. = FALSE)
526      }
527    }
528    if (!identical(runtime, "static")) {
529      stop("Runtime '", runtime, "' is not supported for ",
530           pandoc_to, " output.\nPlease change the output type ",
531           "of this document to HTML.", call. = FALSE)
532    }
533  }
534
535  # clean the files_dir if we've either been asking to clean supporting files or
536  # the knitr cache is active
537  if (output_format$clean_supporting && (is.null(cache_dir) || !dir_exists(cache_dir)))
538      intermediates <- c(intermediates, files_dir)
539
540  # read the input text as UTF-8 then write it back out
541  input_text <- read_lines_utf8(input, encoding)
542  writeLines(input_text, utf8_input, useBytes = TRUE)
543
544  if (run_pandoc) {
545
546    perf_timer_start("pre-processor")
547
548    # call any pre_processor
549    if (!is.null(output_format$pre_processor)) {
550      extra_args <- output_format$pre_processor(yaml_front_matter,
551                                                utf8_input,
552                                                runtime,
553                                                knit_meta,
554                                                files_dir,
555                                                output_dir)
556      output_format$pandoc$args <- c(output_format$pandoc$args, extra_args)
557    }
558
559    # write shiny_prerendered_dependencies if we have them
560    if (is_shiny_prerendered(runtime)) {
561      shiny_prerendered_append_dependencies(utf8_input,
562                                            shiny_prerendered_dependencies,
563                                            files_dir,
564                                            output_dir)
565    }
566
567    perf_timer_stop("pre-processor")
568
569    need_bibtex <- grepl('[.](pdf|tex)$', output_file) &&
570      any(c('--natbib', '--biblatex') %in% output_format$pandoc$args)
571
572    perf_timer_start("pandoc")
573
574    convert <- function(output, citeproc = FALSE) {
575
576      # temporarily move figures to the intermediate dir if specified:
577      # https://github.com/rstudio/rmarkdown/issues/500
578      figures_dir <- gsub('/$', '', knitr::opts_chunk$get("fig.path"))
579      if (!is.null(intermediates_dir) && dir_exists(figures_dir)) {
580        figures_dir_tmp <- intermediates_loc(figures_dir)
581        move_dir(figures_dir, figures_dir_tmp)
582        on.exit(move_dir(figures_dir_tmp, figures_dir), add = TRUE)
583      }
584
585      # ensure we expand paths (for Windows where leading `~/` does
586      # not get expanded by pandoc)
587      utf8_input <- path.expand(utf8_input)
588      output     <- path.expand(output)
589
590      # if we don't detect any invalid shell characters in the
591      # target path, then just call pandoc directly
592      if (!grepl(.shell_chars_regex, output) && !grepl(.shell_chars_regex, utf8_input)) {
593        return(pandoc_convert(
594          utf8_input, pandoc_to, output_format$pandoc$from, output,
595          citeproc, output_format$pandoc$args, !quiet
596        ))
597      }
598
599      # render to temporary file (preserve extension)
600      # this also ensures we don't pass a file path with invalid
601      # characters to our pandoc invocation
602      file_ext <- tools::file_ext(output)
603      ext <- if (nzchar(file_ext))
604        paste(".", file_ext, sep = "")
605      else
606        ""
607
608      # render to a path in the current working directory
609      # (avoid passing invalid characters to shell)
610      pandoc_output_tmp <- basename(tempfile("pandoc", tmpdir = getwd(), fileext = ext))
611
612      # clean up temporary file on exit
613      on.exit(unlink(pandoc_output_tmp), add = TRUE)
614
615      # call pandoc to render file
616      status <- pandoc_convert(
617        utf8_input, pandoc_to, output_format$pandoc$from, pandoc_output_tmp,
618        citeproc, output_format$pandoc$args, !quiet
619      )
620
621      # construct output path (when passed only a file name to '--output',
622      # pandoc seems to render in the same directory as the input file)
623      pandoc_output_tmp_path <- file.path(dirname(utf8_input), pandoc_output_tmp)
624
625      # rename output file to desired location
626      renamed <- suppressWarnings(file.rename(pandoc_output_tmp_path, output))
627
628      # rename can fail if the temporary directory and output path
629      # lie on different volumes; in such a case attempt a file copy
630      # see: https://github.com/rstudio/rmarkdown/issues/705
631      if (!renamed) {
632        copied <- file.copy(pandoc_output_tmp_path, output, overwrite = TRUE)
633        if (!copied) {
634          stop("failed to copy rendered pandoc artefact to '", output, "'")
635        }
636      }
637
638      # return status
639      status
640    }
641    texfile <- file_with_ext(output_file, "tex")
642    # compile Rmd to tex when we need to generate bibliography with natbib/biblatex
643    if (need_bibtex) {
644      convert(texfile)
645      # manually compile tex if PDF output is expected
646      if (grepl('[.]pdf$', output_file)) {
647        latexmk(texfile, output_format$pandoc$latex_engine, '--biblatex' %in% output_format$pandoc$args)
648        file.rename(file_with_ext(texfile, "pdf"), output_file)
649      }
650      # clean up the tex file if necessary
651      if ((texfile != output_file) && !output_format$pandoc$keep_tex)
652        on.exit(unlink(texfile), add = TRUE)
653    } else {
654      # determine whether we need to run citeproc (based on whether we
655      # have references in the input)
656      run_citeproc <- citeproc_required(yaml_front_matter, input_lines)
657      # generate .tex if we want to keep the tex source
658      if (texfile != output_file && output_format$pandoc$keep_tex)
659        convert(texfile, run_citeproc)
660      # run the main conversion if the output file is not .tex
661      convert(output_file, run_citeproc)
662    }
663
664    # pandoc writes the output alongside the input, so if we rendered from an
665    # intermediate directory, move the output file
666    if (!is.null(intermediates_dir)) {
667      intermediate_output <- file.path(intermediates_dir, basename(output_file))
668      if (file.exists(intermediate_output)) {
669        file.rename(intermediate_output, output_file)
670      }
671    }
672
673    perf_timer_stop("pandoc")
674
675    perf_timer_start("post-processor")
676
677    # if there is a post-processor then call it
678    if (!is.null(output_format$post_processor))
679      output_file <- output_format$post_processor(yaml_front_matter,
680                                                  utf8_input,
681                                                  output_file,
682                                                  clean,
683                                                  !quiet)
684
685    if (!quiet) {
686      message("\nOutput created: ", relative_to(oldwd, output_file))
687    }
688
689    perf_timer_stop("post-processor")
690
691  }
692
693  perf_timer_stop("render")
694
695  # write markdown output if requested
696  if (output_format$keep_md && !md_input) {
697    file.copy(input, file_with_ext(output_file, "md"), overwrite = TRUE)
698  }
699
700  if (run_pandoc) {
701    # return the full path to the output file
702    invisible(tools::file_path_as_absolute(output_file))
703  } else {
704    # did not run pandoc; returns the markdown output with attributes of the
705    # knitr meta data and intermediate files
706    structure(input, knit_meta = knit_meta, intermediates = intermediates)
707  }
708}
709
710
711#' Render supporting files for an input document
712#'
713#' Render (copy) required supporting files for an input document to the _files
714#' directory associated with the document.
715#'
716#' @param from Directory to copy from
717#' @param files_dir Directory to copy files into
718#' @param rename_to Optional rename of source directory after it is copied
719#'
720#' @return The relative path to the supporting files. This path is suitable
721#' for inclusion in HTML\code{href} and \code{src} attributes.
722#'
723#' @export
724render_supporting_files <- function(from, files_dir, rename_to = NULL) {
725
726  # auto-create directory for supporting files
727  if (!dir_exists(files_dir))
728    dir.create(files_dir)
729
730  # target directory is based on the dirname of the path or the rename_to
731  # value if it was provided
732  target_stage_dir <- file.path(files_dir, basename(from))
733  target_dir <- file.path(files_dir, ifelse(is.null(rename_to),
734                                     basename(from),
735                                     rename_to))
736
737  # copy the directory if it hasn't already been copied
738  if (!dir_exists(target_dir) && !dir_exists(target_stage_dir)) {
739    file.copy(from = from,
740              to = files_dir,
741              recursive = TRUE,
742              copy.mode = FALSE)
743    if (!is.null(rename_to)) {
744      file.rename(from = target_stage_dir,
745                  to = target_dir)
746    }
747  }
748
749  # return the target dir (used to form links in the HTML)
750  target_dir
751}
752
753# reset knitr meta output (returns any meta output generated since the last
754# call to knit_meta_reset), optionally scoped to a specific output class
755knit_meta_reset <- function(class = NULL) {
756  knitr::knit_meta(class, clean = TRUE)
757}
758
759# render context (render-related state can be stuffed here)
760.render_context <- NULL # initialized in .onLoad
761render_context <- function() {
762  .render_context$peek()
763}
764
765init_render_context <- function() {
766  .render_context$push(new_render_context())
767}
768
769clear_render_context <- function() {
770  .render_context$pop()
771}
772
773new_render_context <- function() {
774  env <- new.env(parent = emptyenv())
775  env$chunk.index <- 1
776  env
777}
778
779merge_render_context <- function(context) {
780  elements <- ls(envir = render_context(), all.names = TRUE)
781  for (el in elements)
782    context[[el]] <- get(el, envir = render_context())
783  context
784}
785
786
787id_prefix_from_args <- function(args) {
788
789  # scan for id-prefix argument
790  for (i in 1:length(args)) {
791    arg <- args[[i]]
792    if (identical(arg, "--id-prefix") && (i < length(args)))
793      return(args[[i + 1]])
794  }
795
796  # default to empty string
797  ""
798}
799
800
801resolve_df_print <- function(df_print) {
802
803  # available methods
804  valid_methods <- c("default", "kable", "tibble", "paged")
805
806  # if we are passed NULL then select the first method
807  if (is.null(df_print))
808    df_print <- valid_methods[[1]]
809
810  # if we are passed all of valid_methods then select the first one
811  if (identical(valid_methods, df_print))
812    df_print <- valid_methods[[1]]
813
814  if (!is.function(df_print)) {
815    if (df_print == "kable")
816      df_print <- knitr::kable
817    else if (df_print == "tibble") {
818      if (!requireNamespace("tibble", quietly = TRUE))
819        stop("Printing 'tibble' without 'tibble' package available")
820
821      df_print <- function(x) print(tibble::as_tibble(x))
822    }
823    else if (df_print == "paged")
824      df_print <- function(x) {
825        if (!identical(knitr::opts_current$get("paged.print"), FALSE)) {
826          knitr::asis_output(paged_table_html(x))
827        }
828        else {
829          print(x)
830        }
831      }
832    else if (df_print == "default")
833      df_print <- print
834    else
835      stop('Invalid value for df_print (valid values are ',
836           paste(valid_methods, collapse = ", "), call. = FALSE)
837  }
838
839  df_print
840}
841
842
843# package level globals
844.globals <- new.env(parent = emptyenv())
845.globals$evaluated_global_chunks <- character()
846
847
848