xref: /plan9/sys/src/cmd/postscript/tr2post/draw.c (revision 456a8764e4ea95d7aa2c2cf34e5112293070bc84)
1219b2ee8SDavid du Colombier #include <u.h>
2219b2ee8SDavid du Colombier #include <libc.h>
3219b2ee8SDavid du Colombier #include <bio.h>
480ee5cbfSDavid du Colombier #include <ctype.h>
5219b2ee8SDavid du Colombier #include "../common/common.h"
6219b2ee8SDavid du Colombier #include "tr2post.h"
7219b2ee8SDavid du Colombier 
8219b2ee8SDavid du Colombier BOOLEAN drawflag = FALSE;
980ee5cbfSDavid du Colombier BOOLEAN	inpath = FALSE;		/* TRUE if we're putting pieces together */
10219b2ee8SDavid du Colombier 
11219b2ee8SDavid du Colombier void
cover(double x,double y)12219b2ee8SDavid du Colombier cover(double x, double y) {
13*456a8764SDavid du Colombier 	USED(x, y);
14219b2ee8SDavid du Colombier }
15219b2ee8SDavid du Colombier 
16219b2ee8SDavid du Colombier void
drawspline(Biobufhdr * Bp,int flag)17219b2ee8SDavid du Colombier drawspline(Biobufhdr *Bp, int flag) {	/* flag!=1 connect end points */
18219b2ee8SDavid du Colombier 	int x[100], y[100];
19219b2ee8SDavid du Colombier 	int i, N;
20219b2ee8SDavid du Colombier /*
21219b2ee8SDavid du Colombier  * Spline drawing routine for Postscript printers. The complicated stuff is
22219b2ee8SDavid du Colombier  * handled by procedure Ds, which should be defined in the library file. I've
23219b2ee8SDavid du Colombier  * seen wrong implementations of troff's spline drawing, so fo the record I'll
24219b2ee8SDavid du Colombier  * write down the parametric equations and the necessary conversions to Bezier
25219b2ee8SDavid du Colombier  * cubic splines (as used in Postscript).
26219b2ee8SDavid du Colombier  *
27219b2ee8SDavid du Colombier  * Parametric equation (x coordinate only):
28219b2ee8SDavid du Colombier  *
29219b2ee8SDavid du Colombier  *	    (x2 - 2 * x1 + x0)    2                    (x0 + x1)
30219b2ee8SDavid du Colombier  *	x = ------------------ * t   + (x1 - x0) * t + ---------
31219b2ee8SDavid du Colombier  *		    2					   2
32219b2ee8SDavid du Colombier  *
33219b2ee8SDavid du Colombier  * The coefficients in the Bezier cubic are,
34219b2ee8SDavid du Colombier  *
35219b2ee8SDavid du Colombier  *	A = 0
36219b2ee8SDavid du Colombier  *	B = (x2 - 2 * x1 + x0) / 2
37219b2ee8SDavid du Colombier  *	C = x1 - x0
38219b2ee8SDavid du Colombier  *
39219b2ee8SDavid du Colombier  * while the current point is,
40219b2ee8SDavid du Colombier  *
41219b2ee8SDavid du Colombier  *	current-point = (x0 + x1) / 2
42219b2ee8SDavid du Colombier  *
43219b2ee8SDavid du Colombier  * Using the relationships given in the Postscript manual (page 121) it's easy to
44219b2ee8SDavid du Colombier  * see that the control points are given by,
45219b2ee8SDavid du Colombier  *
46219b2ee8SDavid du Colombier  *	x0' = (x0 + 5 * x1) / 6
47219b2ee8SDavid du Colombier  *	x1' = (x2 + 5 * x1) / 6
48219b2ee8SDavid du Colombier  *	x2' = (x1 + x2) / 2
49219b2ee8SDavid du Colombier  *
50219b2ee8SDavid du Colombier  * where the primed variables are the ones used by curveto. The calculations
51219b2ee8SDavid du Colombier  * shown above are done in procedure Ds using the coordinates set up in both
52219b2ee8SDavid du Colombier  * the x[] and y[] arrays.
53219b2ee8SDavid du Colombier  *
54219b2ee8SDavid du Colombier  * A simple test of whether your spline drawing is correct would be to use cip
55219b2ee8SDavid du Colombier  * to draw a spline and some tangent lines at appropriate points and then print
56219b2ee8SDavid du Colombier  * the file.
57219b2ee8SDavid du Colombier  */
58219b2ee8SDavid du Colombier 	for (N=2; N<sizeof(x)/sizeof(x[0]); N++)
59219b2ee8SDavid du Colombier 		if (Bgetfield(Bp, 'd', &x[N], 0)<=0 || Bgetfield(Bp, 'd', &y[N], 0)<=0)
60219b2ee8SDavid du Colombier 			break;
61219b2ee8SDavid du Colombier 
62219b2ee8SDavid du Colombier 	x[0] = x[1] = hpos;
63219b2ee8SDavid du Colombier 	y[0] = y[1] = vpos;
64219b2ee8SDavid du Colombier 
65219b2ee8SDavid du Colombier 	for (i = 1; i < N; i++) {
66219b2ee8SDavid du Colombier 		x[i+1] += x[i];
67219b2ee8SDavid du Colombier 		y[i+1] += y[i];
68219b2ee8SDavid du Colombier 	}
69219b2ee8SDavid du Colombier 
70219b2ee8SDavid du Colombier 	x[N] = x[N-1];
71219b2ee8SDavid du Colombier 	y[N] = y[N-1];
72219b2ee8SDavid du Colombier 
73219b2ee8SDavid du Colombier 	for (i = ((flag!=1)?0:1); i < ((flag!=1)?N-1:N-2); i++) {
74219b2ee8SDavid du Colombier 		endstring();
75219b2ee8SDavid du Colombier 		if (pageon())
76219b2ee8SDavid du Colombier 			Bprint(Bstdout, "%d %d %d %d %d %d Ds\n", x[i], y[i], x[i+1], y[i+1], x[i+2], y[i+2]);
77219b2ee8SDavid du Colombier /*		if (dobbox == TRUE) {		/* could be better */
78219b2ee8SDavid du Colombier /*	    		cover((double)(x[i] + x[i+1])/2,(double)-(y[i] + y[i+1])/2);
79219b2ee8SDavid du Colombier /*	    		cover((double)x[i+1], (double)-y[i+1]);
80219b2ee8SDavid du Colombier /*	    		cover((double)(x[i+1] + x[i+2])/2, (double)-(y[i+1] + y[i+2])/2);
81219b2ee8SDavid du Colombier /*		}
82219b2ee8SDavid du Colombier  */
83219b2ee8SDavid du Colombier 	}
84219b2ee8SDavid du Colombier 
85219b2ee8SDavid du Colombier 	hpos = x[N];			/* where troff expects to be */
86219b2ee8SDavid du Colombier 	vpos = y[N];
87219b2ee8SDavid du Colombier }
88219b2ee8SDavid du Colombier 
89219b2ee8SDavid du Colombier void
draw(Biobufhdr * Bp)90219b2ee8SDavid du Colombier draw(Biobufhdr *Bp) {
91219b2ee8SDavid du Colombier 
92219b2ee8SDavid du Colombier 	int r, x1, y1, x2, y2, i;
93219b2ee8SDavid du Colombier 	int d1, d2;
94219b2ee8SDavid du Colombier 
95219b2ee8SDavid du Colombier 	drawflag = TRUE;
96219b2ee8SDavid du Colombier 	r = Bgetrune(Bp);
97219b2ee8SDavid du Colombier 	switch(r) {
98219b2ee8SDavid du Colombier 	case 'l':
99219b2ee8SDavid du Colombier 		if (Bgetfield(Bp, 'd', &x1, 0)<=0 || Bgetfield(Bp, 'd', &y1, 0)<=0 || Bgetfield(Bp, 'r', &i, 0)<=0)
100219b2ee8SDavid du Colombier 			error(FATAL, "draw line function, destination coordinates not found.\n");
101219b2ee8SDavid du Colombier 
102219b2ee8SDavid du Colombier 		endstring();
103219b2ee8SDavid du Colombier 		if (pageon())
104219b2ee8SDavid du Colombier 			Bprint(Bstdout, "%d %d %d %d Dl\n", hpos, vpos, hpos+x1, vpos+y1);
105219b2ee8SDavid du Colombier 		hpos += x1;
106219b2ee8SDavid du Colombier 		vpos += y1;
107219b2ee8SDavid du Colombier 		break;
108219b2ee8SDavid du Colombier 	case 'c':
109219b2ee8SDavid du Colombier 		if (Bgetfield(Bp, 'd', &d1, 0)<=0)
110219b2ee8SDavid du Colombier 			error(FATAL, "draw circle function, diameter coordinates not found.\n");
111219b2ee8SDavid du Colombier 
112219b2ee8SDavid du Colombier 		endstring();
113219b2ee8SDavid du Colombier 		if (pageon())
114219b2ee8SDavid du Colombier 			Bprint(Bstdout, "%d %d %d %d De\n", hpos, vpos, d1, d1);
115219b2ee8SDavid du Colombier 		hpos += d1;
116219b2ee8SDavid du Colombier 		break;
117219b2ee8SDavid du Colombier 	case 'e':
118219b2ee8SDavid du Colombier 		if (Bgetfield(Bp, 'd', &d1, 0)<=0 || Bgetfield(Bp, 'd', &d2, 0)<=0)
119219b2ee8SDavid du Colombier 			error(FATAL, "draw ellipse function, diameter coordinates not found.\n");
120219b2ee8SDavid du Colombier 
121219b2ee8SDavid du Colombier 		endstring();
122219b2ee8SDavid du Colombier 		if (pageon())
123219b2ee8SDavid du Colombier 			Bprint(Bstdout, "%d %d %d %d De\n", hpos, vpos, d1, d2);
124219b2ee8SDavid du Colombier 		hpos += d1;
125219b2ee8SDavid du Colombier 		break;
126219b2ee8SDavid du Colombier 	case 'a':
127219b2ee8SDavid du Colombier 		if (Bgetfield(Bp, 'd', &x1, 0)<=0 || Bgetfield(Bp, 'd', &y1, 0)<=0 || Bgetfield(Bp, 'd', &x2, 0)<=0 || Bgetfield(Bp, 'd', &y2, 0)<=0)
128219b2ee8SDavid du Colombier 			error(FATAL, "draw arc function, coordinates not found.\n");
129219b2ee8SDavid du Colombier 
130219b2ee8SDavid du Colombier 		endstring();
131219b2ee8SDavid du Colombier 		if (pageon())
132219b2ee8SDavid du Colombier 			Bprint(Bstdout, "%d %d %d %d %d %d Da\n", hpos, vpos, x1, y1, x2, y2);
133219b2ee8SDavid du Colombier 		hpos += x1 + x2;
134219b2ee8SDavid du Colombier 		vpos += y1 + y2;
135219b2ee8SDavid du Colombier 		break;
136219b2ee8SDavid du Colombier 	case 'q':
137219b2ee8SDavid du Colombier 		drawspline(Bp, 1);
138219b2ee8SDavid du Colombier 		break;
139219b2ee8SDavid du Colombier 	case '~':
140219b2ee8SDavid du Colombier 		drawspline(Bp, 2);
141219b2ee8SDavid du Colombier 		break;
142219b2ee8SDavid du Colombier 	default:
143219b2ee8SDavid du Colombier 		error(FATAL, "unknown draw function <%c>\n", r);
144219b2ee8SDavid du Colombier 		break;
145219b2ee8SDavid du Colombier 	}
146219b2ee8SDavid du Colombier }
1477dd7cddfSDavid du Colombier 
1487dd7cddfSDavid du Colombier void
beginpath(char * buf,int copy)14980ee5cbfSDavid du Colombier beginpath(char *buf, int copy) {
1507dd7cddfSDavid du Colombier 
1517dd7cddfSDavid du Colombier /*
1527dd7cddfSDavid du Colombier  * Called from devcntrl() whenever an "x X BeginPath" command is read. It's used
1537dd7cddfSDavid du Colombier  * to mark the start of a sequence of drawing commands that should be grouped
1547dd7cddfSDavid du Colombier  * together and treated as a single path. By default the drawing procedures in
1557dd7cddfSDavid du Colombier  * *drawfile treat each drawing command as a separate object, and usually start
1567dd7cddfSDavid du Colombier  * with a newpath (just as a precaution) and end with a stroke. The newpath and
1577dd7cddfSDavid du Colombier  * stroke isolate individual drawing commands and make it impossible to deal with
1587dd7cddfSDavid du Colombier  * composite objects. "x X BeginPath" can be used to mark the start of drawing
1597dd7cddfSDavid du Colombier  * commands that should be grouped together and treated as a single object, and
1607dd7cddfSDavid du Colombier  * part of what's done here ensures that the PostScript drawing commands defined
1617dd7cddfSDavid du Colombier  * in *drawfile skip the newpath and stroke, until after the next "x X DrawPath"
1627dd7cddfSDavid du Colombier  * command. At that point the path that's been built up can be manipulated in
1637dd7cddfSDavid du Colombier  * various ways (eg. filled and/or stroked with a different line width).
1647dd7cddfSDavid du Colombier  *
1657dd7cddfSDavid du Colombier  * Color selection is one of the options that's available in parsebuf(),
1667dd7cddfSDavid du Colombier  * so if we get here we add *colorfile to the output file before doing
1677dd7cddfSDavid du Colombier  * anything important.
1687dd7cddfSDavid du Colombier  *
1697dd7cddfSDavid du Colombier  */
1707dd7cddfSDavid du Colombier 	if (inpath == FALSE) {
1717dd7cddfSDavid du Colombier 		endstring();
17280ee5cbfSDavid du Colombier 	/*	getdraw();	*/
17380ee5cbfSDavid du Colombier 	/*	getcolor(); */
1747dd7cddfSDavid du Colombier 		Bprint(Bstdout, "gsave\n");
1757dd7cddfSDavid du Colombier 		Bprint(Bstdout, "newpath\n");
1767dd7cddfSDavid du Colombier 		Bprint(Bstdout, "%d %d m\n", hpos, vpos);
1777dd7cddfSDavid du Colombier 		Bprint(Bstdout, "/inpath true def\n");
1787dd7cddfSDavid du Colombier 		if ( copy == TRUE )
17980ee5cbfSDavid du Colombier 			Bprint(Bstdout, "%s\n", buf);
1807dd7cddfSDavid du Colombier 		inpath = TRUE;
1817dd7cddfSDavid du Colombier 	}
1827dd7cddfSDavid du Colombier }
18380ee5cbfSDavid du Colombier 
18480ee5cbfSDavid du Colombier static void parsebuf(char*);
18580ee5cbfSDavid du Colombier 
18680ee5cbfSDavid du Colombier void
drawpath(char * buf,int copy)18780ee5cbfSDavid du Colombier drawpath(char *buf, int copy) {
18880ee5cbfSDavid du Colombier 
18980ee5cbfSDavid du Colombier /*
19080ee5cbfSDavid du Colombier  *
19180ee5cbfSDavid du Colombier  * Called from devcntrl() whenever an "x X DrawPath" command is read. It marks the
19280ee5cbfSDavid du Colombier  * end of the path started by the last "x X BeginPath" command and uses whatever
19380ee5cbfSDavid du Colombier  * has been passed along in *buf to manipulate the path (eg. fill and/or stroke
19480ee5cbfSDavid du Colombier  * the path). Once that's been done the drawing procedures are restored to their
19580ee5cbfSDavid du Colombier  * default behavior in which each drawing command is treated as an isolated path.
19680ee5cbfSDavid du Colombier  * The new version (called after "x X DrawPath") has copy set to FALSE, and calls
19780ee5cbfSDavid du Colombier  * parsebuf() to figure out what goes in the output file. It's a feeble attempt
19880ee5cbfSDavid du Colombier  * to free users and preprocessors (like pic) from having to know PostScript. The
19980ee5cbfSDavid du Colombier  * comments in parsebuf() describe what's handled.
20080ee5cbfSDavid du Colombier  *
20180ee5cbfSDavid du Colombier  * In the early version a path was started with "x X BeginObject" and ended with
20280ee5cbfSDavid du Colombier  * "x X EndObject". In both cases *buf was just copied to the output file, and
20380ee5cbfSDavid du Colombier  * was expected to be legitimate PostScript that manipulated the current path.
20480ee5cbfSDavid du Colombier  * The old escape sequence will be supported for a while (for Ravi), and always
20580ee5cbfSDavid du Colombier  * call this routine with copy set to TRUE.
20680ee5cbfSDavid du Colombier  *
20780ee5cbfSDavid du Colombier  *
20880ee5cbfSDavid du Colombier  */
20980ee5cbfSDavid du Colombier 
21080ee5cbfSDavid du Colombier 	if ( inpath == TRUE ) {
21180ee5cbfSDavid du Colombier 		if ( copy == TRUE )
21280ee5cbfSDavid du Colombier 			Bprint(Bstdout, "%s\n", buf);
21380ee5cbfSDavid du Colombier 		else
21480ee5cbfSDavid du Colombier 			parsebuf(buf);
21580ee5cbfSDavid du Colombier 		Bprint(Bstdout, "grestore\n");
21680ee5cbfSDavid du Colombier 		Bprint(Bstdout, "/inpath false def\n");
21780ee5cbfSDavid du Colombier /*		reset();		*/
21880ee5cbfSDavid du Colombier 		inpath = FALSE;
21980ee5cbfSDavid du Colombier 	}
22080ee5cbfSDavid du Colombier }
22180ee5cbfSDavid du Colombier 
22280ee5cbfSDavid du Colombier 
22380ee5cbfSDavid du Colombier static void
parsebuf(char * buf)22480ee5cbfSDavid du Colombier parsebuf(char *buf)
22580ee5cbfSDavid du Colombier {
22680ee5cbfSDavid du Colombier 	char *p;			/* usually the next token */
22780ee5cbfSDavid du Colombier 	char *q;
22880ee5cbfSDavid du Colombier 	int gsavelevel = 0;		/* non-zero if we've done a gsave */
22980ee5cbfSDavid du Colombier 
23080ee5cbfSDavid du Colombier /*
23180ee5cbfSDavid du Colombier  * Simple minded attempt at parsing the string that followed an "x X DrawPath"
23280ee5cbfSDavid du Colombier  * command. Everything not recognized here is simply ignored - there's absolutely
23380ee5cbfSDavid du Colombier  * no error checking and what was originally in buf is clobbered by strtok().
23480ee5cbfSDavid du Colombier  * A typical *buf might look like,
23580ee5cbfSDavid du Colombier  *
23680ee5cbfSDavid du Colombier  *	gray .9 fill stroke
23780ee5cbfSDavid du Colombier  *
23880ee5cbfSDavid du Colombier  * to fill the current path with a gray level of .9 and follow that by stroking the
23980ee5cbfSDavid du Colombier  * outline of the path. Since unrecognized tokens are ignored the last example
24080ee5cbfSDavid du Colombier  * could also be written as,
24180ee5cbfSDavid du Colombier  *
24280ee5cbfSDavid du Colombier  *	with gray .9 fill then stroke
24380ee5cbfSDavid du Colombier  *
24480ee5cbfSDavid du Colombier  * The "with" and "then" strings aren't recognized tokens and are simply discarded.
24580ee5cbfSDavid du Colombier  * The "stroke", "fill", and "wfill" force out appropriate PostScript code and are
24680ee5cbfSDavid du Colombier  * followed by a grestore. In otherwords changes to the grahics state (eg. a gray
24780ee5cbfSDavid du Colombier  * level or color) are reset to default values immediately after the stroke, fill,
24880ee5cbfSDavid du Colombier  * or wfill tokens. For now "fill" gets invokes PostScript's eofill operator and
24980ee5cbfSDavid du Colombier  * "wfill" calls fill (ie. the operator that uses the non-zero winding rule).
25080ee5cbfSDavid du Colombier  *
25180ee5cbfSDavid du Colombier  * The tokens that cause temporary changes to the graphics state are "gray" (for
25280ee5cbfSDavid du Colombier  * setting the gray level), "color" (for selecting a known color from the colordict
25380ee5cbfSDavid du Colombier  * dictionary defined in *colorfile), and "line" (for setting the line width). All
25480ee5cbfSDavid du Colombier  * three tokens can be extended since strncmp() makes the comparison. For example
25580ee5cbfSDavid du Colombier  * the strings "line" and "linewidth" accomplish the same thing. Colors are named
25680ee5cbfSDavid du Colombier  * (eg. "red"), but must be appropriately defined in *colorfile. For now all three
25780ee5cbfSDavid du Colombier  * tokens must be followed immediately by their single argument. The gray level
25880ee5cbfSDavid du Colombier  * (ie. the argument that follows "gray") should be a number between 0 and 1, with
25980ee5cbfSDavid du Colombier  * 0 for black and 1 for white.
26080ee5cbfSDavid du Colombier  *
26180ee5cbfSDavid du Colombier  * To pass straight PostScript through enclose the appropriate commands in double
26280ee5cbfSDavid du Colombier  * quotes. Straight PostScript is only bracketed by the outermost gsave/grestore
26380ee5cbfSDavid du Colombier  * pair (ie. the one from the initial "x X BeginPath") although that's probably
26480ee5cbfSDavid du Colombier  * a mistake. Suspect I may have to change the double quote delimiters.
26580ee5cbfSDavid du Colombier  */
266*456a8764SDavid du Colombier 	for(p = buf; p != nil; p = q) {
267*456a8764SDavid du Colombier 		if( q = strchr(p, ' ') )
26880ee5cbfSDavid du Colombier 			*q++ = '\0';
26980ee5cbfSDavid du Colombier 
27080ee5cbfSDavid du Colombier 		if ( gsavelevel == 0 ) {
27180ee5cbfSDavid du Colombier 			Bprint(Bstdout, "gsave\n");
27280ee5cbfSDavid du Colombier 			gsavelevel++;
27380ee5cbfSDavid du Colombier 		}
27480ee5cbfSDavid du Colombier 		if ( strcmp(p, "stroke") == 0 ) {
27580ee5cbfSDavid du Colombier 			Bprint(Bstdout, "closepath stroke\ngrestore\n");
27680ee5cbfSDavid du Colombier 			gsavelevel--;
27780ee5cbfSDavid du Colombier 		} else if ( strcmp(p, "openstroke") == 0 ) {
27880ee5cbfSDavid du Colombier 			Bprint(Bstdout, "stroke\ngrestore\n");
27980ee5cbfSDavid du Colombier 			gsavelevel--;
28080ee5cbfSDavid du Colombier 		} else if ( strcmp(p, "fill") == 0 ) {
28180ee5cbfSDavid du Colombier 			Bprint(Bstdout, "eofill\ngrestore\n");
28280ee5cbfSDavid du Colombier 			gsavelevel--;
28380ee5cbfSDavid du Colombier 		} else if ( strcmp(p, "wfill") == 0 ) {
28480ee5cbfSDavid du Colombier 			Bprint(Bstdout, "fill\ngrestore\n");
28580ee5cbfSDavid du Colombier 			gsavelevel--;
28680ee5cbfSDavid du Colombier 		} else if ( strcmp(p, "sfill") == 0 ) {
28780ee5cbfSDavid du Colombier 			Bprint(Bstdout, "eofill\ngrestore\ngsave\nstroke\ngrestore\n");
28880ee5cbfSDavid du Colombier 			gsavelevel--;
28980ee5cbfSDavid du Colombier 		} else if ( strncmp(p, "gray", strlen("gray")) == 0 ) {
29080ee5cbfSDavid du Colombier 			if( q ) {
29180ee5cbfSDavid du Colombier 				p = q;
29280ee5cbfSDavid du Colombier 				if ( q = strchr(p, ' ') )
29380ee5cbfSDavid du Colombier 					*q++ = '\0';
29480ee5cbfSDavid du Colombier 				Bprint(Bstdout, "%s setgray\n", p);
29580ee5cbfSDavid du Colombier 			}
29680ee5cbfSDavid du Colombier 		} else if ( strncmp(p, "color", strlen("color")) == 0 ) {
29780ee5cbfSDavid du Colombier 			if( q ) {
29880ee5cbfSDavid du Colombier 				p = q;
29980ee5cbfSDavid du Colombier 				if ( q = strchr(p, ' ') )
30080ee5cbfSDavid du Colombier 					*q++ = '\0';
30180ee5cbfSDavid du Colombier 				Bprint(Bstdout, "/%s setcolor\n", p);
30280ee5cbfSDavid du Colombier 			}
30380ee5cbfSDavid du Colombier 		} else if ( strncmp(p, "line", strlen("line")) == 0 ) {
30480ee5cbfSDavid du Colombier 			if( q ) {
30580ee5cbfSDavid du Colombier 				p = q;
30680ee5cbfSDavid du Colombier 				if ( q = strchr(p, ' ') )
30780ee5cbfSDavid du Colombier 					*q++ = '\0';
30880ee5cbfSDavid du Colombier 				Bprint(Bstdout, "%s resolution mul 2 div setlinewidth\n", p);
30980ee5cbfSDavid du Colombier 			}
31080ee5cbfSDavid du Colombier 		} else if ( strncmp(p, "reverse", strlen("reverse")) == 0 )
31180ee5cbfSDavid du Colombier 			Bprint(Bstdout, "reversepath\n");
31280ee5cbfSDavid du Colombier 		else if ( *p == '"' ) {
31380ee5cbfSDavid du Colombier 			for ( ; gsavelevel > 0; gsavelevel-- )
31480ee5cbfSDavid du Colombier 				Bprint(Bstdout, "grestore\n");
31580ee5cbfSDavid du Colombier 			if ( q != nil )
31680ee5cbfSDavid du Colombier 				*--q = ' ';
31780ee5cbfSDavid du Colombier 			if ( (q = strchr(p, '"')) != nil ) {
31880ee5cbfSDavid du Colombier 				*q++ = '\0';
31980ee5cbfSDavid du Colombier 				Bprint(Bstdout, "%s\n", p);
32080ee5cbfSDavid du Colombier 			}
32180ee5cbfSDavid du Colombier 		}
32280ee5cbfSDavid du Colombier 	}
32380ee5cbfSDavid du Colombier 
32480ee5cbfSDavid du Colombier 	for ( ; gsavelevel > 0; gsavelevel-- )
32580ee5cbfSDavid du Colombier 		Bprint(Bstdout, "grestore\n");
32680ee5cbfSDavid du Colombier 
32780ee5cbfSDavid du Colombier }
328