b8180d9f6d41dc708a2f249ba892cbca311e7a06
jcasper
  Mon Feb 27 11:38:55 2023 -0800
Adding transparency support for colors refs #30569

diff --git src/lib/psGfx.c src/lib/psGfx.c
index bb303bb..734c9a8 100644
--- src/lib/psGfx.c
+++ src/lib/psGfx.c
@@ -1,74 +1,96 @@
 /* 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(FILE *f, double width, double height)
+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;
+    }
+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;
@@ -83,57 +105,60 @@
 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;
 
-psWriteHeader(ps->f, ptWidth, ptHeight);
+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. */
@@ -325,30 +350,42 @@
 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);
@@ -493,24 +530,34 @@
 	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 } ;
-struct pipeline *pl = pipelineOpen1(pipeCmd, pipelineWrite, "/dev/null", NULL, 0);
+    pl = pipelineOpen1(pipeCmd, pipelineWrite, "/dev/null", NULL, 0);
+    }
 sysVal = pipelineWait(pl);
 if(sysVal != 0)
     freez(&pdfName);
 freez(&pdfTmpName);
 return pdfName;
 }