xref: /plan9/sys/src/cmd/proof/htroff.c (revision 7dd7cddf99dd7472612f1413b4da293630e6b1bc)
1 #include	<u.h>
2 #include	<libc.h>
3 #include	<draw.h>
4 #include	<cursor.h>
5 #include	<event.h>
6 #include	<bio.h>
7 #include	"proof.h"
8 
9 int	res;
10 int	hpos;
11 int	vpos;
12 int	DIV = 11;
13 
14 Point offset;
15 Point xyoffset = { 0,0 };
16 
17 Rectangle	view[MAXVIEW];
18 Rectangle	bound[MAXVIEW];		/* extreme points */
19 int	nview = 1;
20 
21 int	lastp;	/* last page number we were on */
22 
23 #define	NPAGENUMS	200
24 struct pagenum {
25 	int	num;
26 	long	adr;
27 } pagenums[NPAGENUMS];
28 int	npagenums;
29 
30 int	curfont, cursize;
31 
32 char	*getcmdstr(void);
33 
34 static void	initpage(void);
35 static void	view_setup(int);
36 static Point	scale(Point);
37 static void	clearview(Rectangle);
38 static int	addpage(int);
39 static void	spline(Image *, int, Point *);
40 static int	skipto(int, int);
41 static void	wiggly(int);
42 static void	devcntrl(void);
43 static void	eatline(void);
44 static int	getn(void);
45 static int	botpage(int);
46 static void	getstr(char *);
47 static void	getutf(char *);
48 
49 #define Do screen->r.min
50 #define Dc screen->r.max
51 
52 /* declarations and definitions of font stuff are in font.c and main.c */
53 
54 static void
initpage(void)55 initpage(void)
56 {
57 	int i;
58 
59 	view_setup(nview);
60 	for (i = 0; i < nview-1; i++)
61 		draw(screen, view[i], screen, nil, view[i+1].min);
62 	clearview(view[nview-1]);
63 	offset = view[nview-1].min;
64 	vpos = 0;
65 }
66 
67 static void
view_setup(int n)68 view_setup(int n)
69 {
70 	int i, j, v, dx, dy, r, c;
71 
72 	switch (n) {
73 	case 1: r = 1; c = 1; break;
74 	case 2: r = 1; c = 2; break;
75 	case 3: r = 1; c = 3; break;
76 	case 4: r = 2; c = 2; break;
77 	case 5: case 6: r = 2; c = 3; break;
78 	case 7: case 8: case 9: r = 3; c = 3; break;
79 	default: r = (n+2)/3; c = 3; break; /* finking out */
80 	}
81 	dx = (Dc.x - Do.x) / c;
82 	dy = (Dc.y - Do.y) / r;
83 	v = 0;
84 	for (i = 0; i < r && v < n; i++)
85 		for (j = 0; j < c && v < n; j++) {
86 			view[v] = screen->r;
87 			view[v].min.x = Do.x + j * dx;
88 			view[v].max.x = Do.x + (j+1) * dx;
89 			view[v].min.y = Do.y + i * dy;
90 			view[v].max.y = Do.y + (i+1) * dy;
91 			v++;
92 		}
93 }
94 
95 static void
clearview(Rectangle r)96 clearview(Rectangle r)
97 {
98 	draw(screen, r, display->white, nil, r.min);
99 }
100 
101 int resized;
eresized(int new)102 void eresized(int new)
103 {
104 	/* this is called if we are resized */
105 	if(new && getwindow(display, Refnone) < 0)
106 		drawerror(display, "can't reattach to window");
107 	initpage();
108 	resized = 1;
109 }
110 
111 static Point
scale(Point p)112 scale(Point p)
113 {
114 	p.x /= DIV;
115 	p.y /= DIV;
116 	return addpt(xyoffset, addpt(offset,p));
117 }
118 
119 static int
addpage(int n)120 addpage(int n)
121 {
122 	int i;
123 
124 	for (i = 0; i < npagenums; i++)
125 		if (n == pagenums[i].num)
126 			return i;
127 	if (npagenums < NPAGENUMS-1) {
128 		pagenums[npagenums].num = n;
129 		pagenums[npagenums].adr = offsetc();
130 		npagenums++;
131 	}
132 	return npagenums;
133 }
134 
135 void
readpage(void)136 readpage(void)
137 {
138 	int c, i, a, alpha, phi;
139 	static int first = 0;
140 	int m, n, gonow = 1;
141 	Rune r[32], t;
142 	Point p,q,qq;
143 
144 	offset = screen->clipr.min;
145 	esetcursor(&deadmouse);
146 	while (gonow)
147 	{
148 		c = getc();
149 		switch (c)
150 		{
151 		case -1:
152 			esetcursor(0);
153 			if (botpage(lastp+1)) {
154 				initpage();
155 				break;
156 			}
157 			exits(0);
158 		case 'p':	/* new page */
159 			lastp = getn();
160 			addpage(lastp);
161 			if (first++ > 0) {
162 				esetcursor(0);
163 				botpage(lastp);
164 				esetcursor(&deadmouse);
165 			}
166 			initpage();
167 			break;
168 		case '\n':	/* when input is text */
169 		case ' ':
170 		case 0:		/* occasional noise creeps in */
171 			break;
172 		case '0': case '1': case '2': case '3': case '4':
173 		case '5': case '6': case '7': case '8': case '9':
174 			/* two motion digits plus a character */
175 			hpos += (c-'0')*10 + getc()-'0';
176 
177 		/* FALLS THROUGH */
178 		case 'c':	/* single ascii character */
179 			r[0] = getrune();
180 			r[1] = 0;
181 			dochar(r);
182 			break;
183 
184 		case 'C':
185 			for(i=0; ; i++){
186 				t = getrune();
187 				if(isspace(t))
188 					break;
189 				r[i] = t;
190 			}
191 			r[i] = 0;
192 			dochar(r);
193 			break;
194 
195 		case 'N':
196 			r[0] = getn();
197 			r[1] = 0;
198 			dochar(r);
199 			break;
200 
201 		case 'D':	/* draw function */
202 			switch (getc())
203 			{
204 			case 'l':	/* draw a line */
205 				n = getn();
206 				m = getn();
207 				p = Pt(hpos,vpos);
208 				q = addpt(p, Pt(n,m));
209 				hpos += n;
210 				vpos += m;
211 				line(screen, scale(p), scale(q), 0, 0, 0, display->black, ZP);
212 				break;
213 			case 'c':	/* circle */
214 				/*nop*/
215 				m = getn()/2;
216 				p = Pt(hpos+m,vpos);
217 				hpos += 2*m;
218 				ellipse(screen, scale(p), m/DIV, m/DIV, 0, display->black, ZP);
219 				/* p=currentpt; p.x+=dmap(m/2);circle bp,p,a,ONES,Mode*/
220 				break;
221 			case 'e':	/* ellipse */
222 				/*nop*/
223 				m = getn()/2;
224 				n = getn()/2;
225 				p = Pt(hpos+m,vpos);
226 				hpos += 2*m;
227 				ellipse(screen, scale(p), m/DIV, n/DIV, 0, display->black, ZP);
228 				break;
229 			case 'a':	/* arc */
230 				p = scale(Pt(hpos,vpos));
231 				n = getn();
232 				m = getn();
233 				hpos += n;
234 				vpos += m;
235 				q = scale(Pt(hpos,vpos));
236 				n = getn();
237 				m = getn();
238 				hpos += n;
239 				vpos += m;
240 				qq = scale(Pt(hpos,vpos));
241 				/*
242 				  * tricky: convert from 3-point clockwise to
243 				  * center, angle1, delta-angle counterclockwise.
244 				 */
245 				a = hypot(qq.x-q.x, qq.y-q.y);
246 				phi = atan2(q.y-p.y, p.x-q.x)*180./PI;
247 				alpha = atan2(q.y-qq.y, qq.x-q.x)*180./PI - phi;
248 				if(alpha < 0)
249 					alpha += 360;
250 				arc(screen, q, a, a, 0, display->black, ZP, phi, alpha);
251 				break;
252 			case '~':	/* wiggly line */
253 				wiggly(0);
254 				break;
255 			default:
256 				break;
257 			}
258 			eatline();
259 			break;
260 		case 's':
261 			n = getn();	/* ignore fractional sizes */
262 			if (cursize == n)
263 				break;
264 			cursize = n;
265 			if (cursize >= NFONT)
266 				cursize = NFONT-1;
267 			break;
268 		case 'f':
269 			curfont = getn();
270 			break;
271 		case 'H':	/* absolute horizontal motion */
272 			hpos = getn();
273 			break;
274 		case 'h':	/* relative horizontal motion */
275 			hpos += getn();
276 			break;
277 		case 'w':	/* word space */
278 			break;
279 		case 'V':
280 			vpos = getn();
281 			break;
282 		case 'v':
283 			vpos += getn();
284 			break;
285 		case '#':	/* comment */
286 		case 'n':	/* end of line */
287 			eatline();
288 			break;
289 		case 'x':	/* device control */
290 			devcntrl();
291 			break;
292 		default:
293 			fprint(2, "unknown input character %o %c at offset %lud\n", c, c, offsetc());
294 			exits("bad char");
295 		}
296 	}
297 	esetcursor(0);
298 }
299 
300 static void
spline(Image * b,int n,Point * pp)301 spline(Image *b, int n, Point *pp)
302 {
303 	long w, t1, t2, t3, fac=1000;
304 	int i, j, steps=10;
305 	Point p, q;
306 
307 	for (i = n; i > 0; i--)
308 		pp[i] = pp[i-1];
309 	pp[n+1] = pp[n];
310 	n += 2;
311 	p = pp[0];
312 	for(i = 0; i < n-2; i++)
313 	{
314 		for(j = 0; j < steps; j++)
315 		{
316 			w = fac * j / steps;
317 			t1 = w * w / (2 * fac);
318 			w = w - fac/2;
319 			t2 = 3*fac/4 - w * w / fac;
320 			w = w - fac/2;
321 			t3 = w * w / (2*fac);
322 			q.x = (t1*pp[i+2].x + t2*pp[i+1].x +
323 				t3*pp[i].x + fac/2) / fac;
324 			q.y = (t1*pp[i+2].y + t2*pp[i+1].y +
325 				t3*pp[i].y + fac/2) / fac;
326 			line(b, p, q, 0, 0, 0, display->black, ZP);
327 			p = q;
328 		}
329 	}
330 }
331 
332 /* Have to parse skipped pages, to find out what fonts are loaded. */
333 static int
skipto(int gotop,int curp)334 skipto(int gotop, int curp)
335 {
336 	char *p;
337 	int i;
338 
339 	if (gotop == curp)
340 		return 1;
341 	for (i = 0; i < npagenums; i++)
342 		if (pagenums[i].num == gotop) {
343 			if (seekc(pagenums[i].adr) == Beof) {
344 				fprint(2, "can't rewind input\n");
345 				return 0;
346 			}
347 			return 1;
348 		}
349 	if (gotop <= curp) {
350 	    restart:
351 		if (seekc(0) == Beof) {
352 			fprint(2, "can't rewind input\n");
353 			return 0;
354 		}
355 	}
356 	for(;;){
357 		p = rdlinec();
358 		if (p == 0) {
359 			if(gotop>curp){
360 				gotop = curp;
361 				goto restart;
362 			}
363 			return 0;
364 		} else if (*p == 'p') {
365 			lastp = curp = atoi(p+1);
366 			addpage(lastp);	/* maybe 1 too high */
367 			if (curp>=gotop)
368 				return 1;
369 		}
370 	}
371 }
372 
373 static void
wiggly(int skip)374 wiggly(int skip)
375 {
376 	Point p[300];
377 	int c,i,n;
378 	for (n = 1; (c = getc()) != '\n' && c>=0; n++) {
379 		ungetc();
380 		p[n].x = getn();
381 		p[n].y = getn();
382 	}
383 	p[0] = Pt(hpos, vpos);
384 	for (i = 1; i < n; i++)
385 		p[i] = addpt(p[i],p[i-1]);
386 	hpos = p[n-1].x;
387 	vpos = p[n-1].y;
388 	for (i = 0; i < n; i++)
389 		p[i] = scale(p[i]);
390 	if (!skip)
391 		spline(screen,n,p);
392 }
393 
394 static void
devcntrl(void)395 devcntrl(void)	/* interpret device control functions */
396 {
397         char str[80];
398 	int n;
399 
400 	getstr(str);
401 	switch (str[0]) {	/* crude for now */
402 	case 'i':	/* initialize */
403 		break;
404 	case 'T':	/* device name */
405 		getstr(devname);
406 		break;
407 	case 't':	/* trailer */
408 		break;
409 	case 'p':	/* pause -- can restart */
410 		break;
411 	case 's':	/* stop */
412 		break;
413 	case 'r':	/* resolution assumed when prepared */
414 		res=getn();
415 		DIV = floor(.5 + res/(100.0*mag));
416 		if (DIV < 1)
417 			DIV = 1;
418 		mag = res/(100.0*DIV); /* adjust mag according to DIV coarseness */
419 		break;
420 	case 'f':	/* font used */
421 		n = getn();
422 		getstr(str);
423 		loadfontname(n, str);
424 		break;
425 	/* these don't belong here... */
426 	case 'H':	/* char height */
427 		break;
428 	case 'S':	/* slant */
429 		break;
430 	case 'X':
431 		break;
432 	}
433 	eatline();
434 }
435 
436 int
isspace(int c)437 isspace(int c)
438 {
439 	return c==' ' || c=='\t' || c=='\n';
440 }
441 
442 static void
getstr(char * is)443 getstr(char *is)
444 {
445 	uchar *s = (uchar *) is;
446 
447 	for (*s = getc(); isspace(*s); *s = getc())
448 		;
449 	for (; !isspace(*s); *++s = getc())
450 		;
451 	ungetc();
452 	*s = 0;
453 }
454 
455 static void
getutf(char * s)456 getutf(char *s)		/* get next utf char, as bytes */
457 {
458 	int c, i;
459 
460 	for (i=0;;) {
461 		c = getc();
462 		if (c < 0)
463 			return;
464 		s[i++] = c;
465 
466 		if (fullrune(s, i)) {
467 			s[i] = 0;
468 			return;
469 		}
470 	}
471 }
472 
473 static void
eatline(void)474 eatline(void)
475 {
476 	int c;
477 
478 	while ((c=getc()) != '\n' && c >= 0)
479 		;
480 }
481 
482 static int
getn(void)483 getn(void)
484 {
485 	int n, c, sign;
486 
487 	while (c = getc())
488 		if (!isspace(c))
489 			break;
490 	if(c == '-'){
491 		sign = -1;
492 		c = getc();
493 	}else
494 		sign = 1;
495 	for (n = 0; '0'<=c && c<='9'; c = getc())
496 		n = n*10 + c - '0';
497 	while (c == ' ')
498 		c = getc();
499 	ungetc();
500 	return(n*sign);
501 }
502 
503 static int
botpage(int np)504 botpage(int np)	/* called at bottom of page np-1 == top of page np */
505 {
506 	char *p;
507 	int n;
508 
509 	while (p = getcmdstr()) {
510 		if (*p == '\0')
511 			return 0;
512 		if (*p == 'q')
513 			exits(p);
514 		if (*p == 'c')		/* nop */
515 			continue;
516 		if (*p == 'm') {
517 			mag = atof(p+1);
518 			if (mag <= .1 || mag >= 10)
519 				mag = DEFMAG;
520 			allfree();	/* zap fonts */
521 			DIV = floor(.5 + res/(100.0*mag));
522 			if (DIV < 1)
523 				DIV = 1;
524 			mag = res/(100.0*DIV);
525 			return skipto(np-1, np);	/* reprint the page */
526 		}
527 		if (*p == 'x') {
528 			xyoffset.x += atoi(p+1)*100;
529 			skipto(np-1, np);
530 			return 1;
531 		}
532 		if (*p == 'y') {
533 			xyoffset.y += atoi(p+1)*100;
534 			skipto(np-1, np);
535 			return 1;
536 		}
537 		if (*p == '/') {	/* divide into n pieces */
538 			nview = atoi(p+1);
539 			if (nview < 1)
540 				nview = 1;
541 			else if (nview > MAXVIEW)
542 				nview = MAXVIEW;
543 			return skipto(np-1, np);
544 		}
545 		if (*p == 'p') {
546 			if (p[1] == '\0'){	/* bare 'p' */
547 				if(skipto(np-1, np))
548 					return 1;
549 				continue;
550 			}
551 			p++;
552 		}
553 		if ('0'<=*p && *p<='9') {
554 			n = atoi(p);
555 			if(skipto(n, np))
556 				return 1;
557 			continue;
558 		}
559 		if (*p == '-' || *p == '+') {
560 			n = atoi(p);
561 			if (n == 0)
562 				n = *p == '-' ? -1 : 1;
563 			if(skipto(np - 1 + n, np))
564 				return 1;
565 			continue;
566 		}
567 		if (*p == 'd') {
568 			dbg = 1 - dbg;
569 			continue;
570 		}
571 
572 		fprint(2, "illegal;  try q, 17, +2, -1, p, m.7, /2, x1, y-.5 or return\n");
573 	}
574 	return 0;
575 }
576