😎 Give your xaringan slides some style
Du kannst nicht mehr als 25 Themen auswählen Themen müssen entweder mit einem Buchstaben oder einer Ziffer beginnen. Sie können Bindestriche („-“) enthalten und bis zu 35 Zeichen lang sein.

867 Zeilen
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. }