😎 Give your xaringan slides some style
Vous ne pouvez pas sélectionner plus de 25 sujets Les noms de sujets doivent commencer par une lettre ou un nombre, peuvent contenir des tirets ('-') et peuvent comporter jusqu'à 35 caractères.

1030 lines
32KB

  1. #' A Plot Theme for ggplot2 by xaringanthemer
  2. #'
  3. #' @description
  4. #'
  5. #' **Lifecycle:** [Maturing](https://www.tidyverse.org/lifecycle/#maturing)
  6. #'
  7. #' Creates \pkg{ggplot2} themes to match the xaringanthemer theme used in the
  8. #' \pkg{xaringan} slides that seamlessly matches the "normal" slide colors and
  9. #' styles.
  10. #'
  11. #' @param text_color Color for text and foreground, inherits from `text_color`
  12. #' @param background_color Color for background, inherits from
  13. #' `background_color`
  14. #' @param accent_color Color for titles and accents, inherits from
  15. #' `header_color`
  16. #' @param accent_secondary_color Color for secondary accents, inherits from
  17. #' `text_bold_color`
  18. #' @param css_file Path to a \pkg{xaringanthemer} CSS file, from which the
  19. #' theme variables and values will be inferred. In general, if you use the
  20. #' \pkg{xaringathemer} defaults, you will not need to set this. This feature
  21. #' lets you create a \pkg{ggplot2} theme for your \pkg{xaringan} slides, even
  22. #' if you have only saved your theme CSS file and you aren't creating your
  23. #' CSS theme with \pkg{xaringanthemer} in your slides' source file.
  24. #' @inheritParams theme_xaringan_base
  25. #'
  26. #' @examples
  27. #' # Requires ggplot2
  28. #' has_ggplot2 <- requireNamespace("ggplot2", quietly = TRUE)
  29. #'
  30. #' if (has_ggplot2) {
  31. #' # Because this is an example, we'll save the CSS to a temp file
  32. #' path_to_css_file <- tempfile(fileext = ".css")
  33. #'
  34. #' # Create the xaringan theme: dark blue background with teal green accents
  35. #' style_duo(
  36. #' primary_color = "#002b36",
  37. #' secondary_color = "#31b09e",
  38. #' # Using basic fonts for this example, but the plot theme will
  39. #' # automatically use your theme font if you use Google fonts
  40. #' text_font_family = "sans",
  41. #' header_font_family = "serif",
  42. #' outfile = path_to_css_file
  43. #' )
  44. #'
  45. #' library(ggplot2)
  46. #' ggplot(iris) +
  47. #' aes(Petal.Length, Petal.Width) +
  48. #' geom_point() +
  49. #' ggtitle("Yet another Iris plot") +
  50. #' theme_xaringan()
  51. #' }
  52. #' @return A ggplot2 theme
  53. #' @family xaringanthemer ggplot2 themes
  54. #' @export
  55. theme_xaringan <- function(
  56. text_color = NULL,
  57. background_color = NULL,
  58. accent_color = NULL,
  59. accent_secondary_color = NULL,
  60. css_file = NULL,
  61. set_ggplot_defaults = TRUE,
  62. text_font = NULL,
  63. text_font_use_google = NULL,
  64. text_font_size = NULL,
  65. title_font = NULL,
  66. title_font_use_google = NULL,
  67. title_font_size = NULL,
  68. use_showtext = TRUE
  69. ) {
  70. requires_xaringanthemer_env(css_file = css_file, try_css = TRUE)
  71. requires_package(fn = "xaringan_theme")
  72. background_color <- background_color %||% xaringanthemer_env$background_color
  73. text_color <- text_color %||% xaringanthemer_env$text_color
  74. accent_color <- accent_color %||% xaringanthemer_env$header_color
  75. accent_secondary_color <- accent_secondary_color %||% xaringanthemer_env$text_bold_color %||% accent_color
  76. theme_xaringan_base(
  77. text_color,
  78. background_color,
  79. accent_color = accent_color,
  80. accent_secondary_color = accent_secondary_color,
  81. set_ggplot_defaults = set_ggplot_defaults,
  82. text_font = text_font,
  83. text_font_use_google = text_font_use_google,
  84. text_font_size = text_font_size,
  85. title_font = title_font,
  86. title_font_use_google = title_font_use_google,
  87. title_font_size = title_font_size,
  88. use_showtext = use_showtext
  89. )
  90. }
  91. #' An Inverse Plot Theme for ggplot2 by xaringanthemer
  92. #'
  93. #' @description
  94. #'
  95. #' **Lifecycle:** [Maturing](https://www.tidyverse.org/lifecycle/#maturing)
  96. #'
  97. #' A \pkg{ggplot2} xaringanthemer plot theme to seamlessly match the "inverse"
  98. #' \pkg{xaringan} slide colors and styles as styled by [xaringanthemer].
  99. #'
  100. #' @param text_color Color for text and foreground, inherits from `text_color`
  101. #' @param background_color Color for background, inherits from
  102. #' `background_color`
  103. #' @param accent_color Color for titles and accents, inherits from
  104. #' `header_color`
  105. #' @param accent_secondary_color Color for secondary accents, inherits from
  106. #' `text_bold_color`
  107. #' @inheritParams theme_xaringan
  108. #' @inheritParams theme_xaringan_base
  109. #'
  110. #' @examples
  111. #' # Requires ggplot2
  112. #' has_ggplot2 <- requireNamespace("ggplot2", quietly = TRUE)
  113. #'
  114. #' if (has_ggplot2) {
  115. #' # Because this is an example, we'll save the CSS to a temp file
  116. #' path_to_css_file <- tempfile(fileext = ".css")
  117. #'
  118. #' # Create the xaringan theme: dark blue background with teal green accents
  119. #' style_duo(
  120. #' primary_color = "#002b36",
  121. #' secondary_color = "#31b09e",
  122. #' # Using basic fonts for this example, but the plot theme will
  123. #' # automatically use your theme font if you use Google fonts
  124. #' text_font_family = "sans",
  125. #' header_font_family = "serif",
  126. #' outfile = path_to_css_file
  127. #' )
  128. #'
  129. #' library(ggplot2)
  130. #' ggplot(iris) +
  131. #' aes(Petal.Length, Petal.Width) +
  132. #' geom_point() +
  133. #' ggtitle("Yet another Iris plot") +
  134. #' # themed to match the inverse slides: teal background with dark blue text
  135. #' theme_xaringan_inverse()
  136. #' }
  137. #' @return A ggplot2 theme
  138. #' @family xaringanthemer ggplot2 themes
  139. #' @export
  140. theme_xaringan_inverse <- function(
  141. text_color = NULL,
  142. background_color = NULL,
  143. accent_color = NULL,
  144. accent_secondary_color = NULL,
  145. css_file = NULL,
  146. set_ggplot_defaults = TRUE,
  147. text_font = NULL,
  148. text_font_use_google = NULL,
  149. text_font_size = NULL,
  150. title_font = NULL,
  151. title_font_use_google = NULL,
  152. title_font_size = NULL,
  153. use_showtext = TRUE
  154. ) {
  155. requires_xaringanthemer_env(css_file = css_file, try_css = TRUE)
  156. requires_package(fn = "xaringan_theme")
  157. background_color <- background_color %||% xaringanthemer_env$inverse_background_color
  158. text_color <- text_color %||% xaringanthemer_env$inverse_text_color
  159. accent_color <- accent_color %||% xaringanthemer_env$inverse_header_color
  160. accent_secondary_color <- accent_secondary_color %||% accent_color
  161. theme_xaringan_base(
  162. text_color,
  163. background_color,
  164. accent_color = accent_color,
  165. accent_secondary_color = accent_secondary_color,
  166. set_ggplot_defaults = set_ggplot_defaults,
  167. text_font = text_font,
  168. text_font_use_google = text_font_use_google,
  169. text_font_size = text_font_size,
  170. title_font = title_font,
  171. title_font_use_google = title_font_use_google,
  172. title_font_size = title_font_size,
  173. use_showtext = use_showtext
  174. )
  175. }
  176. #' The ggplot2 xaringanthemer base plot theme
  177. #'
  178. #' @description
  179. #'
  180. #' **Lifecycle:** [Maturing](https://www.tidyverse.org/lifecycle/#maturing)
  181. #'
  182. #' Provides a base plot theme for \pkg{ggplot2} to match the \pkg{xaringan} slide theme
  183. #' created by [xaringanthemer]. The theme is designed to create a general plot
  184. #' style from two colors, a `background_color` and a `text_color` (or foreground
  185. #' color). Also accepts an `accent_color` and an `accent_secondary_color` that are
  186. #' [xaringanthemer] is not required for the base theme. Use
  187. #' [theme_xaringan()] or [theme_xaringan_inverse()] in xaringan slides styled by
  188. #' xaringanthemer for a plot theme that matches the slide style.
  189. #'
  190. #' @param text_color Color for text and foreground
  191. #' @param background_color Color for background
  192. #' @param accent_color Color for titles and accents, inherits from
  193. #' `header_color` or `text_color`. Used for the `title` base setting in
  194. #' [ggplot2::theme()], and additionally for setting the `color` or `fill` of
  195. #' \pkg{ggplot2} geom defaults.
  196. #' @param accent_secondary_color Color for secondary accents, inherits from
  197. #' `text_bold_color` or `accent_color`. Used only when setting \pkg{ggplot2} geom
  198. #' defaults.
  199. #' @param set_ggplot_defaults Should defaults be set for \pkg{ggplot2} _geoms_?
  200. #' Defaults to TRUE. To restore ggplot's defaults, or the previously set geom
  201. #' defaults, see [theme_xaringan_restore_defaults()].
  202. #' @param text_font Font to use for text elements, passed to
  203. #' [sysfonts::font_add_google()], if available and `text_font_use_google` is
  204. #' `TRUE`. Inherits from `text_font_family`. If manually specified, can be a
  205. #' [google_font()].
  206. #' @param text_font_use_google Is `text_font` available on [Google
  207. #' Fonts](https://fonts.google.com)?
  208. #' @param text_font_size Base text font size, inherits from `text_font_size`, or
  209. #' defaults to 11.
  210. #' @param title_font Font to use for title elements, passed to
  211. #' [sysfonts::font_add_google()], if available and `title_font_use_google` is
  212. #' `TRUE`. Inherits from `title_font_family`. If manually specified, can be a
  213. #' [google_font()].
  214. #' @param title_font_use_google Is `title_font` available on [Google
  215. #' Fonts](https://fonts.google.com)?
  216. #' @param title_font_size Base text font size, inherits from `title_font_size`,
  217. #' or defaults to 14.
  218. #' @param use_showtext If `TRUE` (default) the \pkg{showtext} package will be
  219. #' used to register Google fonts. Set to `FALSE` to disable this feature
  220. #' entirely, which may result in errors during plotting if the fonts used are
  221. #' not available locally.
  222. #' @param ... Ignored
  223. #'
  224. #' @examples
  225. #' # Requires ggplot2
  226. #' has_ggplot2 <- requireNamespace("ggplot2", quietly = TRUE)
  227. #'
  228. #' if (has_ggplot2) {
  229. #' library(ggplot2)
  230. #'
  231. #' plot1 <- ggplot(iris) +
  232. #' aes(Petal.Length, Petal.Width) +
  233. #' geom_point() +
  234. #' theme_xaringan_base(
  235. #' text_color = "#602f6b", # imperial
  236. #' background_color = "#f8f8f8", # light gray
  237. #' accent_color = "#317873", # myrtle green
  238. #' title_font = "sans",
  239. #' text_font = "serif",
  240. #' set_ggplot_defaults = TRUE
  241. #' ) +
  242. #' labs(
  243. #' title = "Basic Iris Plot",
  244. #' subtitle = "+ theme_xaringan_base()",
  245. #' caption = "xaringanthemer"
  246. #' )
  247. #'
  248. #' print(plot1)
  249. #'
  250. #' plot2 <- ggplot(iris) +
  251. #' aes(Sepal.Width) +
  252. #' geom_histogram(binwidth = 0.1) +
  253. #' theme_xaringan_base(
  254. #' text_color = "#a8a9c8", # light purple
  255. #' background_color = "#303163", # deep slate purple
  256. #' accent_color = "#ffff99", # canary yellow
  257. #' title_font = "sans",
  258. #' text_font = "serif",
  259. #' set_ggplot_defaults = TRUE
  260. #' ) +
  261. #' labs(
  262. #' title = "Basic Iris Plot",
  263. #' subtitle = "+ theme_xaringan_base()",
  264. #' caption = "xaringanthemer"
  265. #' )
  266. #'
  267. #' print(plot2)
  268. #' }
  269. #' @return A ggplot2 theme
  270. #' @family xaringanthemer ggplot2 themes
  271. #' @export
  272. theme_xaringan_base <- function(
  273. text_color,
  274. background_color,
  275. ...,
  276. set_ggplot_defaults = TRUE,
  277. accent_color = NULL,
  278. accent_secondary_color = NULL,
  279. text_font = NULL,
  280. text_font_use_google = NULL,
  281. text_font_size = NULL,
  282. title_font = NULL,
  283. title_font_use_google = NULL,
  284. title_font_size = NULL,
  285. use_showtext = TRUE
  286. ) {
  287. text_color <- full_length_hex(text_color)
  288. background_color <- full_length_hex(background_color)
  289. blend <- color_blender(text_color, background_color)
  290. text_font_size <- text_font_size %||% web_to_point(xaringanthemer_env$text_font_size, scale = 1.25) %||% 11
  291. title_font_size <- title_font_size %||% web_to_point(xaringanthemer_env$header_h3_font_size, scale = 0.8) %||% 14
  292. text_font_use_google <- text_font_use_google %||% is_google_font(text_font)
  293. title_font_use_google <- title_font_use_google %||% is_google_font(title_font)
  294. text_font <- if (!is.null(text_font)) {
  295. register_font(text_font, identical(text_font_use_google, TRUE) && use_showtext)
  296. } else {
  297. get_theme_font("text")
  298. }
  299. title_font <- if (!is.null(title_font)) {
  300. register_font(title_font, identical(title_font_use_google, TRUE) && use_showtext)
  301. } else {
  302. get_theme_font("header")
  303. }
  304. text_font <- text_font %||% "sans"
  305. title_font <- title_font %||% "sans"
  306. if (set_ggplot_defaults) {
  307. accent_color <- accent_color %||% xaringanthemer_env$header_color %||% text_color
  308. accent_secondary_color <- accent_secondary_color %||% xaringanthemer_env$text_bold_color %||% accent_color
  309. accent_color <- full_length_hex(accent_color)
  310. accent_secondary_color <- full_length_hex(accent_secondary_color)
  311. theme_xaringan_set_defaults(
  312. text_color = text_color,
  313. background_color = background_color,
  314. accent_color = accent_color,
  315. accent_secondary_color = accent_secondary_color,
  316. text_font = text_font
  317. )
  318. }
  319. theme <- ggplot2::theme(
  320. line = ggplot2::element_line(color = blend(0.2)),
  321. rect = ggplot2::element_rect(fill = background_color),
  322. text = ggplot2::element_text(
  323. color = blend(0.1),
  324. family = text_font,
  325. size = text_font_size
  326. ),
  327. title = ggplot2::element_text(
  328. color = accent_color,
  329. family = title_font,
  330. size = title_font_size
  331. ),
  332. plot.background = ggplot2::element_rect(
  333. fill = background_color,
  334. color = background_color
  335. ),
  336. panel.background = ggplot2::element_rect(
  337. fill = background_color,
  338. color = background_color
  339. ),
  340. panel.grid.major = ggplot2::element_line(
  341. color = blend(0.8),
  342. inherit.blank = TRUE
  343. ),
  344. panel.grid.minor = ggplot2::element_line(
  345. color = blend(0.9),
  346. inherit.blank = TRUE
  347. ),
  348. axis.title = ggplot2::element_text(size = title_font_size * 0.8),
  349. axis.ticks = ggplot2::element_line(color = blend(0.8)),
  350. axis.text = ggplot2::element_text(color = blend(0.4)),
  351. legend.key = ggplot2::element_rect(fill = "transparent", colour = NA),
  352. plot.caption = ggplot2::element_text(
  353. size = text_font_size * 0.8,
  354. color = blend(0.3)
  355. )
  356. )
  357. if (utils::packageVersion("ggplot2") >= package_version("3.3.0")) {
  358. theme + ggplot2::theme(plot.title.position = "plot")
  359. } else theme
  360. }
  361. #' Set and Restore ggplot2 geom Defaults
  362. #'
  363. #' @description
  364. #'
  365. #' **Lifecycle:** [Maturing](https://www.tidyverse.org/lifecycle/#maturing)
  366. #'
  367. #' Set \pkg{ggplot2} _geom_ defaults to match [theme_xaringan()] with
  368. #' `theme_xaringan_set_defaults()` and restore the standard or previously-set
  369. #' defaults with `theme_xaringan_restore_defaults()`. By default,
  370. #' `theme_xaringan_set_defaults()` is run with [theme_xaringan()] or
  371. #' [theme_xaringan_inverse()].
  372. #'
  373. #' @family xaringanthemer ggplot2 themes
  374. #' @param text_font Font to use for text elements, passed to
  375. #' [sysfonts::font_add_google()], if available and `text_font_use_google` is
  376. #' `TRUE`. Inherits from `text_font_family`. Must be a length-one character.
  377. #' @inheritParams theme_xaringan
  378. #' @inheritParams theme_xaringan_base
  379. #' @return Invisibly returns a list of the current ggplot2 geom defaults
  380. #' @export
  381. theme_xaringan_set_defaults <- function(
  382. text_color = NULL,
  383. background_color = NULL,
  384. accent_color = text_color,
  385. accent_secondary_color = accent_color,
  386. text_font = NULL
  387. ) {
  388. requires_package("ggplot2")
  389. text_font %||% stopifnot(is.character(text_font) && length(text_font) == 1)
  390. blend <- color_blender(text_color, background_color)
  391. xaringan_theme_defaults <- list(
  392. "line" = list(color = text_color),
  393. "vline" = list(color = accent_secondary_color),
  394. "hline" = list(color = accent_secondary_color),
  395. "abline" = list(color = accent_secondary_color),
  396. "segment" = list(color = text_color),
  397. "bar" = list(fill = accent_color),
  398. "col" = list(fill = accent_color),
  399. "boxplot" = list(color = text_color),
  400. "contour" = list(color = text_color),
  401. "density" = list(color = text_color,
  402. fill = text_color,
  403. alpha = 0.1),
  404. "dotplot" = list(color = accent_color),
  405. "errorbarh" = list(color = text_color),
  406. "crossbar" = list(color = text_color),
  407. "errorbar" = list(color = text_color),
  408. "linerange" = list(color = text_color),
  409. "pointrange" = list(color = text_color),
  410. "map" = list(color = text_color),
  411. "path" = list(color = text_color),
  412. "line" = list(color = text_color),
  413. "step" = list(color = text_color),
  414. "point" = list(color = accent_color),
  415. "polygon" = list(color = accent_color,
  416. fill = accent_color),
  417. "quantile" = list(color = text_color),
  418. "rug" = list(color = blend(0.5)),
  419. "segment" = list(color = text_color),
  420. "smooth" = list(fill = blend(0.75),
  421. color = accent_secondary_color),
  422. "spoke" = list(color = text_color),
  423. "label" = list(color = text_color,
  424. family= text_font %||% get_theme_font("text")),
  425. "text" = list(color = text_color,
  426. family= text_font %||% get_theme_font("text")),
  427. "rect" = list(fill = text_color),
  428. "tile" = list(fill = text_color),
  429. "violin" = list(fill = text_color),
  430. "sf" = list(color = text_color)
  431. )
  432. geom_names <- purrr::set_names(names(xaringan_theme_defaults))
  433. previous_defaults <- lapply(
  434. geom_names,
  435. function(geom) safely_set_geom(geom, xaringan_theme_defaults[[geom]])
  436. )
  437. if (is.null(xaringanthemer_env$old_ggplot_defaults)) {
  438. xaringanthemer_env$old_ggplot_defaults <- previous_defaults
  439. }
  440. invisible(previous_defaults)
  441. }
  442. #' @describeIn theme_xaringan_set_defaults Restore previous or standard
  443. #' \pkg{ggplot2} _geom_ defaults.
  444. #' @return Invisibly returns a list of the current ggplot2 geom defaults
  445. #' @export
  446. theme_xaringan_restore_defaults <- function() {
  447. requires_package("ggplot2")
  448. requires_xaringanthemer_env(try_css = FALSE, requires_theme = FALSE)
  449. if (is.null(xaringanthemer_env$old_ggplot_defaults)) {
  450. return(invisible())
  451. }
  452. old_default <- xaringanthemer_env$old_ggplot_defaults
  453. old_default_not_std <- vapply(old_default, function(x) length(x) > 0, logical(1))
  454. old_default <- old_default[old_default_not_std]
  455. restore_default <- utils::modifyList(xaringanthemer_env$std_ggplot_defaults, old_default)
  456. geom_names <- purrr::set_names(names(restore_default))
  457. previous_defaults <- lapply(
  458. geom_names,
  459. function(geom) safely_set_geom(geom, restore_default[[geom]])
  460. )
  461. invisible(previous_defaults)
  462. }
  463. safely_set_geom <- function(geom, new) {
  464. warn <- function(x) {
  465. rlang::warn(x$message)
  466. invisible()
  467. }
  468. tryCatch(
  469. {
  470. ggplot2::update_geom_defaults(geom, new)
  471. },
  472. error = warn,
  473. warning = warn
  474. )
  475. }
  476. # Color Scales ------------------------------------------------------------
  477. #' Xaringan Themer ggplot2 Scales
  478. #'
  479. #' @description
  480. #'
  481. #' **Lifecycle:** [Maturing](https://www.tidyverse.org/lifecycle/#maturing)
  482. #'
  483. #' Color and fill single-color scales for discrete and continuous values,
  484. #' created using the primary accent color of the xaringanthemer styles.
  485. #'
  486. #' @param ... Arguments passed on to either the \pkg{colorspace} scale
  487. #' functions — one of [colorspace::scale_color_discrete_sequential],
  488. #' [colorspace::scale_color_continuous_sequential],
  489. #' [colorspace::scale_fill_discrete_sequential], or
  490. #' [colorspace::scale_fill_continuous_sequential] — or to
  491. #' [ggplot2::continuous_scale] or [ggplot2::discrete_scale].
  492. #' @param color A color value, in hex, to override the default color. Otherwise,
  493. #' the primary color of the resulting scale is chosen from the xaringanthemer
  494. #' slide styles.
  495. #' @param inverse If `color` is not supplied and `inverse = TRUE`, a primary
  496. #' color is chosen to work well with the inverse slide styles, namely the
  497. #' value of `inverse_header_color`
  498. #' @param direction Direction of the discrete scale. Use values less than 0 to
  499. #' reverse the direction, e.g. `direction = -1`.
  500. #' @inheritParams colorspace::scale_color_continuous_sequential
  501. #' @param aes_type The type of aesthetic to which the scale is being applied.
  502. #' One of "color", "colour", or "fill".
  503. #'
  504. #'
  505. #' @examples
  506. #' # Requires ggplot2
  507. #' has_ggplot2 <- requireNamespace("ggplot2", quietly = TRUE)
  508. #'
  509. #' if (has_ggplot2) {
  510. #' library(ggplot2)
  511. #' # Saving the theme to a temp file because this is an example
  512. #' path_to_css_file <- tempfile(fileext = ".css")
  513. #'
  514. #' # Create the xaringan theme: dark blue background with teal green accents
  515. #' style_duo(
  516. #' primary_color = "#002b36",
  517. #' secondary_color = "#31b09e",
  518. #' # Using basic fonts for this example, but the plot theme will
  519. #' # automatically use your theme font if you use Google fonts
  520. #' text_font_family = "sans",
  521. #' header_font_family = "serif",
  522. #' outfile = path_to_css_file
  523. #' )
  524. #'
  525. #' # Here's some very basic example data
  526. #' ex <- data.frame(
  527. #' name = c("Couple", "Few", "Lots", "Many"),
  528. #' n = c(2, 3, 5, 7)
  529. #' )
  530. #'
  531. #' # Fill color scales demo
  532. #' ggplot(ex) +
  533. #' aes(name, n, fill = n) +
  534. #' geom_col() +
  535. #' ggtitle("Matching fill scales") +
  536. #' # themed to match the slides: dark blue background with teal text
  537. #' theme_xaringan() +
  538. #' # Fill color matches teal text
  539. #' scale_xaringan_fill_continuous()
  540. #'
  541. #' # Color scales demo
  542. #' ggplot(ex) +
  543. #' aes(name, y = 1, color = name) +
  544. #' geom_point(size = 10) +
  545. #' ggtitle("Matching color scales") +
  546. #' # themed to match the slides: dark blue background with teal text
  547. #' theme_xaringan() +
  548. #' # Fill color matches teal text
  549. #' scale_xaringan_color_discrete(direction = -1)
  550. #' }
  551. #'
  552. #' @name scale_xaringan
  553. NULL
  554. #' @rdname scale_xaringan
  555. #' @export
  556. scale_xaringan_discrete <- function(
  557. aes_type = c("color", "colour", "fill"),
  558. ...,
  559. color = NULL,
  560. direction = 1,
  561. inverse = FALSE
  562. ) {
  563. requires_package("ggplot2", "scale_xaringan_discrete")
  564. aes_type <- match.arg(aes_type)
  565. color <- hex2HCL(get_theme_accent_color(color, inverse))
  566. pal <- function(n) {
  567. colors <- colorspace::sequential_hcl(
  568. n = n,
  569. c1 = color[1, "C"],
  570. l1 = color[1, "L"],
  571. h1 = color[1, "H"],
  572. rev = direction >= 1
  573. )
  574. }
  575. ggplot2::discrete_scale(aes_type, "manual", pal, ...)
  576. }
  577. #' @rdname scale_xaringan
  578. #' @export
  579. scale_xaringan_fill_discrete <- function(
  580. ...,
  581. color = NULL,
  582. direction = 1,
  583. inverse = FALSE
  584. ) {
  585. scale_xaringan_discrete(
  586. "fill",
  587. ...,
  588. color = color,
  589. direction = direction,
  590. inverse = inverse
  591. )
  592. }
  593. #' @rdname scale_xaringan
  594. #' @export
  595. scale_xaringan_color_discrete <- function(
  596. ...,
  597. color = NULL,
  598. direction = 1,
  599. inverse = FALSE
  600. ) {
  601. scale_xaringan_discrete(
  602. "color",
  603. ...,
  604. color = color,
  605. direction = direction,
  606. inverse = inverse
  607. )
  608. }
  609. #' @rdname scale_xaringan
  610. #' @export
  611. scale_xaringan_colour_discrete <- scale_xaringan_color_discrete
  612. #' @rdname scale_xaringan
  613. #' @export
  614. scale_xaringan_continuous <- function(
  615. aes_type = c("color", "colour", "fill"),
  616. ...,
  617. color = NULL,
  618. begin = 0,
  619. end = 1,
  620. inverse = FALSE
  621. ) {
  622. requires_package("ggplot2", "scale_xaringan_continuous")
  623. requires_package("scales", "scale_xaringan_continuous")
  624. aes_type <- match.arg(aes_type)
  625. color <- hex2HCL(get_theme_accent_color(color, inverse))
  626. colors <- suppressWarnings(colorspace::sequential_hcl(
  627. n = 12,
  628. c1 = color[1, "C"],
  629. l1 = color[1, "L"],
  630. h1 = color[1, "H"],
  631. rev = TRUE
  632. ))
  633. rescaler <- function(x, ...) {
  634. scales::rescale(x, to = c(begin, end), from = range(x, na.rm = TRUE))
  635. }
  636. ggplot2::continuous_scale(
  637. aes_type,
  638. "continuous_sequential",
  639. palette = scales::gradient_n_pal(colors, values = NULL),
  640. rescaler = rescaler,
  641. oob = scales::censor,
  642. ...
  643. )
  644. }
  645. #' @rdname scale_xaringan
  646. #' @export
  647. scale_xaringan_fill_continuous <- function(
  648. ...,
  649. color = NULL,
  650. begin = 0,
  651. end = 1,
  652. inverse = FALSE
  653. ) {
  654. scale_xaringan_continuous(
  655. "fill",
  656. ...,
  657. color = color,
  658. begin = begin,
  659. end = end,
  660. inverse = inverse
  661. )
  662. }
  663. #' @rdname scale_xaringan
  664. #' @export
  665. scale_xaringan_color_continuous <- function(
  666. ...,
  667. color = NULL,
  668. begin = 0,
  669. end = 1,
  670. inverse = FALSE
  671. ) {
  672. scale_xaringan_continuous(
  673. "color",
  674. ...,
  675. color = color,
  676. begin = begin,
  677. end = end,
  678. inverse = inverse
  679. )
  680. }
  681. #' @rdname scale_xaringan
  682. #' @export
  683. scale_xaringan_colour_continuous <- scale_xaringan_color_continuous
  684. get_theme_accent_color <- function(color = NULL, inverse = FALSE) {
  685. color <-
  686. if (!inverse) {
  687. color %||%
  688. xaringanthemer_env[["header_color"]] %||%
  689. xaringanthemer_env[["text_color"]]
  690. } else {
  691. color %||% xaringanthemer_env[["inverse_header_color"]]
  692. }
  693. if (is.null(color)) {
  694. stop(
  695. call. = FALSE,
  696. "No color provided and no default available. ",
  697. "Have you forgotten to use a style function to set the xaringan theme?"
  698. )
  699. }
  700. color
  701. }
  702. blend_colors <- function(x, y, alpha = 0.5) {
  703. x <- colorspace::hex2RGB(x)
  704. y <- colorspace::hex2RGB(y)
  705. z <- colorspace::mixcolor(alpha, x, y)
  706. colorspace::hex(z)
  707. }
  708. color_blender <- function(x, y) function(alpha = 0.5) blend_colors(x, y, alpha)
  709. hex2HCL <- function(x) {
  710. colorspace::coords(methods::as(colorspace::hex2RGB(x), "polarLUV"))
  711. }
  712. # Fonts -------------------------------------------------------------------
  713. get_theme_font <- function(element = c("text", "header", "code"), use_showtext = TRUE) {
  714. element <- match.arg(element)
  715. element_family <- paste0(element, "_font_family")
  716. element_google <- paste0(element, "_font_google")
  717. element_is_google <- paste0(element, "_font_is_google")
  718. element_url <- paste0(element, "_font_url")
  719. family <- xaringanthemer_env[[element_family]]
  720. is_google_font <- xaringanthemer_env[[element_is_google]]
  721. if (is.null(is_google_font)) {
  722. is_google_font <- !is.null(xaringanthemer_env[[element_google]]) ||
  723. grepl("fonts.google", xaringanthemer_env[[element_url]], fixed = TRUE)
  724. }
  725. register_font(
  726. family,
  727. google = is_google_font,
  728. fn = sys.calls()[[max(1, sys.nframe() - 1)]][[1]],
  729. use_showtext = use_showtext
  730. )
  731. }
  732. register_font <- function(
  733. family,
  734. google = TRUE,
  735. fn = sys.calls()[[max(1, sys.nframe() - 1)]][[1]],
  736. ...,
  737. use_showtext = TRUE
  738. ) {
  739. if (is.null(family) || !use_showtext) {
  740. return(NULL)
  741. }
  742. if (is_google_font(family)) family <- family$family
  743. family <- gsub("['\"]", "", family)
  744. if (!identical(xaringanthemer_env$showtext_auto, TRUE)) {
  745. if (!requires_package(pkg = "showtext", fn, required = FALSE)) {
  746. return(family)
  747. }
  748. showtext::showtext_auto()
  749. xaringanthemer_env$showtext_auto <- TRUE
  750. }
  751. if (family %in% xaringanthemer_env[["registered_font_families"]] %||% "") {
  752. return(family)
  753. }
  754. if (!requires_package(pkg = "sysfonts", fn, required = FALSE)) {
  755. return(family)
  756. } else if (family == "Droid Serif") {
  757. dstmp <- tempfile("droid-serif", fileext = "ttf")
  758. utils::download.file(
  759. "https://github.com/google/fonts/raw/feb15862e0c66ec0e7531ca4c3ef2607071ea700/apache/droidserif/DroidSerif-Regular.ttf",
  760. dstmp,
  761. quiet = TRUE
  762. )
  763. sysfonts::font_add(
  764. family = "Droid Serif",
  765. regular = dstmp
  766. )
  767. } else if (!family %in% sysfonts::font_families()) {
  768. is_default_font <- family %in% c(
  769. "Roboto",
  770. "Source Code Pro",
  771. "Yanone Kaffeesatz"
  772. )
  773. font_found <- family %in% sysfonts::font_families()
  774. is_google_font <- identical(google, TRUE) || (missing(google) && is_default_font)
  775. if (is_google_font) {
  776. tryCatch(
  777. {
  778. sysfonts::font_add_google(family, ...)
  779. font_found <- TRUE
  780. },
  781. error = function(e) {},
  782. warning = function(w) {}
  783. )
  784. }
  785. if (!font_found) { # warn user if font still not found
  786. msg <- if (is_google_font) glue::glue(
  787. "Font '{family}' not found in Google Fonts. ",
  788. "Please manually register the font using `sysfonts::font_add()`."
  789. ) else {
  790. glue::glue(
  791. "Font '{family}' must be manually registered using `sysfonts::font_add()`."
  792. )
  793. }
  794. warning(str_wrap(msg), call. = FALSE)
  795. } else {
  796. verify_fig_showtext(fn)
  797. }
  798. }
  799. xaringanthemer_env[["registered_font_families"]] <- c(
  800. xaringanthemer_env[["registered_font_families"]],
  801. family
  802. )
  803. family
  804. }
  805. verify_fig_showtext <- function(fn = "theme_xaringan_base") {
  806. if (is.null(knitr::current_input())) return()
  807. # Try to set fig.showtext automatically
  808. if (isTRUE(knitr::opts_current$get("fig.showtext"))) {
  809. return()
  810. }
  811. stop(str_wrap(
  812. "To use ", fn, "() with knitr, you need to set the chunk option ",
  813. "`fig.showtext = TRUE` for this chunk. Or you can set this option ",
  814. "globally with `knitr::opts_chunk$set(fig.showtext = TRUE)`."
  815. ))
  816. }
  817. requires_xaringanthemer_env <- function(
  818. css_file = NULL,
  819. try_css = TRUE,
  820. requires_theme = TRUE
  821. ) {
  822. reload <- !is.null(css_file) && isTRUE(try_css)
  823. pkg_env_exists <- exists("xaringanthemer_env")
  824. missing_theme <- requires_theme && pkg_env_exists && is.null(xaringanthemer_env$header_color)
  825. if (reload || !pkg_env_exists || missing_theme) {
  826. if (try_css) {
  827. css_vars <- read_css_vars(css_file)
  828. for (css_var in names(css_vars)) {
  829. xaringanthemer_env[[css_var]] <- css_vars[[css_var]]
  830. }
  831. return(requires_xaringanthemer_env(try_css = FALSE))
  832. } else {
  833. stop("Please call a xaringanthemer theme function first.")
  834. }
  835. }
  836. }
  837. #' Get the Value of xaringanthemer Style Setting
  838. #'
  839. #' A helper function to retrieve the value of style settings as set by a
  840. #' xaringanthemer style function, for use in plotting and other circumstances.
  841. #'
  842. #' @section Style Settings:
  843. #' Style settings used by xaringanthemer include:
  844. #'
  845. #' - `background_color`
  846. #' - `background_image`
  847. #' - `background_position`
  848. #' - `background_size`
  849. #' - `blockquote_left_border_color`
  850. #' - `code_font_family`
  851. #' - `code_font_family_fallback`
  852. #' - `code_font_google`
  853. #' - `code_font_is_google`
  854. #' - `code_font_size`
  855. #' - `code_font_url`
  856. #' - `code_highlight_color`
  857. #' - `code_inline_background_color`
  858. #' - `code_inline_color`
  859. #' - `code_inline_font_size`
  860. #' - `extra_css`
  861. #' - `extra_fonts`
  862. #' - `footnote_color`
  863. #' - `footnote_font_size`
  864. #' - `footnote_position_bottom`
  865. #' - `header_background_auto`
  866. #' - `header_background_color`
  867. #' - `header_background_content_padding_top`
  868. #' - `header_background_ignore_classes`
  869. #' - `header_background_padding`
  870. #' - `header_background_text_color`
  871. #' - `header_color`
  872. #' - `header_font_family`
  873. #' - `header_font_google`
  874. #' - `header_font_is_google`
  875. #' - `header_font_url`
  876. #' - `header_font_weight`
  877. #' - `header_h1_font_size`
  878. #' - `header_h2_font_size`
  879. #' - `header_h3_font_size`
  880. #' - `inverse_background_color`
  881. #' - `inverse_header_color`
  882. #' - `inverse_text_color`
  883. #' - `inverse_text_shadow`
  884. #' - `left_column_selected_color`
  885. #' - `left_column_subtle_color`
  886. #' - `link_color`
  887. #' - `padding`
  888. #' - `table_border_color`
  889. #' - `table_row_border_color`
  890. #' - `table_row_even_background_color`
  891. #' - `text_bold_color`
  892. #' - `text_color`
  893. #' - `text_font_base`
  894. #' - `text_font_family`
  895. #' - `text_font_family_fallback`
  896. #' - `text_font_google`
  897. #' - `text_font_is_google`
  898. #' - `text_font_size`
  899. #' - `text_font_url`
  900. #' - `text_font_weight`
  901. #' - `text_slide_number_color`
  902. #' - `text_slide_number_font_size`
  903. #' - `title_slide_background_color`
  904. #' - `title_slide_background_image`
  905. #' - `title_slide_background_position`
  906. #' - `title_slide_background_size`
  907. #' - `title_slide_text_color`
  908. #'
  909. #' @param setting A xaringanthemer style setting
  910. #' @inheritParams theme_xaringan
  911. #' @examples
  912. #' # Create a xaringanthemer style in a temporary file for this example
  913. #' xaringan_themer_css <- tempfile("xaringan-themer", fileext = ".css")
  914. #'
  915. #' style_solarized_light(outfile = xaringan_themer_css)
  916. #'
  917. #' theme_xaringan_get_value("text_color")
  918. #' theme_xaringan_get_value("background_color")
  919. #' theme_xaringan_get_value("header_color")
  920. #' theme_xaringan_get_value("text_bold_color")
  921. #'
  922. #' @export
  923. theme_xaringan_get_value <- function(setting, css_file = NULL) {
  924. requires_xaringanthemer_env(css_file = css_file)
  925. if (length(setting) > 1) {
  926. ret <- list()
  927. for (var in setting) {
  928. ret[[var]] <- xaringanthemer_env[[var]]
  929. }
  930. return(ret)
  931. }
  932. xaringanthemer_env[[setting]]
  933. }
  934. web_to_point <- function(x, px_per_em = NULL, scale = 0.75) {
  935. if (is.null(x)) {
  936. return(NULL)
  937. }
  938. px_per_em <- px_per_em %||% get_base_font_size()
  939. if (grepl("pt$", x)) {
  940. return(as.numeric(sub("pt$", "", x)))
  941. } else if (grepl("px$", x)) {
  942. x <- as.numeric(sub("px$", "", x))
  943. return(x * scale)
  944. } else if (grepl("r?em$", x)) {
  945. x <- as.numeric(sub("r?em$", "", x))
  946. return(x * px_per_em * scale)
  947. } else {
  948. return()
  949. }
  950. }
  951. get_base_font_size <- function() {
  952. base_size <- xaringanthemer_env[["base_font_size"]] %||%
  953. xaringanthemer_env[["text_font_size"]]
  954. if (!grepl("px", base_size)) {
  955. # assume 16px base font size
  956. 16
  957. } else {
  958. as.numeric(sub("px", "", base_size))
  959. }
  960. }