draw.c 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342
  1. #include <u.h>
  2. #include <libc.h>
  3. #include <bio.h>
  4. #include <ctype.h>
  5. #include "../common/common.h"
  6. #include "tr2post.h"
  7. BOOLEAN drawflag = FALSE;
  8. BOOLEAN inpath = FALSE; /* TRUE if we're putting pieces together */
  9. void
  10. cover(double x, double y) {
  11. }
  12. void
  13. drawspline(Biobufhdr *Bp, int flag) { /* flag!=1 connect end points */
  14. int x[100], y[100];
  15. int i, N;
  16. /*
  17. *
  18. * Spline drawing routine for Postscript printers. The complicated stuff is
  19. * handled by procedure Ds, which should be defined in the library file. I've
  20. * seen wrong implementations of troff's spline drawing, so fo the record I'll
  21. * write down the parametric equations and the necessary conversions to Bezier
  22. * cubic splines (as used in Postscript).
  23. *
  24. *
  25. * Parametric equation (x coordinate only):
  26. *
  27. *
  28. * (x2 - 2 * x1 + x0) 2 (x0 + x1)
  29. * x = ------------------ * t + (x1 - x0) * t + ---------
  30. * 2 2
  31. *
  32. *
  33. * The coefficients in the Bezier cubic are,
  34. *
  35. *
  36. * A = 0
  37. * B = (x2 - 2 * x1 + x0) / 2
  38. * C = x1 - x0
  39. *
  40. *
  41. * while the current point is,
  42. *
  43. * current-point = (x0 + x1) / 2
  44. *
  45. * Using the relationships given in the Postscript manual (page 121) it's easy to
  46. * see that the control points are given by,
  47. *
  48. *
  49. * x0' = (x0 + 5 * x1) / 6
  50. * x1' = (x2 + 5 * x1) / 6
  51. * x2' = (x1 + x2) / 2
  52. *
  53. *
  54. * where the primed variables are the ones used by curveto. The calculations
  55. * shown above are done in procedure Ds using the coordinates set up in both
  56. * the x[] and y[] arrays.
  57. *
  58. * A simple test of whether your spline drawing is correct would be to use cip
  59. * to draw a spline and some tangent lines at appropriate points and then print
  60. * the file.
  61. *
  62. */
  63. for (N=2; N<sizeof(x)/sizeof(x[0]); N++)
  64. if (Bgetfield(Bp, 'd', &x[N], 0)<=0 || Bgetfield(Bp, 'd', &y[N], 0)<=0)
  65. break;
  66. x[0] = x[1] = hpos;
  67. y[0] = y[1] = vpos;
  68. for (i = 1; i < N; i++) {
  69. x[i+1] += x[i];
  70. y[i+1] += y[i];
  71. }
  72. x[N] = x[N-1];
  73. y[N] = y[N-1];
  74. for (i = ((flag!=1)?0:1); i < ((flag!=1)?N-1:N-2); i++) {
  75. endstring();
  76. if (pageon())
  77. 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]);
  78. /* if (dobbox == TRUE) { /* could be better */
  79. /* cover((double)(x[i] + x[i+1])/2,(double)-(y[i] + y[i+1])/2);
  80. /* cover((double)x[i+1], (double)-y[i+1]);
  81. /* cover((double)(x[i+1] + x[i+2])/2, (double)-(y[i+1] + y[i+2])/2);
  82. /* }
  83. */
  84. }
  85. hpos = x[N]; /* where troff expects to be */
  86. vpos = y[N];
  87. }
  88. void
  89. draw(Biobufhdr *Bp) {
  90. int r, x1, y1, x2, y2, i;
  91. int d1, d2;
  92. drawflag = TRUE;
  93. r = Bgetrune(Bp);
  94. switch(r) {
  95. case 'l':
  96. if (Bgetfield(Bp, 'd', &x1, 0)<=0 || Bgetfield(Bp, 'd', &y1, 0)<=0 || Bgetfield(Bp, 'r', &i, 0)<=0)
  97. error(FATAL, "draw line function, destination coordinates not found.\n");
  98. endstring();
  99. if (pageon())
  100. Bprint(Bstdout, "%d %d %d %d Dl\n", hpos, vpos, hpos+x1, vpos+y1);
  101. hpos += x1;
  102. vpos += y1;
  103. break;
  104. case 'c':
  105. if (Bgetfield(Bp, 'd', &d1, 0)<=0)
  106. error(FATAL, "draw circle function, diameter coordinates not found.\n");
  107. endstring();
  108. if (pageon())
  109. Bprint(Bstdout, "%d %d %d %d De\n", hpos, vpos, d1, d1);
  110. hpos += d1;
  111. break;
  112. case 'e':
  113. if (Bgetfield(Bp, 'd', &d1, 0)<=0 || Bgetfield(Bp, 'd', &d2, 0)<=0)
  114. error(FATAL, "draw ellipse function, diameter coordinates not found.\n");
  115. endstring();
  116. if (pageon())
  117. Bprint(Bstdout, "%d %d %d %d De\n", hpos, vpos, d1, d2);
  118. hpos += d1;
  119. break;
  120. case 'a':
  121. 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)
  122. error(FATAL, "draw arc function, coordinates not found.\n");
  123. endstring();
  124. if (pageon())
  125. Bprint(Bstdout, "%d %d %d %d %d %d Da\n", hpos, vpos, x1, y1, x2, y2);
  126. hpos += x1 + x2;
  127. vpos += y1 + y2;
  128. break;
  129. case 'q':
  130. drawspline(Bp, 1);
  131. break;
  132. case '~':
  133. drawspline(Bp, 2);
  134. break;
  135. default:
  136. error(FATAL, "unknown draw function <%c>\n", r);
  137. break;
  138. }
  139. }
  140. void
  141. beginpath(char *buf, int copy) {
  142. /*
  143. * Called from devcntrl() whenever an "x X BeginPath" command is read. It's used
  144. * to mark the start of a sequence of drawing commands that should be grouped
  145. * together and treated as a single path. By default the drawing procedures in
  146. * *drawfile treat each drawing command as a separate object, and usually start
  147. * with a newpath (just as a precaution) and end with a stroke. The newpath and
  148. * stroke isolate individual drawing commands and make it impossible to deal with
  149. * composite objects. "x X BeginPath" can be used to mark the start of drawing
  150. * commands that should be grouped together and treated as a single object, and
  151. * part of what's done here ensures that the PostScript drawing commands defined
  152. * in *drawfile skip the newpath and stroke, until after the next "x X DrawPath"
  153. * command. At that point the path that's been built up can be manipulated in
  154. * various ways (eg. filled and/or stroked with a different line width).
  155. *
  156. * Color selection is one of the options that's available in parsebuf(),
  157. * so if we get here we add *colorfile to the output file before doing
  158. * anything important.
  159. *
  160. */
  161. if (inpath == FALSE) {
  162. endstring();
  163. /* getdraw(); */
  164. /* getcolor(); */
  165. Bprint(Bstdout, "gsave\n");
  166. Bprint(Bstdout, "newpath\n");
  167. Bprint(Bstdout, "%d %d m\n", hpos, vpos);
  168. Bprint(Bstdout, "/inpath true def\n");
  169. if ( copy == TRUE )
  170. Bprint(Bstdout, "%s\n", buf);
  171. inpath = TRUE;
  172. }
  173. }
  174. static void parsebuf(char*);
  175. void
  176. drawpath(char *buf, int copy) {
  177. /*
  178. *
  179. * Called from devcntrl() whenever an "x X DrawPath" command is read. It marks the
  180. * end of the path started by the last "x X BeginPath" command and uses whatever
  181. * has been passed along in *buf to manipulate the path (eg. fill and/or stroke
  182. * the path). Once that's been done the drawing procedures are restored to their
  183. * default behavior in which each drawing command is treated as an isolated path.
  184. * The new version (called after "x X DrawPath") has copy set to FALSE, and calls
  185. * parsebuf() to figure out what goes in the output file. It's a feeble attempt
  186. * to free users and preprocessors (like pic) from having to know PostScript. The
  187. * comments in parsebuf() describe what's handled.
  188. *
  189. * In the early version a path was started with "x X BeginObject" and ended with
  190. * "x X EndObject". In both cases *buf was just copied to the output file, and
  191. * was expected to be legitimate PostScript that manipulated the current path.
  192. * The old escape sequence will be supported for a while (for Ravi), and always
  193. * call this routine with copy set to TRUE.
  194. *
  195. *
  196. */
  197. if ( inpath == TRUE ) {
  198. if ( copy == TRUE )
  199. Bprint(Bstdout, "%s\n", buf);
  200. else
  201. parsebuf(buf);
  202. Bprint(Bstdout, "grestore\n");
  203. Bprint(Bstdout, "/inpath false def\n");
  204. /* reset(); */
  205. inpath = FALSE;
  206. }
  207. }
  208. /*****************************************************************************/
  209. static void
  210. parsebuf(char *buf)
  211. {
  212. char *p; /* usually the next token */
  213. char *q;
  214. int gsavelevel = 0; /* non-zero if we've done a gsave */
  215. /*
  216. *
  217. * Simple minded attempt at parsing the string that followed an "x X DrawPath"
  218. * command. Everything not recognized here is simply ignored - there's absolutely
  219. * no error checking and what was originally in buf is clobbered by strtok().
  220. * A typical *buf might look like,
  221. *
  222. * gray .9 fill stroke
  223. *
  224. * to fill the current path with a gray level of .9 and follow that by stroking the
  225. * outline of the path. Since unrecognized tokens are ignored the last example
  226. * could also be written as,
  227. *
  228. * with gray .9 fill then stroke
  229. *
  230. * The "with" and "then" strings aren't recognized tokens and are simply discarded.
  231. * The "stroke", "fill", and "wfill" force out appropriate PostScript code and are
  232. * followed by a grestore. In otherwords changes to the grahics state (eg. a gray
  233. * level or color) are reset to default values immediately after the stroke, fill,
  234. * or wfill tokens. For now "fill" gets invokes PostScript's eofill operator and
  235. * "wfill" calls fill (ie. the operator that uses the non-zero winding rule).
  236. *
  237. * The tokens that cause temporary changes to the graphics state are "gray" (for
  238. * setting the gray level), "color" (for selecting a known color from the colordict
  239. * dictionary defined in *colorfile), and "line" (for setting the line width). All
  240. * three tokens can be extended since strncmp() makes the comparison. For example
  241. * the strings "line" and "linewidth" accomplish the same thing. Colors are named
  242. * (eg. "red"), but must be appropriately defined in *colorfile. For now all three
  243. * tokens must be followed immediately by their single argument. The gray level
  244. * (ie. the argument that follows "gray") should be a number between 0 and 1, with
  245. * 0 for black and 1 for white.
  246. *
  247. * To pass straight PostScript through enclose the appropriate commands in double
  248. * quotes. Straight PostScript is only bracketed by the outermost gsave/grestore
  249. * pair (ie. the one from the initial "x X BeginPath") although that's probably
  250. * a mistake. Suspect I may have to change the double quote delimiters.
  251. *
  252. */
  253. for( ; p != nil ; p = q ) {
  254. if( q = strchr(p, ' ') ) {
  255. *q++ = '\0';
  256. }
  257. if ( gsavelevel == 0 ) {
  258. Bprint(Bstdout, "gsave\n");
  259. gsavelevel++;
  260. }
  261. if ( strcmp(p, "stroke") == 0 ) {
  262. Bprint(Bstdout, "closepath stroke\ngrestore\n");
  263. gsavelevel--;
  264. } else if ( strcmp(p, "openstroke") == 0 ) {
  265. Bprint(Bstdout, "stroke\ngrestore\n");
  266. gsavelevel--;
  267. } else if ( strcmp(p, "fill") == 0 ) {
  268. Bprint(Bstdout, "eofill\ngrestore\n");
  269. gsavelevel--;
  270. } else if ( strcmp(p, "wfill") == 0 ) {
  271. Bprint(Bstdout, "fill\ngrestore\n");
  272. gsavelevel--;
  273. } else if ( strcmp(p, "sfill") == 0 ) {
  274. Bprint(Bstdout, "eofill\ngrestore\ngsave\nstroke\ngrestore\n");
  275. gsavelevel--;
  276. } else if ( strncmp(p, "gray", strlen("gray")) == 0 ) {
  277. if( q ) {
  278. p = q;
  279. if ( q = strchr(p, ' ') )
  280. *q++ = '\0';
  281. Bprint(Bstdout, "%s setgray\n", p);
  282. }
  283. } else if ( strncmp(p, "color", strlen("color")) == 0 ) {
  284. if( q ) {
  285. p = q;
  286. if ( q = strchr(p, ' ') )
  287. *q++ = '\0';
  288. Bprint(Bstdout, "/%s setcolor\n", p);
  289. }
  290. } else if ( strncmp(p, "line", strlen("line")) == 0 ) {
  291. if( q ) {
  292. p = q;
  293. if ( q = strchr(p, ' ') )
  294. *q++ = '\0';
  295. Bprint(Bstdout, "%s resolution mul 2 div setlinewidth\n", p);
  296. }
  297. } else if ( strncmp(p, "reverse", strlen("reverse")) == 0 )
  298. Bprint(Bstdout, "reversepath\n");
  299. else if ( *p == '"' ) {
  300. for ( ; gsavelevel > 0; gsavelevel-- )
  301. Bprint(Bstdout, "grestore\n");
  302. if ( q != nil )
  303. *--q = ' ';
  304. if ( (q = strchr(p, '"')) != nil ) {
  305. *q++ = '\0';
  306. Bprint(Bstdout, "%s\n", p);
  307. }
  308. }
  309. }
  310. for ( ; gsavelevel > 0; gsavelevel-- )
  311. Bprint(Bstdout, "grestore\n");
  312. }