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