😎 Give your xaringan slides some style
No puede seleccionar más de 25 temas Los temas deben comenzar con una letra o número, pueden incluir guiones ('-') y pueden tener hasta 35 caracteres de largo.

853 líneas
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. }