1 #include "snd.h"
2 
3 /* create Postscript version of graph */
4 
5 
6 static char *pbuf = NULL;
7 static int bbx = 0, bby = 0, bx0 = 0, by0 = 0;
8 static int ps_fd;
9 
10 static char *nbuf = NULL;
11 static int nbuf_ctr = 0;
12 #define NBUF_SIZE 8192
13 
14 
ps_flush(int fd)15 static void ps_flush(int fd)
16 {
17   if (nbuf_ctr > 0)
18     {
19       ssize_t bytes;
20       bytes = write(fd, nbuf, nbuf_ctr);
21       if (bytes == 0) fprintf(stderr, "ps_flush write error");
22       nbuf_ctr = 0;
23       memset((void *)nbuf, 0, NBUF_SIZE);
24     }
25 }
26 
27 
ps_write(const char * buf)28 static void ps_write(const char *buf)
29 {
30   /* sending tiny buffers over the net is a total loss -- grab a bunch at a time */
31   int i, len;
32   if (!nbuf)
33     {
34       nbuf = (char *)calloc(NBUF_SIZE, sizeof(char));
35       nbuf_ctr = 0;
36     }
37   len = mus_strlen(buf);
38   for (i = 0; i < len; i++)
39     {
40       nbuf[nbuf_ctr++] = buf[i];
41       if (nbuf_ctr == NBUF_SIZE) ps_flush(ps_fd);
42     }
43   memset((void *)buf, 0, PRINT_BUFFER_SIZE);
44 }
45 
46 
start_ps_graph(const char * output,const char * title)47 static int start_ps_graph(const char *output, const char *title)
48 {
49   ps_fd = CREAT(output, 0666);
50   if (ps_fd == -1) return(-1);
51   if (!pbuf) pbuf = (char *)calloc(PRINT_BUFFER_SIZE, sizeof(char));
52   bbx = 0;
53   bby = 0;
54 
55   snprintf(pbuf, PRINT_BUFFER_SIZE,
56 	       "%%!PS-Adobe-2.0 EPSF-2.0\n%%%%Title: %s\n%%%%Creator: Snd: %s\n%%%%CreationDate: %s",
57 	       title,
58 	       SND_DATE,
59 	       snd_local_time());
60   ps_write(pbuf);
61   snprintf(pbuf, PRINT_BUFFER_SIZE,
62 	       "\n%%%%BoundingBox:(atend)\n%%%%EndComments\n%%%%EndProlog\n%%%%Page: 1 1\n");
63   ps_write(pbuf);
64   snprintf(pbuf, PRINT_BUFFER_SIZE,
65 	       "/LT {lineto} bind def\n/RF {rectfill} bind def\n/RG {setrgbcolor} bind def\n/NAF {newpath arc fill} bind def\n\n");
66   ps_write(pbuf);
67 
68   snprintf(pbuf, PRINT_BUFFER_SIZE,
69 	       "gsave [%.3f 0.0 0.0 %.3f %.3f %.3f] concat\n\n",
70 	       eps_size(ss), eps_size(ss), eps_left_margin(ss), eps_bottom_margin(ss));
71   ps_write(pbuf);
72   return(0);
73 }
74 
75 
ps_graph(chan_info * cp,int x0,int y0)76 static void ps_graph(chan_info *cp, int x0, int y0)
77 {
78   cp->printing = PRINTING;
79   bx0 = x0;
80   by0 = y0;
81   display_channel_data(cp);
82   cp->printing = NOT_PRINTING;
83 }
84 
85 
end_ps_graph(void)86 static void end_ps_graph(void)
87 {
88   snprintf(pbuf, PRINT_BUFFER_SIZE,
89 	       "%s\nshowpage\n%%%%Trailer\n%%%%BoundingBox: %d %d %d %d\n",
90 	       ((eps_left_margin(ss) != 0) || (eps_bottom_margin(ss) != 0)) ? "\ngrestore" : "",
91 	       0, 0,
92 	       (int)(bbx + 10 + eps_left_margin(ss)),
93 	       (int)(bby + 10 + eps_bottom_margin(ss)));
94   ps_write(pbuf);
95   ps_flush(ps_fd);
96   snd_close(ps_fd, "eps file");
97   if (nbuf)
98     {
99       free(nbuf);
100       nbuf = NULL;
101       nbuf_ctr = 0;
102     }
103 }
104 
105 
106 /* the x and y values in the "points" are relative to grf_x/y:
107  *
108  *  x: ap->x_axis_x0 + (val - ap->x0) * ap->x_scale
109  *  y: ap->y_axis_y0 + (val * MUS_FIX_TO_FLOAT - ap->y0) * ap->y_scale
110  *
111  * kept here in full precision since normally printers have much higher resolution than screens
112  */
113 
reflect_y(axis_info * ap,int y)114 static int reflect_y(axis_info *ap, int y)
115 {
116   return(ap->height - y);
117 }
118 
119 
120 static mus_float_t *xpts = NULL;
121 static mus_float_t *ypts = NULL;
122 static mus_float_t *ypts1 = NULL;
123 
ps_allocate_grf_points(void)124 void ps_allocate_grf_points(void)
125 {
126   if (!xpts) xpts = (mus_float_t *)calloc(POINT_BUFFER_SIZE, sizeof(mus_float_t));
127   if (!ypts) ypts = (mus_float_t *)calloc(POINT_BUFFER_SIZE, sizeof(mus_float_t));
128   if (!ypts1) ypts1 = (mus_float_t *)calloc(POINT_BUFFER_SIZE, sizeof(mus_float_t));
129 }
130 
131 
ps_set_grf_points(double x,int j,mus_float_t ymin,mus_float_t ymax)132 void ps_set_grf_points(double x, int j, mus_float_t ymin, mus_float_t ymax)
133 {
134   xpts[j] = x;
135   ypts[j] = ymin;
136   ypts1[j] = ymax;
137 }
138 
139 
ps_set_grf_point(double x,int j,mus_float_t y)140 void ps_set_grf_point(double x, int j, mus_float_t y)
141 {
142   xpts[j] = x;
143   ypts[j] = y;
144 }
145 
146 
ps_grf_x(axis_info * ap,mus_float_t val)147 static mus_float_t ps_grf_x(axis_info *ap, mus_float_t val)
148 {
149   return(ap->x_axis_x0 + bx0 + (val - ap->x0) * ap->x_scale);
150 }
151 
152 
ps_grf_y(axis_info * ap,mus_float_t val)153 static mus_float_t ps_grf_y(axis_info *ap, mus_float_t val)
154 {
155   return(by0 + ap->height - (ap->y_axis_y0 + (val - ap->y0) * ap->y_scale));
156 }
157 
158 
ps_draw_lines(axis_info * ap,int j,mus_float_t * xpts,mus_float_t * ypts)159 static void ps_draw_lines(axis_info *ap, int j, mus_float_t *xpts, mus_float_t *ypts)
160 {
161   int i;
162   snprintf(pbuf, PRINT_BUFFER_SIZE, " %.2f %.2f moveto\n", ps_grf_x(ap, xpts[0]), ps_grf_y(ap, ypts[0]));
163   ps_write(pbuf);
164   for (i = 1; i < j; i++)
165     {
166       snprintf(pbuf, PRINT_BUFFER_SIZE, " %.2f %.2f lineto\n", ps_grf_x(ap, xpts[i]), ps_grf_y(ap, ypts[i]));
167       ps_write(pbuf);
168     }
169   snprintf(pbuf, PRINT_BUFFER_SIZE, " stroke\n");
170   ps_write(pbuf);
171 }
172 
173 
ps_draw_dots(axis_info * ap,int j,mus_float_t * xpts,mus_float_t * ypts,int dot_size)174 static void ps_draw_dots(axis_info *ap, int j, mus_float_t *xpts, mus_float_t *ypts, int dot_size)
175 {
176   int i;
177   mus_float_t arc_size;
178   arc_size = .5 * dot_size; /* radius here, diameter in X */
179   for (i = 0; i < j; i++)
180     {
181       snprintf(pbuf, PRINT_BUFFER_SIZE, " %.2f %.2f %.2f 0 360 NAF\n", ps_grf_x(ap, xpts[i]), ps_grf_y(ap, ypts[i]), arc_size);
182       ps_write(pbuf);
183     }
184 }
185 
186 
ps_fill_polygons(axis_info * ap,int j,mus_float_t * xpts,mus_float_t * ypts,mus_float_t y0)187 static void ps_fill_polygons(axis_info *ap, int j, mus_float_t *xpts, mus_float_t *ypts, mus_float_t y0)
188 {
189   int i;
190   for (i = 1; i < j; i++)
191     {
192       snprintf(pbuf, PRINT_BUFFER_SIZE, " %.2f %.2f moveto\n", ps_grf_x(ap, xpts[i - 1]), ps_grf_y(ap, ypts[i - 1]));
193       ps_write(pbuf);
194       snprintf(pbuf, PRINT_BUFFER_SIZE, " %.2f %.2f lineto\n", ps_grf_x(ap, xpts[i]), ps_grf_y(ap, ypts[i]));
195       ps_write(pbuf);
196       snprintf(pbuf, PRINT_BUFFER_SIZE, " %.2f %.2f lineto\n", ps_grf_x(ap, xpts[i]), ps_grf_y(ap, y0));
197       ps_write(pbuf);
198       snprintf(pbuf, PRINT_BUFFER_SIZE, " %.2f %.2f lineto\n", ps_grf_x(ap, xpts[i - 1]), ps_grf_y(ap, y0));
199       ps_write(pbuf);
200       snprintf(pbuf, PRINT_BUFFER_SIZE, " closepath fill\n");
201       ps_write(pbuf);
202     }
203 }
204 
205 
ps_draw_grf_points(axis_info * ap,int j,mus_float_t y0,graph_style_t graph_style,int dot_size)206 void ps_draw_grf_points(axis_info *ap, int j, mus_float_t y0, graph_style_t graph_style, int dot_size)
207 {
208   switch (graph_style)
209     {
210     case GRAPH_LINES:
211     default:
212       ps_draw_lines(ap, j, xpts, ypts);
213       break;
214     case GRAPH_DOTS:
215       ps_draw_dots(ap, j, xpts, ypts, dot_size);
216       break;
217     case GRAPH_FILLED:
218       ps_fill_polygons(ap, j, xpts, ypts, y0);
219       break;
220     case GRAPH_DOTS_AND_LINES:
221       ps_draw_lines(ap, j, xpts, ypts);
222       if (dot_size > 1) ps_draw_dots(ap, j, xpts, ypts, dot_size);
223       break;
224     case GRAPH_LOLLIPOPS:
225       {
226 	int i, gy0, size8, size4;
227 	if (dot_size > 1) ps_draw_dots(ap, j, xpts, ypts, dot_size);
228 	gy0 = (int)ps_grf_y(ap, y0);
229 	size8 = dot_size / 8;
230 	size4 = dot_size / 4;
231 	if (size4 < 1) size4 = 1;
232 	for (i = 0; i < j; i++)
233 	  {
234 	    snprintf(pbuf, PRINT_BUFFER_SIZE, " %.2f %.2f %.2f %.2f RF\n",
235 			 ps_grf_x(ap, xpts[i]) - size8,
236 			 (double)gy0,
237 			 (double)size4,
238 			 ps_grf_y(ap, ypts[i]) - gy0);
239 	    ps_write(pbuf);
240 	  }
241       }
242       break;
243     }
244 }
245 
246 
ps_draw_both_grf_points(axis_info * ap,int j,graph_style_t graph_style,int dot_size)247 void ps_draw_both_grf_points(axis_info *ap, int j, graph_style_t graph_style, int dot_size)
248 {
249   int i, size8, size4;
250   switch (graph_style)
251     {
252     case GRAPH_LINES:
253     default:
254       ps_draw_lines(ap, j, xpts, ypts);
255       ps_draw_lines(ap, j, xpts, ypts1);
256       break;
257     case GRAPH_DOTS:
258       ps_draw_dots(ap, j, xpts, ypts, dot_size);
259       ps_draw_dots(ap, j, xpts, ypts1, dot_size);
260       break;
261     case GRAPH_FILLED:
262       for (i = 1; i < j; i++)
263 	{
264 	  snprintf(pbuf, PRINT_BUFFER_SIZE, " %.2f %.2f moveto\n", ps_grf_x(ap, xpts[i - 1]), ps_grf_y(ap, ypts[i - 1]));
265 	  ps_write(pbuf);
266 	  snprintf(pbuf, PRINT_BUFFER_SIZE, " %.2f %.2f lineto\n", ps_grf_x(ap, xpts[i]), ps_grf_y(ap, ypts[i]));
267 	  ps_write(pbuf);
268 	  snprintf(pbuf, PRINT_BUFFER_SIZE, " %.2f %.2f lineto\n", ps_grf_x(ap, xpts[i]), ps_grf_y(ap, ypts1[i]));
269 	  ps_write(pbuf);
270 	  snprintf(pbuf, PRINT_BUFFER_SIZE, " %.2f %.2f lineto\n", ps_grf_x(ap, xpts[i - 1]), ps_grf_y(ap, ypts1[i - 1]));
271 	  ps_write(pbuf);
272 	  snprintf(pbuf, PRINT_BUFFER_SIZE, " closepath fill\n");
273 	  ps_write(pbuf);
274 	}
275       break;
276     case GRAPH_DOTS_AND_LINES:
277       if (dot_size > 1)
278 	{
279 	  ps_draw_dots(ap, j, xpts, ypts, dot_size);
280 	  ps_draw_dots(ap, j, xpts, ypts1, dot_size);
281 	}
282       ps_draw_lines(ap, j, xpts, ypts);
283       ps_draw_lines(ap, j, xpts, ypts1);
284       break;
285     case GRAPH_LOLLIPOPS:
286       if (dot_size > 1)
287 	{
288 	  ps_draw_dots(ap, j, xpts, ypts, dot_size);
289 	  ps_draw_dots(ap, j, xpts, ypts1, dot_size);
290 	}
291       size8 = dot_size / 8;
292       size4 = dot_size / 4;
293       if (size4 < 1) size4 = 1;
294       for (i = 0; i < j; i++)
295 	{
296 	  snprintf(pbuf, PRINT_BUFFER_SIZE, " %.2f %.2f %.2f %.2f RF\n",
297 		       ps_grf_x(ap, xpts[i]) - size8,
298 		       ps_grf_y(ap, ypts[i]),
299 		       (double)size4,
300 		       ps_grf_y(ap, ypts1[i]) - ps_grf_y(ap, ypts[i]));
301 	  ps_write(pbuf);
302 	}
303 
304       break;
305     }
306 }
307 
308 
309 static int last_color = -1;
310 
ps_draw_sono_rectangle(axis_info * ap,int color,mus_float_t x,mus_float_t y,mus_float_t width,mus_float_t height)311 void ps_draw_sono_rectangle(axis_info *ap, int color, mus_float_t x, mus_float_t y, mus_float_t width, mus_float_t height)
312 {
313   rgb_t r, g, b;
314   if (last_color != color)
315     {
316       get_current_color(color_map(ss), color, &r, &g, &b);
317       last_color = color;
318       snprintf(pbuf, PRINT_BUFFER_SIZE, " %.2f %.2f %.2f RG\n", rgb_to_float(r), rgb_to_float(g), rgb_to_float(b));
319       ps_write(pbuf);
320     }
321   snprintf(pbuf, PRINT_BUFFER_SIZE, " %.1f %.1f %.2f %.2f RF\n", ps_grf_x(ap, x) + 2, ps_grf_y(ap, y), width, height);
322   ps_write(pbuf);
323 }
324 
325 
ps_reset_color(void)326 void ps_reset_color(void)
327 {
328   snprintf(pbuf, PRINT_BUFFER_SIZE, " 0 setgray\n");
329   ps_write(pbuf);
330   last_color = -1;
331 }
332 
333 
334 #if USE_MOTIF
ps_set_color(color_t color)335 static void ps_set_color(color_t color)
336 {
337   Colormap cmap;
338   XColor tmp_color;
339   Display *dpy;
340   dpy = XtDisplay(main_shell(ss));
341   cmap = DefaultColormap(dpy, DefaultScreen(dpy));
342   tmp_color.flags = DoRed | DoGreen | DoBlue;
343   tmp_color.pixel = color;
344   XQueryColor(dpy, cmap, &tmp_color);
345   snprintf(pbuf, PRINT_BUFFER_SIZE, " %.2f %.2f %.2f RG\n",
346 	       rgb_to_float(tmp_color.red),
347 	       rgb_to_float(tmp_color.green),
348 	       rgb_to_float(tmp_color.blue));
349   ps_write(pbuf);
350   last_color = -1;
351 }
352 #endif
353 
354 
ps_bg(axis_info * ap,graphics_context * ax)355 void ps_bg(axis_info *ap, graphics_context *ax)
356 {
357   /* get background color, fill graph, then set foreground for axis */
358   chan_info *cp;
359   cp = ap->cp;
360 #if USE_MOTIF
361   {
362     XGCValues gv;
363     XGetGCValues(main_display(ss), ax->gc, GCBackground, &gv);
364     ps_set_color(gv.background);
365   }
366 #endif
367   snprintf(pbuf, PRINT_BUFFER_SIZE, " %d %d %d %d RF\n",
368 	       ap->graph_x0 + bx0, ap->y_offset + by0, ap->width, ap->height);
369   ps_write(pbuf);
370   ps_fg(cp, ax);
371 }
372 
373 
ps_fg(chan_info * cp,graphics_context * ax)374 void ps_fg(chan_info *cp, graphics_context *ax)
375 {
376   /* set foreground color for subsequent line drawing */
377 #if USE_MOTIF
378   ps_set_color(get_foreground_color(ax));
379 #endif
380 }
381 
382 
383 /* the rest are in real coordinates except upsidedown from PS point of view */
384 
ps_draw_line(axis_info * ap,int x0,int y0,int x1,int y1)385 void ps_draw_line(axis_info *ap, int x0, int y0, int x1, int y1)
386 {
387   int py0, py1, px0, px1;
388   px0 = x0 + bx0;
389   px1 = x1 + bx0;
390   py0 = reflect_y(ap, y0) + by0;
391   py1 = reflect_y(ap, y1) + by0;
392   if (px0 > bbx) bbx = px0;
393   if (px1 > bbx) bbx = px1;
394   if (py0 > bby) bby = py0;
395   if (py1 > bby) bby = py1;
396   snprintf(pbuf, PRINT_BUFFER_SIZE, " 0 setlinewidth %d %d moveto %d %d lineto stroke\n", px0, py0, px1, py1);
397   ps_write(pbuf);
398 }
399 
400 
ps_draw_spectro_line(axis_info * ap,int color,mus_float_t x0,mus_float_t y0,mus_float_t x1,mus_float_t y1)401 void ps_draw_spectro_line(axis_info *ap, int color, mus_float_t x0, mus_float_t y0, mus_float_t x1, mus_float_t y1)
402 {
403   /* these are in local coords */
404   rgb_t r, g, b;
405   if (last_color != color)
406     {
407       get_current_color(color_map(ss), color, &r, &g, &b);
408       last_color = color;
409       snprintf(pbuf, PRINT_BUFFER_SIZE, " %.2f %.2f %.2f RG\n", rgb_to_float(r), rgb_to_float(g), rgb_to_float(b));
410       ps_write(pbuf);
411     }
412   ps_draw_line(ap, (int)x0, (int)y0, (int)x1, (int)y1);
413 }
414 
415 
ps_fill_rectangle(axis_info * ap,int x0,int y0,int width,int height)416 void ps_fill_rectangle(axis_info *ap, int x0, int y0, int width, int height)
417 {
418   int py0, py1, px0, px1;
419   px0 = x0 + bx0;
420   px1 = x0 + bx0 + width;
421   py0 = reflect_y(ap, y0) + by0;
422   py1 = reflect_y(ap, y0 + height) + by0;
423   if (px0 > bbx) bbx = px0;
424   if (px1 > bbx) bbx = px1;
425   if (py0 > bby) bby = py0;
426   if (py1 > bby) bby = py1;
427   snprintf(pbuf, PRINT_BUFFER_SIZE, " %d %d %d %d RF\n", px0, py0, width, -height);
428   ps_write(pbuf);
429 }
430 
431 
ps_draw_string(axis_info * ap,int x0,int y0,const char * str)432 void ps_draw_string(axis_info *ap, int x0, int y0, const char *str)
433 {
434   int px0, py0;
435   px0 = x0 + bx0;
436   py0 = reflect_y(ap, y0) + by0;
437   if (px0 > bbx) bbx = px0;
438   if (py0 > bby) bby = py0;
439   snprintf(pbuf, PRINT_BUFFER_SIZE, " %d %d moveto (%s) show\n", px0, py0, str);
440   ps_write(pbuf);
441 }
442 
443 
ps_set_number_font(void)444 void ps_set_number_font(void)
445 {
446   snprintf(pbuf, PRINT_BUFFER_SIZE, " /Courier findfont 15 scalefont setfont\n");
447   ps_write(pbuf);
448 }
449 
450 
ps_set_label_font(void)451 void ps_set_label_font(void)
452 {
453   snprintf(pbuf, PRINT_BUFFER_SIZE, " /Times-Roman findfont 20 scalefont setfont\n");
454   ps_write(pbuf);
455 }
456 
457 
ps_set_bold_peak_numbers_font(void)458 void ps_set_bold_peak_numbers_font(void)
459 {
460   snprintf(pbuf, PRINT_BUFFER_SIZE, " /Times-Bold findfont 14 scalefont setfont\n");
461   ps_write(pbuf);
462 }
463 
464 
ps_set_peak_numbers_font(void)465 void ps_set_peak_numbers_font(void)
466 {
467   snprintf(pbuf, PRINT_BUFFER_SIZE, " /Times-Roman findfont 14 scalefont setfont\n");
468   ps_write(pbuf);
469 }
470 
471 
ps_set_tiny_numbers_font(void)472 void ps_set_tiny_numbers_font(void)
473 {
474   snprintf(pbuf, PRINT_BUFFER_SIZE, " /Times-Roman findfont 12 scalefont setfont\n");
475   ps_write(pbuf);
476 }
477 
478 
479 #define PRINTED_VERTICAL_SPACING 25
480 
snd_print_or_error(const char * output)481 static char *snd_print_or_error(const char *output)
482 {
483   if ((output) && (*output))
484     {
485       int j, i, err;
486       int *offsets = NULL;
487       sync_info *si;
488       chan_info *ccp;
489       char *errstr = NULL;
490       ccp = current_channel();
491       if (!ccp)
492 	return(mus_strdup("nothing to print?"));
493       si = sync_to_chan(ccp);
494       offsets = (int *)calloc(si->chans, sizeof(int));
495       for (j = 0, i = (si->chans - 1); i >= 0; i--)
496 	{
497 	  offsets[i] = j;
498 	  j += ((((axis_info *)((si->cps[i])->axis))->height) + PRINTED_VERTICAL_SPACING);
499 	}
500       if (si->chans > 1)
501 	for (i = 0; i < si->chans; )
502 	  {
503 	    snd_info *sp;
504 	    sp = (si->cps[i])->sound;
505 	    if (!sp) break;
506 	    if (sp->channel_style == CHANNELS_COMBINED)
507 	      for (j = i + 1; (j < i + (int)sp->nchans) && (j < si->chans); j++)
508 		offsets[j] = offsets[i];
509 	    else
510 	      if (sp->channel_style == CHANNELS_SUPERIMPOSED)
511 		for (j = i; (j < i + (int)sp->nchans - 1) && (j < si->chans); j++)
512 		  offsets[j] = offsets[i + sp->nchans - 1];
513 	    i += sp->nchans;
514 	  }
515       err = start_ps_graph(output, ((si->cps[0])->sound)->filename);
516       if (err == 0)
517 	{
518 	  for (i = 0; i < si->chans; i++)
519 	    ps_graph(si->cps[i], 0, offsets[i]);
520 	  end_ps_graph();
521 	}
522       else errstr = mus_format("print %s failed: %s", output, snd_io_strerror());
523       free_sync_info(si);
524       if (offsets) free(offsets);
525       return(errstr);
526     }
527   else return(mus_strdup("print sound: eps file name needed"));
528 }
529 
530 
snd_print(const char * output)531 bool snd_print(const char *output)
532 {
533   char *error;
534   error = snd_print_or_error(output);
535   if (error)
536     {
537       snd_error_without_format(error);
538       free(error);
539       return(false);
540     }
541   return(true);
542 }
543 
544 
print_enved(const char * output,int y0)545 void print_enved(const char *output, int y0)
546 {
547   if ((output) && (*output))
548     {
549       int err;
550       err = start_ps_graph(output, "Envelope Editor");
551       if (err == 0)
552 	{
553 	  bx0 = 0;
554 	  by0 = y0;
555 	  env_redisplay_with_print();
556 	  end_ps_graph();
557 	}
558       else snd_error("print env %s failed: %s", output, snd_io_strerror());
559     }
560   else snd_error_without_format("print envelope: eps file name needed");
561 }
562 
563 
g_graph_to_ps(Xen filename)564 static Xen g_graph_to_ps(Xen filename)
565 {
566   #define H_graph_to_ps "(" S_graph_to_ps " :optional (filename eps-file)): write the current Snd displays to an EPS file"
567 
568   char *error;
569   const char *file;
570 
571   Xen_check_type(Xen_is_string_or_unbound(filename), filename, 1, S_graph_to_ps, "a string (filename)");
572 
573   if (Xen_is_string(filename))
574     file = Xen_string_to_C_string(filename);
575   else file = eps_file(ss);
576 
577   error = snd_print_or_error(file);
578   if (error)
579     {
580       Xen result;
581       result = C_string_to_Xen_string(error);
582       free(error);
583       Xen_error(Xen_make_error_type("cannot-print"),
584 		Xen_list_3(C_string_to_Xen_string(S_graph_to_ps ": can't print ~S (~A)"),
585 			   C_string_to_Xen_string(file),
586 			   result));
587     }
588   return(C_string_to_Xen_string(file));
589 }
590 
591 
592 /* ---------------- gl -> ps ---------------- */
593 
594 #if HAVE_GL && WITH_GL2PS
595 
596 #include "gl2ps.h"
597 
598 #define NUM_GL2PS_TYPES 6
599 static int gl2ps_types[NUM_GL2PS_TYPES] = {GL2PS_EPS, GL2PS_PS, GL2PS_PDF, GL2PS_TEX, GL2PS_SVG, GL2PS_PGF};
600 
601 
g_gl_graph_to_ps(Xen filename,Xen output_type,Xen snd,Xen chn)602 static Xen g_gl_graph_to_ps(Xen filename, Xen output_type, Xen snd, Xen chn)
603 {
604   #define H_gl_graph_to_ps "(" S_gl_graph_to_ps " :optional filename (type 0) snd chn) produces a postscript output file from \
605 OpenGL graphics. type can be 0: eps, 1: ps, 2: pdf, 3: tex, 4: svg, 5: pgf."
606 
607   const char *file;
608   FILE *fp;
609   chan_info *cp;
610   int state = GL2PS_OVERFLOW, buffsize = 1024 * 1024, type = 0;
611 
612   Xen_check_type(Xen_is_string_or_unbound(filename), filename, 1, S_gl_graph_to_ps, "a string (filename)");
613   Xen_check_type(Xen_is_integer_or_unbound(output_type), output_type, 2, S_gl_graph_to_ps, "an integer, 0=eps");
614 
615   Snd_assert_channel(S_gl_graph_to_ps, snd, chn, 3);
616   cp = get_cp(snd, chn, S_gl_graph_to_ps);
617   if (!cp) return(Xen_false);
618 
619   if (Xen_is_string(filename))
620     file = Xen_string_to_C_string(filename);
621   else file = eps_file(ss);
622 
623   if (Xen_is_integer(output_type))
624     type = Xen_integer_to_C_int(output_type);
625   if ((type < 0) || (type >= NUM_GL2PS_TYPES))
626     Xen_out_of_range_error(S_gl_graph_to_ps, 2, output_type, "must be between 0 and 5");
627 
628   fp = fopen(file, "wb");
629 
630   while (state == GL2PS_OVERFLOW)
631     {
632       GLint err;
633       buffsize += 1024 * 1024;
634       err = gl2psBeginPage(cp->sound->short_filename, "Snd", NULL,
635 			   gl2ps_types[type],
636 			   GL2PS_BSP_SORT,
637 			   GL2PS_DRAW_BACKGROUND | GL2PS_USE_CURRENT_VIEWPORT | GL2PS_OCCLUSION_CULL, /* perhaps also GL2PS_NO_BLENDING */
638 			   GL_RGBA, 0, NULL, 0, 0, 0, buffsize, fp, file);
639       if (err != GL2PS_SUCCESS)
640 	{
641 	  /* if an error occurs, gl2ps itself insists on printing something -- this needs to be fixed! */
642 	  state = (int)err;
643 	}
644       else
645 	{
646 	  ss->gl_printing = true;
647 	  display_channel_fft_data(cp);
648 	  ss->gl_printing = false;
649 
650 	  state = gl2psEndPage();
651 	}
652     }
653   fclose(fp);
654 
655   return(C_string_to_Xen_string(file));
656 }
657 
658 
659 char *gl2ps_version(void);
gl2ps_version(void)660 char *gl2ps_version(void)
661 {
662   char *buf;
663   buf = (char *)calloc(128, sizeof(char));
664   snprintf(buf, 128, "gl2ps %d.%d.%d", GL2PS_MAJOR_VERSION, GL2PS_MINOR_VERSION, GL2PS_PATCH_VERSION);
665   return(buf);
666 }
667 
668 
669 void gl2ps_text(const char *msg);
gl2ps_text(const char * msg)670 void gl2ps_text(const char *msg)
671 {
672   gl2psText(msg, "Times-Roman", 20);
673 }
674 
675 #else
676 
g_gl_graph_to_ps(Xen filename,Xen output_type,Xen snd_ignore,Xen chn_ignore)677 static Xen g_gl_graph_to_ps(Xen filename, Xen output_type, Xen snd_ignore, Xen chn_ignore)
678 {
679   #define H_gl_graph_to_ps "gl-graph->ps is a no-op in this version of Snd"
680   Xen_check_type(Xen_is_string_or_unbound(filename), filename, 1, S_gl_graph_to_ps, "a string (filename)");
681   Xen_check_type(Xen_is_integer_or_unbound(output_type), output_type, 2, S_gl_graph_to_ps, "an integer, 0=eps");
682   return(Xen_false);
683 }
684 #endif
685 
686 /* -------------------------------- */
687 
688 
g_eps_file(void)689 static Xen g_eps_file(void) {return(C_string_to_Xen_string(eps_file(ss)));}
690 
g_set_eps_file(Xen val)691 static Xen g_set_eps_file(Xen val)
692 {
693   #define H_eps_file "(" S_eps_file "): File:Print and " S_graph_to_ps " file name (snd.eps)"
694   Xen_check_type(Xen_is_string(val), val, 1, S_set S_eps_file, "a string");
695   if (eps_file(ss)) free(eps_file(ss));
696   set_eps_file(mus_strdup(Xen_string_to_C_string(val)));
697   return(C_string_to_Xen_string(eps_file(ss)));
698 }
699 
700 
701 #define MAX_EPS_MARGIN 1000.0
702 
g_eps_left_margin(void)703 static Xen g_eps_left_margin(void) {return(C_double_to_Xen_real(eps_left_margin(ss)));}
704 
g_set_eps_left_margin(Xen val)705 static Xen g_set_eps_left_margin(Xen val)
706 {
707   #define H_eps_left_margin "(" S_eps_left_margin "): File:Print and " S_graph_to_ps " left margin"
708   Xen_check_type(Xen_is_number(val), val, 1, S_set S_eps_left_margin, "a number");
709   set_eps_left_margin(mus_fclamp(0.0, Xen_real_to_C_double(val), MAX_EPS_MARGIN));
710   return(C_double_to_Xen_real(eps_left_margin(ss)));
711 }
712 
713 
g_eps_bottom_margin(void)714 static Xen g_eps_bottom_margin(void) {return(C_double_to_Xen_real(eps_bottom_margin(ss)));}
715 
g_set_eps_bottom_margin(Xen val)716 static Xen g_set_eps_bottom_margin(Xen val)
717 {
718   #define H_eps_bottom_margin "(" S_eps_bottom_margin "): File:Print and " S_graph_to_ps " bottom margin"
719   Xen_check_type(Xen_is_number(val), val, 1, S_set S_eps_bottom_margin, "a number");
720   set_eps_bottom_margin(mus_fclamp(0.0, Xen_real_to_C_double(val), MAX_EPS_MARGIN));
721   return(C_double_to_Xen_real(eps_bottom_margin(ss)));
722 }
723 
724 
g_eps_size(void)725 static Xen g_eps_size(void) {return(C_double_to_Xen_real(eps_size(ss)));}
726 
g_set_eps_size(Xen val)727 static Xen g_set_eps_size(Xen val)
728 {
729   #define MAX_EPS_SIZE 1000.0
730   #define H_eps_size "(" S_eps_size "): File:Print and " S_graph_to_ps " output size scaler (1.0)"
731   Xen_check_type(Xen_is_number(val), val, 1, S_set S_eps_size, "a number");
732   set_eps_size(mus_fclamp(0.0, Xen_real_to_C_double(val), MAX_EPS_SIZE));
733   return(C_double_to_Xen_real(eps_size(ss)));
734 }
735 
736 
Xen_wrap_1_optional_arg(g_graph_to_ps_w,g_graph_to_ps)737 Xen_wrap_1_optional_arg(g_graph_to_ps_w, g_graph_to_ps)
738 Xen_wrap_4_optional_args(g_gl_graph_to_ps_w, g_gl_graph_to_ps)
739 Xen_wrap_no_args(g_eps_file_w, g_eps_file)
740 Xen_wrap_1_arg(g_set_eps_file_w, g_set_eps_file)
741 Xen_wrap_no_args(g_eps_left_margin_w, g_eps_left_margin)
742 Xen_wrap_1_arg(g_set_eps_left_margin_w, g_set_eps_left_margin)
743 Xen_wrap_no_args(g_eps_size_w, g_eps_size)
744 Xen_wrap_1_arg(g_set_eps_size_w, g_set_eps_size)
745 Xen_wrap_no_args(g_eps_bottom_margin_w, g_eps_bottom_margin)
746 Xen_wrap_1_arg(g_set_eps_bottom_margin_w, g_set_eps_bottom_margin)
747 
748 #if HAVE_SCHEME
749 static s7_pointer acc_eps_bottom_margin(s7_scheme *sc, s7_pointer args) {return(g_set_eps_bottom_margin(s7_cadr(args)));}
acc_eps_file(s7_scheme * sc,s7_pointer args)750 static s7_pointer acc_eps_file(s7_scheme *sc, s7_pointer args) {return(g_set_eps_file(s7_cadr(args)));}
acc_eps_left_margin(s7_scheme * sc,s7_pointer args)751 static s7_pointer acc_eps_left_margin(s7_scheme *sc, s7_pointer args) {return(g_set_eps_left_margin(s7_cadr(args)));}
acc_eps_size(s7_scheme * sc,s7_pointer args)752 static s7_pointer acc_eps_size(s7_scheme *sc, s7_pointer args) {return(g_set_eps_size(s7_cadr(args)));}
753 #endif
754 
g_init_print(void)755 void g_init_print(void)
756 {
757 #if HAVE_SCHEME
758   s7_pointer i, t, r, s, pcl_s, pcl_r;
759   r = s7_make_symbol(s7, "real?");
760   s = s7_make_symbol(s7, "string?");
761   i = s7_make_symbol(s7, "integer?");
762   t = s7_t(s7);
763   pcl_s = s7_make_circular_signature(s7, 0, 1, s);
764   pcl_r = s7_make_circular_signature(s7, 0, 1, r);
765 #endif
766 
767   Xen_define_typed_procedure(S_graph_to_ps, g_graph_to_ps_w, 0, 1, 0, H_graph_to_ps, pcl_s);
768   Xen_define_typed_procedure(S_gl_graph_to_ps, g_gl_graph_to_ps_w, 0, 4, 0, H_gl_graph_to_ps, s7_make_signature(s7, 5, s, s, i, t, t));
769 
770   Xen_define_typed_dilambda(S_eps_file, g_eps_file_w, H_eps_file,
771 			    S_set S_eps_file, g_set_eps_file_w,  0, 0, 1, 0, pcl_s, pcl_s);
772 
773   Xen_define_typed_dilambda(S_eps_left_margin, g_eps_left_margin_w, H_eps_left_margin,
774 			    S_set S_eps_left_margin, g_set_eps_left_margin_w,  0, 0, 1, 0, pcl_r, pcl_r);
775 
776   Xen_define_typed_dilambda(S_eps_bottom_margin, g_eps_bottom_margin_w, H_eps_bottom_margin,
777 			    S_set S_eps_bottom_margin, g_set_eps_bottom_margin_w,  0, 0, 1, 0, pcl_r, pcl_r);
778 
779   Xen_define_typed_dilambda(S_eps_size, g_eps_size_w, H_eps_size,
780 			    S_set S_eps_size, g_set_eps_size_w,  0, 0, 1, 0, pcl_r, pcl_r);
781 
782 #if HAVE_GL && WITH_GL2PS
783   Xen_provide_feature("gl2ps");
784 #endif
785 
786 #if HAVE_SCHEME
787   s7_set_setter(s7, ss->eps_bottom_margin_symbol, s7_make_function(s7, "[acc-" S_eps_bottom_margin "]", acc_eps_bottom_margin, 2, 0, false, "accessor"));
788   s7_set_setter(s7, ss->eps_file_symbol, s7_make_function(s7, "[acc-" S_eps_file "]", acc_eps_file, 2, 0, false, "accessor"));
789   s7_set_setter(s7, ss->eps_left_margin_symbol, s7_make_function(s7, "[acc-" S_eps_left_margin "]", acc_eps_left_margin, 2, 0, false, "accessor"));
790   s7_set_setter(s7, ss->eps_size_symbol, s7_make_function(s7, "[acc-" S_eps_size "]", acc_eps_size, 2, 0, false, "accessor"));
791 
792   s7_set_documentation(s7, ss->eps_bottom_margin_symbol, "*eps-bottom-margin*: File:Print and graph->ps bottom margin");
793   s7_set_documentation(s7, ss->eps_file_symbol, "*eps-file*: File:Print and graph->ps file name (snd.eps)");
794   s7_set_documentation(s7, ss->eps_left_margin_symbol, "*eps-left-margin*: File:Print and graph->ps left margin");
795   s7_set_documentation(s7, ss->eps_size_symbol, "*eps-size*: File:Print and graph->ps output size scaler (1.0)");
796 #endif
797 }
798