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; }