/* dvi.c The dvi reader for dvi printers. * Copyright 1985 Massachusetts Institute of Technology. * Author: cjl@oz */ #include #include #include #include "fonts.h" #include "dev.h" #include "util.h" #include "dvi.h" extern char *rindex(); extern char *mktemp(); extern long ftell(); #define DVI_STACK_SIZE 512 /* Maximum dvi stack size supported. */ #define NFONTDIR_MAX 16 #define MAXDRIFT 2 #ifndef PXLDIR #define PXLDIR "/usr/lib/tex/pxlfonts" #endif /* Runtime parameters specified by user */ FILE *out = stdout; FILE *in; char *dirvec[NFONTDIR_MAX]; long start_page = -1000000; long end_page = 1000000; long usermag; int dirveclen; unsigned long devopts,fontopts; /* Runtime parameters specified by the dvi file & the user */ char pre_comment[257]; long numerator,denominator,half_denominator,dvinum,dviden,dvimag; long globalmag; /* Runtime data structures */ char stdoutbuf[BUFSIZ]; long h_pix,v_pix,h,v,w,x,y,z,device_dpi,fntspc; unsigned long fntvec[512],chrvec[512][4]; int stackptr,fntveclen; long hstack[DVI_STACK_SIZE]; long h_pixstack[DVI_STACK_SIZE]; long vstack[DVI_STACK_SIZE]; long v_pixstack[DVI_STACK_SIZE]; long wstack[DVI_STACK_SIZE]; long xstack[DVI_STACK_SIZE]; long ystack[DVI_STACK_SIZE]; long zstack[DVI_STACK_SIZE]; /* Stack manipulation: push and pop */ push() { if (stackptr >= DVI_STACK_SIZE) croak("dvi stack overflow"); hstack[stackptr] = h; h_pixstack[stackptr] = h_pix; vstack[stackptr] = v; v_pixstack[stackptr] = v_pix; wstack[stackptr] = w; xstack[stackptr] = x; ystack[stackptr] = y; zstack[stackptr] = z; stackptr++; } pop() { if (stackptr <= 0) croak("dvi stack underflow"); stackptr--; h = hstack[stackptr]; h_pix = h_pixstack[stackptr]; v = vstack[stackptr]; v_pix = v_pixstack[stackptr]; w = wstack[stackptr]; x = xstack[stackptr]; y = ystack[stackptr]; z = zstack[stackptr]; } position() { register int dh = h_pix - (h * numerator + half_denominator) / denominator; register int dv = v_pix - (v * numerator + half_denominator) / denominator; if (dh > MAXDRIFT) h_pix -= MAXDRIFT; else if (dh < -MAXDRIFT) h_pix += MAXDRIFT; if (dv > MAXDRIFT) v_pix -= MAXDRIFT; else if (dv < -MAXDRIFT) v_pix += MAXDRIFT; dev_position(h_pix,v_pix); } /* Drawing: set and setrule */ set(ch,movep) unsigned long ch; int movep; { long texwidth,devwidth; f_use_char(ch,&texwidth,&devwidth); position(); dev_setc(ch,devwidth); if (movep) { h += texwidth; h_pix += devwidth; } } setrule(movep) int movep; { long a = sget4(in); long b = sget4(in); long a_pix,b_pix; if (a > fntspc || a < -fntspc) a_pix = ((v + a) * numerator + half_denominator) / denominator - v_pix; else if (a < 0) a_pix = - ((-a * numerator + half_denominator) / denominator); else a_pix = (a * numerator + half_denominator) / denominator; if (b > fntspc || b < -fntspc) b_pix = ((h + b) * numerator + half_denominator) / denominator - h_pix; else if (b < 0) b_pix = - ((-b * numerator + half_denominator) / denominator); else b_pix = (b * numerator + half_denominator) / denominator; if (a_pix > 0 && b_pix > 0) { position(); dev_draw_box(a_pix,b_pix); } if (movep) { h += b; h_pix += b_pix; } } /* Movement: right and down */ right(dx) long dx; { h += dx; if (dx > fntspc || dx < -fntspc * 4) h_pix = (h * numerator + half_denominator) / denominator; else if (dx < 0) h_pix -= (-dx * numerator + half_denominator) / denominator; else h_pix += (dx * numerator + half_denominator) / denominator; } down(dy) long dy; { v += dy; if (dy > fntspc * 5) v_pix = (v * numerator + half_denominator) / denominator; else if (dy < 0) v_pix -= (-dy * numerator + half_denominator) / denominator; else v_pix += (dy * numerator + half_denominator) / denominator; } /* Special: xxx */ xxx(k) long k; { position(); dev_special(k,in); } /* Fonts: fntdef */ fntdef(fontnum) unsigned long fontnum; { unsigned long tfmchecksum = get4(in); long s = sget4(in); long d = sget4(in); int a = get1(in); int l = get1(in); int i; char area[257]; char name[257]; long mag; for (i = 0; i < a; i++) area[i] = getc(in); area[a] = 0; for (i = 0; i < l; i++) name[i] = getc(in); name[l] = 0; mag = s * globalmag / d; debug("fntdef: num= %d area=\"%s\" name=\"%s\" mag=%d s=%d\n", fontnum,area,name,mag,s); f_define_font(fontnum,0,area,name,mag,s,tfmchecksum); } /* Fonts: fntdef */ nullfntdef(fontnum) unsigned long fontnum; { register int a,l,i; (void) getc(in); (void) getc(in); (void) getc(in); (void) getc(in); (void) getc(in); (void) getc(in); (void) getc(in); (void) getc(in); (void) getc(in); (void) getc(in); (void) getc(in); (void) getc(in); a = getc(in); l = getc(in); for (i = 0; i < a; i++) (void) getc(in); for (i = 0; i < l; i++) (void) getc(in); } /* Record this font number on fntvec * We'll later pass fntvec to the fonts module */ int fntmark(fnt) unsigned long fnt; { register int i; for (i = 0; i < fntveclen; i++) if (fntvec[i] == fnt) return(i); if (fntveclen >= sizeof(fntvec)) croak("too many fonts"); chrvec[fntveclen][0] = 0; chrvec[fntveclen][1] = 0; chrvec[fntveclen][2] = 0; chrvec[fntveclen][3] = 0; fntvec[fntveclen] = fnt; return(fntveclen++); } chrmark(ch,i) unsigned long ch; int i; { chrvec[i][ch/32] |= (1 << (ch % 32)); } /* End of page processing: eop */ eop() { dev_eop(); } noprint_page(c,p) long c[10]; long p; { int f; register int ch; (void) c; (void) p; fntveclen = 0; for (;;) { if (ferror(in)) croak("noprint_page dvi input"); switch (ch = getc(in)) { case SETRULE: case PUTRULE: (void) getc(in); (void) getc(in); (void) getc(in); (void) getc(in); case RIGHT4: case W4: case DOWN4: case X4: case Y4: case Z4: (void) getc(in); case RIGHT3: case W3: case X3: case DOWN3: case Y3: case Z3: (void) getc(in); case RIGHT2: case W2: case X2: case DOWN2: case Y2: case Z2: (void) getc(in); case RIGHT1: case W1: case X1: case DOWN1: case Y1: case Z1: (void) getc(in); case NOP: case PUSH: case POP: case W0: case X0: case Y0: case Z0: break; case EOP: return; case SET1: case PUT1: chrmark(get1(in),f); break; case SET2: case PUT2: chrmark(get2(in),f); break; case SET3: case PUT3: chrmark(get3(in),f); break; case SET4: case PUT4: chrmark(get4(in),f); break; case FNT1: f = fntmark(get1(in)); break; case FNT2: f = fntmark(get2(in)); break; case FNT3: f = fntmark(get3(in)); break; case FNT4: f = fntmark(get4(in)); break; case FNTDEF1: nullfntdef(get1(in)); break; case FNTDEF2: nullfntdef(get2(in)); break; case FNTDEF3: nullfntdef(get3(in)); break; case FNTDEF4: nullfntdef(get4(in)); break; case XXX1: swallow(get1(in),in); break; case XXX2: swallow(get2(in),in); break; case XXX3: swallow(get3(in),in); break; case XXX4: swallow(get4(in),in); break; case EOF: croak("noprint_page EOF between BOP and EOP"); break; default: if (ch >= SETCHAR0 && ch <= SETCHAR127) chrmark((unsigned long) (ch - SETCHAR0),f); else if (ch >= FNTNUM0 && ch <= FNTNUM63) f = fntmark((unsigned long) (ch - FNTNUM0)); else croak("noprint_page dvi command %d between BOP and EOP",ch); break; } } } print_page(c,p) long c[10]; long p; { long ch; (void) p; stackptr = 0; h = v = w = x = y = z = h_pix = v_pix = 0; position(); for (;;) { if (ferror(in)) croak("print_page dvi input"); switch (ch = getc(in)) { case EOF: croak("print_page EOF between BOP and EOP"); break; case SET1: set(get1(in),1); break; case SET2: set(get2(in),1); break; case SET3: set(get3(in),1); break; case SET4: set(get4(in),1); break; case SETRULE: setrule(1); break; case PUT1: set(get1(in),0); break; case PUT2: set(get2(in),0); break; case PUT3: set(get3(in),0); break; case PUT4: set(get4(in),0); break; case PUTRULE: setrule(0); break; case NOP: break; case EOP: eop(); return; case PUSH: push(); break; case POP: pop(); break; case RIGHT1: right(sget1(in)); break; case RIGHT2: right(sget2(in)); break; case RIGHT3: right(sget3(in)); break; case RIGHT4: right(sget4(in)); break; case W0: right(w); break; case W1: right(w = sget1(in)); break; case W2: right(w = sget2(in)); break; case W3: right(w = sget3(in)); break; case W4: right(w = sget4(in)); break; case X0: right(x); break; case X1: right(x = sget1(in)); break; case X2: right(x = sget2(in)); break; case X3: right(x = sget3(in)); break; case X4: right(x = sget4(in)); break; case DOWN1: down(sget1(in)); break; case DOWN2: down(sget2(in)); break; case DOWN3: down(sget3(in)); break; case DOWN4: down(sget4(in)); break; case Y0: down(y); break; case Y1: down(y = sget1(in)); break; case Y2: down(y = sget2(in)); break; case Y3: down(y = sget3(in)); break; case Y4: down(y = sget4(in)); break; case Z0: down(z); break; case Z1: down(z = sget1(in)); break; case Z2: down(z = sget2(in)); break; case Z3: down(z = sget3(in)); break; case Z4: down(z = sget4(in)); break; case FNT1: f_use_font(get1(in),&fntspc); break; case FNT2: f_use_font(get2(in),&fntspc); break; case FNT3: f_use_font(get3(in),&fntspc); break; case FNT4: f_use_font(get4(in),&fntspc); break; case FNTDEF1: nullfntdef(get1(in)); break; case FNTDEF2: nullfntdef(get2(in)); break; case FNTDEF3: nullfntdef(get3(in)); break; case FNTDEF4: nullfntdef(get4(in)); break; case XXX1: xxx(get1(in)); break; case XXX2: xxx(get2(in)); break; case XXX3: xxx(get3(in)); break; case XXX4: xxx(get4(in)); break; default: if (ch >= SETCHAR0 && ch <= SETCHAR127) set((unsigned long) (ch - SETCHAR0),1); else if (ch >= FNTNUM0 && ch <= FNTNUM63) f_use_font((unsigned long) (ch - FNTNUM0),&fntspc); else croak("print_page dvi command %d between BOP and EOP",ch); break; } } } /* Preamble processing. */ computescale() { double dd; /* Compute scale factors for this file. */ if (usermag) globalmag = usermag; else globalmag = dvimag; /* This is real hokey, but will probably work for TEX82. */ if (dvinum != 25400000 || dviden != 473628672) { fprintf(stderr,"DVI file numerator and denominator weren't exactly "); fprintf(stderr,"what was expected.\nScaling may be wrong.\n"); } numerator = ((long) (((double) dvinum) / ((double) 254000) / ((double) 10))); denominator = ((long) (((double) dviden) / ((double) globalmag) * ((double) 1000) / ((double) device_dpi) / ((double) 10))); half_denominator = denominator / 2; debug("Computescale: globalmag=%d numerator=%d denominator=%d\n", globalmag,numerator,denominator); } preamble() { int i = getc(in); int k; dvinum = get4(in); dviden = get4(in); dvimag = get4(in); k = get1(in); if (i != 2) croak("id_byte=%d, probably not a dvi file",i); for (i = 0; i < k; i++) pre_comment[i] = getc(in); pre_comment[k] = '\0'; computescale(); debug("Preamble: dvinum=%d dviden=%d dvimag=%d usermag=%d\n", dvinum,dviden,dvimag,usermag); debug(" comment: \"%s\"\n",pre_comment); } /* Beginning of page processing */ bop() { int i; long c[10]; long p,fpos; for (i = 0; i < 10; i++) c[i] = sget4(in); p = sget4(in); if (start_page <= c[0] && c[0] <= end_page) { debug("first pass of page %d\n",c[0]); fpos = ftell(in); /* Remember where we are */ noprint_page(c,p); /* First pass noprint to get fntvec */ /* Tell font module about the fonts */ f_newpage(fntvec,chrvec,fntveclen); debug("second pass of page %d\n",c[0]); fseek(in,fpos,0); /* Back up */ print_page(c,p); /* And finally do the page */ debug("done with page %d\n",c[0]); } else noprint_page(c,p); } /* Postamble processing. */ find_postamble() { int ch; long i = -4; long q; do (void) fseek(in,i--,2); while ((ch = getc(in)) == TRAILER); if (ch != 2) croak("postamble id_byte=%d, probably not a dvi file",ch); (void) fseek(in,i-4,2); if ((ch = getc(in)) != POSTPOST) croak("no POSTPOST where expected"); q = sget4(in); (void) fseek(in,q,0); if ((ch = getc(in)) != POST) croak("no POST where expected"); } postamble() { int ch; long p = sget4(in); unsigned long l,u,maxstackdepth,npages; dvinum = get4(in); dviden = get4(in); dvimag = get4(in); computescale(); l = get4(in); u = get4(in); maxstackdepth = get2(in); npages = get2(in); debug("Postamble: dvinum=%d dviden=%d dvimag=%d maxstackdepth=%d\n", dvinum,dviden,dvimag,maxstackdepth); debug(" npages=%d\n",npages); if (maxstackdepth > DVI_STACK_SIZE) croak("dvi file has too much stack depth"); f_init(out,pgmnam,dirvec,dirveclen,numerator ,denominator,globalmag,fontopts); for (;;) { if (ferror(in)) croak("postamble input"); switch (ch = getc(in)) { case EOF: croak("EOF found before POSTPOST"); break; case FNTDEF1: fntdef(get1(in)); break; case FNTDEF2: fntdef(get2(in)); break; case FNTDEF3: fntdef(get3(in)); break; case FNTDEF4: fntdef(get4(in)); break; case NOP: break; case POSTPOST: return; default: croak("dvi command %d in postamble",ch); break; } } } /* Process a dvi file */ file_init() { int ch; if ((ch = getc(in)) != PRE) croak("first char %d not PRE, probably not a dvi file",ch); find_postamble(); dev_init(out,devopts,&device_dpi); open_ef(); postamble(); (void) fseek(in,1L,0); preamble(); } file_term() { f_term(); close_ef(); dev_term(); } dvi_file() { int ch; if (!dirveclen) dirvec[dirveclen++] = PXLDIR; file_init(); for (;;) { if (ferror(in)) croak("dvi_file input"); switch (ch = getc(in)) { case EOF: croak("EOF found between pages"); break; case BOP: bop(); break; case POST: file_term(); return; case FNTDEF1: fntdef(get1(in)); break; case FNTDEF2: fntdef(get2(in)); break; case FNTDEF3: fntdef(get3(in)); break; case FNTDEF4: fntdef(get4(in)); break; case NOP: break; default: croak("dvi command %d between pages",ch); break; } } } process_stdin() { int ch; FILE *tf; char tfn[257]; struct stat st; if (fstat(fileno(stdin),&st) < 0) croak("can't stat stdin"); if ((st.st_mode & S_IFMT) == S_IFREG) { in = stdin; infname = "stdin"; dvi_file(); in = NULL; infname = NULL; } else { /* Copy the dvi data to a temp file, since we have to do disk seeks. */ (void) sprintf(tfn,"/usr/tmp/%s.XXXXXX",pgmnam); if (!(tf = fopen(mktemp(tfn),"w"))) croak("couldn't open %s",tfn); unlink(tfn); while (!ferror(stdin) && (ch = getchar()) != EOF) putc(ch,tf); if (ferror(stdin)) croak("error on stdin"); (void) fseek(tf,0L,0); infname = tfn; in = tf; dvi_file(); in = NULL; infname = NULL; (void) fclose(tf); } } main(argc,argv) int argc; char *argv[]; { FILE *f; int nfiles = 0; int i; setbuf(stdout,stdoutbuf); pgmnam = argv[0]; fontopts = 0; devopts = 0; for (i = 1; i < argc; i++) if (argv[i][0] == '-') switch(argv[i][1]) { case '8': fontopts |= F_INIT_QMS800; break; case 'd': debugging++; break; case 'f': start_page = atoi((argv[i][2])? argv[i]+2 : argv[++i]); break; case 'h': host = argv[++i]; break; case 'l': devopts |= DEV_INIT_LANDSCAPE; fontopts |= F_INIT_LANDSCAPE; break; case 'm': usermag = atoi((argv[i][2])? argv[i]+2 : argv[++i]); break; case 'n': user = argv[++i]; break; case 'o': if (f = fopen(argv[++i],"w")) { if (out != stdout) (void) fclose(out); out = f; outfname = argv[i]; f = NULL; } break; case 't': end_page = atoi((argv[i][2])? argv[i]+2 : argv[++i]); break; case 'x': case 'y': break; default: croak("bad switch: %s",argv[i]); break; } else { nfiles++; if (f = fopen(argv[i],"r")) { in = f; infname = argv[i]; dvi_file(); (void) fclose(f); } else croak("couldn't open %s",argv[i]); } /* If I'm a lpd filter, see if I'm for a qms800 */ if (user && host && (out == stdout) && !nfiles && !access("qms800",0)) fontopts |= F_INIT_QMS800; if (!nfiles) process_stdin(); exit(0); }