home *** CD-ROM | disk | FTP | other *** search
/ Il CD di internet / CD.iso / SOURCE / TCL / BLT / BLT1.7L1 / BLT1 / blt-1.7 / src / bltGrPS.c < prev    next >
Encoding:
C/C++ Source or Header  |  1994-04-22  |  46.2 KB  |  1,482 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.  
  261.     if (psPtr->colorVarName != NULL) {
  262.     char *colorDesc;
  263.  
  264.     colorDesc = Tcl_GetVar2(graphPtr->interp, psPtr->colorVarName,
  265.         Tk_NameOfColor(colorPtr), 0);
  266.     if (colorDesc != NULL) {
  267.         Tcl_AppendResult(graphPtr->interp, colorDesc, " ", (char *)NULL);
  268.         return;
  269.     }
  270.     }
  271.     sprintf(graphPtr->scratchPtr, "%g %g %g %s  ",
  272.     RED(colorPtr), GREEN(colorPtr), BLUE(colorPtr),
  273.     (fgOrBg) ? "SetFgColor" : "SetBgColor");
  274.     Tcl_AppendResult(graphPtr->interp, graphPtr->scratchPtr, (char *)NULL);
  275. }
  276.  
  277. /*
  278.  *----------------------------------------------------------------------
  279.  *
  280.  * ReverseBits --
  281.  *
  282.  *    Convert a byte from a X image into PostScript image order.
  283.  *    This requires the nybbles to be reversed but also their bit values.
  284.  *
  285.  * Results:
  286.  *    The converted byte is returned.
  287.  *
  288.  *----------------------------------------------------------------------
  289.  */
  290. static unsigned char
  291. ReverseBits(byte)        /* Reverse bits */
  292.     register unsigned char byte;
  293. {
  294.     byte = ((byte >> 1) & 0x55) | ((byte << 1) & 0xaa);
  295.     byte = ((byte >> 2) & 0x33) | ((byte << 2) & 0xcc);
  296.     byte = ((byte >> 4) & 0x0f) | ((byte << 4) & 0xf0);
  297.     return (byte);
  298. }
  299.  
  300. /*
  301.  * -----------------------------------------------------------------
  302.  *
  303.  * XBitmapToPostScript --
  304.  *
  305.  *      Output a PostScript image string of the given bitmap image.
  306.  *      It is assumed the image is one bit deep and a zero value
  307.  *      indicates an off-pixel.  To convert to PostScript, the
  308.  *      bits need to be reversed from the X11 image order.
  309.  *
  310.  * Results:
  311.  *      None.
  312.  *
  313.  * Side Effects:
  314.  *      A PostScript image string is output to the file pointer in
  315.  *      the page layout structure.
  316.  *
  317.  * -----------------------------------------------------------------
  318.  */
  319. static void
  320. XBitmapToPostScript(graphPtr, bitmap, width, height)
  321.     Graph *graphPtr;
  322.     Pixmap bitmap;
  323.     unsigned int width, height;
  324. {
  325.     register unsigned int byte = 0;
  326.     register int x, y, bitPos;
  327.     unsigned long pixel;
  328.     XImage *imagePtr;
  329.     int byteCount = 0;
  330.  
  331.     imagePtr = XGetImage(graphPtr->display, bitmap, 0, 0, width, height,
  332.     1, ZPixmap);
  333.     Tcl_AppendResult(graphPtr->interp, "<", (char *)NULL);
  334.     bitPos = 0;            /* Inhibit compiler warning */
  335.     for (y = 0; y < height; y++) {
  336.     byte = 0;
  337.     for (x = 0; x < width; x++) {
  338.         pixel = XGetPixel(imagePtr, x, y);
  339.         bitPos = x % 8;
  340.         byte |= (unsigned char)(pixel << bitPos);
  341.         if (bitPos == 7) {
  342.         sprintf(graphPtr->scratchPtr, "%02x", ReverseBits(byte));
  343.         Tcl_AppendResult(graphPtr->interp, graphPtr->scratchPtr,
  344.             (char *)NULL);
  345.         byteCount++;
  346.         byte = 0;
  347.         }
  348.         if (byteCount >= 30) {
  349.         Tcl_AppendResult(graphPtr->interp, "\n", (char *)NULL);
  350.         byteCount = 0;
  351.         }
  352.     }
  353.     if (bitPos != 7) {
  354.         sprintf(graphPtr->scratchPtr, "%02x", ReverseBits(byte));
  355.         Tcl_AppendResult(graphPtr->interp, graphPtr->scratchPtr,
  356.         (char *)NULL);
  357.         byteCount++;
  358.     }
  359.     }
  360.     Tcl_AppendResult(graphPtr->interp, "> ", (char *)NULL);
  361.     XDestroyImage(imagePtr);
  362. }
  363.  
  364. /*
  365.  *----------------------------------------------------------------------
  366.  *
  367.  * NameOfAtom --
  368.  *
  369.  *    Convenience routine to wrap around Tk_GetAtomName.  Returns
  370.  *    NULL instead of "?bad atom?" is the atom cannot be found.
  371.  *
  372.  * Results:
  373.  *    The name of the atom is returned if found. Otherwise NULL.
  374.  *
  375.  *----------------------------------------------------------------------
  376.  */
  377. static char *
  378. NameOfAtom(tkwin, atom)
  379.     Tk_Window tkwin;
  380.     Atom atom;
  381. {
  382.     char *result;
  383.  
  384.     result = Tk_GetAtomName(tkwin, atom);
  385.     if (strcmp(result, "?bad atom?") == 0) {
  386.     return NULL;
  387.     }
  388.     return (result);
  389. }
  390.  
  391. /*
  392.  * -----------------------------------------------------------------
  393.  *
  394.  * XFontStructToPostScript --
  395.  *
  396.  *      Map X11 font to a PostScript font. Currently, only fonts
  397.  *    whose FOUNDRY property are "Adobe" are converted. Simply
  398.  *    gets the XA_FULL_NAME and XA_FAMILY properties and pieces
  399.  *    together a PostScript fontname.
  400.  *
  401.  * Results:
  402.  *      Returns the mapped PostScript font name is one is possible.
  403.  *    Otherwise NULL.
  404.  *
  405.  * -----------------------------------------------------------------
  406.  */
  407. static char *
  408. XFontStructToPostScript(tkwin, fontPtr)
  409.     Tk_Window tkwin;        /* Window to query for atoms */
  410.     XFontStruct *fontPtr;    /* Font structure to map to name */
  411. {
  412.     Atom atom;
  413.     char *fullName, *family, *foundry;
  414.     register char *src, *dest;
  415.     char *start;
  416.     static char string[200];    /* What size? */
  417.  
  418.     if (XGetFontProperty(fontPtr, XA_FULL_NAME, &atom) == False) {
  419.     return NULL;
  420.     }
  421.     fullName = NameOfAtom(tkwin, atom);
  422.     if (fullName == NULL) {
  423.     return NULL;
  424.     }
  425.     family = foundry = NULL;
  426.     if (XGetFontProperty(fontPtr, Tk_InternAtom(tkwin, "FOUNDRY"), &atom)) {
  427.     foundry = NameOfAtom(tkwin, atom);
  428.     }
  429.     if (XGetFontProperty(fontPtr, XA_FAMILY_NAME, &atom)) {
  430.     family = NameOfAtom(tkwin, atom);
  431.     }
  432.     /*
  433.      * Try to map font only if foundry is Adobe
  434.      */
  435.     if ((foundry == NULL) || (strcmp(foundry, "Adobe") != 0) ||
  436.     (family == NULL)) {
  437. #ifdef notdef
  438.     fprintf(stderr, "huh? Full name (%s) for non-PS font\n", fullName);
  439. #endif
  440.     return NULL;
  441.     }
  442.     src = fullName + strlen(family);
  443.     /*
  444.      * Special case: Fix mapping of "New Century Schoolbook"
  445.      */
  446.     if ((*family == 'N') && (strcmp(family, "New Century Schoolbook") == 0)) {
  447.     family = "NewCenturySchlbk";
  448.     }
  449.     /*
  450.      * PostScript font name is in the form <family>-<type face>
  451.      */
  452.     sprintf(string, "%s-", family);
  453.     dest = start = string + strlen(string);
  454.     /*
  455.      * Append the type face (part of the full name trailing the family name)
  456.      * to the the PostScript font name, removing any spaces
  457.      *
  458.      * ex. " Bold Italic" ==> "BoldItalic"
  459.      */
  460.     while (*src != '\0') {
  461.     if (*src != ' ') {
  462.         *dest++ = *src;
  463.     }
  464.     src++;
  465.     }
  466.     if (dest == start) {
  467.     --dest;            /* Remove '-' to leave just the family name */
  468.     }
  469.     *dest = '\0';        /* Make a valid string */
  470.     return (string);
  471. }
  472.  
  473. /*
  474.  * -------------------------------------------------------------------
  475.  * Routines to convert X drawing functions to PostScript commands.
  476.  * -------------------------------------------------------------------
  477.  */
  478. void
  479. Blt_BackgroundToPostScript(graphPtr, colorPtr)
  480.     Graph *graphPtr;
  481.     XColor *colorPtr;
  482. {
  483.     XColorToPostScript(graphPtr, colorPtr, SET_BG_COLOR);
  484.     Tcl_AppendResult(graphPtr->interp, "\n", (char *)NULL);
  485. }
  486.  
  487. void
  488. Blt_ForegroundToPostScript(graphPtr, colorPtr)
  489.     Graph *graphPtr;
  490.     XColor *colorPtr;
  491. {
  492.     XColorToPostScript(graphPtr, colorPtr, SET_FG_COLOR);
  493.     Tcl_AppendResult(graphPtr->interp, "\n", (char *)NULL);
  494. }
  495.  
  496. void
  497. Blt_LineWidthToPostScript(graphPtr, lineWidth)
  498.     Graph *graphPtr;
  499.     int lineWidth;
  500. {
  501.     if (lineWidth < 1) {
  502.     lineWidth = 1;
  503.     }
  504.     sprintf(graphPtr->scratchPtr, "%d setlinewidth\n", lineWidth);
  505.     Tcl_AppendResult(graphPtr->interp, graphPtr->scratchPtr, (char *)NULL);
  506. }
  507.  
  508. void
  509. Blt_LineDashesToPostScript(graphPtr, dashes)
  510.     Graph *graphPtr;
  511.     int dashes;
  512. {
  513.     if (dashes < 0) {
  514.     dashes = 0;
  515.     }
  516.     sprintf(graphPtr->scratchPtr, "%d SetDashes\n", dashes);
  517.     Tcl_AppendResult(graphPtr->interp, graphPtr->scratchPtr, (char *)NULL);
  518. }
  519.  
  520. void
  521. Blt_SetLineAttributes(graphPtr, colorPtr, lineWidth, dashes)
  522.     Graph *graphPtr;
  523.     XColor *colorPtr;
  524.     int lineWidth;
  525.     int dashes;
  526. {
  527.     Blt_ForegroundToPostScript(graphPtr, colorPtr);
  528.     Blt_LineWidthToPostScript(graphPtr, lineWidth);
  529.     Blt_LineDashesToPostScript(graphPtr, dashes);
  530. }
  531.  
  532. void
  533. Blt_RectangleToPostScript(graphPtr, x, y, width, height)
  534.     Graph *graphPtr;
  535.     int x, y;
  536.     unsigned int width, height;
  537. {
  538.     sprintf(graphPtr->scratchPtr, "%d %d %d %d Box Fill\n", x, y, width,
  539.     height);
  540.     Tcl_AppendResult(graphPtr->interp, graphPtr->scratchPtr, (char *)NULL);
  541. }
  542.  
  543. void
  544. Blt_LinesToPostScript(graphPtr, pointArr, numPoints)
  545.     Graph *graphPtr;
  546.     XPoint *pointArr;
  547.     int numPoints;
  548. {
  549.     register int i;
  550.  
  551.     sprintf(graphPtr->scratchPtr, "newpath %d %d moveto\n", pointArr[0].x,
  552.     pointArr[0].y);
  553.     Tcl_AppendResult(graphPtr->interp, graphPtr->scratchPtr, (char *)NULL);
  554.     for (i = 1; i < numPoints; i++) {
  555.     sprintf(graphPtr->scratchPtr, "%d %d lineto\n", pointArr[i].x,
  556.         pointArr[i].y);
  557.     Tcl_AppendResult(graphPtr->interp, graphPtr->scratchPtr, (char *)NULL);
  558.     }
  559. }
  560.  
  561. void
  562. Blt_PolygonToPostScript(graphPtr, pointArr, numPoints)
  563.     Graph *graphPtr;
  564.     XPoint *pointArr;
  565.     int numPoints;
  566. {
  567.     Blt_LinesToPostScript(graphPtr, pointArr, numPoints);
  568.     sprintf(graphPtr->scratchPtr, "%d %d lineto closepath Fill\n",
  569.     pointArr[0].x, pointArr[0].y);
  570.     Tcl_AppendResult(graphPtr->interp, graphPtr->scratchPtr, (char *)NULL);
  571. }
  572.  
  573. void
  574. Blt_SegmentsToPostScript(graphPtr, segArr, numSegments)
  575.     Graph *graphPtr;
  576.     XSegment *segArr;
  577.     int numSegments;
  578. {
  579.     register int i;
  580.  
  581.     for (i = 0; i < numSegments; i++) {
  582.     sprintf(graphPtr->scratchPtr, "%d %d %d %d DrawSegment\n",
  583.         segArr[i].x1, segArr[i].y1, segArr[i].x2, segArr[i].y2);
  584.     Tcl_AppendResult(graphPtr->interp, graphPtr->scratchPtr, (char *)NULL);
  585.     }
  586. }
  587.  
  588. void
  589. Blt_RectanglesToPostScript(graphPtr, rectArr, numRects)
  590.     Graph *graphPtr;
  591.     XRectangle rectArr[];
  592.     int numRects;
  593. {
  594.     register int i;
  595.  
  596.     for (i = 0; i < numRects; i++) {
  597.     Blt_RectangleToPostScript(graphPtr, rectArr[i].x, rectArr[i].y,
  598.         rectArr[i].width, rectArr[i].height);
  599.     }
  600. }
  601.  
  602. /*
  603.  * The Border structure used internally by the Tk_3D* routines.
  604.  * The following is a copy of it from tk3d.c.
  605.  */
  606. typedef struct {
  607.     Display *display;        /* Display for which the resources below are
  608.                                  * allocated. */
  609.     int refCount;        /* Number of different users of this border.*/
  610.     XColor *bgColorPtr;        /* Background color (intensity between
  611.                                  * lightColorPtr and darkColorPtr). */
  612.     XColor *lightColorPtr;    /* Color used for lighter areas of border
  613.                                  * (must free this when deleting structure).*/
  614.     XColor *darkColorPtr;    /* Color for darker areas (must free when
  615.                                  * deleting structure). */
  616.     Pixmap shadow;        /* Stipple pattern to use for drawing
  617.                                  * lighter-shadow-ed areas.  Only used on
  618.                                  * monochrome displays;  on color displays
  619.                                  * this is None. */
  620.     GC lightGC;            /* Used to draw lighter parts of the border.*/
  621.     GC darkGC;            /* Used to draw darker parts of the the
  622.                                  * border. */
  623.     GC bgGC;            /* Used (if necessary) to draw areas in the
  624.                                  * background color. */
  625.     Tcl_HashEntry *hashPtr;    /* Entry in borderTable (needed in order to
  626.                                  * delete structure). */
  627. } Border;
  628.  
  629.  
  630. void
  631. Blt_Print3DRectangle(graphPtr, border, x, y, width, height, borderWidth,
  632.     relief)
  633.     Graph *graphPtr;        /* File pointer to write PS output. */
  634.     Tk_3DBorder border;        /* Token for border to draw. */
  635.     int x, y;            /* Coordinates of rectangle */
  636.     unsigned int width, height;    /* Region to be drawn. */
  637.     int borderWidth;        /* Desired width for border, in pixels. */
  638.     int relief;            /* Should be either TK_RELIEF_RAISED or
  639.                                  * TK_RELIEF_SUNKEN;  indicates position of
  640.                                  * interior of window relative to exterior. */
  641. {
  642.     Border *borderPtr = (Border *) border;
  643.     XColor lightColor, darkColor;
  644.     XColor *lightColorPtr, *darkColorPtr;
  645.     XColor *top, *bottom;
  646.     XPoint points[7];
  647.     Tk_Window tkwin = graphPtr->tkwin;
  648.     int twiceWidth = (borderWidth * 2);
  649.  
  650.     if ((width < twiceWidth) || (height < twiceWidth)) {
  651.     return;
  652.     }
  653.     if ((borderPtr->lightColorPtr == NULL) ||
  654.     (borderPtr->darkColorPtr == NULL)) {
  655.     Screen *screenPtr;
  656.     Colormap colorMap;
  657.  
  658.     lightColor.pixel = borderPtr->bgColorPtr->pixel;
  659.     screenPtr = Tk_Screen(graphPtr->tkwin);
  660.     if (lightColor.pixel == WhitePixelOfScreen(screenPtr)) {
  661.         darkColor.pixel = BlackPixelOfScreen(screenPtr);
  662.     } else {
  663.         darkColor.pixel = WhitePixelOfScreen(screenPtr);
  664.     }
  665.     colorMap = Tk_Colormap(graphPtr->tkwin);
  666.     XQueryColor(graphPtr->display, colorMap, &lightColor);
  667.     lightColorPtr = &lightColor;
  668.     XQueryColor(Tk_Display(tkwin), colorMap, &darkColor);
  669.     darkColorPtr = &darkColor;
  670.     } else {
  671.     lightColorPtr = borderPtr->lightColorPtr;
  672.     darkColorPtr = borderPtr->darkColorPtr;
  673.     }
  674.  
  675.     /*
  676.      * Handle grooves and ridges with recursive calls.
  677.      */
  678.  
  679.     if ((relief == TK_RELIEF_GROOVE) || (relief == TK_RELIEF_RIDGE)) {
  680.     int halfWidth, insideOffset;
  681.  
  682.     halfWidth = borderWidth / 2;
  683.     insideOffset = borderWidth - halfWidth;
  684.     Blt_Print3DRectangle(graphPtr, border, x, y, width, height, halfWidth,
  685.         (relief == TK_RELIEF_GROOVE) ? TK_RELIEF_SUNKEN :
  686.         TK_RELIEF_RAISED);
  687.     Blt_Print3DRectangle(graphPtr, border, x + insideOffset,
  688.         y + insideOffset, width - insideOffset * 2,
  689.         height - insideOffset * 2, halfWidth,
  690.         (relief == TK_RELIEF_GROOVE) ? TK_RELIEF_RAISED :
  691.         TK_RELIEF_SUNKEN);
  692.     return;
  693.     }
  694.     if (relief == TK_RELIEF_RAISED) {
  695.     top = lightColorPtr;
  696.     bottom = darkColorPtr;
  697.     } else if (relief == TK_RELIEF_SUNKEN) {
  698.     top = darkColorPtr;
  699.     bottom = lightColorPtr;
  700.     } else {
  701.     top = bottom = borderPtr->bgColorPtr;
  702.     }
  703.     Blt_BackgroundToPostScript(graphPtr, bottom);
  704.     Blt_RectangleToPostScript(graphPtr, x, y + height - borderWidth,
  705.     width, (unsigned int)borderWidth);
  706.     Blt_RectangleToPostScript(graphPtr, x + width - borderWidth, y,
  707.     (unsigned int)borderWidth, (unsigned int)height);
  708.     points[0].x = points[1].x = points[6].x = x;
  709.     points[0].y = points[6].y = y + height;
  710.     points[1].y = points[2].y = y;
  711.     points[2].x = x + width;
  712.     points[3].x = x + width - borderWidth;
  713.     points[3].y = points[4].y = y + borderWidth;
  714.     points[4].x = points[5].x = x + borderWidth;
  715.     points[5].y = y + height - borderWidth;
  716.     if (relief != TK_RELIEF_FLAT) {
  717.     Blt_BackgroundToPostScript(graphPtr, top);
  718.     }
  719.     Blt_PolygonToPostScript(graphPtr, points, 7);
  720. }
  721.  
  722. void
  723. Blt_3DRectangleToPostScript(graphPtr, border, x, y, width, height,
  724.     borderWidth, relief)
  725.     Graph *graphPtr;
  726.     Tk_3DBorder border;        /* Token for border to draw. */
  727.     int x, y;            /* Coordinates of top-left of border area */
  728.     unsigned int width, height;    /* Dimension of border to be drawn. */
  729.     int borderWidth;        /* Desired width for border, in pixels. */
  730.     int relief;            /* Should be either TK_RELIEF_RAISED or
  731.                                  * TK_RELIEF_SUNKEN;  indicates position of
  732.                                  * interior of window relative to exterior. */
  733. {
  734.     Border *borderPtr = (Border *) border;
  735.  
  736.     /*
  737.      * I'm assuming that the rectangle is to be drawn as a background.
  738.      * Setting the pen color as a foreground or background, only affects
  739.      * the plot when the colormode option is "monochrome".
  740.      */
  741.     Blt_BackgroundToPostScript(graphPtr, borderPtr->bgColorPtr);
  742.     Blt_RectangleToPostScript(graphPtr, x, y, width, height);
  743.     Blt_Print3DRectangle(graphPtr, border, x, y, width, height, borderWidth,
  744.     relief);
  745. }
  746.  
  747. void
  748. Blt_StippleToPostScript(graphPtr, bitmap, width, height, fillOrStroke)
  749.     Graph *graphPtr;
  750.     Pixmap bitmap;
  751.     unsigned int width, height;
  752.     int fillOrStroke;
  753. {
  754.     sprintf(graphPtr->scratchPtr, "%d %d\n", width, height);
  755.     Tcl_AppendResult(graphPtr->interp, graphPtr->scratchPtr, (char *)NULL);
  756.     XBitmapToPostScript(graphPtr, bitmap, width, height);
  757.     Tcl_AppendResult(graphPtr->interp, (fillOrStroke) ? "true" : "false",
  758.     " StippleFill\n", (char *)NULL);
  759. }
  760.  
  761. void
  762. Blt_BitmapToPostScript(graphPtr, bitmap, centerX, centerY,
  763.     width, height, theta, bgColorPtr)
  764.     Graph *graphPtr;
  765.     Pixmap bitmap;        /* Bitmap to be converted to PostScript */
  766.     int centerX, centerY;    /* Bitmap's center coordinates */
  767.     unsigned int width, height;    /* Extents of bitmap */
  768.     double theta;        /* Degrees to rotate bitmap */
  769.     XColor *bgColorPtr;        /* Background color of bitmap: NULL indicates
  770.                  * no background color */
  771. {
  772.     if (bgColorPtr != NULL) {
  773.     Tcl_AppendResult(graphPtr->interp, "{ ", (char *)NULL);
  774.     XColorToPostScript(graphPtr, bgColorPtr, SET_BG_COLOR);
  775.     Tcl_AppendResult(graphPtr->interp, "} true ", (char *)NULL);
  776.     } else {
  777.     Tcl_AppendResult(graphPtr->interp, "false ", (char *)NULL);
  778.     }
  779.     sprintf(graphPtr->scratchPtr, "%d %d %d %d %g\n", centerX, centerY,
  780.     width, height, theta);
  781.     Tcl_AppendResult(graphPtr->interp, graphPtr->scratchPtr, (char *)NULL);
  782.     XBitmapToPostScript(graphPtr, bitmap, width, height);
  783.     Tcl_AppendResult(graphPtr->interp, " DrawBitmap\n", (char *)NULL);
  784. }
  785.  
  786. /*
  787.  * -----------------------------------------------------------------
  788.  *
  789.  * Blt_FontToPostScript --
  790.  *
  791.  *      Map the X font to a PostScript font and point size.
  792.  *
  793.  *    If a Tcl array variable was specified, each element should
  794.  *    be indexed by the X11 font name and contain a list of 1-2
  795.  *    elements; the PostScript font name and the desired point
  796.  *    size.  The point size may be omitted and the X font point
  797.  *    size will be used.
  798.  *
  799.  *    Otherwise, if the foundry is "Adobe", we try to do a pausible
  800.  *    mapping looking at the full name of the font and building
  801.  *    a string in the form of "Family-TypeFace".
  802.  *
  803.  * Returns:
  804.  *      None.
  805.  *
  806.  * Side Effects:
  807.  *      PostScript commands are output to change the type and the
  808.  *      point size of the current font.
  809.  *
  810.  * -----------------------------------------------------------------
  811.  */
  812. void
  813. Blt_FontToPostScript(graphPtr, fontPtr)
  814.     Graph *graphPtr;
  815.     XFontStruct *fontPtr;    /* X font to query about */
  816. {
  817.     PostScript *psPtr = (PostScript *)graphPtr->postscript;
  818.     unsigned long fontProp;
  819.     char *fontName;
  820.     int pointSize;
  821.  
  822.     fontName = "Helvetica-Bold";/* Default font */
  823.     pointSize = 120;        /* Default point size */
  824.     if (XGetFontProperty(fontPtr, XA_POINT_SIZE, &fontProp) != False) {
  825.     pointSize = (int)fontProp;
  826.     }
  827.     if (psPtr->fontVarName != NULL) {
  828.     char *fontInfo;
  829.  
  830.     fontInfo = Tcl_GetVar2(graphPtr->interp, psPtr->fontVarName,
  831.         Tk_NameOfFontStruct(fontPtr), 0);
  832.     if (fontInfo != NULL) {
  833.         int numProps;
  834.         char **propArr = NULL;
  835.  
  836.         if (Tcl_SplitList(graphPtr->interp, fontInfo, &numProps,
  837.             &propArr) == TCL_OK) {
  838.         fontName = propArr[0];
  839.         if (numProps == 2) {
  840.             Tcl_GetInt(graphPtr->interp, propArr[1], &pointSize);
  841.         }
  842.         }
  843.         sprintf(graphPtr->scratchPtr, "%g /%s SetFont\n",
  844.         (double)pointSize, fontName);
  845.         Tcl_AppendResult(graphPtr->interp, graphPtr->scratchPtr,
  846.         (char *)NULL);
  847.         if (propArr != (char **)NULL) {
  848.         free((char *)propArr);
  849.         }
  850.         return;
  851.     }
  852.     }
  853.     /*
  854.      * Otherwise, try to map by querying the X font for its properties
  855.      */
  856.     fontName = XFontStructToPostScript(graphPtr->tkwin, fontPtr);
  857.     if (fontName == NULL) {
  858.     fontName = "Helvetica-Bold";    /* Default font */
  859.     }
  860.     sprintf(graphPtr->scratchPtr, "%g /%s SetFont\n",
  861.     (double)pointSize / 10.0, fontName);
  862.     Tcl_AppendResult(graphPtr->interp, graphPtr->scratchPtr, (char *)NULL);
  863. }
  864.  
  865. /*
  866.  * -----------------------------------------------------------------
  867.  *
  868.  * Blt_TextToPostScript --
  869.  *
  870.  *      Output PostScript commands to print a text string. The string
  871.  *    may be rotated at any arbitrary angle, and placed according
  872.  *    the anchor type given. The anchor indicates how to interpret
  873.  *    the window coordinates as an anchor for the text bounding box.
  874.  *    If a background color is specified (i.e. bgColorPtr != NULL),
  875.  *    output a filled rectangle the size of the bounding box in the
  876.  *    given background color.
  877.  *
  878.  * Results:
  879.  *      None.
  880.  *
  881.  * Side Effects:
  882.  *      Text string is drawn using the given font and GC on the graph
  883.  *    window at the given coordinates, anchor, and rotation
  884.  * -----------------------------------------------------------------
  885.  */
  886. void
  887. Blt_TextToPostScript(graphPtr, text, attrPtr, x, y)
  888.     Graph *graphPtr;
  889.     char *text;            /* Text string to print */
  890.     TextAttributes *attrPtr;    /* Text attribute information: rotation,
  891.                  * font, background color, anchor */
  892.     int x, y;            /* Window coordinates of anchor */
  893. {
  894.     unsigned int width, height;    /* Width and height of unrotated text */
  895.     XPoint upperLeft;        /* Point in upperleft corner of rotated box */
  896.     double centerX, centerY;
  897.     unsigned int rotWidth, rotHeight;
  898.  
  899.     if ((text == NULL) || (*text == '\0')) {
  900.     return;
  901.     }
  902.     width = Blt_TextStringWidth(attrPtr->fontPtr, text);
  903.     height = attrPtr->fontPtr->ascent + attrPtr->fontPtr->descent;
  904.     Blt_GetBoundingBox(width, height, attrPtr->theta, &rotWidth, &rotHeight,
  905.     (XPoint *)NULL);
  906.     /*
  907.      * Find the center of the bounding box
  908.      */
  909.     upperLeft = Blt_TranslateBoxCoords(x, y, rotWidth, rotHeight,
  910.     attrPtr->anchor);
  911.     centerX = upperLeft.x + (rotWidth * 0.5);
  912.     centerY = upperLeft.y + (rotHeight * 0.5);
  913.  
  914.     Blt_FontToPostScript(graphPtr, attrPtr->fontPtr);
  915.     Blt_ForegroundToPostScript(graphPtr, attrPtr->fgColorPtr);
  916.  
  917.     if (attrPtr->bgColorPtr == (XColor *)NULL) {
  918.     Tcl_AppendResult(graphPtr->interp, "false ", (char *)NULL);
  919.     } else {
  920.     Tcl_AppendResult(graphPtr->interp, "{ ", (char *)NULL);
  921.     XColorToPostScript(graphPtr, attrPtr->bgColorPtr, SET_BG_COLOR);
  922.     Tcl_AppendResult(graphPtr->interp, "} true ", (char *)NULL);
  923.     }
  924.     sprintf(graphPtr->scratchPtr, "%g %g %d %d %d %g (%s) DrawText\n",
  925.     centerX, centerY, width, height, attrPtr->fontPtr->ascent,
  926.     attrPtr->theta, text);
  927.     Tcl_AppendResult(graphPtr->interp, graphPtr->scratchPtr, (char *)NULL);
  928. }
  929.  
  930. /*
  931.  * -----------------------------------------------------------------
  932.  *
  933.  * Blt_PrintLine --
  934.  *
  935.  *      Outputs PostScript commands to print a multi-segmented line.
  936.  *    It assumes a procedure DashesProc was previously defined.
  937.  *
  938.  * Results:
  939.  *      None.
  940.  *
  941.  * Side Effects:
  942.  *      Line is printed.
  943.  
  944.  * -----------------------------------------------------------------
  945.  */
  946.  
  947. void
  948. Blt_PrintLine(graphPtr, pointArr, numPoints)
  949.     Graph *graphPtr;
  950.     XPoint *pointArr;
  951.     int numPoints;
  952. {
  953.     register int i;
  954.  
  955.     if (numPoints <= 0) {
  956.     return;
  957.     }
  958.     sprintf(graphPtr->scratchPtr, "newpath %d %d moveto\n", pointArr[0].x,
  959.     pointArr[0].y);
  960.     Tcl_AppendResult(graphPtr->interp, graphPtr->scratchPtr, (char *)NULL);
  961.     for (i = 1; i < (numPoints - 1); i++) {
  962.     if (i % PSMAXPATH) {
  963.         sprintf(graphPtr->scratchPtr, "%d %d lineto\n",
  964.         pointArr[i].x, pointArr[i].y);
  965.     } else {
  966.         sprintf(graphPtr->scratchPtr,
  967.         "%d %d lineto\nDashesProc stroke\n\newpath %d %d moveto\n",
  968.         pointArr[i].x, pointArr[i].y, pointArr[i].x, pointArr[i].y);
  969.     }
  970.     Tcl_AppendResult(graphPtr->interp, graphPtr->scratchPtr, (char *)NULL);
  971.     }
  972.     /*
  973.      * Note: It's assumed that i is numPoints - 1 after finishing loop
  974.      */
  975.     sprintf(graphPtr->scratchPtr, "%d %d lineto\nDashesProc stroke\n",
  976.     pointArr[i].x, pointArr[i].y);
  977.     Tcl_AppendResult(graphPtr->interp, graphPtr->scratchPtr, (char *)NULL);
  978. }
  979.  
  980.  
  981.  
  982. static void
  983. DestroyPostScript(graphPtr)
  984.     Graph *graphPtr;
  985. {
  986.     Tk_FreeOptions(configSpecs, (char *)graphPtr->postscript,
  987.     graphPtr->display, 0);
  988.     free((char *)graphPtr->postscript);
  989. }
  990.  
  991. /*
  992.  *----------------------------------------------------------------------
  993.  *
  994.  * ConfigurePostScript --
  995.  *
  996.  *      This procedure is invoked to print the graph in a file.
  997.  *
  998.  * Results:
  999.  *      None.
  1000.  *
  1001.  * Side effects:
  1002.  *      A new PostScript file is created.
  1003.  *
  1004.  *----------------------------------------------------------------------
  1005.  */
  1006. static int
  1007. ConfigurePostScript(graphPtr, argc, argv)
  1008.     Graph *graphPtr;
  1009.     int argc;            /* Number of options in argv vector */
  1010.     char **argv;        /* Option vector */
  1011. {
  1012.     int flags = TK_CONFIG_ARGV_ONLY;
  1013.  
  1014.     if (argc == 2) {
  1015.     return (Tk_ConfigureInfo(graphPtr->interp, graphPtr->tkwin,
  1016.         configSpecs, (char *)graphPtr->postscript, (char *)NULL, flags));
  1017.     } else if (argc == 3) {
  1018.     return (Tk_ConfigureInfo(graphPtr->interp, graphPtr->tkwin,
  1019.         configSpecs, (char *)graphPtr->postscript, argv[2], flags));
  1020.     }
  1021.     if (Tk_ConfigureWidget(graphPtr->interp, graphPtr->tkwin, configSpecs,
  1022.         argc - 2, argv + 2, (char *)graphPtr->postscript, flags) != TCL_OK) {
  1023.     return TCL_ERROR;
  1024.     }
  1025.     return TCL_OK;
  1026. }
  1027.  
  1028. /*
  1029.  * --------------------------------------------------------------------------
  1030.  *
  1031.  * PrintPreamble
  1032.  *
  1033.  *        The PostScript preamble calculates the needed translation and scaling
  1034.  *        to make X11 coordinates compatible with PostScript.
  1035.  *
  1036.  *     +-----------------------+
  1037.  *     |        1" = 72pica    |   1" left, right, top, and bottom margin
  1038.  *     |  ------------------ur |   leaving a 6.5" x 9" area.
  1039.  *     |  |  O--->          |  |
  1040.  *     |  |  |              |  |
  1041.  *     |  |  | 6.5" = 468 pica |
  1042.  *     |  |  v              |  |
  1043.  *     |  |                 |  |                     468 pica
  1044.  *     |  |  9" = 648 pica  |  |   scaleX =  ---------------------------
  1045.  *     |  |                 |  |                  Width of window
  1046.  * 11" |  |                 |  |
  1047.  *     | ll------------------  |
  1048.  *     |     bounding box      |                     648 pica
  1049.  *     |                       |   scaleY =  ---------------------------
  1050.  *     |                       |                  Height of window
  1051.  *     |                       |
  1052.  *     |                       |  To retain the aspect ratio, we use only
  1053.  *     |                       |  the smaller of the two scales.  The Y scale
  1054.  *     |                       |  is negative since the X11 Y origin is at
  1055.  *     +-----------------------+  the top instead of the bottom.
  1056.  *              8.5"
  1057.  *
  1058.  *     Landscape:
  1059.  *        Rotate the picture by 90 degrees
  1060.  * ---------------------------------------------------------------------
  1061.  */
  1062.  
  1063. #ifdef TIME_WITH_SYS_TIME
  1064. #include <sys/time.h>
  1065. #include <time.h>
  1066. #else
  1067. #ifdef HAVE_SYS_TIME_H
  1068. #include <sys/time.h>
  1069. #else
  1070. #include <time.h>
  1071. #endif /* HAVE_SYS_TIME_H */
  1072. #endif /* TIME_WITH_SYS_TIME */
  1073.  
  1074. static int
  1075. PrintPreamble(graphPtr, fileName)
  1076.     Graph *graphPtr;
  1077.     char *fileName;
  1078. {
  1079.     PostScript *psPtr = (PostScript *)graphPtr->postscript;
  1080.     long date;
  1081.     char *version;
  1082.  
  1083. #ifdef NO_INLINE_PROLOG
  1084. #define STRING_LENGTH 400
  1085.     char string[STRING_LENGTH + 1];
  1086.     char *libDir;
  1087.     FILE *f;
  1088.  
  1089. #endif /* NO_INLINE_PROLOG */
  1090.     XPoint origin;
  1091.  
  1092.     version = Tcl_GetVar(graphPtr->interp, "tk_version", TCL_GLOBAL_ONLY);
  1093.     if (version == NULL) {
  1094.     version = "???";
  1095.     }
  1096.     if (fileName == NULL) {
  1097.     fileName = Tk_PathName(graphPtr->tkwin);
  1098.     }
  1099.     Tcl_AppendResult(graphPtr->interp,
  1100.     "%!PS-Adobe-3.0 EPSF-3.0\n%%Pages: 1\n",
  1101.     "%%Title: (", fileName, ")\n",
  1102.     "%%DocumentNeededResources: font Helvetica Courier\n",
  1103.     (char *)NULL);
  1104.  
  1105.     /* Find upper left coordinate of bounding box */
  1106.     origin = Blt_TranslateBoxCoords(psPtr->x, psPtr->y,
  1107.     (unsigned int)psPtr->width, (unsigned int)psPtr->height,
  1108.     psPtr->anchor);
  1109.     sprintf(graphPtr->scratchPtr, "%%%%BoundingBox:  %d %d %d %d\n",
  1110.     origin.x, 792 - (psPtr->height + origin.y),    /* Lower left */
  1111.     origin.x + psPtr->width, (792 - origin.y));
  1112.     date = time((time_t *) NULL);
  1113.     Tcl_AppendResult(graphPtr->interp, graphPtr->scratchPtr,
  1114.     "%%Creator: ", Tk_Class(graphPtr->tkwin),
  1115.     " (Tk version ", version, ")\n",
  1116.     "%%CreationDate: ", ctime(&date),
  1117.     "%%EndComments\n", (char *)NULL);
  1118. #ifndef NO_INLINE_PROLOG
  1119.     Tcl_AppendResult(graphPtr->interp, postScriptDefinitions, (char *)NULL);
  1120. #else
  1121.     /*
  1122.      * Read a standard prolog file from disk and insert it into
  1123.      * the PostScript.
  1124.      */
  1125.     libDir = Tcl_GetVar(graphPtr->interp, "blt_library", TCL_GLOBAL_ONLY);
  1126.     if (libDir == NULL) {
  1127.     Tcl_ResetResult(graphPtr->interp);
  1128.     Tcl_AppendResult(graphPtr->interp,
  1129.         "couldn't find BLT library directory:",
  1130.         " blt_library variable doesn't exist", (char *)NULL);
  1131.     return TCL_ERROR;
  1132.     }
  1133.     sprintf(string, "%.350s/bltGraph.pro", libDir);
  1134.     f = fopen(string, "r");
  1135.     if (f == NULL) {
  1136.     Tcl_ResetResult(graphPtr->interp);
  1137.     Tcl_AppendResult(graphPtr->interp, "couldn't open prolog file \"",
  1138.         string, "\": ", Tcl_PosixError(graphPtr->interp),
  1139.         (char *)NULL);
  1140.     return TCL_ERROR;
  1141.     }
  1142.     Tcl_AppendResult(graphPtr->interp, "\n% including file \"", string,
  1143.     "\"\n\n", (char *)NULL);
  1144.     while (fgets(graphPtr->scratchPtr, STRING_LENGTH, f) != NULL) {
  1145.     Tcl_AppendResult(graphPtr->interp, graphPtr->scratchPtr, (char *)NULL);
  1146.     }
  1147.     if (ferror(f)) {
  1148.     fclose(f);
  1149.     Tcl_ResetResult(graphPtr->interp);
  1150.     Tcl_AppendResult(graphPtr->interp, "error reading prolog file \"",
  1151.         string, "\": ", Tcl_PosixError(graphPtr->interp),
  1152.         (char *)NULL);
  1153.     return TCL_ERROR;
  1154.     }
  1155.     fclose(f);
  1156. #endif /* NO_INLINE_PROLOG */
  1157.     if (psPtr->landscape) {
  1158.     sprintf(graphPtr->scratchPtr,
  1159.         "/CL %d def\n%d %d translate\n-90 rotate\n%%%%EndSetup\n\n",
  1160.         psPtr->colorLevel, origin.x, origin.y + psPtr->height);
  1161.     } else {
  1162.     sprintf(graphPtr->scratchPtr,
  1163.         "/CL %d def\n%d %d translate\n%%%%EndSetup\n\n",
  1164.         psPtr->colorLevel, origin.x, origin.y);
  1165.     }
  1166.     Tcl_AppendResult(graphPtr->interp, graphPtr->scratchPtr, (char *)NULL);
  1167.     return TCL_OK;
  1168. }
  1169.  
  1170. /*
  1171.  * -----------------------------------------------------------------
  1172.  *
  1173.  * PrintTags --
  1174.  *
  1175.  * -----------------------------------------------------------------
  1176.  */
  1177. static void
  1178. PrintTags(graphPtr)
  1179.     Graph *graphPtr;
  1180. {
  1181.     Blt_ListEntry *entryPtr;
  1182.     register Tag *tagPtr;
  1183.  
  1184.     for (entryPtr = Blt_FirstListEntry(&(graphPtr->tagList)); entryPtr != NULL;
  1185.     entryPtr = Blt_NextListEntry(entryPtr)) {
  1186.     tagPtr = (Tag *)Blt_GetListValue(entryPtr);
  1187.     if ((tagPtr->printProc != NULL) &&
  1188.         ((tagPtr->elemId == (Tk_Uid) NULL) ||
  1189.         Blt_FindListEntry(&(graphPtr->elemList), tagPtr->elemId))) {
  1190.         Tcl_AppendResult(graphPtr->interp, "\n% ",
  1191.         (*tagPtr->typeProc) (tagPtr), " Tag\n\n",
  1192.         (char *)NULL);
  1193.         (*tagPtr->printProc) (graphPtr, tagPtr);
  1194.     }
  1195.     }
  1196. }
  1197.  
  1198. /*
  1199.  * -----------------------------------------------------------------
  1200.  *
  1201.  * PrintElements --
  1202.  *
  1203.  * -----------------------------------------------------------------
  1204.  */
  1205. static void
  1206. PrintElements(graphPtr)
  1207.     Graph *graphPtr;
  1208. {
  1209.     Blt_ListEntry *entryPtr;
  1210.     register Element *elemPtr;
  1211.  
  1212.     for (entryPtr = Blt_FirstListEntry(&(graphPtr->elemList));
  1213.     entryPtr != NULL; entryPtr = Blt_NextListEntry(entryPtr)) {
  1214.     elemPtr = (Element *)Blt_GetListValue(entryPtr);
  1215.     Tcl_AppendResult(graphPtr->interp, "\n% Element \"", elemPtr->id,
  1216.         "\"\n\n", (char *)NULL);
  1217.     (*elemPtr->printProc) (graphPtr, elemPtr, ELEM_NORMAL);
  1218.     }
  1219. }
  1220.  
  1221. static void
  1222. PrintActiveElements(graphPtr)
  1223.     Graph *graphPtr;
  1224. {
  1225.     Blt_ListEntry *entryPtr;
  1226.     register Element *elemPtr;
  1227.  
  1228.     for (entryPtr = Blt_FirstListEntry(&(graphPtr->elemList));
  1229.     entryPtr != NULL; entryPtr = Blt_NextListEntry(entryPtr)) {
  1230.     elemPtr = (Element *)Blt_GetListValue(entryPtr);
  1231.     if (elemPtr->flags & ACTIVE) {
  1232.         Tcl_AppendResult(graphPtr->interp, "\n% Active Element \"",
  1233.         elemPtr->id, "\"\n\n", (char *)NULL);
  1234.         (*elemPtr->printProc) (graphPtr, elemPtr, ELEM_ACTIVE);
  1235.     }
  1236.     }
  1237. }
  1238.  
  1239.  
  1240. static void
  1241. PrintExterior(graphPtr, regionPtr)
  1242.     Graph *graphPtr;
  1243.     XRectangle *regionPtr;    /* Interior plotting region */
  1244. {
  1245.     register int i;
  1246.     int x, y;
  1247.     XRectangle rectArr[4];
  1248.     TextAttributes textAttr;
  1249.     GraphAxis *axisPtr;
  1250.  
  1251.     rectArr[0].x = rectArr[0].y = rectArr[3].x = rectArr[1].x = 0;
  1252.     rectArr[0].width = rectArr[3].width = graphPtr->width;
  1253.     rectArr[0].height = regionPtr->y;
  1254.     rectArr[3].y = regionPtr->y + regionPtr->height;
  1255.     rectArr[3].height = graphPtr->height - rectArr[3].y;
  1256.     rectArr[2].y = rectArr[1].y = regionPtr->y;
  1257.     rectArr[1].width = regionPtr->x;
  1258.     rectArr[2].height = rectArr[1].height = regionPtr->height;
  1259.     rectArr[2].x = regionPtr->x + regionPtr->width;
  1260.     rectArr[2].width = graphPtr->width - rectArr[2].x;
  1261.  
  1262.     /* Clear the surrounding margins and clip the plotting surface */
  1263.     Blt_BackgroundToPostScript(graphPtr, Tk_3DBorderColor(graphPtr->border));
  1264.     for (i = 0; i < 4; i++) {
  1265.     Blt_RectangleToPostScript(graphPtr, rectArr[i].x,
  1266.         rectArr[i].y, rectArr[i].width, rectArr[i].height);
  1267.     }
  1268.     /* Interior 3D border */
  1269.     if ((graphPtr->plotRelief != TK_RELIEF_FLAT) && (graphPtr->plotBW > 0)) {
  1270.     Blt_Print3DRectangle(graphPtr, graphPtr->border, regionPtr->x,
  1271.         regionPtr->y, regionPtr->width, regionPtr->height,
  1272.         graphPtr->plotBW, graphPtr->plotRelief);
  1273.     }
  1274.     /* Print legend if using default location */
  1275.     if (graphPtr->legendPtr->useDefault) {
  1276.     (*graphPtr->legendPtr->printProc) (graphPtr);
  1277.     }
  1278.     textAttr.theta = 0.0;
  1279.     textAttr.anchor = TK_ANCHOR_CENTER;
  1280.     textAttr.fgColorPtr = graphPtr->marginFg;
  1281.     textAttr.bgColorPtr = (XColor *)NULL;
  1282.     textAttr.fontPtr = graphPtr->fontPtr;
  1283.  
  1284.     if (graphPtr->title != NULL) {
  1285.     x = (graphPtr->extreme.x + graphPtr->origin.x) / 2;
  1286.     y = graphPtr->borderWidth + TEXTHEIGHT(graphPtr->fontPtr);
  1287.     Blt_TextToPostScript(graphPtr, graphPtr->title, &textAttr, x, y);
  1288.     }
  1289.     for (i = 0; i < 4; i++) {    /* Print axes */
  1290.     axisPtr = graphPtr->axisArr[i];
  1291.     if (axisPtr->mapped) {
  1292.         (*axisPtr->printProc) (graphPtr, axisPtr, &textAttr);
  1293.     }
  1294.     }
  1295. }
  1296.  
  1297. /*
  1298.  *----------------------------------------------------------------------
  1299.  *
  1300.  * PrintPostScript --
  1301.  *
  1302.  *      This procedure is invoked to print the graph in a file.
  1303.  *
  1304.  * Results:
  1305.  *      None.
  1306.  *
  1307.  * Side effects:
  1308.  *      A new PostScript file is created.
  1309.  *
  1310.  *----------------------------------------------------------------------
  1311.  */
  1312. static int
  1313. PrintPostScript(graphPtr, argc, argv)
  1314.     Graph *graphPtr;        /* Graph widget record */
  1315.     int argc;            /* Number of options in argv vector */
  1316.     char **argv;        /* Option vector */
  1317. {
  1318.     PostScript *psPtr = (PostScript *)graphPtr->postscript;
  1319.     GraphLegend *legendPtr = graphPtr->legendPtr;
  1320.     XRectangle clipRect;
  1321.     int result = TCL_ERROR;
  1322.     char scratchSpace[BUFSIZ * 2];
  1323.     FILE *f = NULL;
  1324.     int twiceBW;
  1325.     char *fileName;        /* Name of file to write PostScript output
  1326.                                  * If NULL, output is returned via
  1327.                                  * interp->result. */
  1328.  
  1329.     fileName = NULL;
  1330.     if ((argc > 2) && (argv[2][0] != '-')) {
  1331.     f = fopen(argv[2], "w");
  1332.     if (f == NULL) {
  1333.         Tcl_AppendResult(graphPtr->interp, "can't create \"", argv[2],
  1334.         "\": ", Tcl_PosixError(graphPtr->interp), (char *)NULL);
  1335.         return TCL_ERROR;
  1336.     }
  1337.     fileName = argv[2];
  1338.     argv++, argc--;
  1339.     }
  1340.     graphPtr->scratchPtr = scratchSpace;
  1341.     if ((argc > 0) &&
  1342.     (Tk_ConfigureWidget(graphPtr->interp, graphPtr->tkwin, configSpecs,
  1343.         argc - 2, argv + 2, (char *)psPtr,
  1344.         TK_CONFIG_ARGV_ONLY) != TCL_OK)) {
  1345.     goto error;
  1346.     }
  1347.     /* Scale coordinates into printer points (pica) */
  1348.     psPtr->x = psPtr->xOffset * psPtr->pointScale;
  1349.     psPtr->y = psPtr->yOffset * psPtr->pointScale;
  1350.     psPtr->width = graphPtr->width;
  1351.     if (psPtr->pageWidth > 0) {
  1352.     psPtr->width = psPtr->pageWidth * psPtr->pointScale;
  1353.     }
  1354.     psPtr->height = graphPtr->height;
  1355.     if (psPtr->pageHeight > 0) {
  1356.     psPtr->height = psPtr->pageHeight * psPtr->pointScale;
  1357.     }
  1358.     if (psPtr->landscape) {
  1359.     graphPtr->width = psPtr->height;
  1360.     graphPtr->height = psPtr->width;
  1361.     } else {
  1362.     graphPtr->height = psPtr->height;
  1363.     graphPtr->width = psPtr->width;
  1364.     }
  1365.     if (Blt_ComputeLayout(graphPtr) != TCL_OK) {
  1366.     graphPtr->interp->result = "not enough space to print graph";
  1367.     goto error;
  1368.     }
  1369.     graphPtr->flags |= LAYOUT_ALL;
  1370.     Blt_LayoutGraph(graphPtr);
  1371.     Tcl_ResetResult(graphPtr->interp);
  1372.     if (PrintPreamble(graphPtr, fileName) != TCL_OK) {
  1373.     goto error;
  1374.     }
  1375.     twiceBW = (graphPtr->plotBW + graphPtr->plotBW);
  1376.     /*
  1377.      * Determine rectangle of the plotting surface the graph window
  1378.      */
  1379.     clipRect.x = graphPtr->origin.x - graphPtr->plotBW;
  1380.     clipRect.y = graphPtr->extreme.y - graphPtr->plotBW;
  1381.     clipRect.width = (graphPtr->extreme.x - graphPtr->origin.x) + twiceBW;
  1382.     clipRect.height = (graphPtr->origin.y - graphPtr->extreme.y) + twiceBW;
  1383.  
  1384.     Blt_FontToPostScript(graphPtr, graphPtr->fontPtr);
  1385.     Blt_BackgroundToPostScript(graphPtr, graphPtr->plotBg);
  1386.     Blt_RectangleToPostScript(graphPtr, clipRect.x, clipRect.y, clipRect.width,
  1387.     clipRect.height);
  1388.     Tcl_AppendResult(graphPtr->interp, "gsave clip\n\n", (char *)NULL);
  1389.     /*
  1390.      * Draw the elements and tags on the interior of the plotting surface
  1391.      */
  1392.     PrintElements(graphPtr);
  1393.     PrintTags(graphPtr);
  1394.     PrintActiveElements(graphPtr);
  1395.     if (!legendPtr->useDefault) {
  1396.     (*legendPtr->printProc) (graphPtr);
  1397.     }
  1398.     Tcl_AppendResult(graphPtr->interp, "\n% Unset clipping\ngrestore\n\n",
  1399.     (char *)NULL);
  1400.     PrintExterior(graphPtr, &clipRect);
  1401.     Tcl_AppendResult(graphPtr->interp,
  1402.     "showpage\n%Trailer\ngrestore\nend\n%EOF\n", (char *)NULL);
  1403.     /*
  1404.      * If a file name was given, write the results to that file
  1405.      */
  1406.     if (f != NULL) {
  1407.     fputs(graphPtr->interp->result, f);
  1408.     Tcl_ResetResult(graphPtr->interp);
  1409.     if (ferror(f)) {
  1410.         Tcl_AppendResult(graphPtr->interp, "error writing file \"",
  1411.         fileName, "\": ", Tcl_PosixError(graphPtr->interp),
  1412.         (char *)NULL);
  1413.         goto error;
  1414.     }
  1415.     }
  1416.     result = TCL_OK;
  1417.  
  1418.   error:
  1419.     if (f != NULL) {
  1420.     fclose(f);
  1421.     }
  1422.     /* Reset height and width of graph window */
  1423.     graphPtr->width = Tk_Width(graphPtr->tkwin);
  1424.     graphPtr->height = Tk_Height(graphPtr->tkwin);
  1425.     graphPtr->flags = LAYOUT_ALL;
  1426.     /*
  1427.      * Redraw the graph in order to re-calculate the layout as soon
  1428.      * as possible. This is in the case the crosshairs are active.
  1429.      */
  1430.     Blt_RedrawGraph(graphPtr);
  1431.     return (result);
  1432. }
  1433.  
  1434. /*
  1435.  *----------------------------------------------------------------------
  1436.  *
  1437.  * Blt_CreatePostScript --
  1438.  *
  1439.  *      Creates a postscript structure.
  1440.  *
  1441.  * Results:
  1442.  *      None.
  1443.  *
  1444.  * Side effects:
  1445.  *      A new PostScript structure is created.
  1446.  *
  1447.  *----------------------------------------------------------------------
  1448.  */
  1449. int
  1450. Blt_CreatePostScript(graphPtr)
  1451.     Graph *graphPtr;
  1452. {
  1453.     double pixelsPerInch;
  1454.     int screenNum;
  1455.     int screenWidth, screenWidthMM;
  1456.     PostScript *psPtr;
  1457.  
  1458.     psPtr = (PostScript *)calloc(1, sizeof(PostScript));
  1459.     if (psPtr == NULL) {
  1460.     graphPtr->interp->result = "can't allocate postscript structure";
  1461.     return TCL_ERROR;
  1462.     }
  1463.     psPtr->colorLevel = FULLCOLOR_LEVEL;
  1464.     psPtr->anchor = TK_ANCHOR_NW;    /* Upperleft anchor origin */
  1465.     psPtr->destroyProc = DestroyPostScript;
  1466.     psPtr->configProc = ConfigurePostScript;
  1467.     psPtr->printProc = PrintPostScript;
  1468.     screenNum = Tk_ScreenNumber(graphPtr->tkwin);
  1469.     screenWidth = DisplayWidth(graphPtr->display, screenNum);
  1470.     screenWidthMM = DisplayWidthMM(graphPtr->display, screenNum);
  1471. #define MM_PER_INCH 25.4
  1472.     pixelsPerInch = (MM_PER_INCH * (double)screenWidth / screenWidthMM);
  1473. #define PICA_PER_INCH 72.0
  1474.     /*
  1475.      * Set margins to one inch
  1476.      */
  1477.     psPtr->pointScale = PICA_PER_INCH / pixelsPerInch;
  1478.     psPtr->xOffset = psPtr->yOffset = (int)(pixelsPerInch + 0.5);
  1479.     graphPtr->postscript = (GraphPostScript *)psPtr;
  1480.     return TCL_OK;
  1481. }
  1482.