Search code examples
rghostscripteps

How to write EPS files using R on Windows 10 from end-to-end?


It's that dreaded time of generating publication ready EPS files for a science manuscript submission. The data, line-art figures for plots, is in R (RStudio coupled with R version 4.02). I've meet this challenge before but on a very well populated Linux machine. Today, I am using x64 bit Windows 10 that was only installed four months ago.

The code is:

require(graphics)
options(printcmd = 'gsprint -ghostscript gswin64')
postscript(file = tempfile("Rps."), print.it=TRUE, family="sans")
plot_grid(image1Agg, image1Bgg, image1Cgg, labels = c('A','B','C'), label_size = 12, label_fontfamily = "sans")
dev.off()

I installed the latest version of gsview and ghostscript for Win10 and to circumvent the annoyance of escaping character confusion with directory structures and the "Program Files", I added the path to both sets of binaries to PATH.

When I execute the code on the Console of RStudio I get...

Warning message:
In dev.off() : error from postscript() in running:
    gsprint -ghostscript gswin64 C:\Users\yewro\AppData\Local\Temp\Rtmp0Yqsfg\Rps.17d065ef330

If I execute this on Windows Commmand I see a lot more (as I was expecting given the R postscript() documentation):

Copyright (C) 2003-2006, Ghostgum Software Pty Ltd.  All Rights Reserved.
2006-02-24 gsprint 1.9
GPL Ghostscript 9.53.1 (2020-09-14)
Copyright (C) 2020 Artifex Software, Inc.  All rights reserved.
This software is supplied under the GNU AGPLv3 and comes with NO WARRANTY:
see the file COPYING for details.
Loading NimbusSans-Regular font from %rom%Resource/Font/NimbusSans-Regular... 4216964 2747487 1743856 440491 2 done.
Loading NimbusSans-Bold font from %rom%Resource/Font/NimbusSans-Bold... 4283076 2921313 1764056 451768 2 done.
Loading NimbusSans-Italic font from %rom%Resource/Font/NimbusSans-Italic... 4470388 3128353 1764056 459255 2 done.
Loading NimbusSans-BoldItalic font from %rom%Resource/Font/NimbusSans-BoldItalic... 4677900 3341192 1784256 470184 2 done.
Loading StandardSymbolsPS font from %rom%Resource/Font/StandardSymbolsPS... 4771268 3431892 1784256 477717 2 done.
GPL Ghostscript 9.53.1: **** Could not open the file 000002dc .
Error: /invalidfileaccess in --showpage--
Operand stack:
   1   true
Execution stack:
   %interp_exit   .runexec2   --nostringval--   showpage   --nostringval--   2   %stopped_push   --nostringval--   showpage   showpage   false   1   %stopped_push   1990   1   3   %oparray_pop   1989   1   3   %oparray_pop   1988   1   3   %oparray_pop   showpage   1977   1   3   %oparray_pop   1833   1   3   %oparray_pop   --nostringval--   %errorexec_pop   .runexec2   --nostringval--   showpage   --nostringval--   2   %stopped_push   --nostringval--   showpage   1840   0   3   %oparray_pop   showpage   showpage
Dictionary stack:
   --dict:733/1123(ro)(G)--   --dict:0/20(G)--   --dict:105/200(L)--
Current allocation mode is local
Last OS error: Permission denied
Current file position is 5271
GPL Ghostscript 9.53.1: Unrecoverable error, exit code 1

Various scenarios I've tried:

  1. Checked to find that the generated .Rps file does exist.
  2. Generated the .Rps file in a directory more local to the code i.e., both Documents and the current working environment or RStudio.
  3. Tried to run the gsprint from the Windows command, specifying absolute paths in one instance and relying on PATH in the second.
  4. I've used both gswin64 and gswin64c, GUI and console, respectively, with no difference.

From reading forums, I know that ghostscript throws a different error if the file to process doesn't exist (undefinnedfilename, I think!), so I'm confident that it sees something, however this one is throwing invalidfileaccess along with the OS throwing "permission denied". Thus I also tried:

  1. Running the above efforts on the Windows command as administrator (is this Windows answer to sudo?!).

I've trawled through the R postscript() documentation, and I'm somewhat confused by the entry under Examples:

## On Windows:
options(printcmd = 'redpr -P"\\printhost\lw"')
postscript(file = tempfile("Rps."), print.it = TRUE)
# produce the desired graph(s)
dev.off()              # send plot file to the printer
## alternative using GSView 4.x :
options(printcmd = '/GhostGum/gsview/gsprint -query')

Note: I haven't tried: options(printcmd = 'redpr -P"\printhost\lw"'), firstly, that line contains invalid R syntax, and secondly, I had more experience working with GSView so I was instantly drawn to the alternative approach.

Interestingly, gsprint -query refers to "Show printer setup dialog" when looking at the Ghostscript instructions. Call it on the command line and it tells me to use -ghostscript, which is what I asked it to do..

All in all, zero luck generating an EPS file but I have a feeling it's command argument related with gswin64c. Any help is much appreciated.

SOLUTION Thanks to KenS, had it not been for their input I would probably still be troubleshooting. Turns out that the version of Ghostscript was the issue (see comments and answer). To generate EPS images for publication I used an alternative approach in R:

On the RStudio Console (NOT within an RMarkdown document):

library("cowplot")
#code for my ggplot2 objects here: image1Agg, image1Bgg, and image1Cgg
#for example

image1Cgg <- ggplot(combinedFrequenciesDF_ByClass, aes(x=reorder(displayDescription, -Frequency), y=Frequency)) + geom_bar(stat="identity") + theme_classic() + theme(text = element_text(size = 10), axis.text.x = element_text(angle = 45, hjust=1),  axis.text = element_text(color="black")) + xlab("First Prescription (Drug Class)") + ylab("Patients") + scale_y_continuous(expand = expansion(mult = c(0,.1)))

image1Grid <- plot_grid(image1Agg, image1Bgg, image1Cgg, labels= c("(a)","(b)","(c)"), label_size = 10, ncol = 1, vjust = 1, hjust=0.03)
image1Grid #in the RStudio console so it appears in the RStudio Plots window
ggsave("image1.eps", units="cm", width=15, height=20, device="eps")

On Windows 10 with the latest GSView and Ghostscript installed, the above generated an EPS file. I won't pretend to know how ggsave works with postscripts, I just know on my OS with those two programs installed correctly, it works. I also found that the cow_plot alternative works:

save_plot("image1.eps", image1Grid, base_height=7.9, base_width=5.9) #this is in inches

Both save_plot and ggsave generated an EPS image which was 15cm width by default, which left 3 cm on the left and right margin. I hope this helps others.


Solution

  • Recent versions of Ghostscript (unfortunately you've trimmed off the version from stdout, but it's (C) 2020 so it must be fairly recent) have altered the default behaviour, because of security vulnerabilities. The default is now to run in SAFER mode (previously the default was NOSAFER). This means that PostScript programs are not permitted to open/read/write/delete files on disk.

    It looks like the file you are running is encountering an invalidfileaccess error when trying to write a file. The filename it's trying to write '00002dc' isn't mentioned as the output file on the Ghostscript command line, and that's the only way Ghostscript has of inferring that a file access should be permitted. Basically if the file isn't named on the command line, and you haven't told GS that it has permission to read/write to the given directory, then it's not permitted to access that file.

    You could try using -dNOSAFER, I've no real idea how to add this to gsprint, which is not part of Ghostscript, but possibly (reading the gsprint.htm file) -option "-dNOSAFER" might work.

    I wouldn't really recommend that as a long-term solution though.