blob: ff4ddc1b92cb5883e309fb69e52631f00905017d [file] [log] [blame]
christophe dervieux92fa4692021-09-21 16:15:17 +02001# function to lookup reveal resource
2reveal_resources <- function(...) {
3 system.file("rmarkdown/templates/revealjs_presentation/resources",
4 ...,
Marc Kupietze195cea2026-08-17 13:17:33 +02005 package = "revealjs.ids")
christophe dervieux24418a82021-09-21 16:17:18 +02006}
7
Christophe Dervieuxe1893ae2021-10-07 17:09:02 +02008
9revealjs_lib_path <- function(...) {
Marc Kupietze195cea2026-08-17 13:17:33 +020010 pkg <- system.file(package = "revealjs.ids")
Christophe Dervieuxe1893ae2021-10-07 17:09:02 +020011 lib_folder <- list.files(pkg, pattern = "reveal.js-")[1]
Marc Kupietze195cea2026-08-17 13:17:33 +020012 system.file(lib_folder, ..., package = "revealjs.ids")
Christophe Dervieuxe1893ae2021-10-07 17:09:02 +020013}
14
15revealjs_version <- function() {
16 as.numeric_version(gsub(".*reveal\\.js-(.*)$", "\\1", revealjs_lib_path()))
17}
18
christophe dervieux24418a82021-09-21 16:17:18 +020019# Convert boolean from R to JS boolean
Christophe Dervieux2f01dc92021-09-22 09:37:18 +020020jsbool <- function(value) ifelse(value, "true", "false")
21
22# transfrom reveal option as pandoc variable
23process_reveal_option <- function(option, value) {
24 if (is.logical(value)) {
25 value <- jsbool(value)
26 } else if (is.character(value)) {
Christophe Dervieuxe1893ae2021-10-07 17:09:02 +020027 # Special handling for some vector options
28 if (
29 # chalkboard plugin options
30 # e.g: color: [ 'rgba(0,0,255,1)', 'rgba(255,255,255,0.5)' ]
31 grepl("chalkboard-(background|draw)", option)
32 # e.g autoAnimateStyles: ['opacity','color']
33 || grepl("autoAnimateStyles", option)
34 ) {
35 if (length(value) > 1 || !grepl("^\\[.*\\]$", value)) {
36 value <- sprintf("[%s]", paste(paste0("'", value, "'"), collapse = ", "))
37 }
Christophe Dervieux2f01dc92021-09-22 09:37:18 +020038 }
39 # Add quotes around some config that can be several type
40 # like number or percent unit or slideNumber = true or slideNumber = 'c/t'
41 if (
42 option %in% c("slideNumber") ||
43 (option %in% c("width", "height") && grepl("%$", value))) {
44 value <- paste0("'", value, "'")
45 }
46 }
47 pandoc_variable_arg(option, value)
Christophe Dervieuxaa008e42021-09-23 16:52:37 +020048}
Marc Kupietz59e95a62026-08-29 10:19:05 +030049
50# Resolve a logo image (yaml entries `partner_logo` / `funder_logo`) to a URL
51# usable inside CSS: http(s) and data URLs pass through unchanged, local
52# files are inlined as base64 data URIs. Inlining is required: CSS background
53# URLs would resolve relative to the theme stylesheet, and document-relative
54# files would be left behind when the rendered html is deployed elsewhere.
55logo_uri <- function(path) {
56 if (grepl("^(https?|data):", path)) {
57 return(path)
58 }
59 if (!file.exists(path)) {
60 stop("Logo file not found: ", path, call. = FALSE)
61 }
62 ext <- tolower(tools::file_ext(path))
63 mime <- switch(ext,
64 svg = "image/svg+xml", png = "image/png", jpg = , jpeg = "image/jpeg",
65 gif = "image/gif", webp = "image/webp",
66 stop("Unsupported logo format '", ext, "' (use svg, png, jpg, gif or webp)",
67 call. = FALSE
68 )
69 )
70 if (!requireNamespace("base64enc", quietly = TRUE)) {
71 stop("Package \"base64enc\" needed to embed local logo files. Please install it.",
72 call. = FALSE
73 )
74 }
75 sprintf("data:%s;base64,%s", mime, base64enc::base64encode(path))
76}
77
Marc Kupietzb2cc5232026-08-29 16:21:52 +030078# qrcode::generate_svg() paints the code -- quiet zone included -- into a
79# fixed 300x300 canvas and emits no viewBox, so the svg cannot be resized by
80# CSS: given a smaller box it keeps drawing at full size and only its top
81# left corner remains visible. Give it a viewBox cropped to the dark modules
82# instead. The code then scales to whatever box it is placed in, and its
83# quiet zone becomes the surrounding layout's business (on the title slide
84# the white plate behind the code supplies it), which keeps the placement
85# independent of how many modules the encoded url happens to need.
86qr_svg_fit <- function(svg) {
87 rects <- regmatches(svg, gregexpr("<rect[^>]*fill=\"black\"[^>]*>", svg))[[1]]
88 attr_num <- function(name) {
89 suppressWarnings(as.numeric(
90 sub(sprintf(".*[ ]%s=\"([0-9.]+)(px)?\".*", name), "\\1", rects)
91 ))
92 }
93 x <- attr_num("x")
94 y <- attr_num("y")
95 w <- attr_num("width")
96 h <- attr_num("height")
97 if (!length(rects) || anyNA(c(x, y, w, h))) {
98 return(svg)
99 }
100 left <- min(x)
101 top <- min(y)
102 side <- max(max(x + w) - left, max(y + h) - top)
103 vb <- sprintf(
104 'viewBox="%s %s %s %s" width="%s" height="%s"',
105 format(left), format(top), format(side), format(side),
106 format(side), format(side)
107 )
108 root <- regmatches(svg, regexpr("<svg[^>]*>", svg))
109 fitted <- sub(
110 "<svg", paste("<svg", vb),
111 gsub("[ ](width|height)=\"[0-9.]+\"", "", root),
112 fixed = TRUE
113 )
114 # the xml prolog would end up as a stray comment once inlined into html
115 sub("^<[?]xml[^>]*[?]>", "", sub(root, fitted, svg, fixed = TRUE))
116}
117
Marc Kupietz59e95a62026-08-29 10:19:05 +0300118#' HTML code for a linked QR code
119#'
120#' Generates a QR code (inline SVG) linking to the given URL and returns the
121#' corresponding HTML code, so a QR code can be placed anywhere in a
122#' presentation with inline R code.
123#'
124#' The `website` YAML entry of [revealjs_presentation()] uses this to
125#' place a linked QR code in the top left of the title slide.
126#'
127#' @param url Target URL for the QR code
128#' @param logo Optional path to a logo image to place in the center of the QR
129#' code
130#' @param text Optional caption displayed below the QR code (alias:
131#' `caption`)
132#' @param caption Alias for `text`
133#' @return Character string with HTML code for a linked QR code
134#' @export
135qrlink <- function(url, logo = NULL, text = NULL, caption = text) {
136 if (!requireNamespace("qrcode", quietly = TRUE)) {
137 stop("Package \"qrcode\" needed to generate QR codes. Please install it.",
138 call. = FALSE
139 )
140 }
141 tmp <- tempfile(fileext = ".svg")
142 qrcode <- qrcode::qr_code(url, ecl = if (is.null(logo)) "L" else "H")
143 if (!is.null(logo)) {
144 qrcode <- qrcode::add_logo(qrcode, logo, ecl = "L")
145 }
146 qrcode::generate_svg(qrcode, tmp, show = FALSE)
147
148 cap_html <- if (!is.null(caption) && nchar(trimws(caption)) > 0) {
149 sprintf('<span class="qrcode-caption">%s</span>', caption)
150 } else {
151 ""
152 }
153
Marc Kupietzb2cc5232026-08-29 16:21:52 +0300154 svg <- qr_svg_fit(gsub("\r?\n|\r", "", readChar(tmp, 1e7)))
Marc Kupietz59e95a62026-08-29 10:19:05 +0300155 qr <- sprintf('<a class="qrcode" href="%s">%s</a>', url, svg)
156 if (nchar(cap_html) > 0) {
157 paste0('<div class="qrcode-container">', qr, cap_html, "</div>")
158 } else {
159 qr
160 }
161}