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