xref: /illumos-gate/usr/src/cmd/lp/filter/postscript/dpost/draw.c (revision 7c478bd95313f5f23a4c958a745db2134aa03244)
1 /*
2  * CDDL HEADER START
3  *
4  * The contents of this file are subject to the terms of the
5  * Common Development and Distribution License, Version 1.0 only
6  * (the "License").  You may not use this file except in compliance
7  * with the License.
8  *
9  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
10  * or http://www.opensolaris.org/os/licensing.
11  * See the License for the specific language governing permissions
12  * and limitations under the License.
13  *
14  * When distributing Covered Code, include this CDDL HEADER in each
15  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
16  * If applicable, add the following below this CDDL HEADER, with the
17  * fields enclosed by brackets "[]" replaced with your own identifying
18  * information: Portions Copyright [yyyy] [name of copyright owner]
19  *
20  * CDDL HEADER END
21  */
22 /*	Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T	*/
23 /*	  All Rights Reserved  	*/
24 
25 
26 /*
27  * University Copyright- Copyright (c) 1982, 1986, 1988
28  * The Regents of the University of California
29  * All Rights Reserved
30  *
31  * University Acknowledgment- Portions of this document are derived from
32  * software developed by the University of California, Berkeley, and its
33  * contributors.
34  */
35 
36 #pragma ident	"%Z%%M%	%I%	%E% SMI"	/* SVr4.0 1.1	*/
37 
38 /*
39  *
40  * Drawing routines used by dpost. Almost no real work is done here. Instead
41  * the required calculations are done in special Postscript procedures that
42  * include:
43  *
44  *
45  *	Dl
46  *
47  *	  x1 y1 x y Dl -
48  *
49  *	    Starts a new path and then draws a line from the current point
50  *	    (x, y) to (x1, y1).
51  *
52  *	De
53  *
54  *	  x y a b De -
55  *
56  *	    Starts a new path and then draws an ellipse that has its left side
57  *	    at the current point (x, y) and horizontal and vertical axes lengths
58  *	    given by a and b respectively.
59  *
60  *	Da
61  *
62  *	  x y dx1 dy1 dx2 dy2 Da -
63  *
64  *	    Starts a new segment and then draws a circular arc from the current
65  *	    point (x, y) to (x + dx1 + dx2, y + dy1 + dy2). The center of the
66  *	    circle is at (x + dx1, y + dy1). Arcs always go counter-clockwise
67  *	    from the starting point to the end point.
68  *
69  *	DA
70  *
71  *	  x y dx1 dy1 dx2 dy2 DA -
72  *
73  *	    Draws a clockwise arc from (x, y) to (x + dx1 + dx2, y + dy1 + dy2)
74  *	    with center at (x + dx1, y + dy1). Only needed when we're building
75  *	    large paths that use arcs and want to control the current point. The
76  *	    arguments passed to drawarc() will be whatever they would have been
77  *	    for a counter-clockwise arc, so we need to map them into appropriate
78  *	    arguments for PostScript's arcn operator. The mapping is,
79  *
80  *			x = hpos + dx1' + dx2'
81  *			y = vpos + dy1' + dy2'
82  *			dx1 = -dx2'
83  *			dy1 = -dy2'
84  *			dx2 = -dx1'
85  *			dy2 = -dy1'
86  *
87  *	   where primed values represent the drawarc() arguments and (hpos, vpos)
88  *	   is our current position.
89  *
90  *	Ds
91  *
92  *	  x0 y0 x1 y1 x2 y2 Ds -
93  *
94  *	    Starts a new segment and then draws a quadratic spline connecting
95  *	    point ((x0 + x1)/2, (y0 + y1)/2) to ((x1 + x2)/2, (y1 + y2)/2).
96  *	    The points used in Postscript's curveto procedure are given by,
97  *
98  *		x0' = (x0 + 5 * x1) / 6
99  *		x1' = (x2 + 5 * x1) / 6
100  *		x2' = (x1 + x2) / 2
101  *
102  *	    with similar equations for the y coordinates.
103  *
104  * By default all the PostScript drawing procedures begin with a newpath (just to
105  * be safe) and end with a stroke, which essentially isolates the path elements
106  * built by the drawing procedures. In order to accommodate big paths built from
107  * smaller pieces each of the PostScript drawing procedures can forced to retain
108  * the path that's being built. That's what happens in beginpath() when an "x X
109  * BeginPath" command is read. beginpath() sets the PostScript variable inpath to
110  * true, and that essentially eliminates the newpath/stroke pair that bracket the
111  * individual pieces. In that case the path is terminated and drawn when dpost
112  * reads an "x X DrawPath" command.
113  *
114  * Early versions of dpost included the PostScript drawing procedures as part of
115  * the prologue, and as a result they were included with every job, even if they
116  * were never used. This version has separated the drawing procedures from the
117  * default prologue (they're now in *drawfile) and only includes them if they're
118  * really needed, which is yet another convenient violation of page independence.
119  * Routine getdraw() is responsible for adding *drawfile to the output file, and
120  * if it can't read *drawfile it continues on as if nothing happened. That means
121  * everything should still work if you append *drawfile to *prologue and then
122  * delete *drawfile.
123  *
124  */
125 
126 
127 #include <stdio.h>
128 #include <math.h>
129 
130 #include "gen.h"			/* general purpose definitions */
131 #include "ext.h"			/* external variable definitions */
132 
133 
134 int	gotdraw = FALSE;		/* TRUE when *drawfile has been added */
135 int	gotbaseline = FALSE;		/* TRUE after *baselinefile is added */
136 int	inpath = FALSE;			/* TRUE if we're putting pieces together */
137 
138 
139 /*
140  *
141  * All these should be defined in file dpost.c.
142  *
143  */
144 
145 
146 extern int		hpos;
147 extern int		vpos;
148 extern int		encoding;
149 extern int		maxencoding;
150 extern int		realencoding;
151 
152 extern char		*drawfile;
153 extern char		*baselinefile;
154 extern FILE		*tf;
155 
156 
157 /*****************************************************************************/
158 
159 
160 getdraw()
161 
162 
163 {
164 
165 
166 /*
167  *
168  * Responsible for making sure the PostScript drawing procedures are downloaded
169  * from *drawfile. Stuff is done at most once per job, and only if the job needs
170  * them. For now I've decided not to quit if we can't read the drawing file. That
171  * pretty much assumes an old version of prologue is being used that includes all
172  * the drawing procedures.
173  *
174  */
175 
176 
177     if ( gotdraw == FALSE && access(drawfile, 04) == 0 )
178 	doglobal(drawfile);
179 
180     if ( tf == stdout )
181 	gotdraw = TRUE;
182 
183 }   /* End of getdraw */
184 
185 
186 /*****************************************************************************/
187 
188 
189 drawline(dx, dy)
190 
191 
192     int		dx, dy;			/* endpoint is (hpos+dx, vpos+dy) */
193 
194 
195 {
196 
197 
198 /*
199  *
200  * Draws a line from (hpos, vpos) to (hpos+dx, vpos+dy), and leaves the current
201  * position at the endpoint.
202  *
203  */
204 
205 
206     if ( dx == 0 && dy == 0 )
207 	drawcirc(1);
208     else fprintf(tf, "%d %d %d %d Dl\n", hpos + dx, vpos + dy, hpos, vpos);
209 
210     hgoto(hpos+dx);			/* where troff expects to be */
211     vgoto(vpos+dy);
212 
213     resetpos();				/* not sure where the printer is */
214 
215 }   /* End of drawline */
216 
217 
218 /*****************************************************************************/
219 
220 
221 drawcirc(d)
222 
223 
224     int		d;			/* diameter of the circle */
225 
226 
227 {
228 
229 
230 /*
231  *
232  * Draws a circle of diameter d with the left 'side' of the circle at the
233  * current point. After we're finished drawing we move the current position
234  * to the right side.
235  *
236  */
237 
238     drawellip(d, d);
239 
240 }   /* End of drawcirc */
241 
242 
243 /*****************************************************************************/
244 
245 
246 drawellip(a, b)
247 
248 
249     int		a, b;			/* axes lengths for the ellipse */
250 
251 
252 {
253 
254 
255 /*
256  *
257  * Draws an ellipse having axes lengths horizontally and vertically of a and
258  * b. The left side of the ellipse is at the current point. After we're done
259  * drawing the path we move the current position to the right side.
260  *
261  */
262 
263 
264     if ( a == 0 && b == 0 )
265 	return;
266 
267     fprintf(tf, "%d %d %d %d De\n", hpos, vpos, a, b);
268 
269     hgoto(hpos + a);			/* where troff expects to be */
270     vgoto(vpos);
271 
272     resetpos();				/* not sure where the printer is */
273 
274 }   /* End of drawellip */
275 
276 
277 /*****************************************************************************/
278 
279 
280 drawarc(dx1, dy1, dx2, dy2, c)
281 
282 
283     int		dx1, dy1;		/* vector from current pos to center */
284     int		dx2, dy2;		/* from center to end of the arc */
285     int		c;			/* clockwise if c is A */
286 
287 
288 {
289 
290 
291 /*
292  *
293  * If c isn't set to 'A' a counter-clockwise arc is drawn from the current point
294  * (hpos, vpos) to (hpos+dx1+dx2, vpos+dy1+dy2). The center of the circle is the
295  * point (hpos+dx1, vpos+dy1). If c is 'A' the arc goes clockwise from the point
296  * (hpos+dx1+dx2, vpos+dy1+dy2) to (hpos, vpos). Clockwise arcs are only needed
297  * if we're building a larger path out of pieces that include arcs, and want to
298  * have PostScript manage the path for us. Arguments (for a clockwise arc) are
299  * what would have been supplied if the arc was drawn in a counter-clockwise
300  * direction, and are converted to values suitable for use with PostScript's arcn
301  * operator.
302  *
303  */
304 
305 
306     if ( (dx1 != 0 || dy1 != 0) && (dx2 != 0 || dy2 != 0) )
307 	if ( c != 'A' )
308 	    fprintf(tf, "%d %d %d %d %d %d Da\n", hpos, vpos, dx1, dy1, dx2, dy2);
309 	else fprintf(tf, "%d %d %d %d %d %d DA\n", hpos+dx1+dx2, vpos+dy1+dy2,
310 						-dx2, -dy2, -dx1, -dy1);
311 
312     hgoto(hpos + dx1 + dx2);		/* where troff expects to be */
313     vgoto(vpos + dy1 + dy2);
314 
315     resetpos();				/* not sure where the printer is */
316 
317 }   /* End of drawarc */
318 
319 
320 /*****************************************************************************/
321 
322 
323 drawspline(fp, flag)
324 
325 
326     FILE	*fp;			/* input for point list */
327     int		flag;			/* flag!=1 connect end points */
328 
329 
330 {
331 
332 
333     int		x[100], y[100];
334     int		i, N;
335 
336 
337 /*
338  *
339  * Spline drawing routine for Postscript printers. The complicated stuff is
340  * handled by procedure Ds, which should be defined in the library file. I've
341  * seen wrong implementations of troff's spline drawing, so fo the record I'll
342  * write down the parametric equations and the necessary conversions to Bezier
343  * cubic splines (as used in Postscript).
344  *
345  *
346  * Parametric equation (x coordinate only):
347  *
348  *
349  *	    (x2 - 2 * x1 + x0)    2                    (x0 + x1)
350  *	x = ------------------ * t   + (x1 - x0) * t + ---------
351  *		    2					   2
352  *
353  *
354  * The coefficients in the Bezier cubic are,
355  *
356  *
357  *	A = 0
358  *	B = (x2 - 2 * x1 + x0) / 2
359  *	C = x1 - x0
360  *
361  *
362  * while the current point is,
363  *
364  *	current-point = (x0 + x1) / 2
365  *
366  * Using the relationships given in the Postscript manual (page 121) it's easy to
367  * see that the control points are given by,
368  *
369  *
370  *	x0' = (x0 + 5 * x1) / 6
371  *	x1' = (x2 + 5 * x1) / 6
372  *	x2' = (x1 + x2) / 2
373  *
374  *
375  * where the primed variables are the ones used by curveto. The calculations
376  * shown above are done in procedure Ds using the coordinates set up in both
377  * the x[] and y[] arrays.
378  *
379  * A simple test of whether your spline drawing is correct would be to use cip
380  * to draw a spline and some tangent lines at appropriate points and then print
381  * the file.
382  *
383  */
384 
385 
386     for ( N = 2; N < sizeof(x)/sizeof(x[0]); N++ )
387 	if (fscanf(fp, "%d %d", &x[N], &y[N]) != 2)
388 		break;
389 
390     x[0] = x[1] = hpos;
391     y[0] = y[1] = vpos;
392 
393     for (i = 1; i < N; i++)  {
394 	x[i+1] += x[i];
395 	y[i+1] += y[i];
396     }	/* End for */
397 
398     x[N] = x[N-1];
399     y[N] = y[N-1];
400 
401     for (i = ((flag!=1)?0:1); i < ((flag!=1)?N-1:N-2); i++)
402 	fprintf(tf, "%d %d %d %d %d %d Ds\n", x[i], y[i], x[i+1], y[i+1], x[i+2], y[i+2]);
403 
404     hgoto(x[N]);			/* where troff expects to be */
405     vgoto(y[N]);
406 
407     resetpos();				/* not sure where the printer is */
408 
409 }   /* End of drawspline */
410 
411 
412 /*****************************************************************************/
413 
414 
415 beginpath(buf, copy)
416 
417 
418     char	*buf;			/* whatever followed "x X BeginPath" */
419     int		copy;			/* ignore *buf if FALSE */
420 
421 
422 {
423 
424 
425 /*
426  *
427  * Called from devcntrl() whenever an "x X BeginPath" command is read. It's used
428  * to mark the start of a sequence of drawing commands that should be grouped
429  * together and treated as a single path. By default the drawing procedures in
430  * *drawfile treat each drawing command as a separate object, and usually start
431  * with a newpath (just as a precaution) and end with a stroke. The newpath and
432  * stroke isolate individual drawing commands and make it impossible to deal with
433  * composite objects. "x X BeginPath" can be used to mark the start of drawing
434  * commands that should be grouped together and treated as a single object, and
435  * part of what's done here ensures that the PostScript drawing commands defined
436  * in *drawfile skip the newpath and stroke, until after the next "x X DrawPath"
437  * command. At that point the path that's been built up can be manipulated in
438  * various ways (eg. filled and/or stroked with a different line width).
439  *
440  * String *buf is unnecessary and is only included for compatibility with an early
441  * verion of that's still in use. In that version "x X BeginObject" marked the
442  * start of a graphical object, and whatever followed it was passed along in *buf
443  * and copied to the output file. Color selection is one of the options that's
444  * available in parsebuf(), so if we get here we add *colorfile to the output
445  * file before doing anything important.
446  *
447  */
448 
449 
450 
451     if ( inpath == FALSE )  {
452 	endtext();
453 	getdraw();
454 	getcolor();
455 	fprintf(tf, "gsave\n");
456 	fprintf(tf, "newpath\n");
457 	fprintf(tf, "%d %d m\n", hpos, vpos);
458 	fprintf(tf, "/inpath true def\n");
459 	if ( copy == TRUE )
460 	    fprintf(tf, "%s", buf);
461 	inpath = TRUE;
462     }	/* End if */
463 
464 }   /* End of beginpath */
465 
466 
467 /*****************************************************************************/
468 
469 
470 drawpath(buf, copy)
471 
472 
473     char	*buf;
474     int		copy;
475 
476 
477 {
478 
479 
480 /*
481  *
482  * Called from devcntrl() whenever an "x X DrawPath" command is read. It marks the
483  * end of the path started by the last "x X BeginPath" command and uses whatever
484  * has been passed along in *buf to manipulate the path (eg. fill and/or stroke
485  * the path). Once that's been done the drawing procedures are restored to their
486  * default behavior in which each drawing command is treated as an isolated path.
487  * The new version (called after "x X DrawPath") has copy set to FALSE, and calls
488  * parsebuf() to figure out what goes in the output file. It's a feeble attempt
489  * to free users and preprocessors (like pic) from having to know PostScript. The
490  * comments in parsebuf() describe what's handled.
491  *
492  * In the early version a path was started with "x X BeginObject" and ended with
493  * "x X EndObject". In both cases *buf was just copied to the output file, and
494  * was expected to be legitimate PostScript that manipulated the current path.
495  * The old escape sequence will be supported for a while (for Ravi), and always
496  * call this routine with copy set to TRUE.
497  *
498  *
499  */
500 
501 
502     if ( inpath == TRUE )  {
503 	if ( copy == TRUE )
504 	    fprintf(tf, "%s", buf);
505 	else parsebuf(buf);
506 	fprintf(tf, "grestore\n");
507 	fprintf(tf, "/inpath false def\n");
508 	reset();
509 	inpath = FALSE;
510     }	/* End if */
511 
512 }   /* End of drawpath */
513 
514 
515 /*****************************************************************************/
516 
517 
518 parsebuf(buf)
519 
520 
521     char	*buf;			/* whatever followed "x X DrawPath" */
522 
523 
524 {
525 
526 
527     char	*p;			/* usually the next token */
528     char	*p1;			/* for grabbing arguments */
529     char	*pend;			/* end of the original string (ie. *buf) */
530     int		gsavelevel = 0;		/* non-zero if we've done a gsave */
531 
532 /*
533  *
534  * Simple minded attempt at parsing the string that followed an "x X DrawPath"
535  * command. Everything not recognized here is simply ignored - there's absolutely
536  * no error checking and what was originally in buf is clobbered by strtok().
537  * A typical *buf might look like,
538  *
539  *	gray .9 fill stroke
540  *
541  * to fill the current path with a gray level of .9 and follow that by stroking the
542  * outline of the path. Since unrecognized tokens are ignored the last example
543  * could also be written as,
544  *
545  *	with gray .9 fill then stroke
546  *
547  * The "with" and "then" strings aren't recognized tokens and are simply discarded.
548  * The "stroke", "fill", and "wfill" force out appropriate PostScript code and are
549  * followed by a grestore. In otherwords changes to the grahics state (eg. a gray
550  * level or color) are reset to default values immediately after the stroke, fill,
551  * or wfill tokens. For now "fill" gets invokes PostScript's eofill operator and
552  * "wfill" calls fill (ie. the operator that uses the non-zero winding rule).
553  *
554  * The tokens that cause temporary changes to the graphics state are "gray" (for
555  * setting the gray level), "color" (for selecting a known color from the colordict
556  * dictionary defined in *colorfile), and "line" (for setting the line width). All
557  * three tokens can be extended since strncmp() makes the comparison. For example
558  * the strings "line" and "linewidth" accomplish the same thing. Colors are named
559  * (eg. "red"), but must be appropriately defined in *colorfile. For now all three
560  * tokens must be followed immediately by their single argument. The gray level
561  * (ie. the argument that follows "gray") should be a number between 0 and 1, with
562  * 0 for black and 1 for white.
563  *
564  * To pass straight PostScript through enclose the appropriate commands in double
565  * quotes. Straight PostScript is only bracketed by the outermost gsave/grestore
566  * pair (ie. the one from the initial "x X BeginPath") although that's probably
567  * a mistake. Suspect I may have to change the double quote delimiters.
568  *
569  */
570 
571 
572     pend = buf + strlen(buf);
573     p = strtok(buf, " \n");
574 
575     while ( p != NULL )  {
576 	if ( gsavelevel == 0 )  {
577 	    fprintf(tf, "gsave\n");
578 	    gsavelevel++;
579 	}   /* End if */
580 	if ( strcmp(p, "stroke") == 0 )  {
581 	    fprintf(tf, "closepath stroke\ngrestore\n");
582 	    gsavelevel--;
583 	} else if ( strcmp(p, "openstroke") == 0 )  {
584 	    fprintf(tf, "stroke\ngrestore\n");
585 	    gsavelevel--;
586 	} else if ( strcmp(p, "fill") == 0 )  {
587 	    fprintf(tf, "eofill\ngrestore\n");
588 	    gsavelevel--;
589 	} else if ( strcmp(p, "wfill") == 0 )  {
590 	    fprintf(tf, "fill\ngrestore\n");
591 	    gsavelevel--;
592 	} else if ( strcmp(p, "sfill") == 0 )  {
593 	    fprintf(tf, "eofill\ngrestore\ngsave\nstroke\ngrestore\n");
594 	    gsavelevel--;
595 	} else if ( strncmp(p, "gray", strlen("gray")) == 0 )  {
596 	    p1 = strtok(NULL, " \n");
597 	    fprintf(tf, "%s setgray\n", p1);
598 	} else if ( strncmp(p, "color", strlen("color")) == 0 )  {
599 	    p1 = strtok(NULL, " \n");
600 	    fprintf(tf, "/%s setcolor\n", p1);
601 	} else if ( strncmp(p, "line", strlen("line")) == 0 )  {
602 	    p1 = strtok(NULL, " \n");
603 	    fprintf(tf, "%s resolution mul 2 div setlinewidth\n", p1);
604 	} else if ( strncmp(p, "reverse", strlen("reverse")) == 0 )
605 	    fprintf(tf, "reversepath\n");
606 	else if ( *p == '"' )  {
607 	    for ( ; gsavelevel > 0; gsavelevel-- )
608 		fprintf(tf, "grestore\n");
609 	    if ( (p1 = p + strlen(p)) < pend )
610 		*p1 = ' ';
611 	    p = strtok(p, "\"\n");
612 	    fprintf(tf, "%s\n", p);
613 	}   /* End else */
614 	p = strtok(NULL, " \n");
615     }	/* End while */
616 
617     for ( ; gsavelevel > 0; gsavelevel-- )
618 	fprintf(tf, "grestore\n");
619 
620 }   /* End of parsebuf */
621 
622 
623 /*****************************************************************************/
624 
625 
626 getbaseline()
627 
628 
629 {
630 
631 
632 /*
633  *
634  * Responsible for making sure the PostScript procedures needed for printing text
635  * along an arbitrary baseline are downloaded from *baselinefile. Done at most
636  * once per job, and only if the the stuff is really used.
637  *
638  */
639 
640 
641     if ( gotbaseline == FALSE && access(baselinefile, 04) == 0 )
642 	doglobal(baselinefile);
643 
644     if ( tf == stdout )
645 	gotbaseline = TRUE;
646 
647 }   /* End of getbaseline */
648 
649 
650 /*****************************************************************************/
651 
652 
653 newbaseline(buf)
654 
655 
656     char	*buf;			/* whatever followed "x X NewBaseline" */
657 
658 
659 {
660 
661 
662     char	*p;			/* for eliminating white space etc. */
663 
664 
665 /*
666  *
667  * Called from devcntrl() whenever an "x X NewBaseline" command is recognized. We
668  * assume whatever is in *buf is a set of parametric equations that describe the
669  * new baseline. Equations for x(t), y(t), dx/dt, and dy/dt must be written in
670  * PostScript, bracketed by { and } characters, and supplied in exactly that order.
671  * In particular the equation for x must come first in *buf and it ends up as the
672  * last one on the stack, while the equation for dy/dt comes last (in *buf) and
673  * ends up on the top of the PostScript stack. For example if *buf is given by,
674  *
675  *	{} {180 mul 3.1416 div cos} {pop 1} {180 mul 3.1416 div sin neg}
676  *
677  * text will be printed along the curve y = cos(x).
678  *
679  * Angles given in radians must be converted to degrees for the PostScript trig
680  * functions, and things are scaled so that 1 unit maps into 1 inch. In the last
681  * example the cosine curve that describes the baseline has an amplitude of 1 inch.
682  * As another example of this rather confusing syntax if *buf is,
683  *
684  *	{} {} {pop 1} {pop 1}
685  *
686  * the baseline will be the 45 degree line y = x.
687  *
688  * When any of the four functions is used they're called with a single number on
689  * the stack that's equal to the current value of the parameter t. The coordinate
690  * system axes run parallel to the PostScript coordinate system that's currently
691  * being used.
692  *
693  */
694 
695 
696     for ( p = buf; *p; p++ )		/* eliminate trailing '\n' */
697 	if ( *p == '\n' )  {
698 	    *p = '\0';
699 	    break;
700 	}   /* End if */
701 
702     for ( p = buf; *p && (*p == ' ' || *p == ':'); p++ ) ;
703 
704     if ( *p != '\0' )  {		/* something's there */
705 	endtext();
706 	getbaseline();
707 	fprintf(tf, "mark resolution %s newbaseline\n", p);
708 	t_sf();
709 	resetpos();
710     }	/* End if */
711 
712 }   /* End of newbaseline */
713 
714 
715 /*****************************************************************************/
716 
717 
718 drawtext(buf)
719 
720 
721     char	*buf;			/* whatever followed "x X DrawText */
722 
723 
724 {
725 
726 
727     char	*p;			/* for eliminating white space etc. */
728 
729 
730 /*
731  *
732  * Called from devcntrl() whenever an "x X DrawText command is recognized. *buf
733  * should contain three arguments in the following order. First comes the text we
734  * want to print along the current baseline. Right now the string should be given
735  * as a PostScript string using characters '(' and ')' as the delimiters. Next in
736  * *buf comes a justification mode that can be the words left, right, or center.
737  * Last comes a number that represents the starting value of the parameter t that's
738  * given as the argument to the parametric equations that describe the current
739  * baseline. For example if *buf is given by,
740  *
741  *	(hello world) left .5
742  *
743  * hello world will be printed along the path described by the current baseline
744  * and left justified at whatever (x(.5), y(.5)) happens to be. Usually will be
745  * preceeded by an "x X NewBaseline" call that defines the current baseline. The
746  * origin of the coordinate system used by the parametric equations will be the
747  * current point.
748  *
749  */
750 
751 
752     for ( p = buf; *p; p++ )		/* eliminate trailing '\n' */
753 	if ( *p == '\n' )  {
754 	    *p = '\0';
755 	    break;
756 	}   /* End if */
757 
758     for ( p = buf; *p && (*p == ' ' || *p == ':'); p++ ) ;
759 
760     if ( *p != '\0' )  {		/* something's there */
761 	endtext();
762 	getbaseline();
763 	xymove(hpos, vpos);
764 	fprintf(tf, "mark %s drawfunnytext\n", p);
765 	resetpos();
766     }	/* End if */
767 
768 }   /* End of drawtext */
769 
770 
771 /*****************************************************************************/
772 
773 
774 settext(buf)
775 
776 
777     char	*buf;
778 
779 
780 {
781 
782 
783     char	*p;
784 
785 
786 /*
787  *
788  * Does whatever is needed to ensure any text that follows will be set along the
789  * curve described by the PostScript procedures listed in *buf. If *buf doesn't
790  * contain anything useful (eg. just a newline) things are restored to whatever
791  * they originally were. Doesn't work well if we try to start in the middle of a
792  * line of text.
793  *
794  * The parametric equations needed are,
795  *
796  *	x = f(t)
797  *	y = g(t)
798  *	dx/dt = f'(t)
799  *	dy/dt = g'(t)
800  *
801  * and must be given as proper PostScript procedures. The equation for x must come
802  * first (ie. it ends up on the bottom of the stack) and the equation for dy/dt
803  * must be given last (ie. it ends up on top of the stack). For example if *buf
804  * is given by,
805  *
806  *	{} {180 mul 3.1416 div cos} {pop 1} {180 mul 3.1416 div sin neg}
807  *
808  * text will be set along the curve y=cos(x).
809  *
810  */
811 
812 
813     endtext();
814     getbaseline();
815 
816     for ( p = buf; *p && *p == ' '; p++ ) ;
817 
818     if ( *p && *p != '\n' )  {
819 	encoding = maxencoding + 2;
820 	fprintf(tf, "mark resolution %s newbaseline\n", buf);
821     } else encoding = realencoding;
822 
823     fprintf(tf, "%d setdecoding\n", encoding);
824     resetpos();
825 
826 }   /* End of settext */
827 
828 
829 /*****************************************************************************/
830 
831