home *** CD-ROM | disk | FTP | other *** search
/ Linux Cubed Series 3: Developer Tools / Linux Cubed Series 3 - Developer Tools.iso / devel / lang / lisp / stk-3.0 / stk-3 / blt-for-STk-3.0 / blt-1.9 / src / bltGrPS.c < prev    next >
Encoding:
C/C++ Source or Header  |  1995-07-01  |  46.6 KB  |  1,499 lines

  1.  
  2. /*
  3.  * bltGrPS.c --
  4.  *
  5.  *      This module implements a graph widget for
  6.  *      the Tk toolkit.
  7.  *
  8.  * Copyright 1991-1994 by AT&T Bell Laboratories.
  9.  * Permission to use, copy, modify, and distribute this software
  10.  * and its documentation for any purpose and without fee is hereby
  11.  * granted, provided that the above copyright notice appear in all
  12.  * copies and that both that the copyright notice and warranty
  13.  * disclaimer appear in supporting documentation, and that the
  14.  * names of AT&T Bell Laboratories any of their entities not be used
  15.  * in advertising or publicity pertaining to distribution of the
  16.  * software without specific, written prior permission.
  17.  *
  18.  * AT&T disclaims all warranties with regard to this software, including
  19.  * all implied warranties of merchantability and fitness.  In no event
  20.  * shall AT&T be liable for any special, indirect or consequential
  21.  * damages or any damages whatsoever resulting from loss of use, data
  22.  * or profits, whether in an action of contract, negligence or other
  23.  * tortuous action, arising out of or in connection with the use or
  24.  * performance of this software.
  25.  *
  26.  */
  27. /*
  28.  * -----------------------------------------------------------------
  29.  *
  30.  * PostScript routines to print the graph
  31.  *
  32.  * -----------------------------------------------------------------
  33.  */
  34.  
  35. #include "blt.h"
  36. #include "bltGraph.h"
  37. #include "bltGrElem.h"
  38. #include "bltGrTag.h"
  39. #include <X11/Xutil.h>
  40. #include <X11/Xatom.h>
  41.  
  42. #define RED(c)          ((double)(c)->red/65536.0)
  43. #define GREEN(c)        ((double)(c)->green/65536.0)
  44. #define BLUE(c)         ((double)(c)->blue/65536.0)
  45.  
  46. #define PSMAXPATH    1450    /* Max length of a PS path */
  47.  
  48. #define SET_BG_COLOR     0
  49. #define SET_FG_COLOR    1
  50.  
  51. typedef enum ColorLevels {
  52.     MONOCHROME_LEVEL, GRAYSCALE_LEVEL, FULLCOLOR_LEVEL
  53. } ColorLevel;
  54.  
  55. static char *modeTokens[] =
  56. {
  57.     "monochrome", "grayscale", "color", (char *)NULL,
  58. };
  59.  
  60. /*
  61.  * Sun's bundled and unbundled C compilers choke on static function
  62.  * typedefs (but they can handle "extern") such as
  63.  *
  64.  *     static Tk_OptionParseProc parseProc;
  65.  *      static Tk_OptionPrintProc printProc;
  66.  *
  67.  * Workaround: provide forward declarations here.
  68.  */
  69. static int ParseColorMode _ANSI_ARGS_((ClientData clientData,
  70.     Tcl_Interp *interp, Tk_Window tkwin, char *value, char *widgRec,
  71.     int offset));
  72. static char *PrintColorMode _ANSI_ARGS_((ClientData clientData,
  73.     Tk_Window tkwin, char *widgRec, int offset,
  74.     Tcl_FreeProc **freeProcPtr));
  75.  
  76. static Tk_CustomOption ColorModeOption =
  77. {
  78.     ParseColorMode, PrintColorMode, (ClientData)0,
  79. };
  80.  
  81. /*
  82.  * -------------------------------------------------------------------
  83.  *
  84.  * PostScript --
  85.  *
  86.  *     Structure contains information specific to the outputing of
  87.  *    PostScript commands to print the graph.
  88.  *
  89.  * -------------------------------------------------------------------
  90.  */
  91. typedef struct {
  92.     PostScriptConfigureProc *configProc;
  93.     PostScriptPrintProc *printProc;
  94.     PostScriptDestroyProc *destroyProc;
  95.  
  96.     int pageWidth, pageHeight;    /* Requested page extents */
  97.     int xOffset, yOffset;    /* Requested page anchor */
  98.     Tk_Anchor anchor;        /* Anchor type for positioning page */
  99.     ColorLevel colorLevel;    /* If zero, use BW color scheme */
  100.     char *colorVarName;        /* If non-NULL, name of color map variable
  101.                  * containing PostScript color translations */
  102.     char *fontVarName;        /* If non-NULL, name of font map varible */
  103.     int landscape;        /* If non-zero, rotation page layout 90
  104.                  * degrees */
  105.  
  106.     int x, y;            /* Actual coordinates of anchor in pica */
  107.     int width, height;        /* Actual extents of window in pica */
  108.  
  109.     double pointScale;        /* Scale factor to convert window coordinates
  110.                  * to printer points (pica) */
  111. } PostScript;
  112.  
  113. #define DEF_PS_COLOR_MAP    (char *)NULL
  114. #define DEF_PS_COLOR_MODE    "color"
  115. #define DEF_PS_FONT_MAP        (char *)NULL
  116. #define DEF_PS_LANDSCAPE    "0"
  117. #define DEF_PS_PAGE_ANCHOR    "nw"
  118. #define DEF_PS_PAGE_HEIGHT    "0"
  119. #define DEF_PS_PAGE_WIDTH    "0"
  120. #define DEF_PS_PAGE_X_OFFSET    "1i"
  121. #define DEF_PS_PAGE_Y_OFFSET    "1i"
  122.  
  123. static Tk_ConfigSpec configSpecs[] =
  124. {
  125.     {TK_CONFIG_CUSTOM, "-colormode", "colorMode", (char *)NULL,
  126.     DEF_PS_COLOR_MODE, Tk_Offset(PostScript, colorLevel),
  127.     TK_CONFIG_DONT_SET_DEFAULT, &ColorModeOption},
  128.     {TK_CONFIG_STRING, "-colormap", "colorMap", (char *)NULL,
  129.     DEF_PS_COLOR_MAP, Tk_Offset(PostScript, colorVarName), 0},
  130.     {TK_CONFIG_STRING, "-fontmap", "fontMap", (char *)NULL,
  131.     DEF_PS_FONT_MAP, Tk_Offset(PostScript, fontVarName), 0},
  132.     {TK_CONFIG_BOOLEAN, "-landscape", "landscape", (char *)NULL,
  133.     DEF_PS_LANDSCAPE, Tk_Offset(PostScript, landscape),
  134.     TK_CONFIG_DONT_SET_DEFAULT},
  135.     {TK_CONFIG_ANCHOR, "-pageanchor", "pageAnchor", (char *)NULL,
  136.     DEF_PS_PAGE_ANCHOR, Tk_Offset(PostScript, anchor),
  137.     TK_CONFIG_DONT_SET_DEFAULT},
  138.     {TK_CONFIG_PIXELS, "-pageheight", "pageHeight", (char *)NULL,
  139.     DEF_PS_PAGE_HEIGHT, Tk_Offset(PostScript, pageHeight),
  140.     TK_CONFIG_DONT_SET_DEFAULT},
  141.     {TK_CONFIG_PIXELS, "-pagewidth", "pageWidth", (char *)NULL,
  142.     DEF_PS_PAGE_WIDTH, Tk_Offset(PostScript, pageWidth),
  143.     TK_CONFIG_DONT_SET_DEFAULT},
  144.     {TK_CONFIG_PIXELS, "-pagex", "pageX", (char *)NULL,
  145.     DEF_PS_PAGE_X_OFFSET, Tk_Offset(PostScript, xOffset),
  146.     TK_CONFIG_DONT_SET_DEFAULT},
  147.     {TK_CONFIG_PIXELS, "-pagey", "pageY", (char *)NULL,
  148.     DEF_PS_PAGE_Y_OFFSET, Tk_Offset(PostScript, yOffset),
  149.     TK_CONFIG_DONT_SET_DEFAULT},
  150.     {TK_CONFIG_END, NULL, NULL, NULL, NULL, 0, 0}
  151. };
  152.  
  153. #ifndef NO_INLINE_PROLOG
  154. #include "bltGrPS.h"
  155. #endif /*NO_INLINE_PROLOG*/
  156.  
  157. extern int Blt_ComputeLayout _ANSI_ARGS_((Graph *graphPtr));
  158. extern void Blt_LayoutGraph _ANSI_ARGS_((Graph *graphPtr));
  159.  
  160. /*
  161.  *----------------------------------------------------------------------
  162.  *
  163.  * ParseColorMode --
  164.  *
  165.  *    Convert the string representation of a PostScript color mode
  166.  *     into the enumerated type representing the color level:
  167.  *
  168.  *        2 - Full color,
  169.  *        1 - Grey scale, and
  170.  *        0 - Monochrome.
  171.  *
  172.  * Results:
  173.  *    The return value is a standard Tcl result.  The color level is
  174.  *    written into the page layout information structure.
  175.  *
  176.  * Side Effects:
  177.  *    Future invocations of the "postscript" widget command will use
  178.  *    this variable to determine how color information will be displayed
  179.  *    in the PostScript output it produces.
  180.  *
  181.  *----------------------------------------------------------------------
  182.  */
  183. /*ARGSUSED*/
  184. static int
  185. ParseColorMode(clientData, interp, tkwin, value, widgRec, offset)
  186.     ClientData clientData;    /* not used */
  187.     Tcl_Interp *interp;        /* Interpreter to send results back to */
  188.     Tk_Window tkwin;        /* not used */
  189.     char *value;        /* New legend position string */
  190.     char *widgRec;        /* Graph widget record */
  191.     int offset;            /* Offset of colorLevel field in record */
  192. {
  193.     ColorLevel *colorLevel = (ColorLevel *)(widgRec + offset);
  194.     int index;
  195.  
  196.     if (value == NULL || *value == '\0') {
  197.     *colorLevel = FULLCOLOR_LEVEL;
  198.     return TCL_OK;
  199.     }
  200.     index = Blt_GetTokenIndex(modeTokens, value, 0);
  201.     if (index < 0) {
  202.     Tcl_AppendResult(interp, "bad color mode \"", value,
  203.         "\": should be ", "\"color\", \"grayscale\", or \"monochrome\"",
  204.         (char *)NULL);
  205.     return TCL_ERROR;
  206.     }
  207.     *colorLevel = (ColorLevel)index;
  208.     return TCL_OK;
  209. }
  210.  
  211. /*
  212.  *----------------------------------------------------------------------
  213.  *
  214.  * PrintColorMode --
  215.  *
  216.  *    Convert the current color level into the string representing a
  217.  *    valid color mode.
  218.  *
  219.  * Results:
  220.  *    The string representing the color mode is returned.
  221.  *
  222.  *----------------------------------------------------------------------
  223.  */
  224. /*ARGSUSED*/
  225. static char *
  226. PrintColorMode(clientData, tkwin, widgRec, offset, freeProcPtr)
  227.     ClientData clientData;    /* not used */
  228.     Tk_Window tkwin;        /* not used */
  229.     char *widgRec;        /* PostScript structure record */
  230.     int offset;            /* field of colorLevel in record */
  231.     Tcl_FreeProc **freeProcPtr;    /* not used */
  232. {
  233.     ColorLevel level = *(ColorLevel *)(widgRec + offset);
  234.  
  235.     return (modeTokens[level]);
  236. }
  237.  
  238. /*
  239.  *----------------------------------------------------------------------
  240.  *
  241.  * XColorToPostScript --
  242.  *
  243.  *    Convert the a XColor (from its RGB values) to a PostScript
  244.  *    command.  If a Tcl color map variable exists, it will be
  245.  *    consulted for a PostScript translation based upon the color name.
  246.  *
  247.  * Results:
  248.  *    The string representing the color mode is returned.
  249.  *
  250.  *----------------------------------------------------------------------
  251.  */
  252. static void
  253. XColorToPostScript(graphPtr, colorPtr, fgOrBg)
  254.     Graph *graphPtr;
  255.     XColor *colorPtr;        /* Color value to be converted */
  256.     int fgOrBg;            /* If non-zero, this represents a foreground
  257.                  * color, otherwise a background color */
  258. {
  259.     PostScript *psPtr = (PostScript *)graphPtr->postscript;
  260.     if (psPtr->colorVarName != NULL) {
  261.     char *colorDesc;
  262.  
  263.     colorDesc = Tcl_GetVar2(graphPtr->interp, psPtr->colorVarName,
  264.         Tk_NameOfColor(colorPtr), 0);
  265.     if (colorDesc != NULL) {
  266.         Tcl_AppendResult(graphPtr->interp, colorDesc, " ", (char *)NULL);
  267.         return;
  268.     }
  269.     }
  270.     sprintf(graphPtr->scratchPtr, "%g %g %g %s  ",
  271.     RED(colorPtr), GREEN(colorPtr), BLUE(colorPtr),
  272.     (fgOrBg) ? "SetFgColor" : "SetBgColor");
  273.     Tcl_AppendResult(graphPtr->interp, graphPtr->scratchPtr, (char *)NULL);
  274. }
  275.  
  276. /*
  277.  *----------------------------------------------------------------------
  278.  *
  279.  * ReverseBits --
  280.  *
  281.  *    Convert a byte from a X image into PostScript image order.
  282.  *    This requires the nybbles to be reversed but also their bit values.
  283.  *
  284.  * Results:
  285.  *    The converted byte is returned.
  286.  *
  287.  *----------------------------------------------------------------------
  288.  */
  289. static unsigned char
  290. ReverseBits(byte)        /* Reverse bits */
  291.     register unsigned char byte;
  292. {
  293.     byte = ((byte >> 1) & 0x55) | ((byte << 1) & 0xaa);
  294.     byte = ((byte >> 2) & 0x33) | ((byte << 2) & 0xcc);
  295.     byte = ((byte >> 4) & 0x0f) | ((byte << 4) & 0xf0);
  296.     return (byte);
  297. }
  298.  
  299. /*
  300.  * -----------------------------------------------------------------
  301.  *
  302.  * XBitmapToPostScript --
  303.  *
  304.  *      Output a PostScript image string of the given bitmap image.
  305.  *      It is assumed the image is one bit deep and a zero value
  306.  *      indicates an off-pixel.  To convert to PostScript, the
  307.  *      bits need to be reversed from the X11 image order.
  308.  *
  309.  * Results:
  310.  *      None.
  311.  *
  312.  * Side Effects:
  313.  *      A PostScript image string is output to the file pointer in
  314.  *      the page layout structure.
  315.  *
  316.  * -----------------------------------------------------------------
  317.  */
  318. static void
  319. XBitmapToPostScript(graphPtr, bitmap, width, height)
  320.     Graph *graphPtr;
  321.     Pixmap bitmap;
  322.     unsigned int width, height;
  323. {
  324.     register unsigned int byte = 0;
  325.     register int x, y, bitPos;
  326.     unsigned long pixel;
  327.     XImage *imagePtr;
  328.     int byteCount = 0;
  329.  
  330.     imagePtr = XGetImage(graphPtr->display, bitmap, 0, 0, width, height,
  331.     1, ZPixmap);
  332.     Tcl_AppendResult(graphPtr->interp, "<", (char *)NULL);
  333.     bitPos = 0;            /* Inhibit compiler warning */
  334.     for (y = 0; y < height; y++) {
  335.     byte = 0;
  336.     for (x = 0; x < width; x++) {
  337.         pixel = XGetPixel(imagePtr, x, y);
  338.         bitPos = x % 8;
  339.         byte |= (unsigned char)(pixel << bitPos);
  340.         if (bitPos == 7) {
  341.         sprintf(graphPtr->scratchPtr, "%02x", ReverseBits(byte));
  342.         Tcl_AppendResult(graphPtr->interp, graphPtr->scratchPtr,
  343.             (char *)NULL);
  344.         byteCount++;
  345.         byte = 0;
  346.         }
  347.         if (byteCount >= 30) {
  348.         Tcl_AppendResult(graphPtr->interp, "\n", (char *)NULL);
  349.         byteCount = 0;
  350.         }
  351.     }
  352.     if (bitPos != 7) {
  353.         sprintf(graphPtr->scratchPtr, "%02x", ReverseBits(byte));
  354.         Tcl_AppendResult(graphPtr->interp, graphPtr->scratchPtr,
  355.         (char *)NULL);
  356.         byteCount++;
  357.     }
  358.     }
  359.     Tcl_AppendResult(graphPtr->interp, "> ", (char *)NULL);
  360.     XDestroyImage(imagePtr);
  361. }
  362.  
  363. /*
  364.  *----------------------------------------------------------------------
  365.  *
  366.  * NameOfAtom --
  367.  *
  368.  *    Convenience routine to wrap around Tk_GetAtomName.  Returns
  369.  *    NULL instead of "?bad atom?" is the atom cannot be found.
  370.  *
  371.  * Results:
  372.  *    The name of the atom is returned if found. Otherwise NULL.
  373.  *
  374.  *----------------------------------------------------------------------
  375.  */
  376. static char *
  377. NameOfAtom(tkwin, atom)
  378.     Tk_Window tkwin;
  379.     Atom atom;
  380. {
  381.     char *result;
  382.  
  383.     result = Tk_GetAtomName(tkwin, atom);
  384.     if (strcmp(result, "?bad atom?") == 0) {
  385.     return NULL;
  386.     }
  387.     return (result);
  388. }
  389.  
  390. /*
  391.  * -----------------------------------------------------------------
  392.  *
  393.  * XFontStructToPostScript --
  394.  *
  395.  *      Map X11 font to a PostScript font. Currently, only fonts
  396.  *    whose FOUNDRY property are "Adobe" are converted. Simply
  397.  *    gets the XA_FULL_NAME and XA_FAMILY properties and pieces
  398.  *    together a PostScript fontname.
  399.  *
  400.  * Results:
  401.  *      Returns the mapped PostScript font name is one is possible.
  402.  *    Otherwise NULL.
  403.  *
  404.  * -----------------------------------------------------------------
  405.  */
  406. static char *
  407. XFontStructToPostScript(tkwin, fontPtr)
  408.     Tk_Window tkwin;        /* Window to query for atoms */
  409.     XFontStruct *fontPtr;    /* Font structure to map to name */
  410. {
  411.     Atom atom;
  412.     char *fullName, *family, *foundry;
  413.     register char *src, *dest;
  414.     char *start;
  415.     static char string[200];    /* What size? */
  416.  
  417.     if (XGetFontProperty(fontPtr, XA_FULL_NAME, &atom) == False) {
  418.     return NULL;
  419.     }
  420.     fullName = NameOfAtom(tkwin, atom);
  421.     if (fullName == NULL) {
  422.     return NULL;
  423.     }
  424.     family = foundry = NULL;
  425.     if (XGetFontProperty(fontPtr, Tk_InternAtom(tkwin, "FOUNDRY"), &atom)) {
  426.     foundry = NameOfAtom(tkwin, atom);
  427.     }
  428.     if (XGetFontProperty(fontPtr, XA_FAMILY_NAME, &atom)) {
  429.     family = NameOfAtom(tkwin, atom);
  430.     }
  431.     /*
  432.      * Try to map font only if foundry is Adobe
  433.      */
  434.     if ((foundry == NULL) || (strcmp(foundry, "Adobe") != 0) ||
  435.     (family == NULL)) {
  436. #ifdef notdef
  437.     fprintf(stderr, "huh? Full name (%s) for non-PS font\n", fullName);
  438. #endif
  439.     return NULL;
  440.     }
  441.     src = fullName + strlen(family);
  442.     /*
  443.      * Special case: Fix mapping of "New Century Schoolbook"
  444.      */
  445.     if ((*family == 'N') && (strcmp(family, "New Century Schoolbook") == 0)) {
  446.     family = "NewCenturySchlbk";
  447.     }
  448.     /*
  449.      * PostScript font name is in the form <family>-<type face>
  450.      */
  451.     sprintf(string, "%s-", family);
  452.     dest = start = string + strlen(string);
  453.     /*
  454.      * Append the type face (part of the full name trailing the family name)
  455.      * to the the PostScript font name, removing any spaces
  456.      *
  457.      * ex. " Bold Italic" ==> "BoldItalic"
  458.      */
  459.     while (*src != '\0') {
  460.     if (*src != ' ') {
  461.         *dest++ = *src;
  462.     }
  463.     src++;
  464.     }
  465.     if (dest == start) {
  466.     --dest;            /* Remove '-' to leave just the family name */
  467.     }
  468.     *dest = '\0';        /* Make a valid string */
  469.     return (string);
  470. }
  471.  
  472. /*
  473.  * -------------------------------------------------------------------
  474.  * Routines to convert X drawing functions to PostScript commands.
  475.  * -------------------------------------------------------------------
  476.  */
  477. void
  478. Blt_BackgroundToPostScript(graphPtr, colorPtr)
  479.     Graph *graphPtr;
  480.     XColor *colorPtr;
  481. {
  482.     XColorToPostScript(graphPtr, colorPtr, SET_BG_COLOR);
  483.     Tcl_AppendResult(graphPtr->interp, "\n", (char *)NULL);
  484. }
  485.  
  486. void
  487. Blt_ForegroundToPostScript(graphPtr, colorPtr)
  488.     Graph *graphPtr;
  489.     XColor *colorPtr;
  490. {
  491.     XColorToPostScript(graphPtr, colorPtr, SET_FG_COLOR);
  492.     Tcl_AppendResult(graphPtr->interp, "\n", (char *)NULL);
  493. }
  494.  
  495. void
  496. Blt_LineWidthToPostScript(graphPtr, lineWidth)
  497.     Graph *graphPtr;
  498.     int lineWidth;
  499. {
  500.     if (lineWidth < 1) {
  501.     lineWidth = 1;
  502.     }
  503.     sprintf(graphPtr->scratchPtr, "%d setlinewidth\n", lineWidth);
  504.     Tcl_AppendResult(graphPtr->interp, graphPtr->scratchPtr, (char *)NULL);
  505. }
  506.  
  507. void
  508. Blt_LineDashesToPostScript(graphPtr, dashes)
  509.     Graph *graphPtr;
  510.     int dashes;
  511. {
  512.     if (dashes < 0) {
  513.     dashes = 0;
  514.     }
  515.     sprintf(graphPtr->scratchPtr, "%d SetDashes\n", dashes);
  516.     Tcl_AppendResult(graphPtr->interp, graphPtr->scratchPtr, (char *)NULL);
  517. }
  518.  
  519. void
  520. Blt_SetLineAttributes(graphPtr, colorPtr, lineWidth, dashes)
  521.     Graph *graphPtr;
  522.     XColor *colorPtr;
  523.     int lineWidth;
  524.     int dashes;
  525. {
  526.     Blt_ForegroundToPostScript(graphPtr, colorPtr);
  527.     Blt_LineWidthToPostScript(graphPtr, lineWidth);
  528.     Blt_LineDashesToPostScript(graphPtr, dashes);
  529. }
  530.  
  531. void
  532. Blt_RectangleToPostScript(graphPtr, x, y, width, height)
  533.     Graph *graphPtr;
  534.     int x, y;
  535.     unsigned int width, height;
  536. {
  537.     sprintf(graphPtr->scratchPtr, "%d %d %d %d Box Fill\n", x, y, width,
  538.     height);
  539.     Tcl_AppendResult(graphPtr->interp, graphPtr->scratchPtr, (char *)NULL);
  540. }
  541.  
  542. void
  543. Blt_LinesToPostScript(graphPtr, pointArr, numPoints)
  544.     Graph *graphPtr;
  545.     XPoint *pointArr;
  546.     int numPoints;
  547. {
  548.     register int i;
  549.  
  550.     sprintf(graphPtr->scratchPtr, "newpath %d %d moveto\n", pointArr[0].x,
  551.     pointArr[0].y);
  552.     Tcl_AppendResult(graphPtr->interp, graphPtr->scratchPtr, (char *)NULL);
  553.     for (i = 1; i < numPoints; i++) {
  554.     sprintf(graphPtr->scratchPtr, "%d %d lineto\n", pointArr[i].x,
  555.         pointArr[i].y);
  556.     Tcl_AppendResult(graphPtr->interp, graphPtr->scratchPtr, (char *)NULL);
  557.     }
  558. }
  559.  
  560. void
  561. Blt_PolygonToPostScript(graphPtr, pointArr, numPoints)
  562.     Graph *graphPtr;
  563.     XPoint *pointArr;
  564.     int numPoints;
  565. {
  566.     Blt_LinesToPostScript(graphPtr, pointArr, numPoints);
  567.     sprintf(graphPtr->scratchPtr, "%d %d lineto closepath Fill\n",
  568.     pointArr[0].x, pointArr[0].y);
  569.     Tcl_AppendResult(graphPtr->interp, graphPtr->scratchPtr, (char *)NULL);
  570. }
  571.  
  572. void
  573. Blt_SegmentsToPostScript(graphPtr, segArr, numSegments)
  574.     Graph *graphPtr;
  575.     XSegment *segArr;
  576.     int numSegments;
  577. {
  578.     register int i;
  579.  
  580.     for (i = 0; i < numSegments; i++) {
  581.     sprintf(graphPtr->scratchPtr, "%d %d %d %d DrawSegment\n",
  582.         segArr[i].x1, segArr[i].y1, segArr[i].x2, segArr[i].y2);
  583.     Tcl_AppendResult(graphPtr->interp, graphPtr->scratchPtr, (char *)NULL);
  584.     }
  585. }
  586.  
  587. void
  588. Blt_RectanglesToPostScript(graphPtr, rectArr, numRects)
  589.     Graph *graphPtr;
  590.     XRectangle rectArr[];
  591.     int numRects;
  592. {
  593.     register int i;
  594.  
  595.     for (i = 0; i < numRects; i++) {
  596.     Blt_RectangleToPostScript(graphPtr, rectArr[i].x, rectArr[i].y,
  597.         rectArr[i].width, rectArr[i].height);
  598.     }
  599. }
  600.  
  601. typedef struct {
  602.     Screen *screen;        /* Screen on which the border will be used. */
  603.     Visual *visual;        /* Visual for all windows and pixmaps using
  604.                  * the border. */
  605.     int depth;            /* Number of bits per pixel of drawables where
  606.                  * the border will be used. */
  607.     Colormap colormap;        /* Colormap out of which pixels are
  608.                  * allocated. */
  609.     int refCount;        /* Number of different users of
  610.                  * this border.  */
  611.     XColor *bgColorPtr;        /* Background color (intensity
  612.                  * between lightColorPtr and
  613.                  * darkColorPtr). */
  614.     XColor *darkColorPtr;    /* Color for darker areas (must free when
  615.                  * deleting structure). NULL means shadows
  616.                  * haven't been allocated yet.*/
  617.     XColor *lightColorPtr;    /* Color used for lighter areas of border
  618.                  * (must free this when deleting structure).
  619.                  * NULL means shadows haven't been allocated
  620.                  * yet. */
  621.     Pixmap shadow;        /* Stipple pattern to use for drawing
  622.                  * shadows areas.  Used for displays with
  623.                  * <= 64 colors or where colormap has filled
  624.                  * up. */
  625.     GC bgGC;            /* Used (if necessary) to draw areas in
  626.                  * the background color. */
  627.     GC darkGC;            /* Used to draw darker parts of the
  628.                  * border. None means the shadow colors
  629.                  * haven't been allocated yet.*/
  630.     GC lightGC;            /* Used to draw lighter parts of
  631.                  * the border. None means the shadow colors
  632.                  * haven't been allocated yet. */
  633.     Tcl_HashEntry *hashPtr;    /* Entry in borderTable (needed in
  634.                  * order to delete structure). */
  635. } Border;
  636.  
  637. void
  638. Blt_Print3DRectangle(graphPtr, border, x, y, width, height, borderWidth,
  639.     relief)
  640.     Graph *graphPtr;        /* File pointer to write PS output. */
  641.     Tk_3DBorder border;        /* Token for border to draw. */
  642.     int x, y;            /* Coordinates of rectangle */
  643.     unsigned int width, height;    /* Region to be drawn. */
  644.     int borderWidth;        /* Desired width for border, in pixels. */
  645.     int relief;            /* Should be either TK_RELIEF_RAISED or
  646.                                  * TK_RELIEF_SUNKEN;  indicates position of
  647.                                  * interior of window relative to exterior. */
  648. {
  649.     Border *borderPtr = (Border *) border;
  650.     XColor lightColor, darkColor;
  651.     XColor *lightColorPtr, *darkColorPtr;
  652.     XColor *top, *bottom;
  653.     XPoint points[7];
  654.     Tk_Window tkwin = graphPtr->tkwin;
  655.     int twiceWidth = (borderWidth * 2);
  656.  
  657.     if ((width < twiceWidth) || (height < twiceWidth)) {
  658.     return;
  659.     }
  660.     if ((borderPtr->lightColorPtr == NULL) ||
  661.     (borderPtr->darkColorPtr == NULL)) {
  662.     Screen *screenPtr;
  663.     Colormap colorMap;
  664.  
  665.     lightColor.pixel = borderPtr->bgColorPtr->pixel;
  666.     screenPtr = Tk_Screen(graphPtr->tkwin);
  667.     if (lightColor.pixel == WhitePixelOfScreen(screenPtr)) {
  668.         darkColor.pixel = BlackPixelOfScreen(screenPtr);
  669.     } else {
  670.         darkColor.pixel = WhitePixelOfScreen(screenPtr);
  671.     }
  672.     colorMap = Tk_Colormap(graphPtr->tkwin);
  673.     XQueryColor(graphPtr->display, colorMap, &lightColor);
  674.     lightColorPtr = &lightColor;
  675.     XQueryColor(Tk_Display(tkwin), colorMap, &darkColor);
  676.     darkColorPtr = &darkColor;
  677.     } else {
  678.     lightColorPtr = borderPtr->lightColorPtr;
  679.     darkColorPtr = borderPtr->darkColorPtr;
  680.     }
  681.  
  682.     /*
  683.      * Handle grooves and ridges with recursive calls.
  684.      */
  685.  
  686.     if ((relief == TK_RELIEF_GROOVE) || (relief == TK_RELIEF_RIDGE)) {
  687.     int halfWidth, insideOffset;
  688.  
  689.     halfWidth = borderWidth / 2;
  690.     insideOffset = borderWidth - halfWidth;
  691.     Blt_Print3DRectangle(graphPtr, border, x, y, width, height, halfWidth,
  692.         (relief == TK_RELIEF_GROOVE) ? TK_RELIEF_SUNKEN :
  693.         TK_RELIEF_RAISED);
  694.     Blt_Print3DRectangle(graphPtr, border, x + insideOffset,
  695.         y + insideOffset, width - insideOffset * 2,
  696.         height - insideOffset * 2, halfWidth,
  697.         (relief == TK_RELIEF_GROOVE) ? TK_RELIEF_RAISED :
  698.         TK_RELIEF_SUNKEN);
  699.     return;
  700.     }
  701.     if (relief == TK_RELIEF_RAISED) {
  702.     top = lightColorPtr;
  703.     bottom = darkColorPtr;
  704.     } else if (relief == TK_RELIEF_SUNKEN) {
  705.     top = darkColorPtr;
  706.     bottom = lightColorPtr;
  707.     } else {
  708.     top = bottom = borderPtr->bgColorPtr;
  709.     }
  710.     Blt_BackgroundToPostScript(graphPtr, bottom);
  711.     Blt_RectangleToPostScript(graphPtr, x, y + height - borderWidth,
  712.     width, (unsigned int)borderWidth);
  713.     Blt_RectangleToPostScript(graphPtr, x + width - borderWidth, y,
  714.     (unsigned int)borderWidth, (unsigned int)height);
  715.     points[0].x = points[1].x = points[6].x = x;
  716.     points[0].y = points[6].y = y + height;
  717.     points[1].y = points[2].y = y;
  718.     points[2].x = x + width;
  719.     points[3].x = x + width - borderWidth;
  720.     points[3].y = points[4].y = y + borderWidth;
  721.     points[4].x = points[5].x = x + borderWidth;
  722.     points[5].y = y + height - borderWidth;
  723.     if (relief != TK_RELIEF_FLAT) {
  724.     Blt_BackgroundToPostScript(graphPtr, top);
  725.     }
  726.     Blt_PolygonToPostScript(graphPtr, points, 7);
  727. }
  728.  
  729. void
  730. Blt_3DRectangleToPostScript(graphPtr, border, x, y, width, height,
  731.     borderWidth, relief)
  732.     Graph *graphPtr;
  733.     Tk_3DBorder border;        /* Token for border to draw. */
  734.     int x, y;            /* Coordinates of top-left of border area */
  735.     unsigned int width, height;    /* Dimension of border to be drawn. */
  736.     int borderWidth;        /* Desired width for border, in pixels. */
  737.     int relief;            /* Should be either TK_RELIEF_RAISED or
  738.                                  * TK_RELIEF_SUNKEN;  indicates position of
  739.                                  * interior of window relative to exterior. */
  740. {
  741.     Border *borderPtr = (Border *) border;
  742.  
  743.     /*
  744.      * I'm assuming that the rectangle is to be drawn as a background.
  745.      * Setting the pen color as a foreground or background, only affects
  746.      * the plot when the colormode option is "monochrome".
  747.      */
  748.     Blt_BackgroundToPostScript(graphPtr, borderPtr->bgColorPtr);
  749.     Blt_RectangleToPostScript(graphPtr, x, y, width, height);
  750.     Blt_Print3DRectangle(graphPtr, border, x, y, width, height, borderWidth,
  751.     relief);
  752. }
  753.  
  754. void
  755. Blt_StippleToPostScript(graphPtr, bitmap, width, height, fillOrStroke)
  756.     Graph *graphPtr;
  757.     Pixmap bitmap;
  758.     unsigned int width, height;
  759.     int fillOrStroke;
  760. {
  761.     sprintf(graphPtr->scratchPtr, "%d %d\n", width, height);
  762.     Tcl_AppendResult(graphPtr->interp, graphPtr->scratchPtr, (char *)NULL);
  763.     XBitmapToPostScript(graphPtr, bitmap, width, height);
  764.     Tcl_AppendResult(graphPtr->interp, (fillOrStroke) ? "true" : "false",
  765.     " StippleFill\n", (char *)NULL);
  766. }
  767.  
  768. void
  769. Blt_BitmapToPostScript(graphPtr, bitmap, centerX, centerY,
  770.     width, height, theta, bgColorPtr)
  771.     Graph *graphPtr;
  772.     Pixmap bitmap;        /* Bitmap to be converted to PostScript */
  773.     int centerX, centerY;    /* Bitmap's center coordinates */
  774.     unsigned int width, height;    /* Extents of bitmap */
  775.     double theta;        /* Degrees to rotate bitmap */
  776.     XColor *bgColorPtr;        /* Background color of bitmap: NULL indicates
  777.                  * no background color */
  778. {
  779.     if (bgColorPtr != NULL) {
  780.     Tcl_AppendResult(graphPtr->interp, "{ ", (char *)NULL);
  781.     XColorToPostScript(graphPtr, bgColorPtr, SET_BG_COLOR);
  782.     Tcl_AppendResult(graphPtr->interp, "} true ", (char *)NULL);
  783.     } else {
  784.     Tcl_AppendResult(graphPtr->interp, "false ", (char *)NULL);
  785.     }
  786.     sprintf(graphPtr->scratchPtr, "%d %d %d %d %g\n", centerX, centerY,
  787.     width, height, theta);
  788.     Tcl_AppendResult(graphPtr->interp, graphPtr->scratchPtr, (char *)NULL);
  789.     XBitmapToPostScript(graphPtr, bitmap, width, height);
  790.     Tcl_AppendResult(graphPtr->interp, " DrawBitmap\n", (char *)NULL);
  791. }
  792.  
  793. /*
  794.  * -----------------------------------------------------------------
  795.  *
  796.  * Blt_FontToPostScript --
  797.  *
  798.  *      Map the X font to a PostScript font and point size.
  799.  *
  800.  *    If a Tcl array variable was specified, each element should
  801.  *    be indexed by the X11 font name and contain a list of 1-2
  802.  *    elements; the PostScript font name and the desired point
  803.  *    size.  The point size may be omitted and the X font point
  804.  *    size will be used.
  805.  *
  806.  *    Otherwise, if the foundry is "Adobe", we try to do a pausible
  807.  *    mapping looking at the full name of the font and building
  808.  *    a string in the form of "Family-TypeFace".
  809.  *
  810.  * Returns:
  811.  *      None.
  812.  *
  813.  * Side Effects:
  814.  *      PostScript commands are output to change the type and the
  815.  *      point size of the current font.
  816.  *
  817.  * -----------------------------------------------------------------
  818.  */
  819. void
  820. Blt_FontToPostScript(graphPtr, fontPtr)
  821.     Graph *graphPtr;
  822.     XFontStruct *fontPtr;    /* X font to query about */
  823. {
  824.     PostScript *psPtr = (PostScript *)graphPtr->postscript;
  825.     unsigned long fontProp;
  826.     char *fontName;
  827.     int pointSize;
  828.  
  829.     fontName = "Helvetica-Bold";/* Default font */
  830.     pointSize = 120;        /* Default point size */
  831.     if (XGetFontProperty(fontPtr, XA_POINT_SIZE, &fontProp) != False) {
  832.     pointSize = (int)fontProp;
  833.     }
  834.     if (psPtr->fontVarName != NULL) {
  835.     char *fontInfo;
  836.  
  837.     fontInfo = Tcl_GetVar2(graphPtr->interp, psPtr->fontVarName,
  838.         Tk_NameOfFontStruct(fontPtr), 0);
  839.     if (fontInfo != NULL) {
  840.         int numProps;
  841.         char **propArr = NULL;
  842.  
  843.         if (Tcl_SplitList(graphPtr->interp, fontInfo, &numProps,
  844.             &propArr) == TCL_OK) {
  845.         fontName = propArr[0];
  846.         if (numProps == 2) {
  847.             Tcl_GetInt(graphPtr->interp, propArr[1], &pointSize);
  848.         }
  849.         }
  850.         sprintf(graphPtr->scratchPtr, "%g /%s SetFont\n",
  851.         (double)pointSize, fontName);
  852.         Tcl_AppendResult(graphPtr->interp, graphPtr->scratchPtr,
  853.         (char *)NULL);
  854.         if (propArr != (char **)NULL) {
  855.         free((char *)propArr);
  856.         }
  857.         return;
  858.     }
  859.     }
  860.     /*
  861.      * Otherwise, try to map by querying the X font for its properties
  862.      */
  863.     fontName = XFontStructToPostScript(graphPtr->tkwin, fontPtr);
  864.     if (fontName == NULL) {
  865.     fontName = "Helvetica-Bold";    /* Default font */
  866.     }
  867.     sprintf(graphPtr->scratchPtr, "%g /%s SetFont\n",
  868.     (double)pointSize / 10.0, fontName);
  869.     Tcl_AppendResult(graphPtr->interp, graphPtr->scratchPtr, (char *)NULL);
  870. }
  871.  
  872. /*
  873.  * -----------------------------------------------------------------
  874.  *
  875.  * Blt_TextToPostScript --
  876.  *
  877.  *      Output PostScript commands to print a text string. The string
  878.  *    may be rotated at any arbitrary angle, and placed according
  879.  *    the anchor type given. The anchor indicates how to interpret
  880.  *    the window coordinates as an anchor for the text bounding box.
  881.  *    If a background color is specified (i.e. bgColorPtr != NULL),
  882.  *    output a filled rectangle the size of the bounding box in the
  883.  *    given background color.
  884.  *
  885.  * Results:
  886.  *      None.
  887.  *
  888.  * Side Effects:
  889.  *      Text string is drawn using the given font and GC on the graph
  890.  *    window at the given coordinates, anchor, and rotation
  891.  * -----------------------------------------------------------------
  892.  */
  893. void
  894. Blt_TextToPostScript(graphPtr, text, attrPtr, x, y)
  895.     Graph *graphPtr;
  896.     char *text;            /* Text string to print */
  897.     TextAttributes *attrPtr;    /* Text attribute information: rotation,
  898.                  * font, background color, anchor */
  899.     int x, y;            /* Window coordinates of anchor */
  900. {
  901.     unsigned int width, height;    /* Width and height of unrotated text */
  902.     XPoint upperLeft;        /* Point in upperleft corner of rotated box */
  903.     double centerX, centerY;
  904.     unsigned int rotWidth, rotHeight;
  905.     char *p, *q, *safeText;
  906.  
  907.     if ((text == NULL) || (*text == '\0')) {
  908.     return;
  909.     }
  910.     width = Blt_TextStringWidth(attrPtr->fontPtr, text);
  911.     height = attrPtr->fontPtr->ascent + attrPtr->fontPtr->descent;
  912.     Blt_GetBoundingBox(width, height, attrPtr->theta, &rotWidth, &rotHeight,
  913.     (XPoint *)NULL);
  914.     /*
  915.      * Find the center of the bounding box
  916.      */
  917.     upperLeft = Blt_TranslateBoxCoords(x, y, rotWidth, rotHeight,
  918.     attrPtr->anchor);
  919.     centerX = upperLeft.x + (rotWidth * 0.5);
  920.     centerY = upperLeft.y + (rotHeight * 0.5);
  921.  
  922.     Blt_FontToPostScript(graphPtr, attrPtr->fontPtr);
  923.     Blt_ForegroundToPostScript(graphPtr, attrPtr->fgColorPtr);
  924.  
  925.     if (attrPtr->bgColorPtr == (XColor *)NULL) {
  926.     Tcl_AppendResult(graphPtr->interp, "false ", (char *)NULL);
  927.     } else {
  928.     Tcl_AppendResult(graphPtr->interp, "{ ", (char *)NULL);
  929.     XColorToPostScript(graphPtr, attrPtr->bgColorPtr, SET_BG_COLOR);
  930.     Tcl_AppendResult(graphPtr->interp, "} true ", (char *)NULL);
  931.     }
  932.     q = safeText = (char *)malloc(strlen(text) * 2 + 1);
  933.     p = text;
  934.     while (*p != '\0') {
  935.       if (*p == '(' || *p == ')')
  936.         *q++ = '\\';
  937.       *q++ = *p++;
  938.     }
  939.     *q = '\0';
  940.     sprintf(graphPtr->scratchPtr, "%g %g %d %d %d %g (%s) DrawText\n",
  941.     centerX, centerY, width, height, attrPtr->fontPtr->ascent,
  942.     attrPtr->theta, safeText);
  943.     free(safeText);
  944.     Tcl_AppendResult(graphPtr->interp, graphPtr->scratchPtr, (char *)NULL);
  945. }
  946.  
  947. /*
  948.  * -----------------------------------------------------------------
  949.  *
  950.  * Blt_PrintLine --
  951.  *
  952.  *      Outputs PostScript commands to print a multi-segmented line.
  953.  *    It assumes a procedure DashesProc was previously defined.
  954.  *
  955.  * Results:
  956.  *      None.
  957.  *
  958.  * Side Effects:
  959.  *      Line is printed.
  960.  
  961.  * -----------------------------------------------------------------
  962.  */
  963.  
  964. void
  965. Blt_PrintLine(graphPtr, pointArr, numPoints)
  966.     Graph *graphPtr;
  967.     XPoint *pointArr;
  968.     int numPoints;
  969. {
  970.     register int i;
  971.  
  972.     if (numPoints <= 0) {
  973.     return;
  974.     }
  975.     sprintf(graphPtr->scratchPtr, "newpath %d %d moveto\n", pointArr[0].x,
  976.     pointArr[0].y);
  977.     Tcl_AppendResult(graphPtr->interp, graphPtr->scratchPtr, (char *)NULL);
  978.     for (i = 1; i < (numPoints - 1); i++) {
  979.     if (i % PSMAXPATH) {
  980.         sprintf(graphPtr->scratchPtr, "%d %d lineto\n",
  981.         pointArr[i].x, pointArr[i].y);
  982.     } else {
  983.         sprintf(graphPtr->scratchPtr,
  984.         "%d %d lineto\nDashesProc stroke\nnewpath %d %d moveto\n",
  985.         pointArr[i].x, pointArr[i].y, pointArr[i].x, pointArr[i].y);
  986.     }
  987.     Tcl_AppendResult(graphPtr->interp, graphPtr->scratchPtr, (char *)NULL);
  988.     }
  989.     /*
  990.      * Note: It's assumed that i is numPoints - 1 after finishing loop
  991.      */
  992.     sprintf(graphPtr->scratchPtr, "%d %d lineto\nDashesProc stroke\n",
  993.     pointArr[i].x, pointArr[i].y);
  994.     Tcl_AppendResult(graphPtr->interp, graphPtr->scratchPtr, (char *)NULL);
  995. }
  996.  
  997.  
  998.  
  999. static void
  1000. DestroyPostScript(graphPtr)
  1001.     Graph *graphPtr;
  1002. {
  1003.     Tk_FreeOptions(configSpecs, (char *)graphPtr->postscript,
  1004.     graphPtr->display, 0);
  1005.     free((char *)graphPtr->postscript);
  1006. }
  1007.  
  1008. /*
  1009.  *----------------------------------------------------------------------
  1010.  *
  1011.  * ConfigurePostScript --
  1012.  *
  1013.  *      This procedure is invoked to print the graph in a file.
  1014.  *
  1015.  * Results:
  1016.  *      None.
  1017.  *
  1018.  * Side effects:
  1019.  *      A new PostScript file is created.
  1020.  *
  1021.  *----------------------------------------------------------------------
  1022.  */
  1023. static int
  1024. ConfigurePostScript(graphPtr, argc, argv)
  1025.     Graph *graphPtr;
  1026.     int argc;            /* Number of options in argv vector */
  1027.     char **argv;        /* Option vector */
  1028. {
  1029.     int flags = TK_CONFIG_ARGV_ONLY;
  1030.  
  1031.     if (argc == 2) {
  1032.     return (Tk_ConfigureInfo(graphPtr->interp, graphPtr->tkwin,
  1033.         configSpecs, (char *)graphPtr->postscript, (char *)NULL, flags));
  1034.     } else if (argc == 3) {
  1035.     return (Tk_ConfigureInfo(graphPtr->interp, graphPtr->tkwin,
  1036.         configSpecs, (char *)graphPtr->postscript, argv[2], flags));
  1037.     }
  1038.     if (Tk_ConfigureWidget(graphPtr->interp, graphPtr->tkwin, configSpecs,
  1039.         argc - 2, argv + 2, (char *)graphPtr->postscript, flags) != TCL_OK) {
  1040.     return TCL_ERROR;
  1041.     }
  1042.     return TCL_OK;
  1043. }
  1044.  
  1045. /*
  1046.  * --------------------------------------------------------------------------
  1047.  *
  1048.  * PrintPreamble
  1049.  *
  1050.  *        The PostScript preamble calculates the needed translation and scaling
  1051.  *        to make X11 coordinates compatible with PostScript.
  1052.  *
  1053.  *     +-----------------------+
  1054.  *     |        1" = 72pica    |   1" left, right, top, and bottom margin
  1055.  *     |  ------------------ur |   leaving a 6.5" x 9" area.
  1056.  *     |  |  O--->          |  |
  1057.  *     |  |  |              |  |
  1058.  *     |  |  | 6.5" = 468 pica |
  1059.  *     |  |  v              |  |
  1060.  *     |  |                 |  |                     468 pica
  1061.  *     |  |  9" = 648 pica  |  |   scaleX =  ---------------------------
  1062.  *     |  |                 |  |                  Width of window
  1063.  * 11" |  |                 |  |
  1064.  *     | ll------------------  |
  1065.  *     |     bounding box      |                     648 pica
  1066.  *     |                       |   scaleY =  ---------------------------
  1067.  *     |                       |                  Height of window
  1068.  *     |                       |
  1069.  *     |                       |  To retain the aspect ratio, we use only
  1070.  *     |                       |  the smaller of the two scales.  The Y scale
  1071.  *     |                       |  is negative since the X11 Y origin is at
  1072.  *     +-----------------------+  the top instead of the bottom.
  1073.  *              8.5"
  1074.  *
  1075.  *     Landscape:
  1076.  *        Rotate the picture by 90 degrees
  1077.  * ---------------------------------------------------------------------
  1078.  */
  1079.  
  1080. #ifdef TIME_WITH_SYS_TIME
  1081. #include <sys/time.h>
  1082. #include <time.h>
  1083. #else
  1084. #ifdef HAVE_SYS_TIME_H
  1085. #include <sys/time.h>
  1086. #else
  1087. #include <time.h>
  1088. #endif /* HAVE_SYS_TIME_H */
  1089. #endif /* TIME_WITH_SYS_TIME */
  1090.  
  1091. static int
  1092. PrintPreamble(graphPtr, fileName)
  1093.     Graph *graphPtr;
  1094.     char *fileName;
  1095. {
  1096.     PostScript *psPtr = (PostScript *)graphPtr->postscript;
  1097.     long date;
  1098.     char *version;
  1099.  
  1100. #ifdef NO_INLINE_PROLOG
  1101. #define STRING_LENGTH 400
  1102.     char string[STRING_LENGTH + 1];
  1103.     char *libDir;
  1104.     FILE *f;
  1105.  
  1106. #endif /* NO_INLINE_PROLOG */
  1107.     XPoint origin;
  1108.  
  1109.     version = Tcl_GetVar(graphPtr->interp, "tk_version", TCL_GLOBAL_ONLY);
  1110.     if (version == NULL) {
  1111.     version = "???";
  1112.     }
  1113.     if (fileName == NULL) {
  1114.     fileName = Tk_PathName(graphPtr->tkwin);
  1115.     }
  1116.     Tcl_AppendResult(graphPtr->interp,
  1117.     "%!PS-Adobe-3.0 EPSF-3.0\n%%Pages: 1\n",
  1118.     "%%Title: (", fileName, ")\n",
  1119.     "%%DocumentNeededResources: font Helvetica Courier\n",
  1120.     (char *)NULL);
  1121.  
  1122.     /* Find upper left coordinate of bounding box */
  1123.     origin = Blt_TranslateBoxCoords(psPtr->x, psPtr->y,
  1124.     (unsigned int)psPtr->width, (unsigned int)psPtr->height,
  1125.     psPtr->anchor);
  1126.     sprintf(graphPtr->scratchPtr, "%%%%BoundingBox:  %d %d %d %d\n",
  1127.     origin.x, 792 - (psPtr->height + origin.y),    /* Lower left */
  1128.     origin.x + psPtr->width, (792 - origin.y));
  1129.     date = time((time_t *) NULL);
  1130.     Tcl_AppendResult(graphPtr->interp, graphPtr->scratchPtr,
  1131.     "%%Creator: ", Tk_Class(graphPtr->tkwin),
  1132.     " (Tk version ", version, ")\n",
  1133.     "%%CreationDate: ", ctime(&date),
  1134.     "%%EndComments\n", (char *)NULL);
  1135. #ifndef NO_INLINE_PROLOG
  1136.     Tcl_AppendResult(graphPtr->interp, postScriptDefinitions, (char *)NULL);
  1137. #else
  1138.     /*
  1139.      * Read a standard prolog file from disk and insert it into
  1140.      * the PostScript.
  1141.      */
  1142.     libDir = Tcl_GetVar(graphPtr->interp, "blt_library", TCL_GLOBAL_ONLY);
  1143.     if (libDir == NULL) {
  1144.     Tcl_ResetResult(graphPtr->interp);
  1145.     Tcl_AppendResult(graphPtr->interp,
  1146.         "couldn't find BLT library directory:",
  1147.         " blt_library variable doesn't exist", (char *)NULL);
  1148.     return TCL_ERROR;
  1149.     }
  1150.     sprintf(string, "%.350s/bltGraph.pro", libDir);
  1151.     f = fopen(string, "r");
  1152.     if (f == NULL) {
  1153.     Tcl_ResetResult(graphPtr->interp);
  1154.     Tcl_AppendResult(graphPtr->interp, "couldn't open prolog file \"",
  1155.         string, "\": ", Tcl_PosixError(graphPtr->interp),
  1156.         (char *)NULL);
  1157.     return TCL_ERROR;
  1158.     }
  1159.     Tcl_AppendResult(graphPtr->interp, "\n% including file \"", string,
  1160.     "\"\n\n", (char *)NULL);
  1161.     while (fgets(graphPtr->scratchPtr, STRING_LENGTH, f) != NULL) {
  1162.     Tcl_AppendResult(graphPtr->interp, graphPtr->scratchPtr, (char *)NULL);
  1163.     }
  1164.     if (ferror(f)) {
  1165.     fclose(f);
  1166.     Tcl_ResetResult(graphPtr->interp);
  1167.     Tcl_AppendResult(graphPtr->interp, "error reading prolog file \"",
  1168.         string, "\": ", Tcl_PosixError(graphPtr->interp),
  1169.         (char *)NULL);
  1170.     return TCL_ERROR;
  1171.     }
  1172.     fclose(f);
  1173. #endif /* NO_INLINE_PROLOG */
  1174.     if (psPtr->landscape) {
  1175.     sprintf(graphPtr->scratchPtr,
  1176.         "/CL %d def\n%d %d translate\n-90 rotate\n%%%%EndSetup\n\n",
  1177.         psPtr->colorLevel, origin.x, origin.y + psPtr->height);
  1178.     } else {
  1179.     sprintf(graphPtr->scratchPtr,
  1180.         "/CL %d def\n%d %d translate\n%%%%EndSetup\n\n",
  1181.         psPtr->colorLevel, origin.x, origin.y);
  1182.     }
  1183.     Tcl_AppendResult(graphPtr->interp, graphPtr->scratchPtr, (char *)NULL);
  1184.     return TCL_OK;
  1185. }
  1186.  
  1187. /*
  1188.  * -----------------------------------------------------------------
  1189.  *
  1190.  * PrintTags --
  1191.  *
  1192.  * -----------------------------------------------------------------
  1193.  */
  1194. static void
  1195. PrintTags(graphPtr)
  1196.     Graph *graphPtr;
  1197. {
  1198.     Blt_ListEntry *entryPtr;
  1199.     register Tag *tagPtr;
  1200.  
  1201.     for (entryPtr = Blt_FirstListEntry(&(graphPtr->tagList)); entryPtr != NULL;
  1202.     entryPtr = Blt_NextListEntry(entryPtr)) {
  1203.     tagPtr = (Tag *)Blt_GetListValue(entryPtr);
  1204.     if ((tagPtr->printProc != NULL) &&
  1205.         ((tagPtr->elemId == (Tk_Uid) NULL) ||
  1206.         Blt_FindListEntry(&(graphPtr->elemList), tagPtr->elemId))) {
  1207.         Tcl_AppendResult(graphPtr->interp, "\n% ",
  1208.         (*tagPtr->typeProc) (tagPtr), " Tag\n\n",
  1209.         (char *)NULL);
  1210.         (*tagPtr->printProc) (graphPtr, tagPtr);
  1211.     }
  1212.     }
  1213. }
  1214.  
  1215. /*
  1216.  * -----------------------------------------------------------------
  1217.  *
  1218.  * PrintElements --
  1219.  *
  1220.  * -----------------------------------------------------------------
  1221.  */
  1222. static void
  1223. PrintElements(graphPtr)
  1224.     Graph *graphPtr;
  1225. {
  1226.     Blt_ListEntry *entryPtr;
  1227.     register Element *elemPtr;
  1228.  
  1229.     for (entryPtr = Blt_FirstListEntry(&(graphPtr->elemList));
  1230.     entryPtr != NULL; entryPtr = Blt_NextListEntry(entryPtr)) {
  1231.     elemPtr = (Element *)Blt_GetListValue(entryPtr);
  1232.     Tcl_AppendResult(graphPtr->interp, "\n% Element \"", elemPtr->id,
  1233.         "\"\n\n", (char *)NULL);
  1234.     (*elemPtr->printProc) (graphPtr, elemPtr, ELEM_NORMAL);
  1235.     }
  1236. }
  1237.  
  1238. static void
  1239. PrintActiveElements(graphPtr)
  1240.     Graph *graphPtr;
  1241. {
  1242.     Blt_ListEntry *entryPtr;
  1243.     register Element *elemPtr;
  1244.  
  1245.     for (entryPtr = Blt_FirstListEntry(&(graphPtr->elemList));
  1246.     entryPtr != NULL; entryPtr = Blt_NextListEntry(entryPtr)) {
  1247.     elemPtr = (Element *)Blt_GetListValue(entryPtr);
  1248.     if (elemPtr->flags & ACTIVE) {
  1249.         Tcl_AppendResult(graphPtr->interp, "\n% Active Element \"",
  1250.         elemPtr->id, "\"\n\n", (char *)NULL);
  1251.         (*elemPtr->printProc) (graphPtr, elemPtr, ELEM_ACTIVE);
  1252.     }
  1253.     }
  1254. }
  1255.  
  1256.  
  1257. static void
  1258. PrintExterior(graphPtr, regionPtr)
  1259.     Graph *graphPtr;
  1260.     XRectangle *regionPtr;    /* Interior plotting region */
  1261. {
  1262.     register int i;
  1263.     int x, y;
  1264.     XRectangle rectArr[4];
  1265.     TextAttributes textAttr;
  1266.     GraphAxis *axisPtr;
  1267.  
  1268.     rectArr[0].x = rectArr[0].y = rectArr[3].x = rectArr[1].x = 0;
  1269.     rectArr[0].width = rectArr[3].width = graphPtr->width;
  1270.     rectArr[0].height = regionPtr->y;
  1271.     rectArr[3].y = regionPtr->y + regionPtr->height;
  1272.     rectArr[3].height = graphPtr->height - rectArr[3].y;
  1273.     rectArr[2].y = rectArr[1].y = regionPtr->y;
  1274.     rectArr[1].width = regionPtr->x;
  1275.     rectArr[2].height = rectArr[1].height = regionPtr->height;
  1276.     rectArr[2].x = regionPtr->x + regionPtr->width;
  1277.     rectArr[2].width = graphPtr->width - rectArr[2].x;
  1278.  
  1279.     /* Clear the surrounding margins and clip the plotting surface */
  1280.     Blt_BackgroundToPostScript(graphPtr, Tk_3DBorderColor(graphPtr->border));
  1281.     for (i = 0; i < 4; i++) {
  1282.     Blt_RectangleToPostScript(graphPtr, rectArr[i].x,
  1283.         rectArr[i].y, rectArr[i].width, rectArr[i].height);
  1284.     }
  1285.     /* Interior 3D border */
  1286.     if ((graphPtr->plotRelief != TK_RELIEF_FLAT) && (graphPtr->plotBW > 0)) {
  1287.     Blt_Print3DRectangle(graphPtr, graphPtr->border, regionPtr->x,
  1288.         regionPtr->y, regionPtr->width, regionPtr->height,
  1289.         graphPtr->plotBW, graphPtr->plotRelief);
  1290.     }
  1291.     /* Print legend if using default location */
  1292.     if (graphPtr->legendPtr->useDefault) {
  1293.     (*graphPtr->legendPtr->printProc) (graphPtr);
  1294.     }
  1295.     textAttr.theta = 0.0;
  1296.     textAttr.anchor = TK_ANCHOR_CENTER;
  1297.     textAttr.fgColorPtr = graphPtr->marginFg;
  1298.     textAttr.bgColorPtr = (XColor *)NULL;
  1299.     textAttr.fontPtr = graphPtr->fontPtr;
  1300.  
  1301.     if (graphPtr->title != NULL) {
  1302.     x = (graphPtr->extreme.x + graphPtr->origin.x) / 2;
  1303.     y = graphPtr->borderWidth + TEXTHEIGHT(graphPtr->fontPtr);
  1304.     Blt_TextToPostScript(graphPtr, graphPtr->title, &textAttr, x, y);
  1305.     }
  1306.     for (i = 0; i < 4; i++) {    /* Print axes */
  1307.     axisPtr = graphPtr->axisArr[i];
  1308.     if (axisPtr->mapped) {
  1309.         (*axisPtr->printProc) (graphPtr, axisPtr, &textAttr);
  1310.     }
  1311.     }
  1312. }
  1313.  
  1314. /*
  1315.  *----------------------------------------------------------------------
  1316.  *
  1317.  * PrintPostScript --
  1318.  *
  1319.  *      This procedure is invoked to print the graph in a file.
  1320.  *
  1321.  * Results:
  1322.  *      None.
  1323.  *
  1324.  * Side effects:
  1325.  *      A new PostScript file is created.
  1326.  *
  1327.  *----------------------------------------------------------------------
  1328.  */
  1329. static int
  1330. PrintPostScript(graphPtr, argc, argv)
  1331.     Graph *graphPtr;        /* Graph widget record */
  1332.     int argc;            /* Number of options in argv vector */
  1333.     char **argv;        /* Option vector */
  1334. {
  1335.     PostScript *psPtr = (PostScript *)graphPtr->postscript;
  1336.     GraphLegend *legendPtr = graphPtr->legendPtr;
  1337.     XRectangle clipRect;
  1338.     int result = TCL_ERROR;
  1339.     char scratchSpace[BUFSIZ * 2];
  1340.     FILE *f = NULL;
  1341.     int twiceBW;
  1342.     char *fileName;        /* Name of file to write PostScript output
  1343.                                  * If NULL, output is returned via
  1344.                                  * interp->result. */
  1345.  
  1346.     fileName = NULL;
  1347.     if ((argc > 2) && (argv[2][0] != '-')) {
  1348.     f = fopen(argv[2], "w");
  1349.     if (f == NULL) {
  1350.         Tcl_AppendResult(graphPtr->interp, "can't create \"", argv[2],
  1351.         "\": ", Tcl_PosixError(graphPtr->interp), (char *)NULL);
  1352.         return TCL_ERROR;
  1353.     }
  1354.     fileName = argv[2];
  1355.     argv++, argc--;
  1356.     }
  1357.     graphPtr->scratchPtr = scratchSpace;
  1358.     if ((argc > 0) &&
  1359.     (Tk_ConfigureWidget(graphPtr->interp, graphPtr->tkwin, configSpecs,
  1360.         argc - 2, argv + 2, (char *)psPtr,
  1361.         TK_CONFIG_ARGV_ONLY) != TCL_OK)) {
  1362.     goto error;
  1363.     }
  1364.     /* Scale coordinates into printer points (pica) */
  1365.     psPtr->x = psPtr->xOffset * psPtr->pointScale;
  1366.     psPtr->y = psPtr->yOffset * psPtr->pointScale;
  1367.     psPtr->width = graphPtr->width;
  1368.     if (psPtr->pageWidth > 0) {
  1369.     psPtr->width = psPtr->pageWidth * psPtr->pointScale;
  1370.     }
  1371.     psPtr->height = graphPtr->height;
  1372.     if (psPtr->pageHeight > 0) {
  1373.     psPtr->height = psPtr->pageHeight * psPtr->pointScale;
  1374.     }
  1375.     if (psPtr->landscape) {
  1376.     graphPtr->width = psPtr->height;
  1377.     graphPtr->height = psPtr->width;
  1378.     } else {
  1379.     graphPtr->height = psPtr->height;
  1380.     graphPtr->width = psPtr->width;
  1381.     }
  1382.     if (Blt_ComputeLayout(graphPtr) != TCL_OK) {
  1383.     graphPtr->interp->result = "not enough space to print graph";
  1384.     goto error;
  1385.     }
  1386.     graphPtr->flags |= LAYOUT_ALL;
  1387.     Blt_LayoutGraph(graphPtr);
  1388.     Tcl_ResetResult(graphPtr->interp);
  1389.     if (PrintPreamble(graphPtr, fileName) != TCL_OK) {
  1390.     goto error;
  1391.     }
  1392.     twiceBW = (graphPtr->plotBW + graphPtr->plotBW);
  1393.     /*
  1394.      * Determine rectangle of the plotting surface the graph window
  1395.      */
  1396.     clipRect.x = graphPtr->origin.x - graphPtr->plotBW;
  1397.     clipRect.y = graphPtr->extreme.y - graphPtr->plotBW;
  1398.     clipRect.width = (graphPtr->extreme.x - graphPtr->origin.x) + twiceBW;
  1399.     clipRect.height = (graphPtr->origin.y - graphPtr->extreme.y) + twiceBW;
  1400.  
  1401.     Blt_FontToPostScript(graphPtr, graphPtr->fontPtr);
  1402.     Blt_BackgroundToPostScript(graphPtr, graphPtr->plotBg);
  1403.     Blt_RectangleToPostScript(graphPtr, clipRect.x, clipRect.y, clipRect.width,
  1404.     clipRect.height);
  1405.     Tcl_AppendResult(graphPtr->interp, "gsave clip\n\n", (char *)NULL);
  1406.     /*
  1407.      * Draw the elements and tags on the interior of the plotting surface
  1408.      */
  1409.     PrintElements(graphPtr);
  1410.     PrintTags(graphPtr);
  1411.     PrintActiveElements(graphPtr);
  1412.     if (!legendPtr->useDefault) {
  1413.     (*legendPtr->printProc) (graphPtr);
  1414.     }
  1415.     Tcl_AppendResult(graphPtr->interp, "\n% Unset clipping\ngrestore\n\n",
  1416.     (char *)NULL);
  1417.     PrintExterior(graphPtr, &clipRect);
  1418.     Tcl_AppendResult(graphPtr->interp,
  1419.     "showpage\n%Trailer\ngrestore\nend\n%EOF\n", (char *)NULL);
  1420.     /*
  1421.      * If a file name was given, write the results to that file
  1422.      */
  1423.     if (f != NULL) {
  1424.     fputs(graphPtr->interp->result, f);
  1425.     Tcl_ResetResult(graphPtr->interp);
  1426.     if (ferror(f)) {
  1427.         Tcl_AppendResult(graphPtr->interp, "error writing file \"",
  1428.         fileName, "\": ", Tcl_PosixError(graphPtr->interp),
  1429.         (char *)NULL);
  1430.         goto error;
  1431.     }
  1432.     }
  1433.     result = TCL_OK;
  1434.  
  1435.   error:
  1436.     if (f != NULL) {
  1437.     fclose(f);
  1438.     }
  1439.     /* Reset height and width of graph window */
  1440.     graphPtr->width = Tk_Width(graphPtr->tkwin);
  1441.     graphPtr->height = Tk_Height(graphPtr->tkwin);
  1442.     graphPtr->flags = LAYOUT_ALL;
  1443.     /*
  1444.      * Redraw the graph in order to re-calculate the layout as soon
  1445.      * as possible. This is in the case the crosshairs are active.
  1446.      */
  1447.     Blt_RedrawGraph(graphPtr);
  1448.     return (result);
  1449. }
  1450.  
  1451. /*
  1452.  *----------------------------------------------------------------------
  1453.  *
  1454.  * Blt_CreatePostScript --
  1455.  *
  1456.  *      Creates a postscript structure.
  1457.  *
  1458.  * Results:
  1459.  *      None.
  1460.  *
  1461.  * Side effects:
  1462.  *      A new PostScript structure is created.
  1463.  *
  1464.  *----------------------------------------------------------------------
  1465.  */
  1466. int
  1467. Blt_CreatePostScript(graphPtr)
  1468.     Graph *graphPtr;
  1469. {
  1470.     double pixelsPerInch;
  1471.     int screenNum;
  1472.     int screenWidth, screenWidthMM;
  1473.     PostScript *psPtr;
  1474.  
  1475.     psPtr = (PostScript *)calloc(1, sizeof(PostScript));
  1476.     if (psPtr == NULL) {
  1477.     graphPtr->interp->result = "can't allocate postscript structure";
  1478.     return TCL_ERROR;
  1479.     }
  1480.     psPtr->colorLevel = FULLCOLOR_LEVEL;
  1481.     psPtr->anchor = TK_ANCHOR_NW;    /* Upperleft anchor origin */
  1482.     psPtr->destroyProc = DestroyPostScript;
  1483.     psPtr->configProc = ConfigurePostScript;
  1484.     psPtr->printProc = PrintPostScript;
  1485.     screenNum = Tk_ScreenNumber(graphPtr->tkwin);
  1486.     screenWidth = DisplayWidth(graphPtr->display, screenNum);
  1487.     screenWidthMM = DisplayWidthMM(graphPtr->display, screenNum);
  1488. #define MM_PER_INCH 25.4
  1489.     pixelsPerInch = (MM_PER_INCH * (double)screenWidth / screenWidthMM);
  1490. #define PICA_PER_INCH 72.0
  1491.     /*
  1492.      * Set margins to one inch
  1493.      */
  1494.     psPtr->pointScale = PICA_PER_INCH / pixelsPerInch;
  1495.     psPtr->xOffset = psPtr->yOffset = (int)(pixelsPerInch + 0.5);
  1496.     graphPtr->postscript = (GraphPostScript *)psPtr;
  1497.     return TCL_OK;
  1498. }
  1499.