1 /*****************************************************************************
2 **  Copyright (C) 1998-2001  Ljubomir Milanovic & Horst Wagner
3 **  This file is part of the g2 library
4 **
5 **  This library is free software; you can redistribute it and/or
6 **  modify it under the terms of the GNU Lesser General Public
7 **  License as published by the Free Software Foundation; either
8 **  version 2.1 of the License, or (at your option) any later version.
9 **
10 **  This library is distributed in the hope that it will be useful,
11 **  but WITHOUT ANY WARRANTY; without even the implied warranty of
12 **  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13 **  Lesser General Public License for more details.
14 **
15 **  You should have received a copy of the GNU Lesser General Public
16 **  License along with this library; if not, write to the Free Software
17 **  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
18 ******************************************************************************/
19 #include <stdio.h>
20 #include <stdlib.h>
21 #include <stdarg.h>
22 #include <limits.h>
23 #include <math.h>
24 #include <string.h>
25 
26 #include "g2.h"
27 #include "g2_device.h"
28 #include "g2_util.h"
29 #include "g2_config.h"
30 
31 #include "g2_PS.h"
32 #include "g2_PS_P.h"
33 #include "g2_PS_funix.h"
34 #include "g2_PS_definitions.h"
35 
36 static int N_PS=0;
37 static g2_PS_device *g2_PS_dev=NULL;
38 
39 /**
40  * \ingroup physdev
41  * \defgroup PS PostScript
42  */
43 
44 
45 /*
46  *
47  * Attach generic PS device
48  *
49  */
g2_open_PS_generic(const char * file_name,enum g2_PS_paper paper,enum g2_PS_orientation orientation,enum g2_PS_format format,long width,long height)50 G2L int g2_open_PS_generic(const char *file_name,
51 	       enum g2_PS_paper paper,
52 	       enum g2_PS_orientation orientation,
53 		   enum g2_PS_format format,
54 		   long width,
55 		   long height)
56 {
57     g2_PS_device *psout=NULL;
58     int pid=-1, i;
59     int vid;
60     FILE *fp;
61 
62     if((fp=fopen(file_name, "w"))==NULL) {
63 	fprintf(stderr, "g2_attach_PS: Error! Can not open file '%s'\n",
64 		file_name);
65 	return -1;
66     }
67 
68     if(g2_PS_dev==NULL) {
69 	g2_PS_dev=g2_malloc(sizeof(g2_PS_device));
70 	N_PS=1;					  /* first PS device */
71 	psout=&g2_PS_dev[N_PS-1];
72 	pid=0;
73     } else {
74 	for(i=0;i<N_PS;i++)			  /* find free place */
75 	    if(g2_PS_dev[i].fp==NULL) {
76 		psout=&g2_PS_dev[i];
77 		pid=i;
78 		break;
79 	    }
80 	if(i==N_PS) {				  /* free place not avail. */
81 	    N_PS++;
82 	    g2_PS_dev=g2_realloc(g2_PS_dev,
83 				 sizeof(g2_PS_device)*N_PS);
84 	    psout=&g2_PS_dev[N_PS-1];
85 	    pid=N_PS-1;
86 	}
87     }
88 
89     vid = g2_register_physical_device(pid, NULL,
90 				      g2_DoubleCoor, g2_PS_funix,
91 				      1.0, 1.0,
92 				      0.0, 0.0);
93 
94     psout->fp=fp;			      /* init PostScript structures */
95     psout->paper=paper;
96     psout->orient=orientation;
97     psout->format=format;
98     psout->width=width;
99     psout->height=height;
100     psout->inks=NULL;
101     psout->N_ink=0;
102     psout->pen=0;
103     psout->page_counter=0;
104     psout->bbox = 0;
105 
106     g2_PS_write_file_header(psout);
107 
108     g2_PS_set_line_width(pid, NULL, 0.0);
109     g2_PS_set_font_size(pid, NULL, 12.0);
110 
111 					    /* g2 settings */
112     g2_allocate_basic_colors(vid);
113     g2_pen(vid, 1);
114 
115     return vid;
116 }
117 
118 
119 /**
120  *
121  * Create a PS device.
122  *
123  * \param file_name postscript file name
124  * \param paper paper type, see ::g2_PS_paper and \ref appendix Appendix
125  * \param orientation paper orientation, see ::g2_PS_orientation
126  *
127  * \return physical device id
128  *
129  * \ingroup PS
130  */
g2_open_PS(const char * file_name,enum g2_PS_paper paper,enum g2_PS_orientation orientation)131 G2L int g2_open_PS(const char *file_name,
132 	       enum g2_PS_paper paper,
133 	       enum g2_PS_orientation orientation)
134 {
135     return g2_open_PS_generic(file_name,paper,orientation,g2_PS_PostScript,0,0);
136 }
137 
138 
139 /**
140  *
141  * Create an encapsulated PS device.
142  *
143  * \param file_name postscript file name
144  *
145  * \return physical device id
146  *
147  * \ingroup PS
148  */
g2_open_EPSF(const char * file_name)149 G2L int g2_open_EPSF(const char *file_name)
150 {
151     return g2_open_PS_generic(file_name,0,0,g2_PS_EPSF,0,0);
152 }
153 
154 
155 /**
156  *
157  * Create an encapsulated PS device with clipping.
158  *
159  * \param file_name postscript file name
160  * \param width clipping region width
161  * \param height clipping region height
162  *
163  * \return physical device id
164  *
165  * \ingroup PS
166  */
g2_open_EPSF_CLIP(const char * file_name,long width,long height)167 G2L int g2_open_EPSF_CLIP(const char *file_name,
168 			long width, long height)
169 {
170     return g2_open_PS_generic(file_name,0,0,g2_PS_EPSF_CLIP,width,height);
171 }
172 
173 /*
174  *
175  *  Write header for postscript file
176  *
177  */
g2_PS_write_file_header(g2_PS_device * ps)178 int g2_PS_write_file_header(g2_PS_device *ps)
179 {
180     int i;
181     if (ps->format == g2_PS_PostScript)
182 	{
183 	    fprintf(ps->fp,"%%!PS-Adobe-2.0\n");
184 	    switch(ps->orient)
185 		{
186 		  case g2_PS_land:
187 		    fprintf(ps->fp,"%%%%Orientation: Landscape\n");
188 		    break;
189 		  case g2_PS_port:
190 		    fprintf(ps->fp,"%%%%Orientation: Portrait\n");
191 		    break;
192 		}
193 	}
194     else if (ps->format == g2_PS_EPSF_CLIP)
195     {
196 	fprintf(ps->fp,"%%!PS-Adobe-3.0 EPSF-2.0\n");
197 	fprintf(ps->fp,"%%%%BoundingBox: 0 0 %ld %ld\n",ps->width,ps->height);
198     }
199     else if (ps->format == g2_PS_EPSF)
200     {
201 	fprintf(ps->fp,"%%!PS-Adobe-3.0 EPSF-2.0\n");
202 	fprintf(ps->fp,"%%%%BoundingBox: (atend)\n");
203     }
204 
205     fprintf(ps->fp,"%%%%Creator: g2 %s\n", G2_VERSION);
206     fprintf(ps->fp, "%%%%EndComments\n");
207 
208     if (ps->format == g2_PS_EPSF_CLIP)
209     {
210 	fprintf(ps->fp,"0 0 moveto\n");
211 	fprintf(ps->fp,"0 %ld rlineto\n",ps->height);
212 	fprintf(ps->fp,"%ld 0 rlineto\n",ps->width);
213 	fprintf(ps->fp,"0 %ld rlineto\n",-ps->height);
214 	fprintf(ps->fp,"closepath\n");
215 	fprintf(ps->fp,"clip\n");
216     }
217 
218     for(i=0;g2_PS_operators[i]!=NULL;i++)
219 	fputs(g2_PS_operators[i], ps->fp);
220 
221     fprintf(ps->fp,"newpath\n");
222     if((ps->orient==g2_PS_land) && (ps->format == g2_PS_PostScript))
223 	fprintf(ps->fp,"%d 0 translate 90 rotate\n",
224 		g2_PS_paper_size[ps->paper][0]);
225 
226     fputs("%%PageTrailer\n%%Page: 1 1\n", ps->fp);
227 
228     return 0;
229 }
230 
231 /*
232  *
233  *    Add circle at (x,y) of dimension size to bounding box
234  *
235  */
g2_PS_bbox_add(g2_PS_device * ps,double x,double y,double size)236 void g2_PS_bbox_add(g2_PS_device *ps,double x,double y,double size)
237 	{
238 	if (ps->bbox == 0) /* bbox is empty */
239 		{
240 		ps->x1=x-size;
241 		ps->x2=x+size;
242 		ps->y1=y-size;
243 		ps->y2=y+size;
244 		ps->bbox=1;
245 		return;
246 		}
247 
248 	if (ps->x1 > x-size)
249 		ps->x1=x-size;
250 	else if (ps->x2 < x+size)
251 		ps->x2=x+size;
252 
253 	if (ps->y1 > y-size)
254 		ps->y1=y-size;
255 	else if (ps->y2 < y+size)
256 		ps->y2=y+size;
257 	return;
258 	}
259 
260 /*
261  *
262  *    Delete PS device
263  *
264  */
g2_PS_delete(int pid,void * pdp)265 int g2_PS_delete(int pid, void *pdp)
266 {
267     g2_PS_device *ps=&g2_PS_dev[pid];
268 	fprintf(ps->fp,"\nshowpage\n");
269     fprintf(ps->fp,"%%%%PageTrailer\n");
270     fprintf(ps->fp,"%%%%EndPage\n");
271 	fprintf(ps->fp,"%%%%Trailer\n");
272 	if (ps->format == g2_PS_EPSF)
273 		{
274 		fprintf(ps->fp,"%%%%BoundingBox: %d %d %d %d\n",
275 				(int)floor(ps->x1),(int)floor(ps->y1),
276 				(int)ceil(ps->x2),(int)ceil(ps->y2));
277 		}
278     fprintf(ps->fp,"%%%%EOF\n");
279 	fclose(ps->fp);
280     free(ps->inks);
281 
282     ps->fp=NULL;				  /* free place */
283 
284     return 0;
285 }
286 
287 
288 
g2_PS_ink(int pid,void * pdp,double red,double green,double blue)289 int g2_PS_ink(int pid, void *pdp,
290 	      double red, double green, double blue)
291 {
292     g2_PS_device *ps=&g2_PS_dev[pid];
293     ps->N_ink++;
294     if(ps->inks==NULL)
295 	ps->inks=(g2_PS_inks *)g2_malloc(ps->N_ink*
296 					  sizeof(g2_PS_inks));
297     else
298 	ps->inks=(g2_PS_inks *)g2_realloc((void *)ps->inks,
299 					   ps->N_ink*
300 					   sizeof(g2_PS_inks));
301 
302     ps->inks[ps->N_ink-1].r=red;
303     ps->inks[ps->N_ink-1].g=green;
304     ps->inks[ps->N_ink-1].b=blue;
305 
306     return ps->N_ink-1;
307 }
308 
309 
310 
g2_PS_pen(int pid,void * pdp,int color)311 int g2_PS_pen(int pid, void *pdp, int color)
312 {
313     g2_PS_device *ps=&g2_PS_dev[pid];
314     if(color>=ps->N_ink || color<0)
315 	return -1;
316     fprintf(ps->fp,"%.4g %.4g %.4g setrgbcolor\n",
317 	    (double)ps->inks[color].r,
318 	    (double)ps->inks[color].g,
319 	    (double)ps->inks[color].b);
320     ps->pen=color;
321     return 0;
322 }
323 
324 
325 
g2_PS_set_background(int pid,void * pdp,int color)326 int g2_PS_set_background(int pid, void *pdp, int color)
327 {
328     return 0;
329 }
330 
331 
332 
g2_PS_clear_palette(int pid,void * pdp)333 int g2_PS_clear_palette(int pid, void *pdp)
334 {
335     g2_PS_device *ps=&g2_PS_dev[pid];
336     free((void *)ps->inks);
337     ps->N_ink=0;
338     ps->inks=NULL;
339     return 0;
340 }
341 
342 
343 
g2_PS_set_line_width(int pid,void * pdp,double w)344 int g2_PS_set_line_width(int pid, void *pdp, double w)
345 {
346     g2_PS_device *ps=&g2_PS_dev[pid];
347     fprintf(ps->fp,"%.4g setlinewidth\n", w);
348 	ps->w = w;
349     return 0;
350 }
351 
352 
353 
g2_PS_set_dash(int pid,void * pdp,int N,double * data)354 int g2_PS_set_dash(int pid, void *pdp, int N, double *data)
355 {
356     g2_PS_device *ps=&g2_PS_dev[pid];
357     int i;
358     fprintf(ps->fp, "[ ");
359     for(i=0;i<N;i++)
360 	if(data[i]>0.0)
361 	    fprintf(ps->fp, "%.4g ", data[i]);
362     fprintf(ps->fp, "] 0 setdash\n");
363     return 0;
364 }
365 
366 
367 
g2_PS_set_font_size(int pid,void * pdp,double size)368 int g2_PS_set_font_size(int pid, void *pdp, double size)
369 {
370     g2_PS_device *ps=&g2_PS_dev[pid];
371     if(size<=0.0)
372 	return -1;
373     fprintf(ps->fp,"%s findfont %.4g scalefont setfont\n", g2_PSFont, size);
374     ps->size = size;
375     return 0;
376 }
377 
378 
379 
g2_PS_clear(int pid,void * pdp)380 int g2_PS_clear(int pid, void *pdp)
381 {
382     g2_PS_device *ps=&g2_PS_dev[pid];
383     fprintf(ps->fp, "gsave showpage\n");
384     fprintf(ps->fp, "%%%%PageTrailer\n%%%%EndPage\n");
385     fprintf(ps->fp, "%%%%Page: %d %d\ngrestore newpath\n",
386 	    ps->page_counter+1, ps->page_counter+1);
387     ps->page_counter++;
388     return 0;
389 }
390 
391 
392 
g2_PS_flush(int pid,void * pdp)393 int g2_PS_flush(int pid, void *pdp)
394 {
395     g2_PS_device *ps=&g2_PS_dev[pid];
396     fflush(ps->fp);
397     return 0;
398 }
399 
400 
401 
g2_PS_plot(int pid,void * pdp,double x,double y)402 int g2_PS_plot(int pid, void *pdp, double x, double y)
403 {
404     g2_PS_device *ps=&g2_PS_dev[pid];
405     fprintf(ps->fp,"%.4g %.4g P\n", x, y);
406 	g2_PS_bbox_add(ps,x,y,1);
407     return 0;
408 }
409 
410 
411 
g2_PS_line(int pid,void * pdp,double x1,double y1,double x2,double y2)412 int g2_PS_line(int pid, void *pdp, double x1, double y1, double x2, double y2)
413 {
414     g2_PS_device *ps=&g2_PS_dev[pid];
415     fprintf(ps->fp,"%.4g %.4g M %.4g %.4g L St\n",
416 	    x1, y1, x2, y2);
417 	g2_PS_bbox_add(ps,x1,y1,ps->w);
418 	g2_PS_bbox_add(ps,x2,y2,ps->w);
419     return 0;
420 }
421 
422 
423 
g2_PS_poly_line(int pid,void * pdp,int N,double * points)424 int g2_PS_poly_line(int pid, void *pdp, int N, double *points)
425 {
426     g2_PS_device *ps=&g2_PS_dev[pid];
427     int i;
428     fprintf(ps->fp,"%.4g %.4g M\n", points[0], points[1]);
429 	g2_PS_bbox_add(ps,points[0], points[1],ps->w);
430     for(i=2;i<2*N;i+=2)
431 		{
432 		fprintf(ps->fp, "%.4g %.4g L\n", points[i], points[i+1]);
433 		g2_PS_bbox_add(ps,points[i], points[i+1],ps->w);
434 		}
435     fprintf(ps->fp, "St\n");
436     return 0;
437 }
438 
439 
440 
g2_PS_polygon(int pid,void * pdp,int N,double * points)441 int g2_PS_polygon(int pid, void *pdp, int N, double *points)
442 {
443     g2_PS_device *ps=&g2_PS_dev[pid];
444     int i;
445     fprintf(ps->fp,"%.4g %.4g M\n",points[0], points[1]);
446 	g2_PS_bbox_add(ps,points[0], points[1],ps->w);
447     for(i=2;i<2*N;i+=2)
448 		{
449 		fprintf(ps->fp, "%.4g %.4g L\n", points[i], points[i+1]);
450 		g2_PS_bbox_add(ps,points[i], points[i+1],ps->w);
451 		}
452     fprintf(ps->fp, "%.4g %.4g L St\n", points[0], points[1]);
453     return 0;
454 }
455 
456 
457 
g2_PS_filled_polygon(int pid,void * pdp,int N,double * points)458 int g2_PS_filled_polygon(int pid, void *pdp, int N, double *points)
459 {
460     g2_PS_device *ps=&g2_PS_dev[pid];
461     int i;
462     fprintf(ps->fp,"newpath %.4g %.4g M\n",points[0], points[1]);
463 	g2_PS_bbox_add(ps,points[0], points[1],ps->w);
464     for(i=2;i<2*N;i+=2)
465 		{
466 		fprintf(ps->fp, "%.4g %.4g L\n", points[i], points[i+1]);
467 		g2_PS_bbox_add(ps,points[i], points[i+1],ps->w);
468 		}
469     fprintf(ps->fp, "%.4g %.4g L fill St\n", points[0], points[1]);
470     return 0;
471 }
472 
473 
474 
g2_PS_rectangle(int pid,void * pdp,double x1,double y1,double x2,double y2)475 int g2_PS_rectangle(int pid, void *pdp,
476 		    double x1, double y1, double x2, double y2)
477 {
478     g2_PS_device *ps=&g2_PS_dev[pid];
479     fprintf(ps->fp,"%.4g %.4g %.4g %.4g R\n",
480 	    x2, y2, x1, y1);
481 	g2_PS_bbox_add(ps,x1,y1,ps->w);
482 	g2_PS_bbox_add(ps,x2,y2,ps->w);
483     return 0;
484 }
485 
486 
487 
g2_PS_filled_rectangle(int pid,void * pdp,double x1,double y1,double x2,double y2)488 int g2_PS_filled_rectangle(int pid, void *pdp,
489 			   double x1, double y1, double x2, double y2)
490 {
491     g2_PS_device *ps=&g2_PS_dev[pid];
492     fprintf(ps->fp,"%.4g %.4g %.4g %.4g FR\n",
493 	    x2, y2, x1, y1);
494 	g2_PS_bbox_add(ps,x1,y1,ps->w);
495 	g2_PS_bbox_add(ps,x2,y2,ps->w);
496     return 0;
497 }
498 
499 
500 
g2_PS_triangle(int pid,void * pdp,double x1,double y1,double x2,double y2,double x3,double y3)501 int g2_PS_triangle(int pid, void *pdp,
502 		   double x1, double y1,
503 		   double x2, double y2,
504 		   double x3, double y3)
505 {
506     g2_PS_device *ps=&g2_PS_dev[pid];
507     fprintf(ps->fp,"%.4g %.4g %.4g %.4g %.4g %.4g T\n",
508 	    x1,y1,x2,y2,x3,y3);
509 	g2_PS_bbox_add(ps,x1,y1,ps->w);
510 	g2_PS_bbox_add(ps,x2,y2,ps->w);
511 	g2_PS_bbox_add(ps,x3,y3,ps->w);
512     return 0;
513 }
514 
515 
g2_PS_filled_triangle(int pid,void * pdp,double x1,double y1,double x2,double y2,double x3,double y3)516 int g2_PS_filled_triangle(int pid, void *pdp,
517 			 double x1, double y1,
518 			 double x2, double y2,
519 			 double x3, double y3)
520 {
521     g2_PS_device *ps=&g2_PS_dev[pid];
522     fprintf(ps->fp,"%.4g %.4g %.4g %.4g %.4g %.4g FT\n",
523 	    x1,y1,x2,y2,x3,y3);
524 	g2_PS_bbox_add(ps,x1,y1,ps->w);
525 	g2_PS_bbox_add(ps,x2,y2,ps->w);
526 	g2_PS_bbox_add(ps,x3,y3,ps->w);
527     return 0;
528 }
529 
530 
531 
g2_PS_arc(int pid,void * pdp,double x,double y,double r1,double r2,double a1,double a2)532 int g2_PS_arc(int pid, void *pdp,
533 	      double x, double y,
534 	      double r1, double r2,
535 	      double a1, double a2)
536 {
537     g2_PS_device *ps=&g2_PS_dev[pid];
538     if(a1==a2) {
539 	a1=0;
540 	a2=360;
541     }
542     fprintf(ps->fp,"%.4g %.4g %.4g %.4g %.4g %.4g A\n",
543 	    a1, a2, r1, r2, x, y);
544 	g2_PS_bbox_add(ps,x+r1,y+r2,ps->w);
545 	g2_PS_bbox_add(ps,x-r1,y-r2,ps->w);
546     return 0;
547 }
548 
549 
550 
g2_PS_filled_arc(int pid,void * pdp,double x,double y,double r1,double r2,double a1,double a2)551 int g2_PS_filled_arc(int pid, void *pdp,
552 		     double x, double y,
553 		     double r1, double r2,
554 		     double a1, double a2)
555 {
556     g2_PS_device *ps=&g2_PS_dev[pid];
557     if(a1==a2) {
558 	a1=0;
559 	a2=360;
560     }
561     fprintf(ps->fp,"%.4g %.4g %.4g %.4g %.4g %.4g FA\n",
562 	    a1, a2, r1, r2, x, y);
563 	g2_PS_bbox_add(ps,x+r1,y+r2,ps->w);
564 	g2_PS_bbox_add(ps,x-r1,y-r2,ps->w);
565     return 0;
566 }
567 
568 
569 
g2_PS_ellipse(int pid,void * pdp,double x,double y,double r1,double r2)570 int g2_PS_ellipse(int pid, void *pdp,
571 		  double x, double y,
572 		  double r1, double r2)
573 {
574     g2_PS_device *ps=&g2_PS_dev[pid];
575     fprintf(ps->fp,"0 360 %.4g %.4g %.4g %.4g A\n",
576 	    r1, r2, x, y);
577 	g2_PS_bbox_add(ps,x+r1,y+r2,ps->w);
578 	g2_PS_bbox_add(ps,x-r1,y-r2,ps->w);
579     return 0;
580 }
581 
582 
583 
584 
g2_PS_filled_ellipse(int pid,void * pdp,double x,double y,double r1,double r2)585 int g2_PS_filled_ellipse(int pid, void *pdp,
586 			 double x, double y,
587 			 double r1, double r2)
588 {
589     g2_PS_device *ps=&g2_PS_dev[pid];
590     fprintf(ps->fp,"0 360 %.4g %.4g %.4g %.4g FA\n",
591 	    r1, r2, x, y);
592 	g2_PS_bbox_add(ps,x+r1,y+r2,ps->w);
593 	g2_PS_bbox_add(ps,x-r1,y-r2,ps->w);
594     return 0;
595 }
596 
597 
598 
g2_PS_draw_string(int pid,void * pdp,double x,double y,const char * text)599 int g2_PS_draw_string(int pid, void *pdp,
600 		      double x, double y, const char *text)
601 {
602     g2_PS_device *ps=&g2_PS_dev[pid];
603     fputc('(', ps->fp);
604     for(;*text!='\0';text++)
605 	switch(*text) {
606 	  case '(':
607 	    fputs("\\(", ps->fp);
608 	    break;
609 	  case ')':
610 	    fputs("\\)", ps->fp);
611 	    break;
612 	  case '\\':
613 	    fputs("\\\\", ps->fp);
614 	    break;
615 	  default:
616 	    fputc(*text, ps->fp);
617 	    break;
618 	}
619     fprintf(ps->fp,") %.4g %.4g S\n", x, y);
620 	g2_PS_bbox_add(ps,x,y,ps->size);
621 	g2_PS_bbox_add(ps,x+ps->size*strlen(text),y,ps->size);
622     return 0;
623 }
624 
625