1 /* ======================================================= *
2  * Copyright 1998-2008 Stephen C. Grubb                    *
3  * http://ploticus.sourceforge.net                         *
4  * Covered by GPL; see the file ./Copyright for details.   *
5  * ======================================================= */
6 
7 /* PCODE - Draw operations.
8 	All draw operations use pcode() except initialization (done in init.c),
9 	and doing filled GD rectangles which use gifrect().
10 
11 	Bounding box, overall output scaling, and other low level operations should
12 	be done here if possible rather than in the individual device interfaces.
13 
14 	Many E function calls such as Emov and Elin are actually macros defined in
15 	plg.h, which call pcode with a single character op code and perhaps some
16 	parameters.  Others go thru a routine in stub.c.
17 
18    ==========================================================================
19    Device codes (Edev):
20 	p		postscript
21 	x		X11
22 	g		GD (png, gif, jpeg, etc.)
23 	s		SVG
24  	f		swf (flash format)
25 
26    Op codes:
27 	L		lineto
28 	M		moveto
29 	P		path to
30 	T, C, J		left-adusted Text, Centered text, right Justified text
31 	r		set pending current color - color change actually occurs on next line/text or 'v'
32 	v		force color change immediately (can be done by app code)
33 	  k		   execute set current color (recursive call from pcode - never done by app code)
34 
35 	s		Fill (color)
36 	I		set text size
37 	F		set text font
38 	D		set text direction
39 	Y		set line properties
40 	.		direct pixel data point (gd and x11 only)
41 	z		clear screen / set background color
42 	H		squelch display (s = "on" or s = "off" )		added 8/5/04 scg
43 	Q		end of file (tells drivers to finish up)
44 
45    These codes are used by paginated postscript only (paper control):
46 	O		paper orientation (ps only)
47 	Z		print/eject (ps only)
48 
49    These codes are used by x11 only (interactive context):
50 	U		flush (x11 only)
51 	W		wait for a key or mouse click (x11 only)
52 	w		cycle notifier from within a loop (x11 only)
53 	b		save window to backing store (x11 only)
54 	B		restore window from backing store (x11 only)
55 	d		make window disappear (x11 only)
56 	a		make window re-appear (x11 only)
57 	e		set text scale factor (x11 only, when user resizes window)
58 
59    ==========================================================================
60 
61 */
62 
63 #include "plg.h"
64 
65 extern int GL_member(), PL_clickmap_inprogress(), PL_clickmap_out();
66 #ifndef NOX11
67   extern int PLGX_pointsize(), PLGX_stroke(), PLGX_moveto(), PLGX_lineto(), PLGX_path(), PLGX_text(), PLGX_color(), PLGX_fill();
68   extern int PLGX_savewin(),  PLGX_restorewin(),  PLGX_centext(),  PLGX_rightjust(),  PLGX_wait(),  PLGX_linetype(),  PLGX_async();
69   extern int PLGX_disappear(), PLGX_appear(), PLGX_scaletext(), PLGX_flush(), PLGX_dot(), PLGX_pixpt();
70 #endif
71 #ifndef NOGD
72   extern int PLGG_lineto(), PLGG_moveto(), PLGG_pathto(), PLGG_text(), PLGG_centext(), PLGG_font(), PLGG_rightjust();
73   extern int PLGG_fill(), PLGG_color(), PLGG_textsize(), PLGG_chardir(), PLGG_linetype(), PLGG_eof(), PLGG_rect();
74   extern int PLGG_imload(), PLGG_implace(), PLGG_pixpt();
75 #endif
76 #ifndef NOSVG
77   extern int PLGS_stroke(), PLGS_moveto(), PLGS_lineto(), PLGS_path(), PLGS_text(), PLGS_fill(), PLGS_color();
78   extern int PLGS_pointsize(), PLGS_font(), PLGS_chardir(), PLGS_linetype(), PLGS_trailer();
79   extern int PLGS_showimg(), PLGS_setimg();
80 #endif
81 #ifndef NOSWF
82   extern int PLGF_stroke(), PLGF_moveto(), PLGF_lineto(), PLGF_path(), PLGF_text(), PLGF_fill(), PLGF_color();
83   extern int PLGF_pointsize(), PLGF_font(), PLGF_chardir(), PLGF_linetype(), PLGF_trailer();
84 #endif
85 #ifndef NOPS
86   extern int PLGP_stroke(), PLGP_moveto(), PLGP_lineto(), PLGP_path(), PLGP_text(), PLGP_fill(), PLGP_color();
87   extern int PLGP_pointsize(), PLGP_font(), PLGP_chardir(), PLGP_linetype(), PLGP_show(), PLGP_paper(), PLGP_trailer(), PLGP_newpage();
88 #endif
89 
90 
91 static int line_new = 0, line_drawing = 0;		/* used by postscript section */
92 static int vertchar = 0; 			/* true if character direction is vertical..*/
93 						/* ..used only by x11 */
94 #ifndef NOX11
95 static double txt_curx;				/* real starting place of centered &rt justified text */
96 #endif
97 static double txt_width;				/* width of most recently drawn text line. */
98 static char prev_op;				/* previous op */
99 static int squelched = 0;			/* is output being squelched? 1=yes, 0=no */
100 static int pagenum = 1;				/* current page # */
101 static int virginpage = 1;		/* 1 when nothing has yet been drawn on current page */
102 
103 static int keeping_bb = 1;
104 
105 static double bb_x1 = 999;		/* coords of bounding box for entire run */
106 static double bb_y1 = 999;
107 static double bb_x2 = -999;
108 static double bb_y2 = -999;
109 
110 static double bb2_x1 = 999;		/* coords of "sub" bounding box available to app */
111 static double bb2_y1 = 999;
112 static double bb2_x2 = -999;
113 static double bb2_y2 = -999;
114 static int keep_bb2 = 0;
115 static int tightbb = 0;
116 static double scx1, scy1, scx2, scy2;   /* specified crop zone */
117 static int specifycrop = 0; /* 0 = no  1 = absolute values   2 = values relative to bounding box */
118 
119 static double globalscale = 1.0;
120 static double globalscaley = 1.0;
121 static double posterxo = 0.0;
122 static double posteryo = 0.0;
123 static int postermode = 0;
124 static double lmlx = 0.0, lmly = 0.0;  /* last move local x and y
125 	(saves location of last 'MOVE', including any effects of globalscale and poster ofs */
126 static int pcodedebug = 0;
127 static FILE *pcodedebugfp = NULL;
128 static int in_obj = 0;
129 static int colorchg_pending = 0;
130 static FILE *dumpfp = NULL;
131 static int dumpfp_closable = 0;
132 static int dumpmax = 0;
133 static int ndumplines = 0;
134 static int susp_dump = 0;
135 /* static double xsanemax, ysanemax;
136  * static int sanezone = 0;
137  */
138 
139 static int verttextsim();
140 
141 /* ======================= */
142 int
PLG_pcode_initstatic()143 PLG_pcode_initstatic()
144 {
145 line_new = 0; line_drawing = 0;
146 vertchar = 0;
147 squelched = 0;
148 pagenum = 1;
149 virginpage = 1;
150 keeping_bb = 1;
151 bb_x1 = 999; bb_y1 = 999; bb_x2 = -999; bb_y2 = -999;
152 bb2_x1 = 999; bb2_y1 = 999; bb2_x2 = -999; bb2_y2 = -999;
153 keep_bb2 = 0;
154 tightbb = 0;
155 specifycrop = 0;
156 globalscale = 1.0;
157 globalscaley = 1.0;
158 posterxo = posteryo = 0.0;
159 postermode = 0;
160 lmlx = lmly = 0.0;
161 pcodedebug = 0;
162 in_obj = 0;
163 colorchg_pending = 0;
164 pcodedebugfp = NULL;
165 dumpfp = NULL;
166 dumpfp_closable = 0;
167 dumpmax = 0;
168 ndumplines = 0;
169 susp_dump = 0;
170 /* sanezone = 0; */
171 return( 0 );
172 }
173 
174 
175 /* ======================= */
176 int
PLG_pcode(op,x,y,s)177 PLG_pcode( op, x, y, s )
178 char op; /* op code */
179 double x, y;  /* coordinates */
180 char s[];     /* optional character string */
181 {
182 char buf[512];
183 int stat;
184 
185 /* postscript: inform driver we're beginning a new page.. */
186 if( Edev == 'p' && virginpage && !GL_member( op, "Q" ) ) {
187 #ifndef NOPS
188 	PLGP_newpage( pagenum );
189 #endif
190 	virginpage = 0;
191 	}
192 
193 if( pcodedebug == 2 ) fprintf( stderr, "%c %g %g %s\n", op, x, y, s );
194 
195 /* use this chunk to step thru one op code at a time - press any key to continue */
196 /*  // if( op != 'w' )
197  * fprintf( stderr, "%c %g %g %s\n", op, x, y, s );
198  * PLGX_flush();
199  * PLGX_wait();
200  */
201 
202 
203 
204 /* lazy color set .. only execute a color change when line, text, or fill is imminent.. scg 6/18/04 */
205 if( op == 'r' ) {
206 	strcpy( Enextcolor, s );
207 	if( strcmp( Enextcolor, Ecurcolor ) !=0 ) colorchg_pending = 1;
208 	else colorchg_pending = 0;
209 	return( 0 );
210 	}
211 else if( colorchg_pending && GL_member( op, "LTCJszv." )) {  /* v added scg 5/10/05 */
212 	PLG_pcode( 'k', 0.0, 0.0, Enextcolor );
213 	colorchg_pending = 0;
214 	if( op == 'v' ) return( 0 ); /* added scg 5/10/05 */
215 	}
216 
217 if( op == 'k' ) strcpy( Ecurcolor, s );
218 
219 
220 /* For clear op: do it then return; don't include in bounding box calculations */
221 if( op == 'z' ) {
222 	keeping_bb = 0;
223 	susp_dump = 1;
224 	/* compensate EWinw and EWiny by globalscale because this op will
225 		be doubly affected by globalscale.. */
226 	Ecblock( 0.0, 0.0, EWinx/globalscale, EWiny/globalscaley, s, 0 );
227 	susp_dump = 0;
228 	keeping_bb = 1;
229 	if( dumpfp != NULL ) fprintf( dumpfp, "z 0.0 0.0 %s\n", s );
230 	return( 0 );
231 	}
232 
233 
234 
235 if( op == 'M' ) { Ex1 = x; Ey1 = y; } /* remember most recent 'move', untainted
236 					by globalscale or posterofs.. */
237 
238 /* scale x, y according to global scale, for all move, draw and text size ops */
239 /* if( globalscale != 1.0 ) {  ... changed scg 3/28/06 */
240 if( globalscale != 1.0 || globalscaley != 1.0 ) {
241 	if( GL_member( op, "LMPI." )) { x *= globalscale; y *= globalscaley; }
242 	if( op == 'Y' ) y *= globalscale; /* dash scale */
243 	}
244 
245 /* if poster offset specified, translate */
246 if( postermode ) {
247 	if( GL_member( Edev, "pcx" ) && GL_member( op, "LMP." )) {
248 		x += posterxo;
249 		y += posteryo;
250 		}
251 	}
252 
253 if( op == 'Q' ) {
254 #ifdef PLOTICUS
255 	/* allow clickmap to be generated on any device.. */
256 	if( Edev != 'g' && Edev != 's' && PL_clickmap_inprogress() ) PL_clickmap_out( 0, 0 ); /* GD & SWF handled in drivers.. */
257 #endif
258 	if( pcodedebug ) fprintf( pcodedebugfp, "Done with page.  Bounding box is: %-5.2f, %-5.2f to %-5.2f, %-5.2f\n",
259 					bb_x1, bb_y1, bb_x2, bb_y2 );
260 	}
261 
262 
263 /* reject endobj's without companion beginobj */
264 if( op == '<' ) in_obj = 1;
265 else if( op == '>' ) {
266 	if( ! in_obj ) return( 0 );
267 	else in_obj = 0;
268 	}
269 
270 
271 /* dump file */
272 if( dumpfp != NULL && !susp_dump ) {
273 	if( GL_member( op, "UWwbBdae" )) ; /* do nothing for these interactive-context ops.. */
274 	else if( dumpmax > 0 && ndumplines > dumpmax ) ; /* # output lines exceeded */
275 	else if( op == 'Q' && dumpfp_closable ) fclose( dumpfp );
276 	else if( op == 'k' ) fprintf( dumpfp, "r 0.0 0.0 %s\n", s ); /* write an r instead.. (color change optim) */
277 	else fprintf( dumpfp, "%c %.3f %.3f %s\n", op, x, y, s );
278 	}
279 
280 
281 if( op == 'H' ) {  /* moved up - scg 8/12/05 */
282 	if( strcmp( s, "on" )==0 ) squelched = 1;
283 	else squelched = 0;
284 	return( 0 );
285 	}
286 
287 
288 
289 /*********** squelch mode: suppress all operations that actually draw something.. */
290 if( squelched && GL_member( op, "LMPTCJs." )) ;   /* scg 10/16/05 */
291 
292 
293 /*********** interface to X11 xlib driver.. */
294 else if( Edev == 'x' ) {
295 #ifndef NOX11
296 
297 	/* this section changed scg 5/4/04 to improve line dashing on lineplots */
298 	if( op != 'L' ) {
299 		if( line_drawing ) PLGX_stroke();
300 		line_drawing = 0;
301 		}
302 
303 	switch (op) {
304 
305                 case 'L' : if( line_new ) PLGX_moveto( lmlx, lmly );
306                             PLGX_lineto( x, y );
307                             line_new = 0;
308                             line_drawing = 1;
309                             break;
310                 case 'M' : line_new = 1; break;
311                 case 'P' : if( line_new ) PLGX_moveto( lmlx, lmly );
312                            PLGX_path( x, y );
313                            line_new = 0;
314                            break;
315 		case 'T' : if( vertchar ) break;
316 			   PLGX_moveto( lmlx, lmly );
317 			   PLGX_text( s, &txt_width );
318 			   break;
319 
320 		case 'k' : PLGX_color( s ); return( 0 );
321 		case 's' : PLGX_fill( ); return( 0 );
322 		case 'U' : PLGX_flush(); return( 0 );
323 		case 'b' : PLGX_savewin(); return( 0 );
324 		case 'B' : PLGX_restorewin(); return( 0 );
325 		case 'I' : PLGX_pointsize( (int)(x), &Ecurtextwidth ); return( 0 );
326 		case 'C' : if( !vertchar ) {
327 				PLGX_moveto( lmlx-6.0, lmly );
328 				PLGX_centext( s, 12.0, &txt_curx, &txt_width );
329 				}
330 			   break;
331 		case 'J' : if( !vertchar ) {
332 				PLGX_moveto( lmlx-12.0, lmly );
333 				PLGX_rightjust( s, 12.0, &txt_curx, &txt_width );
334 				}
335 			   break;
336 		case 'W' : PLGX_wait(); return( 0 );
337 		case 'Y' : PLGX_linetype( s, x, y ); return( 0 );
338 		case 'D' : if( x == 90 || x == 270 ) vertchar = 1;
339 			   else vertchar = 0;
340 			   return( 0 );
341 
342 		case 'w' : PLGX_async(); return( 0 );
343 		case '.' : PLGX_pixpt( x, y, s ); break;   /* scg 5/25/06 */
344 		case 'd' : PLGX_disappear(); return( 0 );
345 		case 'a' : PLGX_appear(); return( 0 );
346 		case 'e' : PLGX_scaletext( x ); return( 0 );
347 		}
348 #endif
349 	}
350 
351 
352 /*************** interface to GD driver.. */
353 else if( Edev == 'g' ) {
354 #ifndef NOGD
355 	switch (op) {
356 		case 'L' : PLGG_lineto( x, y ); break;
357 		case 'M' : PLGG_moveto( x, y ); break;
358 		case 'P' : PLGG_pathto( x, y ); break;
359 		case '.' : PLGG_pixpt( x, y, s ); break;   /* scg 5/25/06 */
360 		case 'T' : PLGG_text( s ); break;
361 		case 'C' : PLGG_centext( s ); break;
362 		case 'F' : PLGG_font( s ); break;
363 		case 'J' : PLGG_rightjust( s ); break;
364 		case 's' : PLGG_fill(); return( 0 );
365 		case 'k' : PLGG_color( s ); return( 0 );
366 		case 'I' : PLGG_textsize( (int)x ); return( 0 );
367 		case 'D' : PLGG_chardir( (int)x );
368 			   if( (int)x != 0 ) vertchar = 1;
369 			   else vertchar = 0;
370 			   return( 0 );
371 		case 'Y' : PLGG_linetype( s, x, y ); return( 0 );
372 		case 'Q' : PLG_getoutfilename( buf );
373 			   if( buf[0] == '\0' ) strcpy( buf, "unnamed_result_image" ); /* fallback */
374 
375 			   /* see if anything has been drawn, if not, return */
376 			   if ( bb_x2 < -998 && bb_y2 < -998 ) return( 0 );
377 
378 			   if( tightbb ) stat = PLGG_eof( buf, bb_x1,  bb_y1, bb_x2, bb_y2 );
379 			   else if( specifycrop == 1 )
380 			     stat = PLGG_eof( buf, scx1, scy1, scx2, scy2 );
381 			   else if( specifycrop == 2 )
382 			     stat = PLGG_eof( buf, (bb_x1)-scx1, (bb_y1)-scy1, (bb_x2)+scx2, (bb_y2)+scy2 );
383 			   else stat = PLGG_eof( buf, bb_x1-0.2,  bb_y1-0.2, bb_x2+0.2, bb_y2+0.2 );
384 			   Eresetbb();
385 			   return( stat );
386 		}
387 #endif
388 	}
389 
390 
391 /*************** interface to svg driver */
392 else if( Edev == 's'  ) {
393 #ifndef NOSVG
394 	if( op != 'L' ) {
395 		if( line_drawing ) PLGS_stroke();
396 		line_drawing = 0;
397 		}
398 
399 	switch( op ) {
400 		case 'L' : if( line_new ) PLGS_moveto( lmlx, lmly );
401 			    PLGS_lineto( x, y );
402 			    line_new = 0;
403 			    line_drawing = 1;
404 			    break;
405 		case 'M' : line_new = 1; break;
406 		case 'P' : if( line_new ) PLGS_moveto( lmlx, lmly );
407 			   PLGS_path( x, y );
408 			   line_new = 0;
409 			   break;
410 
411 		case 'T' : PLGS_text( op, lmlx, lmly, s, 0.0 ); break;
412 
413 		case 'C' : if( !vertchar ) PLGS_text( op, lmlx , lmly, s, 0.0 );
414 			   else if( vertchar ) PLGS_text( op, lmlx, lmly , s, 0.0 );
415 			   break;
416 		case 'J' : if( !vertchar ) PLGS_text( op, lmlx, lmly, s, 0.0 );
417 			   else if( vertchar ) PLGS_text( op, lmlx, lmly , s, 0.0 );
418 			   break;
419 
420 		case 's' : PLGS_fill( ); return( 0 );
421 		case 'k' : PLGS_color( s ); return( 0 );
422 		case 'I' : PLGS_pointsize( (int)x ); return( 0 );
423 		case 'F' : PLGS_font( s ); return( 0 );
424 		case 'D' : PLGS_chardir( (int)x );
425 			   if( x == 90 || x == 270 ) vertchar = 1;
426 			   else vertchar = 0;
427 			   return( 0 );
428 		case 'Y' : PLGS_linetype( s, x, y ); return( 0 );
429 		/* case '<' : PLGS_objbegin( s ); return( 0 );
430 		 * case '>' : PLGS_objend(); return( 0 );
431 		 */
432 		case 'Z' : pagenum++; virginpage = 1; return( 0 );
433 		case 'Q' : if( !virginpage ) pagenum++;
434 			   if( tightbb )
435 			     stat = PLGS_trailer( bb_x1-0.05, bb_y1-0.05, bb_x2+0.05, bb_y2+0.05 );
436 			   else if( specifycrop == 1 )
437 			     stat = PLGS_trailer( scx1, scy1, scx2, scy2 );
438 			   else if( specifycrop == 2 ) {
439 			     stat = PLGS_trailer( (bb_x1-0.05)-scx1, (bb_y1-0.05)-scy1, (bb_x2+0.05)+scx2, (bb_y2+0.05)+scy2 );
440 				}
441 			   else
442 			     /* add 0.2" margin to be generous in cases of fat lines, etc. */
443 			     stat = PLGS_trailer( bb_x1-0.2, bb_y1-0.2, bb_x2+0.2, bb_y2+0.2 );
444 			   Eresetbb();
445 			   return( stat );
446 		}
447 #endif
448 	}
449 
450 /*************** interface to swf driver */
451 else if( Edev == 'f'  ) {
452 #ifndef NOSWF
453  	if( op != 'L' ) {
454  		if( line_drawing ) PLGF_stroke();
455  		line_drawing = 0;
456  		}
457 
458  	switch( op ) {
459  		case 'L' : if( line_new ) PLGF_moveto( lmlx, lmly );
460  			    PLGF_lineto( x, y );
461  			    line_new = 0;
462  			    line_drawing = 1;
463  			    break;
464  		case 'M' : line_new = 1; break;
465  		case 'P' : if( line_new ) PLGF_moveto( lmlx, lmly );
466  			   PLGF_path( x, y );
467  			   line_new = 0;
468  			   break;
469 
470  		case 'T' : PLGF_text( op, lmlx, lmly, s, 0.0 ); break;
471 
472  		case 'C' : if( !vertchar ) PLGF_text( op, lmlx , lmly, s, 0.0 );
473  			   else if( vertchar ) PLGF_text( op, lmlx, lmly , s, 0.0 );
474  			   break;
475  		case 'J' : if( !vertchar ) PLGF_text( op, lmlx, lmly, s, 0.0 );
476  			   else if( vertchar ) PLGF_text( op, lmlx, lmly , s, 0.0 );
477  			   break;
478 
479  		case 's' : PLGF_fill( ); return( 0 );
480  		case 'k' : PLGF_color( s ); return( 0 );
481  		case 'I' : PLGF_pointsize( (int)x ); return( 0 );
482  		case 'F' : PLGF_font( s ); return( 0 );
483  		case 'D' : PLGF_chardir( (int)x );
484  			   if( x == 90 || x == 270 ) vertchar = 1;
485  			   else vertchar = 0;
486  			   return( 0 );
487  		case 'Y' : PLGF_linetype( s, x, y ); return( 0 );
488 		/* case '<' : PLGF_objbegin( s ); return( 0 );
489  		 * case '>' : PLGF_objend(); return( 0 );
490 		 */
491  		case 'Z' : pagenum++; virginpage = 1; return( 0 );
492  		case 'Q' : if( !virginpage ) pagenum++;
493  			   if( tightbb )
494  			     stat = PLGF_trailer( bb_x1-0.05, bb_y1-0.05, bb_x2+0.05, bb_y2+0.05 );
495  			   else if( specifycrop == 1 )
496  			     stat = PLGF_trailer( scx1, scy1, scx2, scy2 );
497  			   else if( specifycrop == 2 ) {
498  			     stat = PLGF_trailer( (bb_x1-0.05)-scx1, (bb_y1-0.05)-scy1, (bb_x2+0.05)+scx2, (bb_y2+0.05)+scy2 );
499  				}
500  			   else
501  			     /* add 0.2" margin to be generous in cases of fat lines, etc. */
502  			     stat = PLGF_trailer( bb_x1-0.2, bb_y1-0.2, bb_x2+0.2, bb_y2+0.2 );
503  			   Eresetbb();
504  			   return( stat );
505  		}
506 #endif
507  	}
508 
509 
510 
511 /* interface to postscript driver */
512 else if( Edev == 'p'  ) {
513 #ifndef NOPS
514 
515 	if( op != 'L' ) {
516 		if( line_drawing ) PLGP_stroke();
517 		line_drawing = 0;
518 		}
519 
520 	switch( op ) {
521 		case 'L' : if( line_new ) PLGP_moveto( lmlx, lmly );
522 			    PLGP_lineto( x, y );
523 			    line_new = 0;
524 			    line_drawing = 1;
525 			    break;
526 		case 'M' : line_new = 1; break;
527 		case 'P' : if( line_new ) PLGP_moveto( lmlx, lmly );
528 			   PLGP_path( x, y );
529 			   line_new = 0;
530 			   break;
531 		case 'T' : PLGP_text( op, lmlx, lmly, s, 0.0 ); break;
532 
533 		case 'C' : if( !vertchar ) PLGP_text( op, lmlx - 6.0, lmly, s, 12.0 );
534 			   else if( vertchar ) PLGP_text( op, lmlx, lmly - 6.0, s, 12.0 );
535 			   break;
536 		case 'J' : if( !vertchar ) PLGP_text( op, lmlx-12.0, lmly, s, 12.0 );
537 			   else if( vertchar ) PLGP_text( op, lmlx, lmly - 12.0, s, 12.0 );
538 			   break;
539 
540 		case 's' : PLGP_fill( ); return( 0 );
541 		case 'k' : PLGP_color( s ); return( 0 );
542 		/* case 'c' : PLGP_closepath(); return( 0 ); */
543 		case 'I' : PLGP_pointsize( (int)x ); return( 0 );
544 		case 'F' : PLGP_font( s ); return( 0 );
545 		case 'D' : PLGP_chardir( (int)x );
546 			   if( x == 90 || x == 270 ) vertchar = 1;
547 			   else vertchar = 0;
548 			   return( 0 );
549 		case 'Y' : PLGP_linetype( s, x, y ); return( 0 );
550 		case 'Z' : PLGP_show(); pagenum++; virginpage = 1; return( 0 );
551 		case 'O' : PLGP_paper( (int)x ); return( 0 );
552 		case 'Q' : if( !virginpage ) { PLGP_show(); pagenum++; }
553 			   if( tightbb )
554 			     PLGP_trailer( pagenum - 1, bb_x1-0.05, bb_y1-0.05, bb_x2+0.05, bb_y2+0.05 );
555 			   else if( specifycrop == 1 )
556 			     PLGP_trailer( pagenum - 1, scx1, scy1, scx2, scy2 );
557 			   else if( specifycrop == 2 ) {
558 			     PLGP_trailer( pagenum - 1, (bb_x1-0.05)-scx1, (bb_y1-0.05)-scy1,
559 							(bb_x2+0.05)+scx2, (bb_y2+0.05)+scy2 );
560 				}
561 			   else
562 			     /* add 0.2" margin to be generous in cases of fat lines, etc. */
563 			     PLGP_trailer( pagenum - 1, bb_x1-0.2, bb_y1-0.2, bb_x2+0.2, bb_y2+0.2 );
564 			   Eresetbb();
565 			   return( 0 );
566 		}
567 #endif
568 	}
569 
570 else if( Edev == 'n' ) ; /* null device.. do nothing */
571 
572 else 	{
573 	if( Edev == '\0' ) return( Eerr( 12021, "Graphics subsystem never initialized", "" ) );
574 	else 	{
575 		char sdev[8];
576 		sprintf( sdev, "%c", Edev );
577 		return( Eerr( 12022, "Unrecognized graphic device code", sdev ) );
578 		}
579 	}
580 
581 
582 
583 
584 if( op == 'M' ) { lmlx = x; lmly = y; } /* remember most recent 'move' */
585 
586 
587 
588 /* figure approximate text dimensions */
589 /* if( Edev != 'x' && GL_member( op, "TCJ" )) { */  /* why not x?  scg 8/12/05 */
590 if( GL_member( op, "TCJ" )) {
591 	txt_width = strlen( s ) * Ecurtextwidth;
592 	txt_width *= globalscale;
593 	}
594 
595 if( keeping_bb ) {
596 	/* keep bounding box info (minima and maxima) */
597 	if( GL_member( op, "LP." ) ) {
598 		if( prev_op == 'M' ) Ebb( lmlx, lmly );
599 		Ebb( x, y );
600 		}
601 	/* normal (horizontal) text operations.  (vertical text below) */
602 	else if( op == 'T' && !vertchar ) {
603 		if( prev_op == 'M' ) Ebb( lmlx, lmly );
604 		Ebb( lmlx + txt_width+0.05, lmly + (Ecurtextheight*globalscale) );
605 		}
606 	else if( op == 'C' && !vertchar ) {
607 		Ebb( lmlx - ((txt_width/2.0)+0.05), lmly );
608 		Ebb( lmlx + ((txt_width/2.0)+0.05), lmly + (Ecurtextheight*globalscale) );
609 		}
610 	else if( op == 'J' && !vertchar ) {
611 		Ebb( lmlx - (txt_width+0.05), lmly );
612 		Ebb( lmlx, lmly + (Ecurtextheight*globalscale) );
613 		}
614 	}
615 
616 prev_op = op;
617 
618 
619 /* handle vertical text .. must be simulated for x windows;
620    also gets bounding box for vertical text operations (all devices) */
621 
622 if( vertchar && GL_member( op, "TCJ" )) verttextsim( op, s );
623 
624 return( 0 );
625 }
626 
627 #ifdef NOX11
628 int
PLG_getclick()629 PLG_getclick()
630 {
631 if( Edev == 'p' ) {
632 	Eshow(); /* eject page and return.. */
633 	return(0);
634 	}
635 else if( Edev == 'g' ) {
636 	Eendoffile();
637 	return( 0 );
638 	}
639 return( 0 );
640 }
641 #endif
642 
643 
644 /* ============================================= */
645 /* BB - keep an overall bounding box for the entire image.
646 	 Also call Echeckbb() to maintain nested object bounding boxes.. */
647 int
PLG_bb(x,y)648 PLG_bb( x, y )
649 double x, y;
650 {
651 if( keeping_bb ) {
652  	/* if( ( x < bb_x1 && x < 0.0 ) || (x > bb_x2 && x > 8.0 ) ) fprintf( pcodedebugfp, "draw out X = %g\n", x );
653 	 * if( ( y < bb_y1 && y < 0.0 ) || (y > bb_y2 && y > 8.0 ) ) fprintf( pcodedebugfp, "draw out Y = %g\n", y );
654 	 *	}
655 	 */
656 	if( x < bb_x1 ) { bb_x1 = x; if( pcodedebug == 2 ) { fprintf( pcodedebugfp, "(new x min %g)\n", x ); }}
657 	if( x > bb_x2 ) { bb_x2 = x; if( pcodedebug == 2 ) { fprintf( pcodedebugfp, "(new x max %g)\n", x ); }}
658 	if( y < bb_y1 ) { bb_y1 = y; if( pcodedebug == 2 ) { fprintf( pcodedebugfp, "(new y min %g)\n", y ); }}
659 	if( y > bb_y2 ) { bb_y2 = y; if( pcodedebug == 2 ) { fprintf( pcodedebugfp, "(new y max %g)\n", y ); }}
660 	}
661 if( keep_bb2 ) {
662 	if( x < bb2_x1 ) bb2_x1 = x;
663 	if( x > bb2_x2 ) bb2_x2 = x;
664 	if( y < bb2_y1 ) bb2_y1 = y;
665 	if( y > bb2_y2 ) bb2_y2 = y;
666 	}
667 
668 
669 return( 0 );
670 }
671 
672 /* ============================================== */
673 /* RESETBB - needed for multiple pages */
674 int
PLG_resetbb()675 PLG_resetbb()
676 {
677 bb_x1 = 999;
678 bb_y1 = 999;
679 bb_x2 = -999;
680 bb_y2 = -999;
681 return( 0 );
682 }
683 
684 /* ============================================= */
685 /* GETBB - get current bounding box.. */
686 int
PLG_getbb(xlo,ylo,xhi,yhi)687 PLG_getbb( xlo, ylo, xhi, yhi )
688 double *xlo, *ylo, *xhi, *yhi;
689 {
690 *xlo = bb_x1 / globalscale;
691 *ylo = bb_y1 / globalscaley;
692 *xhi = bb_x2 / globalscale;
693 *yhi = bb_y2 / globalscaley;
694 return( 0 );
695 }
696 
697 
698 /* ============================================== */
699 /* GETTEXTSIZE - get width and height of last text item.. */
700 
701 int
PLG_gettextsize(w,h)702 PLG_gettextsize( w, h )
703   double *w, *h;
704 {
705 *w = txt_width;
706 *h = Ecurtextheight;
707 return( 0 );
708 }
709 
710 /* ================================================ */
711 /* VERTTEXTSIM - vertical text bounding box, also simulation for X11 displays */
712 static int
verttextsim(op,s)713 verttextsim( op, s )
714 char op, s[];
715 {
716 double dist, y1, y2, x, y;
717 int len;
718 
719 len = strlen( s );
720 
721 if( Edev == 'x' ) dist = len * (Ecurtextheight * globalscale);
722 else dist = len * (Ecurtextwidth * globalscale);
723 
724 if( op == 'T' ) { y1 = lmly; y2 = lmly + dist; }
725 else if( op == 'C' ) { y1 = lmly - (dist/2); y2 = lmly + (dist/2); }
726 else if( op == 'J' ) { y1 = lmly - dist; y2 = lmly; }
727 if( Edev == 'x' ) x = lmlx - (Ecurtextwidth * globalscale);
728 else x = lmlx;
729 y = y2;
730 #ifndef NOX11
731 if( Edev == 'x' ) {
732 	int i;
733 	char let[4];
734 	double w;
735 	for( i = 0; i < len; i++ ) {
736 		sprintf( let, "%c", s[i] );
737 		PLGX_moveto( x, y );
738 		PLGX_text( let, &w );
739 		y -= (Ecurtextheight * globalscale);
740 		}
741 	}
742 #endif
743 Ebb( x-(Ecurtextheight*globalscale), y1 );
744 Ebb( x-(Ecurtextheight*globalscale), y2 );
745 return( 0 );
746 }
747 
748 
749 #ifdef SUSPENDED
750 /* ==================================================== */
751 /* SQUELCH_DISPLAY - 1 = squelch all display activity, 0 restore to normal */
752 /* Used to calculate bounding box without displaying */
753 /* handles nested calls. */
754 int
PLG_squelch_display(mode)755 PLG_squelch_display( mode )
756 int mode;
757 {
758 static int snest = 0;
759 if( mode == 1 ) {
760 	snest++;
761 	squelched = 1;
762 	}
763 else if( mode == 0 ) {
764 	if( snest > 0 ) snest--;
765 	if( snest == 0 )squelched = 0;
766 	}
767 }
768 
769 #endif
770 /* ==================================================== */
771 /* INIT_BB2 - a second bounding box available to app  */
772 int
PLG_init_bb2()773 PLG_init_bb2( )
774 {
775 keep_bb2 = 1;
776 bb2_x1 = 999.0; bb2_y1 = 999.0; bb2_x2 = -999.0; bb2_y2 = -999.0;
777 return( 0 );
778 }
779 /* ==================================================== */
780 /* GET_BB2 - get the second bounding box */
781 int
PLG_get_bb2(x1,y1,x2,y2)782 PLG_get_bb2( x1, y1, x2, y2 )
783 double *x1, *y1, *x2, *y2;
784 {
785 *x1 = bb2_x1/globalscale; *y1 = bb2_y1/globalscaley; *x2 = bb2_x2/globalscale; *y2 = bb2_y2/globalscaley;
786 /* keep_bb2 = 0; */
787 return( 0 );
788 }
789 
790 /* ==================================================== */
791 /* TIGHTBB - switch ON=don't add margin when doing final BB crop */
792 int
PLG_tightbb(mode)793 PLG_tightbb( mode )
794 int mode;
795 {
796 tightbb = mode;
797 return( 0 );
798 }
799 /* ==================================================== */
800 /* SPECIFYCROP - application may use this to specify crop rectangle */
801 int
PLG_specifycrop(mode,x1,y1,x2,y2)802 PLG_specifycrop( mode, x1, y1, x2, y2 )
803 int mode; /* 0=off   1=absolute values   2=relative to tightcrop values  */
804 double x1, y1, x2, y2;
805 {
806 specifycrop = mode;
807 if( specifycrop ) { scx1 = x1; scy1 = y1; scx2 = x2; scy2 = y2; }
808 return( 0 );
809 }
810 
811 #ifdef HOLD
812 /* ==================================================== */
813 /* SANEZONE - application may use this to specify a sane zone.  If pcode gets
814 	a request to draw outside of the sane zone, the draw operation is cancelled
815 	before calling the device drivers.  This addresses the issue where a wild
816 	draw request gets thru to GD which in turn creates an enormous image.  Added scg 5/8/06 */
817 int
PLG_sanezone(x1,y1,x2,y2)818 PLG_sanezone( x1, y1, x2, y2 )
819 double x1, y1, x2, y2;
820 {
821 /* currently only keeping x2,y2, since they seem to be the main problem (in GD anyway..) */
822 xsanemax = x2; ysanemax = y2;
823 sanezone = 1;
824 return( 0 );
825 }
826 #endif
827 
828 
829 /* ==================================================== */
830 /* GIFRECT - direct interface to GD driver for better efficiency on rectangles */
831 int
PLG_gifrect(xlo,yhi,xhi,ylo,color)832 PLG_gifrect( xlo, yhi, xhi, ylo, color )
833 double xlo, yhi, xhi, ylo;
834 char *color;
835 {
836 #ifndef NOGD
837 char oldcolor[COLORLEN];
838 strcpy( oldcolor, Ecurcolor );
839 strcpy( Ecurcolor, color );   /* so that Ecolor() knows to change back below.. scg 6/18/04 */
840 /* if( globalscale != 1.0 ) { .... changed scg 3/28/06 */
841 if( globalscale != 1.0 || globalscaley != 1.0 ) {
842 	xlo *= globalscale; ylo *= globalscaley;
843 	xhi *= globalscale; yhi *= globalscaley;
844 	}
845 PLGG_rect( xlo, yhi, xhi, ylo, color );
846 Ebb( xlo, ylo );
847 Ebb( xhi, yhi );
848 Ecolor( oldcolor );
849 #endif
850 return( 0 );
851 }
852 /* ==================================================== */
853 /* IMLOAD - for GD this loads the named image file...
854  *	  - for SVG this tells the svg driver to remember image filename for later
855  */
856 int
PLG_imload(filename,width,height)857 PLG_imload( filename, width, height )
858 char *filename;
859 int width, height;  /* optional, may be given if known here but not known at time of implace() (eg. symboldetails)... otherwise 0, 0 */
860 {
861 int stat;
862 stat = 1;
863 
864 if( globalscale != 1.0 || globalscaley != 1.0 ) {
865 	width = (int) (width * globalscale); height = (int) (height * globalscaley);
866 	}
867 
868 if( Edev == 'g' ) {
869 #ifndef NOGD
870   	stat = PLGG_imload( filename, width, height );
871 #endif
872 	}
873 else if( Edev == 's' && width != 0 && height != 0 ) {
874 #ifndef NOSVG
875 	stat = PLGS_setimg( filename, width, height );
876 #endif
877 	}
878 return( stat );
879 }
880 
881 /* ==================================================== */
882 /* IMPLACE - for GD this places the currently loaded image file at x,y (filename not used).
883  *	   - for SVG this adds an <image> tag to the svg output file (image filename
884  */
885 int
PLG_implace(x,y,filename,align,width,height)886 PLG_implace( x, y, filename, align, width, height )
887 double x, y;
888 char *filename; /* image file, used by svg only .. but not required if filename was set earlier using imload() */
889 char *align;    /* alignment, used by gd and svg */
890 int width, height;  /* render the image in this width and height (in pixels) */
891 {
892 int stat;
893 stat = 1;
894 if( globalscale != 1.0 || globalscaley != 1.0 ) {
895 	x *= globalscale; y *= globalscaley;
896 	width = (int) (width * globalscale); height = (int) (height * globalscaley);
897 	}
898 if( Edev == 'g' ) {
899 #ifndef NOGD
900 	stat = PLGG_implace( x, y, align, width, height );
901 #endif
902 	}
903 else if( Edev == 's' ) {
904 	if( line_drawing ) PLGS_stroke();
905         line_drawing = 0;
906 	width = (int) (width * 0.72 );  /* svg wants image dimensions in svg pixels (72 per inch) */
907 	height = (int) (height * 0.72 );
908 
909 #ifndef NOSVG
910 	stat = PLGS_showimg( filename, x, y, align, width, height );
911 #endif
912 	}
913 return( stat );
914 }
915 
916 
917 /* ===================================================== */
918 /* SETGLOBALSCALE - set global scale factor */
919 int
PLG_setglobalscale(sx,sy)920 PLG_setglobalscale( sx, sy )
921 double sx, sy;
922 {
923 if( sx < 0.01 || sx > 20.0 ) return( Eerr( 20815, "Invalid global scaling", "" ) );
924 if( sy < 0.01 || sy > 20.0 ) return( Eerr( 20815, "Invalid global scaling", "" ) );
925 globalscale = sx;
926 globalscaley = sy;
927 Estandard_lwscale = 1.0 * sx;
928 return( 0 );
929 }
930 /* ===================================================== */
931 /* GETGLOBALSCALE - get global scale factor */
932 int
PLG_getglobalscale(sx,sy)933 PLG_getglobalscale( sx, sy )
934 double *sx, *sy;
935 {
936 *sx = globalscale;
937 *sy = globalscaley;
938 return( 0 );
939 }
940 
941 /* ======================================= */
942 /* SETPOSTEROFS - set poster offset (paginated postscript only).
943    x, y are in absolute units, and are where the lower-left of the page will be.
944    So if I have a poster made of 4 8.5x11 sheets held portrait style,
945 	the lowerleft would use 0,0
946 	the lowerright would use 8,0
947 	the upperleft would use 0,10.5
948 	the lowerright would use 8,10.5
949    (8 and 10.5 are used because of print margins).
950    The four pages can then be trimmed w/ a paper cutter and butted up against
951    one another to create a poster.
952 */
953 int
PLG_setposterofs(x,y)954 PLG_setposterofs( x, y )
955 double x, y;
956 {
957 postermode = 1;
958 posterxo = x * (-1.0);
959 posteryo = y * (-1.0);
960 return( 0 );
961 }
962 
963 /* ========================================== */
964 /* PCODEDEBUG - turn on/off local debugging */
965 int
PLG_pcodedebug(mode,fp)966 PLG_pcodedebug( mode, fp )
967 int mode;
968 FILE *fp;  /* stream for diagnostic output */
969 {
970 pcodedebug = mode;
971 pcodedebugfp = fp;
972 return( 0 );
973 }
974 
975 /* =========================================== */
976 int
PLG_pcodeboundingbox(mode)977 PLG_pcodeboundingbox( mode )
978 int mode;
979 {
980 keeping_bb = mode;
981 return( 0 );
982 }
983 
984 /* =========================================== */
985 int
PLG_setdumpfile(dumpfile,filemode)986 PLG_setdumpfile( dumpfile, filemode )
987 char *dumpfile;
988 char *filemode;
989 {
990 dumpfp_closable = 0;
991 if( strcmp( dumpfile, "stdout" )==0 ) dumpfp = stdout;
992 else 	{
993 	dumpfp = fopen( dumpfile, filemode );
994 	if( dumpfp == NULL ) return( Eerr( 57202, "cannot open dump file", dumpfile ) );
995 	dumpfp_closable = 1;
996 	fprintf( dumpfp, "A 0 0 init-graphics\n" );  /* added scg 5/24/07 */
997 	}
998 ndumplines = 0;
999 return( 0 );
1000 }
1001 
1002 /* ========================================= */
1003 int
PLG_closedumpfile()1004 PLG_closedumpfile()
1005 {
1006 if( dumpfp_closable && dumpfp != NULL ) fclose( dumpfp );
1007 dumpfp = NULL;  /* can't assume on linux */
1008 return( 0 );
1009 }
1010 /* ========================================= */
1011 int
PLG_setdumpmaxlines(nlines)1012 PLG_setdumpmaxlines( nlines )
1013 int nlines;
1014 {
1015 dumpmax = nlines;
1016 return( 0 );
1017 }
1018 
1019 
1020 /* ======================================================= *
1021  * Copyright 1998-2008 Stephen C. Grubb                    *
1022  * http://ploticus.sourceforge.net                         *
1023  * Covered by GPL; see the file ./Copyright for details.   *
1024  * ======================================================= */
1025