1+ # ' @title Write a ggplot Object to PNG File
2+ # '
3+ # ' @description Renders a ggplot object to a PNG file using ragg for high-quality output.
4+ # '
5+ # ' @param ggobj A ggplot object to render
6+ # ' @param width Numeric, width of the output image
7+ # ' @param height Numeric, height of the output image
8+ # ' @param res Numeric, resolution in DPI (default 200)
9+ # ' @param units Character, units for width and height ("in", "cm", "mm", "px") (default "in")
10+ # ' @param pointsize Integer, The default pointsize of the device in pt
11+ # ' @param scaling scaling factor to apply
12+ # ' @param path Character, output file path. If NULL, a temporary file is created (default NULL)
13+ # '
14+ # ' @return Character, the path to the created PNG file
15+ # '
16+ # ' @importFrom ragg agg_png
17+ # ' @importFrom grDevices dev.off
18+ # ' @examples
19+ # ' plot_in_png(
20+ # ' code = {
21+ # ' barplot(1:10)
22+ # ' },
23+ # ' width = 5,
24+ # ' height = 4,
25+ # ' res = 72,
26+ # ' units = "in"
27+ # ' )
28+ # ' @export
29+ # ' @keywords internal
30+ plot_in_png <- function (
31+ ggobj = NULL ,
32+ code = NULL ,
33+ width ,
34+ height ,
35+ res = 200 ,
36+ units = " in" ,
37+ pointsize = 11 ,
38+ scaling = 1 ,
39+ path = NULL
40+ ) {
41+ if (is.null(path )) {
42+ path <- tempfile(fileext = " .png" )
43+ }
44+
45+ if (! is.null(ggobj )) {
46+ code <- str2lang(" print(ggobj)" )
47+ }
48+
49+ agg_png(
50+ filename = path ,
51+ width = width ,
52+ height = height ,
53+ units = units ,
54+ background = " transparent" ,
55+ res = res ,
56+ pointsize = pointsize ,
57+ scaling = scaling
58+ )
59+ tryCatch(
60+ {
61+ eval(code )
62+ },
63+ finally = dev.off()
64+ )
65+ path
66+ }
67+
68+ # ' @importFrom openssl base64_encode
69+ # ' @export
70+ # ' @title Encode Character Vector to Base64
71+ # ' @description
72+ # ' Encodes one or more elements of a character vector into Base64 format.
73+ # ' @param x A character vector. NA values are preserved.
74+ # ' @return A character vector of Base64-encoded strings, same length as `x`.
75+ # ' @examples
76+ # ' as_base64(letters)
77+ # ' as_base64(c("hello", NA, "world"))
78+ # ' @keywords internal
79+ as_base64 <- function (x ) {
80+ if (! is.character(x )) {
81+ stop(" 'x' must be a character vector." )
82+ }
83+ z <- vapply(
84+ x ,
85+ function (elem ) {
86+ if (is.na(elem )) {
87+ NA_character_
88+ } else {
89+ base64_encode(charToRaw(elem ))
90+ }
91+ },
92+ NA_character_
93+ )
94+ unname(z )
95+ }
96+
97+ # ' @importFrom openssl base64_decode
98+ # ' @export
99+ # ' @title Decode Base64 Vector to Character
100+ # ' @description
101+ # ' Decodes one or more Base64-encoded elements back into their original character form.
102+ # ' @param x A character vector of Base64-encoded strings. NA values are preserved.
103+ # ' @return A character vector of decoded strings, same length as `x`.
104+ # ' @examples
105+ # ' z <- as_base64(c("hello", "world"))
106+ # ' from_base64(z)
107+ # ' @keywords internal
108+ from_base64 <- function (x ) {
109+ if (! is.character(x )) {
110+ stop(" 'x' must be a character vector of Base64 strings." )
111+ }
112+ z <- vapply(
113+ x ,
114+ function (elem ) {
115+ if (is.na(elem )) {
116+ NA_character_
117+ } else {
118+ raw <- tryCatch(
119+ {
120+ b64_str <- base64_decode(elem )
121+ if (all(nchar(b64_str ) < 1 )) {
122+ stop(" empty result" )
123+ }
124+ b64_str
125+ },
126+ error = function (e ) {
127+ stop(" Failed to decode Base64 element: '" , elem , " '." , call. = FALSE )
128+ }
129+ )
130+ rawToChar(raw )
131+ }
132+ },
133+ NA_character_
134+ )
135+ unname(z )
136+ }
137+
138+
139+ # ' Convert Data URIs to PNG Files
140+ # '
141+ # ' @description Decodes base64-encoded data URIs and writes them to PNG files.
142+ # '
143+ # ' @param data_uri Character, a data URI character vector starting with "data:image/png;base64,"
144+ # ' @param output_files Character, paths to the output PNG files
145+ # '
146+ # ' @return Character, the paths to the created PNG files
147+ # '
148+ # ' @importFrom openssl base64_decode
149+ # ' @examples
150+ # ' rlogo <- file.path(R.home("doc"), "html", "logo.jpg")
151+ # ' base64_str <- image_to_base64(rlogo)
152+ # ' base64_to_image(
153+ # ' data_uri = base64_str,
154+ # ' output_files = tempfile(fileext = ".jpeg")
155+ # ' )
156+ # ' @export
157+ base64_to_image <- function (data_uri , output_files ) {
158+
159+ for (i in seq_along(data_uri )) {
160+ base64_part <- sub(" ^data:image/[^;]+;base64," , " " , data_uri [[i ]])
161+ raw_data <- base64_decode(base64_part )
162+ writeBin(raw_data , output_files [[i ]])
163+ }
164+
165+ output_files
166+ }
167+
168+ mime_type <- function (paths ) {
169+ result <- character (length(paths ))
170+ pattern <- " \\ .(png|gif|jpg|jpeg|svg|tiff|pdf|webp)$"
171+ m <- regexpr(pattern = pattern , text = paths )
172+ result [attr(m , " match.length" ) > - 1 ] <- regmatches(paths , m )
173+ result <- gsub(" \\ .jpg" , " .jpeg" , result )
174+ result <- gsub(" \\ .svg" , " .svg+xml" , result )
175+ result <- gsub(" ^\\ .{1}" , " " , result )
176+ prefix <- ifelse(result %in% " pdf" , " application" , " image" )
177+ result <- paste(prefix , result , sep = " /" )
178+ result [attr(m , " match.length" ) < 0 ] <- NA_character_
179+ result
180+ }
181+
1182# ' @importFrom openssl base64_encode
2183# ' @export
3184# ' @title Images to base64
4185# ' @description encodes images into base64 strings.
5186# ' @param filepaths file names.
6187# ' @keywords internal
7188# ' @examples
8- # ' rlogo <- file.path( R.home("doc"), "html", "logo.jpg")
9- # ' image_to_base64(rlogo)
10- image_to_base64 <- function (filepaths ){
11- vapply(
12- filepaths ,
13- function (filepath ){
14- if ( grepl(" \\ .png" , ignore.case = TRUE , x = filepath ) ){
15- mime <- " image/png"
16- } else if ( grepl(" \\ .gif" , ignore.case = TRUE , x = filepath ) ){
17- mime <- " image/gif"
18- } else if ( grepl(" \\ .jpg" , ignore.case = TRUE , x = filepath ) ){
19- mime <- " image/jpeg"
20- } else if ( grepl(" \\ .jpeg" , ignore.case = TRUE , x = filepath ) ){
21- mime <- " image/jpeg"
22- } else if ( grepl(" \\ .svg" , ignore.case = TRUE , x = filepath ) ){
23- mime <- " image/svg+xml"
24- } else if ( grepl(" \\ .tiff" , ignore.case = TRUE , x = filepath ) ){
25- mime <- " image/tiff"
26- } else if ( grepl(" \\ .pdf" , ignore.case = TRUE , x = filepath ) ){
27- mime <- " application/pdf"
28- } else if ( grepl(" \\ .webp" , ignore.case = TRUE , x = filepath ) ){
29- mime <- " image/webp"
30- } else {
31- stop(sprintf(" 'officer' does not know how to encode format of the file '%s'." , filepath ))
32- }
33- if (! file.exists(filepath )){
34- stop(sprintf(" file '%s' can not be found." ,filepath ))
35- }
36- dat <- readBin(filepath , what = " raw" , size = 1 , endian = " little" , n = 1e+8 )
189+ # ' rlogo <- file.path(R.home("doc"), "html", "logo.jpg")
190+ # ' base64_str <- image_to_base64(rlogo)
191+ image_to_base64 <- function (filepaths ) {
192+
193+ mimes <- mime_type(paths = filepaths )
194+ if (any(is.na(mimes ))) {
195+ cli :: cli_abort(
196+ paste0(
197+ " Unknown image(s) format: " ,
198+ cli :: ansi_collapse(
199+ basename(filepaths )[is.na(mimes )],
200+ trunc = 5
201+ )
202+ )
203+ )
204+ }
205+
206+ if (any(! file.exists(filepaths ))) {
207+ cli :: cli_abort(
208+ paste0(
209+ " File(s) not found: " ,
210+ cli :: ansi_collapse(
211+ basename(filepaths )[! file.exists(filepaths )],
212+ trunc = 5
213+ )
214+ )
215+ )
216+ }
217+
218+ base64_lst <- mapply(
219+ FUN = function (filepath , mime ) {
220+ dat <- readBin(
221+ filepath ,
222+ what = " raw" ,
223+ size = 1 ,
224+ endian = " little" ,
225+ n = file.info(filepath )$ size
226+ )
37227 base64_str <- base64_encode(bin = dat )
38228 base64_str <- sprintf(" data:%s;base64,%s" , mime , base64_str )
39229 base64_str
40230 },
41- FUN.VALUE = " " )
231+ filepath = filepaths ,
232+ mime = mimes ,
233+ SIMPLIFY = FALSE
234+ )
235+ unname(unlist(base64_lst ))
42236}
43237
44238# ' @importFrom uuid UUIDgenerate
@@ -56,11 +250,11 @@ uuid_generate <- function(n = 1, ...) {
56250}
57251
58252.url_special_chars <- list (
59- `&` = ' &' ,
60- `<` = ' <' ,
61- `>` = ' >' ,
62- `'` = ' '' ,
63- `"` = ' "' ,
253+ `&` = " &" ,
254+ `<` = " <" ,
255+ `>` = " >" ,
256+ `'` = " '" ,
257+ `"` = " "" ,
64258 ` ` = " "
65259)
66260
0 commit comments