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