f947acce83460c48e4412a8c501decb67a9587cd
jcasper
  Thu Apr 6 00:04:19 2023 -0700
Good idea to close the pipeline when you're done with it (thanks Hiram), no ticket

diff --git src/lib/psGfx.c src/lib/psGfx.c
index 734c9a8..062ffb6 100644
--- src/lib/psGfx.c
+++ src/lib/psGfx.c
@@ -1,563 +1,564 @@
 /* PostScript graphics - 
  * This provides a bit of a shell around writing graphics to
  * a postScript file.  Perhaps the most important thing it
  * does is convert 0,0 from being at the bottom left to
  * being at the top left. */
 
 /* Copyright (C) 2011 The Regents of the University of California 
  * See kent/LICENSE or http://genome.ucsc.edu/license/ for licensing information. */
 
 #include "common.h"
 #include "psPoly.h"
 #include "psGfx.h"
 #include "linefile.h"
 #include "pipeline.h"
 
 static void psFloatOut(FILE *f, double x)
 /* Write out a floating point number, but not in too much
  * precision. */
 {
 int i = round(x);
 if (i == x)
    fprintf(f, "%d ", i);
 else
    fprintf(f, "%0.4f ", x);
 }
 
 void psClipRect(struct psGfx *ps, double x, double y, 
 	double width, double height)
 /* Set clipping rectangle. */
 {
 FILE *f = ps->f;
 fprintf(f, "cliprestore ");
 psXyOut(ps, x, y+height); 
 psWhOut(ps, width, height);       
 fprintf(f, "rectclip\n");
 }
 
 static void psWriteHeader(struct psGfx *ps, double width, double height)
 /* Write postScript header.  It's encapsulated PostScript
  * actually, so you need to supply image width and height
  * in points. */
 {
 FILE *f = ps->f;
 char *s =
 #include "common.pss"
 ;
 
 fprintf(f, "%%!PS-Adobe-3.1 EPSF-3.0\n");
 fprintf(f, "%%%%BoundingBox: 0 0 %d %d\n", (int)ceil(width), (int)ceil(height));
 fprintf(f, "%%%%LanguageLevel: 3\n\n");
 if (ps->newTransOps)
     fprintf(f, "0 .pushpdf14devicefilter\n");
 fprintf(f, "%s", s);
 }
 
 void psSetLineWidth(struct psGfx *ps, double factor)
 /* Set line width to factor * a single pixel width. */
 {
 fprintf(ps->f, "%f setlinewidth\n", factor * ps->xScale);
 }
 
 boolean supportNewTrans(void)
 /* Check the version of GS and return whether it supports the newer transparency operators */
 {
 boolean support = FALSE;
 char *versionCmd[] = { "gs", "--version", NULL };
 struct pipeline *pl = pipelineOpen1(versionCmd, pipelineRead, "/dev/null", NULL, 0);
 if (pl)
     {
     struct lineFile *lf = pipelineLineFile(pl);
     char *versionString;
     lineFileNext(lf, &versionString, NULL);
     char *minorVersion = NULL;
     if (strchr(versionString, '.'))
         minorVersion = strchr(versionString, '.') + 1;
     if ((atoi(versionString) > 9) || (atoi(versionString) == 9 && minorVersion && atoi(minorVersion) > 51))
         support = TRUE;
+    pipelineClose(&pl);
     }
 return support;
 }
 
 struct psGfx *psOpen(char *fileName, 
 	double userWidth, double userHeight, /* Dimension of image in user's units. */
 	double ptWidth, double ptHeight,     /* Dimension of image in points. */
 	double ptMargin)                     /* Image margin in points. */
 /* Open up a new postscript file.  If ptHeight is 0, it will be
  * calculated to keep pixels square. */
 {
 struct psGfx *ps;
 
 /* Allocate structure and open file. */
 AllocVar(ps);
 ps->f = mustOpen(fileName, "w");
 
 /* Save page dimensions and calculate scaling factors. */
 ps->userWidth = userWidth;
 ps->userHeight = userHeight;
 ps->ptWidth = ptWidth;
 ps->xScale = (ptWidth - 2*ptMargin)/userWidth;
 if (ptHeight != 0.0)
    {
    ps->ptHeight = ptHeight;
    ps->yScale = (ptHeight - 2*ptMargin) / userHeight;
    }
 else
    {
    ps->yScale = ps->xScale;
    ptHeight = ps->ptHeight = userHeight * ps->yScale + 2*ptMargin;
    }
 /* 0.5, 0.5 is the center of the pixel in upper-left corner which corresponds to (0,0) */
 ps->xOff = ptMargin;
 ps->yOff = ptMargin;
 ps->fontHeight = 10;
 
 /* Cope with fact y coordinates are bottom to top rather
  * than top to bottom. */
 ps->yScale = -ps->yScale;
 ps->yOff = ps->ptHeight - ps->yOff;
 
 ps->newTransOps = supportNewTrans();
 psWriteHeader(ps, ptWidth, ptHeight);
 
 /* adding a gsave here fixes an old ghostview bug with cliprestore */
 fprintf(ps->f, "gsave\n");
 
 /* Set initial clipping rectangle. */
 psClipRect(ps, 0, 0, ps->userWidth, ps->userHeight);
 
 /* Set line width to a single pixel. */
 psSetLineWidth(ps,1);
 
 return ps;
 }
 
 void psTranslate(struct psGfx *ps, double xTrans, double yTrans)
 /* add a constant to translate all coordinates */
 {
 ps->xOff += xTrans*ps->xScale;   
 ps->yOff += yTrans*ps->yScale;
 }
 
 void psClose(struct psGfx **pPs)
 /* Close out postScript file. */
 {
 struct psGfx *ps = *pPs;
 if (ps != NULL)
     {
     if (ps->newTransOps)
         fprintf(ps->f, ".poppdf14devicefilter\n");
     carefulClose(&ps->f);
     freez(pPs);
     }
 }
 
 void psXyOut(struct psGfx *ps, double x, double y)
 /* Output x,y position transformed into PostScript space. */
 {
 FILE *f = ps->f;
 psFloatOut(f, x * ps->xScale + ps->xOff);
 psFloatOut(f, y * ps->yScale + ps->yOff);
 }
 
 void psWhOut(struct psGfx *ps, double width, double height)
 /* Output width/height transformed into PostScript space. */
 {
 FILE *f = ps->f;
 psFloatOut(f, width * ps->xScale);
 psFloatOut(f, height * -ps->yScale);
 }
 
 void psMoveTo(struct psGfx *ps, double x, double y)
 /* Move PostScript position to given point. */
 {
 psXyOut(ps, x, y);
 fprintf(ps->f, "moveto\n");
 }
 
 void psLineTo(struct psGfx *ps, double x, double y)
 /* Draw line from current point to given point,
  * and make given point new current point. */
 {
 psXyOut(ps, x, y);
 fprintf(ps->f, "lineto\n");
 }
 
 void psDrawBox(struct psGfx *ps, double x, double y, 
 	double width, double height)
 /* Draw a filled box in current color. */
 {
 if (width > 0 && height > 0)
     {
     psWhOut(ps, width, height);
     psXyOut(ps, x, y+height); 
     fprintf(ps->f, "fillBox\n");
     }
 }
 
 void psDrawLine(struct psGfx *ps, double x1, double y1, double x2, double y2)
 /* Draw a line from x1/y1 to x2/y2 */
 {
 FILE *f = ps->f;
 fprintf(f, "newpath\n");
 psMoveTo(ps, x1, y1);
 psXyOut(ps, x2, y2);
 fprintf(ps->f, "lineto\n");
 fprintf(f, "stroke\n");
 }
 
 void psFillUnder(struct psGfx *ps, double x1, double y1, 
 	double x2, double y2, double bottom)
 /* Draw a 4 sided filled figure that has line x1/y1 to x2/y2 at
  * it's top, a horizontal line at bottom at it's bottom,  and
  * vertical lines from the bottom to y1 on the left and bottom to
  * y2 on the right. */
 {
 FILE *f = ps->f;
 fprintf(f, "newpath\n");
 psMoveTo(ps, x1, y1);
 psLineTo(ps, x2, y2);
 psLineTo(ps, x2, bottom);
 psLineTo(ps, x1, bottom);
 fprintf(f, "closepath\n");
 fprintf(f, "fill\n");
 }
 
 void psSetFont(struct psGfx *ps, char *fontName, double size)
 {
 FILE *f = ps->f;
 fprintf(f, "/%s findfont ", fontName);
 
 /* Note the 1.2 and the 1.0 below seem to get it to 
  * position about where the stuff developed for pixel
  * based systems expects it.  It is all a kludge though! */
 fprintf(f, "%f scalefont setfont\n", -size*ps->yScale*1.2);
 ps->fontHeight = size*0.8;
 }
 
 void psTimesFont(struct psGfx *ps, double size)
 /* Set font to times of a certain size. */
 {
 FILE *f = ps->f;
 fprintf(f, "/Helvetica findfont ");
 
 /* Note the 1.2 and the 1.0 below seem to get it to 
  * position about where the stuff developed for pixel
  * based systems expects it.  It is all a kludge though! */
 fprintf(f, "%f scalefont setfont\n", -size*ps->yScale*1.2);
 ps->fontHeight = size*0.8;
 }
 
 void psTextOutEscaped(struct psGfx *ps, char *text)
 /* Output post-script-escaped text. 
  * Notice that ps uses escaping similar to C itself.*/
 {
 char c;
 while ((c = *text++) != 0)
     {
     if (c == '(')
         fprintf(ps->f, "\\(");  // left-paren
     else if (c == ')')
         fprintf(ps->f, "\\)");  // right-paren
     else if (c == '\\')
         fprintf(ps->f, "\\\\"); // back-slash
     else if (c == '\n')
         fprintf(ps->f, "\\n");  // new-line
     else if (c == '\r')
         fprintf(ps->f, "\\r");  // carriage-return
     else if (c == '\t')
         fprintf(ps->f, "\\t");  // tab
     else if (c == '\b')
         fprintf(ps->f, "\\b");  // back-space
     else if (c == '\f')
         fprintf(ps->f, "\\f");  // form-feed
     else
 	fprintf(ps->f, "%c", c);
     }
 }
 
 void psTextBox(struct psGfx *ps, double x, double y, char *text)
 /* Output text in current font at given position. */
 {
 psMoveTo(ps, x, y + ps->fontHeight);
 fprintf(ps->f, "(");
 psTextOutEscaped(ps, text);
 fprintf(ps->f, ") fillTextBox\n");
 }
 
 void psTextAt(struct psGfx *ps, double x, double y, char *text)
 /* Output text in current font at given position. */
 {
 psMoveTo(ps, x, y + ps->fontHeight);
 fprintf(ps->f, "(");
 psTextOutEscaped(ps, text);
 fprintf(ps->f, ") show\n");
 }
 
 void psTextRight(struct psGfx *ps, double x, double y, 
 	double width, double height, 
 	char *text)
 /* Draw a line of text right justified in box defined by x/y/width/height */
 {
 y += (height - ps->fontHeight)/2;
 psMoveTo(ps, x+width, y + ps->fontHeight);
 fprintf(ps->f, "(");
 psTextOutEscaped(ps, text);
 fprintf(ps->f, ") showBefore\n");
 }
 
 void psTextInBox(struct psGfx *ps, double x, double y, 
 	double width, double height, 
 	char *text)
 /* Draw a line of text that fills a box. */
 {
 if (height == 0)
     return;
 if (height > 0)
     {
     psMoveTo(ps, x, y + height);
     fprintf(ps->f, "gsave\n");
     psSetFont(ps, "Courier", 1.0);
     fprintf(ps->f, "%g %g scale\n", width,height);
     fprintf(ps->f, "(");
     psTextOutEscaped(ps, text);
     fprintf(ps->f, ") show\n");
     fprintf(ps->f, "grestore\n");
     }
 else
     {
     //height = -height;
     psMoveTo(ps, x, y);
     fprintf(ps->f, "gsave\n");
     psSetFont(ps, "Courier", 1.0);
     fprintf(ps->f, "%g %g scale\n", width,height);
     fprintf(ps->f, "(");
     psTextOutEscaped(ps, text);
     fprintf(ps->f, ") show\n");
     fprintf(ps->f, "grestore\n");
     }
 }
 
 void psTextCentered(struct psGfx *ps, double x, double y, 
 	double width, double height, 
 	char *text)
 /* Draw a line of text centered in box defined by x/y/width/height */
 {
 y += (height - ps->fontHeight)/2;
 psMoveTo(ps, x+width/2, y + ps->fontHeight);
 fprintf(ps->f, "(");
 psTextOutEscaped(ps, text);
 fprintf(ps->f, ") showMiddle\n");
 }
 
 void psTextDown(struct psGfx *ps, double x, double y, char *text)
 /* Output text going downwards rather than across at position. */
 {
 psMoveTo(ps, x, y);
 fprintf(ps->f, "gsave\n");
 fprintf(ps->f, "-90 rotate\n");
 fprintf(ps->f, "(");
 psTextOutEscaped(ps, text);
 fprintf(ps->f, ") show\n");
 fprintf(ps->f, "grestore\n");
 }
 
 void psSetColorAlpha(struct psGfx *ps, int a)
 /* Sets alpha in the output if transparency is enabled, otherwise does nothing */
 {
 FILE *f = ps->f;
 double scale = 1.0/255;
 if (ps->newTransOps)
     {
     fprintf(f, "%f .setfillconstantalpha\n", scale * a);
     fprintf(f, "%f .setstrokeconstantalpha\n", scale * a);
     }
 }
 
 void psSetColor(struct psGfx *ps, int r, int g, int b)
 /* Set current color. */
 {
 FILE *f = ps->f;
 double scale = 1.0/255;
 if (r == g && g == b)
     {
     psFloatOut(f, scale * r);
     fprintf(f, "setgray\n");
     }
 else
     {
     psFloatOut(f, scale * r);
     psFloatOut(f, scale * g);
     psFloatOut(f, scale * b);
     fprintf(f, "setrgbcolor\n");
     }
 }
 
 void psSetGray(struct psGfx *ps, double grayVal)
 /* Set gray value. */
 {
 FILE *f = ps->f;
 if (grayVal < 0) grayVal = 0;
 if (grayVal > 1) grayVal = 1;
 psFloatOut(f, grayVal);
 fprintf(f, "setgray\n");
 }
 
 void psPushG(struct psGfx *ps)
 /* Save graphics state on stack. */
 {
 fprintf(ps->f, "gsave\n");
 }
 
 void psPopG(struct psGfx *ps)
 /* Pop off saved graphics state. */
 {
 fprintf(ps->f, "grestore\n");
 }
 
 void psDrawPoly(struct psGfx *ps, struct psPoly *poly, boolean filled)
 /* Draw a possibly filled polygon */
 {
 FILE *f = ps->f;
 struct psPoint *p = poly->ptList;
 fprintf(f, "newpath\n");
 psMoveTo(ps, p->x, p->y);
 for (;;)
     {
     p = p->next;
     psLineTo(ps, p->x, p->y);
     if (p == poly->ptList)
 	break;
     }
 if (filled)
     {
     fprintf(f, "fill\n");
     }
 else
     {
     fprintf(f, "closepath\n");
     fprintf(f, "stroke\n");
     }
 }
 
 void psCircle(struct psGfx *ps, double x, double y, double rad, boolean filled)
 {
 FILE *f = ps->f;
 fprintf(f, "newpath\n");
 psXyOut(ps, x, y);
 psFloatOut(f, rad * ps->xScale);
 psFloatOut(f, 0.0);
 psFloatOut(f, 360.0);
 fprintf(f, "arc\n");
 fprintf(f, "closepath\n");
 if (filled)
     fprintf(f, "fill\n");
 else
     fprintf(f, "stroke\n");
 }
 
 void psFillEllipse(struct psGfx *ps, double x, double y, double xrad, double yrad)
 {
 FILE *f = ps->f;
 fprintf(f, "newpath\n");
 psXyOut(ps, x, y);
 psWhOut(ps, xrad, yrad);
 psFloatOut(f, 0.0);
 psFloatOut(f, 360.0);
 fprintf(f, "ellipse\n");
 fprintf(f, "closepath\n");
 fprintf(f, "fill\n");
 }
 
 void psDrawEllipse(struct psGfx *ps, double x, double y, double xrad, double yrad,
                                         double startAngle, double endAngle)
 /* Draw ellipse.  Args are center point x and y, horizontal radius, vertical radius,
         start and end angles (e.g. 0 and 180 to draw top half, 180 and 360 for bottom)
  */
 {
 FILE *f = ps->f;
 fprintf(f, "newpath\n");
 psXyOut(ps, x, y);
 psWhOut(ps, xrad, yrad);
 psFloatOut(f, startAngle);
 psFloatOut(f, endAngle);
 fprintf(f, "ellipse\n");
 fprintf(f, "stroke\n");
 }
 
 void psDrawCurve(struct psGfx *ps, double x1, double y1, double x2, double y2,
                                         double x3, double y3, double x4, double y4)
 /* Draw Bezier curve specified by 4 points: first (p1) and last (p4)
  *      and 2 control points (p2, p3) */
 {
 FILE *f = ps->f;
 psXyOut(ps, x1, y1);
 fprintf(f, "moveto\n");
 psXyOut(ps, x2, y2);
 psXyOut(ps, x3, y3);
 psXyOut(ps, x4, y4);
 fprintf(f, "curveto\n");
 fprintf(f, "stroke\n");
 }
 
 void psSetDash(struct psGfx *ps, boolean on)
 /* Set dashed line mode on or off. If set on, show two points marked, with one point of space */
 {
 FILE *f = ps->f;
 if (on)
     fprintf(f, "[2 1] 0 setdash\n");
 else
     fprintf(f, "[] 0 setdash\n");
 }
 
 char * convertEpsToPdf(char *epsFile) 
 /* Convert EPS to PDF and return filename, or NULL if failure. */
 {
 char *pdfTmpName = NULL, *pdfName=NULL;
 int sysVal = 0;
 struct lineFile *lf = NULL;
 char *line;
 int lineSize=0;
 float width=0, height=0;
 pdfTmpName = cloneString(epsFile);
 
 /* Get the dimensions of bounding box. */
 lf = lineFileOpen(epsFile, TRUE);
 while(lineFileNext(lf, &line, &lineSize)) 
     {
     if(strstr( line, "BoundingBox:")) 
 	{
 	char *words[5];
 	chopLine(line, words);
 	width = atof(words[3]);
 	height = atof(words[4]);
 	break;
 	}
     }
 lineFileClose(&lf);
 	
 /* Do conversion. */
 chopSuffix(pdfTmpName);
 pdfName = addSuffix(pdfTmpName, ".pdf");
 char widthBuff[1024], heightBuff[1024];
 safef(widthBuff, sizeof widthBuff, "-dDEVICEWIDTHPOINTS=%d", round(width));
 safef(heightBuff, sizeof heightBuff, "-dDEVICEHEIGHTPOINTS=%d", round(height));
 struct pipeline *pl = NULL;
 boolean newTrans = supportNewTrans();
 if (newTrans)
     {
     char *pipeCmd[] = { "ps2pdf", "-dALLOWPSTRANSPARENCY", widthBuff, heightBuff, epsFile, pdfName, NULL } ;
     pl = pipelineOpen1(pipeCmd, pipelineWrite, "/dev/null", NULL, 0);
     }
 else
     {
     char *pipeCmd[] = { "ps2pdf", widthBuff, heightBuff, epsFile, pdfName, NULL } ;
     pl = pipelineOpen1(pipeCmd, pipelineWrite, "/dev/null", NULL, 0);
     }
 sysVal = pipelineWait(pl);
 if(sysVal != 0)
     freez(&pdfName);
 freez(&pdfTmpName);
 return pdfName;
 }