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
  ))
}