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

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