aboutsummaryrefslogtreecommitdiff
path: root/src/cmd/postscript/tr2post/draw.c
blob: 0cb368ae419d14c512e0f53008cfde3a406272c6 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
#include <u.h>
#include <libc.h>
#include <bio.h>
#include <ctype.h>
#include "../common/common.h"
#include "tr2post.h"

BOOLEAN drawflag = FALSE;
BOOLEAN	inpath = FALSE;			/* TRUE if we're putting pieces together */

void
cover(double x, double y) {
}

void
drawspline(Biobufhdr *Bp, int flag) {	/* flag!=1 connect end points */
	int x[100], y[100];
	int i, N;
/*
 *
 * Spline drawing routine for Postscript printers. The complicated stuff is
 * handled by procedure Ds, which should be defined in the library file. I've
 * seen wrong implementations of troff's spline drawing, so fo the record I'll
 * write down the parametric equations and the necessary conversions to Bezier
 * cubic splines (as used in Postscript).
 *
 *
 * Parametric equation (x coordinate only):
 *
 *
 *	    (x2 - 2 * x1 + x0)    2                    (x0 + x1)
 *	x = ------------------ * t   + (x1 - x0) * t + ---------
 *		    2					   2
 *
 *
 * The coefficients in the Bezier cubic are,
 *
 *
 *	A = 0
 *	B = (x2 - 2 * x1 + x0) / 2
 *	C = x1 - x0
 *
 *
 * while the current point is,
 *
 *	current-point = (x0 + x1) / 2
 *
 * Using the relationships given in the Postscript manual (page 121) it's easy to
 * see that the control points are given by,
 *
 *
 *	x0' = (x0 + 5 * x1) / 6
 *	x1' = (x2 + 5 * x1) / 6
 *	x2' = (x1 + x2) / 2
 *
 *
 * where the primed variables are the ones used by curveto. The calculations
 * shown above are done in procedure Ds using the coordinates set up in both
 * the x[] and y[] arrays.
 *
 * A simple test of whether your spline drawing is correct would be to use cip
 * to draw a spline and some tangent lines at appropriate points and then print
 * the file.
 *
 */

	for (N=2; N<sizeof(x)/sizeof(x[0]); N++)
		if (Bgetfield(Bp, 'd', &x[N], 0)<=0 || Bgetfield(Bp, 'd', &y[N], 0)<=0)
			break;

	x[0] = x[1] = hpos;
	y[0] = y[1] = vpos;

	for (i = 1; i < N; i++) {
		x[i+1] += x[i];
		y[i+1] += y[i];
	}

	x[N] = x[N-1];
	y[N] = y[N-1];

	for (i = ((flag!=1)?0:1); i < ((flag!=1)?N-1:N-2); i++) {
		endstring();
		if (pageon())
			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]);
/*		if (dobbox == TRUE) {		/* could be better */
/*	    		cover((double)(x[i] + x[i+1])/2,(double)-(y[i] + y[i+1])/2);
/*	    		cover((double)x[i+1], (double)-y[i+1]);
/*	    		cover((double)(x[i+1] + x[i+2])/2, (double)-(y[i+1] + y[i+2])/2);
/*		}
 */
	}

	hpos = x[N];			/* where troff expects to be */
	vpos = y[N];
}

void
draw(Biobufhdr *Bp) {

	int r, x1, y1, x2, y2, i;
	int d1, d2;

	drawflag = TRUE;
	r = Bgetrune(Bp);
	switch(r) {
	case 'l':
		if (Bgetfield(Bp, 'd', &x1, 0)<=0 || Bgetfield(Bp, 'd', &y1, 0)<=0 || Bgetfield(Bp, 'r', &i, 0)<=0)
			error(FATAL, "draw line function, destination coordinates not found.\n");

		endstring();
		if (pageon())
			Bprint(Bstdout, "%d %d %d %d Dl\n", hpos, vpos, hpos+x1, vpos+y1);
		hpos += x1;
		vpos += y1;
		break;
	case 'c':
		if (Bgetfield(Bp, 'd', &d1, 0)<=0)
			error(FATAL, "draw circle function, diameter coordinates not found.\n");

		endstring();
		if (pageon())
			Bprint(Bstdout, "%d %d %d %d De\n", hpos, vpos, d1, d1);
		hpos += d1;
		break;
	case 'e':
		if (Bgetfield(Bp, 'd', &d1, 0)<=0 || Bgetfield(Bp, 'd', &d2, 0)<=0)
			error(FATAL, "draw ellipse function, diameter coordinates not found.\n");

		endstring();
		if (pageon())
			Bprint(Bstdout, "%d %d %d %d De\n", hpos, vpos, d1, d2);
		hpos += d1;
		break;
	case 'a':
		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)
			error(FATAL, "draw arc function, coordinates not found.\n");

		endstring();
		if (pageon())
			Bprint(Bstdout, "%d %d %d %d %d %d Da\n", hpos, vpos, x1, y1, x2, y2);
		hpos += x1 + x2;
		vpos += y1 + y2;
		break;
	case 'q':
		drawspline(Bp, 1);
		break;
	case '~':
		drawspline(Bp, 2);
		break;
	default:
		error(FATAL, "unknown draw function <%c>\n", r);
		break;
	}
}

void
beginpath(char *buf, int copy) {

/*
 * Called from devcntrl() whenever an "x X BeginPath" command is read. It's used
 * to mark the start of a sequence of drawing commands that should be grouped
 * together and treated as a single path. By default the drawing procedures in
 * *drawfile treat each drawing command as a separate object, and usually start
 * with a newpath (just as a precaution) and end with a stroke. The newpath and
 * stroke isolate individual drawing commands and make it impossible to deal with
 * composite objects. "x X BeginPath" can be used to mark the start of drawing
 * commands that should be grouped together and treated as a single object, and
 * part of what's done here ensures that the PostScript drawing commands defined
 * in *drawfile skip the newpath and stroke, until after the next "x X DrawPath"
 * command. At that point the path that's been built up can be manipulated in
 * various ways (eg. filled and/or stroked with a different line width).
 *
 * Color selection is one of the options that's available in parsebuf(),
 * so if we get here we add *colorfile to the output file before doing
 * anything important.
 *
 */
	if (inpath == FALSE) {
		endstring();
	/*	getdraw();	*/
	/*	getcolor(); */
		Bprint(Bstdout, "gsave\n");
		Bprint(Bstdout, "newpath\n");
		Bprint(Bstdout, "%d %d m\n", hpos, vpos);
		Bprint(Bstdout, "/inpath true def\n");
		if ( copy == TRUE )
			Bprint(Bstdout, "%s\n", buf);
		inpath = TRUE;
	}
}

static void parsebuf(char*);

void
drawpath(char *buf, int copy) {

/*
 *
 * Called from devcntrl() whenever an "x X DrawPath" command is read. It marks the
 * end of the path started by the last "x X BeginPath" command and uses whatever
 * has been passed along in *buf to manipulate the path (eg. fill and/or stroke
 * the path). Once that's been done the drawing procedures are restored to their
 * default behavior in which each drawing command is treated as an isolated path.
 * The new version (called after "x X DrawPath") has copy set to FALSE, and calls
 * parsebuf() to figure out what goes in the output file. It's a feeble attempt
 * to free users and preprocessors (like pic) from having to know PostScript. The
 * comments in parsebuf() describe what's handled.
 *
 * In the early version a path was started with "x X BeginObject" and ended with
 * "x X EndObject". In both cases *buf was just copied to the output file, and
 * was expected to be legitimate PostScript that manipulated the current path.
 * The old escape sequence will be supported for a while (for Ravi), and always
 * call this routine with copy set to TRUE.
 * 
 *
 */

	if ( inpath == TRUE ) {
		if ( copy == TRUE )
			Bprint(Bstdout, "%s\n", buf);
		else
			parsebuf(buf);
		Bprint(Bstdout, "grestore\n");
		Bprint(Bstdout, "/inpath false def\n");
/*		reset();		*/
		inpath = FALSE;
	}
}


/*****************************************************************************/

static void
parsebuf(char *buf)
{
	char	*p;			/* usually the next token */
	char *q;
	int		gsavelevel = 0;		/* non-zero if we've done a gsave */

/*
 *
 * Simple minded attempt at parsing the string that followed an "x X DrawPath"
 * command. Everything not recognized here is simply ignored - there's absolutely
 * no error checking and what was originally in buf is clobbered by strtok().
 * A typical *buf might look like,
 *
 *	gray .9 fill stroke
 *
 * to fill the current path with a gray level of .9 and follow that by stroking the
 * outline of the path. Since unrecognized tokens are ignored the last example
 * could also be written as,
 *
 *	with gray .9 fill then stroke
 *
 * The "with" and "then" strings aren't recognized tokens and are simply discarded.
 * The "stroke", "fill", and "wfill" force out appropriate PostScript code and are
 * followed by a grestore. In otherwords changes to the grahics state (eg. a gray
 * level or color) are reset to default values immediately after the stroke, fill,
 * or wfill tokens. For now "fill" gets invokes PostScript's eofill operator and
 * "wfill" calls fill (ie. the operator that uses the non-zero winding rule).
 *
 * The tokens that cause temporary changes to the graphics state are "gray" (for
 * setting the gray level), "color" (for selecting a known color from the colordict
 * dictionary defined in *colorfile), and "line" (for setting the line width). All
 * three tokens can be extended since strncmp() makes the comparison. For example
 * the strings "line" and "linewidth" accomplish the same thing. Colors are named
 * (eg. "red"), but must be appropriately defined in *colorfile. For now all three
 * tokens must be followed immediately by their single argument. The gray level
 * (ie. the argument that follows "gray") should be a number between 0 and 1, with
 * 0 for black and 1 for white.
 *
 * To pass straight PostScript through enclose the appropriate commands in double
 * quotes. Straight PostScript is only bracketed by the outermost gsave/grestore
 * pair (ie. the one from the initial "x X BeginPath") although that's probably
 * a mistake. Suspect I may have to change the double quote delimiters.
 *
 */

	for( ; p != nil ; p = q ) {
		if( q = strchr(p, ' ') ) {
			*q++ = '\0';
		}

		if ( gsavelevel == 0 ) {
			Bprint(Bstdout, "gsave\n");
			gsavelevel++;
		}
		if ( strcmp(p, "stroke") == 0 ) {
			Bprint(Bstdout, "closepath stroke\ngrestore\n");
			gsavelevel--;
		} else if ( strcmp(p, "openstroke") == 0 ) {
			Bprint(Bstdout, "stroke\ngrestore\n");
			gsavelevel--;
		} else if ( strcmp(p, "fill") == 0 ) {
			Bprint(Bstdout, "eofill\ngrestore\n");
			gsavelevel--;
		} else if ( strcmp(p, "wfill") == 0 ) {
			Bprint(Bstdout, "fill\ngrestore\n");
			gsavelevel--;
		} else if ( strcmp(p, "sfill") == 0 ) {
			Bprint(Bstdout, "eofill\ngrestore\ngsave\nstroke\ngrestore\n");
			gsavelevel--;
		} else if ( strncmp(p, "gray", strlen("gray")) == 0 ) {
			if( q ) {
				p = q;
				if ( q = strchr(p, ' ') )
					*q++ = '\0';
				Bprint(Bstdout, "%s setgray\n", p);
			}
		} else if ( strncmp(p, "color", strlen("color")) == 0 ) {
			if( q ) {
				p = q;
				if ( q = strchr(p, ' ') )
					*q++ = '\0';
				Bprint(Bstdout, "/%s setcolor\n", p);
			}
		} else if ( strncmp(p, "line", strlen("line")) == 0 ) {
			if( q ) {
				p = q;
				if ( q = strchr(p, ' ') )
					*q++ = '\0';
				Bprint(Bstdout, "%s resolution mul 2 div setlinewidth\n", p);
			}
		} else if ( strncmp(p, "reverse", strlen("reverse")) == 0 )
			Bprint(Bstdout, "reversepath\n");
		else if ( *p == '"' ) {
			for ( ; gsavelevel > 0; gsavelevel-- )
				Bprint(Bstdout, "grestore\n");
			if ( q != nil )
				*--q = ' ';
			if ( (q = strchr(p, '"')) != nil ) {
				*q++ = '\0';
				Bprint(Bstdout, "%s\n", p);
			}
		}
	}

	for ( ; gsavelevel > 0; gsavelevel-- )
		Bprint(Bstdout, "grestore\n");

}