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