1 /* vsort.c 1.4 83/08/19 2 * 3 * Sorts and shuffles ditroff output for versatec wide printer. It 4 * puts pages side-by-side on the output, and fits as many as it can 5 * on one horizontal span. The versatec driver sees only pages of 6 * full width, not the individual pages. Output is sorted vertically 7 * and bands are created NLINES pixels high. Any object that has 8 * ANY part of it in a band is put on that band. 9 */ 10 11 12 #include <stdio.h> 13 #include <ctype.h> 14 #include <math.h> 15 16 17 #define DEBUGABLE /* compile-time flag for debugging */ 18 #define FATAL 1 19 #define NVLIST 3000 /* size of list of vertical spans */ 20 #define OBUFSIZ 250000 /* size of character buffer before sorting */ 21 #define SLOP 1000 /* extra bit of buffer to allow for passing OBUFSIZ */ 22 23 #define FONTDIR "/usr/lib/font" 24 #define INCH 200 /* assumed resolution of the printer (dots/inch) */ 25 #define POINT 72 /* number of points per inch */ 26 #define WIDTH 7040 /* number of pixels across the page */ 27 #define HALF (INCH/2) 28 #define BAND 3 29 #define NLINES (int)(BAND * INCH) /* number of pixels in each band */ 30 31 #define hgoto(n) if((hpos = leftmarg + n) > maxh) maxh = hpos 32 #define hmot(n) if((hpos += n) > maxh) maxh = hpos 33 #define vmot(n) vgoto(vpos + n) 34 35 36 #ifdef DEBUGABLE 37 int dbg = 0; /* debug flag != 0 means do debug output */ 38 #endif 39 40 int size = 10; /* current size (points) */ 41 int up = 0; /* number of pixels that the current size pushes up */ 42 int down = 0; /* # of pixels that the current size will hang down */ 43 int font = 1; /* current font */ 44 char * fontdir = FONTDIR; /* place to find DESC.out file */ 45 int thick = 3; /* line thickness */ 46 int style = -1; /* line style bit-mask */ 47 int hpos = 0; /* horizontal position to be at next (left = 0) */ 48 int vpos = 0; /* current vertical position (down positive) */ 49 50 int maxh = 0; /* farthest right we've gone on the current span */ 51 int leftmarg= 0; /* current page offset */ 52 int pageno = 0; /* number of pages on this horizontal span */ 53 int spanno = 0; /* current span number for driver in 'p#' commands */ 54 55 56 struct vlist { 57 short v; /* vertical position of this spread */ 58 short h; /* horizontal position */ 59 short t; /* line thickness */ 60 short st; /* style mask */ 61 short u; /* upper extent of height */ 62 short d; /* depth of height */ 63 char s; /* point size */ 64 char f; /* font number */ 65 char *p; /* text pointer to this spread */ 66 }; 67 68 struct vlist vlist[NVLIST + 1]; 69 struct vlist *vlp; /* current spread being added to */ 70 int nvlist = 0; /* number of spreads in list */ 71 int obufsiz = OBUFSIZ; 72 char obuf[OBUFSIZ + SLOP]; 73 char *op = obuf; /* pointer to current spot in buffer */ 74 75 76 main(argc, argv) 77 int argc; 78 char *argv[]; 79 { 80 FILE *fp; 81 82 83 vlp = &vlist[0] - 1; /* initialize pointer to one less */ 84 startspan(0); /* than beginning so "startspan" can */ 85 /* increment it before using it */ 86 while (argc > 1 && **++argv == '-') { 87 switch ((*argv)[1]) { 88 case 'f': 89 fontdir = &(*argv)[2]; 90 break; 91 #ifdef DEBUGABLE 92 case 'd': 93 dbg = atoi(&(*argv)[2]); 94 if (!dbg) dbg = 1; 95 break; 96 97 case 's': 98 if((obufsiz = atoi(&(*argv)[2])) > OBUFSIZ) 99 obufsiz = OBUFSIZ; 100 break; 101 #endif 102 } 103 argc--; 104 } 105 106 if (argc <= 1) 107 conv(stdin); 108 else 109 while (--argc > 0) { 110 if ((fp = fopen(*argv, "r")) == NULL) 111 error(FATAL, "can't open %s", *argv); 112 conv(fp); 113 fclose(fp); 114 } 115 done(); 116 } 117 118 /* read number from input: copy to output */ 119 int getnumber (fp) 120 register FILE *fp; 121 { 122 register int k; 123 register char c; 124 125 while ((c = getc(fp)) == ' ') 126 ; 127 k = 0; 128 do { 129 k = 10 * k + (*op++ = c) - '0'; 130 } while (isdigit(c = getc(fp))); 131 ungetc(c, fp); 132 return (k); 133 } 134 135 /* read number from input: do _N_O_T copy to output */ 136 int ngetnumber (fp) 137 register FILE *fp; 138 { 139 register int k; 140 register char c; 141 142 while ((c = getc(fp)) == ' ') 143 ; 144 k = 0; 145 do { 146 k = 10 * k + c - '0'; 147 } while (isdigit(c = getc(fp))); 148 ungetc(c, fp); 149 return (k); 150 } 151 152 153 conv(fp) 154 register FILE *fp; 155 { 156 register int c; 157 int m, n, m1, n1; 158 char buf[SLOP]; 159 160 while ((c = getc(fp)) != EOF) { 161 #ifdef DEBUGABLE 162 if (dbg > 2) fprintf(stderr, "%c i=%d V=%d\n", c, op-obuf, vpos); 163 #endif 164 if (op > obuf + obufsiz) 165 oflush(); 166 switch (c) { 167 case '\n': /* let text input through */ 168 case '\t': 169 case ' ': 170 *op++ = c; 171 break; 172 case '{': /* push down current environment */ 173 *op++ = c; 174 t_push(); 175 break; 176 case '}': /* pop up last environment */ 177 *op++ = c; 178 t_pop(); 179 break; 180 case '0': case '1': case '2': case '3': case '4': 181 case '5': case '6': case '7': case '8': case '9': 182 /* two motion digits plus a character */ 183 *op++ = c; 184 hmot((c-'0') * 10 + (*op++ = getc(fp)) - '0'); 185 *op++ = getc(fp); 186 setlimit(); 187 break; 188 case 'c': /* single ascii character */ 189 *op++ = c; 190 *op++ = getc(fp); 191 setlimit(); 192 break; 193 case 'C': /* white-space terminated funny character */ 194 *op++ = c; 195 while ((*op++ = c = getc(fp)) != ' ' && c != '\n') 196 ; 197 setlimit(); 198 break; 199 case 't': /* straight text */ 200 *op++ = c; 201 fgets(op, SLOP, fp); 202 op += strlen(op); 203 setlimit(); 204 break; 205 case 'D': /* draw function */ 206 fgets(buf, SLOP, fp); 207 switch (buf[0]) { 208 case 's': /* "style" */ 209 sscanf(buf+1, "%d", &style); 210 sprintf(op, "D%s", buf); 211 break; 212 case 't': /* thickness */ 213 sscanf(buf+1, "%d", &thick); 214 sprintf(op, "D%s", buf); 215 break; 216 case 'l': /* draw a line */ 217 sscanf(buf+1, "%d %d", &n, &m); 218 /* put line on its own spread */ 219 if (m < 0) { 220 startspan(vpos + m); 221 vlp->d = vpos - m; 222 } else { 223 startspan(vpos); 224 vlp->d = vpos + m; 225 } 226 sprintf(op, "V%dD%s", vpos, buf); 227 op += strlen(op); 228 hmot(n); 229 vmot(m); 230 break; 231 case 'c': /* circle */ 232 sscanf(buf+1, "%d", &n); /* put circle */ 233 startspan(vpos - n/2); /* on its own */ 234 vlp->d = vpos + n/2; /* spread */ 235 sprintf(op, "V%dD%s", vpos, buf); 236 op += strlen(op); 237 hmot(n); 238 startspan(vpos); 239 break; 240 case 'e': /* ellipse */ 241 sscanf(buf+1, "%d %d", &m, &n); /* same here */ 242 startspan(vpos - n/2); 243 vlp->d = vpos + n/2; 244 sprintf(op, "V%dD%s", vpos, buf); 245 op += strlen(op); 246 hmot(m); 247 startspan(vpos); 248 break; 249 case 'a': /* arc */ 250 sscanf(buf+1, "%d %d %d %d", &n, &m, &n1, &m1); 251 startspan(vpos); 252 arcbounds(n, m, n1, m1); 253 sprintf(op, "V%dD%s", vpos, buf); 254 op += strlen(op); 255 hmot(n + n1); 256 vmot(m + m1); 257 break; 258 case '~': /* wiggly line */ 259 case 'g': /* gremlin curve */ 260 { 261 register char *pop; 262 /* a curve goes on */ 263 startspan(vpos); /* its own span */ 264 sprintf(op, "V%dD", vpos); /* vertical move */ 265 pop = op += strlen(op); /* to curve start */ 266 do { /* read in rest of */ 267 sprintf(op, "%s", buf); /* point input */ 268 op += strlen(op); 269 if (*(op - 1) != '\n') 270 fgets(buf, SLOP, fp); 271 } while (*(op - 1) != '\n'); 272 m = n = vpos; /* = max/min vertical */ 273 /* position for curve */ 274 while (*++pop == ' '); /* skip '~' & blanks */ 275 do { /* calculate minimum */ 276 hpos += atoi(pop); /* vertical */ 277 while (isdigit(*++pop)); /* position */ 278 while (*++pop == ' '); 279 vpos += atoi(pop); 280 while (isdigit(*++pop)); 281 while (*pop == ' ') pop++; 282 if (vpos < n) n = vpos; 283 else if (vpos > m) m = vpos; 284 } while (*pop != '\n'); 285 286 vlp->u = vlp->v = n < 0 ? 0 : n; 287 vlp->d = m; 288 startspan(vpos); 289 } 290 break; 291 292 default: 293 error(FATAL,"unknown drawing command %s\n",buf); 294 break; 295 } 296 break; 297 case 's': 298 *op++ = c; 299 size = getnumber(fp); 300 up = (size * INCH) / POINT; 301 down = up / 3; 302 break; 303 case 'f': 304 *op++ = c; 305 font = getnumber(fp); 306 break; 307 case 'H': /* absolute horizontal motion */ 308 *op++ = c; 309 hgoto(ngetnumber(fp)); 310 sprintf(op, "%d", hpos); 311 op += strlen(op); /* reposition by page offset */ 312 break; 313 case 'h': /* relative horizontal motion */ 314 *op++ = c; 315 hmot(getnumber(fp)); 316 break; 317 case 'w': /* useless */ 318 break; 319 case 'V': /* absolute vertical motion */ 320 vgoto(ngetnumber(fp)); 321 break; 322 case 'v': 323 vmot(ngetnumber(fp)); 324 break; 325 case 'p': /* new page */ 326 t_page(ngetnumber(fp)); 327 vpos = 0; 328 break; 329 case 'n': /* end of line */ 330 hpos = leftmarg; 331 case '#': /* comment */ 332 case 'x': /* device control */ 333 *op++ = c; 334 while ((*op++ = getc(fp)) != '\n') 335 ; 336 break; 337 default: 338 error(!FATAL, "unknown input character %o %c\n", c, c); 339 done(); 340 } 341 } 342 } 343 344 /* set the "u" and "d" parts of the vlist given the current */ 345 setlimit() /* up and down limits set by the point size */ 346 { 347 register int upv = vpos - up; 348 register int downv = vpos + down; 349 350 if (upv < vlp->u) vlp->u = upv; 351 if (downv > vlp->d) vlp->d = downv; 352 } 353 354 355 arcbounds(h, v, h1, v1) /* make a circle out of the arc to estimate */ 356 int h, v, h1, v1; /* how far up/down the arc will span */ 357 { 358 register int center = vpos + v; 359 register int rad = ((int) sqrt ((double) (h*h + v*v))) >> 1; 360 /* set the vertical extents */ 361 vlp->v = vlp->u = (center - rad) < 0 ? 0 : center - rad; 362 vlp->d = center + rad; 363 } 364 365 366 oflush() /* sort, then dump out contents of obuf */ 367 { 368 register struct vlist *vp; 369 register int notdone; 370 register int topv; 371 register int botv; 372 register int i; 373 register char *p; 374 int compar(); 375 376 #ifdef DEBUGABLE 377 if (dbg) fprintf(stderr, "into oflush, V=%d\n", vpos); 378 #endif 379 if (op == obuf) 380 return; 381 qsort((char *) vlist, nvlist, sizeof (struct vlist), compar); 382 *op++ = 0; 383 384 topv = 0; 385 botv = NLINES - 1; 386 do { 387 notdone = 0; 388 vp = vlist; 389 #ifdef DEBUGABLE 390 if (dbg) fprintf(stderr, "topv=%d, botv=%d\n", topv, botv); 391 #endif 392 for (i = 0; i < nvlist; i++, vp++) { 393 #ifdef DEBUGABLE 394 if(dbg>1)fprintf(stderr,"u=%d, d=%d,%.60s\n",vp->u,vp->d,vp->p); 395 #endif 396 if (vp->u <= botv && vp->d >= topv) { 397 printf("V%dH%ds%df%dDs%d\nDt%d\n", 398 vp->v, vp->h, vp->s, vp->f, vp->st, vp->t); 399 for (p = vp->p; *p != 0; p++) putchar(*p); 400 } 401 notdone |= vp->d > botv; /* not done if there's still */ 402 } /* something to put lower */ 403 #ifdef DEBUGABLE 404 if (dbg) fprintf(stderr, "topv=%d, botv=%d\n", topv, botv); 405 #endif 406 if (notdone) putchar('P'); /* mark the end of the spread */ 407 topv += NLINES; /* unless it's the last one */ 408 botv += NLINES; 409 } while (notdone); 410 411 fflush(stdout); 412 vlp = vlist; 413 vlp->p = op = obuf; 414 vlp->h = hpos; 415 vlp->v = vpos; 416 vlp->u = vpos; 417 vlp->d = vpos; 418 vlp->s = size; 419 vlp->f = font; 420 vlp->st = style; 421 vlp->t = thick; 422 *op = 0; 423 nvlist = 1; 424 } 425 426 427 compar(p1, p2) 428 struct vlist *p1, *p2; 429 { 430 return(p1->v - p2->v); 431 } 432 433 done() 434 { 435 oflush(); 436 exit(0); 437 } 438 439 error(f, s, a1, a2, a3, a4, a5, a6, a7) { 440 fprintf(stderr, "vsort: "); 441 fprintf(stderr, s, a1, a2, a3, a4, a5, a6, a7); 442 fprintf(stderr, "\n"); 443 if (f) 444 done(); 445 } 446 447 #define MAXSTATE 5 448 449 struct state { 450 int ssize; 451 int sfont; 452 int shpos; 453 int svpos; 454 }; 455 struct state state[MAXSTATE]; 456 struct state *statep = state; 457 458 t_push() /* begin a new block */ 459 { 460 statep->ssize = size; 461 statep->sfont = font; 462 statep->shpos = hpos; 463 statep->svpos = vpos; 464 hpos = vpos = 0; 465 if (statep++ >= state+MAXSTATE) 466 error(FATAL, "{ nested too deep"); 467 hpos = vpos = 0; 468 } 469 470 t_pop() /* pop to previous state */ 471 { 472 if (--statep < state) 473 error(FATAL, "extra }"); 474 size = statep->ssize; 475 font = statep->sfont; 476 hpos = statep->shpos; 477 vpos = statep->svpos; 478 } 479 480 481 /* vertical motion: start new vertical span if necessary */ 482 vgoto(n) 483 register int n; 484 { 485 if (n != vpos) 486 startspan(n); 487 vpos = n; 488 } 489 490 491 t_page(n) 492 int n; 493 { 494 register int x; 495 496 pageno++; 497 if (maxh > (WIDTH - INCH) /* if we're close to the edge */ 498 || n == 1 /* or if i think we'll go over with this page */ 499 || leftmarg + leftmarg / pageno > (WIDTH - INCH)) { 500 oflush(); /* make it a REAL page-break */ 501 sprintf(op, "p%d\n", ++spanno); 502 op += strlen(op); 503 pageno = leftmarg = maxh = 0; 504 } else { /* x = last page's width */ 505 x = (maxh - leftmarg + (HALF - 1)) / HALF; /* (in half-inches) */ 506 if (x > 12 && x <= 17) 507 leftmarg += (8 * INCH) + HALF; /* if close to 8.5" */ 508 else /* then make it so */ 509 leftmarg = ((maxh + HALF) / HALF) * HALF; /* else set it to the */ 510 } /* nearest half-inch */ 511 } 512 513 514 startspan(n) 515 register int n; 516 { 517 *op++ = 0; 518 if (nvlist >= NVLIST) { 519 oflush(); 520 } 521 vlp++; 522 vlp->p = op; 523 vlp->v = n; 524 vlp->d = n; 525 vlp->u = n; 526 vlp->h = hpos; 527 vlp->s = size; 528 vlp->f = font; 529 vlp->st = style; 530 vlp->t = thick; 531 nvlist++; 532 } 533