#ifndef lint static char rcsid[] = "$Header: imagen1.c,v 2.4 86/11/18 02:26:18 chris Exp $"; #endif /* * DVI to Imagen driver * * Reads DVI version 2 files and converts to imPRESS commands for spooling to * the Imagen (via ipr). * * TODO: * think about fonts with characters outside [0..127] */ #include #include "types.h" #include "conv.h" #include "dvi.h" #include "dviclass.h" #include "dvicodes.h" #include "fio.h" #include "font.h" #include "postamble.h" #include "search.h" #include "dmddev.h" #include "dmdcodes.h" char *ProgName; extern int errno; extern char *optarg; extern int optind; /* Globals */ char serrbuf[BUFSIZ]; /* buffer for stderr */ /* * DVI style arithmetic: when moving horizontally by a DVI distance >= * `space', we are to recompute horizontal position from DVI units; * otherwise, we are to use device resolution units to keep track of * horizontal position. A similar scheme must be used for vertical * positioning. */ struct fontinfo { struct font *f; /* the font */ i32 pspace; /* boundary between `small' & `large' spaces (for positive horizontal motion) */ i32 nspace; /* -4 * pspace, for negative motion */ i32 vspace; /* 5 * pspace, for vertical motion */ int family; /* DMD family number (we get one) */ #ifdef notyet int UseTime; /* cache info: flush fonts on LRU basis */ #endif }; /* * We use one of the per-glyph user flags to keep track of whether a * glyph has been loaded into the Imagen. */ #define GF_LOADED GF_USR0 /* * The exception that proves the rule is that hh and fromSP(dvi_h) are not * allowed to get more than MaxDrift units apart. */ int MaxDrift; /* the maximum allowable difference between hh and fromSP(dvi_h) */ struct fontinfo *CurrentFont; /* the current font */ int ExpectBOP; /* true => BOP ok */ int ExpectEOP; /* true => EOP ok */ int DPI; /* -d => device resolution (dots/inch) */ int PFlag = 1; /* -p => no page reversal */ int LFlag; /* -l => landscape mode (eventually...) */ int SFlag = 1; /* -s => silent (no page numbers) */ int Exflag; /* -x => exit emulator */ int Debug; /* -D => debug flag */ int XOffset; int YOffset; /* offsets for margins */ int hh; /* current horizontal position, in DEVs */ int vv; /* current vertical position, in DEVs */ /* * Similar to dvi_stack, but includes `hh' and `vv', which are usually * but not always the same as fromSP(h) and fromSP(v): */ struct localstack { int stack_hh; int stack_vv; struct dvi_stack stack_dvi; }; struct localstack *dvi_stack; /* base of stack */ struct localstack *dvi_stackp; /* current place in stack */ int HHMargin; /* horizontal margin (in DEVs) */ int VVMargin; /* vertical margin (in DEVs) */ long CurrentPagePointer; /* current page we are processing */ long PrevPagePointer; /* The previous page pointer from the DVI file. This allows us to read the file backwards, which obviates the need for page reversal (reversal is unsupported on the 8/300). */ int Numerator; /* numerator from DVI file */ int Denominator; /* denominator from DVI file */ int DVIMag; /* magnification from DVI file */ int UserMag; /* user-specified magnification */ int ImHH; /* Imagen horizontal position */ int ImVV; /* Imagen vertical position */ int ImFamily; /* Imagen current-font number */ char *PrintEngine; /* e.g., canon, ricoh */ struct search *FontFinder; /* maps from DVI index to internal fontinfo */ int FontErrors; /* true => error(s) occurred during font definitions from DVI postamble */ struct fontinfo NoFont; /* a fake font to help get things started */ char *getenv(), *malloc(); /* Absolute value */ #define ABS(n) ((n) >= 0 ? (n) : -(n)) /* Put a two-byte (word) value to the Imagen */ #define putword(w) (putchar((w) >> 8), putchar(w)) /* * Correct devpos (the actual device position) to be within MaxDrift pixels * of dvipos (the virtual DVI position). */ #define FIXDRIFT(devpos, dvipos) \ if ((devpos) < (dvipos)) \ if ((dvipos) - (devpos) <= MaxDrift) \ /* void */; \ else \ (devpos) = (dvipos) - MaxDrift; \ else \ if ((devpos) - (dvipos) <= MaxDrift) \ /* void */; \ else \ (devpos) = (dvipos) + MaxDrift SelectFont(n) i32 n; { int x = S_LOOKUP; if ((CurrentFont = (struct fontinfo *)SSearch(FontFinder, n, &x)) == 0) GripeNoSuchFont(n); } /* * Start a page (process a DVI_BOP). */ BeginPage() { register int *i; static int count[10]; /* the 10 counters */ static int beenhere; if (!ExpectBOP) GripeUnexpectedOp("BOP"); if (beenhere) { if (!SFlag) putc(' ', stderr); } else beenhere++; CurrentPagePointer = ftell(stdin) - 1; dvi_stackp = dvi_stack; ExpectBOP = 0; ExpectEOP++; /* set the new "expect" state */ for (i = count; i < &count[sizeof count / sizeof *count]; i++) fGetLong(stdin, *i); fGetLong(stdin, PrevPagePointer); if (!SFlag) { (void) fprintf(stderr, "[%d", count[0]); (void) fflush(stderr); } putchar(DMD_PAGE); ImHH = 0; ImVV = 0; hh = HHMargin; vv = VVMargin; dvi_h = toSP(hh); dvi_v = toSP(vv); dvi_w = 0; dvi_x = 0; dvi_y = 0; dvi_z = 0; } /* * End a page (process a DVI_EOP) */ EndPage() { int newpage; if (!ExpectEOP) GripeUnexpectedOp("EOP"); if (!SFlag) { putc(']', stderr); (void) fflush(stderr); } ExpectEOP = 0; ExpectBOP++; again: putchar(DMD_ENDPAGE); newpage = pagecmd(); switch (newpage) { case -1: if (PrevPagePointer != -1) fseek(stdin, PrevPagePointer, 0); else goto again; break; case 0: fseek(stdin, CurrentPagePointer, 0); break; case 1: default: break; } } /* * Store the relevant information from the DVI postamble, and set up * various internal things. */ PostAmbleHeader(p) register struct PostAmbleInfo *p; { register int n; PrevPagePointer = p->pai_PrevPagePointer; Numerator = p->pai_Numerator; Denominator = p->pai_Denominator; DVIMag = p->pai_DVIMag; /* * Set the conversion factor. This must be done before using * any fonts. */ SetConversion(DPI, UserMag, Numerator, Denominator, DVIMag); n = p->pai_DVIStackSize * sizeof *dvi_stack; dvi_stack = (struct localstack *) malloc((unsigned) n); if ((dvi_stackp = dvi_stack) == NULL) GripeOutOfMemory(n, "DVI stack"); } /* Handle one of the font definitions from the DVI postamble. */ PostAmbleFontDef(p) register struct PostAmbleFont *p; { register int i; register struct glyph *g; register struct fontinfo *fi; register struct font *f; register char *s; char *fname; int def = S_CREATE | S_EXCL; char loaded[16]; char *rindex(), *strsave(); fi = (struct fontinfo *) SSearch(FontFinder, p->paf_DVIFontIndex, &def); if (fi == NULL) { if (def & S_COLL) GripeFontAlreadyDefined(p->paf_DVIFontIndex); else error(1, 0, "can't stash font %ld (out of memory?)", p->paf_DVIFontIndex); /*NOTREACHED*/ } f = GetFont(p->paf_name, p->paf_DVIMag, p->paf_DVIDesignSize, PrintEngine, &fname); if ((fi->f = f) == NULL) { GripeCannotGetFont(p->paf_name, p->paf_DVIMag, p->paf_DVIDesignSize, PrintEngine, fname); FontErrors++; return; } if (Debug) { (void) fprintf(stderr, "[%s -> %s]\n", Font_TeXName(f), fname); (void) fflush(stderr); } /* match checksums, if not zero */ if (p->paf_DVIChecksum && f->f_checksum && p->paf_DVIChecksum != f->f_checksum) GripeDifferentChecksums(fname, p->paf_DVIChecksum, f->f_checksum); fi->pspace = p->paf_DVIMag / 6; /* a three-unit "thin space" */ fi->nspace = -4 * fi->pspace; fi->vspace = 5 * fi->pspace; putchar(DMD_MKFONT); if (s = rindex(fname, '/')) s++; else s = fname; while (*s) putchar(*s++); putchar(0); fi->family = inkbd(); if (fi->family == 255) error(1, 0, "out of space in remote dmd"); s = loaded; for (i = 0; i < sizeof loaded; i++) *s++ = inkbd(); #define bitset(a, i) (a[i>>3] & (1 << ((~i) & 07))) for (i = 0; i < 128; i++) { if (bitset(loaded, i)) { g = GLYPH(f, i); g->g_pixwidth = fromSP(g->g_tfmwidth); g->g_flags |= GF_LOADED; } } } /* Read the postamble. */ ReadPostAmble() { if ((FontFinder = SCreate(sizeof(struct fontinfo))) == 0) error(1, 0, "can't create FontFinder (out of memory?)"); ScanPostAmble(stdin, PostAmbleHeader, PostAmbleFontDef); if (FontErrors) GripeMissingFontsPreventOutput(FontErrors); } /* Read the preamble and do a few sanity checks */ ReadPreAmble() { register int n; rewind(stdin); if (GetByte(stdin) != Sign8(DVI_PRE)) GripeMissingOp("PRE"); if (GetByte(stdin) != Sign8(DVI_VERSION)) GripeMismatchedValue("version numbers"); if (GetLong(stdin) != Numerator) GripeMismatchedValue("numerator"); if (GetLong(stdin) != Denominator) GripeMismatchedValue("denominator"); if (GetLong(stdin) != DVIMag) GripeMismatchedValue("\\magfactor"); n = UnSign8(GetByte(stdin)); while (--n >= 0) (void) GetByte(stdin); } main(argc, argv) int argc; register char **argv; { register int c; char *inname; setbuf(stderr, serrbuf); ProgName = *argv; UserMag = 1000; MaxDrift = DefaultMaxDrift; DPI = DefaultDPI; inname = "stdin"; PrintEngine = "dmd"; while ((c = getopt(argc, argv, "d:e:lm:pr:sxDX:Y:")) != EOF) { switch (c) { case 'd': /* max drift value */ MaxDrift = atoi(optarg); break; case 'e': /* engine */ PrintEngine = optarg; break; case 'l': /* landscape mode */ LFlag++; break; case 'm': /* magnification */ UserMag = atoi(optarg); break; case 'p': /* no page reversal */ PFlag++; break; case 'r': /* resolution */ DPI = atoi(optarg); break; case 's': /* silent */ SFlag++; break; case 'x': /* Exit */ Exflag++; break; case 'D': Debug++; break; case 'X': /* x offset, in 1/10 inch increments */ XOffset = atoi(optarg); break; case 'Y': /* y offset */ YOffset = atoi(optarg); break; case '?': (void) fprintf(stderr, "\ Usage: %s [-d drift] [-m mag] [-x] [more options, see manual] [file]\n", ProgName); (void) fflush(stderr); exit(1); } } if (optind < argc) if (freopen(inname = argv[optind], "r", stdin) == NULL) error(1, errno, "can't open %s", inname); dmdstart(); if (isatty(fileno(stdin))) { putchar(Exflag ? DMD_EXIT : DMD_TERM); exit(0); } /* fontinit((char *) NULL); */ ReadPostAmble(); /* Margins -- needs work! */ HHMargin = DefaultLeftMargin + XOffset * DPI / 10; VVMargin = DefaultTopMargin + YOffset * DPI / 10; ReadPreAmble(); ExpectBOP++; if (!PFlag) (void) fseek(stdin, PrevPagePointer, 0); /* All set! */ /* * If the first command in the DVI file involves motion, we will need * to compare it to the current font `space' parameter; so start with * a fake current font of all zeros. */ CurrentFont = &NoFont; ReadDVIFile(); if (!SFlag) { (void) fprintf(stderr, "\n"); (void) fflush(stderr); } putchar(DMD_TERM); exit(0); } /* * Skip a font definition (since we are using those from the postamble) */ /*ARGSUSED*/ SkipFontDef(font) i32 font; { register int i; (void) GetLong(stdin); (void) GetLong(stdin); (void) GetLong(stdin); i = UnSign8(GetByte(stdin)) + UnSign8(GetByte(stdin)); while (--i >= 0) (void) GetByte(stdin); } /* * Draw a rule at the current (hh,vv) position. There are two 4 byte * parameters. The first is the height of the rule, and the second is the * width. (hh,vv) is the lower left corner of the rule. */ SetRule(advance) int advance; { register i32 h, w, rw; fGetLong(stdin, h); fGetLong(stdin, rw); h = ConvRule(h); w = ConvRule(rw); /* put the rule out */ if (ImHH != hh || ImVV != vv) ImSetPosition(hh, vv); putchar(DMD_RULE); putword(w); putword(h); if (advance) { hh += w; dvi_h += rw; w = fromSP(dvi_h); FIXDRIFT(hh, w); } } /* if anyone ever uses character codes > 127, this driver will need work */ char chartoobig[] = "Warning: character code %d too big for Imagen!"; /* * This rather large routine reads the DVI file and calls on other routines * to do anything moderately difficult (except put characters: there is * some ugly code with `goto's which makes things faster). */ ReadDVIFile() { register int c; register struct glyph *g; register struct font *f; register i32 p; int advance; ImFamily = -1; /* force DMD_SETFONT command */ /* * Only way out is via "return" statement. I had a `for (;;)' here, * but everything crawled off the right. */ loop: /* * Get the DVI byte, and switch on its parameter length and type. * Note that getchar() returns unsigned values. */ c = getchar(); /* * Handling characters (the most common case) early makes the * program run a bit faster. */ if (DVI_IsChar(c)) { advance = 1; do_char: f = CurrentFont->f; g = GLYPH(f, c); if (!GVALID(g)) { error(0, 0, "there is no character %d in %s", c, f->f_path); goto loop; } if ((g->g_flags & GF_LOADED) == 0) DownLoadGlyph(c, g); if (HASRASTER(g)) { /* workaround for Imagen bug */ /* BEGIN INLINE EXPANSION OF ImSetPosition */ register int delta; if (ImHH != hh) { delta = hh - ImHH; if (delta == 1) putchar(DMD_FORW); else if (delta == -1) putchar(DMD_BACK); else if (-128 <= delta && delta <= 127) { putchar(DMD_HREL); putchar(delta); } else { putchar(DMD_HABS); putword(hh); } ImHH = hh; } if (ImVV != vv) { delta = vv - ImVV; if (-128 <= delta && delta <= 127) { putchar(DMD_VREL); putchar(delta); } else { putchar(DMD_VABS); putword(vv); } ImVV = vv; } /* END INLINE EXPANSION OF ImSetPosition */ if (ImFamily != CurrentFont->family) { putchar(DMD_SETFONT); putchar(CurrentFont->family); ImFamily = CurrentFont->family; } putchar(c); ImHH += g->g_pixwidth; } if (advance) { hh += g->g_pixwidth; dvi_h += g->g_tfmwidth; p = fromSP(dvi_h); FIXDRIFT(hh, p); } goto loop; } switch (DVI_OpLen(c)) { case DPL_NONE: break; case DPL_SGN1: p = getchar(); p = Sign8(p); break; case DPL_SGN2: fGetWord(stdin, p); p = Sign16(p); break; case DPL_SGN3: fGet3Byte(stdin, p); p = Sign24(p); break; case DPL_SGN4: fGetLong(stdin, p); break; case DPL_UNS1: p = UnSign8(getchar()); break; case DPL_UNS2: fGetWord(stdin, p); p = UnSign16(p); break; case DPL_UNS3: fGet3Byte(stdin, p); p = UnSign24(p); break; default: panic("DVI_OpLen(%d) = %d", c, DVI_OpLen(c)); /* NOTREACHED */ } switch (DVI_DT(c)) { case DT_SET: advance = 1; c = p; if (c > 127) error(0, 0, chartoobig, c); goto do_char; case DT_PUT: advance = 0; c = p; if (c > 127) error(0, 0, chartoobig, c); goto do_char; case DT_SETRULE: SetRule(1); break; case DT_PUTRULE: SetRule(0); break; case DT_NOP: break; case DT_BOP: BeginPage(); break; case DT_EOP: EndPage(); break; case DT_PUSH: dvi_stackp->stack_hh = hh; dvi_stackp->stack_vv = vv; dvi_stackp->stack_dvi = dvi_current; dvi_stackp++; break; case DT_POP: dvi_stackp--; hh = dvi_stackp->stack_hh; vv = dvi_stackp->stack_vv; dvi_current = dvi_stackp->stack_dvi; break; case DT_W0: /* there should be a way to make these pretty */ p = dvi_w; goto move_right; case DT_W: dvi_w = p; goto move_right; case DT_X0: p = dvi_x; goto move_right; case DT_X: dvi_x = p; goto move_right; case DT_RIGHT: move_right: dvi_h += p; /* * DVItype tells us that we must round motions in this way: * `When the horizontal motion is small, like a kern, hh * changes by rounding the kern; but when the motion is * large, hh changes by rounding the true position so that * accumulated rounding errors disappear.' */ if (p >= CurrentFont->pspace || p <= CurrentFont->nspace) hh = fromSP(dvi_h); else { hh += fromSP(p); p = fromSP(dvi_h); FIXDRIFT(hh, p); } break; case DT_Y0: p = dvi_y; goto move_down; case DT_Y: dvi_y = p; goto move_down; case DT_Z0: p = dvi_z; goto move_down; case DT_Z: dvi_z = p; goto move_down; case DT_DOWN: move_down: dvi_v += p; /* * `Vertical motion is done similarly, but with the threshold * between ``small'' and ``large'' increased by a factor of * 5. The idea is to make fractions like $1\over2$ round * consistently, but to absorb accumulated rounding errors in * the baseline-skip moves.' */ if (ABS(p) >= CurrentFont->vspace) vv = fromSP(dvi_v); else { vv += fromSP(p); p = fromSP(dvi_v); FIXDRIFT(vv, p); } break; case DT_FNTNUM: SelectFont((i32) (c - DVI_FNTNUM0)); break; case DT_FNT: SelectFont(p); break; case DT_XXX: DoSpecial(p); break; case DT_FNTDEF: SkipFontDef(p); break; case DT_PRE: GripeUnexpectedOp("PRE"); /* NOTREACHED */ case DT_POST: if (PFlag) return; GripeUnexpectedOp("POST"); /* NOTREACHED */ case DT_POSTPOST: GripeUnexpectedOp("POSTPOST"); /* NOTREACHED */ case DT_UNDEF: GripeUndefinedOp(c); /* NOTREACHED */ default: panic("DVI_DT(%d) = %d", c, DVI_DT(c)); /* NOTREACHED */ } goto loop; } /* * Download the character c/g in the current font. */ DownLoadGlyph(c, g) int c; register struct glyph *g; { register char *p; register int i, j, w; g->g_pixwidth = fromSP(g->g_tfmwidth); g->g_flags |= GF_LOADED; if (!HASRASTER(g)) /* never load dull glyphs */ return; if (!LFlag) { w = 0; p = RASTER(g, CurrentFont->f, ROT_NORM); } else { w = 1 << 14; p = RASTER(g, CurrentFont->f, ROT_RIGHT); } w |= (CurrentFont->family << 7) | c; /* Define the character */ if (-128 <= g->g_pixwidth && g->g_pixwidth <= 127 && g->g_width <= 255 && g->g_height <= 255 && -128 <= g->g_xorigin && g->g_xorigin <= 127 && -128 <= g->g_yorigin && g->g_yorigin <= 127) { putchar(DMD_SGLYPH); /* a.k.a. SGLY */ putword(w); /* rotation, family, member */ putchar(g->g_pixwidth); /* advance */ putchar(g->g_width); /* width */ putchar(g->g_xorigin); /* left offset */ putchar(g->g_height); /* height */ putchar(g->g_yorigin); /* top-offset */ } else { putchar(DMD_BGLYPH); /* a.k.a. BGLY */ putword(w); /* rotation, family, member */ putchar(g->g_pixwidth); /* advance */ putchar(g->g_width); /* width */ putchar(g->g_xorigin); /* left offset */ putchar(g->g_height); /* height */ putchar(g->g_yorigin); /* top-offset */ } /* * Now put out the bitmap. */ w = (g->g_width + 7) >> 3; for (i = g->g_height; --i >= 0;) for (j = w; --j >= 0;) (void) putchar(*p++); if (g->g_raster) { /* XXX */ free(g->g_raster); g->g_raster = NULL; } } /* * Set the Imagen's h & v positions. It is currently at ImHH, ImVV. */ ImSetPosition(h, v) register int h, v; { register int delta; if (ImHH != h) { delta = h - ImHH; if (delta == 1) putchar(DMD_FORW); else if (delta == -1) putchar(DMD_BACK); else if (-128 <= delta && delta <= 127) { putchar(DMD_HREL); putchar(delta); } else { putchar(DMD_HABS); putword(h); } ImHH = h; } if (ImVV != v) { delta = v - ImVV; if (-128 <= delta && delta <= 127) { putchar(DMD_VREL); putchar(delta); } else { putchar(DMD_VABS); putword(v); } ImVV = v; } }