Re: Help in using gsapi_set_stdio() to capture stdout into a memory buffer

"Joaquim Luis" <[email protected]>
Newsgroups gmane.comp.printing.ghostscript.devel
Organization UALG
Message-ID <op.yety9icpdbaoe8@macmarilu>
Hi again. I'm afraid I have to get back to this because I got stuck in a  
mystery.
The attached code, sorry for the #ifdefs mess used for debugging, works  
when compiled as a standalone program (use the -DI_AM_MAIN pre-processor  
flag). It works only with small files where we can just do

gs_rasterize ps_file.ps

However, if I built it as a dll and send as argument the postscript code  
as a string, which will be called either from Matlab or Julia, than the  
call

	gsapi_run_string (GSinst, ps, 0, &GSexitCode)

returns an error code GSexitCode = -12

I confirmed that the problem is not in the PS in memory with the code  
chunk under the #if 0. That is, that piece of code runs well from the  
stand-alone but fails when run from a dll.

I'm completely clueless with this. Can it be because gs tries to do  
something that it has not right to do when controlled by another process  
(the one that calls the dll)?

Thanks again

Joaquim

> At 16:17 17/03/2016 +0000, Joaquim Luis wrote:
>
>
>> NOTE: the code file is attached, as it was in my first message.
>
> I'm afraid (for me) your email ended up in a Google spam folder and I  
> had to dig it out manually. Somewhere in there the code obviously fell  
> off, apologies for that.
>
>
>              Ken

_______________________________________________
gs-devel mailing list
[email protected]
http://ghostscript.com/cgi-bin/mailman/listinfo/gs-devel
gs_rasterize.c (application/octet-stream, 7.2 KB)
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#include <fcntl.h>
#include <sys/stat.h>

#ifdef _WIN32
#	include "ierrors.h"
#	include "iapi.h"
#	include <process.h>
#	include <io.h>
#	define dup2 _dup2
#	define fileno _fileno
#	define close _close
#	define stat _stat
#else
#	include "ghostscript/ierrors.h"
#	include "ghostscript/iapi.h"
#	include <unistd.h>
#endif

#	ifdef _WIN32
#		ifdef LIBRARY_EXPORTS
#			define LIBSPEC __declspec(dllimport)
#		else
#			define LIBSPEC __declspec(dllexport)
#		endif /* ifdef LIBRARY_EXPORTS */
#	else /* ifdef _WIN32 */
#		define LIBSPEC
#	endif /* ifdef _WIN32 */

/* By default, we use the standard "extern" declarations. */
#	define EXTERN_MSC_ extern LIBSPEC

#ifndef I_AM_MAIN
EXTERN_MSC_ unsigned char *gsrasterize_rip (char *ps, int dpi, int dim[]);
EXTERN_MSC_ void gsrasterize_free (char *img);
#endif

char *ps_test = "newpath\n"
"100 600 moveto 200 650 lineto 100 700 lineto\n"
"closepath\n"
"gsave\n"
"0.5 setgray\n"
"fill\n"
"grestore\n"
"4 setlinewidth\n"
"stroke\n"
"/Times-Roman findfont\n"
"18 scalefont\n"
"setfont\n"
"newpath\n"
"150 600 moveto\n"
"(Julia-Ghostscript in action) show\n"
"/csquare {\n"
   "newpath\n"
   "0 26 moveto\n"
   "0 1 rlineto\n"
   "1 0 rlineto\n"
   "0 -1 rlineto\n"
   "closepath\n"
   "setrgbcolor\n"
   "fill\n"
"} def\n"
"\n"
"20 20 scale\n"
"\n"
"1 5 translate\n"
"1 0 0 csquare\n"
"\n"
"1 0 translate\n"
"0 1 0 csquare\n"
"\n"
"1 0 translate\n"
"0 0 1 csquare\n"
"showpage";

void gsrasterize_free (char *img) {
	/* Free memory allocated in this file. Needed because the cross boundary dll on Windows */
	if (img) free (img);
}

/* stdio functions */
static int GSDLLCALL gsdll_stdin (void *instance, char *buf, int len) {
	return 0;
}

static int GSDLLCALL gsdll_stdout (void *instance, const char *str, int len) {
	//fwrite(str, 1, len, fp);
	//fflush(fp);
	return len;
}

static int GSDLLCALL gsdll_stderr (void *instance, const char *str, int len) {
	fwrite (str, 1, len, stderr);
	fflush (stderr);
	return len;
}

#define ROWS  0
#define COLS  1
#define BANDS 2

/* The I_AM_MAIN brach is initially intended as a dev/debug feature, where we can build this
   as a stand alone program and send for example a PS file name.
*/

#ifdef I_AM_MAIN
int main (int argc, char *argv[]) {
	int   dpi = 300;
	int   dim[3];			/* Not meant to be used for now */
	int   arg_c = argc;
	char *ps = NULL;
#else
unsigned char *gsrasterize_rip (char *ps, int dpi, int dim[]) {
	int   arg_c = 0;
#endif
	int GScode = 0, GSexitCode, n, fd[2] = {0, 0};
	size_t size;
	char buf[1025], t[16] = {""}, s_dpi[16] = {"-r300x300"}, *ps_t;
	unsigned char *img = NULL;
	void *GSinst = NULL; 
	FILE *fp = NULL;

	char *GSargs[] = {	/* Presumably need to specify dpi at some point */
		"", "-sDEVICE=ppmraw", "-dBATCH", "-dNOPAUSE", "-sOutputFile=-", "-q", NULL
	};
	char line[BUFSIZ];
	struct stat F;

#ifdef I_AM_MAIN
	//char line[BUFSIZ];
	//struct stat F;

	if (arg_c > 1) {
		stat (argv[1], &F);
		if ((fp = fopen (argv[1], "r")) == NULL) {
			fprintf(stderr, "Error reading PS %s file. Bye bye\n", argv[1]);
			return -1;
		}
		fprintf (stderr, "PS file size is %d bytes\n", (int)F.st_size);
		ps = (char *)calloc(F.st_size+1, sizeof(char));
		while (fgets (line, BUFSIZ, fp))
			strcat(ps, line);
	}
	else {
		ps = ps_test;
	}
#endif

	if (dpi > 0) sprintf (s_dpi, "-r%dx%d", dpi, dpi); else sprintf (s_dpi, "-r300x300");
	GSargs[6] = s_dpi;

	if ((GScode = gsapi_new_instance (&GSinst, NULL )) < 0 ) {
		fprintf (stderr, "Error: GhostScript gsapi_new_instance failed (error code %d)\n", GScode);
#ifdef I_AM_MAIN
		if (arg_c > 1) free (ps);
		return -1;
#else
	return NULL;
#endif
	}

	gsapi_set_stdio (GSinst, gsdll_stdin, gsdll_stdout, gsdll_stderr);

	if ((GScode = gsapi_init_with_args (GSinst, 6, GSargs)) < 0 ) {
		fprintf (stderr, "Error: GhostScript gsapi_init_with_args failed (error code %d)\n", GScode);
		gsapi_exit (GSinst);
		gsapi_delete_instance (GSinst);
#ifdef I_AM_MAIN
		if (arg_c > 1) free (ps);
		return -1;
#else
		return NULL;
#endif
	}

	/* Establish a pipe to read from stdout */
#ifdef _WIN32
	if (_pipe(fd, 145227600, O_BINARY) == -1) {
		fprintf(stderr, "Error: failed to open the pipe\n");
		gsapi_exit(GSinst);
		gsapi_delete_instance(GSinst);
		if (arg_c > 1) free(ps);
		return -1;
	}
#else
	pipe (fd);
#endif

	if (dup2 (fd[1], fileno (stdout)) < 0) {	/* Failed to duplicate pipe */
		fprintf (stderr, "Error: Failed to duplicate pipe\n");
		gsapi_exit (GSinst);
		gsapi_delete_instance (GSinst);
#ifdef I_AM_MAIN
		if (arg_c > 1) free (ps);
		return -1;
#else
		return NULL;
#endif
	}
	close (fd[1]); 		/* Close original write end of pipe */

#if 0
	stat ("V:\\lili.ps", &F);
	if ((fp = fopen ("V:\\lili.ps", "r")) == NULL) {
		fprintf(stderr, "Error reading PS %s file. Bye bye\n", "V:\\lili.ps");
		return -1;
	}
	ps_t = (char *)calloc(F.st_size+1, sizeof(char));
	while (fgets (line, BUFSIZ, fp))
		strcat(ps_t, line);
	if (gsapi_run_string (GSinst, ps_t, 0, &GSexitCode) < 0)
		fprintf (stderr, "Error: GhostScript gsapi_run_string failed (error code %d)\n", GSexitCode);
	free (ps_t);
#endif

	if ((n = gsapi_run_string (GSinst, ps, 0, &GSexitCode)) < 0) {
		fprintf (stderr, "Error: GhostScript gsapi_run_string failed (error code %d\t%d)\n", GSexitCode, n);
		gsapi_exit (GSinst);
		gsapi_delete_instance (GSinst);
#ifdef I_AM_MAIN
		if (arg_c > 1) free (ps);
		return -1;
#else
		return NULL;
#endif
	}

	/* Successful so far so let us read the raw pbm image into memory */
	
	n = read (fd[0], buf, 3U);				/* Consume first header line */
	while (read (fd[0], buf, 1U) && buf[0] != '\n') 	/* OK, by the end of this we are at the end of second header line */
		fprintf(stderr, "%c", buf[0]);
	n = 0;
	while (read(fd[0], buf, 1U) && buf[0] != ' ') 		/* Get string with number of columns from 3rd header line */
		t[n++] = buf[0];
	dim[COLS] = atoi (t);
	n = 0;
	while (read(fd[0], buf, 1U) && buf[0] != '\n') 		/* Get string with number of rows from 3rd header line */
		t[n++] = buf[0];
	t[n] = '\0';						/* Make sure no character is left from previous usage */

	while (read(fd[0], buf, 1U) && buf[0] != '\n')		/* Consume fourth header line */
		fprintf(stderr, "%c", buf[0]);

	dim[ROWS] = atoi (t);
	dim[BANDS] = 3;	/* This might change if we do monochrome at some point */

	size = dim[ROWS] * dim[COLS] * dim[BANDS];	/* Determine number of bytes needed to hold image */
	if ((img = malloc (size)) == NULL) {
		fprintf (stderr, "Error: Unable to allocate space for image [%d bytes]\n", (int)size);
		gsapi_exit (GSinst);
		gsapi_delete_instance (GSinst);
#ifdef I_AM_MAIN
		if (arg_c > 1) free (ps);
		return -1;
#else
		return NULL;
#endif
	}
	/* Read the image */
	if ((n = read (fd[0], img, size)) >= 0) {
		fprintf (stderr, "read %d bytes from the pipe:\n %s\n", n, buf);
	}
	else
		perror ("read");

	/* Clean up and return image */
	
	gsapi_exit (GSinst);
	gsapi_delete_instance (GSinst);
#ifdef I_AM_MAIN
	if (arg_c > 1) free (ps);
	n = write(fileno(stdout), img, size);
	fprintf(stderr, "wrote %d bytes to stdout\n", n);
	gsrasterize_free (img);
	return 0;
#else
	return img;
#endif
}
lmpx.com only provides a reader for public news (NNTP) servers. It is not affiliated with the servers or forums shown here and is not responsible for the content of articles, which is written by their respective authors.