/* * Copyright (c) 1987 University of Maryland Department of Computer Science. * All rights reserved. Permission to copy for any purpose is hereby granted * so long as this copyright notice remains intact. */ #ifndef lint static char rcsid[] = "$Header$"; #endif /* * DVI to Interpress driver * * Reads DVI version 2 files and converts to Xerox Interpress format. */ #include "types.h" #include "dvi.h" #include "dviclass.h" #include "dvicodes.h" #include "fio.h" #include "pxl.h" #include "search.h" #include "interpress.h" #include 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 pxltail *px; /* pxl file info */ int ipfont; /* Interpress font index */ 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 cwidth[128]; /* width (in DEVs) of each char */ char cload[128]; /* flag for ``char loaded into Imagen'' */ }; int MaxDrift; /* the maximum allowable difference between hh and SPtoDEV(dvi_h), and vv and.... */ struct search *FontFinder; /* search table for DVI index => fontinfo */ struct fontinfo *CurrentFont; /* the current font (if any) */ int NextIPFont; /* during font definition, the next ip font index */ int FontErrors; /* true => error(s) during font definition */ char *TeXfonts; /* getenv("TEXFONTS") */ int ExpectBOP; /* true => BOP ok */ int ExpectEOP; /* true => EOP ok */ int DPI; /* -d => device resolution (dots/inch) */ /* int LFlag; /* -l => landscape mode (eventually...) */ int SFlag; /* -s => silent (no page #s) */ int XFlag; /* -x => debug (undocumented) */ 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 SPtoDEV(h) and SPtoDEV(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) */ int Numerator; /* numerator from DVI file */ int Denominator; /* denominator from DVI file */ int DVIMag; /* magnification from DVI file */ double UserMag; /* user specified magnification */ double GlobalMag; /* overall magnification (UserMag*DVIMag) */ double conv; /* conversion factor for magnified DVI units */ double OneHalf = 0.5; /* .5, so compiler can generate constant only once */ double Zero = 0.0; /* likewise */ double _d_; /* Used to store intermediate results. The compiler should do this for us, but it's too stupid. */ int IpHH; /* Interpress horizontal position */ int IpVV; /* Interpress vertical position */ int IpFont; /* Interpress current-font number */ char *getenv (), *malloc (); /* Absolute value */ #define ABS(n) ((n) >= 0 ? (n) : -(n)) /* Round a floating point number to integer */ #define ROUND(f) ((int) (_d_ = (f), \ _d_ < Zero ? _d_ - OneHalf : _d_ + OneHalf) /* Convert to floating point */ #define FLOAT(i) ((double) (i)) /* Convert a value in sp's to dev's, and vice versa */ #define SPtoDEV(sp) (ROUND ((sp) * conv)) #define DEVtoSP(dev) (ROUND ((dev) / conv)) /* Put a two-byte (word) value */ #define putword(w) (putchar ((w) >> 8), putchar (w)) /* Correct devpos (the virtual device position) to be within MaxDrift pixels of dvipos (the virtual DVI position). */ #define FIXDRIFT(devpos, dvipos) \ if (ABS ((devpos) - (dvipos)) <= MaxDrift); \ else \ if ((devpos) < (dvipos)) \ (devpos) = (dvipos) - MaxDrift; \ else \ (devpos) = (dvipos) + MaxDrift /* Compute the DEV widths of the characters in the given font */ ComputeCWidths (fi) struct fontinfo *fi; { register int i; register struct chinfo *ch; register int *cw; ch = fi -> px -> px_info; cw = fi -> cwidth; i = 128; while (--i >= 0) { *cw++ = SPtoDEV (ch -> ch_TFMwidth); ch++; } } SelectFont (n) int n; { register struct fontinfo *f; CurrentFont = f = FindFont ((i32) n, (struct fontinfo *) 0); CurrentFontIndex = f - FontInfo; } /* Start a page (process a DVI_BOP) */ /* NOTE: I'm depending on getting a BOP before any characters or rules on a page! */ BeginPage () { register int *i; static int count[10]; /* the 10 counters */ static int beenhere; if (!ExpectBOP) error (1, 0, "unexpected BOP"); if (beenhere) { if (!SFlag) putc (' ', stderr); } else beenhere++; 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, i); /* previous page pointer */ if (!SFlag) { fprintf (stderr, "[%d", count[0]); (void) fflush (stderr); } putchar (imP_Page); /* XXX */ IpHH = 0; IpVV = 0; hh = HHMargin; vv = VVMargin; dvi_h = DEVtoSP (hh); dvi_v = DEVtoSP (vv); dvi_w = 0; dvi_x = 0; dvi_y = 0; dvi_z = 0; } /* End a page (process a DVI_EOP) */ EndPage () { if (!ExpectEOP) error (1, 0, "unexpected EOP"); if (!SFlag) { putc (']', stderr); (void) fflush (stderr); } ExpectEOP = 0; ExpectBOP++; putchar (imP_EndPage); /* XXX */ } /* Begin XXX */ /* Store the relevant information from the DVI postamble, and set up various internal things. */ PosotAmbleHeader (p) register struct PostAmbleInfo *p; { register int n; PrevPagePointer = p -> pai_PrevPagePointer; Numerator = p -> pai_Numerator; Denominator = p -> pai_Denominator; DVIMag = p -> pai_DVIMag; /* Here we sneakily correct for the actual device resolution, so that we can pretend that it's 200 dots per inch. */ UserMag *= FLOAT (DPI) / 200.0; GlobalMag = DMagFactor (DVIMag) * UserMag; /* The conversion factor is figured as follows: there are exactly n/d DVI units per decimicron, and 254000 decimicrons per inch, and 200 pixels per inch. Then we have to adjust this by the stated magnification. */ conv = (Numerator / 254000.0) * (200.0 / Denominator) * GlobalMag; n = p -> pai_DVIStackSize * sizeof *dvi_stack; dvi_stack = (struct localstack *) malloc ((unsigned) n); if ((dvi_stackp = dvi_stack) == 0) error (1, errno, "can't allocate %d DVI stack bytes", n); IntGlobalMag = ROUND (GlobalMag * 1000.0); } /* Handle one of the font definitions from the DVI postamble. */ PostAmbleFontDef (p) register struct PostAmbleFont *p; { register struct fontinfo *f; register char *s; int def = S_CREATE | S_EXCL; f = (struct fontinfo *) SSearch (FontFinder, p -> paf_DVIFontIndex, &def); if (f == 0) if (def & S_COLL) error (1, 0, "font %d already defined", p -> paf_DVIFontIndex); else error (1, 0, "can't stash font %d (out of memory?)", p -> paf_DVIFontIndex); f -> ipfont = NextIPFont++; s = GenPXLFileName (p -> paf_name, p -> paf_DVIMag, p -> paf_DVIDesignSize, IntGlobalMag, TeXfonts); if ((f -> px = ReadPXLFile (s, 1)) == 0) { error (0, errno, "can't find font \"%s\"", s); FontErrors++; return; } if (p -> paf_DVIChecksum != f -> px -> px_checksum) error (0, 0, "\ WARNING: width tables and raster tables have different\n\ \tchecksums for font \"%s\"\n\ \tPlease notify your TeX maintainer\n\ \t(TFM checksum = 0%o, PXL checksum = 0%o)", s, dvi_checksum, f -> px -> px_checksum); ScaleTFMWidths (f -> px, dvi_mag); ComputeCWidths (f); f -> pspace = p -> paf_DVIMag / 6; /* a three-unit ``thin space'' */ f -> nspace = -4 * f -> pspace; f -> vspace = 5 * f -> pspace; } /* Read the postamble. */ ReadPostAmble () { static char s[2] = { 's', 0 }; if ((FontFinder = SCreate (sizeof (struct fontinfo))) == 0) error (1, 0, "can't create FontFinder (out of memory?)"); ScanPostAmble (stdin, PostAmbleHeader, PostAmbleFontDef); if (FontErrors) error (1, 0, "missing font%s prevent%s output (sorry)", FontErrors > 1 ? s : &s[1], FontErrors == 1 ? s : &s[1]); } /* End XXX */ /* Read the preamble and do a few sanity checks */ ReadPreAmble () { register int n; rewind (stdin); if (GetByte (stdin) != Sign8 (DVI_PRE)) error (1, 0, "missing PRE"); if (GetByte (stdin) != Sign8 (DVI_VERSION)) error (1, 0, "mismatched version numbers"); if (GetLong (stdin) != Numerator) error (1, 0, "mismatched numerator"); if (GetLong (stdin) != Denominator) error (1, 0, "mismatched denominator"); if (GetLong (stdin) != DVIMag) error (1, 0, "mismatched \\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 = 1.0; MaxDrift = DefaultMaxDrift; DPI = DefaultDPI; inname = "stdin"; while ((c = getopt (argc, argv, "d:m:r:s")) != EOF) { switch (c) { case 'd': /* max drift value */ MaxDrift = atoi (optarg); break; /* case 'l': /* landscape mode */ /* LFlag++; */ /* break; */ case 'm': /* magnification */ UserMag = DMagFactor (atoi (optarg)); break; case 'r': /* resolution */ DPI = atoi (optarg); break; case 's': /* silent */ SFlag++; break; case 'x': /* enable debugging */ XFlag++; break; case '?': fprintf (stderr, "\ Usage: %s [-d drift] [-m mag] [-s] [-r resolution] [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); /* ReadPostAmble does an fseek which, if performed on a tty, tends to make the shell log one out, thus the following kludge: */ if (isatty (fileno (stdin))) error (1, 0, "input from ttys is expressly forbidden!"); TeXfonts = getenv ("TEXFONTS"); if (TeXfonts == 0) TeXfonts = ""; ReadPostAmble (); /* Margins -- needs work! */ HHMargin = DefaultLeftMargin;/* XXX */ VVMargin = DefaultTopMargin;/* XXX */ ReadPreAmble (); ExpectBOP++; (void) fseek (stdin, PrevPagePointer, 0); /* All set! */ printf ("Interpress/Xerox/2.1 "); PutInstructionsBody (inname); PutPreamble (); ReadDVIFile (); /* EOF is implicit (?) */ exit (0); } /* Skip a font definition (since we are using those from the postamble) */ /* ARGSUSED */ SkipFontDef (font) int 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); } /* Perform a \special - right now ignore all of them */ DoSpecial (len) int len; /* length of the \special string */ { error (0, 0, "warning: ignoring \\special"); (void) fseek (stdin, (long) len, 1); } /* 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; { i32 rwidth, /* rule width from DVI file */ rheight; /* rule height from DVI file */ register int h, w; fGetLong (stdin, rheight); fGetLong (stdin, rwidth); /* Rule sizes must be computed in this manner: */ h = conv * rheight; if (FLOAT (h) < (conv * rheight)) h++; w = conv * rwidth; if (FLOAT (w) < (conv * rwidth)) w++; /* put the rule out */ if (IpHH != hh || IpVV != vv) SetPosition (hh, vv); putchar (imP_Rule); /* XXX */ putword (w); /* XXX */ putword (h); /* XXX */ putword (-h + 1); /* XXX */ if (advance) { hh += w; dvi_h += rwidth; if (ABS (hh - (w = SPtoDEV (dvi_h))) > MaxDrift) hh = w + (hh < w ? -MaxDrift : MaxDrift); } } /* FIXME (kerns, etc, etc) */ /* resume XXX */ /* This rather large routine reads the DVI file and calls on other routines to do anything moderately difficult (except put characters: there is a bunch of ugly code with ``goto''s which makes things much faster) */ ReadDVIFile () { register int c, p; int advance; IpFamily = -1; /* Only way out is via "return" statement */ for (;;) { /* Get the DVI byte, and if it's a character, put it */ /* c = UnSign8 (getchar ()); */ c = getchar (); /* getchar() returns unsigned values */ if (DVI_IsChar (c)) { /* I know, ugly, but ... no function call overhead this way */ register struct chinfo *ch; register struct fontinfo *cf; set: advance = 1; put: cf = CurrentFont; ch = &cf -> px -> px_info[c]; if (ch -> ch_width == 0) goto ignore; /* not a real character, just advance */ /* BEGIN INLINE EXPANSION OF IpSetPosition (the things we do in the name of efficency! ;-) ) */ if (ImHH != hh) { if (ImHH == hh - 1) putchar (imP_Forw); else if (ImHH == hh + 1) putchar (imP_Backw); else { putchar (imP_SetHAbs); putword (hh); } ImHH = hh; } if (ImVV != vv) { putchar (imP_SetVAbs); putword (vv); ImVV = vv; } /* END INLINE EXPANSION OF ImSetPosition */ if (ImFamily != CurrentFontIndex) { putchar (imP_SetFamily); putchar (CurrentFontIndex); ImFamily = CurrentFontIndex; } putchar (c); ImHH += cf -> cwidth[c]; ignore: if (advance) { #ifdef DEBUG if (XFlag) { fprintf (stderr, "setchar%d h:=%d+%d=%d, hh:=%d\n", c, dvi_h - DEVtoSP (HHMargin), ch->ch_TFMwidth, dvi_h - DEVtoSP (HHMargin) + ch->ch_TFMwidth, hh + CurrentFont->cwidth[c] - HHMargin); fflush (stderr); } #endif DEBUG hh += cf -> cwidth[c]; dvi_h += ch -> ch_TFMwidth; p = SPtoDEV (dvi_h); FIXDRIFT (hh, p); } continue; } /* Wasn't a character, maybe a font? */ if (DVI_IsFont (c)) { SelectFont ((i32) (c - DVI_FNTNUM0)); continue; } /* Wasn't a font, see if it's a generic one */ if (p = DVI_OpLen (c)) { /* It's generic, get its parameter */ switch (p) { case 1: p = Sign8 (getchar ()); break; case 2: fGetWord (stdin, p); p = Sign16 (p); break; case 3: fGet3Byte (stdin, p); p = Sign24 (p); break; case 4: fGetLong (stdin, p); break; case 5: p = UnSign8 (getchar ()); break; case 6: fGetWord (stdin, p); p = UnSign16 (p); break; case 7: fGet3Byte (stdin, p); p = UnSign24 (p); break; } /* Now that we have the parameter, perform the command */ switch (DVI_DT (c)) { case DT_SET: c = p; goto set; case DT_PUT: c = p; advance = 0; goto put; 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 = SPtoDEV (dvi_h); else { hh += SPtoDEV (p); p = SPtoDEV (dvi_h); FIXDRIFT (hh, p); } break; case DT_W: dvi_w = p; goto move_right; case DT_X: dvi_x = p; goto move_right; 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 baeline-skip moves.'' */ if (ABS (p) >= CurrentFont -> vspace) vv = SPtoDEV (dvi_v); else { vv += SPtoDEV (p); p = SPtoDEV (dvi_v); FIXDRIFT (vv, p); } break; case DT_Y: dvi_y = p; goto move_down; case DT_Z: dvi_z = p; goto move_down; case DT_FNT: SelectFont (p); break; case DT_XXX: DoSpecial (p); break; case DT_FNTDEF: SkipFontDef (p); break; #ifdef PARANOID default: error (1, 0, "bad DVI_DT(%d): (%d)", c, DVI_DT (c)); #endif PARANOID } continue; } /* Wasn't a char, wasn't a generic command, just pick it out from the whole mess */ switch (c) { case DVI_SETRULE: SetRule (1); break; case DVI_PUTRULE: SetRule (0); break; case DVI_NOP: break; case DVI_BOP: BeginPage (); break; case DVI_EOP: EndPage (); if (PrevPagePointer == -1) { if (!SFlag) { fprintf (stderr, "\n"); (void) fflush (stderr); } return; } break; case DVI_PUSH: dvi_stackp -> stack_hh = hh; dvi_stackp -> stack_vv = vv; dvi_stackp -> stack_dvi = dvi_current; dvi_stackp++; break; case DVI_POP: dvi_stackp--; hh = dvi_stackp -> stack_hh; vv = dvi_stackp -> stack_vv; dvi_current = dvi_stackp -> stack_dvi; break; case DVI_W0: p = dvi_w; goto move_right; case DVI_X0: p = dvi_x; goto move_right; case DVI_Y0: p = dvi_y; goto move_down; case DVI_Z0: p = dvi_z; goto move_down; case DVI_PRE: error (1, 0, "unexpected PRE"); case DVI_POST: /* shouldn't get this, reading backwards */ error (1, 0, "unexpected POST"); case DVI_POSTPOST: error (1, 0, "unexpected POSTPOST"); default: error (1, 0, "undefined DVI opcode (%d)", c); } } }