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