summaryrefslogtreecommitdiff
path: root/blt/src/bltGrPs.c
diff options
context:
space:
mode:
Diffstat (limited to 'blt/src/bltGrPs.c')
-rw-r--r--blt/src/bltGrPs.c1271
1 files changed, 1271 insertions, 0 deletions
diff --git a/blt/src/bltGrPs.c b/blt/src/bltGrPs.c
new file mode 100644
index 00000000000..a8e2c11b188
--- /dev/null
+++ b/blt/src/bltGrPs.c
@@ -0,0 +1,1271 @@
+/*
+ * bltGrPs.c --
+ *
+ * This module implements the "postscript" operation for BLT
+ * graph widget.
+ *
+ * Copyright 1991-1998 Lucent Technologies, Inc.
+ *
+ * Permission to use, copy, modify, and distribute this software and
+ * its documentation for any purpose and without fee is hereby
+ * granted, provided that the above copyright notice appear in all
+ * copies and that both that the copyright notice and warranty
+ * disclaimer appear in supporting documentation, and that the names
+ * of Lucent Technologies any of their entities not be used in
+ * advertising or publicity pertaining to distribution of the software
+ * without specific, written prior permission.
+ *
+ * Lucent Technologies disclaims all warranties with regard to this
+ * software, including all implied warranties of merchantability and
+ * fitness. In no event shall Lucent Technologies be liable for any
+ * special, indirect or consequential damages or any damages
+ * whatsoever resulting from loss of use, data or profits, whether in
+ * an action of contract, negligence or other tortuous action, arising
+ * out of or in connection with the use or performance of this
+ * software.
+ */
+
+/*
+ * -----------------------------------------------------------------
+ *
+ * PostScript routines to print a graph
+ *
+ * -----------------------------------------------------------------
+ */
+#include "bltGraph.h"
+#include <X11/Xutil.h>
+#if defined(__STDC__)
+#include <stdarg.h>
+#else
+#include <varargs.h>
+#endif
+
+#define PS_PREVIEW_EPSI 0
+#define PS_PREVIEW_WMF 1
+#define PS_PREVIEW_TIFF 2
+
+static Tk_OptionParseProc StringToColorMode;
+static Tk_OptionPrintProc ColorModeToString;
+static Tk_CustomOption colorModeOption =
+{
+ StringToColorMode, ColorModeToString, (ClientData)0,
+};
+static Tk_OptionParseProc StringToFormat;
+static Tk_OptionPrintProc FormatToString;
+static Tk_CustomOption formatOption =
+{
+ StringToFormat, FormatToString, (ClientData)0,
+};
+extern Tk_CustomOption bltDistanceOption;
+extern Tk_CustomOption bltPositiveDistanceOption;
+extern Tk_CustomOption bltPadOption;
+
+#define DEF_PS_CENTER "yes"
+#define DEF_PS_COLOR_MAP (char *)NULL
+#define DEF_PS_COLOR_MODE "color"
+#define DEF_PS_DECORATIONS "yes"
+#define DEF_PS_FONT_MAP (char *)NULL
+#define DEF_PS_FOOTER "no"
+#define DEF_PS_HEIGHT "0"
+#define DEF_PS_LANDSCAPE "no"
+#define DEF_PS_MAXPECT "no"
+#define DEF_PS_PADX "1.0i"
+#define DEF_PS_PADY "1.0i"
+#define DEF_PS_PAPERHEIGHT "11.0i"
+#define DEF_PS_PAPERWIDTH "8.5i"
+#define DEF_PS_PREVIEW "no"
+#define DEF_PS_PREVIEW_FORMAT "epsi"
+#define DEF_PS_WIDTH "0"
+
+static Tk_ConfigSpec configSpecs[] =
+{
+ {TK_CONFIG_BOOLEAN, "-center", "center", "Center",
+ DEF_PS_CENTER, Tk_Offset(PostScript, center),
+ TK_CONFIG_DONT_SET_DEFAULT},
+ {TK_CONFIG_STRING, "-colormap", "colorMap", "ColorMap",
+ DEF_PS_COLOR_MAP, Tk_Offset(PostScript, colorVarName),
+ TK_CONFIG_NULL_OK},
+ {TK_CONFIG_CUSTOM, "-colormode", "colorMode", "ColorMode",
+ DEF_PS_COLOR_MODE, Tk_Offset(PostScript, colorMode),
+ TK_CONFIG_DONT_SET_DEFAULT, &colorModeOption},
+ {TK_CONFIG_BOOLEAN, "-decorations", "decorations", "Decorations",
+ DEF_PS_DECORATIONS, Tk_Offset(PostScript, decorations),
+ TK_CONFIG_DONT_SET_DEFAULT},
+ {TK_CONFIG_STRING, "-fontmap", "fontMap", "FontMap",
+ DEF_PS_FONT_MAP, Tk_Offset(PostScript, fontVarName),
+ TK_CONFIG_NULL_OK},
+ {TK_CONFIG_BOOLEAN, "-footer", "footer", "Footer",
+ DEF_PS_FOOTER, Tk_Offset(PostScript, footer),
+ TK_CONFIG_DONT_SET_DEFAULT},
+ {TK_CONFIG_CUSTOM, "-height", "height", "Height",
+ DEF_PS_HEIGHT, Tk_Offset(PostScript, reqHeight),
+ TK_CONFIG_DONT_SET_DEFAULT, &bltDistanceOption},
+ {TK_CONFIG_BOOLEAN, "-landscape", "landscape", "Landscape",
+ DEF_PS_LANDSCAPE, Tk_Offset(PostScript, landscape),
+ TK_CONFIG_DONT_SET_DEFAULT},
+ {TK_CONFIG_BOOLEAN, "-maxpect", "maxpect", "Maxpect",
+ DEF_PS_MAXPECT, Tk_Offset(PostScript, maxpect),
+ TK_CONFIG_DONT_SET_DEFAULT},
+ {TK_CONFIG_CUSTOM, "-padx", "padX", "PadX",
+ DEF_PS_PADX, Tk_Offset(PostScript, padX), 0, &bltPadOption},
+ {TK_CONFIG_CUSTOM, "-pady", "padY", "PadY",
+ DEF_PS_PADY, Tk_Offset(PostScript, padY), 0, &bltPadOption},
+ {TK_CONFIG_CUSTOM, "-paperheight", "paperHeight", "PaperHeight",
+ DEF_PS_PAPERHEIGHT, Tk_Offset(PostScript, reqPaperHeight),
+ 0, &bltPositiveDistanceOption},
+ {TK_CONFIG_CUSTOM, "-paperwidth", "paperWidth", "PaperWidth",
+ DEF_PS_PAPERWIDTH, Tk_Offset(PostScript, reqPaperWidth),
+ 0, &bltPositiveDistanceOption},
+ {TK_CONFIG_BOOLEAN, "-preview", "preview", "Preview",
+ DEF_PS_PREVIEW, Tk_Offset(PostScript, addPreview),
+ TK_CONFIG_DONT_SET_DEFAULT},
+ {TK_CONFIG_CUSTOM, "-previewformat", "previewFormat", "PreviewFormat",
+ DEF_PS_PREVIEW_FORMAT, Tk_Offset(PostScript, previewFormat),
+ TK_CONFIG_DONT_SET_DEFAULT, &formatOption},
+ {TK_CONFIG_CUSTOM, "-width", "width", "Width",
+ DEF_PS_WIDTH, Tk_Offset(PostScript, reqWidth),
+ TK_CONFIG_DONT_SET_DEFAULT, &bltDistanceOption},
+ {TK_CONFIG_END, NULL, NULL, NULL, NULL, 0, 0}
+};
+
+extern void Blt_MarkersToPostScript _ANSI_ARGS_((Graph *graphPtr,
+ PsToken psToken, int under));
+extern void Blt_ElementsToPostScript _ANSI_ARGS_((Graph *graphPtr,
+ PsToken psToken));
+extern void Blt_ActiveElementsToPostScript _ANSI_ARGS_((Graph *graphPtr,
+ PsToken psToken));
+extern void Blt_LegendToPostScript _ANSI_ARGS_((Legend *legendPtr,
+ PsToken psToken));
+extern void Blt_GridToPostScript _ANSI_ARGS_((Graph *graphPtr,
+ PsToken psToken));
+extern void Blt_AxesToPostScript _ANSI_ARGS_((Graph *graphPtr,
+ PsToken psToken));
+extern void Blt_AxisLimitsToPostScript _ANSI_ARGS_((Graph *graphPtr,
+ PsToken psToken));
+/*
+ *----------------------------------------------------------------------
+ *
+ * StringToColorMode --
+ *
+ * Convert the string representation of a PostScript color mode
+ * into the enumerated type representing the color level:
+ *
+ * PS_MODE_COLOR - Full color
+ * PS_MODE_GREYSCALE - Color converted to greyscale
+ * PS_MODE_MONOCHROME - Only black and white
+ *
+ * Results:
+ * A standard Tcl result. The color level is written into the
+ * page layout information structure.
+ *
+ * Side Effects:
+ * Future invocations of the "postscript" option will use this
+ * variable to determine how color information will be displayed
+ * in the PostScript output it produces.
+ *
+ *----------------------------------------------------------------------
+ */
+/*ARGSUSED*/
+static int
+StringToColorMode(clientData, interp, tkwin, string, widgRec, offset)
+ ClientData clientData; /* Not used. */
+ Tcl_Interp *interp; /* Interpreter to send results back to */
+ Tk_Window tkwin; /* Not used. */
+ char *string; /* New value. */
+ char *widgRec; /* Widget record */
+ int offset; /* Offset of field in record */
+{
+ PsColorMode *modePtr = (PsColorMode *) (widgRec + offset);
+ unsigned int length;
+ char c;
+
+ c = string[0];
+ length = strlen(string);
+ if ((c == 'c') && (strncmp(string, "color", length) == 0)) {
+ *modePtr = PS_MODE_COLOR;
+ } else if ((c == 'g') && (strncmp(string, "grayscale", length) == 0)) {
+ *modePtr = PS_MODE_GREYSCALE;
+ } else if ((c == 'g') && (strncmp(string, "greyscale", length) == 0)) {
+ *modePtr = PS_MODE_GREYSCALE;
+ } else if ((c == 'm') && (strncmp(string, "monochrome", length) == 0)) {
+ *modePtr = PS_MODE_MONOCHROME;
+ } else {
+ Tcl_AppendResult(interp, "bad color mode \"", string, "\": should be \
+\"color\", \"greyscale\", or \"monochrome\"", (char *)NULL);
+ return TCL_ERROR;
+ }
+ return TCL_OK;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * NameOfColorMode --
+ *
+ * Convert the PostScript mode value into the string representing
+ * a valid color mode.
+ *
+ * Results:
+ * The static string representing the color mode is returned.
+ *
+ *----------------------------------------------------------------------
+ */
+static char *
+NameOfColorMode(colorMode)
+ PsColorMode colorMode;
+{
+ switch (colorMode) {
+ case PS_MODE_COLOR:
+ return "color";
+ case PS_MODE_GREYSCALE:
+ return "greyscale";
+ case PS_MODE_MONOCHROME:
+ return "monochrome";
+ default:
+ return "unknown color mode";
+ }
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * ColorModeToString --
+ *
+ * Convert the current color mode into the string representing a
+ * valid color mode.
+ *
+ * Results:
+ * The string representing the color mode is returned.
+ *
+ *----------------------------------------------------------------------
+ */
+/*ARGSUSED*/
+static char *
+ColorModeToString(clientData, tkwin, widgRec, offset, freeProcPtr)
+ ClientData clientData; /* Not used. */
+ Tk_Window tkwin; /* Not used. */
+ char *widgRec; /* Widget record. */
+ int offset; /* field of colorMode in record */
+ Tcl_FreeProc **freeProcPtr; /* Not used. */
+{
+ PsColorMode mode = *(PsColorMode *) (widgRec + offset);
+
+ return NameOfColorMode(mode);
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * StringToFormat --
+ *
+ * Convert the string of the PostScript preview format into
+ * an enumerated type representing the desired format. The
+ * available formats are:
+ *
+ * PS_PREVIEW_WMF - Windows Metafile.
+ * PS_PREVIEW_TIFF - TIFF bitmap image.
+ * PS_PREVIEW_EPSI - Device independent ASCII preview
+ *
+ * Results:
+ * A standard Tcl result. The format is written into the
+ * page layout information structure.
+ *
+ * Side Effects:
+ * Future invocations of the "postscript" option will use this
+ * variable to determine how to format a preview image (if one
+ * is selected) when the PostScript output is produced.
+ *
+ *----------------------------------------------------------------------
+ */
+/*ARGSUSED*/
+static int
+StringToFormat(clientData, interp, tkwin, string, widgRec, offset)
+ ClientData clientData; /* Not used. */
+ Tcl_Interp *interp; /* Interpreter to send results back to */
+ Tk_Window tkwin; /* Not used. */
+ char *string; /* New value. */
+ char *widgRec; /* Widget record */
+ int offset; /* Offset of field in record */
+{
+ int *formatPtr = (int *) (widgRec + offset);
+ unsigned int length;
+ char c;
+
+ c = string[0];
+ length = strlen(string);
+ if ((c == 'c') && (strncmp(string, "epsi", length) == 0)) {
+ *formatPtr = PS_PREVIEW_EPSI;
+#ifdef WIN32
+#ifdef HAVE_TIFF_H
+ } else if ((c == 't') && (strncmp(string, "tiff", length) == 0)) {
+ *formatPtr = PS_PREVIEW_TIFF;
+#endif /* HAVE_TIFF_H */
+ } else if ((c == 'w') && (strncmp(string, "wmf", length) == 0)) {
+ *formatPtr = PS_PREVIEW_WMF;
+#endif /* WIN32 */
+ } else {
+ Tcl_AppendResult(interp, "bad format \"", string, "\": should be ",
+#ifdef WIN32
+#ifdef HAVE_TIFF_H
+ "\"tiff\" or ",
+#endif /* HAVE_TIFF_H */
+ "\"wmf\" or ",
+#endif /* WIN32 */
+ "\"epsi\"", (char *)NULL);
+ return TCL_ERROR;
+ }
+ return TCL_OK;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * FormatToString --
+ *
+ * Convert the preview format into the string representing its
+ * type.
+ *
+ * Results:
+ * The string representing the preview format is returned.
+ *
+ *----------------------------------------------------------------------
+ */
+/*ARGSUSED*/
+static char *
+FormatToString(clientData, tkwin, widgRec, offset, freeProcPtr)
+ ClientData clientData; /* Not used. */
+ Tk_Window tkwin; /* Not used. */
+ char *widgRec; /* PostScript structure record */
+ int offset; /* field of colorMode in record */
+ Tcl_FreeProc **freeProcPtr; /* Not used. */
+{
+ int format = *(int *)(widgRec + offset);
+
+ switch (format) {
+ case PS_PREVIEW_EPSI:
+ return "epsi";
+ case PS_PREVIEW_WMF:
+ return "wmf";
+ case PS_PREVIEW_TIFF:
+ return "tiff";
+ }
+ return "?unknown preview format?";
+}
+
+void
+Blt_DestroyPostScript(graphPtr)
+ Graph *graphPtr;
+{
+ Tk_FreeOptions(configSpecs, (char *)graphPtr->postscript,
+ graphPtr->display, 0);
+ Blt_Free(graphPtr->postscript);
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * CgetOp --
+ *
+ *----------------------------------------------------------------------
+ */
+/*ARGSUSED*/
+static int
+CgetOp(graphPtr, interp, argc, argv)
+ Graph *graphPtr;
+ Tcl_Interp *interp;
+ int argc;
+ char *argv[];
+{
+ PostScript *psPtr = (PostScript *)graphPtr->postscript;
+
+ if (Tk_ConfigureValue(interp, graphPtr->tkwin, configSpecs, (char *)psPtr,
+ argv[3], 0) != TCL_OK) {
+ return TCL_ERROR;
+ }
+ return TCL_OK;
+}
+
+/*
+ * ----------------------------------------------------------------------
+ *
+ * ConfigureOp --
+ *
+ * This procedure is invoked to print the graph in a file.
+ *
+ * Results:
+ * A standard TCL result.
+ *
+ * Side effects:
+ * A new PostScript file is created.
+ *
+ * ----------------------------------------------------------------------
+ */
+static int
+ConfigureOp(graphPtr, interp, argc, argv)
+ Graph *graphPtr;
+ Tcl_Interp *interp;
+ int argc; /* Number of options in argv vector */
+ char **argv; /* Option vector */
+{
+ int flags = TK_CONFIG_ARGV_ONLY;
+ PostScript *psPtr = (PostScript *)graphPtr->postscript;
+
+ if (argc == 3) {
+ return Tk_ConfigureInfo(interp, graphPtr->tkwin, configSpecs,
+ (char *)psPtr, (char *)NULL, flags);
+ } else if (argc == 4) {
+ return Tk_ConfigureInfo(interp, graphPtr->tkwin, configSpecs,
+ (char *)psPtr, argv[3], flags);
+ }
+ if (Tk_ConfigureWidget(interp, graphPtr->tkwin, configSpecs, argc - 3,
+ argv + 3, (char *)psPtr, flags) != TCL_OK) {
+ return TCL_ERROR;
+ }
+ return TCL_OK;
+}
+
+/*
+ * --------------------------------------------------------------------------
+ *
+ * ComputeBoundingBox --
+ *
+ * Computes the bounding box for the PostScript plot. First get
+ * the size of the plot (by default, it's the size of graph's X
+ * window). If the plot plus the page border is bigger than the
+ * designated paper size, or if the "-maxpect" option is turned
+ * on, scale the plot to the page.
+ *
+ * Note: All coordinates/sizes are in screen coordinates, not
+ * PostScript coordinates. This includes the computed
+ * bounding box and paper size. They will be scaled to
+ * printer points later.
+ *
+ * Results:
+ * Returns the height of the paper in screen coordinates.
+ *
+ * Side Effects:
+ * The graph dimensions (width and height) are changed to the
+ * requested PostScript plot size.
+ *
+ * --------------------------------------------------------------------------
+ */
+static int
+ComputeBoundingBox(graphPtr, psPtr)
+ Graph *graphPtr;
+ PostScript *psPtr;
+{
+ int paperWidth, paperHeight;
+ int x, y, hSize, vSize, hBorder, vBorder;
+ double hScale, vScale, scale;
+
+ x = psPtr->padLeft;
+ y = psPtr->padTop;
+ hBorder = PADDING(psPtr->padX);
+ vBorder = PADDING(psPtr->padY);
+
+ if (psPtr->reqWidth > 0) {
+ graphPtr->width = psPtr->reqWidth;
+ }
+ if (psPtr->reqHeight > 0) {
+ graphPtr->height = psPtr->reqHeight;
+ }
+ if (psPtr->landscape) {
+ hSize = graphPtr->height;
+ vSize = graphPtr->width;
+ } else {
+ hSize = graphPtr->width;
+ vSize = graphPtr->height;
+ }
+ /*
+ * If the paper size wasn't specified, set it to the graph size plus
+ * the paper border.
+ */
+ paperWidth = psPtr->reqPaperWidth;
+ paperHeight = psPtr->reqPaperHeight;
+ if (paperWidth < 1) {
+ paperWidth = hSize + hBorder;
+ }
+ if (paperHeight < 1) {
+ paperHeight = vSize + vBorder;
+ }
+ hScale = vScale = 1.0;
+ /*
+ * Scale the plot size (the graph itself doesn't change size) if
+ * it's bigger than the paper or if -maxpect was set.
+ */
+ if ((psPtr->maxpect) || ((hSize + hBorder) > paperWidth)) {
+ hScale = (double)(paperWidth - hBorder) / (double)hSize;
+ }
+ if ((psPtr->maxpect) || ((vSize + vBorder) > paperHeight)) {
+ vScale = (double)(paperHeight - vBorder) / (double)vSize;
+ }
+ scale = MIN(hScale, vScale);
+ if (scale != 1.0) {
+ hSize = (int)((hSize * scale) + 0.5);
+ vSize = (int)((vSize * scale) + 0.5);
+ }
+ psPtr->pageScale = scale;
+ if (psPtr->center) {
+ if (paperWidth > hSize) {
+ x = (paperWidth - hSize) / 2;
+ }
+ if (paperHeight > vSize) {
+ y = (paperHeight - vSize) / 2;
+ }
+ }
+ psPtr->left = x;
+ psPtr->bottom = y;
+ psPtr->right = x + hSize - 1;
+ psPtr->top = y + vSize - 1;
+
+ graphPtr->flags |= LAYOUT_NEEDED | MAP_WORLD;
+ Blt_LayoutGraph(graphPtr);
+ return paperHeight;
+}
+
+/*
+ * --------------------------------------------------------------------------
+ *
+ * PreviewImage --
+ *
+ * Generates a EPSI thumbnail of the graph. The thumbnail is
+ * restricted to a certain size. This is to keep the size of the
+ * PostScript file small and the processing time low.
+ *
+ * The graph is drawn into a pixmap. We then take a snapshot
+ * of that pixmap, and rescale it to a smaller image. Finally,
+ * the image is dumped to PostScript.
+ *
+ * Results:
+ * None.
+ *
+ * --------------------------------------------------------------------------
+ */
+static void
+PreviewImage(graphPtr, psToken)
+ Graph *graphPtr;
+ PsToken psToken;
+{
+ PostScript *psPtr = (PostScript *)graphPtr->postscript;
+ int noBackingStore = 0;
+ Pixmap drawable;
+ Blt_Colorimage image;
+ int nLines;
+ Tcl_DString dString;
+
+ /* Create a pixmap and draw the graph into it. */
+
+ drawable = Tk_GetPixmap(graphPtr->display, Tk_WindowId(graphPtr->tkwin),
+ graphPtr->width, graphPtr->height, Tk_Depth(graphPtr->tkwin));
+ Blt_DrawGraph(graphPtr, drawable, noBackingStore);
+
+ /* Get a color image from the pixmap */
+ image = Blt_DrawableToColorimage(graphPtr->tkwin, drawable, 0, 0,
+ graphPtr->width, graphPtr->height, 1.0);
+ Tk_FreePixmap(graphPtr->display, drawable);
+ if (image == NULL) {
+ return; /* Can't grab pixmap? */
+ }
+#ifdef THUMBNAIL_PREVIEW
+ {
+ double scale, xScale, yScale;
+ int width, height;
+ Blt_Colorimage destImage;
+
+ /* Scale the source image into a size appropriate for a thumbnail. */
+#define PS_MAX_PREVIEW_WIDTH 300.0
+#define PS_MAX_PREVIEW_HEIGHT 300.0
+ xScale = PS_MAX_PREVIEW_WIDTH / (double)graphPtr->width;
+ yScale = PS_MAX_PREVIEW_HEIGHT / (double)graphPtr->height;
+ scale = MIN(xScale, yScale);
+
+ width = (int)(scale * graphPtr->width + 0.5);
+ height = (int)(scale * graphPtr->height + 0.5);
+ destImage = Blt_ResampleColorimage(image, width, height,
+ bltBoxFilterPtr, bltBoxFilterPtr);
+ Blt_FreeColorimage(image);
+ image = destImage;
+ }
+#endif /* THUMBNAIL_PREVIEW */
+ Blt_ColorimageToGreyscale(image);
+ if (psPtr->landscape) {
+ Blt_Colorimage oldImage;
+
+ oldImage = image;
+ image = Blt_RotateColorimage(image, 90.0);
+ Blt_FreeColorimage(oldImage);
+ }
+ Tcl_DStringInit(&dString);
+ /* Finally, we can generate PostScript for the image */
+ nLines = Blt_ColorimageToPsData(image, 1, &dString, "%");
+
+ Blt_AppendToPostScript(psToken, "%%BeginPreview: ", (char *)NULL);
+ Blt_FormatToPostScript(psToken, "%d %d 8 %d\n", Blt_ColorimageWidth(image),
+ Blt_ColorimageHeight(image), nLines);
+ Blt_AppendToPostScript(psToken, Tcl_DStringValue(&dString), (char *)NULL);
+ Blt_AppendToPostScript(psToken, "%%EndPreview\n\n", (char *)NULL);
+ Tcl_DStringFree(&dString);
+ Blt_FreeColorimage(image);
+}
+
+/*
+ * --------------------------------------------------------------------------
+ *
+ * PostScriptPreamble
+ *
+ * The PostScript preamble calculates the needed translation and scaling
+ * to make X11 coordinates compatible with PostScript.
+ *
+ * ---------------------------------------------------------------------
+ */
+
+#ifdef TIME_WITH_SYS_TIME
+#include <sys/time.h>
+#include <time.h>
+#else
+#ifdef HAVE_SYS_TIME_H
+#include <sys/time.h>
+#else
+#include <time.h>
+#endif /* HAVE_SYS_TIME_H */
+#endif /* TIME_WITH_SYS_TIME */
+
+static int
+PostScriptPreamble(graphPtr, fileName, psToken)
+ Graph *graphPtr;
+ char *fileName;
+ PsToken psToken;
+{
+ PostScript *psPtr = (PostScript *)graphPtr->postscript;
+ time_t ticks;
+ char date[200]; /* Hold the date string from ctime() */
+ char *version;
+ double dpiX, dpiY;
+ double xPixelsToPica, yPixelsToPica; /* Scales to convert pixels to pica */
+ Screen *screenPtr;
+ char *nl;
+ int paperHeightPixels;
+
+ paperHeightPixels = ComputeBoundingBox(graphPtr, psPtr);
+ if (fileName == NULL) {
+ fileName = Tk_PathName(graphPtr->tkwin);
+ }
+ Blt_AppendToPostScript(psToken, "%!PS-Adobe-3.0 EPSF-3.0\n",
+ (char *)NULL);
+
+ /*
+ * Compute the scale factors to convert PostScript to X11 coordinates.
+ * Round the pixels per inch (dpi) to an integral value before computing
+ * the scale.
+ */
+#define MM_INCH 25.4
+#define PICA_INCH 72.0
+ screenPtr = Tk_Screen(graphPtr->tkwin);
+ dpiX = (WidthOfScreen(screenPtr) * MM_INCH) / WidthMMOfScreen(screenPtr);
+ xPixelsToPica = PICA_INCH / dpiX;
+ dpiY = (HeightOfScreen(screenPtr) * MM_INCH) / HeightMMOfScreen(screenPtr);
+ yPixelsToPica = PICA_INCH / dpiY;
+
+ /*
+ * The "BoundingBox" comment is required for EPS files. The box
+ * coordinates are integers, so we need round away from the
+ * center of the box.
+ */
+ Blt_FormatToPostScript(psToken, "%%%%BoundingBox: %d %d %d %d\n",
+ (int)floor(psPtr->left * xPixelsToPica),
+ (int)floor((paperHeightPixels - psPtr->top) * yPixelsToPica),
+ (int)ceil(psPtr->right * xPixelsToPica),
+ (int)ceil((paperHeightPixels - psPtr->bottom) * yPixelsToPica));
+
+ Blt_AppendToPostScript(psToken, "%%Pages: 0\n", (char *)NULL);
+
+ version = Tcl_GetVar(graphPtr->interp, "blt_version", TCL_GLOBAL_ONLY);
+ if (version == NULL) {
+ version = "???";
+ }
+ Blt_FormatToPostScript(psToken, "%%%%Creator: (BLT %s %s)\n", version,
+ Tk_Class(graphPtr->tkwin));
+
+ ticks = time((time_t *) NULL);
+ strcpy(date, ctime(&ticks));
+ nl = date + strlen(date) - 1;
+ if (*nl == '\n') {
+ *nl = '\0';
+ }
+ Blt_FormatToPostScript(psToken, "%%%%CreationDate: (%s)\n", date);
+ Blt_FormatToPostScript(psToken, "%%%%Title: (%s)\n", fileName);
+ Blt_AppendToPostScript(psToken, "%%DocumentData: Clean7Bit\n",
+ (char *)NULL);
+ if (psPtr->landscape) {
+ Blt_AppendToPostScript(psToken, "%%Orientation: Landscape\n",
+ (char *)NULL);
+ } else {
+ Blt_AppendToPostScript(psToken, "%%Orientation: Portrait\n",
+ (char *)NULL);
+ }
+ Blt_AppendToPostScript(psToken,
+ "%%DocumentNeededResources: font Helvetica Courier\n", (char *)NULL);
+ Blt_AppendToPostScript(psToken, "%%EndComments\n\n", (char *)NULL);
+ if ((psPtr->addPreview) && (psPtr->previewFormat == PS_PREVIEW_EPSI)) {
+ PreviewImage(graphPtr, psToken);
+ }
+ if (Blt_FileToPostScript(psToken, "bltGraph.pro") != TCL_OK) {
+ return TCL_ERROR;
+ }
+ if (psPtr->footer) {
+ char *who;
+
+ who = getenv("LOGNAME");
+ if (who == NULL) {
+ who = "???";
+ }
+ Blt_AppendToPostScript(psToken,
+ "8 /Helvetica SetFont\n",
+ "10 30 moveto\n",
+ "(Date: ", date, ") show\n",
+ "10 20 moveto\n",
+ "(File: ", fileName, ") show\n",
+ "10 10 moveto\n",
+ "(Created by: ", who, "@", Tcl_GetHostName(), ") show\n",
+ "0 0 moveto\n",
+ (char *)NULL);
+ }
+ /*
+ * Set the conversion from PostScript to X11 coordinates. Scale
+ * pica to pixels and flip the y-axis (the origin is the upperleft
+ * corner).
+ */
+ Blt_AppendToPostScript(psToken,
+ "% Transform coordinate system to use X11 coordinates\n\n",
+ "% 1. Flip y-axis over by reversing the scale,\n",
+ "% 2. Translate the origin to the other side of the page,\n",
+ "% making the origin the upper left corner\n", (char *)NULL);
+ Blt_FormatToPostScript(psToken, "%g -%g scale\n", xPixelsToPica,
+ yPixelsToPica);
+ /* Papersize is in pixels. Translate the new origin *after*
+ * changing the scale. */
+ Blt_FormatToPostScript(psToken, "0 %d translate\n\n",
+ -paperHeightPixels);
+ Blt_AppendToPostScript(psToken, "% User defined page layout\n\n",
+ "% Set color level\n", (char *)NULL);
+ Blt_FormatToPostScript(psToken, "/CL %d def\n\n", psPtr->colorMode);
+ Blt_FormatToPostScript(psToken, "%% Set origin\n%d %d translate\n\n",
+ psPtr->left, psPtr->bottom);
+ if (psPtr->landscape) {
+ Blt_FormatToPostScript(psToken,
+ "%% Landscape orientation\n0 %g translate\n-90 rotate\n",
+ ((double)graphPtr->width * psPtr->pageScale));
+ }
+ if (psPtr->pageScale != 1.0) {
+ Blt_AppendToPostScript(psToken, "\n% Setting graph scale factor\n",
+ (char *)NULL);
+ Blt_FormatToPostScript(psToken, " %g %g scale\n", psPtr->pageScale,
+ psPtr->pageScale);
+ }
+ Blt_AppendToPostScript(psToken, "\n%%EndSetup\n\n", (char *)NULL);
+ return TCL_OK;
+}
+
+
+static void
+MarginsToPostScript(graphPtr, psToken)
+ Graph *graphPtr;
+ PsToken psToken;
+{
+ PostScript *psPtr = (PostScript *)graphPtr->postscript;
+ XRectangle margin[4];
+
+ margin[0].x = margin[0].y = margin[3].x = margin[1].x = 0;
+ margin[0].width = margin[3].width = graphPtr->width;
+ margin[0].height = graphPtr->top;
+ margin[3].y = graphPtr->bottom;
+ margin[3].height = graphPtr->height - graphPtr->bottom;
+ margin[2].y = margin[1].y = graphPtr->top;
+ margin[1].width = graphPtr->left;
+ margin[2].height = margin[1].height = graphPtr->bottom - graphPtr->top;
+ margin[2].x = graphPtr->right;
+ margin[2].width = graphPtr->width - graphPtr->right;
+
+ /* Clear the surrounding margins and clip the plotting surface */
+ if (psPtr->decorations) {
+ Blt_BackgroundToPostScript(psToken,
+ Tk_3DBorderColor(graphPtr->border));
+ } else {
+ Blt_ClearBackgroundToPostScript(psToken);
+ }
+ Blt_RectanglesToPostScript(psToken, margin, 4);
+
+ /* Interior 3D border */
+ if ((psPtr->decorations) && (graphPtr->plotBW > 0)) {
+ int x, y, width, height;
+
+ x = graphPtr->left - graphPtr->plotBW;
+ y = graphPtr->top - graphPtr->plotBW;
+ width = (graphPtr->right - graphPtr->left) + (2 * graphPtr->plotBW);
+ height = (graphPtr->bottom - graphPtr->top) + (2 * graphPtr->plotBW);
+ Blt_Draw3DRectangleToPostScript(psToken, graphPtr->border, (double)x,
+ (double)y, width, height, graphPtr->plotBW, graphPtr->plotRelief);
+ }
+ if (Blt_LegendSite(graphPtr->legend) & LEGEND_IN_MARGIN) {
+ /*
+ * Print the legend if we're using a site which lies in one
+ * of the margins (left, right, top, or bottom) of the graph.
+ */
+ Blt_LegendToPostScript(graphPtr->legend, psToken);
+ }
+ if (graphPtr->titleText != NULL) {
+ Blt_TextToPostScript(psToken, graphPtr->titleText,
+ &(graphPtr->titleStyle),
+ (double)graphPtr->titleX,
+ (double)graphPtr->titleY);
+ }
+ Blt_AxesToPostScript(graphPtr, psToken);
+}
+
+
+static int
+GraphToPostScript(graphPtr, ident, psToken)
+ Graph *graphPtr;
+ char *ident; /* Identifier string (usually the filename) */
+ PsToken psToken;
+{
+ int x, y, width, height;
+ int result = TCL_ERROR;
+
+ /*
+ * We need to know how big a graph to print. If the graph hasn't
+ * been drawn yet, the width and height will be 1. Instead use
+ * the requested size of the widget. The user can still override
+ * this with the -width and -height postscript options.
+ */
+ if (graphPtr->height <= 1) {
+ graphPtr->height = Tk_ReqHeight(graphPtr->tkwin);
+ }
+ if (graphPtr->width <= 1) {
+ graphPtr->width = Tk_ReqWidth(graphPtr->tkwin);
+ }
+ result = PostScriptPreamble(graphPtr, ident, psToken);
+ if (result != TCL_OK) {
+ goto error;
+ }
+ /*
+ * Determine rectangle of the plotting area for the graph window
+ */
+ x = graphPtr->left - graphPtr->plotBW;
+ y = graphPtr->top - graphPtr->plotBW;
+
+ width = (graphPtr->right - graphPtr->left + 1) + (2 * graphPtr->plotBW);
+ height = (graphPtr->bottom - graphPtr->top + 1) + (2 * graphPtr->plotBW);
+
+ Blt_FontToPostScript(psToken, graphPtr->titleStyle.font);
+ Blt_RegionToPostScript(psToken, (double)x, (double)y, width, height);
+ if (graphPtr->postscript->decorations) {
+ Blt_BackgroundToPostScript(psToken, graphPtr->plotBg);
+ } else {
+ Blt_ClearBackgroundToPostScript(psToken);
+ }
+ Blt_AppendToPostScript(psToken, "Fill\n", (char *)NULL);
+ Blt_AppendToPostScript(psToken, "gsave clip\n\n", (char *)NULL);
+ /* Draw the grid, elements, and markers in the plotting area. */
+ if (!graphPtr->gridPtr->hidden) {
+ Blt_GridToPostScript(graphPtr, psToken);
+ }
+ Blt_MarkersToPostScript(graphPtr, psToken, TRUE);
+ if ((Blt_LegendSite(graphPtr->legend) & LEGEND_IN_PLOT) &&
+ (!Blt_LegendIsRaised(graphPtr->legend))) {
+ /* Print legend underneath elements and markers */
+ Blt_LegendToPostScript(graphPtr->legend, psToken);
+ }
+ Blt_AxisLimitsToPostScript(graphPtr, psToken);
+ Blt_ElementsToPostScript(graphPtr, psToken);
+ if ((Blt_LegendSite(graphPtr->legend) & LEGEND_IN_PLOT) &&
+ (Blt_LegendIsRaised(graphPtr->legend))) {
+ /* Print legend above elements (but not markers) */
+ Blt_LegendToPostScript(graphPtr->legend, psToken);
+ }
+ Blt_MarkersToPostScript(graphPtr, psToken, FALSE);
+ Blt_ActiveElementsToPostScript(graphPtr, psToken);
+ Blt_AppendToPostScript(psToken, "\n",
+ "% Unset clipping\n",
+ "grestore\n\n", (char *)NULL);
+ MarginsToPostScript(graphPtr, psToken);
+ Blt_AppendToPostScript(psToken,
+ "showpage\n",
+ "%Trailer\n",
+ "grestore\n",
+ "end\n",
+ "%EOF\n", (char *)NULL);
+ error:
+ /* Reset height and width of graph window */
+ graphPtr->width = Tk_Width(graphPtr->tkwin);
+ graphPtr->height = Tk_Height(graphPtr->tkwin);
+ graphPtr->flags = MAP_WORLD;
+
+ /*
+ * Redraw the graph in order to re-calculate the layout as soon as
+ * possible. This is in the case the crosshairs are active.
+ */
+ Blt_EventuallyRedrawGraph(graphPtr);
+ return result;
+}
+
+#ifdef WIN32
+
+static void
+InitAPMHeader(
+ Tk_Window tkwin,
+ int width, int height,
+ APMHEADER *headerPtr)
+{
+ unsigned int *p;
+ unsigned int sum;
+ Screen *screen;
+#define MM_INCH 25.4
+ double dpiX, dpiY;
+
+ headerPtr->key = 0x9ac6cdd7L;
+ headerPtr->hmf = 0;
+ headerPtr->inch = 1440;
+
+ screen = Tk_Screen(tkwin);
+ dpiX = (WidthOfScreen(screen) * MM_INCH) / WidthMMOfScreen(screen);
+ dpiY = (HeightOfScreen(screen) * MM_INCH) / HeightMMOfScreen(screen);
+
+ headerPtr->bbox.Left = headerPtr->bbox.Top = 0;
+ headerPtr->bbox.Bottom = (SHORT)((width * 1440) / dpiX);
+ headerPtr->bbox.Right = (SHORT)((height * 1440) / dpiY);
+ headerPtr->reserved = 0;
+ sum = 0;
+ for (p = (unsigned int *)headerPtr;
+ p < (unsigned int *)&(headerPtr->checksum); p++) {
+ sum ^= *p;
+ }
+ headerPtr->checksum = sum;
+}
+
+/*
+ * --------------------------------------------------------------------------
+ *
+ * CreateWindowEPS --
+ *
+ * Generates an EPS file with a Window metafile preview.
+ *
+ * Windows metafiles aren't very robust. Including exactly the
+ * same metafile (one embedded in a DOS EPS, the other as .wmf
+ * file) will play back differently.
+ *
+ * Results:
+ * None.
+ *
+ * --------------------------------------------------------------------------
+ */
+static int
+CreateWindowsEPS(
+ Graph *graphPtr,
+ PsToken psToken,
+ FILE *f)
+{
+ DWORD size;
+ DOSEPSHEADER epsHeader;
+ HANDLE hMem;
+ HDC hRefDC, hDC;
+ HENHMETAFILE hMetaFile;
+ Tcl_DString dString;
+ TkWinDC drawableDC;
+ TkWinDCState state;
+ int result;
+ unsigned char *buffer, *psBuffer;
+
+ result = TCL_ERROR;
+
+ Blt_AppendToPostScript(psToken, "\n", (char *)NULL);
+ psBuffer = Blt_PostScriptFromToken(psToken);
+ /*
+ * Fill out as much information as we can into the DOS EPS header.
+ * We won't know the start of the length of the WMF segment until
+ * we create the metafile.
+ */
+ epsHeader.magic[0] = 0xC5;
+ epsHeader.magic[1] = 0xD0;
+ epsHeader.magic[2] = 0xD3;
+ epsHeader.magic[3] = 0xC6;
+ epsHeader.psStart = sizeof(epsHeader);
+ epsHeader.psLength = strlen(psBuffer) + 1;
+ epsHeader.wmfStart = epsHeader.psStart + epsHeader.psLength;
+ epsHeader.wmfLength = 0L; /* Fill in later. */
+ epsHeader.tiffStart = 0L;
+ epsHeader.tiffLength = 0L;
+ epsHeader.checksum = 0xFFFF;
+
+ hMem = NULL;
+ result = TCL_ERROR;
+ hRefDC = TkWinGetDrawableDC(graphPtr->display,
+ Tk_WindowId(graphPtr->tkwin), &state);
+
+ /* Build a description string. */
+ Tcl_DStringInit(&dString);
+ Tcl_DStringAppend(&dString, "BLT Graph ", -1);
+ Tcl_DStringAppend(&dString, BLT_VERSION, -1);
+ Tcl_DStringAppend(&dString, "\0", -1);
+ Tcl_DStringAppend(&dString, Tk_PathName(graphPtr->tkwin), -1);
+ Tcl_DStringAppend(&dString, "\0", -1);
+
+ hDC = CreateEnhMetaFile(hRefDC, NULL, NULL, Tcl_DStringValue(&dString));
+ Tcl_DStringFree(&dString);
+
+ if (hDC == NULL) {
+ Tcl_AppendResult(graphPtr->interp, "can't create metafile: ",
+ Blt_LastError(), (char *)NULL);
+ return TCL_ERROR;
+ }
+ /* Assemble a Tk drawable that points to the metafile and let the
+ * graph's drawing routine draw into it. */
+ drawableDC.hdc = hDC;
+ drawableDC.type = TWD_WINDC;
+
+ graphPtr->width = Tk_Width(graphPtr->tkwin);
+ graphPtr->height = Tk_Height(graphPtr->tkwin);
+ graphPtr->flags |= RESET_WORLD;
+ Blt_LayoutGraph(graphPtr);
+ Blt_DrawGraph(graphPtr, (Drawable)&drawableDC, FALSE);
+ GdiFlush();
+ hMetaFile = CloseEnhMetaFile(hDC);
+
+ size = GetWinMetaFileBits(hMetaFile, 0, NULL, MM_ANISOTROPIC, hRefDC);
+ hMem = GlobalAlloc(GHND, size);
+ if (hMem == NULL) {
+ Tcl_AppendResult(graphPtr->interp, "can't allocate global memory:",
+ Blt_LastError(), (char *)NULL);
+ goto error;
+ }
+ buffer = (LPVOID)GlobalLock(hMem);
+ if (!GetWinMetaFileBits(hMetaFile, size, buffer, MM_ANISOTROPIC, hRefDC)) {
+ Tcl_AppendResult(graphPtr->interp, "can't get metafile data:",
+ Blt_LastError(), (char *)NULL);
+ goto error;
+ }
+
+ /*
+ * Fix up the EPS header with the correct metafile length and PS
+ * offset (now that we what they are).
+ */
+ epsHeader.wmfLength = size;
+ epsHeader.wmfStart = epsHeader.psStart + epsHeader.psLength;
+
+ /* Write out the eps header, */
+ if (fwrite(&epsHeader, 1, sizeof(epsHeader), f) != sizeof(epsHeader)) {
+ Tcl_AppendResult(graphPtr->interp, "error writing eps header:",
+ Blt_LastError(), (char *)NULL);
+ goto error;
+ }
+ /* the PostScript, */
+ if (fwrite(psBuffer, 1, epsHeader.psLength, f) != epsHeader.psLength) {
+ Tcl_AppendResult(graphPtr->interp, "error writing PostScript data:",
+ Blt_LastError(), (char *)NULL);
+ goto error;
+ }
+ /* and finally the metadata itself. */
+ if (fwrite(buffer, 1, size, f) != size) {
+ Tcl_AppendResult(graphPtr->interp, "error writing metafile data:",
+ Blt_LastError(), (char *)NULL);
+ goto error;
+ }
+ result = TCL_OK;
+
+ error:
+ DeleteEnhMetaFile(hMetaFile);
+ TkWinReleaseDrawableDC(Tk_WindowId(graphPtr->tkwin), hRefDC, &state);
+ fclose(f);
+ if (hMem != NULL) {
+ GlobalUnlock(hMem);
+ GlobalFree(hMem);
+ }
+ graphPtr->flags = MAP_WORLD;
+ Blt_EventuallyRedrawGraph(graphPtr);
+ return result;
+}
+
+#endif /*WIN32*/
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * OutputOp --
+ *
+ * This procedure is invoked to print the graph in a file.
+ *
+ * Results:
+ * Standard TCL result. TCL_OK if plot was successfully printed,
+ * TCL_ERROR otherwise.
+ *
+ * Side effects:
+ * A new PostScript file is created.
+ *
+ *----------------------------------------------------------------------
+ */
+static int
+OutputOp(graphPtr, interp, argc, argv)
+ Graph *graphPtr; /* Graph widget record */
+ Tcl_Interp *interp;
+ int argc; /* Number of options in argv vector */
+ char **argv; /* Option vector */
+{
+ PostScript *psPtr = (PostScript *)graphPtr->postscript;
+ int result = TCL_ERROR;
+ FILE *f = NULL;
+ PsToken psToken;
+ char *fileName; /* Name of file to write PostScript output
+ * If NULL, output is returned via
+ * interp->result. */
+ fileName = NULL;
+ if (argc > 3) {
+ if (argv[3][0] != '-') {
+ fileName = argv[3]; /* First argument is the file name. */
+ argv++, argc--;
+ }
+ if (Tk_ConfigureWidget(interp, graphPtr->tkwin, configSpecs, argc - 3,
+ argv + 3, (char *)psPtr, TK_CONFIG_ARGV_ONLY) != TCL_OK) {
+ return TCL_ERROR;
+ }
+ if (fileName != NULL) {
+#ifdef WIN32
+ f = fopen(fileName, "wb");
+#else
+ f = fopen(fileName, "w");
+#endif
+ if (f == NULL) {
+ Tcl_AppendResult(interp, "can't create \"", fileName, "\": ",
+ Tcl_PosixError(interp), (char *)NULL);
+ return TCL_ERROR;
+ }
+ }
+ }
+ psToken = Blt_GetPsToken(graphPtr->interp, graphPtr->tkwin);
+ psToken->fontVarName = psPtr->fontVarName;
+ psToken->colorVarName = psPtr->colorVarName;
+ psToken->colorMode = psPtr->colorMode;
+
+ if (GraphToPostScript(graphPtr, fileName, psToken) != TCL_OK) {
+ goto error;
+ }
+ /*
+ * If a file name was given, write the results to that file
+ */
+ if (f != NULL) {
+#ifdef WIN32
+ if ((psPtr->addPreview) && (psPtr->previewFormat != PS_PREVIEW_EPSI)) {
+ if (CreateWindowsEPS(graphPtr, psToken, f)) {
+ return TCL_ERROR;
+ }
+ } else {
+ fputs(Blt_PostScriptFromToken(psToken), f);
+ if (ferror(f)) {
+ Tcl_AppendResult(interp, "error writing file \"", fileName,
+ "\": ", Tcl_PosixError(interp), (char *)NULL);
+ goto error;
+ }
+ fclose(f);
+ }
+#else
+ fputs(Blt_PostScriptFromToken(psToken), f);
+ if (ferror(f)) {
+ Tcl_AppendResult(interp, "error writing file \"", fileName, "\": ",
+ Tcl_PosixError(interp), (char *)NULL);
+ goto error;
+ }
+ fclose(f);
+#endif /* WIN32 */
+ } else {
+ Tcl_SetResult(interp, Blt_PostScriptFromToken(psToken), TCL_VOLATILE);
+ }
+ result = TCL_OK;
+
+ error:
+ if (f != NULL) {
+ fclose(f);
+ }
+ Blt_ReleasePsToken(psToken);
+ return result;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * Blt_CreatePostScript --
+ *
+ * Creates a postscript structure.
+ *
+ * Results:
+ * Always TCL_OK.
+ *
+ * Side effects:
+ * A new PostScript structure is created.
+ *
+ *----------------------------------------------------------------------
+ */
+int
+Blt_CreatePostScript(graphPtr)
+ Graph *graphPtr;
+{
+ PostScript *psPtr;
+
+ psPtr = Blt_Calloc(1, sizeof(PostScript));
+ assert(psPtr);
+ psPtr->colorMode = PS_MODE_COLOR;
+ psPtr->center = TRUE;
+ psPtr->decorations = TRUE;
+ graphPtr->postscript = psPtr;
+
+ if (Blt_ConfigureWidgetComponent(graphPtr->interp, graphPtr->tkwin,
+ "postscript", "Postscript", configSpecs, 0, (char **)NULL,
+ (char *)psPtr, 0) != TCL_OK) {
+ return TCL_ERROR;
+ }
+ return TCL_OK;
+}
+
+/*
+ *--------------------------------------------------------------
+ *
+ * Blt_PostScriptOp --
+ *
+ * This procedure is invoked to process the Tcl command
+ * that corresponds to a widget managed by this module.
+ * See the user documentation for details on what it does.
+ *
+ * Results:
+ * A standard Tcl result.
+ *
+ * Side effects:
+ * See the user documentation.
+ *
+ *--------------------------------------------------------------
+ */
+static Blt_OpSpec psOps[] =
+{
+ {"cget", 2, (Blt_Op)CgetOp, 4, 4, "option",},
+ {"configure", 2, (Blt_Op)ConfigureOp, 3, 0, "?option value?...",},
+ {"output", 1, (Blt_Op)OutputOp, 3, 0,
+ "?fileName? ?option value?...",},
+};
+
+static int nPsOps = sizeof(psOps) / sizeof(Blt_OpSpec);
+
+int
+Blt_PostScriptOp(graphPtr, interp, argc, argv)
+ Graph *graphPtr; /* Graph widget record */
+ Tcl_Interp *interp;
+ int argc; /* # arguments */
+ char **argv; /* Argument list */
+{
+ Blt_Op proc;
+ int result;
+
+ proc = Blt_GetOp(interp, nPsOps, psOps, BLT_OP_ARG2, argc, argv, 0);
+ if (proc == NULL) {
+ return TCL_ERROR;
+ }
+ result = (*proc) (graphPtr, interp, argc, argv);
+ return result;
+}
+