skills/figure-export/SKILL.md
Figure export conventions for publication-quality R figures (PDF/PNG/SVG). Use when saving plots to files, choosing figure formats, setting DPI or dimensions, or exporting ggplot2 or base R figures for manuscripts or Inkscape editing.
npx skillsauth add musserlab/lab-claude-skills figure-exportInstall this skill globally with one command. Works with Claude Code, Cursor, and Windsurf.
3 of 9 scanners reported clean
Some scanners were skipped, did not run, or reported a non-clean status. Review each row below.
Best practices for saving R figures as PDF, PNG, and SVG so they are high quality and editable in Inkscape.
Every figure should be saved as PDF, PNG, and SVG:
# PDF — use cairo_pdf to embed fonts properly (handles Arial, Helvetica, etc.)
ggsave("plot.pdf", plot = p, width = 6.8, height = 5.5, device = cairo_pdf)
# PNG — always 300 dpi
ggsave("plot.png", plot = p, width = 6.8, height = 5.5, dpi = 300)
# SVG — use svglite with fix_text_size = FALSE
ggsave("plot.svg", plot = p, width = 6.8, height = 5.5,
device = function(...) svglite::svglite(..., fix_text_size = FALSE))
Critical: svglite defaults to fix_text_size = TRUE, which adds textLength and lengthAdjust attributes to every <text> element. These lock the text width and prevent editing in Inkscape. Always set fix_text_size = FALSE.
This applies everywhere svglite is used:
# Via ggsave (ggplot2 plots)
ggsave(..., device = function(...) svglite::svglite(..., fix_text_size = FALSE))
# Via direct device call (pheatmap, base R graphics)
svglite::svglite("plot.svg", width = w, height = h, fix_text_size = FALSE)
grid::grid.newpage()
grid::grid.draw(p$gtable)
dev.off()
Do NOT use base R svg() or Cairo SVG — they produce bloated SVGs with many style attributes per element and can crash Inkscape with large plots.
Use cairo_pdf instead of the default pdf() device. The default PDF device cannot find non-standard fonts (e.g., Arial) and will error with "failed to find or load PDF CID font". cairo_pdf embeds fonts as outlines.
# CORRECT
ggsave("plot.pdf", plot = p, device = cairo_pdf)
# WRONG — will fail with Arial or other non-default fonts
ggsave("plot.pdf", plot = p) # uses default pdf() device
Use Arial across all figure elements for consistency and Inkscape compatibility:
# In theme
theme_paper <- function(base_size = 12, base_family = "Arial") {
theme_bw(base_size = base_size, base_family = base_family) + ...
}
# In geom_text_repel and other text geoms
geom_text_repel(..., family = "Arial")
For plots with thousands of points (volcano plots, MA plots, scatter plots), rasterize the background points to prevent SVG bloat and Inkscape crashes. Keep foreground points and text as vectors for editability.
library(ggrastr)
ggplot() +
# Background points — rasterized (becomes single <image> in SVG)
rasterise(geom_point(
data = df_background,
aes(x = x, y = y),
colour = "grey70", alpha = 0.35, size = 0.9
), dpi = 1200) +
# Foreground points — vector (stays editable)
geom_point(
data = df_foreground,
aes(x = x, y = y, colour = group),
size = 1.2
) +
# Text labels — vector (stays editable)
geom_text_repel(...)
DPI for rasterized layers: Use 1200 dpi for publication quality. 300 dpi is sufficient for drafts.
ggrastr only works with ggplot2. For pheatmap or base R graphics, the cells are drawn as simple <rect> elements which are lightweight — rasterization is not needed.
When plots will be assembled into multi-panel figures, use identical width and height across all plots of the same type. This ensures the plotting windows align without rescaling in Inkscape.
# Define once, use everywhere
VOLCANO_WIDTH <- 6.8
VOLCANO_HEIGHT <- 5.5
ggsave_safe(..., width = VOLCANO_WIDTH, height = VOLCANO_HEIGHT)
Different axis limits or thresholds are fine — the key is that the outer plot dimensions match so panels can be tiled.
A helper that saves all 3 formats from a single call:
ggsave_safe <- function(filename, plot, width, height, units = "in", max_inches = 49, ...) {
# Guard against oversized plots
scale_factor <- max(width / max_inches, height / max_inches, 1)
if (scale_factor > 1) {
width <- width / scale_factor
height <- height / scale_factor
}
# PDF
ggplot2::ggsave(filename, plot = plot, width = width, height = height,
units = units, device = cairo_pdf, limitsize = FALSE, ...)
# PNG
ggplot2::ggsave(sub("\\.pdf$", ".png", filename), plot = plot,
width = width, height = height, units = units,
dpi = 300, limitsize = FALSE, ...)
# SVG
ggplot2::ggsave(sub("\\.pdf$", ".svg", filename), plot = plot,
width = width, height = height, units = units,
device = function(...) svglite::svglite(..., fix_text_size = FALSE),
limitsize = FALSE, ...)
}
library(ggrastr) # rasterise() for dense point layers
library(svglite) # clean SVG output
# cairo_pdf is built into grDevices (no extra package needed)
development
Phylogenetic tree visualization and formatting with ggtree (R) or iTOL (web). Use when rendering a phylogenetic tree as a figure, choosing tree layout, coloring branches or labels by taxonomy, collapsing clades, displaying support values, or adding overlays to a tree. Do NOT load for tree inference (use protein-phylogeny skill) or domain annotation (future separate skill).
development
Configure and manage Claude Code security protections for sensitive files, credentials, and data. Use when the user invokes /security-setup to set up or modify protections against unauthorized file access, credential exposure, or sensitive data leaks.
development
Script organization for data science analysis projects with numbered scripts, data/outs/ directories, and reproducibility conventions. Use when creating new analysis scripts in projects that follow data science conventions (numbered XX_ prefix scripts, outs/ directories, BUILD_INFO.txt). Do NOT load for documentation projects (Quarto books), infrastructure repos, or projects without data/outs/ directory structure.
testing
R renv package management for data science projects. Use when working with renv (renv.lock, renv::restore, renv::snapshot) in R analysis projects. Do NOT load for projects that do not use R or renv.