File size: 7,448 Bytes
e232e39 | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 | # Image Processing and Layer Combination Functions
# Part of the jigsawR package
#' Convert SVG to PNG using available tools
#' @param svg_content SVG content as string
#' @param output_file Output PNG filename
#' @param width_px Width in pixels
#' @param height_px Height in pixels
#' @return TRUE if successful, FALSE otherwise
convert_svg_to_png <- function(svg_content, output_file, width_px = 2000, height_px = 2000) {
# Ensure parent directory exists
output_parent <- dirname(output_file)
if (nzchar(output_parent) && output_parent != "." && !dir.exists(output_parent)) {
dir.create(output_parent, recursive = TRUE)
}
# Save SVG to temporary file
temp_svg <- tempfile(fileext = ".svg")
writeLines(svg_content, temp_svg)
log_info("Converting SVG to PNG: {.file {output_file}}")
# Try different conversion methods in order of preference
# Method 1: Try rsvg package (R-based, most reliable if available)
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}")
})
}
# Method 2: Try magick package
if (requireNamespace("magick", quietly = TRUE)) {
log_info("Using magick package...")
tryCatch({
# Read SVG content directly instead of file path
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}")
})
}
# Method 3: Try Inkscape command line (if available)
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)
}
}
# Method 4: Try ImageMagick convert command line
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)
}
}
# All methods failed
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 background and overlay images
#' @param background_file Path to background PNG file
#' @param overlay_file Path to overlay PNG file
#' @param combined_file Output path for combined image
#' @param transparent_background Logical, whether to make final background transparent
#' @return TRUE if successful, FALSE otherwise
combine_image_layers <- function(background_file, overlay_file, combined_file, transparent_background = FALSE) {
# Ensure parent directory exists
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)
# Combine the layers first
combined <- magick::image_composite(background, overlay, operator = "over")
if (transparent_background) {
# Create a circular mask to make areas outside the circle transparent
img_info <- magick::image_info(combined)
width <- img_info$width
height <- img_info$height
# Create circular mask - everything outside circle becomes transparent
# Calculate center and radius based on image dimensions
center_x <- width / 2
center_y <- height / 2
# Puzzle circle diameter / canvas_size ratio ~= 0.833 (180/216)
# So radius = (diameter/canvas_size) * (image_size/2) = 0.833 * (width/2)
radius <- min(width, height) * 0.833 / 2 # More precise puzzle circle size
# Create SVG circle mask - white circle on black background
# This makes the circle area opaque and everything outside transparent
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
)
# Apply circular mask to make background transparent outside circle
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 available conversion tools and report status
#' @return List of available tools
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
))
} |