#' Encode/decode data into/from base64 encoding.
#'
#' The function `base64_encode()` encodes a file or a raw vector into the
#' base64 encoding. The function `base64_decode()` decodes data from the
#' base64 encoding.
#' @param x For `base64_encode()`, a raw vector. If not raw, it is assumed
#' to be a file or a connection to be read via `readBin()`. For
#' `base64_decode()`, a string.
#' @param from If provided (and `x` is not provided), a connection or file
#' to be read via `readChar()`, and the result will be passed to the
#' argument `x`.
#' @return `base64_encode()` returns a character string.
#' `base64_decode()` returns a raw vector.
#' @useDynLib xfun, .registration = TRUE
#' @export
#' @examples xfun::base64_encode(as.raw(1:10))
#' logo = xfun:::R_logo()
#' xfun::base64_encode(logo)
base64_encode = function(x) {
if (!is.raw(x)) x = read_bin(x)
.Call('base64_enc', x, PACKAGE = 'xfun')
}
#' @export
#' @rdname base64_encode
#' @examples xfun::base64_decode("AQIDBAUGBwgJCg==")
base64_decode = function(x, from = NA) {
if (!is.na(from)) {
if (!missing(x)) stop("Please provide either 'x' or 'from', but not both.")
x = readChar(from, file.size(from), TRUE)
}
if (!is.character(x) || length(x) != 1) stop("'x' must be a single character string.")
.Call('base64_dec', x, PACKAGE = 'xfun')
}
# an R implementation of base64 encoding by Wush Wu moved from knitr (of
# historic interest only): https://github.com/yihui/knitr/pull/324
base64_encode_r = function(x) {
if (!is.raw(x)) x = read_bin(x)
chars = c(LETTERS, letters, 0:9, '+', '/')
n = length(s <- as.integer(x))
res = rep(NA, (n + 2) / 3 * 4)
i = 0L # index of res vector
j = 1L # index of base64_table
while (n > 2L) {
res[i <- i + 1L] = chars[s[j] %/% 4L + 1L]
res[i <- i + 1L] = chars[16 * (s[j] %% 4L) + s[j + 1L] %/% 16 + 1L]
res[i <- i + 1L] = chars[4L * (s[j + 1L] %% 16) + s[j + 2L] %/% 64L + 1L]
res[i <- i + 1L] = chars[s[j + 2L] %% 64L + 1L]
j = j + 3L
n = n - 3L
}
if (n) {
res[i <- i + 1L] = chars[s[j] %/% 4L + 1L]
if (n > 1L) {
res[i <- i + 1L] = chars[16 * (s[j] %% 4L) + s[j + 1L] %/% 16 + 1L]
res[i <- i + 1L] = chars[4L * (s[j + 1L] %% 16) + 1L]
res[i <- i + 1L] = '='
} else {
res[i <- i + 1L] = chars[16 * (s[j] %% 4L) + 1L]
res[i <- i + 1L] = '='
res[i <- i + 1L] = '='
}
}
paste(res[!is.na(res)], collapse = '')
}
#' Generate the Data URI for a file
#'
#' Encode the file in the base64 encoding, and add the media type. The data URI
#' can be used to embed data in HTML documents, e.g., in the `src` attribute of
#' the `<img />` tag.
#' @param x A file path.
#' @param type The MIME type of the file, e.g., `"image/png"` for a PNG image
#' file.
#' @return A string of the form `data:<media type>;base64,<data>`.
#' @note This function requires the \pkg{mime} package to determine the MIME
#' type of the file except for a few common MIME types.
#' @export
#' @examples
#' logo = xfun:::R_logo()
#' img = htmltools::img(src = xfun::base64_uri(logo), alt = 'R logo')
#' if (interactive()) htmltools::browsable(img)
base64_uri = function(x, type = mime_type(x)) {
paste0("data:", type, ";base64,", base64_encode(x))
}
#' Get the MIME types of files
#'
#' If the \pkg{mime} package is installed, call [mime::guess_type()], otherwise
#' use the system command `file --mime-type` to obtain the MIME type of a file.
#' Typically, the `file` command exists on *nix. On Windows, the command should
#' exist if Cygwin or Rtools is installed. If it is not found, .NET's
#' `MimeMapping` class will be used instead (which requires the .NET framework).
#' @param x A vector of file paths.
#' @param use_mime Whether to use the \pkg{mime} package.
#' @param empty The MIME type for files without extensions (e.g., `Makefile`).
#' If `NA`, the type will be obtained from system command. This argument is
#' used only for `use_mime = FALSE`.
#' @return A character vector of MIME types.
#' @note When querying the MIME type via the system command, the result will be
#' cached to `xfun:::cache_dir()`. This will make future queries much faster,
#' since running the command in real time can be a little slow.
#' @export
#' @examplesIf tolower(Sys.getenv('CI')) == 'true'
#' f = list.files(R.home('doc'), full.names = TRUE)
#' mime_type(f)
#' mime_type(f, FALSE) # don't use mime
#' mime_type(f, FALSE, NA) # run command for files without extension
mime_type = function(x, use_mime = loadable('mime'), empty = 'text/plain') {
if (use_mime) return(mime::guess_type(x))
ext = tolower(file_ext(x))
res = character(n <- length(x))
# try to read from cache since running command in real time can be slow
p = file.path(cache_dir(), 'mime.rds')
db = mimemap
for (i in seq_len(n)) {
e = ext[i]
# use full path for files without extension like Makefile and DESCRIPTION
if (e == '') m = empty else if (is.na(m <- db[e])) {
if (file_exists(p)) db = readRDS(p)
m = db[e]
}
if (is.na(m)) {
m = .mime_type(x[i])
if (e != '') {
db[e] = m; if (dir_create(dirname(p))) saveRDS(db, p)
}
}
res[i] = m
}
res
}
# get the MIME type from system via `powershell` or `file`
.mime_type = function(x) {
if (!file.exists(x <- path.expand(x))) stop("The file '", x, "' does not exist.")
cmd = 'file'
arg = c('--mime-type', '-b')
if (Sys.which(cmd) == '') {
if (!is_windows()) stop("The 'file' command is not found")
cmd = 'powershell'
arg = c('-ExecutionPolicy', 'Bypass', '-File', shQuote(pkg_file('scripts', 'mime-type.ps1')))
}
system2(cmd, c(arg, shQuote(x)), stdout = TRUE)[1]
}
# a comprehensive version is mime::mimemap
mimemap = c(
css = 'text/css', csv = 'text/csv', gif = 'image/gif', jpeg = 'image/jpeg',
jpg = 'image/jpeg', js = 'application/javascript', png = 'image/png',
r = 'text/plain', rmd = 'text/x-markdown', svg = 'image/svg+xml',
ttf = 'application/font-sfnt',
woff = 'application/font-woff', woff2 = 'application/octet-stream'
)
Add the following code to your website.
For more information on customizing the embed code, read Embedding Snippets.