Search code examples
rggplot2plotexportwmf

Force ggsave to vectorize point geoms in .wmf-files


Plots produced with R are not usable for publication if they cannot be exorted properly. I work on a Windows Machine and use MS Word 2016 for all writing purposes. So, I wish to export my plots as .wmf files (.emf would also do, I suppose).

I produce all graphs with ggplot2, so ggsave (device = "wmf") seems a good choice, I suppose. However, I have a major problem with the resulting files: point geoms seem to be printed as raster instead of vector format. Here is an example for producing a simple scatterplot:

library (ggplot2)    

plot_data <- data.frame (a = runif (1:20), 
                         b = seq (1:20))

x11 (width =  3, height = 3)

ggplot (data = plot_data, mapping = aes (x = a, y = b)) +
    geom_point () +
    labs (x = "my x-label", y = "my y-label") +
    theme (panel.background = element_blank(),
           panel.border = element_rect (fill = NA, size = 0.7),
           axis.ticks = element_line (color = "black", lineend = "round"),
           axis.ticks.length = unit (2, "mm"),
           axis.text = element_text (color = "black"),
           plot.margin = unit(rep (0, 4), "cm")
           )

I save the plot with the following code:

ggsave(filename = "my_file.wmf", device = "wmf")

When I open the plot in MS Word or Libre Office, I see that the points are not rendered in good quality, at all. In Libre Office Draw, a point looks like this (zoomed in quite a lot):

enter image description here

In MS Word, the plot looks like this:

enter image description here

with these "points":

enter image description here

The labels and axes, however, are ok. MS Word:

enter image description here

Libre Office Draw:

enter image description here

I suppose that the labels, tick annotations and axes (and even circles around the points) are stored in vector format, whereas the point geoms seem to be stored as rasters. The resulting plots are not useable, I fear. So, I want to find an option to force ggsave () to vectorize point geoms instead of printing raster. I hope very much someone can help - I urgently need a simple way to export plots from R for publication in order to convince my lab to rely more on R.


Solution

  • You can directly create a fully vectorized EMF (or EMF+) file in R using the add-on CRAN package devEMF. This avoids any need to export to SVG and later convert to EMF. I'm not sure why the ggsave(..., device="wmf") call (which uses the window API to generate the file) is using raster plotting symbols here, but the functions in devEMF definitely use vectors (I say this both as the developer of devEMF and confirmed by zooming in with LibreOffice).

    After installing devEMF, you can run the example code from the original question and then:

    library(devEMF)
    ggsave(filename = "my_file.emf", device = emf)
    

    NOTE: emf (without quotes) used in the code above is the name of a function defined by devEMF (analogous to x11 used in the original question). "emf" (with quotes) would be interpreted by ggsave as a request to the use windows API. You want the former without quotes, not the latter with quotes.

    Alternatively, the original call to x11 could be replaced and the emf file generated directly without the need for ggsave:

    library (ggplot2)
    library (devEMF)
    
    plot_data <- data.frame (a = runif (1:20), 
                             b = seq (1:20))
    
    emf (file = "my_file.emf", width =  3, height = 3)
    
    ggplot (data = plot_data, mapping = aes (x = a, y = b)) +
        geom_point () +
        labs (x = "my x-label", y = "my y-label") +
        theme (panel.background = element_blank(),
               panel.border = element_rect (fill = NA, size = 0.7),
               axis.ticks = element_line (color = "black", lineend = "round"),
               axis.ticks.length = unit (2, "mm"),
               axis.text = element_text (color = "black"),
               plot.margin = unit(rep (0, 4), "cm")
               )
    
    dev.off() #must close device to close file!
    

    In my experience, MS Office support for EMF & EMF+ is as complete as for WMF, so generating EMF is a reasonable solution for this use case. If you run into compatibility problems, look at the emfPlus* arguments to the emf function (see help("emf", package="devEMF")).