😎 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.

1067 lines
33KB

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