1#'Convert to an HTML document
2#'
3#'Format for converting from R Markdown to an HTML document.
4#'
5#' @inheritParams output_format
6#'
7#'@param toc \code{TRUE} to include a table of contents in the output
8#'@param toc_depth Depth of headers to include in table of contents
9#'@param toc_float \code{TRUE} to float the table of contents to the left of the
10#'  main document content. Rather than \code{TRUE} you may also pass a list of
11#'  options that control the behavior of the floating table of contents. See the
12#'  \emph{Floating Table of Contents} section below for details.
13#'@param number_sections \code{TRUE} to number section headings
14#'@param fig_width Default width (in inches) for figures
15#'@param fig_height Default width (in inches) for figures
16#'@param fig_retina Scaling to perform for retina displays (defaults to 2, which
17#'  currently works for all widely used retina displays). Set to \code{NULL} to
18#'  prevent retina scaling. Note that this will always be \code{NULL} when
19#'  \code{keep_md} is specified (this is because \code{fig_retina} relies on
20#'  outputting HTML directly into the markdown document).
21#'@param fig_caption \code{TRUE} to render figures with captions
22#'@param dev Graphics device to use for figure output (defaults to png)
23#'@param code_folding Enable document readers to toggle the display of R code
24#'  chunks. Specify \code{"none"} to display all code chunks (assuming
25#'  they were knit with \code{echo = TRUE}). Specify \code{"hide"} to hide all R
26#'  code chunks by default (users can show hidden code chunks either
27#'  individually or document-wide). Specify \code{"show"} to show all R code
28#'  chunks by default.
29#'@param code_download Embed the Rmd source code within the document and provide
30#'  a link that can be used by readers to download the code.
31#'@param smart Produce typographically correct output, converting straight
32#'  quotes to curly quotes, --- to em-dashes, -- to en-dashes, and ... to
33#'  ellipses.
34#'@param self_contained Produce a standalone HTML file with no external
35#'  dependencies, using data: URIs to incorporate the contents of linked
36#'  scripts, stylesheets, images, and videos. Note that even for self contained
37#'  documents MathJax is still loaded externally (this is necessary because of
38#'  it's size).
39#'@param theme Visual theme ("default", "cerulean", "journal", "flatly",
40#'  "readable", "spacelab", "united", "cosmo", "lumen", "paper", "sandstone",
41#'  "simplex", or "yeti"). Pass \code{NULL} for no theme (in this case you can
42#'  use the \code{css} parameter to add your own styles).
43#'@param highlight Syntax highlighting style. Supported styles include
44#'  "default", "tango", "pygments", "kate", "monochrome", "espresso", "zenburn",
45#'  "haddock", and "textmate". Pass \code{NULL} to prevent syntax highlighting.
46#'@param mathjax Include mathjax. The "default" option uses an https URL from a
47#'  MathJax CDN. The "local" option uses a local version of MathJax (which is
48#'  copied into the output directory). You can pass an alternate URL or pass
49#'  \code{NULL} to exclude MathJax entirely.
50#'@param section_divs Wrap sections in <div> tags (or <section> tags in HTML5),
51#'  and attach identifiers to the enclosing <div> (or <section>) rather than the
52#'  header itself.
53#'@param template Pandoc template to use for rendering. Pass "default" to use
54#'  the rmarkdown package default template; pass \code{NULL} to use pandoc's
55#'  built-in template; pass a path to use a custom template that you've created.
56#'  Note that if you don't use the "default" template then some features of
57#'  \code{html_document} won't be available (see the Templates section below for
58#'  more details).
59#'@param css One or more css files to include
60#'@param includes Named list of additional content to include within the
61#'  document (typically created using the \code{\link{includes}} function).
62#'@param keep_md Keep the markdown file generated by knitting.
63#'@param lib_dir Directory to copy dependent HTML libraries (e.g. jquery,
64#'  bootstrap, etc.) into. By default this will be the name of the document with
65#'  \code{_files} appended to it.
66#'@param md_extensions Markdown extensions to be added or removed from the
67#'  default definition or R Markdown. See the \code{\link{rmarkdown_format}} for
68#'  additional details.
69#'@param pandoc_args Additional command line options to pass to pandoc
70#'@param extra_dependencies,... Additional function arguments to pass to the
71#'  base R Markdown HTML output formatter \code{\link{html_document_base}}
72#'
73#'@return R Markdown output format to pass to \code{\link{render}}
74#'
75#'@details
76#'
77#'See the \href{http://rmarkdown.rstudio.com/html_document_format.html}{online
78#'documentation} for additional details on using the \code{html_document}
79#'format.
80#'
81#'R Markdown documents can have optional metadata that is used to generate a
82#'document header that includes the title, author, and date. For more details
83#'see the documentation on R Markdown \link[=rmd_metadata]{metadata}.
84#'
85#'R Markdown documents also support citations. You can find more information on
86#'the markdown syntax for citations in the
87#'\href{http://rmarkdown.rstudio.com/authoring_bibliographies_and_citations.html}{Bibliographies
88#'and Citations} article in the online documentation.
89#'
90#'
91#'@section Navigation Bars:
92#'
93#'  If you have a set of html documents which you'd like to provide a common
94#'  global navigation bar for, you can include a "_navbar.yml" or "_navbar.html"
95#'  file within the same directory as your html document and it will automatically
96#'  be included at the top of the document.
97#'
98#'  The "_navbar.yml" file includes \code{title}, \code{type}, \code{left}, and
99#'  \code{right} fields (to define menu items for the left and right of the navbar
100#'  resspectively). Menu items include \code{title} and \code{href} fields. For example:
101#'
102#'  \preformatted{ title: "My Website"
103#'  type: default
104#'  left:
105#'    - text: "Home"
106#'      href: index.html
107#'    - text: "Other"
108#'      href: other.html
109#'  right:
110#'    - text: GitHub
111#'      href: https://github.com}
112#'  The \code{type} field is optional and can take the value "default" or "inverse" (which
113#'  provides a different color scheme for the navigation bar).
114#'
115#'  Alternatively, you can include a "_navbar.html" file which is a full HTML definition
116#'  of a bootstrap navigation bar. For a simple example of including a navigation bar see
117#'  \href{https://github.com/rstudio/rmarkdown-website/blob/master/_navbar.html}{https://github.com/rstudio/rmarkdown-website/blob/master/_navbar.html}.
118#'   For additional documentation on creating Bootstrap navigation bars see
119#'  \href{http://getbootstrap.com/components/#navbar}{http://getbootstrap.com/components/#navbar}.
120#'
121#'
122#'@section Floating Table of Contents:
123#'
124#'  You may specify a list of options for the \code{toc_float} parameter which
125#'  control the behavior of the floating table of contents. Options include:
126#'
127#'  \itemize{ \item{\code{collapsed} (defaults to \code{TRUE}) controls whether
128#'  the table of contents appears with only the top-level (H2) headers. When
129#'  collapsed the table of contents is automatically expanded inline when
130#'  necessary.} \item{\code{smooth_scroll} (defaults to \code{TRUE}) controls
131#'  whether page scrolls are animated when table of contents items are navigated
132#'  to via mouse clicks.} \item{\code{print} (defaults to \code{TRUE}) controls
133#'  whether the table of contents appears when user prints out the HTML page.}}
134#'
135#'@section Tabbed Sections:
136#'
137#'  You can organize content using tabs by applying the \code{.tabset} class
138#'  attribute to headers within a document. This will cause all sub-headers of
139#'  the header with the \code{.tabset} attribute to appear within tabs rather
140#'  than as standalone sections. For example:
141#'
142#'  \preformatted{ ## Quarterly Results {.tabset}
143#'
144#'  ### By Product
145#'
146#'  ### By Region }
147#'
148#'  You can also specify two additional attributes to control the appearance and
149#'  behavior of the tabs. The \code{.tabset-fade} attributes causes the tabs to
150#'  fade in and out when switching. The \code{.tabset-pills} attribute causes
151#'  the visual appearance of the tabs to be "pill" rather than traditional tabs.
152#'  For example:
153#'
154#'  \preformatted{ ## Quarterly Results {.tabset .tabset-fade .tabset-pills} }
155#'
156#'@section Templates:
157#'
158#'  You can provide a custom HTML template to be used for rendering. The syntax
159#'  for templates is described in the
160#'  \href{http://pandoc.org/README.html}{pandoc documentation}. You can also use
161#'  the basic pandoc template by passing \code{template = NULL}.
162#'
163#'  Note however that if you choose not to use the "default" HTML template then
164#'  several aspects of HTML document rendering will behave differently:
165#'
166#'  \itemize{ \item{The \code{theme} parameter does not work (you can still
167#'  provide styles using the \code{css} parameter). } \item{For the
168#'  \code{highlight} parameter, the default highlighting style will resolve to
169#'  "pygments" and the "textmate" highlighting style is not available }
170#'  \item{The \code{toc_float} parameter will not work. } \item{The
171#'  \code{code_folding} parameter will not work. } \item{Tabbed sections (as
172#'  described above) will not work.} \item{Navigation bars (as described above)
173#'  will not work. }\item{MathJax will not work if \code{self_contained} is
174#'  \code{TRUE} (these two options can't be used together in normal pandoc
175#'  templates). } }
176#'
177#'  Due to the above restrictions, you might consider using the \code{includes}
178#'  parameter as an alternative to providing a fully custom template.
179#'
180#' @examples
181#' \dontrun{
182#'
183#' library(rmarkdown)
184#'
185#' render("input.Rmd", html_document())
186#'
187#' render("input.Rmd", html_document(toc = TRUE))
188#' }
189#'
190#'@export
191html_document <- function(toc = FALSE,
192                          toc_depth = 3,
193                          toc_float = FALSE,
194                          number_sections = FALSE,
195                          section_divs = TRUE,
196                          fig_width = 7,
197                          fig_height = 5,
198                          fig_retina = 2,
199                          fig_caption = TRUE,
200                          dev = 'png',
201                          df_print = "default",
202                          code_folding = c("none", "show", "hide"),
203                          code_download = FALSE,
204                          smart = TRUE,
205                          self_contained = TRUE,
206                          theme = "default",
207                          highlight = "default",
208                          mathjax = "default",
209                          template = "default",
210                          extra_dependencies = NULL,
211                          css = NULL,
212                          includes = NULL,
213                          keep_md = FALSE,
214                          lib_dir = NULL,
215                          md_extensions = NULL,
216                          pandoc_args = NULL,
217                          ...) {
218
219  # build pandoc args
220  args <- c("--standalone")
221
222  # use section divs
223  if (section_divs)
224    args <- c(args, "--section-divs")
225
226  # table of contents
227  args <- c(args, pandoc_toc_args(toc, toc_depth))
228
229  md_extensions <- smart_extension(smart, md_extensions)
230
231  # toc_float
232  if (toc && !identical(toc_float, FALSE)) {
233
234    # must have a theme
235    if (is.null(theme))
236      stop("You must use a theme when specifying the 'toc_float' option")
237
238    # resolve options
239    toc_float_options <- list(collapsed = TRUE,
240                              smooth_scroll = TRUE,
241                              print = TRUE)
242    if (is.list(toc_float)) {
243      toc_float_options <- merge_lists(toc_float_options, toc_float)
244      toc_float <- TRUE
245    } else if (!isTRUE(toc_float)) {
246      stop("toc_float must be a logical or a list with options")
247    }
248
249    # dependencies
250    extra_dependencies <- append(extra_dependencies,
251                                 list(html_dependency_jquery(),
252                                      html_dependency_jqueryui(),
253                                      html_dependency_tocify()))
254
255    # flag for template
256    args <- c(args, pandoc_variable_arg("toc_float", "1"))
257
258    # selectors
259    selectors <- paste0("h", seq(1, toc_depth), collapse = ",")
260    args <- c(args, pandoc_variable_arg("toc_selectors", selectors))
261
262    # options
263    if (toc_float_options$collapsed)
264      args <- c(args, pandoc_variable_arg("toc_collapsed", "1"))
265    if (toc_float_options$smooth_scroll)
266      args <- c(args, pandoc_variable_arg("toc_smooth_scroll", "1"))
267    if (toc_float_options$print)
268      args <- c(args, pandoc_variable_arg("toc_print", "1"))
269  }
270
271  # template path and assets
272  if (identical(template, "default"))
273    args <- c(args, "--template",
274              pandoc_path_arg(rmarkdown_system_file("rmd/h/default.html")))
275  else if (!is.null(template))
276    args <- c(args, "--template", pandoc_path_arg(template))
277
278  # validate code_folding
279  code_folding <- match.arg(code_folding)
280
281  # navigation dependencies
282  if (!is.null(theme)) {
283    code_menu <- !identical(code_folding, "none") || code_download
284    source_embed <- code_download
285    extra_dependencies <- append(extra_dependencies,
286      list(
287        html_dependency_jquery(),
288        html_dependency_navigation(code_menu = code_menu,
289                                   source_embed = source_embed)
290      )
291    )
292  }
293
294  # highlight
295  args <- c(args, pandoc_html_highlight_args(template, highlight))
296
297  # add highlight.js html_dependency if required
298  if (identical(template, "default") && is_highlightjs(highlight)) {
299    extra_dependencies <- append(extra_dependencies, list(html_dependency_highlightjs(highlight)))
300  }
301
302  # numbered sections
303  if (number_sections)
304    args <- c(args, "--number-sections")
305
306  # additional css
307  for (css_file in css)
308    args <- c(args, "--css", pandoc_path_arg(css_file))
309
310  # manage list of exit_actions (backing out changes to knitr options)
311  exit_actions <- list()
312  on_exit <- function() {
313    for (action in exit_actions)
314      try(action())
315  }
316
317  # capture the source code if requested
318  source_code <- NULL
319  source_file <- NULL
320  pre_knit <- function(input, ...) {
321    if (code_download) {
322      source_file <<- basename(input)
323      source_code <<- paste0(
324        '<div id="rmd-source-code">',
325        base64enc::base64encode(input),
326        '</div>')
327    }
328  }
329
330  # pagedtable
331  if (identical(df_print, "paged")) {
332    extra_dependencies <- append(extra_dependencies,
333                                 list(html_dependency_pagedtable()))
334  }
335
336  # pre-processor for arguments that may depend on the name of the
337  # the input file AND which need to inject html dependencies
338  # (otherwise we could just call the pre_processor)
339  post_knit <- function(metadata, input_file, runtime, encoding, ...) {
340
341    # extra args
342    args <- c()
343
344    # navbar (requires theme)
345    if (!is.null(theme)) {
346
347      # add navbar to includes if necessary
348      navbar <- file.path(normalize_path(dirname(input_file)), "_navbar.html")
349
350      # if there is no _navbar.html look for a _navbar.yml
351      if (!file.exists(navbar)) {
352        navbar_yaml <- file.path(dirname(navbar), "_navbar.yml")
353        if (file.exists(navbar_yaml))
354          navbar <- navbar_html_from_yaml(navbar_yaml)
355        # if there is no _navbar.yml then look in site config (if we have it)
356        config <- site_config(input_file, encoding)
357        if (!is.null(config) && !is.null(config$navbar))
358          navbar <- navbar_html(config$navbar)
359      }
360
361      if (file.exists(navbar)) {
362
363        # include the navbar html
364        includes <- list(before_body = navbar)
365        args <- c(args, includes_to_pandoc_args(includes,
366                                  filter = if (is_shiny_classic(runtime))
367                                    function(x) normalize_path(x, mustWork = FALSE)
368                                  else
369                                    identity))
370
371        # flag indicating we need extra navbar css and js
372        args <- c(args, pandoc_variable_arg("navbar", "1"))
373        # variables controlling padding from navbar
374        args <- c(args, pandoc_body_padding_variable_args(theme))
375
376        # navbar icon dependencies
377        iconDeps <- navbar_icon_dependencies(navbar)
378        if (length(iconDeps) > 0)
379          knitr::knit_meta_add(list(iconDeps))
380      }
381    }
382
383    args
384  }
385
386  # pre-processor for arguments that may depend on the name of the
387  # the input file (e.g. ones that need to copy supporting files)
388  pre_processor <- function(metadata, input_file, runtime, knit_meta, files_dir,
389                            output_dir) {
390
391    # use files_dir as lib_dir if not explicitly specified
392    if (is.null(lib_dir))
393      lib_dir <- files_dir
394
395    # extra args
396    args <- c()
397
398    # track whether we have a code menu
399    code_menu <- FALSE
400
401    # code_folding
402    if (code_folding %in% c("show", "hide")) {
403      # must have a theme
404      if (is.null(theme))
405        stop("You must use a theme when specifying the 'code_folding' option")
406      args <- c(args, pandoc_variable_arg("code_folding", code_folding))
407      code_menu <- TRUE
408    }
409
410    # source_embed
411    if (code_download) {
412      if (is.null(theme))
413        stop("You must use a theme when specifying the 'code_download' option")
414      args <- c(args, pandoc_variable_arg("source_embed", source_file))
415      sourceCodeFile <- tempfile(fileext = ".html")
416      writeLines(source_code, sourceCodeFile)
417      args <- c(args, pandoc_include_args(after_body = sourceCodeFile))
418      code_menu <- TRUE
419    }
420
421    # code menu
422    if (code_menu)
423      args <- c(args, pandoc_variable_arg("code_menu", "1"))
424
425    # content includes (we do this here so that user include-in-header content
426    # goes after dependency generated content). make the paths absolute if
427    # making a Shiny document so we can resolve them even if rendering
428    # elsewhere.
429    args <- c(args, includes_to_pandoc_args(includes,
430                      filter = if (is_shiny_classic(runtime))
431                        function(x) normalize_path(x, mustWork = FALSE)
432                      else
433                        identity))
434
435    # return additional args
436    args
437  }
438
439  # return format
440  output_format(
441    knitr = knitr_options_html(fig_width, fig_height, fig_retina, keep_md, dev),
442    pandoc = pandoc_options(to = "html",
443                            from = from_rmarkdown(fig_caption, md_extensions),
444                            args = args),
445    keep_md = keep_md,
446    clean_supporting = self_contained,
447    df_print = df_print,
448    pre_knit = pre_knit,
449    post_knit = post_knit,
450    pre_processor = pre_processor,
451    on_exit = on_exit,
452    base_format = html_document_base(smart = smart, theme = theme,
453                                     self_contained = self_contained,
454                                     lib_dir = lib_dir, mathjax = mathjax,
455                                     template = template,
456                                     pandoc_args = pandoc_args,
457                                     extra_dependencies = extra_dependencies,
458                                     ...)
459  )
460}
461
462
463#' Knitr options for an HTML output format
464#'
465#' Define knitr options for an R Markdown output format that creates
466#' HTML output.
467#'
468#' @inheritParams html_document
469#'
470#' @return An list that can be passed as the \code{knitr} argument of the
471#'   \code{\link{output_format}} function.
472#'
473#' @seealso \link{knitr_options}, \link{output_format}
474#'
475#' @export
476knitr_options_html <- function(fig_width, fig_height, fig_retina, keep_md, dev = 'png') {
477
478  opts_chunk <- list(dev = dev,
479                     dpi = 96,
480                     fig.width = fig_width,
481                     fig.height = fig_height,
482                     fig.retina = fig_retina)
483
484  if (keep_md)
485    opts_chunk$fig.retina <- NULL
486
487  knitr_options(opts_chunk = opts_chunk)
488}
489
490themes <- function() {
491  c("default",
492    "cerulean",
493    "journal",
494    "flatly",
495    "readable",
496    "spacelab",
497    "united",
498    "cosmo",
499    "lumen",
500    "paper",
501    "sandstone",
502    "simplex",
503    "yeti")
504}
505
506html_highlighters <- function() {
507  c(highlighters(), "textmate")
508}
509
510default_mathjax <- function() {
511  paste0("https://mathjax.rstudio.com/latest/", mathjax_config())
512}
513
514mathjax_config <- function() {
515  "MathJax.js?config=TeX-AMS-MML_HTMLorMML"
516}
517
518# variable which controls body offset (depends on height of navbar in theme)
519pandoc_body_padding_variable_args <- function(theme) {
520
521  # height of navbar in bootstrap 3.3.5
522  navbarHeights <- c("default" = 51,
523                     "cerulean" = 51,
524                     "journal" = 61 ,
525                     "flatly" = 60,
526                     "readable" = 66,
527                     "spacelab" = 52,
528                     "united" = 51,
529                     "cosmo" = 51,
530                     "lumen" = 54,
531                     "paper" = 64,
532                     "sandstone" = 61,
533                     "simplex" = 41,
534                     "yeti" = 45)
535
536  # body padding is navbar height
537  bodyPadding <- navbarHeights[[theme]]
538
539  # header padding is bodyPadding + 5
540  headerPadding <- bodyPadding + 5
541
542  # return variables
543  c(pandoc_variable_arg("body_padding", bodyPadding),
544    pandoc_variable_arg("header_padding", headerPadding))
545}
546
547navbar_html_from_yaml <- function(navbar_yaml) {
548
549  # parse the yaml
550  navbar <- yaml_load_file_utf8(navbar_yaml)
551
552  # generate the html
553  navbar_html(navbar)
554}
555
556
557#' Create a navbar HTML file from a navbar definition
558#'
559#' @param navbar Navbar definition
560#' @param links List of navbar links
561#' @return Path to temporary file with navbar definition
562#'
563#' @keywords internal
564#' @export
565navbar_html <- function(navbar) {
566
567  # title and type
568  if (is.null(navbar$title))
569    navbar$title <- ""
570  if (is.null(navbar$type))
571    navbar$type <- "default"
572
573  # menu entries
574  left <- navbar_links_html(navbar$left)
575  right <- navbar_links_html(navbar$right)
576
577  # build the navigation bar and return it as a temp file
578  template_file <- rmarkdown_system_file("rmd/h/_navbar.html")
579  template <- paste(readLines(template_file), collapse = "\n")
580  navbar_html <- sprintf(template,
581                         navbar$type,
582                         navbar$title,
583                         left,
584                         right)
585  as_tmpfile(navbar_html)
586}
587
588
589#' @keywords internal
590#' @name navbar_html
591#' @export
592navbar_links_html <- function(links) {
593  as.character(navbar_links_tags(links))
594}
595
596navbar_links_tags <- function(links) {
597  if (!is.null(links)) {
598    tags <- lapply(links, function(x) {
599
600      # sub-menu
601      if (!is.null(x$menu)) {
602        submenuLinks <- navbar_links_tags(x$menu)
603        tags$li(class = "dropdown",
604          tags$a(href = "#", class = "dropdown-toggle", `data-toggle` = "dropdown",
605                 role = "button", `aria-expanded` = "false",
606                   navbar_link_text(x, " ", tags$span(class = "caret"))),
607          tags$ul(class = "dropdown-menu", role = "menu", submenuLinks)
608        )
609
610      # divider
611      } else if (!is.null(x$text) && grepl("^\\s*-{3,}\\s*$", x$text)) {
612        tags$li(class = "divider")
613
614      # header
615      } else if (!is.null(x$text) && is.null(x$href)) {
616        tags$li(class = "dropdown-header", x$text)
617
618      # standard menu item
619      } else {
620        textTags <- navbar_link_text(x)
621        tags$li(tags$a(href = x$href, textTags))
622      }
623    })
624    tagList(tags)
625  } else {
626    tagList()
627  }
628}
629
630navbar_link_text <- function(x, ...) {
631  if (!is.null(x$icon)) {
632    # find the iconset
633    split <- strsplit(x$icon, "-")
634    if (length(split[[1]]) > 1)
635      iconset <- split[[1]][[1]]
636    else
637      iconset <- ""
638    tagList(tags$span(class = paste(iconset, x$icon)), " ", x$text, ...)
639  }
640  else
641    tagList(x$text, ...)
642}
643
644
645
646