1 /*
2  * Fig2dev: Translate Fig code to various Devices
3  * Copyright (c) 1991 by Micah Beck
4  * Parts Copyright (c) 1985-1988 by Supoj Sutanthavibul
5  * Parts Copyright (c) 1989-2015 by Brian V. Smith
6  * Parts Copyright (c) 2015-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
13  * of 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  * genps.c: convert fig to PostScript
21  *
22  * Modified by Herbert Bauer to support ISO-Characters,
23  * multiple page output, color mode etc.
24  * heb@regent.e-technik.tu-muenchen.de
25  *
26  * Modified by Eric Picheral to support the whole set of ISO-Latin-1
27  * Modified by Herve Soulard to allow non-iso coding on special fonts
28  * Herve.Soulard@inria.fr (8 Apr 1993)
29  *
30  * Development for new extensions at TU Darmstadt, Germany starting 2002
31  * Allow to "build" pictures incrementally.
32  * To achieve this we split the complete figure into layers in separate
33  * ps-figures. The complete figure will be seen when overlapping all layers.
34  * A layer is combined from adjacent depths in xfig. This makes it possible
35  * to overlap items also when splitting into layers.
36  */
37 
38 #ifdef HAVE_CONFIG_H
39 #include "config.h"
40 #endif
41 #include "genps.h"
42 
43 #include <stdio.h>
44 #include <stdlib.h>
45 #include <string.h>
46 #ifdef	HAVE_STRINGS_H
47 #include <strings.h>
48 #endif
49 #ifdef HAVE_UNISTD_H
50 #include <unistd.h>
51 #endif
52 #include <math.h>
53 #include <ctype.h>
54 #include <sys/stat.h>	/* struct stat */
55 #ifdef HAVE_GETPWUID
56 #include <pwd.h>
57 #endif
58 #include <locale.h>
59 
60 #include "fig2dev.h"	/* includes bool.h and object.h */
61 //#include "object.h"	/* NUMSHADES, NUMTINTS */
62 #include "bound.h"
63 #include "colors.h"	/* lookup_X_color(), rgb2luminance() */
64 #include "creationdate.h"
65 #include "encode.h"
66 #include "messages.h"
67 #include "pi.h"
68 #include "psfonts.h"
69 #include "readpics.h"
70 #include "xtmpfile.h"
71 
72 /* include the PostScript preamble, patterns etc */
73 #include "psprolog.h"
74 
75 extern int	v2_flag, v21_flag, v30_flag;		/* read.c */
76 #ifdef I18N
77 extern bool	support_i18n;				/* fig2dev.c */
78 #endif
79 
80 /* exported symbols */
81 bool		epsflag = false;	/* to distinguish PS and EPS */
82 bool		pdfflag = false;	/* to distinguish PDF and PS/EPS */
83 
84 /*
85  * The procedures to embed image files into ps code are defined in a number of
86  * source files (readgif.c, readjpg.c, readeps.c,...). These procedures could
87  * simply be included here in genps.c, which would result in an overwhelmingly
88  * big file. Reading procedures are separated into their respective files, but
89  * since they are used only here, the functions are declared here and separate
90  * header files are not written.
91  */
92 #define READ_SIGNATURE \
93 	F_pic *pic, struct xfig_stream *restrict pic_stream, int *llx, int *lly
94 /* readeps.c */
95 extern int  read_eps(READ_SIGNATURE);
96 extern int  read_pdf(READ_SIGNATURE);
97 extern int  append_epsi(FILE *in, const char *filename, FILE *out);
98 extern int  pdftops(struct xfig_stream *restrict pic_stream, FILE *out);
99 /* readgif.c */
100 extern int  read_gif(READ_SIGNATURE);
101 /* readjpg.c */
102 extern int  read_jpg(READ_SIGNATURE);
103 extern void JPEGtoPS(FILE *f, FILE *PSfile);
104 /* readpcx.c */
105 extern int  read_pcx(READ_SIGNATURE);
106 /* readpng.c */
107 #ifdef HAVE_PNG_H
108 extern int  read_png(READ_SIGNATURE);
109 #endif
110 /* readppm.c */
111 extern int  read_ppm(READ_SIGNATURE);
112 /* readtif.c */
113 extern int  read_tif(READ_SIGNATURE);
114 /* readxbm.c */
115 extern int  read_xbm(READ_SIGNATURE);
116 /* readxpm.c */
117 extern int  read_xpm(READ_SIGNATURE);
118 #undef READ_SIGNATURE
119 
120 #ifdef I18N
121 static bool	enable_composite_font = false;
122 static bool	append_find_composite(FILE *restrict out, FILE *restrict in);
123 #endif
124 
125 #define		POINT_PER_INCH		72
126 #define		ULIMIT_FONT_SIZE	300
127 /* In order that gridlines have maximum depth */
128 #define		MAXDEPTH		999
129 #define		min(a, b)		(((a) < (b)) ? (a) : (b))
130 #define		SHADEVAL(F)		1.0*(F)/(NUMSHADES-1)
131 #define		TINTVAL(F)		1.0*(F-NUMSHADES+1)/NUMTINTS
132 #define		NEEDS_CLIPPING(obj)	((obj->for_arrow || obj->back_arrow) &&\
133 							obj->thickness > 0)
134 
135 /* variables obtained from command line options */
136 static bool	anonymous = true;
137 static bool	asciipreview = false;	/* add ASCII preview? */
138 static bool	tiffpreview = false;	/* add a TIFF preview? */
139 static bool	tiffcolor = false;	/* color or b/w TIFF preview */
140 static int	border_margin = 0;
141 static bool	correct_font_size = false;
142 static int	pagewidth = -1;
143 static int	pageheight = -1;
144 static bool	useabsolutecoo = false;
145 static int	xoff=0;
146 static int	yoff=0;
147 
148 static FILE	*saveofile; /* temp filename for eps when adding tiff preview */
149 static char	tmpeps_buf[128] = "f2depsXXXXXX"; /* temp filename for
150 						     ASCII or tiff preview */
151 static char	tmpprev_buf[128] = "f2dprevXXXXXX";
152 static char	*tmpeps = tmpeps_buf;
153 static char	*tmpprev = tmpprev_buf;
154 static int	width, height;
155 static double	cur_thickness = 0.0;
156 static int	cur_joinstyle = 0;
157 static int	cur_capstyle = 0;
158 static int	pages;
159 static int	no_obj = 0;
160 static float	fllx, flly, furx, fury;
161 static double	scalex, scaley;
162 static double	origx, origy;
163 static double	userorigx, userorigy;
164 static double	userwidthx, userwidthy;
165 
166 /* arrowhead arrays */
167 static F_pos	bpoints[50], fpoints[50];
168 static int	nbpoints, nfpoints;
169 static F_pos	bfillpoints[50], ffillpoints[50], clippoints[50];
170 static int	nbfillpoints, nffillpoints, nclippoints;
171 static int	fpntx1, fpnty1;	/* first point of object */
172 static int	fpntx2, fpnty2;	/* second point of object */
173 static int	lpntx1, lpnty1;	/* last point of object */
174 static int	lpntx2, lpnty2;	/* second-to-last point of object */
175 /*
176  *  Static variables for variant methods:
177  *   fig_number has the "current" figure number which has been created.
178  *   last_depth remembers the last level number processed
179  *	   (we need a sufficiently large initial value)
180 */
181 static int	fig_number = 0;
182 static int	last_depth = MAXDEPTH + 4;
183 
184 /* local procedures */
185 static int	append(const char *restrict infilename, FILE *restrict outfile);
186 static void	appendhex(char *infilename,FILE *outfile,int width,int height);
187 static bool	approx_spline_exist(F_compound *ob);
188 static void	do_split(int actual_depth);/* split different depths' objects */
189 					   /* but only as comment */
190 static void	clip_arrows(F_line *obj, int objtype);
191 static void	draw_arrow(F_arrow *arrow, F_pos *points, int npoints,
192 				F_pos *fillpoints, int nfillpoints, int col);
193 static void	draw_gridline(float x1, float y1, float x2, float y2);
194 static bool	ellipse_exist(F_compound *ob);
195 static void	encode_all_fonts(F_compound *ob);
196 static void	fill_area(int fill, int pen_color, int fill_color);
197 static void	genps_ctl_spline(F_spline *s);
198 static void	genps_itp_spline(F_spline *s);
199 static void	genps_std_colors(void);
200 static void	genps_usr_colors(void);
201 static bool	iso_text_exist(F_compound *ob);
202 static void	putword(int word, FILE *file);
203 static void	set_linewidth(double w);
204 
205 /* define the standard 32 colors */
206 struct	_rgb {
207 	double r, g, b;
208 } rgbcols[NUM_STD_COLS] = {
209 	{0.00, 0.00, 0.00},	/* black */
210 	{0.00, 0.00, 1.00},	/* blue */
211 	{0.00, 1.00, 0.00},	/* green */
212 	{0.00, 1.00, 1.00},	/* cyan */
213 	{1.00, 0.00, 0.00},	/* red */
214 	{1.00, 0.00, 1.00},	/* magenta */
215 	{1.00, 1.00, 0.00},	/* yellow */
216 	{1.00, 1.00, 1.00},	/* white */
217 	{0.00, 0.00, 0.56},	/* blue1 */
218 	{0.00, 0.00, 0.69},	/* blue2 */
219 	{0.00, 0.00, 0.82},	/* blue3 */
220 	{0.53, 0.81, 1.00},	/* blue4 */
221 	{0.00, 0.56, 0.00},	/* green1 */
222 	{0.00, 0.69, 0.00},	/* green2 */
223 	{0.00, 0.82, 0.00},	/* green3 */
224 	{0.00, 0.56, 0.56},	/* cyan1 */
225 	{0.00, 0.69, 0.69},	/* cyan2 */
226 	{0.00, 0.82, 0.82},	/* cyan3 */
227 	{0.56, 0.00, 0.00},	/* red1 */
228 	{0.69, 0.00, 0.00},	/* red2 */
229 	{0.82, 0.00, 0.00},	/* red3 */
230 	{0.56, 0.00, 0.56},	/* magenta1 */
231 	{0.69, 0.00, 0.69},	/* magenta2 */
232 	{0.82, 0.00, 0.82},	/* magenta3 */
233 	{0.50, 0.19, 0.00},	/* brown1 */
234 	{0.63, 0.25, 0.00},	/* brown2 */
235 	{0.75, 0.38, 0.00},	/* brown3 */
236 	{1.00, 0.50, 0.50},	/* pink1 */
237 	{1.00, 0.63, 0.63},	/* pink2 */
238 	{1.00, 0.75, 0.75},	/* pink3 */
239 	{1.00, 0.88, 0.88},	/* pink4 */
240 	{1.00, 0.84, 0.00}	/* gold */
241 };
242 
243 static char	*psfontnames[] = {
244 	"Times-Roman", "Times-Roman",	/* default */
245 	"Times-Roman",			/* roman */
246 	"Times-Bold",			/* bold */
247 	"Times-Italic",			/* italic */
248 	"Helvetica",			/* sans serif */
249 	"Courier"			/* typewriter */
250 };
251 
252 #define PS_FONTNAMES(T)	\
253 	(((v2_flag&&!(v21_flag||v30_flag)) || \
254 		psfont_text(T)) ? PSfontnames : psfontnames)
255 
256 #define PSFONT(T) \
257  ((T->font) <= MAXFONT(T) ? PS_FONTNAMES(T)[T->font+1] : PS_FONTNAMES(T)[0])
258 
259 #define PSFONTMAG(T)  (((T->size) <= ULIMIT_FONT_SIZE ? \
260 		T->size :  ULIMIT_FONT_SIZE) \
261 		* ppi/(correct_font_size? (metric ? 72*80/76.2 : 72): 80))
262 
263 /* define the fill patterns */
264 static char	*fill_def[NUMPATTERNS] = {
265 	FILL_PAT01,FILL_PAT02,FILL_PAT03,FILL_PAT04,
266 	FILL_PAT05,FILL_PAT06,FILL_PAT07,FILL_PAT08,
267 	FILL_PAT09,FILL_PAT10,FILL_PAT11,FILL_PAT12,
268 	FILL_PAT13,FILL_PAT14,FILL_PAT15,FILL_PAT16,
269 	FILL_PAT17,FILL_PAT18,FILL_PAT19,FILL_PAT20,
270 	FILL_PAT21,FILL_PAT22,
271 };
272 
273 /* headers for various image files */
274 static	 struct hdr {
275 	    char	*type;
276 	    char	*bytes;
277 	    int		(*readfunc)();
278 	    bool	pipeok;
279 	    /* buf[12] below must be large enough for the file signature */
280 	} headers[] = {	{"GIF", "GIF",			read_gif,	false},
281 #ifdef V4_0
282 			{"FIG", "#FIG",			read_figure,	true},
283 #endif /* V4_0 */
284 			{"PCX", "\012\005\001",		read_pcx,	true},
285 			{"EPS", "%!",			read_eps,	true},
286 			{"EPSI", "\xc5\xd0\xd3\xc6",	read_eps,	true},
287 			{"PDF", "%PDF",			read_pdf,	true},
288 			{"PPM", "P3",			read_ppm,	true},
289 			{"PPM", "P6",			read_ppm,	true},
290 			{"TIFF", "II*\000",		read_tif,	false},
291 			{"TIFF", "MM\000*",		read_tif,	false},
292 			{"XBM", "#define",		read_xbm,	true},
293 #ifdef HAVE_PNG_H
294 			{"PNG", "\211\120\116\107\015\012\032\012",
295 							read_png,	true},
296 #endif
297 			{"JPEG", "\377\330\377\340",	read_jpg,	true},
298 			{"JPEG", "\377\330\377\341",	read_jpg,	true},
299 			{"XPM", "/* XPM */",		read_xpm,	false},
300 };
301 
302 #define NUMHEADERS	(sizeof(headers)/sizeof(headers[0]))
303 
304 
305 /******************************/
306 /* various methods start here */
307 /******************************/
308 
309 static void
write_data(FILE * out,char * name,unsigned char * in,size_t len)310 write_data(FILE *out, char *name, unsigned char *in, size_t len)
311 {
312 #ifdef HAVE_ZLIB_H
313 	if (deflate_ascii85encode(out, in, len)) {
314 		put_msg("Could not compress image %s.", name);
315 		exit(EXIT_FAILURE);
316 	}
317 #else
318 	if (ascii85encode(out, in, len)) {
319 		put_msg("Could not embed image %s.", name);
320 		exit(EXIT_FAILURE);
321 	}
322 #endif
323 	/* Output end of data marker for the ascii85 encoded stream */
324 	fputs("~>\n", out);
325 }
326 
327 /*
328  * the image dictionary string is needed twice,
329  * here and in indexed_image() below
330  */
331 #ifdef HAVE_ZLIB_H
332 #define DATASOURCE	"    /DataSource Data /FlateDecode filter\n"
333 #else
334 #define DATASOURCE	"    /DataSource Data\n"
335 #endif
336 
337 static void
write_rgbimage(FILE * out,F_pic * pic)338 write_rgbimage(FILE *out, F_pic *pic)
339 {
340 	fputs(		"/Data currentfile /ASCII85Decode filter def\n"
341 			"/DeviceRGB setcolorspace\n", out);
342 
343 	if (pic->num_transp == NO_TRANSPARENCY ||
344 			/* != TRANSP_COLOR should not happen */
345 			pic->num_transp != TRANSP_COLOR) {
346 		fputs(	" << /ImageType 1\n", out);
347 	} else {			/* transparency by color key masking */
348 		fputs(	" << /ImageType 4\n", out);
349 		fprintf(out, "    /MaskColor [ %d %d %d ]\n",
350 				pic->transp_col[RED], pic->transp_col[GREEN],
351 				pic->transp_col[BLUE]);
352 	}
353 	/* gcc warned: %1$d not ISO C */
354 	fprintf(out,	"    /Width %d /Height %d\n"
355 			"    /ImageMatrix [ %d 0 0 -%d 0 %d ]\n",
356 				pic->bit_size.x, pic->bit_size.y,
357 			     pic->bit_size.x, pic->bit_size.y, pic->bit_size.y);
358 	fputs(		DATASOURCE
359 			"    /BitsPerComponent 8 /Decode [0 1 0 1 0 1]\n"
360 			" >> xfig_image\n", out);
361 
362 	write_data(out, pic->file, pic->bitmap,
363 			(size_t)pic->bit_size.x * pic->bit_size.y * 3);
364 }
365 
366 static void
indexed_image(FILE * out,F_pic * pic)367 indexed_image(FILE *out, F_pic *pic)
368 {
369 	int	i = 0;
370 
371 	fprintf(out,
372 			"/Data currentfile /ASCII85Decode filter def\n"
373 			"[ /Indexed /DeviceRGB %d\n <", pic->numcols - 1);
374 	/* write the hex-encoded colormap */
375 	fprintf(out, "%.2hhx%.2hhx%.2hhx", pic->cmap[RED][i],
376 			pic->cmap[GREEN][i], pic->cmap[BLUE][i]);
377 	for (i = 1; i < pic->numcols; ++i) {
378 		if (i % 11 == 0)
379 			fputs("\n ", out);
380 		fprintf(out, " %.2hhx%.2hhx%.2hhx", pic->cmap[RED][i],
381 				pic->cmap[GREEN][i], pic->cmap[BLUE][i]);
382 	}
383 	fputs(		">\n] setcolorspace\n", out);
384 	/* continue with image dictionary */
385 	if (pic->num_transp == NO_TRANSPARENCY ||
386 			pic->num_transp == TRANSP_COLOR) { /* colormap only! */
387 		fputs(	" << /ImageType 1\n", out);
388 	} else {			/* transparent color */
389 		fputs(	" << /ImageType 4\n    /MaskColor [ ", out);
390 		for (i = 0; i < pic->num_transp; ++i)
391 			fprintf(out, "%d ", pic->transp_cols[i]);
392 		fputs("]\n", out);
393 	}
394 	fprintf(out,	"    /Width %d /Height %d\n"
395 			"    /ImageMatrix [ %d 0 0 -%d 0 %d ]\n",
396 				pic->bit_size.x, pic->bit_size.y,
397 			     pic->bit_size.x, pic->bit_size.y, pic->bit_size.y);
398 	fputs(		DATASOURCE
399 			"    /BitsPerComponent 8 /Decode [0 255]\n"
400 			" >> xfig_image\n", out);
401 
402 	write_data(out, pic->file, pic->bitmap,
403 			(size_t)pic->bit_size.x * pic->bit_size.y);
404 }
405 
406 
407 /******************************/
408 /* main procedures start here */
409 /******************************/
410 
411 void
geneps_option(char opt,char * optarg)412 geneps_option(char opt, char *optarg)
413 {
414 	static bool init = false;
415 	if (!init) {
416 		init = true;
417 		epsflag = true;
418 	}
419 	gen_ps_eps_option(opt, optarg);
420 }
421 
422 void
genps_option(char opt,char * optarg)423 genps_option(char opt, char *optarg)
424 {
425 	gen_ps_eps_option(opt, optarg);
426 }
427 
428 void
gen_ps_eps_option(char opt,char * optarg)429 gen_ps_eps_option(char opt, char *optarg)
430 {
431 	int i;
432 
433 	switch (opt) {
434 
435 	/* don't do anything for the following args (already parsed in main) */
436 	case 'G':		/* grid */
437 	case 'L':		/* language */
438 	/* option parsed in genpdf_option(), genpdf.c */
439 	case 'P':		/* pagemode */
440 		break;
441 
442 	case 'a':		/* anonymous (don't output user name) */
443 	      anonymous = true;
444 	      break;
445 
446 	case 'A':		/* add ASCII preview */
447 		asciipreview = true;
448 		break;
449 
450 	case 'b':		/* border margin around figure */
451 		sscanf(optarg,"%d",&border_margin);
452 		break;
453 
454 	case 'B':		/* bounding box in absolute coordinates */
455 		if (epsflag) {
456 		    (void) strcpy (boundingbox, optarg);
457 		    boundingboxspec = true;	/* user-specified */
458 		    useabsolutecoo = true;
459 		}
460 		break;
461 
462 	case 'C':		/* add color TIFF preview (for MicroSloth) */
463 		tiffpreview = true;
464 		tiffcolor = true;
465 		break;
466 
467 	case 'c':		/* center figure */
468 		if (!epsflag) {
469 		    center = true;
470 		    centerspec = true;	/* user-specified */
471 		}
472 		break;
473 
474 	case 'e':		/* don't center ('e' means edge) figure */
475 		if (!epsflag) {
476 		    center = false;
477 		    centerspec = true;	/* user-specified */
478 		}
479 		break;
480 	case 'F':		/* fontsize */
481 		correct_font_size = true;
482 		break;
483 
484 	case 'f':		/* default font name */
485 		for ( i = 1; i <= MAX_PSFONT; i++ )
486 			if (!strcmp(optarg, PSfontnames[i]))
487 				break;
488 
489 		if ( i > MAX_PSFONT )
490 			fprintf(stderr, "warning: non-standard font name %s\n",
491 				optarg);
492 
493 		psfontnames[0] = psfontnames[1] = optarg;
494 		PSfontnames[0] = PSfontnames[1] = optarg;
495 		break;
496 
497 	case 'g':			/* background color */
498 		if (lookup_X_color(optarg,&background) >= 0) {
499 			bgspec = true;
500 		} else {
501 			fprintf(stderr,
502 			 "Can't parse color '%s', ignoring background option\n",
503 				optarg);
504 		}
505 		break;
506 
507 	case 'l':			/* landscape mode */
508 		if (!epsflag) {
509 		    landscape = true;	/* override the figure file setting */
510 		    orientspec = true;	/* user-specified */
511 		}
512 		break;
513 
514 	case 'M':			/* multi-page option */
515 		if (!epsflag) {
516 		    multi_page = true;
517 		    multispec = true; /* user has overridden anything in file */
518 		}
519 		break;
520 
521 	case 'n':			/* name to put in the "Title:" spec */
522 		name = optarg;
523 		break;
524 
525 	case 'N':			/* convert colors to grayscale */
526 		grayonly = true;
527 		break;
528 
529 	case 'O':			/* overlap multipage output */
530 		if (!epsflag)
531 			overlap = true;
532 		break;
533 
534 	case 'o':			/* turn off multi-page option */
535 		if (!epsflag) {
536 		    multi_page = false;
537 		    multispec = true; /* user has overridden anything in file */
538 		}
539 		break;
540 
541 	case 'p':			/* portrait mode */
542 		if (!epsflag) {
543 		    landscape = false;	/* override the figure file setting */
544 		    orientspec = true;	/* user-specified */
545 		}
546 		break;
547 
548 	case 'R':		       /* boundingbox in relative coordinates */
549 		if (epsflag) {
550 		    (void) strcpy (boundingbox, optarg);
551 		    boundingboxspec = true;	/* user-specified */
552 		}
553 		break;
554 
555 		case 'T':		/* add monochrome TIFF preview */
556 		tiffpreview = true;
557 		tiffcolor = false;
558 		break;
559 
560 	case 'x':			/* x offset on page */
561 		if (!epsflag) {
562 		    xoff = atoi(optarg);
563 		}
564 		break;
565 
566 	case 'y':			/* y offset on page */
567 		if (!epsflag) {
568 		    yoff = atoi(optarg);
569 		}
570 		break;
571 
572 	case 'z':			/* papersize */
573 		if (!epsflag) {
574 		    (void) strcpy (papersize, optarg);
575 		    paperspec = true;	/* user-specified */
576 		}
577 		break;
578 
579 	  default:
580 		put_msg(Err_badarg, opt, "ps");
581 		exit(1);
582 	}
583 }
584 
585 void
genps_start(F_compound * objects)586 genps_start(F_compound *objects)
587 {
588 	char		 host[256];
589 	char		 date_buf[CREATION_TIME_LEN];
590 	struct passwd	*who;
591 	int		 itmp, jtmp;
592 	int		 i;
593 	int		 cliplx, cliply, clipux, clipuy;
594 	int		 userllx, userlly, userurx, userury;
595 	const struct paperdef	*pd;
596 	char		 psize[20];
597 
598 	char		*libdir;
599 	char		 filename[512];
600 
601 	/* make sure user isn't asking for both TIFF and ASCII preview */
602 	if (tiffpreview && asciipreview) {
603 		put_msg("Only one type of preview allowed: -A or -T/-C");
604 		exit(EXIT_FAILURE);
605 	}
606 
607 	/* if the user wants a TIFF preview, route the eps file
608 	   to a temporary one */
609 	if (tiffpreview) {
610 		saveofile = tfp;
611 		/* make name for temp output file */
612 		if ((tfp = xtmpfile(&tmpeps, sizeof tmpeps_buf)) == NULL) {
613 			put_msg("Can not create temporary file %s", tmpeps);
614 			put_msg("No preview will be produced.");
615 			if (tmpeps != tmpeps_buf)
616 				free(tmpeps);
617 			tfp = saveofile;
618 			tiffpreview = false;
619 		}
620 	}
621 
622 	/* now that the file has been read,
623 	   turn off multipage mode if eps output */
624 	if (epsflag)
625 		multi_page = false;
626 
627 	scalex = scaley = mag * POINT_PER_INCH / ppi;
628 
629 	/* this seems to work around Solaris' cc optimizer bug */
630 	/* the problem was that llx had garbage in it - this "fixes" it */
631 	sprintf(host,"llx=%d\n",llx);
632 
633 	/* convert to point unit */
634 	fllx = llx * scalex;
635 	flly = lly * scaley;
636 	furx = urx * scalex;
637 	fury = ury * scaley;
638 
639 	/* adjust for any border margin */
640 
641 	fllx -= border_margin;
642 	flly -= border_margin;
643 	furx += border_margin;
644 	fury += border_margin;
645 
646 	/* convert ledger (deprecated) to tabloid */
647 	if (strcasecmp(papersize, "ledger") == 0)
648 		strcpy(papersize, "tabloid");
649 	for (pd = paperdef; pd->name != NULL; ++pd) {
650 		if (strcasecmp(papersize, pd->name) == 0) {
651 			pagewidth = pd->width;
652 			pageheight = pd->height;
653 			strcpy(papersize, pd->name);  /* use the "nice" form */
654 			break;
655 		}
656 	}
657 
658 	if (pagewidth < 0 || pageheight < 0) {
659 		put_msg("Unknown paper size `%s'", papersize);
660 		exit (1);
661 	}
662 
663 	if (epsflag) {
664 	    /* shift figure to 0,0 */
665 	    origx = -fllx;
666 	    origy =  fury;
667 	    if (boundingboxspec) {
668 		    jtmp = sscanf(boundingbox, "%lf %lf %lf %lf",
669 				    &userwidthx, &userwidthy,
670 				    &userorigx, &userorigy);
671 		    switch (jtmp) {
672 		    case 0:
673 			    userwidthx=(furx-fllx)/POINT_PER_INCH;
674 			    if (metric)
675 				    userwidthx *= 2.54;
676 			    /* the comment below silences gcc's
677 			       -Wimplicit-fallthrough warning */
678 			    /* intentionally fall through */
679 			    /* and set the other user... vars */
680 		    case 1:
681 			    userwidthy=(fury-flly)/POINT_PER_INCH;
682 			    if (metric)
683 				    userwidthy *= 2.54;
684 			    /* intentionally fall through */
685 			    /* and set the other user... vars */
686 		    case 2:
687 			    userorigx=0;
688 			    /* intentionally fall through */
689 			    /* and set the last user... var */
690 		    case 3:
691 			    userorigy=0;
692 		    }
693 		    if (userwidthx <= 0) {
694 			    userwidthx=(furx-fllx)/POINT_PER_INCH;
695 			    if (metric)
696 				    userwidthx *= 2.54;
697 		    }
698 		    if (userwidthy <= 0) {
699 			    userwidthy=(fury-flly)/POINT_PER_INCH;
700 			    if (metric)
701 				    userwidthy *= 2.54;
702 		    }
703 
704 		    userorigx  *= POINT_PER_INCH;
705 		    userorigy  *= POINT_PER_INCH;
706 		    userwidthx *= POINT_PER_INCH;
707 		    userwidthy *= POINT_PER_INCH;
708 
709 		    if (metric) {
710 			    userorigx  /= 2.54;
711 			    userorigy  /= 2.54;
712 			    userwidthx /= 2.54;
713 			    userwidthy /= 2.54;
714 		    }
715 
716 		    userllx = (int) floor(userorigx);
717 		    userlly = (int) floor(userorigy);
718 		    userurx = (int) ceil(userorigx+userwidthx);
719 		    userury = (int) ceil(userorigy+userwidthy);
720 
721 		    /* adjust for any border margin */
722 		    userllx -= border_margin;
723 		    userlly -= border_margin;
724 		    userurx += border_margin;
725 		    userury += border_margin;
726 
727 		    if (useabsolutecoo) {
728 			    userllx += origx;
729 			    userurx += origx;
730 		    }
731 	    }
732 	} else {	/* postscript */
733 		if (landscape) {
734 			itmp = pageheight;
735 			pageheight = pagewidth;
736 			pagewidth = itmp;
737 		}
738 		if (center) {
739 			origx = (pagewidth - furx - fllx)/2.0;
740 			origy = (pageheight + fury + flly)/2.0;
741 		} else {
742 			origx = 0.0;
743 			origy = pageheight;
744 		}
745 	}
746 
747 	/* finally, adjust by any offset the user wants */
748 	if (!epsflag) {
749 		origx += xoff;
750 		origy += yoff;
751 	}
752 
753 	if (epsflag)
754 		fputs("%!PS-Adobe-3.0 EPSF-3.0\n", tfp);
755 	else
756 		fputs("%!PS-Adobe-3.0\n", tfp);
757 
758 #ifdef HAVE_GETHOSTNAME
759 	if (gethostname(host, sizeof(host)) == -1)
760 #endif
761 		(void) strcpy(host, "unknown host");
762 	fprintf(tfp, "%%%%Title: %s\n",
763 		(name? name: ((from) ? from : "stdin")));
764 	fprintf(tfp, "%%%%Creator: %s Version %s\n",
765 		prog, PACKAGE_VERSION);
766 	if (creation_date(date_buf))
767 		fprintf(tfp, "%%%%CreationDate: %s\n", date_buf);
768 #ifdef HAVE_GETPWUID
769 	if (!anonymous) {
770 		who = getpwuid(getuid());
771 		if (who)
772 			fprintf(tfp, "%%%%For: %s@%s (%s)\n",
773 				who->pw_name, host, who->pw_gecos);
774 	}
775 #endif
776 
777 	/* calc initial clipping area to size of the bounding box
778 	   (this is needed for later clipping by arrowheads) */
779 	cliplx = cliply = 0;
780 	if (epsflag) {
781 		clipux = (int) ceil(furx-fllx);
782 		clipuy = (int) ceil(fury-flly);
783 		pages = 1;
784 	} else {
785 		clipux = pagewidth;
786 		clipuy = pageheight;
787 		if (multi_page)
788 			/* account for overlap */
789 			pages = (int)(1.11111 * (furx - 0.1*pagewidth)
790 					/ pagewidth + 1) *
791 				(int)(1.11111 * (fury - 0.1*pageheight)
792 						/ pageheight + 1);
793 		else
794 			pages = 1;
795 		/*
796 		 * If the line %%Orientation: Landscape is included, then
797 		 *  - evince displays the page rotated by -90 degrees,
798 		 *  - the apple previewer (v8.1) displays it correctly,
799 		 *  - gs displays the ps-file correctly, but
800 		 *  - the pdf produced from this ps is rotated by -90 degrees
801 		 * Therefore, omit the %%Orientation comment. Although, I
802 		 * believe, evince and the pdf produced by gs do not get the
803 		 * %%Orientation comment right.
804 		 * The PostScript Language Document Structuring Conventions
805 		 * Specification, Version 3.0, says to %%Orientation:
806 		 * This comment indicates the orientation of the pages in the
807 		 * document. It can be used by previewing applications and
808 		 * post-processors to determine how to orient the viewing
809 		 * window. A portrait orientation indicates that the longest
810 		 * edge of the paper is parallel to the vertical (y) axis. A
811 		 * landscape orientation indicates that the longest edge of the
812 		 * paper is parallel to the horizontal (x) axis. If more than
813 		 * one orientation applies to the document, an individual page
814 		 * should specify its orientation by using the
815 		 * %%PageOrientation: comment.
816 		if (landscape)
817 			fputs("%%Orientation: Landscape\n", tfp);
818 		else
819 			fputs("%%Orientation: Portrait\n", tfp);
820 		 */
821 	}
822 	if (!epsflag) {
823 		/* only print Pages if PostScript or PDF */
824 		fprintf(tfp, "%%%%Pages: %d\n", pages );
825 	}
826 	if (!boundingboxspec) {
827 		fprintf(tfp, "%%%%BoundingBox: %d %d %d %d\n",
828 				cliplx, cliply, clipux, clipuy);
829 		/* width for tiff preview */
830 		width = clipux-cliplx+1;
831 		height = clipuy-cliply+1;
832 	} else {
833 		fprintf(tfp, "%%%%BoundingBox: %d %d %d %d\n",
834 				userllx, userlly, userurx, userury);
835 		/* width for tiff preview */
836 		width = userurx - userllx + 1;
837 		height = userury - userlly + 1;
838 	}
839 
840 	/* only include a pagesize command if PS */
841 	if (!epsflag) {
842 		/* add comment for ghostview to recognize the page size */
843 		/* make sure to use the lowercase paper size name */
844 		strcpy(psize,papersize);
845 		for (i = strlen(psize)-1; i >= 0; --i)
846 			psize[i] = tolower(psize[i]);
847 		fprintf(tfp, "%%%%DocumentPaperSizes: %s\n", psize);
848 	}
849 
850 	/* put in the magnification for information purposes */
851 	fprintf(tfp, "%%%%Magnification: %.4f\n",metric? mag*76.2/80.0 : mag);
852 	fputs("%%EndComments\n", tfp);
853 
854 	/* This %%BeginSetup .. %%EndSetup has to occur after
855 	 * %%EndComments even though it includes comments, they are
856 	 * not header comments. The header comment block must be
857 	 * contiguous, with no non-comment lines in it.
858 	 */
859 	if (!epsflag && !pdfflag) {
860 		fputs("%%BeginSetup\n", tfp);
861 		fputs("[{\n", tfp);
862 		fprintf(tfp, "%%%%BeginFeature: *PageSize %s\n", papersize);
863 		fprintf(tfp, "<</PageSize [%d %d]>> setpagedevice\n",
864 				pagewidth, pageheight);
865 		fputs("%%EndFeature\n", tfp);
866 		fputs("} stopped cleartomark\n", tfp);
867 		fputs("%%EndSetup\n", tfp);
868 	} else if (pdfflag) {
869 		/* set the page size for PDF to the figure size */
870 		fprintf(tfp, "<< /PageSize [%d %d] >> setpagedevice\n",
871 			clipux - cliplx, clipuy - cliply);
872 	}
873 
874 	/* if the user wants an ASCII preview,
875 	   route the rest of the eps to a temp file */
876 	if (asciipreview) {
877 	    saveofile = tfp;
878 	    /* make name for temp output file */
879 	    if ((tfp = xtmpfile(&tmpeps, sizeof tmpeps_buf)) == NULL) {
880 		fprintf(stderr, "Can not create temporary file %s.\n", tmpeps);
881 		fputs("No preview will be produced.\n", stderr);
882 		if (tmpeps != tmpeps_buf)
883 			free(tmpeps);
884 		asciipreview = false;
885 		tfp = saveofile;
886 	    }
887 	}
888 
889 	/* print any whole-figure comments prefixed with "%" */
890 	if (objects->comments) {
891 		fputs("%\n", tfp);
892 		print_comments("% ",objects->comments, "");
893 		fputs("%\n", tfp);
894 	}
895 
896 	/* insert PostScript codes to select paper size, if exist */
897 	libdir = getenv("FIG2DEV_LIBDIR");
898 #ifdef I18N_DATADIR
899 	if (libdir == NULL)
900 		libdir = I18N_DATADIR;
901 #endif
902 	if (libdir != NULL) {
903 		sprintf(filename, "%s/%s.ps", libdir, papersize);
904 		/* get filename like "/usr/local/lib/fig2dev/A3.ps" and
905 		   prepend it to the postscript code;
906 		   do not mind, if it does not work */
907 		(void)append(filename, tfp);
908 	}
909 
910 	fputs("%%BeginProlog\n", tfp);
911 	if (pats_used)
912 		fprintf(tfp,"/MyAppDict 100 dict dup begin def\n");
913 	fprintf(tfp, "%s", BEGIN_PROLOG1);
914 	/* define the standard colors */
915 	genps_std_colors();
916 	/* define the user colors */
917 	genps_usr_colors();
918 	fputs("\nend\n", tfp);
919 
920 	/* fill the Background now if specified */
921 	if (bgspec) {
922 		fprintf(tfp, "%% Fill background color\n");
923 		fprintf(tfp, "%d %d moveto %d %d lineto ",
924 				cliplx, cliply, clipux, cliply);
925 		fprintf(tfp, "%d %d lineto %d %d lineto\n",
926 				clipux, clipuy, cliplx, clipuy);
927 		if (grayonly)
928 			fprintf(tfp, "closepath %.2f setgray fill\n\n",
929 				rgb2luminance(background.red/65535.0,
930 						background.green/65535.0,
931 						background.blue/65535.0));
932 		else
933 			fprintf(tfp,
934 				"closepath %.2f %.2f %.2f setrgbcolor fill\n\n",
935 				background.red/65535.0,
936 				background.green/65535.0,
937 				background.blue/65535.0);
938 	}
939 
940 	/* translate (in multi-page mode this is done at end of this proc) */
941 	/* (rotation and y flipping is done in %%BeginPageSetup area */
942 	if (pats_used) {
943 		int i;
944 		/* only define the patterns that are used */
945 		for (i=0; i<NUMPATTERNS; i++)
946 			if (pattern_used[i])
947 				fprintf(tfp, "\n%s", fill_def[i]);
948 	}
949 	fprintf(tfp, "\n%s", BEGIN_PROLOG2);
950 	if (iso_text_exist(objects)) {
951 		fprintf(tfp, "%s%s%s",
952 			SPECIAL_CHAR_1, SPECIAL_CHAR_2, SPECIAL_CHAR_3);
953 		encode_all_fonts(objects);
954 	}
955 	if (ellipse_exist(objects))
956 		fprintf(tfp, "%s\n", ELLIPSE_PS);
957 	if (approx_spline_exist(objects))
958 		fprintf(tfp, "%s\n", SPLINE_PS);
959 #ifdef I18N
960 	if (support_i18n && iso_text_exist(objects)) {
961 		char *libdir, *locale;
962 		char localefile_buf[128];
963 		char *localefile = localefile_buf;
964 		FILE *fp;
965 		libdir = getenv("FIG2DEV_LIBDIR");
966 #ifdef I18N_DATADIR
967 		if (libdir == NULL)
968 			libdir = I18N_DATADIR;
969 #endif
970 		locale = setlocale(LC_CTYPE, NULL);
971 		if (locale == NULL) {
972 			fprintf(stderr,
973 			      "fig2dev: LANG not defined; assuming C locale\n");
974 			locale = "C";
975 		}
976 		if (strlen(libdir) + strlen(locale) + 5 > sizeof localefile_buf)
977 			localefile = malloc(strlen(libdir) + strlen(locale) + 5);
978 		if (localefile != NULL) {
979 			sprintf(localefile, "%s/%s.ps", libdir, locale);
980 			/* get filename like
981 			   ``/usr/local/lib/fig2dev/japanese.ps'' */
982 			fp = fopen(localefile, "rb");
983 			if (fp == NULL) {
984 				fprintf(stderr, "fig2dev: can not open file: %s\n",
985 						localefile);
986 			} else {
987 				enable_composite_font =
988 					append_find_composite(tfp, fp);
989 
990 				if (ferror(tfp)) {
991 					fputs("Error writing output file.\n",
992 							stderr);
993 					exit(EXIT_FAILURE);
994 				}
995 				if (ferror(fp)) {
996 					fprintf(stderr,
997 						"Error reading file %s.\n"
998 						"The output might be broken.\n",
999 						localefile);
1000 				}
1001 			fclose(fp);
1002 			}
1003 		}
1004 		if (localefile != localefile_buf)
1005 			free(localefile);
1006 	}
1007 #endif /* I18N */
1008 
1009 	fprintf(tfp, "%s\n", END_PROLOG);
1010 
1011 	fputs("/pageheader {\n", tfp);
1012 
1013 	/* must specify translation/rotation
1014 	   before definition of fill patterns */
1015 	fputs("sa\n", tfp);
1016 
1017 	/* now make the clipping path for the BoundingBox */
1018 	fprintf(tfp, "n %d %d m %d %d l %d %d l %d %d l cp clip\n",
1019 		cliplx,clipuy, cliplx,cliply, clipux,cliply, clipux,clipuy);
1020 	if (!multi_page) {
1021 		fprintf(tfp, "%.1f %.1f tr\n", origx, origy);
1022 		if (epsflag)
1023 			/* increasing y goes down */
1024 			fprintf(tfp, "1 -1 sc\n");
1025 	}
1026 
1027 	fputs("$F2psBegin\n", tfp);
1028 
1029 	fputs("10 setmiterlimit\n", tfp);  /* make like X server (11 degrees) */
1030 	/* set initial join style to miter and cap to butt */
1031 	fputs("0 slj 0 slc\n", tfp);
1032 	if( !multi_page)
1033 		fprintf(tfp, " %.5f %.5f sc\n", scalex, scaley );
1034 	fputs("} bind def\n", tfp);
1035 
1036 
1037 	fputs("/pagefooter {\n", tfp);
1038 	fputs("$F2psEnd\n", tfp);
1039 	fputs("restore\n", tfp);
1040 	fputs("} bind def\n", tfp);
1041 
1042 
1043 	if (multi_page) {
1044 		/* reset the matrix for multipage mode */
1045 		fputs("initmatrix\n", tfp);
1046 	} else {
1047 		fputs("%%EndProlog\n", tfp);
1048 		if (!epsflag) {
1049 			fputs("%%Page: 1 1\n", tfp);
1050 			fputs("%%BeginPageSetup\n", tfp);
1051 			fputs("pageheader\n", tfp);
1052 			/* increasing y goes down */
1053 			fputs("1 -1 scale\n", tfp);
1054 			fputs("%%EndPageSetup\n", tfp);
1055 		} else {
1056 			fputs("pageheader\n", tfp);
1057 		}
1058 	}
1059 
1060 	fputs("%\n", tfp);
1061 	fputs("% Fig objects follow\n", tfp);
1062 	fputs("%\n", tfp);
1063 }
1064 
1065 /* Draw a grid on the figure */
1066 
1067 void
genps_grid(float major,float minor)1068 genps_grid(float major, float minor)
1069 {
1070 	float	lx, ly, ux, uy;
1071 	float	x, y;
1072 	double	thick, thin;
1073 	int	itick, ntick;
1074 	float	m;
1075 
1076 	/* see if grid specified */
1077 	if (minor == 0.0 && major == 0.0)
1078 		return;
1079 
1080 	/* In case of multi postscript gridlines should be the
1081 	   figure with maximum depth */
1082 	do_split(MAXDEPTH+2);
1083 
1084 	m = minor;
1085 	if (minor == 0.0)
1086 		m = major;
1087 
1088 	/* start here */
1089 	if (epsflag) {
1090 		/* for eps, only do bounding area of figure */
1091 		lx = floor((fllx / scalex) / m) * m;
1092 		ly = floor((flly / scaley) / m) * m;
1093 		ux = furx / scalex;
1094 		uy = fury / scaley;
1095 	} else {
1096 		/* for pdf and PostScript, grid the whole page */
1097 		lx = 0.0;
1098 		ly = 0.0;
1099 		ux = pagewidth / scalex;
1100 		uy = pageheight / scaley;
1101 	}
1102 	thin = THICK_SCALE;
1103 	thick = THICK_SCALE * 2.5;
1104 
1105 	fputs("% Grid\n", tfp);
1106 	fputs("0.5 setgray\n", tfp);
1107 	/* adjust scale for difference in xfig/actual scale in metric mode */
1108 	if (metric)
1109 		fprintf(tfp,"gs 450 472 div dup scale\n");
1110 	/* first the vertical lines */
1111 	fputs("% Vertical\n", tfp);
1112 	for (x = lx; x <= ux; x += m) {
1113 		if (major > 0.0) {
1114 			itick = (int)(x/major)*major;
1115 			/* if on a major tick, or if next minor tick is beyond
1116 			   major tick, make a major tick */
1117 			if (itick == x)
1118 				set_linewidth(thick);
1119 			else {
1120 				/* not exactly on a major tick, see if next
1121 				   minor would be beyond a major */
1122 				ntick = (int)((x+minor)/major)*major;
1123 				if (ntick < x+minor) {
1124 					/* yes, draw the major */
1125 					set_linewidth(thick);
1126 					draw_gridline((float)ntick, ly,
1127 							(float)ntick, uy);
1128 				}
1129 				/* reset to draw the thin grid line */
1130 				set_linewidth(thin);
1131 			}
1132 		} else {
1133 			set_linewidth(thin);
1134 		}
1135 		draw_gridline(x, ly, x, uy);
1136 	}
1137 	/* now the horizontal */
1138 	fputs("% Horizontal\n", tfp);
1139 	for (y = ly; y <= uy; y += m) {
1140 		if (major > 0.0) {
1141 			itick = (int)(y/major)*major;
1142 			/* if on a major tick, or if next minor tick is beyond
1143 			   major tick, make a major tick */
1144 			if (itick == y)
1145 				set_linewidth(thick);
1146 			else {
1147 				/* not exactly on a major tick, see if next
1148 				   minor would be beyond a major */
1149 				ntick = (int)((y+minor)/major)*major;
1150 				if (ntick < y+minor) {
1151 					/* yes, draw the major */
1152 					set_linewidth(thick);
1153 					draw_gridline(lx, (float)ntick, ux,
1154 							(float)ntick);
1155 				}
1156 				/* reset to draw the thin grid line */
1157 				set_linewidth(thin);
1158 			}
1159 		} else {
1160 			set_linewidth(thin);
1161 		}
1162 		draw_gridline(lx, y, ux, y);
1163 	}
1164 	/* restore original scale */
1165 	if (metric)
1166 		fprintf(tfp,"gr\n");
1167 }
1168 
1169 static void
draw_gridline(float x1,float y1,float x2,float y2)1170 draw_gridline(float x1, float y1, float x2, float y2)
1171 {
1172 	fprintf(tfp, "n %.1f %.1f m %.1f %.1f l s\n", x1, y1, x2, y2);
1173 }
1174 
1175 
1176 int
genps_end(void)1177 genps_end(void)
1178 {
1179 	double	dx, dy, mul;
1180 	int		i, page;
1181 	const int	h = pageheight, w = pagewidth;
1182 	int		epslen, tiflen;
1183 	struct stat	fstat;
1184 
1185 	/* for multipage, translate and output objects for each page */
1186 	if (multi_page) {
1187 		fputs("%%EndProlog\n", tfp);
1188 		page = 1;
1189 		if (overlap)
1190 			mul = 0.9;
1191 		else
1192 			mul = 1.0;
1193 
1194 		for (dy=0; (dy < (fury-h*0.1)) || (page == 1); dy += h*mul) {
1195 			for (dx=0; (dx < (furx-w*0.1)) || (page == 1);
1196 					dx += w*mul) {
1197 				fprintf(tfp, "%%%%Page: %d %d\n", page, page);
1198 
1199 				fputs("pageheader\n", tfp);
1200 				/* do page rotation here */
1201 				fputs("%%BeginPageSetup\n", tfp);
1202 				/* increasing y goes down */
1203 				fputs(" 1 -1 sc\n", tfp);
1204 				fputs("%%EndPageSetup\n", tfp);
1205 
1206 				fputs("gs\n", tfp);
1207 				fprintf(tfp, "%.1f %.1f tr\n", -dx,-(dy+h*mul));
1208 				fprintf(tfp, " %.3f %.3f sc\n", scalex, scaley);
1209 				for (i=0; i<no_obj; ++i) {
1210 					fprintf(tfp, "o%d ", i);
1211 					if (!(i%20))
1212 						fputc('\n', tfp);
1213 				}
1214 				fputs("gr\n", tfp);
1215 				fputs("pagefooter\n", tfp);
1216 				fputs("showpage\n", tfp);
1217 				++page;
1218 			}
1219 		}
1220 	}
1221 	/* Close the (last) figure */
1222 	do_split(-10);
1223 
1224 	/* add showpage if requested */
1225 	if (!multi_page) {
1226 		fputs("pagefooter\n", tfp);
1227 		fputs("showpage\n", tfp);
1228 	}
1229 
1230 	/* does the user want an ASCII or TIFF preview? */
1231 	if (tiffpreview || asciipreview) {
1232 		/* close temp eps file */
1233 		fclose(tfp);
1234 		/* revert original file back to tfp */
1235 		tfp = saveofile;
1236 
1237 		/* make name for temp output file */
1238 		if ((saveofile = xtmpfile(&tmpprev, sizeof tmpprev_buf)) ==
1239 				NULL) {
1240 			put_msg("Can not create temporary file %s.", tmpprev);
1241 			put_msg("No preview will be produced.");
1242 			if (tmpprev != tmpprev_buf)
1243 				free(tmpprev);
1244 			/* Output the eps stored in tmpeps */
1245 			if (append(tmpeps, tfp) == -1) {
1246 				put_msg("Cannot open temp file %s.", tmpeps);
1247 				remove(tmpeps);
1248 				/* unnecessary:
1249 				   if (tmpeps != tmpeps_buf) free(tmpeps); */
1250 				exit(EXIT_FAILURE);
1251 			}
1252 			remove(tmpeps);
1253 			if (tmpeps != tmpeps_buf)
1254 				free(tmpeps);
1255 			asciipreview = tiffpreview = false;
1256 		} else {
1257 #ifdef GSEXE
1258 			/* make the ghostscript command to generate the ASCII
1259 			   or TIFF file from the temp eps file */
1260 			sprintf(gscom, "%s -q -dSAFER -sDEVICE=%s -r72 -g%dx%d "
1261 					"-o \'%s\' %s",
1262 					GSEXE, asciipreview ? "bit" :
1263 					(tiffcolor ? "tiff24nc" : "tifflzw"),
1264 					width, height, tmpprev, tmpeps);
1265 			if (system(gscom) != 0) {
1266 				fprintf(stderr,
1267 					     "Error calling ghostscript: %s\n",
1268 					     gscom);
1269 #else
1270 				fputs("Ghostscript not available. ", stderr);
1271 #endif
1272 				fprintf(stderr,"No preview will be produced\n");
1273 				remove(tmpprev);
1274 				if (tmpprev != tmpprev_buf)
1275 					free(tmpprev);
1276 				/* append the eps */
1277 				if (append(tmpeps, tfp) == -1) {
1278 					put_msg("Cannot open temp file %s.",
1279 							tmpeps);
1280 					remove(tmpeps);
1281 					exit(EXIT_FAILURE);
1282 				}
1283 				remove(tmpeps);
1284 				if (tmpeps != tmpeps_buf)
1285 					free(tmpeps);
1286 				/* and cancel the preview */
1287 				asciipreview = tiffpreview = false;
1288 #ifdef GSEXE
1289 			}
1290 #endif
1291 			fclose(saveofile);
1292 		}
1293 		if (asciipreview) {
1294 			--width;
1295 			--height;
1296 			/* now attach the preview after the prolog
1297 			   then attach the rest of the eps */
1298 			fprintf(tfp, "%%%%BeginPreview: %d %d %d %d\n",
1299 					width, height, 1, height);
1300 			appendhex(tmpprev, tfp, width, height);
1301 			remove(tmpprev);
1302 			fputs("%%EndPreview\n", tfp);
1303 			if (append(tmpeps, tfp) == -1) {
1304 				put_msg("Cannot open temp file %s.", tmpeps);
1305 				remove(tmpeps);
1306 				exit(EXIT_FAILURE);
1307 			}
1308 			remove(tmpeps);
1309 
1310 		} else if (tiffpreview) {
1311 			/*  now make the binary header in the final output file
1312 			    and append the eps and tiff files */
1313 
1314 			stat(tmpeps, &fstat);
1315 			epslen = fstat.st_size;	/* size of eps file */
1316 
1317 			stat(tmpprev, &fstat);
1318 			tiflen = fstat.st_size;	/* size of tif file */
1319 
1320 			/* write header ident C5D0D3C6 */
1321 			putc(0xC5, tfp);
1322 			putc(0xD0, tfp);
1323 			putc(0xD3, tfp);
1324 			putc(0xC6, tfp);
1325 			/* put byte offset of the EPS part
1326 			   (always 30 - immediately after the header) */
1327 			putword(30, tfp);
1328 			/* now size of eps part */
1329 			putword(epslen, tfp);
1330 			/* no Metafile */
1331 			putword(0, tfp);
1332 			putword(0, tfp);
1333 			/* byte offset of TIFF part */
1334 			putword(epslen+30, tfp);
1335 			/* and length of TIFF part */
1336 			putword(tiflen, tfp);
1337 			/* finally, FFFF (no checksum) */
1338 			putc(0xFF, tfp);
1339 			putc(0xFF, tfp);
1340 			/* now copy eps out */
1341 			if (append(tmpeps, tfp) == -1) {
1342 				put_msg("Cannot open temp file %s.", tmpeps);
1343 				remove(tmpprev);
1344 				remove(tmpeps);
1345 				exit(EXIT_FAILURE);
1346 			}
1347 			remove(tmpeps);
1348 			/* and finally, the tiff file */
1349 			if (append(tmpprev, tfp) == -1) {
1350 				put_msg("Cannot open temp file %s.", tmpprev);
1351 				remove(tmpprev);
1352 				remove(tmpeps);
1353 				exit(EXIT_FAILURE);
1354 			}
1355 			remove(tmpprev);
1356 			putc('\n', tfp);
1357 
1358 		}
1359 		if (tmpeps != tmpeps_buf)
1360 			free(tmpeps);
1361 		if (tmpprev != tmpprev_buf)
1362 			free(tmpprev);
1363 	}
1364 	/* put any cleanup between %%Trailer and %EOF */
1365 	fputs("%%Trailer\n", tfp);
1366 	if (pats_used)
1367 		fputs("end\n", tfp);		/* close off MyAppDict */
1368 	/* final DSC comment for eps output (EOF = end of document) */
1369 	fputs("%EOF\n", tfp);
1370 
1371 	/* all ok */
1372 	return 0;
1373 }
1374 
1375 /* write a 32-bit value LSB first to file */
1376 
1377 static void
putword(int word,FILE * file)1378 putword(int word, FILE *file)
1379 {
1380 	register int i;
1381 
1382 	for (i=0; i<4; i++) {
1383 		putc((unsigned char) word & 0xff, file);
1384 		word >>= 8;
1385 	}
1386 }
1387 
1388 /*
1389  * append file named in "infilename" to already open FILE "outfile"
1390  * Return 0 on sucess, -1 if "infilename" cannot be opened.
1391  */
1392 
1393 static int
append(const char * restrict infilename,FILE * outfile)1394 append(const char *restrict infilename, FILE *outfile)
1395 {
1396 	FILE	*infile;
1397 	char	buf[BUFSIZ];
1398 	size_t	buf_len = sizeof buf;
1399 	size_t	chars;
1400 
1401 	if ((infile = fopen(infilename, "rb")) == 0)
1402 		return -1;
1403 
1404 	while ((chars = fread(buf, (size_t)1, buf_len, infile)) == buf_len &&
1405 			buf_len == fwrite(buf, (size_t)1, buf_len, outfile))
1406 		;
1407 	if (!ferror(outfile) && chars > 0)
1408 		fwrite(buf, (size_t)1, chars, outfile);
1409 
1410 	fclose(infile);
1411 	return 0;
1412 }
1413 
1414 #ifdef I18N
1415 /*
1416  * Append open file in to open file out while searching for the string
1417  * "CompositeRoman".  Return true if found, false otherwise.
1418  */
1419 static bool
append_find_composite(FILE * restrict out,FILE * restrict in)1420 append_find_composite(FILE *restrict out, FILE *restrict in)
1421 {
1422 	/*
1423 	 * Initially, the needle was passed as a function parameter. However,
1424 	 * Visual Studio cannot create an array of a size depending on a
1425 	 * function parameter (str[], below). Therefore, use the fixed needle
1426 	 * "CompositeRoman". Moreover, Visual Studio does not accept a const
1427 	 * qualified parameter as array size, but "sizeof ...".
1428 	 */
1429 	bool		found = false;
1430 	const char	needle[] = "CompositeRoman";
1431 	char		str[BUFSIZ + sizeof needle];
1432 	const size_t	needle_len = sizeof needle - 1;
1433 	const size_t	str_len = (size_t)BUFSIZ;
1434 	size_t		chars;
1435 
1436 	/* The needle could be split up between two consecutive buffers.
1437 	   Keep some margin and initialize it with spaces. */
1438 	for (chars = 0; chars < needle_len; ++chars)
1439 		str[chars] = ' ';
1440 	/* 0-terminate str, otherwise strstr() scans into unknown territory. */
1441 	str[BUFSIZ + needle_len] = '\0';
1442 
1443 	while ((chars = fread(str + needle_len, (size_t)1, str_len, in)) ==
1444 			str_len) {
1445 		if (!found && strstr(str, needle))
1446 			found = true;
1447 		memcpy(str, str + str_len, needle_len);
1448 		if (fwrite(str + needle_len, (size_t)1, str_len, out) !=
1449 				str_len)
1450 			break;
1451 	}
1452 	/* Copy the last, incomplete read. */
1453 	if (!ferror(out) && chars > 0) {
1454 		if (!found) {
1455 			str[needle_len + chars] = '\0';
1456 			if (strstr(str, needle))
1457 				found = true;
1458 		}
1459 		fwrite(str + needle_len, (size_t)1, chars, out);
1460 	}
1461 	return found;
1462 }
1463 #endif /* I18N */
1464 
1465 
1466 /* read file named in "infilename", converting the binary to hex and
1467    append to already open FILE "outfile".
1468    width is the number of hex values per line that should be written
1469    and height is the number of lines total. */
1470 
1471 static void
appendhex(char * infilename,FILE * outfile,int width,int height)1472 appendhex(char *infilename, FILE *outfile, int width, int height)
1473 {
1474 	FILE		*infile;
1475 	unsigned char	byte;
1476 	int		len, i, j;
1477 
1478 	if ((infile = fopen(infilename, "r")) == 0) {
1479 		fprintf(stderr, "Can't open temp file %s\n", infilename);
1480 		exit(1);
1481 	}
1482 	len = (width+7)/8;
1483 	for (j=0; j<height; ++j) {
1484 		fputs("% ", outfile);
1485 		for (i=0; i<len; ++i) {
1486 			if (fread(&byte, 1, 1, infile) == 0)
1487 				break;
1488 			fprintf(outfile, "%02X", byte);
1489 		}
1490 		fputc('\n', outfile);
1491 	}
1492 	fclose(infile);
1493 }
1494 
1495 
1496 static void
set_style(int s,double v)1497 set_style(int s, double v)
1498 {
1499 	v /= 80.0 / ppi;
1500 	if (s == DASH_LINE) {
1501 		if (v > 0.0) fprintf(tfp, " [%d] 0 sd\n", round(v));
1502 	} else if (s == DOTTED_LINE) {
1503 		if (v > 0.0) fprintf(tfp, " [%d %d] %d sd\n",
1504 				round(ppi/80.0), round(v), round(v));
1505 	} else if (s == DASH_DOT_LINE) {
1506 		if (v > 0.0) fprintf(tfp, " [%d %d %d %d] 0 sd\n",
1507 				round(v), round(v*0.5),
1508 				round(ppi/80.0), round(v*0.5));
1509 	} else if (s == DASH_2_DOTS_LINE) {
1510 		if (v > 0.0) fprintf(tfp, " [%d %d %d %d %d %d] 0 sd\n",
1511 				round(v), round(v*0.45),
1512 				round(ppi/80.0), round(v*0.333),
1513 				round(ppi/80.0), round(v*0.45));
1514 	} else if (s == DASH_3_DOTS_LINE) {
1515 		if (v > 0.0) fprintf(tfp,
1516 				" [%d %d %d %d %d %d %d %d ] 0 sd\n",
1517 				round(v), round(v*0.4),
1518 				round(ppi/80.0), round(v*0.3),
1519 				round(ppi/80.0), round(v*0.3),
1520 				round(ppi/80.0), round(v*0.4));
1521 	}
1522 }
1523 
1524 static void
reset_style(int s,double v)1525 reset_style(int s, double v)
1526 {
1527 	if (v > 0.0 && s >= DASH_LINE && s <= DASH_3_DOTS_LINE)
1528 		fputs(" [] 0 sd\n", tfp);
1529 }
1530 
1531 static void
set_linejoin(int j)1532 set_linejoin(int j)
1533 {
1534 	if (j != cur_joinstyle) {
1535 		cur_joinstyle = j;
1536 		fprintf(tfp, "%d slj\n", cur_joinstyle);
1537 	}
1538 }
1539 
1540 static void
set_linecap(int j)1541 set_linecap(int j)
1542 {
1543 	if (j != cur_capstyle) {
1544 		cur_capstyle = j;
1545 		fprintf(tfp, "%d slc\n", cur_capstyle);
1546 	}
1547 }
1548 
1549 static void
set_linewidth(double w)1550 set_linewidth(double w)
1551 {
1552 	if (w != cur_thickness) {
1553 		cur_thickness = w;
1554 		fprintf(tfp, "%.3f slw\n",     /* make lines a little thinner */
1555 				cur_thickness <= THICK_SCALE ?
1556 				0.5* cur_thickness :
1557 				cur_thickness - THICK_SCALE);
1558 	}
1559 }
1560 
1561 void
genps_line(F_line * l)1562 genps_line(F_line *l)
1563 {
1564 	F_point		*p, *q;
1565 	int		 radius;
1566 	int		 i;
1567 	int		 xmin,xmax,ymin,ymax;
1568 	int		 pic_w, pic_h, img_w, img_h;
1569 	float		 hf_wid;
1570 
1571 	do_split(l->depth);
1572 
1573 	if (multi_page)
1574 	   fprintf(tfp, "/o%d {", no_obj++);
1575 
1576 	/* print any comments prefixed with "%" */
1577 	print_comments("% ",l->comments, "");
1578 
1579 	fputs("% Polyline\n", tfp);
1580 	if (l->type != T_PIC_BOX) {  /* pic object has no line thickness */
1581 		set_linejoin(l->join_style);
1582 		set_linecap(l->cap_style);
1583 		set_linewidth((double)l->thickness);
1584 	}
1585 	p = l->points;
1586 	q = p->next;
1587 	if (q == NULL) { /* A single point line */
1588 		if (l->cap_style > 0)
1589 			hf_wid = 1.0;
1590 		else if (l->thickness <= THICK_SCALE)
1591 			hf_wid = l->thickness/4.0;
1592 		else
1593 			hf_wid = (l->thickness-THICK_SCALE)/2.0;
1594 		fprintf(tfp, "n %d %d m %d %d l gs col%d s gr\n",
1595 				round(p->x-hf_wid), p->y, round(p->x+hf_wid),
1596 				p->y, l->pen_color);
1597 		if (multi_page)
1598 			fputs("} bind def\n", tfp);
1599 		return;
1600 	}
1601 	if (l->type != T_PIC_BOX) {
1602 		set_style(l->style, l->style_val);
1603 	}
1604 
1605 	xmin = xmax = p->x;
1606 	ymin = ymax = p->y;
1607 	while (p->next != NULL) { /* find lower left and upper right corners */
1608 		p=p->next;
1609 		if (xmin > p->x)
1610 			xmin = p->x;
1611 		else if (xmax < p->x)
1612 			xmax = p->x;
1613 		if (ymin > p->y)
1614 			ymin = p->y;
1615 		else if (ymax < p->y)
1616 			ymax = p->y;
1617 	}
1618 
1619 	if (l->type == T_ARC_BOX) {
1620 		/* ARC BOX */
1621 		radius = l->radius;		/* radius of the corner */
1622 		/* limit the radius to the smaller of the two sides or
1623 		   postscript crashes; from T.Sato */
1624 		if ((xmax - xmin) / 2 < radius)
1625 			radius = (xmax - xmin) / 2;
1626 		if ((ymax - ymin) / 2 < radius)
1627 			radius = (ymax - ymin) / 2;
1628 		fprintf(tfp, "n %d %d m",xmin+radius, ymin);
1629 		fprintf(tfp, " %d %d %d %d %d arcto 4 {pop} repeat\n",
1630 				xmin, ymin, xmin, ymax-radius, radius);
1631 		/* arc through bl to br */
1632 		fprintf(tfp, "  %d %d %d %d %d arcto 4 {pop} repeat\n",
1633 				xmin, ymax, xmax-radius, ymax, radius);
1634 		/* arc through br to tr */
1635 		fprintf(tfp, "  %d %d %d %d %d arcto 4 {pop} repeat\n",
1636 				xmax, ymax, xmax, ymin+radius, radius);
1637 		/* arc through tr to tl */
1638 		fprintf(tfp, "  %d %d %d %d %d arcto 4 {pop} repeat\n",
1639 				xmax, ymin, xmin+radius, ymin, radius);
1640 	} else if (l->type == T_PIC_BOX) {  /* imported picture */
1641 	  /* PICTURE OBJECT */
1642 		int		dx, dy, rotation;
1643 		int		pllx, plly, purx, pury;
1644 		int		i, j;
1645 		char		buf[12];
1646 		FILE		*picf;
1647 		struct xfig_stream	pic_stream;
1648 
1649 		dx = l->points->next->next->x - l->points->x;
1650 		dy = l->points->next->next->y - l->points->y;
1651 		rotation = 0;
1652 		if (dx < 0 && dy < 0)
1653 			   rotation = 180;
1654 		else if (dx < 0 && dy >= 0)
1655 			   rotation = 90;
1656 		else if (dy < 0 && dx >= 0)
1657 			   rotation = 270;
1658 
1659 		fputs("%\n", tfp);
1660 
1661 		fputs("% pen to black in case this eps object doesn't set "
1662 			"color first\n", tfp);
1663 		if (grayonly)
1664 		    fputs("0 setgray\n", tfp);
1665 		else
1666 		    fputs("0 0 0 setrgbcolor\n", tfp);
1667 
1668 		init_stream(&pic_stream);
1669 
1670 		/* open the file and read a few bytes of the header
1671 		   to see what it is */
1672 		if ((picf = open_stream(l->pic->file, &pic_stream)) == NULL) {
1673 			put_msg("No such picture file: %s", l->pic->file);
1674 			free_stream(&pic_stream);
1675 			return;
1676 		}
1677 
1678 		for (i = 0; i < (int)(sizeof buf); ++i) {
1679 			int	c;
1680 			if ((c = getc(picf)) == EOF)
1681 				break;
1682 			buf[i] = (char)c;
1683 		}
1684 
1685 		/* now find which header it is */
1686 		for (i = 0; i < (int)(sizeof(headers)/sizeof(headers[0])); ++i)
1687 			if (!memcmp(buf, headers[i].bytes,
1688 						strlen(headers[i].bytes)))
1689 				break;
1690 
1691 		if (i == sizeof(headers) / sizeof(headers[0])) {
1692 			/* not found */
1693 			put_msg("%s: Unknown image format", l->pic->file);
1694 			close_stream(&pic_stream);
1695 			free_stream(&pic_stream);
1696 			return;
1697 		}
1698 		/* found */
1699 		/*
1700 		 * readfunc() expects an open file stream, positioned not at the
1701 		 * start of the stream. The stream remains open after returning.
1702 		 */
1703 		if (!headers[i].readfunc(l->pic, &pic_stream, &pllx, &plly)) {
1704 			put_msg("%s: Bad %s format", l->pic->file,
1705 					headers[i].type);
1706 			close_stream(&pic_stream);
1707 			free_stream(&pic_stream);
1708 			return;
1709 		}
1710 
1711 		/* width, height of image bits (unrotated) */
1712 		img_w = l->pic->bit_size.x;
1713 		img_h = l->pic->bit_size.y;
1714 
1715 		/* calc upper-right from size and lower-left */
1716 		/* pllx, plly may not be (0,0) from some image formats */
1717 
1718 		purx = img_w+pllx;
1719 		pury = img_h+plly;
1720 
1721 		fputs("n gs\n", tfp);
1722 
1723 		/* pic_w, pic_h are the width, height of the Fig pic object,
1724 		   possibly rotated */
1725 		if (((rotation == 90 || rotation == 270) && !l->pic->flipped) ||
1726 		    (rotation != 90 && rotation != 270 && l->pic->flipped)) {
1727 			pic_w = pury - plly;
1728 			pic_h = purx - pllx;
1729 		} else {
1730 			pic_w = purx - pllx;
1731 			pic_h = pury - plly;
1732 		}
1733 
1734 		/* translate the pic stuff to the right spot on the page */
1735 		fprintf(tfp, "%d %d tr\n", xmin, ymin);
1736 
1737 		/* scale the pic stuff to fit into the bounding box */
1738 		/* Note: the origin for fig is in the upper-right corner;
1739 		 *	 for postscript its in the lower right hand corner.
1740 		 *	 To fix it, we use a "negative"-y scale factor, then
1741 		 *	 translate the image up on the page
1742 		 */
1743 		fprintf(tfp, "%f %f sc\n", fabs((double)(xmax-xmin)/pic_w),
1744 					-1.0*(double)(ymax-ymin)/pic_h);
1745 
1746 		/* flip the pic stuff.  Always translate it back so that the
1747 		 * lower-left corner is at the origin.
1748 		 * note: fig measures rotation clockwise;
1749 		 * postscript is counter-clockwise
1750 		 */
1751 		switch (rotation) {
1752 		case 0:
1753 			if (l->pic->flipped) {
1754 				fprintf(tfp, "%d 0 tr\n", pic_w);
1755 				fprintf(tfp, "%d rot\n", 270);
1756 				fputs("1 -1 sc\n", tfp);
1757 			} else {
1758 				fprintf(tfp, "0 %d tr\n", -pic_h);
1759 			}
1760 			break;
1761 		case 90:
1762 			if (l->pic->flipped) {
1763 				fprintf(tfp, "%d %d tr\n", pic_w, -pic_h);
1764 				fputs("-1 1 sc\n", tfp);
1765 			} else {
1766 				fprintf(tfp, "%d rot\n", 270);
1767 			}
1768 			break;
1769 		case 180:
1770 			if (l->pic->flipped) {
1771 				fprintf(tfp, "0 %d tr\n", -pic_h);
1772 				fprintf(tfp, "%d rot\n", 270);
1773 				fputs("-1 1 sc\n", tfp);
1774 			} else {
1775 				fprintf(tfp, "%d 0 tr\n", pic_w);
1776 				fprintf(tfp, "%d rot\n", 180);
1777 			}
1778 			break;
1779 		case 270:
1780 			if (l->pic->flipped) {
1781 				fputs("1 -1 sc\n", tfp);
1782 			} else {
1783 				fprintf(tfp, "%d %d tr\n", pic_w, -pic_h);
1784 				fprintf(tfp, "%d rot\n", 90);
1785 			}
1786 			break;
1787 		}
1788 
1789 		/* translate the pic stuff so that the lower-left corner is at
1790 		   the origin */
1791 		fprintf(tfp, "%d %d tr\n", -pllx, -plly);
1792 		/* save vm so pic file won't change anything */
1793 		fputs("sa\n", tfp);
1794 
1795 		/* if PIC object is EPS file, set up clipping rectangle to BB
1796 		 * and prepare to clean up stacks and dicts of included EPS file
1797 		 */
1798 		if (l->pic->subtype == P_EPS) {
1799 			fprintf(tfp,
1800 				"n %d %d m %d %d l %d %d l %d %d l cp clip n\n",
1801 				pllx,plly, purx,plly, purx,pury, pllx,pury);
1802 			fputs("countdictstack\n", tfp);
1803 			fputs("mark\n", tfp);
1804 			/* if user wants grayscale (-N) then redefine
1805 			   setrgbcolor to setgray in imported figure */
1806 			if (grayonly)
1807 				fputs("/setrgbcolor { 0.11 mul exch 0.59 mul "
1808 					  "add exch 0.3 mul add setgray} def\n",
1809 					tfp);
1810 
1811 			/* undefine showpage - and setpagedevice. Some ps-files,
1812 			 * e.g., produced by latex, may be included this way
1813 			 */
1814 			fputs("/showpage {} def\n/setpagedevice {pop} def\n",
1815 					tfp);
1816 		}
1817 
1818 
1819 		/* embed the image */
1820 		if (l->pic->subtype == P_XBM) {
1821 			unsigned char	*bit;
1822 			int		 cwid;
1823 
1824 			fprintf(tfp, "col%d\n ", l->pen_color);
1825 			fputs("% Bitmap image follows:\n", tfp);
1826 			/* scale for size in bits */
1827 			fprintf(tfp, "%d %d sc\n", purx, pury);
1828 			fprintf(tfp, "/pix %d string def\n", (int)((purx+7)/8));
1829 			/* width, height and paint 0 bits */
1830 			fprintf(tfp, "%d %d false\n", purx, pury);
1831 			/* transformation matrix */
1832 			fprintf(tfp, "[%d 0 0 %d 0 %d]\n", purx, -pury, pury);
1833 			/* function for reading bits */
1834 			fputs("{currentfile pix readhexstring pop}\n", tfp);
1835 			/* use imagemask to draw in color */
1836 			fputs("imagemask\n", tfp);
1837 			bit = l->pic->bitmap;
1838 			cwid = 0;
1839 			for (i=0; i<pury; ++i) {	/* for each row */
1840 				/* for each byte */
1841 				for (j=0; j<(int)((purx+7)/8); ++j) {
1842 					fprintf(tfp,"%02x",
1843 						     (unsigned char) ~(*bit++));
1844 					cwid+=2;
1845 					if (cwid >= 80) {
1846 						fputs("\n", tfp);
1847 						cwid=0;
1848 					}
1849 				}
1850 				fputs("\n", tfp);
1851 			}
1852 
1853 		} else if (l->pic->subtype == P_GIF || l->pic->subtype == P_PNG
1854 				|| l->pic->subtype == P_JPEG
1855 				|| l->pic->subtype == P_PCX
1856 				|| l->pic->subtype == P_PPM
1857 				|| l->pic->subtype == P_XPM) {
1858 			if (l->pic->subtype == P_GIF)
1859 				fputs("% GIF", tfp);
1860 			else if (l->pic->subtype == P_PNG)
1861 				fputs("% PNG", tfp);
1862 			else if (l->pic->subtype == P_JPEG)
1863 				fputs("% JPEG", tfp);
1864 			else if (l->pic->subtype == P_PCX)
1865 				fputs("% PCX", tfp);
1866 			else if (l->pic->subtype == P_XPM)
1867 				fputs("% XPM", tfp);
1868 			else
1869 				fputs("% PPM", tfp);
1870 
1871 			fputs(" image follows:\n", tfp);
1872 			/* scale for size in bits */
1873 			fprintf(tfp, "%d %d sc\n", purx, pury);
1874 			if (l->pic->subtype == P_JPEG) {
1875 				/* read and format the jpeg file for PS */
1876 				rewind_stream(&pic_stream);
1877 				JPEGtoPS(pic_stream.fp, tfp);
1878 			} else {
1879 				if (l->pic->numcols > 256)
1880 					write_rgbimage(tfp, l->pic);
1881 				else
1882 					indexed_image(tfp, l->pic);
1883 			}
1884 
1885 		/* EPS file */
1886 		} else if (l->pic->subtype == P_EPS &&
1887 				strcmp(headers[i].type, "PDF")) {
1888 		    fputs("% EPS file follows:\n", tfp);
1889 		    if (!rewind_stream(&pic_stream)) {
1890 			    err_msg("Unable to open EPS file '%s'");
1891 			    fputs("gr\n", tfp);
1892 			    return;
1893 		    }
1894 
1895 		    /* flush buffer first */
1896 		    fflush(tfp);
1897 		    if (strcmp(headers[i].type, "EPSI") == 0) {
1898 			    /* currently, if append_epsi() returns with
1899 			       an error, it did not write anything */
1900 			    if (append_epsi(pic_stream.fp, l->pic->file, tfp))
1901 				    put_msg("Could not embed EPSI file %s.",
1902 						    l->pic->file);
1903 		    } else {
1904 			    size_t	len;
1905 			    char	buffer[BUFSIZ];
1906 
1907 			    while ((len = fread(buffer, 1, sizeof buffer,
1908 							    pic_stream.fp)))
1909 				    fwrite(buffer, 1, len, tfp);
1910 		    }
1911 		} else if (!strcmp(headers[i].type, "PDF")) {
1912 			fputs("% PDF file converted to EPS follows:\n", tfp);
1913 			fflush(tfp);
1914 			pdftops(&pic_stream, tfp);
1915 		}
1916 
1917 		close_stream(&pic_stream);
1918 		free_stream(&pic_stream);
1919 
1920 		/* if PIC object is EPS file, clean up stacks and dicts
1921 		 * before 'restore'ing vm
1922 		 */
1923 		if (l->pic->subtype == P_EPS) {
1924 			fputs("\ncleartomark\n", tfp);
1925 			fputs("countdictstack exch sub { end } repeat\n", tfp);
1926 		}
1927 
1928 		/* restore vm and gsave */
1929 		fputs("rs gr\n", tfp);
1930 		fputs("%\n", tfp);
1931 		fprintf(tfp, "%% End Imported PIC File: %s\n", l->pic->file);
1932 		if (l->pic->subtype == P_EPS)
1933 			fputs("%%EndDocument\n", tfp);
1934 		fputs("%\n", tfp);
1935 	} else {
1936 		/* POLYLINE */
1937 		p = l->points;
1938 		q = p->next;
1939 		/* first point */
1940 		fpntx1 = p->x;
1941 		fpnty1 = p->y;
1942 		/* second point */
1943 		fpntx2 = q->x;
1944 		fpnty2 = q->y;
1945 		/* go through the points to get the last two */
1946 		while (q->next != NULL) {
1947 			p = q;
1948 			q = q->next;
1949 		}
1950 		/* next to last point */
1951 		lpntx2 = p->x;
1952 		lpnty2 = p->y;
1953 		/* last point */
1954 		lpntx1 = q->x;
1955 		lpnty1 = q->y;
1956 		/* set clipping for any arrowheads */
1957 		if (NEEDS_CLIPPING(l)) {
1958 			fprintf(tfp, "gs ");
1959 			clip_arrows(l, OBJ_POLYLINE);
1960 		}
1961 
1962 		/* now output the points */
1963 		p = l->points;
1964 		q = p->next;
1965 		fprintf(tfp, "n %d %d m", p->x, p->y);
1966 		i=1;
1967 		while (q->next != NULL) {
1968 			p = q;
1969 			q = q->next;
1970 			fprintf(tfp, " %d %d l", p->x, p->y);
1971 			if (i%5 == 0)
1972 				fputs("\n", tfp);
1973 			++i;
1974 		}
1975 		fputs("\n", tfp);
1976 	}
1977 
1978 	/* now fill it, draw the line and/or draw arrow heads */
1979 	if (l->type != T_PIC_BOX) {  /* make sure it isn't a picture object */
1980 		if (l->type == T_POLYLINE) {
1981 			fprintf(tfp, " %d %d l ", q->x, q->y);
1982 			if (fpntx1==lpntx1 && fpnty1==lpnty1)
1983 				fputs(" cp ", tfp);
1984 			/* endpoints are coincident, close path
1985 			   so that line join is used */
1986 		} else {
1987 			fputs(" cp ", tfp);	/* polygon, close path */
1988 		}
1989 		/* fill it if there is a fill style */
1990 		if (l->fill_style != UNFILLED)
1991 			fill_area(l->fill_style, l->pen_color, l->fill_color);
1992 		/* stroke if there is a line thickness */
1993 		if (l->thickness > 0)
1994 		     fprintf(tfp, "gs col%d s gr ", l->pen_color);
1995 
1996 		/* reset clipping */
1997 		if (l->type == T_POLYLINE && NEEDS_CLIPPING(l))
1998 			fputs("gr\n", tfp);
1999 		reset_style(l->style, l->style_val);
2000 
2001 		if (l->back_arrow && l->thickness > 0)
2002 			draw_arrow(l->back_arrow, bpoints, nbpoints,
2003 					bfillpoints, nbfillpoints,l->pen_color);
2004 		if (l->for_arrow && l->thickness > 0)
2005 			draw_arrow(l->for_arrow, fpoints, nfpoints, ffillpoints,
2006 					nffillpoints, l->pen_color);
2007 	}
2008 	if (multi_page)
2009 		fputs("} bind def\n", tfp);
2010 }
2011 
2012 void
genps_spline(F_spline * s)2013 genps_spline(F_spline *s)
2014 {
2015 	do_split(s->depth);
2016 
2017 	if (multi_page)
2018 		fprintf(tfp, "/o%d {", no_obj++);
2019 
2020 	/* print any comments prefixed with "%" */
2021 	print_comments("% ",s->comments, "");
2022 
2023 	if (closed_spline(s)) {
2024 		if (s->style == DOTTED_LINE)
2025 			set_linecap(1);		/* round dots for dotted line */
2026 	} else {		/* open splines can explicitely set capstyle */
2027 		set_linecap(s->cap_style);
2028 	}
2029 	/* set the line thickness */
2030 	set_linewidth((double)s->thickness);
2031 	if (int_spline(s))
2032 		genps_itp_spline(s);
2033 	else
2034 		genps_ctl_spline(s);
2035 	if (multi_page)
2036 		fputs("} bind def\n", tfp);
2037 }
2038 
2039 static void
genps_itp_spline(F_spline * s)2040 genps_itp_spline(F_spline *s)
2041 {
2042 	F_point		*p, *q;
2043 	F_control	*a, *b;
2044 	int		 xmin, ymin;
2045 
2046 	fprintf(tfp, "%% Interp Spline\n");
2047 
2048 	a = s->controls;
2049 	p = s->points;
2050 	/* first point */
2051 	fpntx1 = p->x;
2052 	fpnty1 = p->y;
2053 	/* second point */
2054 	fpntx2 = round(a->rx);
2055 	fpnty2 = round(a->ry);
2056 	/* go through the points to find the last two */
2057 	for (q = p->next; q != NULL; p = q, q = q->next) {
2058 		b = a->next;
2059 		a = b;
2060 	}
2061 	/* next to last point */
2062 	lpntx2 = round(b->lx);
2063 	lpnty2 = round(b->ly);
2064 	/* last point */
2065 	lpntx1 = p->x;
2066 	lpnty1 = p->y;
2067 	/* set clipping for any arrowheads */
2068 	if (NEEDS_CLIPPING(s)) {
2069 		fprintf(tfp, "gs ");
2070 		clip_arrows((F_line *)s, OBJ_SPLINE);
2071 	}
2072 
2073 	a = s->controls;
2074 	p = s->points;
2075 	set_style(s->style, s->style_val);
2076 	fprintf(tfp, "n %d %d m\n", p->x, p->y);
2077 	xmin = 999999;
2078 	ymin = 999999;
2079 	for (q = p->next; q != NULL; p = q, q = q->next) {
2080 		xmin = min(xmin, p->x);
2081 		ymin = min(ymin, p->y);
2082 		b = a->next;
2083 		fprintf(tfp, "\t%.1f %.1f %.1f %.1f %d %d curveto\n",
2084 				a->rx, a->ry, b->lx, b->ly, q->x, q->y);
2085 		a = b;
2086 	}
2087 	if (closed_spline(s)) fprintf(tfp, " cp ");
2088 	if (s->fill_style != UNFILLED)
2089 		fill_area(s->fill_style, s->pen_color, s->fill_color);
2090 	if (s->thickness > 0) {
2091 		fprintf(tfp, " gs col%d s gr\n", s->pen_color);
2092 		/* reset clipping */
2093 		if (NEEDS_CLIPPING(s))
2094 			fprintf(tfp," gr\n");
2095 	}
2096 	reset_style(s->style, s->style_val);
2097 
2098 	/* draw arrowheads after spline for open arrow */
2099 	if (s->back_arrow && s->thickness > 0)
2100 		draw_arrow(s->back_arrow, bpoints, nbpoints,
2101 				bfillpoints, nbfillpoints, s->pen_color);
2102 
2103 	if (s->for_arrow && s->thickness > 0)
2104 		draw_arrow(s->for_arrow, fpoints, nfpoints,
2105 				ffillpoints, nffillpoints, s->pen_color);
2106 }
2107 
2108 static void
genps_ctl_spline(F_spline * s)2109 genps_ctl_spline(F_spline *s)
2110 {
2111 	double		a, b, c, d, x1, y1, x2, y2, x3, y3;
2112 	F_point		*p, *q;
2113 	int		xmin, ymin;
2114 
2115 	if (closed_spline(s))
2116 		fprintf(tfp, "%% Closed spline\n");
2117 	else
2118 		fprintf(tfp, "%% Open spline\n");
2119 
2120 	p = s->points;
2121 	x1 = p->x;
2122 	y1 = p->y;
2123 	p = p->next;
2124 	c = p->x;
2125 	d = p->y;
2126 	x3 = a = (x1 + c) / 2;
2127 	y3 = b = (y1 + d) / 2;
2128 
2129 	/* first point */
2130 	fpntx1 = round(x1);
2131 	fpnty1 = round(y1);
2132 	/* second point */
2133 	fpntx2 = round(x3);
2134 	fpnty2 = round(y3);
2135 
2136 	/* in case there are only two points in this spline */
2137 	x2=x1; y2=y1;
2138 	/* go through the points to find the last two */
2139 	for (q = p->next; q != NULL; p = q, q = q->next) {
2140 		x1 = x3;
2141 		y1 = y3;
2142 		x2 = c;
2143 		y2 = d;
2144 		c = q->x;
2145 		d = q->y;
2146 		x3 = (x2 + c) / 2;
2147 		y3 = (y2 + d) / 2;
2148 	}
2149 	/* next to last point */
2150 	lpntx2 = round(x2);
2151 	lpnty2 = round(y2);
2152 	/* last point */
2153 	lpntx1 = round(c);
2154 	lpnty1 = round(d);
2155 	/* set clipping for any arrowheads */
2156 	if (NEEDS_CLIPPING(s)) {
2157 		fprintf(tfp, "gs ");
2158 		clip_arrows((F_line *)s, OBJ_SPLINE);
2159 	}
2160 
2161 	/* now output the points */
2162 	set_style(s->style, s->style_val);
2163 	xmin = 999999;
2164 	ymin = 999999;
2165 
2166 	p = s->points;
2167 	x1 = p->x;
2168 	y1 = p->y;
2169 	p = p->next;
2170 	c = p->x;
2171 	d = p->y;
2172 	x3 = a = (x1 + c) / 2;
2173 	y3 = b = (y1 + d) / 2;
2174 	/* in case there are only two points in this spline */
2175 	x2=x1; y2=y1;
2176 	if (closed_spline(s))
2177 		fprintf(tfp, "n %.1f %.1f m\n", a, b);
2178 	else
2179 		fprintf(tfp, "n %.1f %.1f m %.1f %.1f l\n", x1, y1, x3, y3);
2180 
2181 	for (q = p->next; q != NULL; p = q, q = q->next) {
2182 		xmin = min(xmin, p->x);
2183 		ymin = min(ymin, p->y);
2184 		x1 = x3;
2185 		y1 = y3;
2186 		x2 = c;
2187 		y2 = d;
2188 		c = q->x;
2189 		d = q->y;
2190 		x3 = (x2 + c) / 2;
2191 		y3 = (y2 + d) / 2;
2192 		fprintf(tfp,
2193 			  "\t%.1f %.1f %.1f %.1f %.1f %.1f DrawSplineSection\n",
2194 				x1, y1, x2, y2, x3, y3);
2195 	}
2196 	/*
2197 	 * At this point, (x2,y2) and (c,d) are the position of the
2198 	 * next-to-last and last point respectively, in the point list
2199 	 */
2200 	if (closed_spline(s)) {
2201 		fprintf(tfp, "\t%.1f %.1f %.1f %.1f %.1f %.1f DrawSplineSection"
2202 				" closepath ", x3, y3, c, d, a, b);
2203 	} else {
2204 		fprintf(tfp, "\t%.1f %.1f l ", c, d);
2205 	}
2206 	if (s->fill_style != UNFILLED)
2207 		fill_area(s->fill_style, s->pen_color, s->fill_color);
2208 	if (s->thickness > 0) {
2209 		fprintf(tfp, " gs col%d s gr\n", s->pen_color);
2210 		/* reset clipping */
2211 		if (NEEDS_CLIPPING(s))
2212 			fprintf(tfp," gr\n");
2213 	}
2214 	reset_style(s->style, s->style_val);
2215 
2216 	/* draw arrowheads after spline */
2217 	if (s->back_arrow && s->thickness > 0)
2218 		draw_arrow(s->back_arrow, bpoints, nbpoints,
2219 				bfillpoints, nbfillpoints, s->pen_color);
2220 	if (s->for_arrow && s->thickness > 0)
2221 		draw_arrow(s->for_arrow, fpoints, nfpoints,
2222 				ffillpoints, nffillpoints, s->pen_color);
2223 }
2224 
2225 void
genps_arc(F_arc * a)2226 genps_arc(F_arc *a)
2227 {
2228 	double		angle1, angle2, dx, dy, radius;
2229 	double		cx, cy, sx, sy, ex, ey;
2230 	int		direction;
2231 
2232 	do_split(a->depth);
2233 
2234 	/* print any comments prefixed with "%" */
2235 	print_comments("% ",a->comments, "");
2236 
2237 	fprintf(tfp, "%% Arc\n");
2238 
2239 	if (multi_page)
2240 		fprintf(tfp, "/o%d {", no_obj++);
2241 
2242 	cx = a->center.x; cy = a->center.y;
2243 	sx = a->point[0].x; sy = a->point[0].y;
2244 	ex = a->point[2].x; ey = a->point[2].y;
2245 
2246 	direction = a->direction;
2247 	set_linewidth((double)a->thickness);
2248 	set_linecap(a->cap_style);
2249 	dx = cx - sx;
2250 	dy = cy - sy;
2251 	radius = sqrt(dx*dx+dy*dy);
2252 	if (cx==sx)
2253 		angle1 = (sy-cy > 0? 90.0: -90.0);
2254 	else
2255 		angle1 = atan2(sy-cy, sx-cx) * 180.0 / M_PI;
2256 	if (cx==ex)
2257 		angle2 = (ey-cy > 0? 90.0: -90.0);
2258 	else
2259 		angle2 = atan2(ey-cy, ex-cx) * 180.0 / M_PI;
2260 
2261 	/* workaround for arcs with start point = end point;
2262 	   make angles slightly different */
2263 	if (fabs(angle1 - angle2) < 0.001)
2264 		angle2 = angle1 + 0.01;
2265 
2266 	if (a->type == T_OPEN_ARC && NEEDS_CLIPPING(a)) {
2267 		/* set clipping for any arrowheads */
2268 		fprintf(tfp, "gs ");
2269 		if (a->for_arrow || a->back_arrow)
2270 			clip_arrows((F_line *)a, OBJ_ARC);
2271 	}
2272 
2273 	set_style(a->style, a->style_val);
2274 
2275 	/* draw the arc now */
2276 	/* direction = 1 -> Counterclockwise */
2277 	fprintf(tfp, "n %.1f %.1f %.1f %.4f %.4f %s\n",
2278 			cx, cy, radius, angle1, angle2,
2279 			((direction == 1) ? "arcn" : "arc"));
2280 
2281 	if (a->type == T_PIE_WEDGE_ARC)
2282 		fprintf(tfp,"%.1f %.1f l %.1f %.1f l ",cx,cy,sx,sy);
2283 
2284 	/******	The upper-left values (dx, dy) aren't really correct so	  *****/
2285 	/******	the fill pattern alignment between a filled arc and other *****/
2286 	/******	filled objects will not be correct			  *****/
2287 	if (a->fill_style != UNFILLED)
2288 		fill_area(a->fill_style, a->pen_color, a->fill_color);
2289 	if (a->thickness > 0)
2290 		fprintf(tfp, "gs col%d s gr\n", a->pen_color);
2291 
2292 	if (a->type == T_OPEN_ARC && NEEDS_CLIPPING(a)) {
2293 		/* reset clipping */
2294 		fprintf(tfp," gr\n");
2295 	}
2296 	reset_style(a->style, a->style_val);
2297 
2298 	/* now draw the arrowheads, if any */
2299 	if (a->type == T_OPEN_ARC) {
2300 		if (a->back_arrow && a->thickness > 0)
2301 			draw_arrow(a->back_arrow, bpoints, nbpoints,
2302 					bfillpoints, nbfillpoints,a->pen_color);
2303 		if (a->for_arrow && a->thickness > 0)
2304 			draw_arrow(a->for_arrow, fpoints, nfpoints,
2305 					ffillpoints, nffillpoints,a->pen_color);
2306 	}
2307 	if (multi_page)
2308 		fprintf(tfp, "} bind def\n");
2309 }
2310 
2311 void
genps_ellipse(F_ellipse * e)2312 genps_ellipse(F_ellipse *e)
2313 {
2314 	do_split(e->depth);
2315 
2316 	fprintf(tfp, "%% Ellipse\n");
2317 
2318 	/* print any comments prefixed with "%" */
2319 	print_comments("% ",e->comments, "");
2320 
2321 	if (multi_page)
2322 		fprintf(tfp, "/o%d {", no_obj++);
2323 
2324 	set_linewidth((double)e->thickness);
2325 	set_style(e->style, e->style_val);
2326 	if (e->style == DOTTED_LINE)
2327 		set_linecap(1);	/* round dots */
2328 	else
2329 		set_linecap(0);
2330 	if (e->angle == 0) {
2331 		fprintf(tfp, "n %d %d %d %d 0 360 DrawEllipse ",
2332 				e->center.x, e->center.y, e->radiuses.x,
2333 				e->radiuses.y);
2334 	} else {
2335 		fprintf(tfp, "gs\n");
2336 		fprintf(tfp, "%d %d tr\n",e->center.x, e->center.y);
2337 		fprintf(tfp, "%6.3f rot\n",-e->angle*180.0/M_PI);
2338 		fprintf(tfp, "n 0 0 %d %d 0 360 DrawEllipse ",
2339 				e->radiuses.x, e->radiuses.y);
2340 		/* rotate back so any fill pattern will come out correct */
2341 		fprintf(tfp, "%6.3f rot\n",e->angle*180.0/M_PI);
2342 	}
2343 	if (e->fill_style != UNFILLED)
2344 		fill_area(e->fill_style, e->pen_color, e->fill_color);
2345 	if (e->thickness > 0)
2346 		fprintf(tfp, "gs col%d s gr\n", e->pen_color);
2347 	if (e->angle != 0)
2348 		fprintf(tfp, "gr\n");
2349 	reset_style(e->style, e->style_val);
2350 	if (multi_page)
2351 		fprintf(tfp, "} bind def\n");
2352 }
2353 
2354 
2355 #define	TEXT_PS		"\
2356 /%s%s ff %.2f scf sf\n\
2357 "
2358 void
genps_text(F_text * t)2359 genps_text(F_text *t)
2360 {
2361 	unsigned char		*cp;
2362 #ifdef I18N
2363 #define LINE_LENGTH_LIMIT 200
2364 	bool composite = false;
2365 	bool state_gr = false;
2366 	int chars = 0;
2367 	int gr_chars = 0;
2368 	unsigned char ch;
2369 #endif /* I18N */
2370 
2371 	do_split(t->depth);
2372 
2373 	/* ignore hidden text (new for xfig3.2.3/fig2dev3.2.3) */
2374 	if (hidden_text(t))
2375 		return;
2376 
2377 	if (multi_page)
2378 		fprintf(tfp, "/o%d {", no_obj++);
2379 
2380 	/* print any comments prefixed with "%" */
2381 	print_comments("% ",t->comments, "");
2382 
2383 #ifdef I18N
2384 	if (enable_composite_font && ((t->flags & PSFONT_TEXT) ?
2385 				(t->font <= 0 || t->font == 2) :
2386 				(t->font <= 2))) {
2387 		composite = true;
2388 		if (t->font <= 0)
2389 			fprintf(tfp, TEXT_PS, "CompositeRoman", "",
2390 					PSFONTMAG(t));
2391 		else
2392 			fprintf(tfp, TEXT_PS, "CompositeBold", "",PSFONTMAG(t));
2393 	} else
2394 #endif /* I18N */
2395 		if (PSisomap[t->font+1] == true)
2396 			fprintf(tfp, TEXT_PS, PSFONT(t), "-iso", PSFONTMAG(t));
2397 		else
2398 			fprintf(tfp, TEXT_PS, PSFONT(t), "", PSFONTMAG(t));
2399 
2400 	fprintf(tfp, "%d %d m\ngs ", t->base_x,  t->base_y);
2401 	fprintf(tfp, "1 -1 sc ");
2402 
2403 	if (t->angle != 0.0)
2404 		fprintf(tfp, " %.1f rot ", t->angle*180.0/M_PI);
2405 	/* this loop escapes characters '(', ')', and '\' */
2406 	fputc('(', tfp);
2407 #ifdef I18N
2408 	for(cp = (unsigned char *)t->cstring; *cp; cp++) {
2409 		if (LINE_LENGTH_LIMIT < chars) {
2410 			fputs("\\\n", tfp);
2411 			chars = 0;
2412 		}
2413 		ch = *cp;
2414 		if (enable_composite_font && composite) {
2415 			if (ch & 0x80) {	/* GR */
2416 				if (!state_gr) {
2417 					fprintf(tfp, "\\377\\001");
2418 					chars += 8;
2419 					state_gr = true;
2420 					gr_chars = 0;
2421 				}
2422 				gr_chars++;
2423 			} else {	/* GL */
2424 				if (state_gr) {
2425 					if (gr_chars % 2) {
2426 						put_msg("warning: incomplete "
2427 							"multi-byte text: %s",
2428 								t->cstring);
2429 						fputc('?', tfp);
2430 					}
2431 					fprintf(tfp, "\\377\\000");
2432 					chars += 8;
2433 					state_gr = false;
2434 				}
2435 			}
2436 		}
2437 		if (strchr("()\\", ch)) {
2438 			fputc('\\', tfp);
2439 			chars += 1;
2440 		}
2441 		if (ch>=0x80) {
2442 			fprintf(tfp,"\\%o", ch);
2443 			chars += 4;
2444 		} else {
2445 			fputc(ch, tfp);
2446 			chars += 1;
2447 		}
2448 	}
2449 	if (enable_composite_font && composite && state_gr) {
2450 		if (gr_chars % 2) {
2451 			put_msg("warning: incomplete multi-byte text: %s",
2452 					t->cstring);
2453 			fputc('?', tfp);
2454 		}
2455 	}
2456 #else
2457 	for(cp = (unsigned char *)t->cstring; *cp; cp++) {
2458 		if (strchr("()\\", *cp))
2459 			fputc('\\', tfp);
2460 		if (*cp>=0x80)
2461 			fprintf(tfp,"\\%o", *cp);
2462 		else
2463 			fputc(*cp, tfp);
2464 	}
2465 #endif /* I18N */
2466 	fputc(')', tfp);
2467 
2468 	if ((t->type == T_CENTER_JUSTIFIED) || (t->type == T_RIGHT_JUSTIFIED)){
2469 
2470 		fprintf(tfp, " dup sw pop ");
2471 		if (t->type == T_CENTER_JUSTIFIED) fprintf(tfp, "2 div ");
2472 		fprintf(tfp, "neg 0 rm ");
2473 	}
2474 
2475 	else if ((t->type != T_LEFT_JUSTIFIED) && (t->type != DEFAULT))
2476 		fprintf(stderr, "Text incorrectly positioned\n");
2477 
2478 	fprintf(tfp, " col%d sh gr\n", t->color);
2479 
2480 	if (multi_page)
2481 		fprintf(tfp, "} bind def\n");
2482 }
2483 
2484 /* draw arrow from the points array */
2485 
2486 static void
draw_arrow(F_arrow * arrow,F_pos * points,int npoints,F_pos * fillpoints,int nfillpoints,int col)2487 draw_arrow(F_arrow *arrow, F_pos *points, int npoints,
2488 		F_pos *fillpoints, int nfillpoints, int col)
2489 {
2490 	int i, type;
2491 
2492 	fprintf(tfp,"%% arrowhead\n");
2493 	set_linecap(0);			/* butt line cap for arrowheads */
2494 	set_linejoin(0);		/* miter join for sharp points */
2495 	set_linewidth(arrow->thickness);
2496 	fprintf(tfp, "n ");
2497 	for (i=0; i<npoints; i++) {
2498 		fprintf(tfp, "%d %d ",points[i].x,points[i].y);
2499 		if (i==0)
2500 			fprintf(tfp, "m ");
2501 		else
2502 			fprintf(tfp, "l ");
2503 		if ((i+1)%5 == 0)
2504 			fprintf(tfp,"\n");
2505 	}
2506 
2507 	type = arrow->type;
2508 	if (type != 0 && type != 6 && type < 13) /* old heads, close the path */
2509 		fprintf(tfp, " cp ");
2510 	if (type == 0) {
2511 		/* stroke */
2512 		fprintf(tfp, " col%d s\n",col);
2513 	} else {
2514 		if (arrow->style == 0 && nfillpoints == 0) {
2515 			/* hollow, fill with white */
2516 			fill_area(NUMSHADES-1, WHITE_COLOR, WHITE_COLOR);
2517 			/* stroke */
2518 			fprintf(tfp, " col%d s\n",col);
2519 		} else {
2520 			if (nfillpoints == 0) {
2521 				if (type < 13) {
2522 					if (arrow->style == 0)
2523 						/* fill with white */
2524 						fill_area(NUMSHADES-1,
2525 								WHITE_COLOR,
2526 								WHITE_COLOR);
2527 					else
2528 						/* fill with color */
2529 						fill_area(NUMSHADES-1, col,col);
2530 				}
2531 				/* stroke */
2532 				fprintf(tfp, " col%d s\n",col);
2533 			} else {
2534 				/* special fill, first fill whole head
2535 				   with white */
2536 				fill_area(NUMSHADES-1, WHITE_COLOR,WHITE_COLOR);
2537 				/* stroke */
2538 				fprintf(tfp, " col%d s\n",col);
2539 				/* now describe the special fill area */
2540 				fprintf(tfp, "n ");
2541 				for (i=0; i<nfillpoints; i++) {
2542 					fprintf(tfp, "%d %d ", fillpoints[i].x,
2543 							fillpoints[i].y);
2544 					if (i==0)
2545 						fprintf(tfp, "m ");
2546 					else
2547 						fprintf(tfp, "l ");
2548 					if ((i+1)%5 == 0)
2549 						fprintf(tfp,"\n");
2550 				}
2551 				/* then fill special fill area */
2552 				fill_area(NUMSHADES-1, col, col);
2553 			}
2554 		}
2555 	}
2556 }
2557 
2558 /****************************************************************
2559 
2560  clip_arrows - calculate a clipping region which is the current
2561 	clipping area minus the polygons at the arrowheads.
2562 
2563  This will prevent the object (line, spline etc.) from protruding
2564  on either side of the arrowhead Also calculate the arrowheads
2565  themselves and put the polygons in fpoints[nfpoints] for forward
2566  arrow and bpoints[nbpoints] for backward arrow.
2567  The calling routine should first do a "gs" (graphics state save)
2568  so that it can restore the original clip area later.
2569 
2570 ****************************************************************/
2571 
2572 static void
clip_arrows(F_line * obj,int objtype)2573 clip_arrows(F_line *obj, int objtype)
2574 {
2575 	int		    i;
2576 
2577 	/* get current clip area */
2578 	fprintf(tfp," clippath\n");
2579 	/* get points for any forward arrowhead */
2580 	if (obj->for_arrow) {
2581 		if (objtype == OBJ_ARC) {
2582 			F_arc  *a = (F_arc *) obj;
2583 			/* last point */
2584 			lpntx1 = a->point[2].x;
2585 			lpnty1 = a->point[2].y;
2586 			compute_arcarrow_angle(a->center.x, a->center.y,
2587 					(double)lpntx1, (double)lpnty1,
2588 					a->direction, a->for_arrow,
2589 					&lpntx2, &lpnty2);
2590 		}
2591 		calc_arrow(lpntx2, lpnty2, lpntx1, lpnty1, obj->thickness,
2592 				obj->for_arrow, fpoints, &nfpoints, ffillpoints,
2593 				&nffillpoints, clippoints, &nclippoints);
2594 		/* set the clipping area */
2595 		for (i=nclippoints-1; i>=0; i--) {
2596 			fprintf(tfp,"%d %d %c ", clippoints[i].x,
2597 					clippoints[i].y,
2598 					i == nclippoints - 1 ? 'm' : 'l');
2599 		}
2600 		fprintf(tfp, "cp\n");
2601 	}
2602 
2603 	/* get points for any backward arrowhead */
2604 	if (obj->back_arrow) {
2605 		if (objtype == OBJ_ARC) {
2606 			F_arc  *a = (F_arc *) obj;
2607 			/* first point */
2608 			fpntx1 = a->point[0].x;
2609 			fpnty1 = a->point[0].y;
2610 			compute_arcarrow_angle(a->center.x, a->center.y,
2611 					(double)fpntx1, (double)fpnty1,
2612 					a->direction ^ 1, a->back_arrow,
2613 					&fpntx2, &fpnty2);
2614 		}
2615 		calc_arrow(fpntx2, fpnty2, fpntx1, fpnty1, obj->thickness,
2616 				obj->back_arrow, bpoints, &nbpoints,bfillpoints,
2617 				&nbfillpoints, clippoints, &nclippoints);
2618 		/* set the clipping area */
2619 		for (i=nclippoints-1; i>=0; i--) {
2620 			fprintf(tfp,"%d %d %c ",clippoints[i].x,clippoints[i].y,
2621 					i == nclippoints - 1 ? 'm' : 'l');
2622 		}
2623 		fprintf(tfp, "cp\n");
2624 	}
2625 	/* intersect the arrowhead clip path(s) with current clip path */
2626 	/* use eoclip so that the intersection with the current path
2627 	   guarantees the correct clip path */
2628 	fprintf(tfp, "eoclip\n");
2629 }
2630 
2631 /* uses eofill (even/odd rule fill) */
2632 /* ulx and uly define the upper-left corner of the object for pattern alignment */
2633 
2634 static void
fill_area(int fill,int pen_color,int fill_color)2635 fill_area(int fill, int pen_color, int fill_color)
2636 {
2637 	double pen_r, pen_g, pen_b, fill_r, fill_g, fill_b;
2638 
2639 	/* get the rgb values for the fill pattern (if necessary) */
2640 	if (fill_color < NUM_STD_COLS) {
2641 		fill_r=rgbcols[fill_color>0? fill_color: 0].r;
2642 		fill_g=rgbcols[fill_color>0? fill_color: 0].g;
2643 		fill_b=rgbcols[fill_color>0? fill_color: 0].b;
2644 	} else {
2645 		fill_r=user_colors[fill_color-NUM_STD_COLS].r/255.0;
2646 		fill_g=user_colors[fill_color-NUM_STD_COLS].g/255.0;
2647 		fill_b=user_colors[fill_color-NUM_STD_COLS].b/255.0;
2648 	}
2649 	if (pen_color < NUM_STD_COLS) {
2650 		pen_r=rgbcols[pen_color>0? pen_color: 0].r;
2651 		pen_g=rgbcols[pen_color>0? pen_color: 0].g;
2652 		pen_b=rgbcols[pen_color>0? pen_color: 0].b;
2653 	} else {
2654 		pen_r=user_colors[pen_color-NUM_STD_COLS].r/255.0;
2655 		pen_g=user_colors[pen_color-NUM_STD_COLS].g/255.0;
2656 		pen_b=user_colors[pen_color-NUM_STD_COLS].b/255.0;
2657 	}
2658 
2659 	if (fill_color <= 0 && fill < NUMSHADES+NUMTINTS)
2660 		/* use gray levels for default and black shades and tints */
2661 		fprintf(tfp, "gs %.2f setgray ef gr ", 1.0 - SHADEVAL(fill));
2662 
2663 	else if (fill < NUMSHADES)
2664 		/* a shaded color (not black) */
2665 		fprintf(tfp, "gs col%d %.2f shd ef gr ", fill_color,
2666 				SHADEVAL(fill));
2667 
2668 	else if (fill < NUMSHADES+NUMTINTS)
2669 		/* a tint */
2670 		fprintf(tfp, "gs col%d %.2f tnt ef gr ", fill_color,
2671 				TINTVAL(fill));
2672 
2673 	else {
2674 		/* one of the patterns */
2675 		int patnum = fill-NUMSHADES-NUMTINTS+1;
2676 		char colorspace[13], pencolor[25], fillcolor[25];
2677 
2678 		if (grayonly) {
2679 			double grayfill, graypen;
2680 			grayfill = rgb2luminance(fill_r, fill_g, fill_b);
2681 			graypen  = rgb2luminance(pen_r, pen_g, pen_b);
2682 			sprintf(colorspace, "/DeviceGray");
2683 			sprintf(fillcolor, "%.2f", grayfill);
2684 			sprintf(pencolor, "%.2f", graypen);
2685 		} else {
2686 			sprintf(colorspace, "/DeviceRGB");
2687 			sprintf(fillcolor, "%.2f %.2f %.2f",
2688 					fill_r, fill_g, fill_b);
2689 			sprintf(pencolor, "%.2f %.2f %.2f",pen_r, pen_g, pen_b);
2690 		}
2691 
2692 		fprintf(tfp, "\n%% Fill with pattern background color\n");
2693 		fprintf(tfp, "gs %s setcolorspace %s setcolor fill gr\n",
2694 				colorspace, fillcolor);
2695 		fprintf(tfp, "\n%% Fill with pattern pen color\n");
2696 		fprintf(tfp,"gs %s setcolorspace %s P%d setpattern fill gr\n\n",
2697 				colorspace, pencolor, patnum);
2698 	}
2699 }
2700 
2701 /* define standard colors as "col##" where ## is the number */
2702 static void
genps_std_colors(void)2703 genps_std_colors(void)
2704 {
2705 	int i;
2706 	for (i=0; i<NUM_STD_COLS; i++) {
2707 		/* hollow arrows are filled with white */
2708 		if (i == WHITE_COLOR || std_color_used[i]) {
2709 			if (grayonly)
2710 				fprintf(tfp, "/col%d {%.3f setgray} bind def\n",
2711 						i, rgb2luminance(rgbcols[i].r,
2712 								rgbcols[i].g,
2713 								rgbcols[i].b));
2714 			else
2715 				fprintf(tfp, "/col%d {%.3f %.3f %.3f srgb} "
2716 						"bind def\n", i, rgbcols[i].r,
2717 						rgbcols[i].g, rgbcols[i].b);
2718 		}
2719 	}
2720 }
2721 
2722 /* define user colors as "col##" where ## is the number */
2723 static void
genps_usr_colors(void)2724 genps_usr_colors(void)
2725 {
2726 	int i;
2727 	for (i=0; i<num_usr_cols; i++) {
2728 		if (grayonly)
2729 			fprintf(tfp, "/col%d {%.3f setgray} bind def\n",
2730 					i+NUM_STD_COLS,
2731 					rgb2luminance(user_colors[i].r/255.0,
2732 						user_colors[i].g/255.0,
2733 						user_colors[i].b/255.0));
2734 		else
2735 			fprintf(tfp, "/col%d {%.3f %.3f %.3f srgb} bind def\n",
2736 					i+NUM_STD_COLS, user_colors[i].r/255.0,
2737 					user_colors[i].g/255.0,
2738 					user_colors[i].b/255.0);
2739 	}
2740 }
2741 
2742 static bool
iso_text_exist(F_compound * ob)2743 iso_text_exist(F_compound *ob)
2744 {
2745 	F_compound	*c;
2746 	F_text	    *t;
2747 	unsigned char   *s;
2748 
2749 	if (ob->texts != NULL) {
2750 		for (t = ob->texts; t != NULL; t = t->next) {
2751 			/* look for any ISO (non-ASCII) chars in
2752 			   non-special text except for pstex */
2753 			if (!strcmp(lang,"pstex") && special_text(t))
2754 				continue;
2755 			for (s = (unsigned char*)t->cstring; *s != '\0'; s++) {
2756 				/* look for characters >= 128 or ASCII '-' */
2757 				if ((*s>127) || (*s=='-'))
2758 					return true;
2759 			}
2760 		}
2761 	}
2762 
2763 	for (c = ob->compounds; c != NULL; c = c->next) {
2764 		if (iso_text_exist(c))
2765 			return true;
2766 	}
2767 	return false;
2768 }
2769 
2770 static void
encode_all_fonts(F_compound * ob)2771 encode_all_fonts(F_compound *ob)
2772 {
2773 	F_compound *c;
2774 	F_text     *t;
2775 
2776 	if (ob->texts != NULL) {
2777 		for (t = ob->texts; t != NULL; t = t->next)
2778 			if (PSisomap[t->font+1] == false) {
2779 				fprintf(tfp, "/%s /%s-iso isovec ReEncode\n",
2780 						PSFONT(t), PSFONT(t));
2781 				PSisomap[t->font+1] = true;
2782 			}
2783 	}
2784 
2785 	for (c = ob->compounds; c != NULL; c = c->next) {
2786 		encode_all_fonts(c);
2787 	}
2788 }
2789 
2790 static bool
ellipse_exist(F_compound * ob)2791 ellipse_exist(F_compound *ob)
2792 {
2793 	F_compound	*c;
2794 
2795 	if (NULL != ob->ellipses)
2796 		return true;
2797 
2798 	for (c = ob->compounds; c != NULL; c = c->next) {
2799 		if (ellipse_exist(c))
2800 			return true;
2801 	}
2802 
2803 	return false;
2804 }
2805 
2806 static bool
approx_spline_exist(F_compound * ob)2807 approx_spline_exist(F_compound *ob)
2808 {
2809 	F_spline	*s;
2810 	F_compound	*c;
2811 
2812 	for (s = ob->splines; s != NULL; s = s->next) {
2813 		if (approx_spline(s))
2814 			return true;
2815 	}
2816 
2817 	for (c = ob->compounds; c != NULL; c = c->next) {
2818 		if (approx_spline_exist(c))
2819 			return true;
2820 	}
2821 
2822 	return false;
2823 }
2824 
2825 /*
2826  * We must start new figure if the current
2827  * depth and the last_depth differ by more than one.
2828  * Depths will be seen with decreasing values.
2829  * Only comments will be output */
2830 static void
do_split(int actual_depth)2831 do_split(int actual_depth)
2832 {
2833 	if (actual_depth+1 < last_depth) {
2834 		/* depths differ by more than one */
2835 		if (fig_number > 0) {
2836 			/* end the current figure, if we already had one */
2837 			fprintf(tfp,"%% here ends figure;\n");
2838 		}
2839 		if (actual_depth >= 0) {
2840 			/* start a new figure with a comment */
2841 			fprintf(tfp,"%% \n");
2842 			fprintf(tfp,"%% here starts figure with depth %d\n",
2843 					actual_depth);
2844 			fig_number++;
2845 			/* reset cur_values for multi-postscript. So a new
2846 			   image gets values being set */
2847 			/* This forces the procs that emit the codes to reset
2848 			 * their current values because these sections of code
2849 			 * may be rearranged */
2850 			cur_thickness = -1;
2851 			cur_capstyle = -1;
2852 			cur_joinstyle = -1;
2853 		}
2854 	}
2855 	last_depth = actual_depth;
2856 }
2857 
2858 /* driver defs */
2859 
2860 struct
2861 driver dev_ps = {
2862 	genps_option,
2863 	genps_start,
2864 	genps_grid,
2865 	genps_arc,
2866 	genps_ellipse,
2867 	genps_line,
2868 	genps_spline,
2869 	genps_text,
2870 	genps_end,
2871 	INCLUDE_TEXT
2872 };
2873 
2874 /* eps is just like ps except with no: pages, pagesize, orientation, offset */
2875 
2876 struct
2877 driver dev_eps = {
2878 	geneps_option,
2879 	genps_start,
2880 	genps_grid,
2881 	genps_arc,
2882 	genps_ellipse,
2883 	genps_line,
2884 	genps_spline,
2885 	genps_text,
2886 	genps_end,
2887 	INCLUDE_TEXT
2888 };
2889