svgedit

Create complex figures reproducibly with R and Inkscape

Daniel Thédié

Chronopsychiatry team meeting

03/03/2026

Sleep report

The code

\noindent
\color{white}
\begin{minipage}[t]{0.32\textwidth}
  \begin{tcolorbox}[standard jigsaw, height=0.45\textheight, valign=center, arc=12pt, opacityback=0.5, boxrule=0pt]
    \large
    \vspace*{2.5mm}
    \begin{center}
      \Large\textbf{Sleep Monitoring}
    \end{center}
      Your sleep was monitored using a Somnofy device. The device uses radar technology to detect your movements and breathing patterns during sleep.
      This was used to record:
      \begin{itemize}
        \item When you fell asleep and woke up
        \item How long you slept
        \item Your sleep efficiency (the percentage of time you spent asleep while in bed)
      \end{itemize}
      \begin{tabular}{@{}m{0.55\textwidth}@{\hspace{0.02\textwidth}}m{0.42\textwidth}@{}}
        Note: the recording may not be 100\% accurate, and can be influenced by external factors such as pets coming into bed. &
        \begin{center}
          \includegraphics[width=0.2\textwidth]{../../resources/Somnofy_device.png}
        \end{center}
      \end{tabular}
  \end{tcolorbox}
  \vfill
  \begin{tcolorbox}[standard jigsaw, height=0.45\textheight, valign=top, arc=12pt, opacityback=0.5, boxrule=0pt]
    \large
    \begin{center}
      \Large\textbf{Number of hours slept per night}
    \end{center}
\```
\```{r, echo=FALSE, warning=FALSE, fig.align='center', out.width='100%', fig.height=5, fig.width=6}
print(sleep_duration_plot)
\```
\```{=latex}
  \end{tcolorbox}
\end{minipage}%
\hfill
\begin{minipage}[t]{0.33\textwidth}
  \centering
  \vspace*{-60mm}

Inkscape

  • Free to use
  • Multi-platform
    • Windows, Mac, Linux
  • Vectorial drawing

Vector

<rect
  style="fill:none;stroke:#000000;stroke-width:0.579312"
  id="rect1"
  width="75.521484"
  height="73.926834"
  x="24.698519"
  y="67.334633" />
  • pdf, svg, eps

Raster

  • jpg, png, bmp

svgedit

Create a template

  • Rectangles for plots and images
  • {} to insert text

Set template labels

  • Open “Layers and Objects” panel (Ctrl + Shift + L)
  • Click on objects to select them in the panel
  • Edit the corresponding label

Create plots (ggplot2)

# Number of penguins per species/island
penguins_count <- ggplot(penguins, aes(x = island, fill = species)) +
  geom_bar(alpha = 0.8) +
  scale_fill_manual(values = c("darkorange", "purple", "cyan4"), 
                    guide = "none") +
  theme_minimal() +
  facet_wrap(~species, ncol = 1) +
  coord_flip()

# Flipper length distribution by species
flipper_length <- ggplot(data = penguins, aes(x = flipper_length_mm)) +
  geom_histogram(aes(fill = species), alpha = 0.5, position = "identity") +
  scale_fill_manual(values = c("darkorange", "darkorchid", "cyan4"))

# Body mass vs flipper length colored by species
body_mass_vs_flipper_length <- ggplot(data = penguins, aes(x = flipper_length_mm, y = body_mass_g)) +
  geom_point(aes(color = species, 
                 shape = species),
             size = 2) +
  scale_color_manual(values = c("darkorange", "darkorchid", "cyan4"))

svgedit::draw

svgedit::draw(
  input_svg = "Template.svg",
  output_svg = "Output.svg",
)

Add plots

svgedit::draw(
  input_svg = "Template.svg",
  output_svg = "Output.svg",
  plots = list(
    panel_A = penguins_count,
    panel_B = body_mass_vs_flipper_length,
    panel_C = flipper_length
  ),
)

Add text

svgedit::draw(
  input_svg = "Template.svg",
  output_svg = "Output.svg",
  plots = list(
    panel_A = penguins_count,
    panel_B = body_mass_vs_flipper_length,
    panel_C = flipper_length
  ),
  text = list(
    fig_caption = c(
      "1",
      "Number of penguins of each species per island",
      "Body mass vs flipper length colored by species",
      "Flipper length distribution by species",
      "Photo of an Adelie Penguin (Diego Tirira, CC BY-SA 2.0)"
    )
  ),
)

Add an image

svgedit::draw(
  input_svg = "Template.svg",
  output_svg = "Output.svg",
  plots = list(
    panel_A = penguins_count,
    panel_B = body_mass_vs_flipper_length,
    panel_C = flipper_length
  ),
  text = list(
    fig_caption = c(
      "1",
      "Number of penguins of each species per island",
      "Body mass vs flipper length colored by species",
      "Flipper length distribution by species",
      "Photo of an Adelie Penguin (Diego Tirira, CC BY-SA 2.0)"
    )
  ),
  images = list(
    panel_D = "adelie_penguin.jpeg"
  )
)

And run…

Sleep report template

…filled in

Sleep report code

  svgedit::draw(
    input_svg = paste0(template_path, "/Sleep_report_template.svg"),
    output_svg = filled_svg,
    plots = list(
      clock_plot = clock_plot,
      sleep_duration_plot = sleep_duration_plot,
      sleep_times = sleep_times
    ),
    plot_scale = list(
      clock_plot = 0.66,
      sleep_duration_plot = 0.66,
      sleep_times = 0.5
    ),
    text = list(
      title = title,
      dates = c(dates[1], dates[2]),
      time_to_fall_asleep = stats$time_to_fall_asleep,
      sleep_efficiency = stats$sleep_efficiency,
      chronotype = stats$chronotype,
      sleep_regularity = stats$sleep_regularity,
      social_jet_lag = stats$social_jet_lag,
      credits = c(nocturn_version, stats$chronotype_credit)
    ),
    images = list(
      chronotype_image = paste0(template_path, "/", stats$chronotype_image)
    )
  )

Participant Data Sheet

Applications of svgedit

  • Dynamically generated visuals
    • Sleep report
    • Data Sheet
  • Figures that change often
    • Incremental changes to an article figure
  • Figure generation as part of computational workflows

Use svgedit yourself!

Thank you!