Consistent fonts and figure export for scientific publication workflows in R
Canton is a small R package for preparing figures for scientific manuscripts. It addresses two recurring problems:
R does not have one universal plot object. A ggplot2
plot, a grid grob, pheatmap and ComplexHeatmap
results, and a base R plot are represented and rendered differently.
Canton provides a single imagesave() interface that detects
common plot types and selects an appropriate rendering strategy. It also
provides checked font configuration, reusable export presets, a
publication-oriented ggplot2 theme, and colour scales.
The package does not submit files, contact journals, install system fonts, or silently download external resources. Output is written only when the user explicitly calls a saving function and selects an output directory.
| Function | Purpose |
|---|---|
imagesave() |
Save supported R plots to one or more formats |
figure_preset() |
Create reusable size, resolution, and format settings |
setfont() |
Validate a font or apply it within a code block |
fontcheck() |
Test whether a font family is available |
fontlist() |
List or search available system font families |
resetfont() |
Compatibility helper; restoration is automatic |
theme_canton() |
Apply a compact publication-oriented ggplot2 theme |
scale_*_canton_d() |
Apply Canton palettes to discrete ggplot2 scales |
scale_*_canton_c() |
Apply Canton palettes to continuous ggplot2 scales |
hue() |
Return a Canton colour palette |
using() |
Attach multiple installed packages quietly |
mkdir() |
Create output directories recursively and idempotently |
pwd() |
Return the current working directory |
Install the development version from GitHub:
install.packages("remotes")
remotes::install_github("ScienceAdvances/Canton")Load the package:
library(Canton)imagesave() does not coerce every input into a ggplot
object. Instead, it dispatches internally according to the input
type:
| Input | Rendering strategy |
|---|---|
ggplot |
Saved with ggplot2::ggsave() |
pheatmap |
Extracts and saves the returned $gtable |
ComplexHeatmap Heatmap or HeatmapList |
Drawn with ComplexHeatmap::draw() on each output
device |
grob, gTree, gtable, or
gList |
Saved as a grid graphical object |
recordedplot |
Replayed with grDevices::replayPlot() |
trellis |
Printed on a format-specific graphics device |
| zero-argument function | Executed on each requested graphics device |
NULL |
Captures the current plot with
grDevices::recordPlot() |
This approach preserves the intended rendering method of each
graphics system. Unsupported objects produce an informative error rather
than being passed implicitly to plot().
imagesave() supports:
"pdf");"png");"jpg" or "jpeg"); and"tif" or "tiff").One call can create one file or several formats. Each format is rendered directly from the original plot; Canton does not convert a previously created raster image into PDF.
library(ggplot2)
p <- ggplot(mtcars, aes(mpg, wt, colour = factor(cyl))) +
geom_point(size = 2.5) +
labs(
x = "Fuel economy (mpg)",
y = "Weight (1000 lbs)",
colour = "Cylinders"
)
paths <- imagesave(
p,
name = "figure_1",
outdir = file.path(tempdir(), "canton-figures"),
format = c("png", "pdf", "tiff"),
width = 7,
height = 5,
units = "in",
dpi = 300
)
pathsThe return value is an invisible named character vector containing
the complete paths of the generated files. Missing output directories
are created recursively. Existing files are overwritten by default; use
overwrite = FALSE to prevent replacement.
ggplot2 objects are saved through ggplot2::ggsave().
Device-specific settings such as JPEG quality and TIFF compression are
forwarded only to the relevant device.
imagesave(
p,
name = "figure_ggplot",
outdir = file.path(tempdir(), "canton-figures"),
format = c("pdf", "png"),
width = 180,
height = 120,
units = "mm",
dpi = 300
)Base R plotting functions generally draw immediately and do not
return a reusable object equivalent to a ggplot. After drawing a base
plot, imagesave() can capture the current device display
list:
grDevices::pdf(tempfile(fileext = ".pdf"))
grDevices::dev.control("enable")
plot(
mtcars$mpg,
mtcars$wt,
pch = 19,
xlab = "Fuel economy (mpg)",
ylab = "Weight (1000 lbs)"
)
abline(lm(wt ~ mpg, data = mtcars), col = "red", lwd = 2)
imagesave(
name = "figure_base",
outdir = file.path(tempdir(), "canton-figures"),
format = c("png", "pdf"),
width = 7,
height = 5,
dpi = 300
)
grDevices::dev.off()Current-plot capture is intended for the current R session. For scripts, pipelines, and non-interactive rendering, a plotting function is more robust because it can be executed separately on every output device:
draw_base_figure <- function() {
plot(mtcars$mpg, mtcars$wt, pch = 19)
abline(lm(wt ~ mpg, data = mtcars), col = "red", lwd = 2)
}
imagesave(
draw_base_figure,
name = "figure_base_function",
outdir = file.path(tempdir(), "canton-figures"),
format = c("png", "pdf", "tiff"),
width = 7,
height = 5,
dpi = 300
)pheatmap::pheatmap() invisibly returns a list that
contains a gtable. Canton extracts this component and saves
it as a grid object.
if (requireNamespace("pheatmap", quietly = TRUE)) {
set.seed(1)
mat <- matrix(stats::rnorm(100), nrow = 10)
ph <- pheatmap::pheatmap(
mat,
main = "Expression heatmap",
silent = TRUE
)
imagesave(
ph,
name = "figure_heatmap",
outdir = file.path(tempdir(), "canton-figures"),
format = c("png", "pdf", "tiff"),
width = 7,
height = 7,
dpi = 300
)
}pheatmap is a suggested package, not a required
dependency. Canton can be installed and used for other plot types
without it.
ComplexHeatmap objects are not equivalent to pheatmap
objects and do not contain a $gtable component. A
Heatmap or HeatmapList is laid out and
rendered by ComplexHeatmap::draw(). Canton opens each
requested output device and calls that method directly.
if (requireNamespace("ComplexHeatmap", quietly = TRUE)) {
set.seed(2)
mat <- matrix(stats::rnorm(100), nrow = 10)
ht <- ComplexHeatmap::Heatmap(
mat,
name = "z-score",
column_title = "ComplexHeatmap example"
)
imagesave(
ht,
name = "figure_complex_heatmap",
outdir = file.path(tempdir(), "canton-figures"),
format = c("png", "pdf", "tiff"),
width = 7,
height = 7,
dpi = 300
)
}The same interface also accepts a HeatmapList, including
objects constructed with the ComplexHeatmap + operator.
ComplexHeatmap is a suggested Bioconductor package; it is
needed only when saving these object types.
figure_preset() supplies reusable starting points for
common output layouts:
figure_preset("publication")
figure_preset("single_column")
figure_preset("double_column")
figure_preset("high_resolution")
figure_preset("presentation")For example:
imagesave(
p,
name = "figure_single_column",
outdir = file.path(tempdir(), "canton-figures"),
preset = "single_column"
)Preset values can be customised:
custom_preset <- figure_preset(
"publication",
format = c("pdf", "png"),
width = 6.5,
height = 4.5,
dpi = 600
)
imagesave(
p,
name = "figure_custom",
outdir = file.path(tempdir(), "canton-figures"),
preset = custom_preset
)Explicit arguments supplied to imagesave() override the
corresponding preset values. Presets are convenience defaults, not
permanent representations of any journal’s author instructions. Users
should always compare the selected values with the current requirements
of the target journal.
Font validation leaves session settings unchanged:
family <- setfont("sans", quiet = TRUE)
p <- p + theme_canton(base_family = family)
imagesave(p, outdir = tempdir(), family = family)Use a code block for temporary defaults. Settings restore automatically on normal return and errors. Print ggplot objects inside the block.
setfont("sans", code = {
print(p)
imagesave(p, outdir = tempdir())
})For base plots, pass family to imagesave()
and draw on its output device. No permanent graphics hooks or system
font registrations are installed. resetfont() remains as a
compatibility no-op because restoration is automatic. Use
fontcheck() and fontlist() to inspect
installed fonts.
theme_canton() is based on
ggplot2::theme_classic() and provides explicit defaults for
text, axes, ticks, legends, facet strips, margins, and optional grid
lines.
p_publication <- ggplot(
mtcars,
aes(mpg, wt, colour = factor(cyl))
) +
geom_point(size = 2.5) +
labs(
x = "Fuel economy (mpg)",
y = "Weight (1000 lbs)",
colour = "Cylinders"
) +
theme_canton(
base_size = 10,
grid = "none",
legend_position = "right"
)Grid options are "none", "major", and
"both". The font defaults to the family inside a
setfont(code = ...) block, or to the portable
"sans" alias.
List available palettes:
hue()Palette matching is case-insensitive:
colours <- hue("NPG")
dark_colours <- hue("dark2")Use Canton palettes directly with discrete ggplot2 scales:
p_publication + scale_colour_canton_d("NPG")
ggplot(mtcars, aes(factor(cyl), fill = factor(am))) +
geom_bar() +
scale_fill_canton_d("Dark2") +
theme_canton()Continuous colour and fill scales are also available:
ggplot(mtcars, aes(mpg, wt, colour = qsec)) +
geom_point(size = 2.5) +
scale_colour_canton_c("NPG") +
theme_canton(grid = "major")Both British (colour) and American (color)
spellings are exported for colour scales.
| Format | Typical use | Important properties |
|---|---|---|
| manuscript submission, vector artwork, typesetting | text and vector lines scale without loss; embedded raster layers retain their own resolution | |
| TIFF | journal submission and print workflows | commonly requested at 300 or 600 DPI; files can be large |
| PNG | reports, slides, web pages, line and text graphics | lossless raster format; supports transparent backgrounds on suitable devices |
| JPEG | photographs and continuous-tone images | lossy compression; generally not preferred for sharp text or statistical line art |
For raster output, physical size and DPI determine pixel dimensions. A 7-inch-wide image at 300 DPI is 2100 pixels wide. DPI does not define the resolution of vector text and lines in a PDF, although raster content embedded inside the PDF still has a finite resolution.
Example high-resolution TIFF output:
imagesave(
p,
name = "figure_print",
outdir = file.path(tempdir(), "canton-figures"),
format = "tiff",
width = 7,
height = 5,
units = "in",
dpi = 600,
compression = "lzw"
)TIFF compression capabilities differ between graphics devices. Canton avoids passing unsupported compression arguments to the default macOS Quartz TIFF device.
using() accepts bare names, strings, or character
vectors. It suppresses package startup messages, returns a named logical
vector, and reports packages that could not be loaded. It never installs
missing packages.
using(ggplot2, grid)
packages <- c("ggplot2", "grid")
loaded <- using(packages)
loadedmkdir() creates directories recursively. Calling it
again for an existing directory is silent and safe.
figure_directory <- mkdir(
file.path(tempdir(), "canton-project", "figures")
)
figure_directorypwd() returns the current working directory:
pwd()Canton follows these principles:
using();setfont();setfont(code = ...) restore
automatically on exit.The test suite covers:
pheatmap output when the suggested package is
available;Heatmap and
HeatmapList output when the suggested package is
available;The package is intentionally focused on figure creation and small supporting workflow helpers. It does not attempt to analyse scientific data, enforce a particular journal’s changing submission policy, inspect manuscript content, or guarantee font embedding by third-party PDF software.
GPL (>= 3)