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