1 /*
2 * Idraw Output
3 *
4 * Beorn Johnson
5 * Alan Kramer
6 * David Harrison
7 */
8
9 #include <stdlib.h>
10 #include <string.h>
11 #include <stdio.h>
12 #include <X11/Xlib.h>
13 #include "hard_devices.h"
14 #include "xgout.h"
15
16 #define HEIGHT 792
17 #define FIX(X) X = HEIGHT - X;
18
19 typedef struct {
20 char *title_font;
21 char *axis_font;
22 int title_size;
23 int axis_size;
24 FILE *strm;
25 } Info;
26
27 char *idraw_prologue[ ] = {
28 "%I Idraw 4",
29 "Begin",
30 "%I b u",
31 "%I cfg u",
32 "%I cbg u",
33 "%I f u",
34 "%I p u",
35 "%I t",
36 "[ 1 0 0 1 0 0 ] concat",
37 "/originalCTM matrix currentmatrix def",
38 0
39 };
40
41 /*
42 * Hardcopy Interface for Xgraph
43 *
44 * Major differences from first version:
45 * Four new parameters are passed to the device initialization routine:
46 * title_family, title_size, axis_family, and axis_size. See the
47 * description of xg_init() for details.
48 *
49 * Clipping is done automatically by xgraph. The xg_clip() routine
50 * is obsolete.
51 *
52 * The xg_line() routine has become the xg_seg() routine. It now
53 * draws segments rather than a series of lines.
54 *
55 * A new field (max_segs) in the device structure now specifies
56 * the maximum number of segments the device can handle in a group.
57 */
58
59
60 /*
61 * Adding an output device to xgraph
62 *
63 * Step 1
64 * Write versions of the following routines for your device:
65 * xg_init(), xg_text(), xg_seg(), xg_dot(), and xg_end().
66 * The interface and function of these routines are described
67 * in detail below. These routines should be named according
68 * to your device. For example, the initialization routine
69 * for the Postscript output device is psInit(). Also, name
70 * your source file after your device (e.g. the postscript
71 * routines are in the file ps.c). Instructions continue
72 * after the description of the interface routines.
73 */
74
75
76 void idrawText( );
77 void idrawDot( );
78 void idrawSeg( );
79 void idrawEnd( );
80
idrawInit(strm,width,height,title_family,title_size,axis_family,axis_size,flags,out_info,errmsg)81 int idrawInit(strm, width, height, title_family, title_size,
82 axis_family, axis_size, flags, out_info, errmsg)
83 FILE *strm; /* Output stream */
84 int width, height; /* Size of space (microns) */
85 char *title_family; /* Name of title font family */
86 double title_size; /* Title font height (points) */
87 char *axis_family; /* Name of axis font family */
88 double axis_size; /* Axis font height (points) */
89 int flags; /* Flags */
90 xgOut *out_info; /* Device info (RETURN) */
91 char errmsg[ERRBUFSIZE]; /* Error message area */
92 /*
93 * This routine is called by xgraph just before drawing is to
94 * begin. The desired size of the plot is given by `width'
95 * and `height'. The parameters `title_family', `title_size',
96 * `axis_family', and `axis_size' specify the names of the
97 * title and axis fonts and their vertical sizes (in points).
98 * These parameters can be ignored if your device does not
99 * support multiple fonts. Binary flags are specified in
100 * the `flags' field. These include:
101 * D_DOCU:
102 * If this flag is set, it indicates the user has specified that
103 * the output will be included in some larger document. Devices
104 * may choose to use this information to produce output that
105 * can be integrated into documents with less effort. For example,
106 * the Postscript output routines produce bounding box information
107 * when this flag is set.
108 * The routine should fill in all of the fields of `out_info' with
109 * appropriate values. The values are described below:
110 * area_w, area_h:
111 * Size of the drawing space in device coordinates.
112 * This should take in account the requested area
113 * given by `width', and `height'.
114 * bdr_pad:
115 * Xgraph will leave this number of device coordinates around
116 * all of the outer edges of the graph.
117 * axis_pad:
118 * Additional space around axis labels (in devcoords)
119 * so that the labels do not appear crowded.
120 * legend_pad:
121 * Space (in devcoords) from the top of legend text to
122 * the representative line drawn above the legend text.
123 * tick_len:
124 * Size of a tick mark placed on axis (in devcoords)
125 * axis_width:
126 * An estimate of the width of a large character in
127 * the axis font (in devcoords). This can be an overestimate. An
128 * underestimate may produce bad results.
129 * axis_height:
130 * An estimate of the height of a large character in
131 * the axis labeling font (in devcoords).
132 * title_width, title_height:
133 * Same as above except for the title font.
134 * max_segs:
135 * Due to buffering constraints, some devices may not be able to
136 * handle massive segment lists. This parameter tells xgraph not
137 * to send more than `max_segs' segments in one request.
138 * Output to the device should be written to the stream `strm'.
139 * The functions are described individually below. After filling
140 * in the parameters and setting the function pointers, the routine
141 * should initialize its drawing state and store any extra needed
142 * information in `user_state'. This value will be passed to all
143 * other routines during the drawing sequence. If the device
144 * cannot initialize, it should return a zero status and fill
145 * `errmsg' with an informative error message.
146 */
147 {
148 Info *idraw_info;
149 char **l;
150 double scx, scy;
151
152 idraw_info = (Info *) malloc(sizeof(*idraw_info));
153
154 for (l = idraw_prologue; *l; l++)
155 fprintf(strm, "%s\n", *l);
156
157 out_info -> dev_flags = 0;
158 scx = width / 612;
159 scy = height / 792.0;
160 if (scx > scy) {
161 scy /= scx;
162 scx = 1;
163 }
164 else {
165 scx /= scy;
166 scy = 1;
167 }
168 out_info -> bdr_pad = title_size/4;
169 out_info -> axis_pad = 2.0 * axis_size;
170 out_info -> legend_pad = 0;
171
172 out_info -> area_w = width * 0.00283; /* pts per micron */
173 out_info -> area_h = height * 0.00283;
174
175 out_info -> tick_len = axis_size;
176 out_info -> axis_height = axis_size;
177 out_info -> title_height = title_size;
178 out_info -> axis_width = (axis_size*5.0) / 12.0;
179 out_info -> title_width = (title_size*5.0) / 12.0;
180 out_info -> max_segs = 100;
181 out_info -> xg_text = idrawText;
182 out_info -> xg_seg = idrawSeg;
183 out_info -> xg_dot = idrawDot;
184 out_info -> xg_end = idrawEnd;
185 out_info -> user_state = (char *) idraw_info;
186
187 idraw_info -> title_font = title_family;
188 idraw_info -> axis_font = axis_family;
189 idraw_info -> title_size = title_size;
190 idraw_info -> axis_size = axis_size;
191 idraw_info -> strm = strm;
192 return 1;
193 }
194
195 /* Text justifications */
196 #define T_CENTER 0
197 #define T_LEFT 1
198 #define T_UPPERLEFT 2
199 #define T_TOP 3
200 #define T_UPPERRIGHT 4
201 #define T_RIGHT 5
202 #define T_LOWERRIGHT 6
203 #define T_BOTTOM 7
204 #define T_LOWERLEFT 8
205
206 /* Text styles */
207 #define T_AXIS 0
208 #define T_TITLE 1
209
idraw_just(x,y,just,size,len)210 static void idraw_just(x, y, just, size, len)
211 int *x, *y; /* Given location (lower left) */
212 int just; /* Justification */
213 int size; /* Size in points */
214 int len; /* Number of chars */
215 /*
216 * Unfortunately, idraw really can't display text with a justification.
217 * This is a horrible hack to try to get around the problem. It tries
218 * to compute a rough bounding box for the text based on the text height
219 * and the string length and offset `x,y' appropriately for the justification.
220 * This is only a hack...
221 */
222 {
223 int t_width, t_height;
224
225 t_height = size;
226 t_width = (size * len * 5)/12; /* Horrible estimate */
227
228 switch (just) {
229 case T_CENTER:
230 *x -= t_width/2;
231 *y += t_height/2;
232 break;
233 case T_LEFT:
234 *y += t_height/2;
235 break;
236 case T_UPPERLEFT:
237 /* nothing */
238 break;
239 case T_TOP:
240 *x -= t_width/2;
241 break;
242 case T_UPPERRIGHT:
243 *x -= t_width;
244 break;
245 case T_RIGHT:
246 *x -= t_width;
247 *y += t_height/2;
248 break;
249 case T_LOWERRIGHT:
250 *x -= t_width;
251 *y += t_height;
252 break;
253 case T_BOTTOM:
254 *x -= t_width/2;
255 *y += t_height;
256 break;
257 case T_LOWERLEFT:
258 *y += t_height;
259 break;
260 }
261
262 /*
263 * Also, idraw seems to put a space above all text it draws.
264 * The computation below compensates for this.
265 */
266 *y += (size/3);
267 }
268
idrawText(user_state,x,y,text,just,style)269 void idrawText(user_state, x, y, text, just, style)
270 char *user_state; /* Value set in xg_init */
271 int x, y; /* Text position (pixels) */
272 char *text; /* Null terminated text */
273 int just; /* Justification (above) */
274 int style; /* Text style (above) */
275 /*
276 * This routine should draw text at the indicated position using
277 * the indicated justification and style. The justification refers
278 * to the location of the point in reference to the text. For example,
279 * if just is T_LOWERLEFT, (x,y) should be located at the lower left
280 * edge of the text string.
281 */
282 {
283 char *font;
284 int size;
285 Info *idraw = (Info *) user_state;
286
287 FIX(y);
288 font = style == T_AXIS ? idraw -> axis_font:
289 idraw -> title_font;
290 size = style == T_AXIS ? idraw -> axis_size:
291 idraw -> title_size;
292
293 idraw_just(&x, &y, just, size, strlen(text));
294
295 fprintf(idraw -> strm, "Begin %%I Text\n");
296 fprintf(idraw -> strm, "%%I cfg Black\n");
297 fprintf(idraw -> strm, "0 0 0 SetCFg\n");
298 fprintf(idraw -> strm, "%%I f *%s*-%d-*\n", font, size);
299 fprintf(idraw -> strm, "/%s %d SetF\n", font, size);
300 fprintf(idraw -> strm, "%%I t\n");
301 fprintf(idraw -> strm, "[ 1 0 0 1 %d %d ] concat\n", x, y);
302 fprintf(idraw -> strm, "%%I\n");
303 fprintf(idraw -> strm, "[\n");
304 fprintf(idraw -> strm, "(%s)\n", text);
305 fprintf(idraw -> strm, "] Text\n");
306 fprintf(idraw -> strm, "End\n");
307
308 }
309
310 /* Line Styles */
311 #define L_AXIS 0
312 #define L_ZERO 1
313 #define L_VAR 2
314
idrawSeg(user_state,ns,seglist,width,style,lappr,color)315 void idrawSeg(user_state, ns, seglist, width, style, lappr, color)
316 char *user_state; /* Value set in xg_init */
317 int ns; /* Number of segments */
318 XSegment *seglist; /* X array of segments */
319 int width; /* Width of lines */
320 int style; /* See above */
321 int lappr; /* Line appearence */
322 int color; /* Line color (if any) */
323 /*
324 * This routine draws a number of line segments at the points
325 * given in `seglist'. Note that contiguous segments need not share
326 * endpoints but often do. All segments should be `width' devcoords wide
327 * and drawn in style `style'. If `style' is L_VAR, the parameters
328 * `color' and `lappr' should be used to draw the line. Both
329 * parameters vary from 0 to 7. If the device is capable of
330 * color, `color' varies faster than `style'. If the device
331 * has no color, `style' will vary faster than `color' and
332 * `color' can be safely ignored. However, if the
333 * the device has more than 8 line appearences, the two can
334 * be combined to specify 64 line style variations.
335 * Xgraph promises not to send more than the `max_segs' in the
336 * xgOut structure passed back from xg_init().
337 */
338 {
339 Info *idraw = (Info *) user_state;
340 short to_style;
341 int i, j, k;
342
343 static unsigned short style_list[ ] = {
344 0xffff, 0xf0f0, 0xcccc, 0xaaaa,
345 0xf060, 0xf198, 0x7f55, 0xfff0,
346 };
347
348 to_style = style == L_AXIS ? 65535
349 : style == L_ZERO ? 65535
350 : style_list[lappr];
351
352 for (i = 0; i < ns; i++) {
353 FIX(seglist[i].y1);
354 FIX(seglist[i].y2);
355 }
356
357 for (i = 0; i < ns; i = j) {
358
359 for (j = i + 1; j < ns
360 && seglist[j - 1].x2 == seglist[j].x1
361 && seglist[j - 1].y2 == seglist[j].y1;
362 j++) ;
363
364 fprintf(idraw -> strm, "Begin %%I MLine\n");
365 fprintf(idraw -> strm, "%%I b %d\n", to_style);
366 fprintf(idraw -> strm, "%d 0 0 [", width);
367 /* fprintf(idraw -> strm, "%d"); */
368 fprintf(idraw -> strm, "] 0 SetB\n");
369 fprintf(idraw -> strm, "%%I cfg Black\n");
370 fprintf(idraw -> strm, "0 0 0 SetCFg\n");
371 fprintf(idraw -> strm, "%%I cbg White\n");
372 fprintf(idraw -> strm, "1 1 1 SetCBg\n");
373 fprintf(idraw -> strm, "none SetP %%I p n\n");
374 fprintf(idraw -> strm, "%%I t u\n");
375
376 fprintf(idraw -> strm, "%%I %d\n", j - i + 1);
377
378 for (k = i; k < j; k++)
379 fprintf(idraw -> strm, "%d %d\n",
380 seglist[k].x1, seglist[k].y1);
381
382 fprintf(idraw -> strm, "%d %d\n",
383 seglist[k - 1].x2, seglist[k - 1].y2);
384
385 fprintf(idraw -> strm, "%d MLine\n", j - i + 1);
386 fprintf(idraw -> strm, "End\n");
387 }
388
389
390 }
391
392 /* Marker styles */
393 #define P_PIXEL 0
394 #define P_DOT 1
395 #define P_MARK 2
396
idrawDot(user_state,x,y,style,type,color)397 void idrawDot(user_state, x, y, style, type, color)
398 char *user_state; /* Value set in xg_init */
399 int x, y; /* Location in pixel units */
400 int style; /* Dot style */
401 int type; /* Type of marker */
402 int color; /* Marker color (if any) */
403 /*
404 * This routine should draw a marker at location `x,y'. If the
405 * style is P_PIXEL, the dot should be a single pixel. If
406 * the style is P_DOT, the dot should be a reasonably large
407 * dot. If the style is P_MARK, it should be a distinguished
408 * mark which is specified by `type' (0-7). If the output
409 * device is capable of color, the marker should be drawn in
410 * `color' (0-7) which corresponds with the color for xg_line.
411 */
412 {
413 }
414
idrawEnd(user_state)415 void idrawEnd(user_state)
416 char *user_state;
417 /*
418 * This routine is called after a drawing sequence is complete.
419 * It can be used to clean up the user state and set the device
420 * state appropriately. This routine is optional in the structure.
421 */
422 {
423 Info *idraw = (Info *) user_state;
424
425 fprintf(idraw -> strm, "End %%I eop\n");
426 fclose(idraw -> strm);
427 }
428
429 /*
430 * Adding an output device to xgraph
431 *
432 * Step 2
433 * Edit the file hard_devices.c. Declare your initialization
434 * function and add your device to the list of devices,
435 * hard_devices[]. The structure hard_dev is described below:
436 */
437
438 #ifdef notdef
439 extern int idrawInit( );
440
441 struct hard_dev idraw = {
442 "idraw format", idrawInit,
443 0, ".clipboard", 0,
444 25, "Times-Bold", 18, "Times", 12
445 };
446 #endif
447
448 /*
449 * dev_spec:
450 * The dev_spec field should be a command that directly outputs to
451 * your device. The command should contain one %s directive that
452 * will be filled in with the name of the device from the hardcopy
453 * dialog.
454 * dev_file:
455 * The default file to write output to if the user selects `To File'.
456 * dev_printer:
457 * The default printer to write output to if the user selects
458 * `To Device'.
459 * dev_max_dim:
460 * The default maximum dimension for the device in centimeters.
461 * dev_title_font, dev_title_size:
462 * The default title font and size. Sizes are specified in
463 * points (1/72 inch).
464 * dev_axis_font, dev_axis_size:
465 * The default axis font and size.
466 */
467
468 /*
469 * Adding an output device to xgraph
470 *
471 * Step 3
472 * Edit the file Makefile. Add your source file to the SRC variable
473 * and the corresponding object file to the OBJ variable. Finally,
474 * remake xgraph. Your device should now be available in the
475 * hardcopy dialog.
476 */
477
478