1#' Define an R Markdown output format
2#'
3#' Define an R Markdown output format based on a combination of knitr and pandoc
4#' options.
5#'
6#' @param knitr Knitr options for an output format (see
7#'   \code{\link{knitr_options}})
8#' @param pandoc Pandoc options for an output format (see
9#'   \code{\link{pandoc_options}})
10#' @param keep_md Keep the markdown file generated by knitting. Note that
11#'   if this is \code{TRUE} then \code{clean_supporting} will always be
12#'   \code{FALSE}.
13#' @param clean_supporting Cleanup any supporting files after conversion see
14#'   \code{\link{render_supporting_files}}
15#' @param df_print Method to be used for printing data frames. Valid values
16#'   include "default", "kable", "tibble", and "paged". The "default" method uses
17#'   \code{print.data.frame}. The "kable" method uses the
18#'   \code{\link[knitr:kable]{knitr::kable}} function. The "tibble" method uses
19#'   the \pkg{tibble} package to print a summary of the data frame. The "paged"
20#'   method creates a paginated HTML table (note that this method is only valid
21#'   for formats that produce HTML). In addition
22#'   to the named methods you can also pass an arbitrary function to be used
23#'   for printing data frames. You can disable the df_print behavior entirely
24#'   by setting the option \code{rmarkdown.df_print} to \code{FALSE}.
25#' @param pre_knit An optional function that runs before kniting which
26#'   receives the \code{input} (input filename passed to \code{render}) and
27#'   \code{...} (for future expansion) arguments.
28#' @param post_knit An optional function that runs after kniting which
29#'   receives the \code{metadata}, \code{input_file}, \code{runtime}, and \code{...}
30#'   (for future expansion) arguments. This function can return additional
31#'   arguments to pass to pandoc and can call \code{knitr::knit_meta_add}
32#'   to add additional dependencies based on the contents of the input_file or on other
33#'   assets side by side with it that may be used to produce html with dependencies
34#'   during subsequent processing.
35#' @param pre_processor An optional pre-processor function that receives the
36#'   \code{metadata}, \code{input_file}, \code{runtime}, \code{knit_meta},
37#'   \code{files_dir}, and \code{output_dir} and can return additional arguments
38#'   to pass to pandoc.
39#' @param intermediates_generator An optional function that receives the
40#'   original \code{input_file}, its \code{encoding}, and the intermediates
41#'   directory (i.e. the \code{intermediates_dir} argument to
42#'   \code{\link{render}}). The function should generate and return the names of
43#'   any intermediate files required to render the \code{input_file}.
44#' @param post_processor An optional post-processor function that receives the
45#'   \code{metadata}, \code{input_file}, \code{output_file}, \code{clean},
46#'   and \code{verbose} parameters, and can return an alternative
47#'   \code{output_file}.
48#' @param on_exit A function to call when \code{rmarkdown::render()} finishes
49#'   execution (as registered with a \code{\link{on.exit}} handler).
50#' @param base_format An optional format to extend.
51#'
52#' @return An R Markdown output format definition that can be passed to
53#'   \code{\link{render}}.
54#'
55#' @seealso \link{render}, \link{knitr_options}, \link{pandoc_options}
56#'
57#' @examples
58#' \dontrun{
59#' output_format(knitr = knitr_options(opts_chunk = list(dev = 'png')),
60#'               pandoc = pandoc_options(to = "html"))
61#' }
62#'
63#' @export
64output_format <- function(knitr,
65                          pandoc,
66                          keep_md = FALSE,
67                          clean_supporting = TRUE,
68                          df_print = NULL,
69                          pre_knit = NULL,
70                          post_knit = NULL,
71                          pre_processor = NULL,
72                          intermediates_generator = NULL,
73                          post_processor = NULL,
74                          on_exit = NULL,
75                          base_format = NULL) {
76
77  format <- list(
78    knitr = knitr,
79    pandoc = pandoc,
80    keep_md = keep_md,
81    clean_supporting = clean_supporting && !keep_md,
82    df_print = df_print,
83    pre_knit = pre_knit,
84    post_knit = post_knit,
85    pre_processor = pre_processor,
86    intermediates_generator = intermediates_generator,
87    post_processor = post_processor,
88    on_exit = on_exit
89  )
90
91  class(format) <- "rmarkdown_output_format"
92
93  # if a base format was supplied, merge it with the format we just created
94  if (!is.null(base_format))
95    merge_output_formats(base_format, format)
96  else
97    format
98}
99
100# merges two scalar values; picks the overlay if non-NULL and then the base
101merge_scalar <- function(base, overlay) {
102  if (is.null(base) && is.null(overlay))
103    NULL
104  else if (is.null(overlay))
105    base
106  else
107    overlay
108}
109
110# merges two functions: if both are non-NULL, produces a new function that
111# invokes each and then uses the supplied operation to combine their outputs
112merge_function_outputs <- function(base, overlay, op) {
113  if (!is.null(base) && !is.null(overlay)) {
114    function(...) {
115      op(base(...), overlay(...))
116    }
117  } else {
118    merge_scalar(base, overlay)
119  }
120}
121
122# merges two post-processors; if both are non-NULL, produces a new function that
123# calls the overlay post-processor and then the base post-processor.
124merge_post_processors <- function(base, overlay) {
125  if (!is.null(base) && !is.null(overlay)) {
126    function(metadata, input_file, output_file, ...) {
127      output_file <- overlay(metadata, input_file, output_file, ...)
128      base(metadata, input_file, output_file, ...)
129    }
130  }
131  else {
132    merge_scalar(base, overlay)
133  }
134}
135
136# merges two output formats
137merge_output_formats <- function(base, overlay)  {
138  structure(list(
139    knitr = merge_lists(base$knitr, overlay$knitr),
140    pandoc = merge_pandoc_options(base$pandoc, overlay$pandoc),
141    keep_md =
142      merge_scalar(base$keep_md, overlay$keep_md),
143    clean_supporting =
144      merge_scalar(base$clean_supporting, overlay$clean_supporting),
145    df_print =
146      merge_scalar(base$df_print, overlay$df_print),
147    pre_knit =
148      merge_function_outputs(base$pre_knit, overlay$pre_knit, c),
149    post_knit =
150      merge_function_outputs(base$post_knit, overlay$post_knit, c),
151    pre_processor =
152      merge_function_outputs(base$pre_processor, overlay$pre_processor, c),
153    intermediates_generator =
154      merge_function_outputs(base$intermediates_generator,
155                             overlay$intermediates_generator, c),
156    post_processor =
157      merge_post_processors(base$post_processor, overlay$post_processor),
158    on_exit =
159      merge_on_exit(base$on_exit, overlay$on_exit)
160  ), class = "rmarkdown_output_format")
161}
162
163merge_on_exit <- function(base, overlay) {
164  function() {
165    if (is.function(base)) base()
166    if (is.function(overlay)) overlay()
167  }
168}
169
170merge_pandoc_options <- function(base, overlay) {
171  res <- merge_lists(base, overlay, recursive = FALSE)
172  res$args <- c(base$args, overlay$args)
173  res
174}
175
176#' Knitr options for an output format
177#'
178#' Define the knitr options for an R Markdown output format.
179#'
180#' @param opts_knit List of package level knitr options (see
181#'   \code{\link[knitr:opts_knit]{opts_knit}})
182#' @param opts_chunk List of chunk level knitr options (see
183#'   \code{\link[knitr:opts_chunk]{opts_chunk}})
184#' @param knit_hooks List of hooks for R code chunks, inline R code, and output
185#'   (see \code{\link[knitr:knit_hooks]{knit_hooks}})
186#' @param opts_hooks List of hooks for code chunk options
187#'   (see \code{\link[knitr:opts_hooks]{opts_hooks}})
188#' @param opts_template List of templates for chunk level knitr options (see
189#'   \code{\link[knitr:opts_template]{opts_template}})
190#'
191#' @return An list that can be passed as the \code{knitr} argument of the
192#'   \code{\link{output_format}} function.
193#'
194#' @seealso \link{output_format}
195#'
196#' @export
197knitr_options <- function(opts_knit = NULL,
198                          opts_chunk = NULL,
199                          knit_hooks = NULL,
200                          opts_hooks = NULL,
201                          opts_template = NULL) {
202  list(opts_knit = opts_knit,
203       opts_chunk = opts_chunk,
204       knit_hooks = knit_hooks,
205       opts_hooks = opts_hooks,
206       opts_template = opts_template)
207}
208
209#' Knitr options for a PDF output format
210#'
211#' Define knitr options for an R Markdown output format that creates PDF output.
212#'
213#' @inheritParams html_document
214#' @inheritParams pdf_document
215#'
216#' @return An list that can be passed as the \code{knitr} argument of the
217#'   \code{\link{output_format}} function.
218#'
219#' @seealso \link{knitr_options}, \link{output_format}
220#'
221#' @export
222knitr_options_pdf <- function(fig_width, fig_height, fig_crop, dev = 'pdf') {
223
224  # default options
225  opts_knit <- NULL
226  opts_chunk <- list(dev = dev,
227                     fig.width = fig_width,
228                     fig.height = fig_height)
229
230  # set the dingbats option for the pdf device if requried
231  if (dev == 'pdf')
232    opts_chunk$dev.args <- list(pdf = list(useDingbats = FALSE))
233
234  knit_hooks <- NULL
235
236  # apply cropping if requested and we have pdfcrop
237  crop <- fig_crop && !is_windows() && nzchar(find_program("pdfcrop"))
238  if (crop) {
239    knit_hooks = list(crop = knitr::hook_pdfcrop)
240    opts_chunk$crop = TRUE
241  }
242
243  # return options
244  knitr_options(opts_knit = opts_knit,
245                opts_chunk = opts_chunk,
246                knit_hooks = knit_hooks)
247}
248
249
250#' Pandoc options for an output format
251#'
252#' Define the pandoc options for an R Markdown output format.
253#'
254#' @param to Pandoc format to convert to
255#' @param from Pandoc format to convert from
256#' @param args Character vector of command line arguments to pass to pandoc
257#' @param keep_tex Keep the intermediate tex file used in the conversion to PDF
258#'   (applies only to 'latex' and 'beamer' target formats)
259#' @param latex_engine LaTeX engine to producing PDF output (applies only to
260#'   'latex' and 'beamer' target formats)
261#' @param ext File extension (e.g. ".tex") for output file (if \code{NULL}
262#'   chooses default based on \code{to}). This is typically used to force
263#'   the final output of a latex or beamer converstion to be \code{.tex}
264#'   rather than \code{.pdf}.
265#'
266#' @return An list that can be passed as the \code{pandoc} argument of the
267#'   \code{\link{output_format}} function.
268#'
269#' @details The \code{from} argument should be used very cautiously as it's
270#'   important for users to be able to rely on a stable definition of supported
271#'   markdown extensions.
272#'
273#' @seealso \link{output_format}, \link{rmarkdown_format}
274#'
275#' @export
276pandoc_options <- function(to,
277                           from = rmarkdown_format(),
278                           args = NULL,
279                           keep_tex = FALSE,
280                           latex_engine = c("pdflatex", "lualatex", "xelatex"),
281                           ext = NULL) {
282  list(to = to,
283       from = from,
284       args = args,
285       keep_tex = keep_tex,
286       latex_engine = match.arg(latex_engine),
287       ext = ext)
288}
289
290#' R Markdown input format definition
291#'
292#' Compose a pandoc markdown input definition for R Markdown that can be
293#' passed as the \code{from} argument of \link{pandoc_options}.
294#'
295#'
296#' @param implicit_figures Automatically make figures from images (defaults to \code{TRUE}).
297#' @param extensions Markdown extensions to be added or removed from the
298#' default definition of R Markdown.
299#'
300#' @return Pandoc markdown format specification
301#'
302#' @details
303#'
304#' By default R Markdown is defined as all pandoc markdown extensions with
305#' the following tweaks for backward compatibility with the markdown package
306#' (+ features are added, - features are removed):
307#'
308#' \tabular{l}{
309#' \code{+autolink_bare_uris} \cr
310#' \code{+ascii_identifier} \cr
311#' \code{+tex_math_single_backslash} \cr
312#' }
313#'
314#'
315#' For more on pandoc markdown see the \href{http://pandoc.org/README.html}{pandoc online documentation}.
316#'
317#' @examples
318#' \dontrun{
319#' rmarkdown_format("-implicit_figures")
320#' }
321#'
322#' @seealso \link{output_format}, \link{pandoc_options}
323#'
324#' @export
325rmarkdown_format <- function(extensions = NULL) {
326
327  format <- c("markdown")
328
329  # only add extensions if the user hasn't already specified
330  # a manual override for them
331  addExtension <- function(extension) {
332    if (length(grep(extension, extensions)) == 0)
333      format <<- c(format, paste0("+", extension))
334  }
335
336  addExtension("autolink_bare_uris")
337  addExtension("ascii_identifiers")
338  addExtension("tex_math_single_backslash")
339
340  format <- c(format, extensions, recursive = TRUE)
341
342  paste(format, collapse = "")
343}
344
345# Add the +smart extension for Pandoc >= 2.0
346smart_extension <- function(smart, extension) {
347  c(extension, if (smart && pandoc2.0()) "+smart")
348}
349
350#' Determine the default output format for an R Markdown document
351#'
352#' Read the YAML metadata (and any common _output.yml file) for the
353#' document and return the output format that will be generated by
354#' a call to \code{\link{render}}.
355#'
356#' @param input Input file (Rmd or plain markdown)
357#' @param encoding The encoding of the input file; see \code{\link{file}}
358#'
359#' @return A named list with a \code{name} value containing the format
360#'   name and an \code{options} value that is a list containing all the options
361#'   for the format and their values. An option's default value will be returned
362#'   if the option isn't set explicitly in the document.
363#'
364#' @details
365#'
366#' This function is useful for front-end tools that require additional
367#' knowledge of the output to be produced by \code{\link{render}} (e.g. to
368#' customize the preview experience).
369#'
370#' @export
371default_output_format <- function(input, encoding = getOption("encoding")) {
372
373  # execute within the input file's directory (this emulates the way
374  # yaml front matter discovery is done within render)
375  oldwd <- setwd(dirname(tools::file_path_as_absolute(input)))
376  on.exit(setwd(oldwd), add = TRUE)
377
378  # because we're now within the same directory as the input file,
379  # we just need its basename
380  input <- basename(input)
381
382  # parse the YAML and front matter and get the explicitly set options
383  input_lines <- read_lines_utf8(input, encoding)
384  format <- output_format_from_yaml_front_matter(input_lines, encoding = encoding)
385
386  # look up the formals of the output function to get the full option list and
387  # merge against the explicitly set list
388  format_function <- eval(parse(text = format$name))
389  format$options <- merge_lists(as.list(formals(format_function)),
390                                format$options,
391                                recursive = FALSE)
392  format
393}
394
395#' Resolve the output format for an R Markdown document
396#'
397#' Read the YAML metadata (and any common _output.yml file) for the
398#' document and return an output format object that can be
399#' passed to the \code{\link{render}} function.
400#'
401#' @param input Input file (Rmd or plain markdown)
402#' @param output_format Name of output format (or \code{NULL} to use
403#'   the default format for the input file).
404#' @param output_options List of output options that should override the
405#'   options specified in metadata.
406#' @param encoding The encoding of the input file; see \code{\link{file}}
407#'
408#' @return An R Markdown output format definition that can be passed to
409#'   \code{\link{render}}.
410#'
411#' @details
412#'
413#' This function is useful for front-end tools that need to modify
414#' the default behavior of an output format.
415#'
416#' @export
417resolve_output_format <- function(input,
418                                  output_format = NULL,
419                                  output_options = NULL,
420                                  encoding = getOption("encoding")) {
421
422  # read the input file
423  input_lines <- read_lines_utf8(input, encoding)
424
425  # validate that the output format is either NULL or a character vector
426  if (!is.null(output_format) && !is.character(output_format))
427    stop("output_format must be a character vector")
428
429  # resolve the output format by looking at the yaml
430  output_format <- output_format_from_yaml_front_matter(input_lines,
431                                                        output_options,
432                                                        output_format,
433                                                        encoding = encoding)
434
435  # return it
436  create_output_format(output_format$name, output_format$options)
437}
438
439
440#' Determine all output formats for an R Markdown document
441#'
442#' Read the YAML metadata (and any common _output.yml file) for the
443#' document and return the output formats that will be generated by
444#' a call to \code{\link{render}}.
445#'
446#' @param input Input file (Rmd or plain markdown)
447#' @param encoding The encoding of the input file; see \code{\link{file}}
448#'
449#' @return A character vector with the names of all output formats.
450#'
451#' @details
452#'
453#' This function is useful for front-end tools that require additional
454#' knowledge of the output to be produced by \code{\link{render}} (e.g. to
455#' customize the preview experience).
456#'
457#' @export
458all_output_formats <- function(input, encoding = getOption("encoding")) {
459  enumerate_output_formats(input = input,
460                           envir = parent.frame(),
461                           encoding = encoding)
462
463}
464
465
466# Synthesize the output format for a document from it's YAML. If we can't
467# find an output format then we just return html_document
468output_format_from_yaml_front_matter <- function(input_lines,
469                                                 output_options = NULL,
470                                                 output_format_name = NULL,
471                                                 encoding = getOption("encoding")) {
472
473  format_name <- output_format_name
474  # ensure input is the correct data type
475  if (!is_null_or_string(format_name)) {
476    stop("Unrecognized output format specified", call. = FALSE)
477  }
478
479  # parse the yaml
480  yaml_input <- parse_yaml_front_matter(input_lines)
481
482  # default to no options
483  format_options <- list()
484
485  # parse _site.yml output format if we have it
486  config <- site_config(".", encoding = encoding)
487  yaml_site <- config[["output"]]
488
489  # parse common _output.yml if we have it
490  yaml_common <- if (file.exists("_output.yml")) {
491    yaml_load_file_utf8("_output.yml")
492  } else if (file.exists("_output.yaml")) {
493    yaml_load_file_utf8("_output.yaml")
494  }
495
496  # merge _site.yml and _output.yml
497  yaml_common <- merge_output_options(yaml_site, yaml_common)
498
499  # parse output format from front-matter if we have it
500  if (length(yaml_common) || length(yaml_input[["output"]])) {
501
502    # alias the output format yaml
503    yaml_output <- yaml_input[["output"]]
504
505    # merge against common _output.yml
506    yaml_output <- merge_output_options(yaml_common, yaml_output)
507
508    # if a named format was provided then try to find it
509    if (!is.null(format_name)) {
510
511      # if this is a named element of the list then use that
512      if (format_name %in% names(yaml_output)) {
513
514        format_options <- yaml_output[[format_name]]
515
516      # otherwise this could be a heterogeneous list of characters and
517      # lists so scan for an embedded list
518      } else {
519        for (format in yaml_output) {
520          if (is.list(format) && !is.null(format[[format_name]]))
521            format_options <- format[[format_name]]
522        }
523      }
524
525      # if the options are just "default" then that's the same as empty list
526      if (identical(format_options, "default")) format_options <- list()
527
528    # no named format passed so take the first element
529    } else {
530      if (is.list(yaml_output[[1]])) {
531        # check for named list
532        if (nzchar(format_name <- names(yaml_output)[[1]])) {
533          format_options <- yaml_output[[1]]
534        # nested named list
535        } else {
536          format_name <- names(yaml_output[[1]])[[1]]
537          format_options <- yaml_output[[1]][[format_name]]
538        }
539      } else if (is.list(yaml_output) &&
540                   (is.null(yaml_output[[1]]) ||
541                      identical(yaml_output[[1]], "default"))) {
542        format_name <- names(yaml_output)[[1]]
543
544      } else {
545        format_name <- yaml_output[[1]]
546      }
547    }
548
549  # no output formats defined in the file, just take the passed format
550  # by name (or default to html_document if no named format was specified)
551  } else {
552    if (is.null(format_name)) format_name <- "html_document"
553  }
554
555  # merge any output_options passed in the call to render
556  if (!is.null(output_options)) {
557    format_options <- merge_output_options(format_options, output_options)
558  }
559
560  # return the format name and options
561  list(name = format_name, options = format_options)
562}
563
564create_output_format <- function(name, options) {
565
566  # validate the name
567  if (is.null(name))
568    stop("The output format name must not be NULL", call. = FALSE)
569  if (name == "revealjs_presentation")
570    stop("reveal.js presentations are now located in a separate package: ",
571         "https://github.com/jjallaire/revealjs")
572
573  # lookup the function
574  output_format_func <- eval(parse(text = name))
575  if (!is.function(output_format_func))
576    stop("YAML output format must evaluate to a function", call. = FALSE)
577
578  # call the function
579  output_format <- do.call(output_format_func, options)
580  if (!is_output_format(output_format))
581    stop("Format is not of class rmarkdown_output_format", call. = FALSE)
582
583  # return the format
584  output_format
585}
586
587is_output_format <- function(x) {
588  inherits(x, "rmarkdown_output_format")
589}
590
591enumerate_output_formats <- function(input, envir, encoding) {
592
593  # read the input
594  input_lines <- read_lines_utf8(input, encoding)
595
596  # if this is an R file then spin it
597  if (identical(tolower(tools::file_ext(input)), "r"))
598    input_lines <- knitr::spin(text = input_lines, knit = FALSE, envir = envir)
599
600  # parse _site.yml output format if we have it
601  config <- site_config(input, encoding = encoding)
602  if (!is.null(config) && !is.null(config[["output"]])) {
603    site_output_format_yaml <- config[["output"]]
604  } else {
605    site_output_format_yaml <- list()
606  }
607
608  # read the ymal front matter
609  yaml_front_matter <- parse_yaml_front_matter(input_lines)
610
611  # read any _output.yml
612  output_yml <- file.path(dirname(input), "_output.yml")
613  output_yaml <- file.path(dirname(input), "_output.yaml")
614  if (file.exists(output_yml))
615    common_output_format_yaml <- yaml_load_file_utf8(output_yml)
616  else if (file.exists(output_yaml))
617    common_output_format_yaml <- yaml_load_file_utf8(output_yaml)
618  else
619    common_output_format_yaml <- list()
620
621  # merge site and common
622  common_output_format_yaml <- merge_output_options(site_output_format_yaml,
623                                                    common_output_format_yaml)
624
625  # parse output formats from front-matter if we have it
626  if (length(common_output_format_yaml) > 0 ||
627      length(yaml_front_matter[["output"]]) > 0) {
628
629    # alias the output format yaml
630    output_format_yaml <- yaml_front_matter[["output"]]
631
632    # merge against common _output.yml
633    output_format_yaml <- merge_output_options(common_output_format_yaml,
634                                               output_format_yaml)
635  }
636  else {
637    output_format_yaml <- NULL
638  }
639
640  # return them by name
641  if (is.character(output_format_yaml)) {
642    output_format_yaml
643  } else if (is.list(output_format_yaml)) {
644    names(output_format_yaml)
645  } else {
646    NULL
647  }
648}
649
650#' Parse the YAML front matter from a file
651#'
652#' @inheritParams default_output_format
653#'
654#' @keywords internal
655#' @export
656yaml_front_matter <- function(input, encoding = getOption("encoding")) {
657
658   # read the input file
659  input_lines <- read_lines_utf8(input, encoding)
660
661  # parse the yaml front matter
662  parse_yaml_front_matter(input_lines)
663}
664
665parse_yaml_front_matter <- function(input_lines) {
666
667  partitions <- partition_yaml_front_matter(input_lines)
668  if (!is.null(partitions$front_matter)) {
669    front_matter <- partitions$front_matter
670    if (length(front_matter) > 2) {
671      front_matter <- front_matter[2:(length(front_matter) - 1)]
672      front_matter <- paste(front_matter, collapse = "\n")
673      validate_front_matter(front_matter)
674      parsed_yaml <- yaml_load_utf8(front_matter)
675      if (is.list(parsed_yaml))
676        parsed_yaml
677      else
678        list()
679    }
680    else
681      list()
682  }
683  else
684    list()
685}
686
687validate_front_matter <- function(front_matter) {
688  front_matter <- trim_trailing_ws(front_matter)
689  if (grepl(":$", front_matter))
690    stop("Invalid YAML front matter (ends with ':')", call. = FALSE)
691}
692
693
694
695partition_yaml_front_matter <- function(input_lines) {
696
697  validate_front_matter <- function(delimiters) {
698    if (length(delimiters) >= 2 &&
699        (delimiters[2] - delimiters[1] > 1) &&
700        grepl("^---\\s*$", input_lines[delimiters[1]])) {
701      # verify that it's truly front matter (not preceded by other content)
702      if (delimiters[1] == 1)
703        TRUE
704      else
705        is_blank(input_lines[1:delimiters[1] - 1])
706    } else {
707      FALSE
708    }
709  }
710
711  # is there yaml front matter?
712  delimiters <- grep("^(---|\\.\\.\\.)\\s*$", input_lines)
713  if (validate_front_matter(delimiters)) {
714
715    front_matter <- input_lines[(delimiters[1]):(delimiters[2])]
716
717    input_body <- c()
718
719    if (delimiters[1] > 1)
720      input_body <- c(input_body,
721                      input_lines[1:delimiters[1] - 1])
722
723    if (delimiters[2] < length(input_lines))
724      input_body <- c(input_body,
725                      input_lines[-(1:delimiters[2])])
726
727    list(front_matter = front_matter,
728         body = input_body)
729  }
730  else {
731    list(front_matter = NULL,
732         body = input_lines)
733  }
734}
735
736merge_output_options <- function(base_options, overlay_options) {
737
738  # if either one of these is a character vector then normalize to a named list
739  normalize_list <- function(target) {
740    if (is.null(target)) {
741      list()
742    } else if (is.character(target)) {
743      setNames(lapply(target, function(x) list()), target)
744    } else {
745      target[names(target) != "..."]  # remove symbols (...) from list
746    }
747  }
748
749  merge_lists(normalize_list(base_options), normalize_list(overlay_options))
750}
751
752is_pandoc_to_html <- function(options) {
753  identical(options$to, "html") || identical(options$to, "html5")
754}
755
756citeproc_required <- function(yaml_front_matter, input_lines = NULL) {
757  (
758    is.null(yaml_front_matter$citeproc) ||
759      yaml_front_matter$citeproc
760  ) && (
761    !is.null(yaml_front_matter$bibliography) ||
762      !is.null(yaml_front_matter$references) ||
763      length(grep("^references\\:\\s*$", input_lines)) > 0
764  )
765}
766