1 
2 /*
3  * bltGrPs.c --
4  *
5  *      This module implements the "postscript" operation for BLT
6  *      graph widget.
7  *
8  * Copyright 1991-1998 Lucent Technologies, Inc.
9  *
10  * Permission to use, copy, modify, and distribute this software and
11  * its documentation for any purpose and without fee is hereby
12  * granted, provided that the above copyright notice appear in all
13  * copies and that both that the copyright notice and warranty
14  * disclaimer appear in supporting documentation, and that the names
15  * of Lucent Technologies any of their entities not be used in
16  * advertising or publicity pertaining to distribution of the software
17  * without specific, written prior permission.
18  *
19  * Lucent Technologies disclaims all warranties with regard to this
20  * software, including all implied warranties of merchantability and
21  * fitness.  In no event shall Lucent Technologies be liable for any
22  * special, indirect or consequential damages or any damages
23  * whatsoever resulting from loss of use, data or profits, whether in
24  * an action of contract, negligence or other tortuous action, arising
25  * out of or in connection with the use or performance of this
26  * software.
27  */
28 
29 /*
30  * -----------------------------------------------------------------
31  *
32  * PostScript routines to print a graph
33  *
34  * -----------------------------------------------------------------
35  */
36 #include "bltGraph.h"
37 #include <X11/Xutil.h>
38 #if defined(__STDC__)
39 #include <stdarg.h>
40 #else
41 #include <varargs.h>
42 #endif
43 
44 #define PS_PREVIEW_EPSI	0
45 #define PS_PREVIEW_WMF	1
46 #define PS_PREVIEW_TIFF	2
47 
48 static Tk_OptionParseProc StringToColorMode;
49 static Tk_OptionPrintProc ColorModeToString;
50 static Tk_CustomOption colorModeOption =
51 {
52     StringToColorMode, ColorModeToString, (ClientData)0,
53 };
54 static Tk_OptionParseProc StringToFormat;
55 static Tk_OptionPrintProc FormatToString;
56 static Tk_CustomOption formatOption =
57 {
58     StringToFormat, FormatToString, (ClientData)0,
59 };
60 extern Tk_CustomOption bltDistanceOption;
61 extern Tk_CustomOption bltPositiveDistanceOption;
62 extern Tk_CustomOption bltPadOption;
63 
64 #define DEF_PS_CENTER		"yes"
65 #define DEF_PS_COLOR_MAP	(char *)NULL
66 #define DEF_PS_COLOR_MODE	"color"
67 #define DEF_PS_DECORATIONS	"yes"
68 #define DEF_PS_FONT_MAP		(char *)NULL
69 #define DEF_PS_FOOTER		"no"
70 #define DEF_PS_HEIGHT		"0"
71 #define DEF_PS_LANDSCAPE	"no"
72 #define DEF_PS_MAXPECT		"no"
73 #define DEF_PS_PADX		"1.0i"
74 #define DEF_PS_PADY		"1.0i"
75 #define DEF_PS_PAPERHEIGHT	"11.0i"
76 #define DEF_PS_PAPERWIDTH	"8.5i"
77 #define DEF_PS_PREVIEW		"no"
78 #define DEF_PS_PREVIEW_FORMAT   "epsi"
79 #define DEF_PS_WIDTH		"0"
80 
81 static Tk_ConfigSpec configSpecs[] =
82 {
83     {TK_CONFIG_BOOLEAN, "-center", "center", "Center",
84 	DEF_PS_CENTER, Tk_Offset(PostScript, center),
85 	TK_CONFIG_DONT_SET_DEFAULT},
86     {TK_CONFIG_STRING, "-colormap", "colorMap", "ColorMap",
87 	DEF_PS_COLOR_MAP, Tk_Offset(PostScript, colorVarName),
88 	TK_CONFIG_NULL_OK},
89     {TK_CONFIG_CUSTOM, "-colormode", "colorMode", "ColorMode",
90 	DEF_PS_COLOR_MODE, Tk_Offset(PostScript, colorMode),
91 	TK_CONFIG_DONT_SET_DEFAULT, &colorModeOption},
92     {TK_CONFIG_BOOLEAN, "-decorations", "decorations", "Decorations",
93 	DEF_PS_DECORATIONS, Tk_Offset(PostScript, decorations),
94 	TK_CONFIG_DONT_SET_DEFAULT},
95     {TK_CONFIG_STRING, "-fontmap", "fontMap", "FontMap",
96 	DEF_PS_FONT_MAP, Tk_Offset(PostScript, fontVarName),
97 	TK_CONFIG_NULL_OK},
98     {TK_CONFIG_BOOLEAN, "-footer", "footer", "Footer",
99 	DEF_PS_FOOTER, Tk_Offset(PostScript, footer),
100 	TK_CONFIG_DONT_SET_DEFAULT},
101     {TK_CONFIG_CUSTOM, "-height", "height", "Height",
102 	DEF_PS_HEIGHT, Tk_Offset(PostScript, reqHeight),
103 	TK_CONFIG_DONT_SET_DEFAULT, &bltDistanceOption},
104     {TK_CONFIG_BOOLEAN, "-landscape", "landscape", "Landscape",
105 	DEF_PS_LANDSCAPE, Tk_Offset(PostScript, landscape),
106 	TK_CONFIG_DONT_SET_DEFAULT},
107     {TK_CONFIG_BOOLEAN, "-maxpect", "maxpect", "Maxpect",
108 	DEF_PS_MAXPECT, Tk_Offset(PostScript, maxpect),
109 	TK_CONFIG_DONT_SET_DEFAULT},
110     {TK_CONFIG_CUSTOM, "-padx", "padX", "PadX",
111 	DEF_PS_PADX, Tk_Offset(PostScript, padX), 0, &bltPadOption},
112     {TK_CONFIG_CUSTOM, "-pady", "padY", "PadY",
113 	DEF_PS_PADY, Tk_Offset(PostScript, padY), 0, &bltPadOption},
114     {TK_CONFIG_CUSTOM, "-paperheight", "paperHeight", "PaperHeight",
115 	DEF_PS_PAPERHEIGHT, Tk_Offset(PostScript, reqPaperHeight),
116 	0, &bltPositiveDistanceOption},
117     {TK_CONFIG_CUSTOM, "-paperwidth", "paperWidth", "PaperWidth",
118 	DEF_PS_PAPERWIDTH, Tk_Offset(PostScript, reqPaperWidth),
119 	0, &bltPositiveDistanceOption},
120     {TK_CONFIG_BOOLEAN, "-preview", "preview", "Preview",
121 	DEF_PS_PREVIEW, Tk_Offset(PostScript, addPreview),
122 	TK_CONFIG_DONT_SET_DEFAULT},
123     {TK_CONFIG_CUSTOM, "-previewformat", "previewFormat", "PreviewFormat",
124 	DEF_PS_PREVIEW_FORMAT, Tk_Offset(PostScript, previewFormat),
125         TK_CONFIG_DONT_SET_DEFAULT, &formatOption},
126     {TK_CONFIG_CUSTOM, "-width", "width", "Width",
127 	DEF_PS_WIDTH, Tk_Offset(PostScript, reqWidth),
128 	TK_CONFIG_DONT_SET_DEFAULT, &bltDistanceOption},
129     {TK_CONFIG_END, NULL, NULL, NULL, NULL, 0, 0}
130 };
131 
132 extern void Blt_MarkersToPostScript _ANSI_ARGS_((Graph *graphPtr,
133 	PsToken psToken, int under));
134 extern void Blt_ElementsToPostScript _ANSI_ARGS_((Graph *graphPtr,
135 	PsToken psToken));
136 extern void Blt_ActiveElementsToPostScript _ANSI_ARGS_((Graph *graphPtr,
137 	PsToken psToken));
138 extern void Blt_LegendToPostScript _ANSI_ARGS_((Legend *legendPtr,
139 	PsToken psToken));
140 extern void Blt_GridToPostScript _ANSI_ARGS_((Graph *graphPtr,
141 	PsToken psToken));
142 extern void Blt_AxesToPostScript _ANSI_ARGS_((Graph *graphPtr,
143 	PsToken psToken));
144 extern void Blt_AxisLimitsToPostScript _ANSI_ARGS_((Graph *graphPtr,
145 	PsToken psToken));
146 /*
147  *----------------------------------------------------------------------
148  *
149  * StringToColorMode --
150  *
151  *	Convert the string representation of a PostScript color mode
152  *	into the enumerated type representing the color level:
153  *
154  *	    PS_MODE_COLOR 	- Full color
155  *	    PS_MODE_GREYSCALE  	- Color converted to greyscale
156  *	    PS_MODE_MONOCHROME 	- Only black and white
157  *
158  * Results:
159  *	A standard Tcl result.  The color level is written into the
160  *	page layout information structure.
161  *
162  * Side Effects:
163  *	Future invocations of the "postscript" option will use this
164  *	variable to determine how color information will be displayed
165  *	in the PostScript output it produces.
166  *
167  *----------------------------------------------------------------------
168  */
169 /*ARGSUSED*/
170 static int
StringToColorMode(clientData,interp,tkwin,string,widgRec,offset)171 StringToColorMode(clientData, interp, tkwin, string, widgRec, offset)
172     ClientData clientData;	/* Not used. */
173     Tcl_Interp *interp;		/* Interpreter to send results back to */
174     Tk_Window tkwin;		/* Not used. */
175     char *string;		/* New value. */
176     char *widgRec;		/* Widget record */
177     int offset;			/* Offset of field in record */
178 {
179     PsColorMode *modePtr = (PsColorMode *) (widgRec + offset);
180     unsigned int length;
181     char c;
182 
183     c = string[0];
184     length = strlen(string);
185     if ((c == 'c') && (strncmp(string, "color", length) == 0)) {
186 	*modePtr = PS_MODE_COLOR;
187     } else if ((c == 'g') && (strncmp(string, "grayscale", length) == 0)) {
188 	*modePtr = PS_MODE_GREYSCALE;
189     } else if ((c == 'g') && (strncmp(string, "greyscale", length) == 0)) {
190 	*modePtr = PS_MODE_GREYSCALE;
191     } else if ((c == 'm') && (strncmp(string, "monochrome", length) == 0)) {
192 	*modePtr = PS_MODE_MONOCHROME;
193     } else {
194 	Tcl_AppendResult(interp, "bad color mode \"", string, "\": should be \
195 \"color\", \"greyscale\", or \"monochrome\"", (char *)NULL);
196 	return TCL_ERROR;
197     }
198     return TCL_OK;
199 }
200 
201 /*
202  *----------------------------------------------------------------------
203  *
204  * NameOfColorMode --
205  *
206  *	Convert the PostScript mode value into the string representing
207  *	a valid color mode.
208  *
209  * Results:
210  *	The static string representing the color mode is returned.
211  *
212  *----------------------------------------------------------------------
213  */
214 static char *
NameOfColorMode(colorMode)215 NameOfColorMode(colorMode)
216     PsColorMode colorMode;
217 {
218     switch (colorMode) {
219     case PS_MODE_COLOR:
220 	return "color";
221     case PS_MODE_GREYSCALE:
222 	return "greyscale";
223     case PS_MODE_MONOCHROME:
224 	return "monochrome";
225     default:
226 	return "unknown color mode";
227     }
228 }
229 
230 /*
231  *----------------------------------------------------------------------
232  *
233  * ColorModeToString --
234  *
235  *	Convert the current color mode into the string representing a
236  *	valid color mode.
237  *
238  * Results:
239  *	The string representing the color mode is returned.
240  *
241  *----------------------------------------------------------------------
242  */
243 /*ARGSUSED*/
244 static char *
ColorModeToString(clientData,tkwin,widgRec,offset,freeProcPtr)245 ColorModeToString(clientData, tkwin, widgRec, offset, freeProcPtr)
246     ClientData clientData;	/* Not used. */
247     Tk_Window tkwin;		/* Not used. */
248     char *widgRec;		/* Widget record. */
249     int offset;			/* field of colorMode in record */
250     Tcl_FreeProc **freeProcPtr;	/* Not used. */
251 {
252     PsColorMode mode = *(PsColorMode *) (widgRec + offset);
253 
254     return NameOfColorMode(mode);
255 }
256 
257 /*
258  *----------------------------------------------------------------------
259  *
260  * StringToFormat --
261  *
262  *	Convert the string of the PostScript preview format into
263  *	an enumerated type representing the desired format.  The
264  *	available formats are:
265  *
266  *	    PS_PREVIEW_WMF 	- Windows Metafile.
267  *	    PS_PREVIEW_TIFF  	- TIFF bitmap image.
268  *	    PS_PREVIEW_EPSI 	- Device independent ASCII preview
269  *
270  * Results:
271  *	A standard Tcl result.  The format is written into the
272  *	page layout information structure.
273  *
274  * Side Effects:
275  *	Future invocations of the "postscript" option will use this
276  *	variable to determine how to format a preview image (if one
277  *	is selected) when the PostScript output is produced.
278  *
279  *----------------------------------------------------------------------
280  */
281 /*ARGSUSED*/
282 static int
StringToFormat(clientData,interp,tkwin,string,widgRec,offset)283 StringToFormat(clientData, interp, tkwin, string, widgRec, offset)
284     ClientData clientData;	/* Not used. */
285     Tcl_Interp *interp;		/* Interpreter to send results back to */
286     Tk_Window tkwin;		/* Not used. */
287     char *string;		/* New value. */
288     char *widgRec;		/* Widget record */
289     int offset;			/* Offset of field in record */
290 {
291     int *formatPtr = (int *) (widgRec + offset);
292     unsigned int length;
293     char c;
294 
295     c = string[0];
296     length = strlen(string);
297     if ((c == 'c') && (strncmp(string, "epsi", length) == 0)) {
298 	*formatPtr = PS_PREVIEW_EPSI;
299 #ifdef WIN32
300 #ifdef HAVE_TIFF_H
301     } else if ((c == 't') && (strncmp(string, "tiff", length) == 0)) {
302 	*formatPtr = PS_PREVIEW_TIFF;
303 #endif /* HAVE_TIFF_H */
304     } else if ((c == 'w') && (strncmp(string, "wmf", length) == 0)) {
305 	*formatPtr = PS_PREVIEW_WMF;
306 #endif /* WIN32 */
307     } else {
308 	Tcl_AppendResult(interp, "bad format \"", string, "\": should be ",
309 #ifdef WIN32
310 #ifdef HAVE_TIFF_H
311 			 "\"tiff\" or ",
312 #endif /* HAVE_TIFF_H */
313 			 "\"wmf\" or ",
314 #endif /* WIN32 */
315 			 "\"epsi\"", (char *)NULL);
316 	return TCL_ERROR;
317     }
318     return TCL_OK;
319 }
320 
321 /*
322  *----------------------------------------------------------------------
323  *
324  * FormatToString --
325  *
326  *	Convert the preview format into the string representing its
327  *	type.
328  *
329  * Results:
330  *	The string representing the preview format is returned.
331  *
332  *----------------------------------------------------------------------
333  */
334 /*ARGSUSED*/
335 static char *
FormatToString(clientData,tkwin,widgRec,offset,freeProcPtr)336 FormatToString(clientData, tkwin, widgRec, offset, freeProcPtr)
337     ClientData clientData;	/* Not used. */
338     Tk_Window tkwin;		/* Not used. */
339     char *widgRec;		/* PostScript structure record */
340     int offset;			/* field of colorMode in record */
341     Tcl_FreeProc **freeProcPtr;	/* Not used. */
342 {
343     int format = *(int *)(widgRec + offset);
344 
345     switch (format) {
346     case PS_PREVIEW_EPSI:
347 	return "epsi";
348     case PS_PREVIEW_WMF:
349 	return "wmf";
350     case PS_PREVIEW_TIFF:
351 	return "tiff";
352     }
353     return "?unknown preview format?";
354 }
355 
356 void
Blt_DestroyPostScript(graphPtr)357 Blt_DestroyPostScript(graphPtr)
358     Graph *graphPtr;
359 {
360     Tk_FreeOptions(configSpecs, (char *)graphPtr->postscript,
361 	   graphPtr->display, 0);
362     Blt_Free(graphPtr->postscript);
363 }
364 
365 /*
366  *----------------------------------------------------------------------
367  *
368  * CgetOp --
369  *
370  *----------------------------------------------------------------------
371  */
372 /*ARGSUSED*/
373 static int
CgetOp(graphPtr,interp,argc,argv)374 CgetOp(graphPtr, interp, argc, argv)
375     Graph *graphPtr;
376     Tcl_Interp *interp;
377     int argc;
378     char *argv[];
379 {
380     PostScript *psPtr = (PostScript *)graphPtr->postscript;
381 
382     if (Tk_ConfigureValue(interp, graphPtr->tkwin, configSpecs, (char *)psPtr,
383 	    argv[3], 0) != TCL_OK) {
384 	return TCL_ERROR;
385     }
386     return TCL_OK;
387 }
388 
389 /*
390  * ----------------------------------------------------------------------
391  *
392  * ConfigureOp --
393  *
394  *      This procedure is invoked to print the graph in a file.
395  *
396  * Results:
397  *      A standard TCL result.
398  *
399  * Side effects:
400  *      A new PostScript file is created.
401  *
402  * ----------------------------------------------------------------------
403  */
404 static int
ConfigureOp(graphPtr,interp,argc,argv)405 ConfigureOp(graphPtr, interp, argc, argv)
406     Graph *graphPtr;
407     Tcl_Interp *interp;
408     int argc;			/* Number of options in argv vector */
409     CONST char **argv;		/* Option vector */
410 {
411     int flags = TK_CONFIG_ARGV_ONLY;
412     PostScript *psPtr = (PostScript *)graphPtr->postscript;
413 
414     if (argc == 3) {
415 	return Tk_ConfigureInfo(interp, graphPtr->tkwin, configSpecs,
416 		(char *)psPtr, (char *)NULL, flags);
417     } else if (argc == 4) {
418 	return Tk_ConfigureInfo(interp, graphPtr->tkwin, configSpecs,
419 		(char *)psPtr, argv[3], flags);
420     }
421     if (Tk_ConfigureWidget(interp, graphPtr->tkwin, configSpecs, argc - 3,
422 	    argv + 3, (char *)psPtr, flags) != TCL_OK) {
423 	return TCL_ERROR;
424     }
425     return TCL_OK;
426 }
427 
428 /*
429  * --------------------------------------------------------------------------
430  *
431  * ComputeBoundingBox --
432  *
433  * 	Computes the bounding box for the PostScript plot.  First get
434  * 	the size of the plot (by default, it's the size of graph's X
435  * 	window).  If the plot plus the page border is bigger than the
436  * 	designated paper size, or if the "-maxpect" option is turned
437  * 	on, scale the plot to the page.
438  *
439  *	Note: All coordinates/sizes are in screen coordinates, not
440  *	      PostScript coordinates.  This includes the computed
441  *	      bounding box and paper size.  They will be scaled to
442  *	      printer points later.
443  *
444  * Results:
445  *	Returns the height of the paper in screen coordinates.
446  *
447  * Side Effects:
448  *	The graph dimensions (width and height) are changed to the
449  *	requested PostScript plot size.
450  *
451  * --------------------------------------------------------------------------
452  */
453 static int
ComputeBoundingBox(graphPtr,psPtr)454 ComputeBoundingBox(graphPtr, psPtr)
455     Graph *graphPtr;
456     PostScript *psPtr;
457 {
458     int paperWidth, paperHeight;
459     int x, y, hSize, vSize, hBorder, vBorder;
460     double hScale, vScale, scale;
461 
462     x = psPtr->padLeft;
463     y = psPtr->padTop;
464     hBorder = PADDING(psPtr->padX);
465     vBorder = PADDING(psPtr->padY);
466 
467     if (psPtr->reqWidth > 0) {
468 	graphPtr->width = psPtr->reqWidth;
469     }
470     if (psPtr->reqHeight > 0) {
471 	graphPtr->height = psPtr->reqHeight;
472     }
473     if (psPtr->landscape) {
474 	hSize = graphPtr->height;
475 	vSize = graphPtr->width;
476     } else {
477 	hSize = graphPtr->width;
478 	vSize = graphPtr->height;
479     }
480     /*
481      * If the paper size wasn't specified, set it to the graph size plus
482      * the paper border.
483      */
484     paperWidth = psPtr->reqPaperWidth;
485     paperHeight = psPtr->reqPaperHeight;
486     if (paperWidth < 1) {
487 	paperWidth = hSize + hBorder;
488     }
489     if (paperHeight < 1) {
490 	paperHeight = vSize + vBorder;
491     }
492     hScale = vScale = 1.0;
493     /*
494      * Scale the plot size (the graph itself doesn't change size) if
495      * it's bigger than the paper or if -maxpect was set.
496      */
497     if ((psPtr->maxpect) || ((hSize + hBorder) > paperWidth)) {
498 	hScale = (double)(paperWidth - hBorder) / (double)hSize;
499     }
500     if ((psPtr->maxpect) || ((vSize + vBorder) > paperHeight)) {
501 	vScale = (double)(paperHeight - vBorder) / (double)vSize;
502     }
503     scale = MIN(hScale, vScale);
504     if (scale != 1.0) {
505 	hSize = (int)((hSize * scale) + 0.5);
506 	vSize = (int)((vSize * scale) + 0.5);
507     }
508     psPtr->pageScale = scale;
509     if (psPtr->center) {
510 	if (paperWidth > hSize) {
511 	    x = (paperWidth - hSize) / 2;
512 	}
513 	if (paperHeight > vSize) {
514 	    y = (paperHeight - vSize) / 2;
515 	}
516     }
517     psPtr->left = x;
518     psPtr->bottom = y;
519     psPtr->right = x + hSize - 1;
520     psPtr->top = y + vSize - 1;
521 
522     graphPtr->flags |= LAYOUT_NEEDED | MAP_WORLD;
523     Blt_LayoutGraph(graphPtr);
524     return paperHeight;
525 }
526 
527 /*
528  * --------------------------------------------------------------------------
529  *
530  * PreviewImage --
531  *
532  * 	Generates a EPSI thumbnail of the graph.  The thumbnail is
533  *	restricted to a certain size.  This is to keep the size of the
534  *	PostScript file small and the processing time low.
535  *
536  *	The graph is drawn into a pixmap.  We then take a snapshot
537  *	of that pixmap, and rescale it to a smaller image.  Finally,
538  * 	the image is dumped to PostScript.
539  *
540  * Results:
541  *	None.
542  *
543  * --------------------------------------------------------------------------
544  */
545 static void
PreviewImage(graphPtr,psToken)546 PreviewImage(graphPtr, psToken)
547     Graph *graphPtr;
548     PsToken psToken;
549 {
550     PostScript *psPtr = (PostScript *)graphPtr->postscript;
551     int noBackingStore = 0;
552     Pixmap drawable;
553     Blt_ColorImage image;
554     int nLines;
555     Tcl_DString dString;
556 
557     /* Create a pixmap and draw the graph into it. */
558 
559     drawable = Tk_GetPixmap(graphPtr->display, Tk_WindowId(graphPtr->tkwin),
560 	graphPtr->width, graphPtr->height, Tk_Depth(graphPtr->tkwin));
561     Blt_DrawGraph(graphPtr, drawable, noBackingStore);
562 
563     /* Get a color image from the pixmap */
564     image = Blt_DrawableToColorImage(graphPtr->tkwin, drawable, 0, 0,
565 	graphPtr->width, graphPtr->height, 1.0);
566     Tk_FreePixmap(graphPtr->display, drawable);
567     if (image == NULL) {
568 	return;			/* Can't grab pixmap? */
569     }
570 #ifdef THUMBNAIL_PREVIEW
571     {
572 	double scale, xScale, yScale;
573 	int width, height;
574 	Blt_ColorImage destImage;
575 
576 	/* Scale the source image into a size appropriate for a thumbnail. */
577 #define PS_MAX_PREVIEW_WIDTH	300.0
578 #define PS_MAX_PREVIEW_HEIGHT	300.0
579 	xScale = PS_MAX_PREVIEW_WIDTH / (double)graphPtr->width;
580 	yScale = PS_MAX_PREVIEW_HEIGHT / (double)graphPtr->height;
581 	scale = MIN(xScale, yScale);
582 
583 	width = (int)(scale * graphPtr->width + 0.5);
584 	height = (int)(scale * graphPtr->height + 0.5);
585 	destImage = Blt_ResampleColorImage(image, width, height,
586 		bltBoxFilterPtr, bltBoxFilterPtr);
587 	Blt_FreeColorImage(image);
588 	image = destImage;
589     }
590 #endif /* THUMBNAIL_PREVIEW */
591     Blt_ColorImageToGreyscale(image);
592     if (psPtr->landscape) {
593 	Blt_ColorImage oldImage;
594 
595 	oldImage = image;
596 	image = Blt_RotateColorImage(image, 90.0);
597 	Blt_FreeColorImage(oldImage);
598     }
599     Tcl_DStringInit(&dString);
600     /* Finally, we can generate PostScript for the image */
601     nLines = Blt_ColorImageToPsData(image, 1, &dString, "%");
602 
603     Blt_AppendToPostScript(psToken, "%%BeginPreview: ", (char *)NULL);
604     Blt_FormatToPostScript(psToken, "%d %d 8 %d\n", Blt_ColorImageWidth(image),
605 	Blt_ColorImageHeight(image), nLines);
606     Blt_AppendToPostScript(psToken, Tcl_DStringValue(&dString), (char *)NULL);
607     Blt_AppendToPostScript(psToken, "%%EndPreview\n\n", (char *)NULL);
608     Tcl_DStringFree(&dString);
609     Blt_FreeColorImage(image);
610 }
611 
612 /*
613  * --------------------------------------------------------------------------
614  *
615  * PostScriptPreamble
616  *
617  *    	The PostScript preamble calculates the needed translation and scaling
618  *    	to make X11 coordinates compatible with PostScript.
619  *
620  * ---------------------------------------------------------------------
621  */
622 
623 #ifdef TIME_WITH_SYS_TIME
624 #include <sys/time.h>
625 #include <time.h>
626 #else
627 #ifdef HAVE_SYS_TIME_H
628 #include <sys/time.h>
629 #else
630 #include <time.h>
631 #endif /* HAVE_SYS_TIME_H */
632 #endif /* TIME_WITH_SYS_TIME */
633 
634 static int
PostScriptPreamble(graphPtr,fileName,psToken)635 PostScriptPreamble(graphPtr, fileName, psToken)
636     Graph *graphPtr;
637     CONST char *fileName;
638     PsToken psToken;
639 {
640     PostScript *psPtr = (PostScript *)graphPtr->postscript;
641     time_t ticks;
642     char date[200];		/* Hold the date string from ctime() */
643     CONST char *version;
644     double dpiX, dpiY;
645     double xPixelsToPica, yPixelsToPica; /* Scales to convert pixels to pica */
646     Screen *screenPtr;
647     char *nl;
648     int paperHeightPixels;
649 
650     paperHeightPixels = ComputeBoundingBox(graphPtr, psPtr);
651     if (fileName == NULL) {
652 	fileName = Tk_PathName(graphPtr->tkwin);
653     }
654     Blt_AppendToPostScript(psToken, "%!PS-Adobe-3.0 EPSF-3.0\n",
655 			   (char *)NULL);
656 
657     /*
658      * Compute the scale factors to convert PostScript to X11 coordinates.
659      * Round the pixels per inch (dpi) to an integral value before computing
660      * the scale.
661      */
662 #define MM_INCH		25.4
663 #define PICA_INCH	72.0
664     screenPtr = Tk_Screen(graphPtr->tkwin);
665     dpiX = (WidthOfScreen(screenPtr) * MM_INCH) / WidthMMOfScreen(screenPtr);
666     xPixelsToPica = PICA_INCH / dpiX;
667     dpiY = (HeightOfScreen(screenPtr) * MM_INCH) / HeightMMOfScreen(screenPtr);
668     yPixelsToPica = PICA_INCH / dpiY;
669 
670     /*
671      * The "BoundingBox" comment is required for EPS files. The box
672      * coordinates are integers, so we need round away from the
673      * center of the box.
674      */
675     Blt_FormatToPostScript(psToken, "%%%%BoundingBox: %d %d %d %d\n",
676 	(int)floor(psPtr->left * xPixelsToPica),
677 	(int)floor((paperHeightPixels - psPtr->top) * yPixelsToPica),
678 	(int)ceil(psPtr->right * xPixelsToPica),
679 	(int)ceil((paperHeightPixels - psPtr->bottom) * yPixelsToPica));
680 
681     Blt_AppendToPostScript(psToken, "%%Pages: 0\n", (char *)NULL);
682 
683     version = Tcl_GetVar(graphPtr->interp, "blt_version", TCL_GLOBAL_ONLY);
684     if (version == NULL) {
685 	version = "???";
686     }
687     Blt_FormatToPostScript(psToken, "%%%%Creator: (BLT %s %s)\n", version,
688 	Tk_Class(graphPtr->tkwin));
689 
690     ticks = time((time_t *) NULL);
691     strcpy(date, ctime(&ticks));
692     nl = date + strlen(date) - 1;
693     if (*nl == '\n') {
694 	*nl = '\0';
695     }
696     Blt_FormatToPostScript(psToken, "%%%%CreationDate: (%s)\n", date);
697     Blt_FormatToPostScript(psToken, "%%%%Title: (%s)\n", fileName);
698     Blt_AppendToPostScript(psToken, "%%DocumentData: Clean7Bit\n",
699 			   (char *)NULL);
700     if (psPtr->landscape) {
701 	Blt_AppendToPostScript(psToken, "%%Orientation: Landscape\n",
702 			       (char *)NULL);
703     } else {
704 	Blt_AppendToPostScript(psToken, "%%Orientation: Portrait\n",
705 			       (char *)NULL);
706     }
707     Blt_AppendToPostScript(psToken,
708 	"%%DocumentNeededResources: font Helvetica Courier\n", (char *)NULL);
709     Blt_AppendToPostScript(psToken, "%%EndComments\n\n", (char *)NULL);
710     if ((psPtr->addPreview) && (psPtr->previewFormat == PS_PREVIEW_EPSI)) {
711 	PreviewImage(graphPtr, psToken);
712     }
713     if (Blt_FileToPostScript(psToken, "bltGraph.pro") != TCL_OK) {
714 	return TCL_ERROR;
715     }
716     if (psPtr->footer) {
717 	char *who;
718 
719 	who = getenv("LOGNAME");
720 	if (who == NULL) {
721 	    who = "???";
722 	}
723 	Blt_AppendToPostScript(psToken,
724 	    "8 /Helvetica SetFont\n",
725 	    "10 30 moveto\n",
726 	    "(Date: ", date, ") show\n",
727 	    "10 20 moveto\n",
728 	    "(File: ", fileName, ") show\n",
729 	    "10 10 moveto\n",
730 	    "(Created by: ", who, "@", Tcl_GetHostName(), ") show\n",
731 	    "0 0 moveto\n",
732 	    (char *)NULL);
733     }
734     /*
735      * Set the conversion from PostScript to X11 coordinates.  Scale
736      * pica to pixels and flip the y-axis (the origin is the upperleft
737      * corner).
738      */
739     Blt_AppendToPostScript(psToken,
740 	"% Transform coordinate system to use X11 coordinates\n\n",
741 	"% 1. Flip y-axis over by reversing the scale,\n",
742 	"% 2. Translate the origin to the other side of the page,\n",
743 	"%    making the origin the upper left corner\n", (char *)NULL);
744     Blt_FormatToPostScript(psToken, "%g -%g scale\n", xPixelsToPica,
745 	yPixelsToPica);
746     /* Papersize is in pixels.  Translate the new origin *after*
747      * changing the scale. */
748     Blt_FormatToPostScript(psToken, "0 %d translate\n\n",
749 	-paperHeightPixels);
750     Blt_AppendToPostScript(psToken, "% User defined page layout\n\n",
751 	"% Set color level\n", (char *)NULL);
752     Blt_FormatToPostScript(psToken, "/CL %d def\n\n", psPtr->colorMode);
753     Blt_FormatToPostScript(psToken, "%% Set origin\n%d %d translate\n\n",
754 	psPtr->left, psPtr->bottom);
755     if (psPtr->landscape) {
756 	Blt_FormatToPostScript(psToken,
757 	    "%% Landscape orientation\n0 %g translate\n-90 rotate\n",
758 	    ((double)graphPtr->width * psPtr->pageScale));
759     }
760     if (psPtr->pageScale != 1.0) {
761 	Blt_AppendToPostScript(psToken, "\n% Setting graph scale factor\n",
762 	    (char *)NULL);
763 	Blt_FormatToPostScript(psToken, " %g %g scale\n", psPtr->pageScale,
764 	    psPtr->pageScale);
765     }
766     Blt_AppendToPostScript(psToken, "\n%%EndSetup\n\n", (char *)NULL);
767     return TCL_OK;
768 }
769 
770 
771 static void
MarginsToPostScript(graphPtr,psToken)772 MarginsToPostScript(graphPtr, psToken)
773     Graph *graphPtr;
774     PsToken psToken;
775 {
776     PostScript *psPtr = (PostScript *)graphPtr->postscript;
777     XRectangle margin[4];
778 
779     margin[0].x = margin[0].y = margin[3].x = margin[1].x = 0;
780     margin[0].width = margin[3].width = graphPtr->width;
781     margin[0].height = graphPtr->top;
782     margin[3].y = graphPtr->bottom;
783     margin[3].height = graphPtr->height - graphPtr->bottom;
784     margin[2].y = margin[1].y = graphPtr->top;
785     margin[1].width = graphPtr->left;
786     margin[2].height = margin[1].height = graphPtr->bottom - graphPtr->top;
787     margin[2].x = graphPtr->right;
788     margin[2].width = graphPtr->width - graphPtr->right;
789 
790     /* Clear the surrounding margins and clip the plotting surface */
791     if (psPtr->decorations) {
792 	Blt_BackgroundToPostScript(psToken,
793 	    Tk_3DBorderColor(graphPtr->border));
794     } else {
795 	Blt_ClearBackgroundToPostScript(psToken);
796     }
797     Blt_RectanglesToPostScript(psToken, margin, 4);
798 
799     /* Interior 3D border */
800     if ((psPtr->decorations) && (graphPtr->plotBorderWidth > 0)) {
801 	int x, y, width, height;
802 
803 	x = graphPtr->left - graphPtr->plotBorderWidth;
804 	y = graphPtr->top - graphPtr->plotBorderWidth;
805 	width = (graphPtr->right - graphPtr->left) +
806 	    (2 * graphPtr->plotBorderWidth);
807 	height = (graphPtr->bottom - graphPtr->top) +
808 	    (2 * graphPtr->plotBorderWidth);
809 	Blt_Draw3DRectangleToPostScript(psToken, graphPtr->border,
810 	   (double)x, (double)y, width, height, graphPtr->plotBorderWidth,
811 	   graphPtr->plotRelief);
812     }
813     if (Blt_LegendSite(graphPtr->legend) & LEGEND_IN_MARGIN) {
814 	/*
815 	 * Print the legend if we're using a site which lies in one
816 	 * of the margins (left, right, top, or bottom) of the graph.
817 	 */
818 	Blt_LegendToPostScript(graphPtr->legend, psToken);
819     }
820     if (graphPtr->title != NULL) {
821 	Blt_TextToPostScript(psToken, graphPtr->title,
822 		&graphPtr->titleTextStyle, (double)graphPtr->titleX,
823 		(double)graphPtr->titleY);
824     }
825     Blt_AxesToPostScript(graphPtr, psToken);
826 }
827 
828 
829 static int
GraphToPostScript(graphPtr,ident,psToken)830 GraphToPostScript(graphPtr, ident, psToken)
831     Graph *graphPtr;
832     CONST char *ident;		/* Identifier string (usually the filename) */
833     PsToken psToken;
834 {
835     int x, y, width, height;
836     int result;
837 
838     /*
839      * We need to know how big a graph to print.  If the graph hasn't
840      * been drawn yet, the width and height will be 1.  Instead use
841      * the requested size of the widget.  The user can still override
842      * this with the -width and -height postscript options.
843      */
844     if (graphPtr->height <= 1) {
845 	graphPtr->height = Tk_ReqHeight(graphPtr->tkwin);
846     }
847     if (graphPtr->width <= 1) {
848 	graphPtr->width = Tk_ReqWidth(graphPtr->tkwin);
849     }
850     result = PostScriptPreamble(graphPtr, ident, psToken);
851     if (result != TCL_OK) {
852 	goto error;
853     }
854     /*
855      * Determine rectangle of the plotting area for the graph window
856      */
857     x = graphPtr->left - graphPtr->plotBorderWidth;
858     y = graphPtr->top - graphPtr->plotBorderWidth;
859 
860     width = (graphPtr->right - graphPtr->left + 1) +
861 	(2 * graphPtr->plotBorderWidth);
862     height = (graphPtr->bottom - graphPtr->top + 1) +
863 	(2 * graphPtr->plotBorderWidth);
864 
865     Blt_FontToPostScript(psToken, graphPtr->titleTextStyle.font);
866     Blt_RegionToPostScript(psToken, (double)x, (double)y, width, height);
867     if (graphPtr->postscript->decorations) {
868 	Blt_BackgroundToPostScript(psToken, graphPtr->plotBg);
869     } else {
870 	Blt_ClearBackgroundToPostScript(psToken);
871     }
872     Blt_AppendToPostScript(psToken, "Fill\n", (char *)NULL);
873     Blt_AppendToPostScript(psToken, "gsave clip\n\n", (char *)NULL);
874     /* Draw the grid, elements, and markers in the plotting area. */
875     if (!graphPtr->gridPtr->hidden) {
876 	Blt_GridToPostScript(graphPtr, psToken);
877     }
878     Blt_MarkersToPostScript(graphPtr, psToken, TRUE);
879     if ((Blt_LegendSite(graphPtr->legend) & LEGEND_IN_PLOT) &&
880 	(!Blt_LegendIsRaised(graphPtr->legend))) {
881 	/* Print legend underneath elements and markers */
882 	Blt_LegendToPostScript(graphPtr->legend, psToken);
883     }
884     Blt_AxisLimitsToPostScript(graphPtr, psToken);
885     Blt_ElementsToPostScript(graphPtr, psToken);
886     if ((Blt_LegendSite(graphPtr->legend) & LEGEND_IN_PLOT) &&
887 	(Blt_LegendIsRaised(graphPtr->legend))) {
888 	/* Print legend above elements (but not markers) */
889 	Blt_LegendToPostScript(graphPtr->legend, psToken);
890     }
891     Blt_MarkersToPostScript(graphPtr, psToken, FALSE);
892     Blt_ActiveElementsToPostScript(graphPtr, psToken);
893     Blt_AppendToPostScript(psToken, "\n",
894 	"% Unset clipping\n",
895 	"grestore\n\n", (char *)NULL);
896     MarginsToPostScript(graphPtr, psToken);
897     Blt_AppendToPostScript(psToken,
898 	"showpage\n",
899 	"%%Trailer\n",
900 	"grestore\n",
901 	"end\n",
902 	"%%EOF\n", (char *)NULL);
903   error:
904     /* Reset height and width of graph window */
905     graphPtr->width = Tk_Width(graphPtr->tkwin);
906     graphPtr->height = Tk_Height(graphPtr->tkwin);
907     graphPtr->flags = MAP_WORLD;
908 
909     /*
910      * Redraw the graph in order to re-calculate the layout as soon as
911      * possible. This is in the case the crosshairs are active.
912      */
913     Blt_EventuallyRedrawGraph(graphPtr);
914     return result;
915 }
916 
917 #ifdef WIN32
918 
919 static void
InitAPMHeader(Tk_Window tkwin,int width,int height,APMHEADER * headerPtr)920 InitAPMHeader(
921     Tk_Window tkwin,
922     int width, int height,
923     APMHEADER *headerPtr)
924 {
925     unsigned int *p;
926     unsigned int sum;
927     Screen *screen;
928 #define MM_INCH		25.4
929     double dpiX, dpiY;
930 
931     headerPtr->key = 0x9ac6cdd7L;
932     headerPtr->hmf = 0;
933     headerPtr->inch = 1440;
934 
935     screen = Tk_Screen(tkwin);
936     dpiX = (WidthOfScreen(screen) * MM_INCH) / WidthMMOfScreen(screen);
937     dpiY = (HeightOfScreen(screen) * MM_INCH) / HeightMMOfScreen(screen);
938 
939     headerPtr->bbox.Left = headerPtr->bbox.Top = 0;
940     headerPtr->bbox.Bottom = (SHORT)((width * 1440) / dpiX);
941     headerPtr->bbox.Right = (SHORT)((height * 1440) / dpiY);
942     headerPtr->reserved = 0;
943     sum = 0;
944     for (p = (unsigned int *)headerPtr;
945 	 p < (unsigned int *)&(headerPtr->checksum); p++) {
946 	sum ^= *p;
947     }
948     headerPtr->checksum = sum;
949 }
950 
951 /*
952  * --------------------------------------------------------------------------
953  *
954  * CreateWindowEPS --
955  *
956  * 	Generates an EPS file with a Window metafile preview.
957  *
958  *	Windows metafiles aren't very robust.  Including exactly the
959  *	same metafile (one embedded in a DOS EPS, the other as .wmf
960  *	file) will play back differently.
961  *
962  * Results:
963  *	None.
964  *
965  * --------------------------------------------------------------------------
966  */
967 static int
CreateWindowsEPS(Graph * graphPtr,PsToken psToken,FILE * f)968 CreateWindowsEPS(
969     Graph *graphPtr,
970     PsToken psToken,
971     FILE *f)
972 {
973     DWORD size;
974     DOSEPSHEADER epsHeader;
975     HANDLE hMem;
976     HDC hRefDC, hDC;
977     HENHMETAFILE hMetaFile;
978     Tcl_DString dString;
979     TkWinDC drawableDC;
980     TkWinDCState state;
981     int result;
982     unsigned char *buffer, *psBuffer;
983 
984     Blt_AppendToPostScript(psToken, "\n", (char *)NULL);
985     psBuffer = Blt_PostScriptFromToken(psToken);
986     /*
987      * Fill out as much information as we can into the DOS EPS header.
988      * We won't know the start of the length of the WMF segment until
989      * we create the metafile.
990      */
991     epsHeader.magic[0] = 0xC5;
992     epsHeader.magic[1] = 0xD0;
993     epsHeader.magic[2] = 0xD3;
994     epsHeader.magic[3] = 0xC6;
995     epsHeader.psStart = sizeof(epsHeader);
996     epsHeader.psLength = strlen(psBuffer) + 1;
997     epsHeader.wmfStart = epsHeader.psStart + epsHeader.psLength;
998     epsHeader.wmfLength = 0L;	/* Fill in later. */
999     epsHeader.tiffStart = 0L;
1000     epsHeader.tiffLength = 0L;
1001     epsHeader.checksum = 0xFFFF;
1002 
1003     result = TCL_ERROR;
1004     hRefDC = TkWinGetDrawableDC(graphPtr->display,
1005 	Tk_WindowId(graphPtr->tkwin), &state);
1006 
1007     /* Build a description string. */
1008     Tcl_DStringInit(&dString);
1009     Tcl_DStringAppend(&dString, "BLT Graph ", -1);
1010     Tcl_DStringAppend(&dString, BLT_VERSION, -1);
1011     Tcl_DStringAppend(&dString, "\0", -1);
1012     Tcl_DStringAppend(&dString, Tk_PathName(graphPtr->tkwin), -1);
1013     Tcl_DStringAppend(&dString, "\0", -1);
1014 
1015     hDC = CreateEnhMetaFile(hRefDC, NULL, NULL, Tcl_DStringValue(&dString));
1016     Tcl_DStringFree(&dString);
1017 
1018     if (hDC == NULL) {
1019 	Tcl_AppendResult(graphPtr->interp, "can't create metafile: ",
1020 		Blt_LastError(), (char *)NULL);
1021 	return TCL_ERROR;
1022     }
1023     /* Assemble a Tk drawable that points to the metafile and let the
1024      * graph's drawing routine draw into it. */
1025     drawableDC.hdc = hDC;
1026     drawableDC.type = TWD_WINDC;
1027 
1028     graphPtr->width = Tk_Width(graphPtr->tkwin);
1029     graphPtr->height = Tk_Height(graphPtr->tkwin);
1030     graphPtr->flags |= RESET_WORLD;
1031     Blt_LayoutGraph(graphPtr);
1032     Blt_DrawGraph(graphPtr, (Drawable)&drawableDC, FALSE);
1033     GdiFlush();
1034     hMetaFile = CloseEnhMetaFile(hDC);
1035 
1036     size = GetWinMetaFileBits(hMetaFile, 0, NULL, MM_ANISOTROPIC, hRefDC);
1037     hMem = GlobalAlloc(GHND, size);
1038     if (hMem == NULL) {
1039 	Tcl_AppendResult(graphPtr->interp, "can't allocate global memory:",
1040 		Blt_LastError(), (char *)NULL);
1041 	goto error;
1042     }
1043     buffer = (LPVOID)GlobalLock(hMem);
1044     if (!GetWinMetaFileBits(hMetaFile, size, buffer, MM_ANISOTROPIC, hRefDC)) {
1045 	Tcl_AppendResult(graphPtr->interp, "can't get metafile data:",
1046 		Blt_LastError(), (char *)NULL);
1047 	goto error;
1048     }
1049 
1050     /*
1051      * Fix up the EPS header with the correct metafile length and PS
1052      * offset (now that we what they are).
1053      */
1054     epsHeader.wmfLength = size;
1055     epsHeader.wmfStart = epsHeader.psStart + epsHeader.psLength;
1056 
1057     /* Write out the eps header, */
1058     if (fwrite(&epsHeader, 1, sizeof(epsHeader), f) != sizeof(epsHeader)) {
1059 	Tcl_AppendResult(graphPtr->interp, "error writing eps header:",
1060 		Blt_LastError(), (char *)NULL);
1061 	goto error;
1062     }
1063     /* the PostScript, */
1064     if (fwrite(psBuffer, 1, epsHeader.psLength, f) != epsHeader.psLength) {
1065 	Tcl_AppendResult(graphPtr->interp, "error writing PostScript data:",
1066 		Blt_LastError(), (char *)NULL);
1067 	goto error;
1068     }
1069     /* and finally the metadata itself. */
1070     if (fwrite(buffer, 1, size, f) != size) {
1071 	Tcl_AppendResult(graphPtr->interp, "error writing metafile data:",
1072 		Blt_LastError(), (char *)NULL);
1073 	goto error;
1074     }
1075     result = TCL_OK;
1076 
1077  error:
1078     DeleteEnhMetaFile(hMetaFile);
1079     TkWinReleaseDrawableDC(Tk_WindowId(graphPtr->tkwin), hRefDC, &state);
1080     fclose(f);
1081     if (hMem != NULL) {
1082 	GlobalUnlock(hMem);
1083 	GlobalFree(hMem);
1084     }
1085     graphPtr->flags = MAP_WORLD;
1086     Blt_EventuallyRedrawGraph(graphPtr);
1087     return result;
1088 }
1089 
1090 #endif /*WIN32*/
1091 
1092 /*
1093  *----------------------------------------------------------------------
1094  *
1095  * OutputOp --
1096  *
1097  *      This procedure is invoked to print the graph in a file.
1098  *
1099  * Results:
1100  *      Standard TCL result.  TCL_OK if plot was successfully printed,
1101  *	TCL_ERROR otherwise.
1102  *
1103  * Side effects:
1104  *      A new PostScript file is created.
1105  *
1106  *----------------------------------------------------------------------
1107  */
1108 static int
OutputOp(graphPtr,interp,argc,argv)1109 OutputOp(graphPtr, interp, argc, argv)
1110     Graph *graphPtr;		/* Graph widget record */
1111     Tcl_Interp *interp;
1112     int argc;			/* Number of options in argv vector */
1113     CONST char **argv;		/* Option vector */
1114 {
1115     PostScript *psPtr = (PostScript *)graphPtr->postscript;
1116     FILE *f = NULL;
1117     PsToken psToken;
1118     CONST char *fileName;		/* Name of file to write PostScript output
1119                                  * If NULL, output is returned via
1120                                  * interp->result. */
1121     fileName = NULL;
1122     if (argc > 3) {
1123 	if (argv[3][0] != '-') {
1124 	    fileName = argv[3];	/* First argument is the file name. */
1125 	    argv++, argc--;
1126 	}
1127 	if (Tk_ConfigureWidget(interp, graphPtr->tkwin, configSpecs, argc - 3,
1128 		argv + 3, (char *)psPtr, TK_CONFIG_ARGV_ONLY) != TCL_OK) {
1129 	    return TCL_ERROR;
1130 	}
1131 	if (fileName != NULL) {
1132 #ifdef WIN32
1133 	    f = fopen(fileName, "wb");
1134 #else
1135 	    f = fopen(fileName, "w");
1136 #endif
1137 	    if (f == NULL) {
1138 		Tcl_AppendResult(interp, "can't create \"", fileName, "\": ",
1139 		    Tcl_PosixError(interp), (char *)NULL);
1140 		return TCL_ERROR;
1141 	    }
1142 	}
1143     }
1144     psToken = Blt_GetPsToken(graphPtr->interp, graphPtr->tkwin);
1145     psToken->fontVarName = psPtr->fontVarName;
1146     psToken->colorVarName = psPtr->colorVarName;
1147     psToken->colorMode = psPtr->colorMode;
1148 
1149     if (GraphToPostScript(graphPtr, fileName, psToken) != TCL_OK) {
1150 	goto error;
1151     }
1152     /*
1153      * If a file name was given, write the results to that file
1154      */
1155     if (f != NULL) {
1156 #ifdef WIN32
1157 	if ((psPtr->addPreview) && (psPtr->previewFormat != PS_PREVIEW_EPSI)) {
1158 	    if (CreateWindowsEPS(graphPtr, psToken, f)) {
1159 		return TCL_ERROR;
1160 	    }
1161 	} else {
1162 	    fputs(Blt_PostScriptFromToken(psToken), f);
1163 	    if (ferror(f)) {
1164 		Tcl_AppendResult(interp, "error writing file \"", fileName,
1165 			"\": ", Tcl_PosixError(interp), (char *)NULL);
1166 		goto error;
1167 	    }
1168 	}
1169 #else
1170 	fputs(Blt_PostScriptFromToken(psToken), f);
1171 	if (ferror(f)) {
1172 	    Tcl_AppendResult(interp, "error writing file \"", fileName, "\": ",
1173 		Tcl_PosixError(interp), (char *)NULL);
1174 	    goto error;
1175 	}
1176 #endif /* WIN32 */
1177         fclose(f);
1178     } else {
1179 	Tcl_SetResult(interp, Blt_PostScriptFromToken(psToken), TCL_VOLATILE);
1180     }
1181     Blt_ReleasePsToken(psToken);
1182     return TCL_OK;
1183 
1184   error:
1185     if (f != NULL) {
1186 	fclose(f);
1187     }
1188     Blt_ReleasePsToken(psToken);
1189     return TCL_ERROR;
1190 }
1191 
1192 /*
1193  *----------------------------------------------------------------------
1194  *
1195  * Blt_CreatePostScript --
1196  *
1197  *      Creates a postscript structure.
1198  *
1199  * Results:
1200  *      Always TCL_OK.
1201  *
1202  * Side effects:
1203  *      A new PostScript structure is created.
1204  *
1205  *----------------------------------------------------------------------
1206  */
1207 int
Blt_CreatePostScript(graphPtr)1208 Blt_CreatePostScript(graphPtr)
1209     Graph *graphPtr;
1210 {
1211     PostScript *psPtr;
1212 
1213     psPtr = Blt_Calloc(1, sizeof(PostScript));
1214     assert(psPtr);
1215     psPtr->colorMode = PS_MODE_COLOR;
1216     psPtr->center = TRUE;
1217     psPtr->decorations = TRUE;
1218     graphPtr->postscript = psPtr;
1219 
1220     if (Blt_ConfigureWidgetComponent(graphPtr->interp, graphPtr->tkwin,
1221 	    "postscript", "Postscript", configSpecs, 0, (char **)NULL,
1222 	    (char *)psPtr, 0) != TCL_OK) {
1223 	return TCL_ERROR;
1224     }
1225     return TCL_OK;
1226 }
1227 
1228 /*
1229  *--------------------------------------------------------------
1230  *
1231  * Blt_PostScriptOp --
1232  *
1233  *	This procedure is invoked to process the Tcl command
1234  *	that corresponds to a widget managed by this module.
1235  *	See the user documentation for details on what it does.
1236  *
1237  * Results:
1238  *	A standard Tcl result.
1239  *
1240  * Side effects:
1241  *	See the user documentation.
1242  *
1243  *--------------------------------------------------------------
1244  */
1245 static Blt_OpSpec psOps[] =
1246 {
1247     {"cget", 2, (Blt_Op)CgetOp, 4, 4, "option",},
1248     {"configure", 2, (Blt_Op)ConfigureOp, 3, 0, "?option value?...",},
1249     {"output", 1, (Blt_Op)OutputOp, 3, 0,
1250 	"?fileName? ?option value?...",},
1251 };
1252 
1253 static int nPsOps = sizeof(psOps) / sizeof(Blt_OpSpec);
1254 
1255 int
Blt_PostScriptOp(graphPtr,interp,argc,argv)1256 Blt_PostScriptOp(graphPtr, interp, argc, argv)
1257     Graph *graphPtr;		/* Graph widget record */
1258     Tcl_Interp *interp;
1259     int argc;			/* # arguments */
1260     char **argv;		/* Argument list */
1261 {
1262     Blt_Op proc;
1263     int result;
1264 
1265     proc = Blt_GetOp(interp, nPsOps, psOps, BLT_OP_ARG2, argc, argv, 0);
1266     if (proc == NULL) {
1267 	return TCL_ERROR;
1268     }
1269     result = (*proc) (graphPtr, interp, argc, argv);
1270     return result;
1271 }
1272 
1273