Export Graphics with exact size and font size

Here is how it worked:

p1 <- ggplot(aes(y = Var1, x = Treat,  na.rm=TRUE, fill=Location), data = df1) +
  stat_boxplot(geom ='errorbar', width= 0.5) +
  geom_boxplot(outlier.shape = 1, outlier.size = 2, linetype="solid", size = 1) +
  facet_grid( Year ~ Location) + 
  scale_y_continuous(limits=c(-20,60))  +
  scale_fill_manual(values=c("#E69F00", "#009E73", "#0072B2", "#D55E00")) +
  ylab("Magic Skills") +
  xlab("Magic Juice") +
  theme(text=element_text(family="Arial", size=36*96/72),
        axis.text.x = element_text(angle = 30, hjust = 1),
        line=element_line(size=1),
        rect=element_rect(colour="red", size=1),
        panel.background=element_rect(colour="black",fill="white", size=1),
        strip.background=element_rect(colour="dark grey", fill="black"),
        strip.text=element_text(face="bold", colour="white"),
        panel.grid.major=element_blank(),
        panel.grid.minor=element_blank(),
        legend.position="none");p1


ggsave(p1, file="Results/Figures/Trial.svg", width=35*1.25, height=20*1.25, units="cm", dpi=96) 

With this code, I got an 35x20 cm plot both in Inkscape and in Powerpoint. Furthermore the size of the letters was pretty much the size of letters in powerpoint.

This time I chose letter size 36pt, since this is more useful for Poster-presentations. I did not make further trials with changing the dpi.

Here my findings so far:

  1. R uses 72 dpi by default, the formula: size * new resolution DPI / 72 DPI, e.g.: 36 * 96/72, delivers almost similar pt sizes in Microsoft office documents. Irrespective of both the dpi settings used in ggsave as well as the height and width settings.

  2. 10 cm specified in R via ggsave() correspond to 8.060 cm in Inkscape. Multiplying the admired width/height by 1.25 delivers correct image sizes in Inkscape and also in MS Office, irrespective of the dpi set in the Export settings.

Something that makes me still wonder is that although I made the same settings for font size for the WHOLE plot via "text=element_text(family="Arial", size=36*96/72)" Label titles are taller than tick labels or text in the facet boxes.

best wishes, Pharcyde


There's a few things to consider:

  • SVG units – Note the discussion about user units, and pt vs px
  • grid graphics parameters and how they're interpreted by the device
  • ggplot2, and how its theme settings affect the font size

Here's a pure grid example that illustrates the second point,

library(grid)
g <- grobTree(rectGrob(), textGrob("test", gp=gpar(fontsize=12)))

ggplot2::ggsave("size.svg", g, width = 10, height=10, units = "in")

tmp <- readLines("size.svg")

grep("test", tmp, value = TRUE)
# "<text x='350.33' y='364.30' style='font-size: 12.00px; font-family: Arial;' textLength='19.34px' lengthAdjust='spacingAndGlyphs'>test</text>"
grep("viewBox", tmp, value = TRUE)
# "<svg xmlns='http://www.w3.org/2000/svg' xmlns:xlink='http://www.w3.org/1999/xlink' viewBox='0 0 720.00 720.00'>"

So short answer is that the svg produced by R (svglite here) contains a seemingly consistent font size (12px), but the 10 inches is interpreted as 720px regardless of dpi. Looking at the source this 72 factor comes from R graphics conventions (typically devices follow the pdf device, which defaults to a 72DPI resolution. Why this is not affected by the various dpi/res/pointsize parameters is a mystery to me).

The gridSVG package by-passes the usual graphics engine system, and appears to produce more straight-forward sizes,

library(gridSVG)
gridsvg("size.svg", res = 1000, width=10, height=5)
grid.draw(g)
dev.off()

tmp <- readLines("size.svg")

grep("viewBox", tmp, value = TRUE)
# "<svg xmlns=\"http://www.w3.org/2000/svg\" xmlns:xlink=\"http://www.w3.org/1999/xlink\" width=\"10000px\" height=\"5000px\" viewBox=\"0 0 10000 5000\" version=\"1.1\">"

though this time the font size is scaled (it is 12 for the default 72DPI resolution),

grep("font-size", tmp, value = TRUE)
# "<text x=\"0\" y=\"0\" id=\"GRID.text.41.1.1.text\" text-anchor=\"middle\" font-size=\"166.67\" fill=\"rgb(0,0,0)\" fill-opacity=\"1\">"                                       

On the ggplot2 side, things get even murkier because of some built-in settings for the inheritance of theme elements, rel()ative scaling, scales, that may or may not be documented (and have changed in the past IIRC).


"18pt" in an SVG is not the same as 18pt on a printed page.

One point ("pt") in a CSS style attribute has been defined - like in the printing industry - as 1/72 of an inch. However in CSS, an inch is not a real-world inch, but instead it is 96 "CSS pixels".

That dpi of 96, is a arbitrary value chosen a long time ago. Computer monitors aren't 96 pixels per inch any more (if any ever were).

If you want something to match the real world - for example one real inch on a printed page or screen - you will have to test your environment and output method, and apply a scaling factor to your SVG units. Or to your conversion technique.

Tags:

Svg

R

Ggplot2