😎 Give your xaringan slides some style
Ви не можете вибрати більше 25 тем Теми мають розпочинатися з літери або цифри, можуть містити дефіси (-) і не повинні перевищувати 35 символів.

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. #'
  559. #' @name scale_xaringan
  560. NULL
  561. #' @rdname scale_xaringan
  562. #' @export
  563. scale_xaringan_discrete <- function(
  564. aes_type = c("color", "colour", "fill"),
  565. ...,
  566. color = NULL,
  567. direction = 1,
  568. inverse = FALSE
  569. ) {
  570. requires_package("ggplot2", "scale_xaringan_discrete")
  571. aes_type <- match.arg(aes_type)
  572. color <- hex2HCL(get_theme_accent_color(color, inverse))
  573. pal <- function(n) {
  574. colors <- colorspace::sequential_hcl(
  575. n = n,
  576. c1 = color[1, "C"],
  577. l1 = color[1, "L"],
  578. h1 = color[1, "H"],
  579. rev = direction >= 1
  580. )
  581. }
  582. ggplot2::discrete_scale(aes_type, "manual", pal, ...)
  583. }
  584. #' @rdname scale_xaringan
  585. #' @export
  586. scale_xaringan_fill_discrete <- function(
  587. ...,
  588. color = NULL,
  589. direction = 1,
  590. inverse = FALSE
  591. ) {
  592. scale_xaringan_discrete(
  593. "fill",
  594. ...,
  595. color = color,
  596. direction = direction,
  597. inverse = inverse
  598. )
  599. }
  600. #' @rdname scale_xaringan
  601. #' @export
  602. scale_xaringan_color_discrete <- function(
  603. ...,
  604. color = NULL,
  605. direction = 1,
  606. inverse = FALSE
  607. ) {
  608. scale_xaringan_discrete(
  609. "color",
  610. ...,
  611. color = color,
  612. direction = direction,
  613. inverse = inverse
  614. )
  615. }
  616. #' @rdname scale_xaringan
  617. #' @export
  618. scale_xaringan_colour_discrete <- scale_xaringan_color_discrete
  619. #' @rdname scale_xaringan
  620. #' @export
  621. scale_xaringan_continuous <- function(
  622. aes_type = c("color", "colour", "fill"),
  623. ...,
  624. color = NULL,
  625. begin = 0,
  626. end = 1,
  627. inverse = FALSE
  628. ) {
  629. requires_package("ggplot2", "scale_xaringan_continuous")
  630. requires_package("scales", "scale_xaringan_continuous")
  631. aes_type <- match.arg(aes_type)
  632. color <- hex2HCL(get_theme_accent_color(color, inverse))
  633. colors <- suppressWarnings(colorspace::sequential_hcl(
  634. n = 12,
  635. c1 = color[1, "C"],
  636. l1 = color[1, "L"],
  637. h1 = color[1, "H"],
  638. rev = TRUE
  639. ))
  640. rescaler <- function(x, ...) {
  641. scales::rescale(x, to = c(begin, end), from = range(x, na.rm = TRUE))
  642. }
  643. ggplot2::continuous_scale(
  644. aes_type,
  645. "continuous_sequential",
  646. palette = scales::gradient_n_pal(colors, values = NULL),
  647. rescaler = rescaler,
  648. oob = scales::censor,
  649. ...
  650. )
  651. }
  652. #' @rdname scale_xaringan
  653. #' @export
  654. scale_xaringan_fill_continuous <- function(
  655. ...,
  656. color = NULL,
  657. begin = 0,
  658. end = 1,
  659. inverse = FALSE
  660. ) {
  661. scale_xaringan_continuous(
  662. "fill",
  663. ...,
  664. color = color,
  665. begin = begin,
  666. end = end,
  667. inverse = inverse
  668. )
  669. }
  670. #' @rdname scale_xaringan
  671. #' @export
  672. scale_xaringan_color_continuous <- function(
  673. ...,
  674. color = NULL,
  675. begin = 0,
  676. end = 1,
  677. inverse = FALSE
  678. ) {
  679. scale_xaringan_continuous(
  680. "color",
  681. ...,
  682. color = color,
  683. begin = begin,
  684. end = end,
  685. inverse = inverse
  686. )
  687. }
  688. #' @rdname scale_xaringan
  689. #' @export
  690. scale_xaringan_colour_continuous <- scale_xaringan_color_continuous
  691. get_theme_accent_color <- function(color = NULL, inverse = FALSE) {
  692. color <-
  693. if (!inverse) {
  694. color %||%
  695. xaringanthemer_env[["header_color"]] %||%
  696. xaringanthemer_env[["text_color"]]
  697. } else {
  698. color %||% xaringanthemer_env[["inverse_header_color"]]
  699. }
  700. if (is.null(color)) {
  701. stop(
  702. call. = FALSE,
  703. "No color provided and no default available. ",
  704. "Have you forgotten to use a style function to set the xaringan theme?"
  705. )
  706. }
  707. color
  708. }
  709. blend_colors <- function(x, y, alpha = 0.5) {
  710. x <- colorspace::hex2RGB(x)
  711. y <- colorspace::hex2RGB(y)
  712. z <- colorspace::mixcolor(alpha, x, y)
  713. colorspace::hex(z)
  714. }
  715. color_blender <- function(x, y) function(alpha = 0.5) blend_colors(x, y, alpha)
  716. hex2HCL <- function(x) {
  717. colorspace::coords(methods::as(colorspace::hex2RGB(x), "polarLUV"))
  718. }
  719. # Fonts -------------------------------------------------------------------
  720. get_theme_font <- function(element = c("text", "header", "code"), use_showtext = TRUE) {
  721. element <- match.arg(element)
  722. element_family <- paste0(element, "_font_family")
  723. element_google <- paste0(element, "_font_google")
  724. element_is_google <- paste0(element, "_font_is_google")
  725. element_url <- paste0(element, "_font_url")
  726. family <- xaringanthemer_env[[element_family]]
  727. is_google_font <- xaringanthemer_env[[element_is_google]]
  728. if (is.null(is_google_font)) {
  729. is_google_font <- !is.null(xaringanthemer_env[[element_google]]) ||
  730. grepl("fonts.google", xaringanthemer_env[[element_url]], fixed = TRUE)
  731. }
  732. register_font(
  733. family,
  734. google = is_google_font,
  735. fn = sys.calls()[[max(1, sys.nframe() - 1)]][[1]],
  736. use_showtext = use_showtext
  737. )
  738. }
  739. register_font <- function(
  740. family,
  741. google = TRUE,
  742. fn = sys.calls()[[max(1, sys.nframe() - 1)]][[1]],
  743. ...,
  744. use_showtext = TRUE
  745. ) {
  746. if (is.null(family) || !use_showtext) {
  747. return(NULL)
  748. }
  749. if (is_google_font(family)) family <- family$family
  750. family <- gsub("['\"]", "", family)
  751. if (!identical(xaringanthemer_env$showtext_auto, TRUE)) {
  752. if (!requires_package(pkg = "showtext", fn, required = FALSE)) {
  753. return(family)
  754. }
  755. showtext::showtext_auto()
  756. xaringanthemer_env$showtext_auto <- TRUE
  757. }
  758. if (family %in% xaringanthemer_env[["registered_font_families"]] %||% "") {
  759. return(family)
  760. }
  761. if (!requires_package(pkg = "sysfonts", fn, required = FALSE)) {
  762. return(family)
  763. } else if (family == "Droid Serif") {
  764. dstmp <- download_tmp_droid_serif()
  765. if (!is.null(dstmp)) {
  766. sysfonts::font_add(
  767. family = "Droid Serif",
  768. regular = dstmp
  769. )
  770. }
  771. } else if (!family %in% sysfonts::font_families()) {
  772. is_default_font <- family %in% c(
  773. "Roboto",
  774. "Source Code Pro",
  775. "Yanone Kaffeesatz"
  776. )
  777. font_found <- family %in% sysfonts::font_families()
  778. is_google_font <- identical(google, TRUE) || (missing(google) && is_default_font)
  779. if (is_google_font) {
  780. tryCatch(
  781. {
  782. sysfonts::font_add_google(family, ...)
  783. font_found <- TRUE
  784. },
  785. error = function(e) {},
  786. warning = function(w) {}
  787. )
  788. }
  789. if (!font_found) { # warn user if font still not found
  790. msg <- if (is_google_font) glue::glue(
  791. "Font '{family}' not found in Google Fonts. ",
  792. "Please manually register the font using `sysfonts::font_add()`."
  793. ) else {
  794. glue::glue(
  795. "Font '{family}' must be manually registered using `sysfonts::font_add()`."
  796. )
  797. }
  798. warning(str_wrap(msg), call. = FALSE)
  799. } else {
  800. verify_fig_showtext(fn)
  801. }
  802. }
  803. xaringanthemer_env[["registered_font_families"]] <- c(
  804. xaringanthemer_env[["registered_font_families"]],
  805. family
  806. )
  807. family
  808. }
  809. download_tmp_droid_serif <- function() {
  810. if (isTRUE(xaringanthemer_env[["declined_droid_serif"]])) return(NULL)
  811. message(
  812. "Using 'Droid Serif' in `theme_xaringan()` requires downloading the font from Google Fonts into a temporary file. "
  813. )
  814. ok_to_download <- utils::askYesNo("Do you want to try to download this font now?")
  815. if (identical(ok_to_download, FALSE)) {
  816. xaringanthemer_env[["declined_droid_serif"]] <- TRUE
  817. }
  818. if (!isTRUE(ok_to_download)) {
  819. return(NULL)
  820. }
  821. dstmp <- tempfile("droid-serif", fileext = "ttf")
  822. utils::download.file(
  823. "https://github.com/google/fonts/raw/feb15862e0c66ec0e7531ca4c3ef2607071ea700/apache/droidserif/DroidSerif-Regular.ttf",
  824. dstmp,
  825. quiet = TRUE
  826. )
  827. dstmp
  828. }
  829. verify_fig_showtext <- function(fn = "theme_xaringan_base") {
  830. if (is.null(knitr::current_input())) return()
  831. # Try to set fig.showtext automatically
  832. if (isTRUE(knitr::opts_current$get("fig.showtext"))) {
  833. return()
  834. }
  835. stop(str_wrap(
  836. "To use ", fn, "() with knitr, you need to set the chunk option ",
  837. "`fig.showtext = TRUE` for this chunk. Or you can set this option ",
  838. "globally with `knitr::opts_chunk$set(fig.showtext = TRUE)`."
  839. ))
  840. }
  841. set_fig_showtext <- function() {
  842. if (!requireNamespace("showtext", quietly = TRUE)) {
  843. return(invisible())
  844. }
  845. curr_fst <- 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. #'
  956. #' @return The value of the xaringanthemer style parameter.
  957. #' @export
  958. theme_xaringan_get_value <- function(setting, css_file = NULL) {
  959. requires_xaringanthemer_env(css_file = css_file)
  960. if (length(setting) > 1) {
  961. ret <- list()
  962. for (var in setting) {
  963. ret[[var]] <- xaringanthemer_env[[var]]
  964. }
  965. return(ret)
  966. }
  967. xaringanthemer_env[[setting]]
  968. }
  969. web_to_point <- function(x, px_per_em = NULL, scale = 0.75) {
  970. if (is.null(x)) {
  971. return(NULL)
  972. }
  973. px_per_em <- px_per_em %||% get_base_font_size()
  974. if (grepl("pt$", x)) {
  975. return(as.numeric(sub("pt$", "", x)))
  976. } else if (grepl("px$", x)) {
  977. x <- as.numeric(sub("px$", "", x))
  978. return(x * scale)
  979. } else if (grepl("r?em$", x)) {
  980. x <- as.numeric(sub("r?em$", "", x))
  981. return(x * px_per_em * scale)
  982. } else {
  983. return()
  984. }
  985. }
  986. get_base_font_size <- function() {
  987. base_size <- xaringanthemer_env[["base_font_size"]] %||%
  988. xaringanthemer_env[["text_font_size"]]
  989. if (!grepl("px", base_size)) {
  990. # assume 16px base font size
  991. 16
  992. } else {
  993. as.numeric(sub("px", "", base_size))
  994. }
  995. }