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

867 lines
26KB

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