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