summaryrefslogtreecommitdiff
path: root/Build/source/texk/dvipsk/drawPS.c
diff options
context:
space:
mode:
Diffstat (limited to 'Build/source/texk/dvipsk/drawPS.c')
-rw-r--r--Build/source/texk/dvipsk/drawPS.c667
1 files changed, 667 insertions, 0 deletions
diff --git a/Build/source/texk/dvipsk/drawPS.c b/Build/source/texk/dvipsk/drawPS.c
new file mode 100644
index 00000000000..6ce17f6483e
--- /dev/null
+++ b/Build/source/texk/dvipsk/drawPS.c
@@ -0,0 +1,667 @@
+/*
+ * D R A W P S . C
+ *
+ * Changed to take magnification into account, 27 August 1990.
+ *
+ * (minor mods by don on 5 Jan 90 to accommodate highres PostScript)
+ *
+ * $Revision: 1.1 $
+ *
+ * $Log: drawPS.c,v $
+ * Revision 1.1 90/03/10 20:32:48 grunwald
+ * Initial revision
+ *
+ *
+ * 89/04/24: decouty@irisa.fr, priol@irisa.fr
+ * added three shading levels for fig,
+ * removed '\n' in cmdout() calls
+ *
+ * Revision 1.4 87/05/07 15:06:24 dorab
+ * relinked back hh to h and vv to v undoing a previous change.
+ * this was causing subtle quantization problems. the main change
+ * is in the definition of hconvPS and vconvPS.
+ *
+ * changed the handling of the UC seal. the PS file should now be
+ * sent via the -i option.
+ *
+ * Revision 1.3 86/04/29 23:20:55 dorab
+ * first general release
+ *
+ * Revision 1.3 86/04/29 22:59:21 dorab
+ * first general release
+ *
+ * Revision 1.2 86/04/29 13:23:40 dorab
+ * Added distinctive RCS header
+ *
+ */
+#ifndef lint
+char RCSid[] =
+ "@(#)$Header: /usr/local/src/TeX/Dvips-5.0.2/RCS/drawPS.c,v 1.1 90/03/10 20:32:48 grunwald Exp $ (UCLA)";
+#endif
+
+/*
+ the driver for handling the \special commands put out by
+ the tpic program as modified by Tim Morgan <morgan@uci.edu>
+ the co-ordinate system is with the origin at the top left
+ and the x-axis is to the right, and the y-axis is to the bottom.
+ when these routines are called, the origin is set at the last dvi position,
+ which has to be gotten from the dvi-driver (in this case, dvips) and will
+ have to be converted to device co-ordinates (in this case, by [hv]convPS).
+ all dimensions are given in milli-inches and must be converted to what
+ dvips has set up (i.e. there are convRESOLUTION units per inch).
+
+ it handles the following \special commands
+ pn n set pen size to n
+ pa x y add path segment to (x,y)
+ fp flush path
+ ip flush invisible path, i.e. do shading only
+ da l flush dashed - each dash is l (inches)
+ dt l flush dotted - one dot per l (inches)
+ sp [l] flush spline - optional l is dot/dash length
+ ar x y xr yr sa ea arc centered at (x,y) with x-radius xr
+ and y-radius yr, start angle sa (in rads),
+ end angle ea (in rads)
+ ia x y xr yr sa ea invisible arc with same parameters as above
+ sh [s] shade last path (box, circle, ellipse)
+ s = gray level (0 = white, 1 = black)
+ wh whiten last path (box, circle, ellipse)
+ bk blacken last path (box,circle, ellipse)
+ tx texture command - Added by T. Priol
+ (priol@irisa.fr), enhanced by M. Jourdan:
+ textures are translated into uniform gray levels
+
+ this code is in the public domain
+
+ written by dorab patel <dorab@cs.ucla.edu>
+ december 1985
+ released feb 1986
+ changes for dvips july 1987
+
+ */
+
+#include "dvips.h"
+
+#ifdef TPIC /* rest of the file !! */
+
+#include <math.h> /* used in function "arc" */
+
+/*
+ * The external declarations:
+ */
+#include "protos.h"
+
+#define convRESOLUTION DPI
+#define convVRESOLUTION VDPI
+#define tpicRESOLUTION 1000 /* number of tpic units per inch */
+#ifndef TRUE
+#define TRUE 1
+#define FALSE 0
+#endif
+
+/* convert from tpic units to PS units */
+#define PixRound(a,b) zPixRound((a),(b))
+#define convPS(x) PixRound((x), dir ? convVRESOLUTION : convRESOLUTION)
+#define convVPS(x) PixRound((x), dir ? convRESOLUTION : convVRESOLUTION)
+#define ConvPS(x) PixRound((x), convRESOLUTION)
+#define ConvVPS(x) PixRound((x), convVRESOLUTION)
+
+/* convert from tpic locn to abs horiz PS locn */
+#define hconvPS(x) dir ? (vv + ConvVPS(x)) : (hh + ConvPS(x))
+/* convert from tpic locn to abs vert PS locn */
+#define vconvPS(x) dir ? (-hh + ConvPS(x)) : (vv + ConvVPS(x))
+/* convert to degrees */
+#define convDeg(x) (360*(x)/(2*3.14159265358979))
+
+/* if PostScript had splines, i wouldnt need to store the path */
+#define MAXPATHS 6000 /* maximum number of path segments */
+#define NONE 0 /* for shading */
+#define BLACK 1 /* for shading */
+#define GRAY 2 /* MJ; for shading */
+#define WHITE 3 /* for shading */
+
+/* the following defines are used to make the PostScript shorter;
+ the corresponding defines are in the PostScript prolog special.lpro */
+#define MOVETO "a"
+#define LINETO "li"
+#define RCURVETO "rc"
+#define RLINETO "rl"
+#define STROKE "st"
+#define FILL "fil"
+#define NEWPATH "np"
+#define CLOSEPATH "closepath"
+/*
+ * STROKE and FILL must restore the current point to that
+ * saved by NEWPATH
+ */
+
+static double xx[MAXPATHS], yy[MAXPATHS]; /* the current path in milli-inches */
+static integer pathLen = 0; /* the current path length */
+static integer shading = NONE; /* what to shade the last figure */
+static integer penSize = 2; /* pen size in PS units */
+
+/* forward declarations */
+/* static void doShading(); */
+static double zPixRound(double, double); /* (x/y)PixRound(x,y) */
+static double shadetp = 0.5;
+ /* shading level, initialized as requested by tpic 2.0 -- MJ */
+
+void
+setPenSize(char *cp)
+{
+ long ps;
+
+ if (sscanf(cp, " %ld ", &ps) != 1)
+ {
+ error("Illegal .ps command format");
+ return;
+ }
+
+ penSize = convPS(ps);
+ doubleout((integer)penSize);
+ cmdout("setlinewidth");
+} /* end setPenSize */
+
+void
+addPath(char *cp)
+{
+ double x,y;
+
+ if (++pathLen >= MAXPATHS) error("! Too many points");
+ if (sscanf(cp, " %lg %lg ", &x, &y) != 2)
+ error("! Malformed path expression");
+ xx[pathLen] = x;
+ yy[pathLen] = y;
+} /* end of addPath */
+
+void
+arc(char *cp, int invis)
+{
+ double xc, yc, xrad, yrad;
+ double startAngle, endAngle;
+
+ if (sscanf(cp, " %lg %lg %lg %lg %lg %lg ", &xc, &yc, &xrad, &yrad,
+ &startAngle, &endAngle) != 6)
+ {
+ error("Illegal arc specification");
+ return;
+ }
+
+/* To have filled ellipses/circles also have borders, we duplicate the object
+ specification; first time we emit a FILL command, second time we emit a
+ STROKE command.
+ There certainly exists a better way to do that in PostScript, by
+ remembering the object specification (path) across the fill command.
+ However I (MJ) am completely unproficient at PostScript...
+*/
+
+/* we need the newpath since STROKE doesnt do a newpath */
+
+ if (shading) {
+ /* first time for shading */
+ cmdout(NEWPATH);
+ doubleout(hconvPS(xc));
+ doubleout(vconvPS(yc));
+ doubleout(convPS(xrad));
+ if (xrad != yrad || VDPI != DPI)
+ doubleout(convVPS(yrad));
+ doubleout(convDeg(startAngle));
+ doubleout(convDeg(endAngle));
+
+ if (xrad == yrad && VDPI == DPI) /* for arcs and circles */
+ cmdout("arc");
+ else
+ cmdout("ellipse");
+
+ cmdout(FILL);
+ shading = NONE;
+ cmdout("0 setgray"); /* default of black */
+ }
+
+ if (!invis) {
+ /* There is a problem with tpic 2.0's handling of dotted ellipses.
+ In most (all?) cases, after conversion to degrees and rounding
+ to integers, startAngle and endAngle become the same, so nothing,
+ not even a dot (as requested), is printed in the output (at least
+ on my Apple LaserWriter).
+ So I (MJ) singled out this case.
+ */
+ double degStAng = convDeg(startAngle),
+ degEnAng = convDeg(endAngle);
+
+ cmdout(NEWPATH); /* save current point */
+
+ if (degStAng != degEnAng) { /* normal case */
+ doubleout(hconvPS(xc));
+ doubleout(vconvPS(yc));
+ doubleout(convPS(xrad));
+ if (xrad != yrad || VDPI != DPI)
+ doubleout(convVPS(yrad));
+ doubleout(degStAng);
+ doubleout(degEnAng);
+
+ if (xrad == yrad && VDPI == DPI) /* for arcs and circles */
+ cmdout("arc");
+ else
+ cmdout("ellipse");
+ }
+ else { /* draw a single dot */
+ double xdot, ydot;
+ xdot = ((double)xc + (double)xrad * cos ((startAngle + endAngle) / 2.0));
+ ydot = ((double)yc + (double)yrad * sin ((startAngle + endAngle) / 2.0));
+ doubleout(hconvPS(xdot));
+ doubleout(vconvPS(ydot));
+ cmdout(MOVETO);
+ /* +1 to make sure the dot is printed */
+ doubleout(hconvPS(xdot)+1);
+ doubleout(vconvPS(ydot)+1);
+ cmdout(LINETO);
+ }
+
+ cmdout(STROKE);
+ }
+} /* end of arc */
+
+/*
+ * In pic dashed lines are created in a manner which makes boths ends
+ * of a line solid.
+ * This means that the corner of a dotted box is a dot and the corner
+ * of a dashed box is solid.
+ * The number of inches/dash must be adjusted accordingly.
+ */
+static void
+flushDashedPath(int dotted, double inchesPerDash)
+{
+ register int i;
+ int nipd = (integer) convPS((int) inchesPerDash);
+
+ if (nipd == 0)
+ nipd = 1;
+ if (pathLen < 2)
+ {
+ error("Path less than 2 points - ignored");
+ pathLen = 0;
+ return;
+ }
+
+ cmdout(NEWPATH); /* to save the current point */
+ for (i=2; i <= pathLen; i++) {
+ integer dx = hconvPS(xx[i-1]) - hconvPS(xx[i]);
+ integer dy = vconvPS(yy[i-1]) - vconvPS(yy[i]);
+ /* Cast to double before multiplying; otherwise, can result in
+ integer overflow and thus a negative result (eepic-nan.test). */
+ double delta = sqrt(((double) dx) * dx + ((double) dy) * dy);
+ double ipd;
+ int ndashes;
+
+ /*
+ * If a line is dashed there must be an odd number of dashes.
+ * This makes the first and last dash of the line solid.
+ * If a line is dotted the length of a dot + space sequence must be
+ * chosen such that (n+1) * dotlength + n space length = linelength
+ * => (dot + space) = (line - dot) / n
+ */
+ if (dotted)
+ delta -= penSize;
+ ndashes = (integer) (delta / nipd + 0.5); /* number of dashes on line */
+ /* only odd when dashed, choose odd number with smallest deviation */
+ if (ndashes > 2 && (ndashes & 0x1) == 0 && !dotted) {
+ if (fabs(nipd - delta/(ndashes - 1)) <
+ (fabs(nipd - delta/(ndashes + 1))))
+ ndashes -= 1;
+ else
+ ndashes += 1;
+ } else if (ndashes < 1)
+ ndashes = 1;
+ ipd = delta / ndashes;
+ if (ipd <= 0.0)
+ ipd = 1.0;
+ cmdout("[");
+ if (dotted) {
+ doubleout(penSize);
+ doubleout(fabs(ipd - penSize));
+ } else /* if dashed */
+ doubleout(ipd);
+
+ cmdout("]");
+ doubleout(0);
+ cmdout("setdash");
+ doubleout(hconvPS(xx[i-1]));
+ doubleout(vconvPS(yy[i-1]));
+ cmdout(MOVETO);
+ doubleout(hconvPS(xx[i]));
+ doubleout(vconvPS(yy[i]));
+ cmdout(LINETO);
+ cmdout(STROKE);
+ }
+ cmdout("[] 0 setdash");
+ pathLen = 0;
+}
+
+void
+flushPath(int invis)
+{
+ register int i;
+
+ if (pathLen < 2)
+ {
+ error("Path less than 2 points - ignored");
+ pathLen = 0;
+ return;
+ }
+
+/* To have filled boxes also have borders, we duplicate the object
+ specification; first time we emit a FILL command, second time we emit a
+ STROKE command.
+ There certainly exists a better way to do that in PostScript, by
+ remembering the object specification (path) across the fill command.
+ However I (MJ) am completely unproficient at PostScript...
+*/
+
+#ifdef DEBUG
+ if (dd(D_SPECIAL))
+ fprintf(stderr,
+#ifdef SHORTINT
+ "flushpath(1): hh=%ld, vv=%ld, x=%lg, y=%lg, xPS=%lg, yPS=%lg\n",
+#else /* ~SHORTINT */
+ "flushpath(1): hh=%d, vv=%d, x=%lg, y=%lg, xPS=%lg, yPS=%lg\n",
+#endif /* ~SHORTINT */
+ hh, vv, xx[1], yy[1], hconvPS(xx[1]), vconvPS(yy[1]));
+#endif /* DEBUG */
+ if (shading) {
+ /* first time for shading */
+ cmdout(NEWPATH); /* to save the current point */
+ doubleout(hconvPS(xx[1]));
+ doubleout(vconvPS(yy[1]));
+ cmdout(MOVETO);
+ for (i=2; i < pathLen; i++) {
+#ifdef DEBUG
+ if (dd(D_SPECIAL))
+ fprintf(stderr,
+#ifdef SHORTINT
+ "flushpath(%ld): hh=%ld, vv=%ld, x=%lg, y=%lg, xPS=%lg, yPS=%lg\n",
+#else /* ~SHORTINT */
+ "flushpath(%d): hh=%d, vv=%d, x=%lg, y=%lg, xPS=%lg, yPS=%lg\n",
+#endif /* ~SHORTINT */
+ i, hh, vv, xx[i], yy[i], hconvPS(xx[i]), vconvPS(yy[i]));
+#endif /* DEBUG */
+ doubleout(hconvPS(xx[i]));
+ doubleout(vconvPS(yy[i]));
+ cmdout(LINETO);
+ }
+ /* Shading should always apply to a closed path
+ force it and complain if necessary */
+ if (xx[1] == xx[pathLen] && yy[1] == yy[pathLen])
+ cmdout(CLOSEPATH);
+ else {
+ doubleout(hconvPS(xx[pathLen]));
+ doubleout(vconvPS(yy[pathLen]));
+ cmdout(LINETO);
+ cmdout(CLOSEPATH);
+ error("Attempt to fill a non-closed path");
+ fprintf(stderr,
+#ifdef SHORTINT
+ "\tfirst point: x=%lg, y=%lg; last point: x=%lg, y=%lg\n",
+#else /* ~SHORTINT */
+ "\tfirst point: x=%lg, y=%lg; last point: x=%lg, y=%lg\n",
+#endif /* ~SHORTINT */
+ xx[1], yy[1], xx[pathLen], yy[pathLen]);
+
+ }
+ cmdout(FILL);
+ shading = NONE;
+ cmdout("0 setgray"); /* default of black */
+ }
+
+ if (!invis) {
+ cmdout(NEWPATH); /* to save the current point */
+ doubleout(hconvPS(xx[1]));
+ doubleout(vconvPS(yy[1]));
+ cmdout(MOVETO);
+ for (i=2; i < pathLen; i++) {
+ doubleout(hconvPS(xx[i]));
+ doubleout(vconvPS(yy[i]));
+ cmdout(LINETO);
+ }
+ if (xx[1] == xx[pathLen] && yy[1] == yy[pathLen])
+ cmdout(CLOSEPATH);
+ else {
+ doubleout(hconvPS(xx[pathLen]));
+ doubleout(vconvPS(yy[pathLen]));
+ cmdout(LINETO);
+ }
+ cmdout(STROKE);
+ }
+
+ pathLen = 0;
+} /* end of flushPath */
+
+void
+flushDashed(char *cp, int dotted)
+{
+ double inchesPerDash;
+ int savelen = pathLen;
+
+ if (sscanf(cp, " %lg ", &inchesPerDash) != 1)
+ {
+ error ("Illegal format for dotted/dashed line");
+ return;
+ }
+
+ if (inchesPerDash <= 0.0)
+ {
+ error ("Length of dash/dot cannot be negative");
+ return;
+ }
+
+ inchesPerDash = 1000 * inchesPerDash; /* to get milli-inches */
+ flushPath(1);/* print filled, always invisible bounds */
+ pathLen = savelen;
+ flushDashedPath(dotted,inchesPerDash);
+
+ cmdout("[] 0 setdash");
+} /* end of flushDashed */
+
+void
+flushSpline(char *cp)
+{ /* as exact as psdit!!! */
+ register long i;
+ register double px, py, dx, dy;
+
+ if (*cp) {
+ double inchesPerDash;
+ int ipd;
+
+ if (sscanf(cp, "%lg ", &inchesPerDash) != 1) {
+ error ("Illegal format for dotted/dashed spline");
+ return;
+ }
+
+ ipd = (int)(1000.0 * inchesPerDash); /* to get milli-inches */
+
+ if (ipd != 0) {
+ cmdout("[");
+ if (inchesPerDash < 0.0) /* dotted */ {
+ doubleout(penSize);
+ doubleout(fabs(convPS((int)-ipd) - penSize));
+ } else /* dashed */
+ doubleout(convPS((int)ipd));
+
+ cmdout("]");
+ doubleout(0);
+ cmdout("setdash");
+ }
+ }
+
+ if (pathLen < 2)
+ {
+ error("Spline less than two points - ignored");
+ return;
+ }
+
+ cmdout(NEWPATH); /* to save the current point */
+ doubleout(hconvPS(xx[1]));
+ doubleout(vconvPS(yy[1]));
+ cmdout(MOVETO);
+ px = convPS(xx[1]);
+ py = convVPS(yy[1]);
+ doubleout(dx = convPS((xx[2]+xx[1])/2) - px);
+ doubleout(dy = convVPS((yy[2]+yy[1])/2) - py);
+ cmdout(RLINETO);
+
+ for (i=2; i < pathLen; i++)
+ {
+ px += dx;
+ py += dy;
+ doubleout(convPS((xx[i-1]+5*xx[i])/6) - px);
+ doubleout(convVPS((yy[i-1]+5*yy[i])/6) - py);
+ doubleout(convPS((5*xx[i]+xx[i+1])/6) - px);
+ doubleout(convVPS((5*yy[i]+yy[i+1])/6) - py);
+ doubleout(dx = convPS((xx[i]+xx[i+1])/2) - px);
+ doubleout(dy = convVPS((yy[i]+yy[i+1])/2) - py);
+ cmdout(RCURVETO);
+ }
+
+ doubleout(hconvPS(xx[pathLen]));
+ doubleout(vconvPS(yy[pathLen]));
+ cmdout(LINETO);
+
+/* doShading(); */
+ cmdout(STROKE); /* assume no shaded splines */
+ pathLen = 0;
+
+ if (*cp)
+ cmdout("[] 0 setdash");
+
+} /* end of flushSpline */
+
+/* set shading level (used with Fig 1.4-TFX). priol@irisa.fr, 89/04 */
+/* A better trial at setting shading level -- jourdan@minos.inria.fr */
+/* Count number of black bits in the pattern together with total number, */
+/* compute the average and use this as the PostScript gray level */
+void
+SetShade(register char *cp)
+{
+ int blackbits = 0, totalbits = 0;
+
+ while (*cp) {
+ switch (*cp) {
+ case '0':
+ totalbits += 4;
+ break;
+ case '1':
+ case '2':
+ case '4':
+ case '8':
+ blackbits += 1;
+ totalbits += 4;
+ break;
+ case '3':
+ case '5':
+ case '6':
+ case '9':
+ case 'a':
+ case 'A':
+ case 'c':
+ case 'C':
+ blackbits += 2;
+ totalbits += 4;
+ break;
+ case '7':
+ case 'b':
+ case 'B':
+ case 'd':
+ case 'D':
+ case 'e':
+ case 'E':
+ blackbits += 3;
+ totalbits += 4;
+ break;
+ case 'f':
+ case 'F':
+ blackbits += 4;
+ totalbits += 4;
+ break;
+ case ' ':
+ break;
+ default:
+ error("Invalid character in .tx pattern");
+ break;
+ }
+ cp++;
+ }
+ shadetp = 1.0 - ((double) blackbits / (double) totalbits);
+ shading = GRAY;
+} /* end of SetShade */
+
+void
+shadeLast(char *cp)
+{
+ char tpout[100];
+
+ if (*cp) {
+ double tempShadetp;
+
+ if (sscanf(cp, "%lg ", &tempShadetp) != 1)
+ error ("Illegal format for shade level");
+ else if (tempShadetp < 0.0 || tempShadetp > 1.0)
+ error ("Invalid shade level");
+ else
+ /* if "sh" has an argument we can safely assume that tpic 2.0 is used
+ so that all subsequent "sh" commands will come with an explicit
+ argument. Hence we may overwrite shadetp's old value */
+ /* Also note the inversion of gray levels for tpic 2.0 (0 = white,
+ 1 = black) w.r.t. PostScript (0 = black, 1 = white) */
+ shadetp = 1.0 - tempShadetp;
+ }
+
+ shading = GRAY;
+ snprintf(tpout, sizeof(tpout), "%1.3f setgray", shadetp);
+ /* priol@irisa.fr, MJ */
+ cmdout(tpout);
+} /* end of shadeLast */
+
+void
+whitenLast(void)
+{
+ shading = WHITE;
+ cmdout("1 setgray");
+} /* end of whitenLast */
+
+void
+blackenLast(void)
+{
+ shading = BLACK;
+ cmdout("0 setgray"); /* actually this aint needed */
+} /* end of whitenLast */
+
+#if 0
+/* Not used */
+static void
+doShading(void)
+{
+ if (shading)
+ {
+ cmdout(FILL);
+ shading = NONE;
+ cmdout("0 setgray"); !* default of black *!
+ }
+ else
+ cmdout(STROKE);
+} !* end of doShading *!
+#endif
+
+/*
+ * We need to calculate (x * convDPI * mag) / (tpicResolution * 1000)
+ * So we use doubleing point. (This should have a very small impact
+ * on the speed of the program.)
+ */
+static double
+zPixRound(register double x, /* in DVI units */
+ register double convDPI /* dots per inch */
+ ) /* return rounded number of pixels */
+{
+ return ((x * mag * (double)convDPI /
+ (1000.0 * tpicRESOLUTION)));
+}
+
+#endif /* TPIC */