1 /*
2  * FIG : Facility for Interactive Generation of figures
3  * Copyright (c) 1985-1988 by Supoj Sutanthavibul
4  * Parts Copyright (c) 1989-2015 by Brian V. Smith
5  * Parts Copyright (c) 1991 by Paul King
6  * Parts Copyright (c) 2016-2020 by Thomas Loimer
7  *
8  * Any party obtaining a copy of these files is granted, free of charge, a
9  * full and unrestricted irrevocable, world-wide, paid up, royalty-free,
10  * nonexclusive right and license to deal in this software and documentation
11  * files (the "Software"), including without limitation the rights to use,
12  * copy, modify, merge, publish, distribute, sublicense and/or sell copies of
13  * the Software, and to permit persons who receive copies from any such
14  * party to do so, with the only requirement being that the above copyright
15  * and this permission notice remain intact.
16  *
17  */
18 
19 /*
20  * Call ghostscript to get the /MediaBox, and convert eps/pdf to bitmaps.
21  * Autor: Thomas Loimer, 2020.
22  */
23 
24 #ifdef HAVE_CONFIG_H
25 #include "config.h"
26 #endif
27 
28 #include <inttypes.h>		/* includes stdint.h */
29 #include <math.h>
30 #include <stdbool.h>
31 #include <stdio.h>
32 #include <stdlib.h>
33 #include <string.h>
34 #include <X11/X.h>		/* TrueColor */
35 
36 #ifdef HAVE_GSLIB
37 #include <ghostscript/gdevdsp.h>
38 #include <ghostscript/gserrors.h>
39 #include <ghostscript/iapi.h>
40 #endif
41 
42 #include "object.h"
43 #include "resources.h"
44 #include "f_util.h"		/* map_to_pattern(), map_to_mono() */
45 #include "w_msgpanel.h"		/* file_msg() */
46 
47 /*
48  * Exported functions: gs_mediabox(), gs_bitmap().
49  * These are currently only used in f_readeps.c, hence
50  * an extra header file is not provided.
51  */
52 
53 #define BITMAP_PPI	160	/* the resolution for rendering bitmaps */
54 #define GS_ERROR	(-2)
55 
56 /*
57  * Link into the ghostscript library.
58  * If invoked via the library, ghostscript calls three callback functions when
59  * wishing to read from stdin or write to stdout or stderr, respectively.
60  * However, if a device is given, ghostscript writes directly to stdout. This
61  * was found rather late, therefore some functions below are more modular than
62  * necessary.
63  * Here, set up calls to ghostscript and callback functions to read a pdf
64  * file, scanning the output for the /MediaBox specification.
65  *
66  * If linking into the library by dlopen() fails, call the ghostscript
67  * executable via popen("gs..", "r").
68  */
69 
70 #ifdef HAVE_GSLIB
71 /*
72  * callback data struct
73  * A pointer to this struct can be passed to ghostscript, which
74  * is then passed to the callback functions.
75  */
76 struct _callback_data {
77 	int	*bb;		/* for stdout_mediabox() callback function */
78 	char	*errbuf;	/* fixed buffer */
79 	int	errsize;
80 };
81 
82 /* callback functions  */
83 static int
stdin_void(void * caller_handle,char * buf,int len)84 stdin_void(void *caller_handle, char *buf, int len)
85 {
86 	(void)caller_handle;
87 	(void)buf;
88 
89 	return len;
90 }
91 
92 static int
stderr_buf(void * caller_handle,const char * str,int len)93 stderr_buf(void *caller_handle, const char *str, int len)
94 {
95 	struct _callback_data	*data = (struct _callback_data *)caller_handle;
96 	static int		pos = 0;
97 
98 	/* buffer full, comparison for == should be sufficient */
99 	if (pos >= data->errsize - 1)
100 		return len;
101 
102 	if (pos + len >= data->errsize)	/* leave space for a terminating '\0' */
103 		len = data->errsize - pos - 1;
104 	memcpy(data->errbuf + pos, str, (size_t)len);
105 	pos += len;
106 	data->errbuf[pos] = '\0';
107 
108 	return len;
109 }
110 
111 static int
stdout_mediabox(void * caller_handle,const char * str,int len)112 stdout_mediabox(void *caller_handle, const char *str, int len)
113 {
114 	struct _callback_data	*data = (struct _callback_data *)caller_handle;
115 	int	err;
116 	double	fbb[4];
117 
118 	/* This rests on the assumption that ghostscript writes the required
119 	   information all at once to str. Should use a buffer, instead. */
120 
121 	/* gs always uses full stops as decimal character. The locale is already
122 	   set to C in read_pdf() */
123 	err = sscanf(str, "[%lf %lf %lf %lf]", fbb, fbb+1, fbb+2, fbb+3);
124 	if (err == 4) {
125 		data->bb[0] = (int)floor(fbb[0]);
126 		data->bb[1] = (int)floor(fbb[1]);
127 		data->bb[2] = (int)ceil(fbb[2]);
128 		data->bb[3] = (int)ceil(fbb[3]);
129 	} else {
130 		/* Either the pdf is corrupt, which yields a matching failure,
131 		 * or a read error occured or EOF is reached.
132 		 * Ghostscript returns with zero from a corrupt pdf, and writes
133 		 * error information to stdout.
134 		 * Use the bounding box to report a, most likely, corrupt pdf.
135 		 */
136 		data->bb[0] = data->bb[1] = data->bb[2] = 0;
137 		data->bb[3] = GS_ERROR;
138 	}
139 
140 	return len;
141 }
142 
143 
144 /*
145  * Link into the ghostscript library with argcnew and argvnew[] for gs >= 9.50,
146  * argcold and argcold[] for gs < 9.50.
147  * Return 0 on success, -1 if the library could not be called, -2 (GS_ERROR)
148  * on a ghostscript error.
149  */
150 static int
gslib(struct _callback_data * data,int (* gs_stdin)(void *,char *,int),int (* gs_stdout)(void *,const char *,int),int (* gs_stderr)(void *,const char *,int),int argcnew,int argcold,char * argvnew[],char * argvold[])151 gslib(struct _callback_data *data, int (*gs_stdin)(void *, char*, int),
152 			int (*gs_stdout)(void *, const char*, int),
153 			int (*gs_stderr)(void *, const char*, int),
154 			int argcnew, int argcold,
155 			char *argvnew[], char *argvold[])
156 {
157 	int		i;
158 	int		code;
159 	const int	call_gsexe = -1;
160 	int		argc;
161 	char		**argv;
162 	struct gsapi_revision_s	rev;
163 	void		*minst = NULL;	/* must be initialized to NULL */
164 
165 
166 	/* get gs version */
167 	if (gsapi_revision(&rev, (int)(sizeof rev)))
168 		return call_gsexe;
169 
170 	if (rev.revision >= 950) {
171 		argc = argcnew;
172 		argv = (char **)argvnew;
173 	} else {
174 		argc = argcold;
175 		argv = (char **)argvold;
176 	}
177 	if (appres.DEBUG) {
178 		fprintf(stderr, "Using ghostscript library, revision %ld\n",
179 				rev.revision);
180 		fputs("Arguments:", stderr);
181 		for (i = 0; i < argc; ++i)
182 			fprintf(stderr, " %s", argv[i]);
183 		fputc('\n', stderr);
184 	}
185 
186 	code = gsapi_new_instance(&minst, (void *)data);
187 	if (code < 0) {
188 		return call_gsexe;
189 	}
190 	/* All gsapi_*() functions below return an int, but some
191 	   probably do not return an useful error code. */
192 	gsapi_set_stdio(minst, gs_stdin, gs_stdout, gs_stderr);
193 	code = gsapi_set_arg_encoding(minst, GS_ARG_ENCODING_UTF8);
194 	if (code == 0)
195 		code = gsapi_init_with_args(minst, argc, argv);
196 	if (code == 0 || code == gs_error_Quit || code < 0 ||
197 			code <= gs_error_Fatal)
198 		code = gsapi_exit(minst);
199 	gsapi_delete_instance(minst);
200 
201 	if (code == 0 || code == gs_error_Quit) {
202 		return 0;
203 	}
204 
205 	/*
206 	 * Unfortunately, ghostscript does not report corrupt pdf files, but
207 	 * still returns zero.  Corrupt ps/eps input seems to be reported.
208 	 */
209 	file_msg("Error in ghostscript library, %s.\nOptions:", argv[0]);
210 	for (i = 1; i < argc; ++i)
211 		file_msg("  %s", argv[i]);
212 	if (data->errbuf[0] != '\0')
213 		file_msg("Ghostscript error message:\n%s", data->errbuf);
214 	return GS_ERROR;
215 }
216 #endif /* HAVE_GSLIB */
217 
218 /*
219  * Call ghostscript.
220  * Return an open file stream for reading,
221  *   *out = popen({exenew, exeold}, "r");
222  * The user must call pclose(out) after calling gsexe(&out,...).
223  * Use exenew for gs > 9.49, exeold otherwise.
224  * Return 0 for success, -1 on failure to call ghostscript.
225  */
226 static int
gsexe(FILE ** out,bool * isnew,char * exenew,char * exeold)227 gsexe(FILE **out, bool *isnew, char *exenew, char *exeold)
228 {
229 #define	old_version	1
230 #define	new_version	2
231 #define no_version	0
232 	static int	version = no_version;
233 	const int	failure = -1;
234 	char		*exe;
235 	FILE		*fp;
236 
237 
238 	if (version == no_version) {
239 		int		n;
240 		int		stat;
241 		size_t		len;
242 		double		rev;
243 		char		cmd_buf[128];
244 		char		*cmd = cmd_buf;
245 		const char	version_arg[] = " --version";
246 
247 		if (appres.DEBUG)
248 			fprintf(stderr,
249 				"Trying to call ghostscript executable %s...\n",
250 				appres.ghostscript);
251 
252 		/* get the ghostscript version */
253 		/* allocate the command buffer, if necessary */
254 		len = strlen(appres.ghostscript) + sizeof version_arg;
255 		if (len > sizeof cmd_buf) {
256 			if ((cmd = malloc(len)) == NULL)
257 				return failure;
258 		}
259 
260 		/* write the command string */
261 		sprintf(cmd, "%s%s", appres.ghostscript, version_arg);
262 		fp = popen(cmd, "r");
263 		if (cmd != cmd_buf)
264 			free(cmd);
265 		if (fp == NULL)
266 			return failure;
267 
268 		/* scan for the ghostscript version */
269 		/* Currently, gsexe() is only called from gsexe_mediabox(), and
270 		 * the locale is already set to C in read_pdf. If this changes,
271 		 * make sure to have here the C or POSIX locale. */
272 		n = fscanf(fp, "%lf", &rev);
273 		stat = pclose(fp);
274 		if (n != 1 || stat != 0)
275 			return failure;
276 
277 		if (rev > 9.49) {
278 			exe = exenew;
279 			version = new_version;
280 			*isnew = true;
281 		} else {
282 			exe = exeold;
283 			version = old_version;
284 			*isnew = false;
285 		}
286 
287 		if (appres.DEBUG)
288 			fprintf(stderr, "...version %.2f\nCommand line: %s\n",
289 					rev, exe);
290 	} else { /* version == no_version */
291 		if (version == new_version) {
292 			exe = exenew;
293 			*isnew = true;
294 		} else {
295 			exe = exeold;
296 			*isnew = false;
297 		}
298 #undef new_version
299 #undef old_version
300 #undef no_version
301 
302 		if (appres.DEBUG)
303 			fprintf(stderr,
304 				"Calling ghostscript.\nCommand line: %s\n",
305 				exe);
306 	}
307 
308 	if ((*out = popen(exe, "r")) == NULL)
309 		return failure;
310 
311 	return 0;
312 }
313 
314 /*
315  * Call ghostscript to extract the /MediaBox from the pdf given in file.
316  * Command line, for gs >= 9.50,
317  *    gs -q -dNODISPLAY --permit-file-read=in.pdf -c \
318  *	"(in.pdf) (r) file runpdfbegin 1 pdfgetpage /MediaBox pget pop == quit"
319  * gs < 9.50:
320  *    gs -q -dNODISPLAY -dNOSAFER -c \
321  *	"(in.pdf) (r) file runpdfbegin 1 pdfgetpage /MediaBox pget pop == quit"
322  * The command line was found, and modified a bit, at
323  *https://stackoverflow.com/questions/2943281/using-ghostscript-to-get-page-size
324  * Beginning with gs 9.50, "-dSAFER" is the default, and permission to access
325  * files must be explicitly given with the --permit-file-{read,write,..}
326  * options. Before gs 9.50, "-dNOSAFER" is the default.
327  *
328  * Return 0 on success, -1 on failure, -2 (GS_ERROR) for a ghostscript-error,
329  * -3 if the path to the ghostscript executable is not given.
330  */
331 static int
gsexe_mediabox(char * file,int * llx,int * lly,int * urx,int * ury)332 gsexe_mediabox(char *file, int *llx, int *lly, int *urx, int *ury)
333 {
334 	bool	isnew;
335 	int	n;
336 	int	stat;
337 	size_t	len;
338 	char	*fmt;
339 	char	exenew_buf[256];
340 	char	exeold_buf[sizeof exenew_buf];
341 	char	*exenew;
342 	char	*exeold;
343 	double	bb[4] = { 0.0, 0.0, -1.0, -1.0 };
344 	FILE	*gs_output;
345 
346 	if (*appres.ghostscript == '\0')
347 		return -3;
348 
349 	exenew = "%s -q -dNODISPLAY \"--permit-file-read=%s\" -c \"(%s) (r) "
350 		"file runpdfbegin 1 pdfgetpage /MediaBox pget pop == quit\"";
351 	exeold = "%s -q -dNODISPLAY -c \"(%s) (r) "
352 		"file runpdfbegin 1 pdfgetpage /MediaBox pget pop == quit\"";
353 
354 	/* malloc() buffers for the command line, if necessary */
355 	fmt = exenew;
356 	len = strlen(exenew) + 2*strlen(file) + strlen(appres.ghostscript) - 5;
357 	if (len > sizeof exenew_buf) {
358 		if ((exenew = malloc(len)) == NULL)
359 			return -1;
360 	} else {
361 		exenew = exenew_buf;
362 	}
363 	sprintf(exenew, fmt, appres.ghostscript, file, file);
364 
365 	fmt = exeold;
366 	len = strlen(exeold) + strlen(file) + strlen(appres.ghostscript) - 3;
367 	if (len > sizeof exeold_buf) {
368 		if ((exeold = malloc(len)) == NULL) {
369 			if (exenew != exenew_buf)
370 				free(exenew);
371 			return -1;
372 		}
373 	} else {
374 		exeold = exeold_buf;
375 	}
376 	sprintf(exeold, fmt, appres.ghostscript, file);
377 
378 	/* call ghostscript */
379 	stat = gsexe(&gs_output, &isnew, exenew, exeold);
380 
381 	if (exenew != exenew_buf)
382 		free(exenew);
383 	if (exeold != exeold_buf)
384 		free(exeold);
385 
386 	if (stat != 0) {
387 		file_msg("Cannot open pipe with command:\n%s",
388 				isnew ? exenew : exeold);
389 		return -1;
390 	}
391 
392 	/* scan the output */
393 	n = fscanf(gs_output, "[%lf %lf %lf %lf]", bb, bb+1, bb+2, bb+3);
394 	stat = pclose(gs_output);
395 	if (n != 4 || stat != 0) {
396 		if (stat) {
397 			file_msg("Error calling ghostscript. Command:\n%s",
398 				isnew ? exenew : exeold);
399 			return GS_ERROR;
400 		} else {
401 			return -1;
402 		}
403 	}
404 
405 	*llx = (int)floor(bb[0]);
406 	*lly = (int)floor(bb[1]);
407 	*urx = (int)ceil(bb[2]);
408 	*ury = (int)ceil(bb[3]);
409 
410 	return 0;
411 }
412 
413 #ifdef HAVE_GSLIB
414 /*
415  * Return codes: 0..success, -1..within the function, an error occured,
416  *  GS_ERROR..the ghostscript interpreter returned an error
417  */
418 static int
gslib_mediabox(char * file,int * llx,int * lly,int * urx,int * ury)419 gslib_mediabox(char *file, int *llx, int *lly, int *urx, int *ury)
420 {
421 	int		stat;
422 	int		bb[4] = { 0, 0, -1, -1};
423 	char		*fmt;
424 	size_t		len;
425 #define argc	6
426 	char		errbuf[256] = "";
427 	char		*argnew[argc];
428 	char		*argold[argc];
429 	char		permit_buf[sizeof errbuf];
430 	char		cmd_buf[sizeof errbuf];
431 	struct _callback_data	data = {
432 		bb,		/* bb */
433 		errbuf,		/* errbuf */
434 		sizeof errbuf	/* errsize */
435 	};
436 
437 	/* write the argument list and command line */
438 	argnew[0] = "libgs";
439 	argnew[1] = "-q";
440 	argnew[2] = "-dNODISPLAY";
441 	argnew[3] = "--permit-file-read=%s";	/* file */
442 	argnew[4] = "-c";
443 	argnew[5] =
444 	    "(%s) (r) file runpdfbegin 1 pdfgetpage /MediaBox pget pop == quit";
445 
446 	argold[0] = argnew[0];
447 	argold[1] = argnew[1];
448 	argold[2] = argnew[2];
449 	argold[3] = "-dNOSAFER";
450 	argold[4] = argnew[4];
451 	/* argold[5] = argnew[5], assigned below */
452 
453 	/* write and, if necessary, malloc() argument strings */
454 	fmt = argnew[3];
455 	len = strlen(file) + strlen(fmt) - 1;
456 	if (len > sizeof permit_buf) {
457 	       if ((argnew[3] = malloc(len)) == NULL)
458 		       return -1;
459 	} else {
460 		argnew[3] = permit_buf;
461 	}
462 	sprintf(argnew[3], fmt, file);
463 
464 	fmt = argnew[5];
465 	len = strlen(file) + strlen(fmt) - 1;
466 	if (len > sizeof cmd_buf) {
467 	       if ((argnew[5] = malloc(len)) == NULL) {
468 		       if (argnew[3] != permit_buf)
469 			       free(argnew[3]);
470 		       return -1;
471 	       }
472 	} else {
473 		argnew[5] = cmd_buf;
474 	}
475 	sprintf(argnew[5], fmt, file);
476 	argold[5] = argnew[5];
477 
478 	/* call into the ghostscript library */
479 	stat = gslib(&data, stdin_void, stdout_mediabox, stderr_buf,
480 				argc, argc, argnew, argold);
481 	if (argnew[3] != permit_buf)
482 		free(argnew[3]);
483 	if (argnew[5] != cmd_buf)
484 		free(argnew[5]);
485 #undef argc
486 
487 	if (stat == 0) {
488 		if (data.bb[1] == 0 && data.bb[3] == GS_ERROR)
489 			return GS_ERROR;
490 		*llx = data.bb[0];
491 		*lly = data.bb[1];
492 		*urx = data.bb[2];
493 		*ury = data.bb[3];
494 	}
495 
496 	return stat;
497 }
498 #endif /* HAVE_GSLIB */
499 
500 /*
501  * Call ghostscript to extract the /MediaBox from the pdf given in file.
502  * Return 0 on success, -1 on failure, GS_ERROR (-2) for a ghostscript error.
503  */
504 int
gs_mediabox(char * file,int * llx,int * lly,int * urx,int * ury)505 gs_mediabox(char *file, int *llx, int *lly, int *urx, int *ury)
506 {
507 	int	stat;
508 
509 #ifdef HAVE_GSLIB
510 	stat = gslib_mediabox(file, llx, lly, urx, ury);
511 	if (stat == -1)
512 #endif
513 		stat = gsexe_mediabox(file, llx, lly, urx, ury);
514 	if (stat == GS_ERROR) {
515 		file_msg("Could not parse file '%s' with ghostscript.", file);
516 		file_msg("If available, error messages are displayed above.");
517 	}
518 	return stat;
519 }
520 
521 /*
522  * Invoke (approximately) the command
523  *
524  *   gs [..] -sDEVICE=bitrgb -dRedValues=256 -r<ppi> -g<widht>x<height> \
525  *	-o- <file>,
526  *
527  * to obtain a 24bit bitmap of RGB-Values. (It is sufficient to give one out of
528  * -dGreenValues=256, -dBlueValues=256 or -dRedValues=256.)
529  * For monochrome images, invoke
530  *
531  *   gs [..] -sDEVICE=bit -r<ppi> -g<widht>x<height> -o- <file>.
532  *
533  * The neural net that reduces the bitmap to a colormapped image of 256 colors
534  * expects BGR triples. Hence, swap the red and blue values.
535  * Return 0 on success, -1 for failure, or GS_ERROR if ghostscript returned with
536  * a non-zero exit code.
537  *
538  */
539 static int
gsexe_bitmap(char * file,F_pic * pic,int llx,int lly,int urx,int ury)540 gsexe_bitmap(char *file, F_pic *pic, int llx, int lly, int urx, int ury)
541 {
542 #define string_of(ppi)	#ppi
543 #define	rgb_fmt(ppi)	"%s -q -dSAFER -sDEVICE=bitrgb -dBlueValues=256 -r" \
544 	string_of(ppi) " -g%dx%d -sPageList=1 -o- -c '%d %d translate' -f %s"
545 #define	bw_fmt(ppi)	"%s -q -dSAFER -sDEVICE=bit -r" string_of(ppi)	    \
546 			" -g%dx%d -sPageList=1 -o- -c '%d %d translate' -f %s"
547 /*
548  * Instead of -llx -lly translate, the commands passed to ghostscript used to
549  * be:
550  *  -llx -lly translate
551  *  % mark dictionary (otherwise fails for tiger.ps (e.g.):
552  *  % many ps files don't 'end' their dictionaries)
553  *  countdictstack
554  *  mark
555  *  /oldshowpage {showpage} bind def
556  *  /showpage {} def
557  *  /initgraphics {} def	<<< this nasty command should never be used!
558  *  /initmmatrix {} def		<<< this one too
559  *  (psfile) run
560  *  oldshowpage
561  *  % clean up stacks and dicts
562  *  cleartomark
563  *  countdictstack exch sub { end } repeat
564  *  quit
565  */
566 	int		stat;
567 	int		w, h;
568 	const int	failure = -1;
569 	size_t		len;
570 	size_t		len_bitmap;
571 	char		*fmt;
572 	char		*exe;
573 	unsigned char	*pos;
574 	char		exe_buf[256];
575 	FILE		*gs_output;
576 
577 	if (*appres.ghostscript == '\0')
578 		return failure;
579 
580 	/* This is the size, to which a pixmap is rendered for display
581 	   on the canvas, at a magnification of 2.
582 	   The +1 is sometimes correct, sometimes not */
583 	w = (urx - llx) * BITMAP_PPI / 72 + 1;
584 	h = (ury - lly) * BITMAP_PPI / 72 + 1;
585 	if (tool_cells <= 2 || appres.monochrome) {
586 		fmt = bw_fmt(BITMAP_PPI);
587 		len_bitmap = (w + 7) / 8 * h;
588 		pic->pic_cache->numcols = 0;
589 	} else {
590 		fmt = rgb_fmt(BITMAP_PPI);
591 		if (tool_vclass == TrueColor && image_bpp == 4)
592 			len_bitmap = w * h * image_bpp;
593 		else
594 			len_bitmap = w * h * 3;
595 	}
596 	pic->pic_cache->bit_size.x = w;
597 	pic->pic_cache->bit_size.y = h;
598 
599 	/* malloc() buffer for the command line, if necessary; The "+ 80" allows
600 	   four integers of 20 digits each. */
601 	len = strlen(fmt) + strlen(file) + strlen(appres.ghostscript) + 80;
602 	if (len > sizeof exe_buf) {
603 		if ((exe = malloc(len)) == NULL)
604 			return failure;
605 	} else {
606 		exe = exe_buf;
607 	}
608 
609 	/* still check for overflow, because of the integers */
610 	if (len <= sizeof exe_buf)
611 		len = sizeof exe_buf;
612 	stat = snprintf(exe, len, fmt, appres.ghostscript, w, h, -llx, -lly,
613 			file);
614 	if ((size_t)stat >= len) {
615 		if (exe == exe_buf) {
616 			if ((exe = malloc((size_t)(stat + 1))) == NULL)
617 				return failure;
618 		} else {
619 			if ((exe = realloc(exe, (size_t)(stat + 1))) == NULL) {
620 				free(exe);
621 				return failure;
622 			}
623 		}
624 		sprintf(exe, fmt, appres.ghostscript, w, h, -llx, -lly, file);
625 	}
626 #undef rgb_fmt
627 #undef bw_fmt
628 
629 	if (appres.DEBUG)
630 		fprintf(stderr, "Calling ghostscript. Command:\n  %s\n", exe);
631 
632 	gs_output = popen(exe, "r");
633 	if (gs_output == NULL)
634 		file_msg("Cannot open pipe with command:\n%s", exe);
635 	if (exe != exe_buf)
636 		free(exe);
637 	if (gs_output == NULL)
638 		return failure;
639 
640 	if ((pic->pic_cache->bitmap = malloc(len_bitmap)) == NULL) {
641 		file_msg("Out of memory.\nCannot create pixmap for %s.", file);
642 		return failure;
643 	}
644 
645 	/* write result to pic->pic_cache->bitmap */
646 	pos = pic->pic_cache->bitmap;
647 	if (tool_cells <= 2 || appres.monochrome) {
648 		int	c;
649 		while ((c = fgetc(gs_output)) != EOF &&
650 			(size_t)(pos - pic->pic_cache->bitmap) < len_bitmap) {
651 			*(pos++) = (unsigned char)c;
652 		}
653 		pic->pic_cache->numcols = 0;
654 
655 	} else if (tool_vclass == TrueColor && image_bpp == 4) {
656 		int	c[3];
657 		while ((c[0] = fgetc(gs_output)) != EOF &&
658 				(c[1] = fgetc(gs_output)) != EOF &&
659 				(c[2] = fgetc(gs_output)) != EOF &&
660 				(size_t)(pos - pic->pic_cache->bitmap) <
661 								len_bitmap) {
662 			/* this should take care of endian-ness */
663 			*(unsigned int *)pos = ((unsigned int)c[0] << 16) +
664 				((unsigned int)c[1] << 8) + (unsigned int)c[2];
665 			pos += image_bpp;
666 		}
667 		pic->pic_cache->numcols = -1;	/* no colormap */
668 
669 	} else {
670 		int	c[3];
671 		/* map_to_palette() expects BGR triples, swap the RGB triples */
672 		while ((c[0] = fgetc(gs_output)) != EOF &&
673 				(c[1] = fgetc(gs_output)) != EOF &&
674 				(c[2] = fgetc(gs_output)) != EOF &&
675 				(size_t)(pos - pic->pic_cache->bitmap) <
676 								len_bitmap) {
677 			*(pos++) = (unsigned char)c[2];
678 			*(pos++) = (unsigned char)c[1];
679 			*(pos++) = (unsigned char)c[0];
680 		}
681 	}
682 	stat = pclose(gs_output);
683 	/* if reading stops just at the last byte in the file, then
684 	   neither is c[?] == EOF, nor does feof() necessarily return true. */
685 	if ((size_t)(pos - pic->pic_cache->bitmap) != len_bitmap) {
686 		free(pic->pic_cache->bitmap);
687 		pic->pic_cache->bitmap = NULL;
688 		file_msg("Error reading pixmap to render %s.", file);
689 		return failure;
690 	}
691 	if (stat) {
692 		free(pic->pic_cache->bitmap);
693 		pic->pic_cache->bitmap = NULL;
694 		return GS_ERROR;
695 	}
696 
697 	if (tool_vclass != TrueColor && tool_cells > 2 && !appres.monochrome) {
698 		if (!map_to_palette(pic)) {
699 			file_msg("Cannot create colormapped image for %s.",
700 					file);
701 			return failure;
702 		}
703 	}
704 
705 	return 0;
706 }
707 
708 #ifdef HAVE_GSLIB
709 
710 struct _stdio_data {
711 	size_t	errsize;
712 	size_t	errpos;
713 	size_t	outsize;
714 	size_t	outpos;
715 	char	*outbuf;
716 	char	*errbuf;
717 };
718 
719 static int
bitmap_stderr(void * data_handle,const char * str,int len)720 bitmap_stderr(void *data_handle, const char *str, int len)
721 {
722 	struct _stdio_data	*data = (struct _stdio_data *)data_handle;
723 	char	*buf = data->errbuf;
724 	size_t	*pos = &(data->errpos);
725 	size_t	*size = &(data->errsize);
726 
727 	/* buffer full, comparison for == should be sufficient */
728 	if (*pos >= *size - 1)
729 		return len;
730 
731 	if (*pos + len >= *size)	/* leave space for terminating '\0' */
732 		len = *size - *pos - 1;
733 	memcpy(buf + *pos, str, (size_t)len);
734 	*pos += len;
735 	buf[*pos] = '\0';
736 
737 	return len;
738 }
739 
740 static int
bitmap_stdout(void * data_handle,const char * str,int len)741 bitmap_stdout(void *data_handle, const char *str, int len)
742 {
743 	struct _stdio_data	*data = (struct _stdio_data *)data_handle;
744 	char	*buf = data->outbuf;
745 	size_t	*pos = &(data->outpos);
746 	size_t	*size = &(data->outsize);
747 
748 	/* buffer full, comparison for == should be sufficient */
749 	if (*pos >= *size - 1)
750 		return len;
751 
752 	if (*pos + len >= *size)	/* leave space for terminating '\0' */
753 		len = *size - *pos - 1;
754 	memcpy(buf + *pos, str, (size_t)len);
755 	*pos += len;
756 	buf[*pos] = '\0';
757 
758 	return len;
759 }
760 
761 /* Data provided to the callback functions */
762 struct calldata {
763 	int		width;
764 	int		height;
765 	int		raster;
766 	unsigned char	*img;
767 };
768 
769 
770 /*
771  * Callback functions for the ghostscript display device.
772  * The functions are sorted in the order they are called.
773  * See ghostscript/gdevdsp.h.
774  */
775 static int
display_open(void * handle,void * device)776 display_open(void *handle, void *device)
777 {
778 	(void) device;
779 	(void) handle;
780 
781 	return 0;
782 }
783 
784 static int
display_presize(void * handle,void * device,int width,int height,int raster,unsigned int format)785 display_presize(void *handle, void *device, int width, int height,
786         int raster, unsigned int format)
787 {
788 	(void) device;
789 	(void) raster;
790 	(void) format;
791 	struct calldata	*data = (struct calldata *)handle;
792 
793 	if (width == data->width && height == data->height) {
794 		return 0;
795 	} else {
796 		if (appres.DEBUG)
797 			fputs("display_presize: Wrong image dimensions.\n",
798 					stderr);
799 		return -1;
800 	}
801 }
802 
803 static void *
display_memalloc(void * handle,void * device,unsigned long size)804 display_memalloc(void *handle, void *device, unsigned long size)
805 {
806 	(void) device;
807 	struct calldata	*data = (struct calldata *)handle;
808 
809 	data->img = malloc((size_t)size);
810 
811 	if (appres.DEBUG && data->img == NULL)
812 		fputs("gslib_bitmap() - display_memalloc(): Out of memory.\n",
813 				stderr);
814 
815 	return (void *)data->img;
816 }
817 
818 static int
display_size(void * handle,void * device,int width,int height,int raster,unsigned int format,unsigned char * pimage)819 display_size(void *handle, void *device, int width, int height,
820         int raster, unsigned int format, unsigned char *pimage)
821 {
822 	(void) device;
823 	(void) raster;
824 	(void) format;
825 	(void) pimage;
826 	struct calldata	*data = (struct calldata *)handle;
827 
828 	if (width == data->width && height == data->height) {
829 		data->raster = raster;
830 		return 0;
831 	} else {
832 		if (appres.DEBUG)
833 			fputs("display_size: Wrong image dimensions.\n",
834 					stderr);
835 		return -1;
836 	}
837 }
838 
839 static int
display_sync(void * handle,void * device)840 display_sync(void *handle, void *device)
841 {
842 	(void) handle;
843 	(void) device;
844 
845 	return 0;
846 }
847 
848 static int
display_page(void * handle,void * device,int copies,int flush)849 display_page(void *handle, void *device, int copies, int flush)
850 {
851 	(void) handle;
852 	(void) device;
853 	(void) copies;
854 	(void) flush;
855 
856 	/* Other error codes cause messages from ghostscript on stderr. */
857 	return gs_error_InterpreterExit;
858 }
859 
860 static int
display_preclose(void * handle,void * device)861 display_preclose(void *handle, void *device)
862 {
863 	(void) handle;
864 	(void) device;
865 
866 	return 0;
867 }
868 
869 static int
display_memfree(void * handle,void * device,void * mem)870 display_memfree(void *handle, void *device, void *mem)
871 {
872 	(void) handle;
873 	(void) device;
874 	(void) mem;
875 
876 	return 0;
877 }
878 
879 static int
display_close(void * handle,void * device)880 display_close(void *handle, void *device)
881 {
882 	(void) handle;
883 	(void) device;
884 
885 	return 0;
886 }
887 
888 /*
889  * Link into the ghostscript library, writing to the display device.
890  * Return 0 on success, -1 on failure, or GS_ERROR for a ghostscript error.
891  * TODO: With a corrupt pdf, ghostscript will write messages to stdout and
892  * return 0. A stdout callback should be used to catch these messages.
893  * Warning messages seem to be directed to stderr.
894  * TODO: After gs 9.52, ghostscript uses a callout function for registering the
895  * callback functions. Implement that interface.
896  */
897 static int
gslib_bitmap(char * file,F_pic * pic,int llx,int lly,int urx,int ury)898 gslib_bitmap(char *file, F_pic *pic, int llx, int lly, int urx, int ury)
899 {
900 	int		code;
901 	int		w, h;
902 	int		format;
903 	int		rowstride;
904 	const int	failure = -1;
905 	const int	argc = 14;
906 	char		*arg[14];
907 			/* the digits in a hexadezimal number are 2 * sizeof */
908 	char		arg6[20 + 2 * sizeof(uintptr_t)];
909 			/* the number of digits in a decimal number are less
910 			 * than ((sizeof(int) / 2) * 3 + sizeof(int)) + 2,
911 			 * see https://stackoverflow.com/questions/43787672
912 			 */
913 #define	DIGITS_IN_INT	(((sizeof(int)/ 2) * 3 + sizeof(int)) + 2)
914 	char		arg7[17 + DIGITS_IN_INT];
915 	char		arg8[3 + DIGITS_IN_INT];
916 	char		arg9[4 + 2 * DIGITS_IN_INT];
917 	char		arg11[12 + 2 * DIGITS_IN_INT];
918 #undef DIGITS_IN_INT
919 	void		*minst = NULL;	/* must be initialized to NULL */
920 	char		errbuf[BUFSIZ];
921 	char		outbuf[BUFSIZ];
922 	struct calldata handle;		/* passed to the callback functions */
923 
924 	struct _stdio_data stdio_data = {
925 		sizeof errbuf,		/* errsize */
926 		(size_t) 0,		/* errpos */
927 		sizeof outbuf,		/* outsize */
928 		(size_t) 0,		/* outpos */
929 		outbuf,
930 		errbuf
931 	};
932 
933 	/* callback structure for "display" device */
934 	struct display_callback_s callback_functions = {
935 		sizeof(struct display_callback_s),
936 		DISPLAY_VERSION_MAJOR,
937 		DISPLAY_VERSION_MINOR,
938 		display_open,
939 		display_preclose,
940 		display_close,
941 		display_presize,
942 		display_size,
943 		display_sync,
944 		display_page,
945 		NULL,	/* display_update */
946 		display_memalloc,
947 		display_memfree,
948 		NULL	/* display_separation */
949 	};
950 
951 
952 	/* This should be the size to which a pixmap is rendered for display
953 	   on the canvas, at a magnification of 2.
954 	   The +1 is sometimes correct, sometimes not */
955 	w = (urx - llx) * BITMAP_PPI / 72 + 1;
956 	h = (ury - lly) * BITMAP_PPI / 72 + 1;
957 	handle.width = w;
958 	handle.height = h;
959 
960 	/* Set the format flags for -dDisplayFormat=%d. */
961 	/* DISPLAY_ROW_ALIGN_x must be equal or greater than
962 	   the size of a pointer */
963 	format = DISPLAY_TOPFIRST | DISPLAY_ROW_ALIGN_8;
964 	if (tool_cells <= 2 || appres.monochrome) {
965 		format |= DISPLAY_COLORS_NATIVE | /*  DISPLAY_ALPHA_NONE */
966 			DISPLAY_DEPTH_1;
967 
968 		rowstride = (w + 7) / 8;
969 		/* size = (size_t)((w + 7) / 8 * h); */
970 		pic->pic_cache->numcols = 0;
971 	} else {
972 		format = DISPLAY_COLORS_RGB | DISPLAY_DEPTH_8 |
973 #ifdef WORDS_BIGENDIAN
974 				DISPLAY_BIGENDIAN;	/* RGB */
975 #else
976 				DISPLAY_LITTLEENDIAN;	/* BGR */
977 #endif
978 		if (tool_vclass == TrueColor && image_bpp == 4) {
979 #ifdef WORDS_BIGENDIAN
980 			format |= DISPLAY_UNUSED_FIRST;	/* xRGB */
981 #else
982 			format |= DISPLAY_UNUSED_LAST;	/* BGRX */
983 #endif
984 			rowstride = w * image_bpp;
985 			/* size = (size_t)(w * h * image_bpp); */
986 			pic->pic_cache->numcols = -1;
987 		} else {
988 			/* DISPLAY_ALPHA_NONE ... == 0, not necessary */
989 			rowstride = w * 3;
990 			/* size = (size_t)(w * h * 3); */
991 			/* not necessary to set pic->pic_cache->numcols */
992 		}
993 	}
994 
995 	/*
996 	 * Options to ghostscript: It was not possible to use switches from the
997 	 * family of -dLastPage=1 or -sPageList=1, because these resulted in an
998 	 * all white pixmap. Therefore, interrupt the interpreter by having the
999 	 * display_page() callback return gs_error_InterpreterExit. For this
1000 	 * error code, ghostscript returns 0 and does not write messages to
1001 	 * stderr.
1002 	 */
1003 	arg[0] = "libgs";
1004 	arg[1] = "-q";
1005 	arg[2] = "-dSAFER";	/* default for gs >= 9.50; for gs < 9.50,
1006 				   default is -dNOSAFER */
1007 	arg[3] = "-dBATCH";
1008 	arg[4] = "-dNOPAUSE";
1009 	arg[5] = "-sDEVICE=display";
1010 	arg[6] = "-sDisplayHandle=16#%" PRIxPTR;
1011 	arg[7] = "-dDisplayFormat=%d";
1012 	arg[8] = "-r%d";	/* resolution */
1013 	arg[9] = "-g%dx%d";	/* width x height */
1014 	arg[10] = "-c";
1015 	arg[11] = "%d %d translate";
1016 	arg[12] = "-f";		/* terminate list of tokens for the -c switch */
1017 	arg[13] = file;
1018 
1019 	sprintf(arg6, arg[6], (uintptr_t)&handle);
1020 	arg[6] = arg6;
1021 	sprintf(arg7, arg[7], format);
1022 	arg[7] = arg7;
1023 	sprintf(arg8, arg[8], BITMAP_PPI);
1024 	arg[8] = arg8;
1025 	sprintf(arg9, arg[9], w, h);
1026 	arg[9] = arg9;
1027 	sprintf(arg11, arg[11], -llx, -lly);
1028 	arg[11] = arg11;
1029 
1030 	if (appres.DEBUG) {
1031 		int	i;
1032 		fputs("Using ghostscript library, arguments:\n ", stderr);
1033 		for (i = 0; i < argc; ++i) {
1034 			fputc(' ', stderr);
1035 			fputs(arg[i], stderr);
1036 		}
1037 		fputc('\n', stderr);
1038 	}
1039 
1040 	/* call ghostscript */
1041 	code = gsapi_new_instance(&minst, (void *)&stdio_data);
1042 	if (code == 0)
1043 		code = gsapi_set_stdio(minst,NULL,bitmap_stdout,bitmap_stderr);
1044 	else
1045 		return gsexe_bitmap(file, pic, llx, lly, urx, ury);
1046 
1047 	if (code == 0)
1048 		code = gsapi_set_display_callback(minst, &callback_functions);
1049 	if (code == 0)
1050 		code = gsapi_set_arg_encoding(minst, GS_ARG_ENCODING_UTF8);
1051 	if (code == 0)
1052 		code = gsapi_init_with_args(minst, argc, arg);
1053 	if (code != 0 && code != gs_error_Quit)
1054 		gsapi_exit(minst);
1055 	else
1056 		code = gsapi_exit(minst);
1057 
1058 	gsapi_delete_instance(minst);
1059 
1060 	if (stdio_data.outpos > (size_t)0)
1061 		file_msg("Message from ghostscript when creating pixmap:\n%s",
1062 				stdio_data.outbuf);
1063 	if (stdio_data.errpos > (size_t)0)
1064 		file_msg("Error message from ghostscript when creating pixmap:\n%s",
1065 				stdio_data.errbuf);
1066 
1067 	if (code != 0 && code != gs_error_Quit) {
1068 		free(handle.img);
1069 		return GS_ERROR;
1070 	}
1071 	/* code == 0 || code == gs_error_Quit */
1072 	if (rowstride > handle.raster) {
1073 		if (appres.DEBUG)
1074 			fputs("The pixmap rendered by ghostscript is larger "
1075 					"than xfig expected.\n", stderr);
1076 		free(handle.img);
1077 		return failure;
1078 	} else if (rowstride < handle.raster) {
1079 		/* Move pixmap data to the alignment expected by xfig. */
1080 		int		i;
1081 		unsigned char	*src;
1082 		unsigned char	*dst;
1083 
1084 		for (i = 1; i < h; ++i) {
1085 			src = handle.img + i * handle.raster;
1086 			dst = handle.img + i * rowstride;
1087 			for (src = handle.img + i * handle.raster;
1088 			     src < handle.img + i * handle.raster + rowstride;
1089 					) {
1090 				*(dst++) = *(src++);
1091 			}
1092 		}
1093 		handle.img = realloc(handle.img, (size_t)(dst - handle.img));
1094 	}
1095 
1096 	pic->pic_cache->bit_size.x = w;
1097 	pic->pic_cache->bit_size.y = h;
1098 	pic->pic_cache->bitmap = handle.img;
1099 
1100 	if (tool_vclass != TrueColor && tool_cells > 2 && !appres.monochrome) {
1101 		if (!map_to_palette(pic)) {
1102 			file_msg("Cannot create colormapped image for %s.",
1103 					file);
1104 			return failure;
1105 		}
1106 	}
1107 
1108 	return 0;
1109 }
1110 #endif /* HAVE_GSLIB */
1111 
1112 /*
1113  * Create a pixmap in pic->pic_cache->bitmap from the ps/eps/pdf file "file"
1114  * having the bounding box llx lly urx ury.
1115  * Return 0 on success, -1 on failure, or GS_ERROR for a ghostscript error.
1116  */
1117 int
gs_bitmap(char * file,F_pic * pic,int llx,int lly,int urx,int ury)1118 gs_bitmap(char *file, F_pic *pic, int llx, int lly, int urx, int ury)
1119 {
1120 	int	stat;
1121 
1122 #ifdef HAVE_GSLIB
1123 	stat = gslib_bitmap(file, pic, llx, lly, urx, ury);
1124 #else
1125 	stat = gsexe_bitmap(file, pic, llx, lly, urx, ury);
1126 #endif
1127 	if (stat == GS_ERROR) {
1128 		file_msg("Could not create pixmap from '%s' with ghostscript.",
1129 				file);
1130 	}
1131 	return stat;
1132 }
1133