| |
| |
|
|
| |
| |
| |
| |
| |
| |
| convert_svg_to_png <- function(svg_content, output_file, width_px = 2000, height_px = 2000) { |
| |
| |
| output_parent <- dirname(output_file) |
| if (nzchar(output_parent) && output_parent != "." && !dir.exists(output_parent)) { |
| dir.create(output_parent, recursive = TRUE) |
| } |
|
|
| |
| temp_svg <- tempfile(fileext = ".svg") |
| writeLines(svg_content, temp_svg) |
|
|
| log_info("Converting SVG to PNG: {.file {output_file}}") |
|
|
| |
|
|
| |
| if (requireNamespace("rsvg", quietly = TRUE)) { |
| log_info("Using rsvg package...") |
| tryCatch({ |
| png_data <- rsvg::rsvg_png(temp_svg, width = width_px, height = height_px) |
| writeBin(png_data, output_file) |
| unlink(temp_svg) |
| return(TRUE) |
| }, error = function(e) { |
| log_warn("rsvg failed: {e$message}") |
| }) |
| } |
|
|
| |
| if (requireNamespace("magick", quietly = TRUE)) { |
| log_info("Using magick package...") |
| tryCatch({ |
| |
| img <- magick::image_read(svg_content, density = 300) |
| img <- magick::image_resize(img, paste0(width_px, "x", height_px)) |
| magick::image_write(img, output_file, format = "PNG") |
| unlink(temp_svg) |
| return(TRUE) |
| }, error = function(e) { |
| log_warn("magick failed: {e$message}") |
| }) |
| } |
|
|
| |
| inkscape_cmd <- Sys.which("inkscape") |
| if (nzchar(inkscape_cmd)) { |
| log_info("Using Inkscape command line...") |
| result <- tryCatch({ |
| system2(inkscape_cmd, |
| args = c("--export-type=png", |
| paste0("--export-filename=", output_file), |
| paste0("--export-width=", width_px), |
| paste0("--export-height=", height_px), |
| temp_svg), |
| stdout = TRUE, stderr = TRUE) |
| file.exists(output_file) |
| }, error = function(e) FALSE) |
|
|
| unlink(temp_svg) |
| if (result && file.exists(output_file)) { |
| return(TRUE) |
| } |
| } |
|
|
| |
| convert_cmd <- Sys.which("convert") |
| if (nzchar(convert_cmd)) { |
| log_info("Using ImageMagick convert...") |
| result <- tryCatch({ |
| system2(convert_cmd, |
| args = c("-density", "300", |
| "-resize", paste0(width_px, "x", height_px), |
| temp_svg, output_file), |
| stdout = TRUE, stderr = TRUE) |
| file.exists(output_file) |
| }, error = function(e) FALSE) |
|
|
| unlink(temp_svg) |
| if (result && file.exists(output_file)) { |
| return(TRUE) |
| } |
| } |
|
|
| |
| unlink(temp_svg) |
| log_error("No suitable SVG conversion tool found!") |
| log_info("Install one of: rsvg, magick packages, or Inkscape/ImageMagick CLI tools") |
| return(FALSE) |
| } |
|
|
| |
| |
| |
| |
| |
| |
| combine_image_layers <- function(background_file, overlay_file, combined_file, transparent_background = FALSE) { |
| |
| |
| output_parent <- dirname(combined_file) |
| if (nzchar(output_parent) && output_parent != "." && !dir.exists(output_parent)) { |
| dir.create(output_parent, recursive = TRUE) |
| } |
|
|
| if (requireNamespace("magick", quietly = TRUE)) { |
| log_info("Combining layers...") |
|
|
| tryCatch({ |
| background <- magick::image_read(background_file) |
| overlay <- magick::image_read(overlay_file) |
|
|
| |
| combined <- magick::image_composite(background, overlay, operator = "over") |
|
|
| if (transparent_background) { |
| |
| img_info <- magick::image_info(combined) |
| width <- img_info$width |
| height <- img_info$height |
|
|
| |
| |
| center_x <- width / 2 |
| center_y <- height / 2 |
| |
| |
| radius <- min(width, height) * 0.833 / 2 |
|
|
| |
| |
| mask_svg <- sprintf( |
| '<svg width="%d" height="%d"><rect width="100%%" height="100%%" fill="black"/><circle cx="%g" cy="%g" r="%g" fill="white"/></svg>', |
| width, height, center_x, center_y, radius |
| ) |
|
|
| |
| mask <- magick::image_read_svg(mask_svg, width = width, height = height) |
| combined <- magick::image_composite(combined, mask, operator = "copyopacity") |
|
|
| log_success("Applied circular transparency mask") |
| } |
|
|
| magick::image_write(combined, combined_file, quality = 95) |
| log_success("Combined PNG saved: {.file {combined_file}}") |
| return(TRUE) |
| }, error = function(e) { |
| log_error("Failed to combine: {e$message}") |
| return(FALSE) |
| }) |
| } else { |
| log_warn("magick package not available for layer combination") |
| return(FALSE) |
| } |
| } |
|
|
| |
| |
| check_conversion_tools <- function() { |
|
|
| log_subheader("Checking available conversion tools") |
|
|
| has_rsvg <- requireNamespace("rsvg", quietly = TRUE) |
| has_magick <- requireNamespace("magick", quietly = TRUE) |
| has_inkscape <- nzchar(Sys.which("inkscape")) |
| has_convert <- nzchar(Sys.which("convert")) |
|
|
| if (has_rsvg) { |
| log_success("rsvg package: Available") |
| } else { |
| log_warn("rsvg package: Not available") |
| } |
|
|
| if (has_magick) { |
| log_success("magick package: Available") |
| } else { |
| log_warn("magick package: Not available") |
| } |
|
|
| if (has_inkscape) { |
| log_success("Inkscape CLI: Available") |
| } else { |
| log_warn("Inkscape CLI: Not available") |
| } |
|
|
| if (has_convert) { |
| log_success("ImageMagick CLI: Available") |
| } else { |
| log_warn("ImageMagick CLI: Not available") |
| } |
|
|
| tools_available <- has_rsvg || has_magick || has_inkscape || has_convert |
|
|
| if (!tools_available) { |
| log_error("No SVG conversion tools available!") |
| log_info("Please install one of:") |
| log_info(" - R packages: install.packages(c('rsvg', 'magick'))") |
| log_info(" - Inkscape: {.url https://inkscape.org/}") |
| log_info(" - ImageMagick: {.url https://imagemagick.org/}") |
| } |
|
|
| return(list( |
| rsvg = has_rsvg, |
| magick = has_magick, |
| inkscape = has_inkscape, |
| imagemagick = has_convert, |
| any_available = tools_available |
| )) |
| } |