DataMuseum.dk

Presents historical artifacts from the history of:

DKUUG/EUUG Conference tapes

This is an automatic "excavation" of a thematic subset of
artifacts from Datamuseum.dk's BitArchive.

See our Wiki for more about DKUUG/EUUG Conference tapes

Excavated with: AutoArchaeologist - Free & Open Source Software.


top - metrics - download
Index: T t

⟦9461c2a05⟧ TextFile

    Length: 35021 (0x88cd)
    Types: TextFile
    Names: »tour.tex«

Derivation

└─⟦52210d11f⟧ Bits:30007239 EUUGD2: TeX 3 1992-12
    └─⟦af5ba6c8e⟧ »unix3.0/DVIWARE.tar.Z« 
        └─⟦ca79c7339⟧ 
            └─⟦this⟧ »DVIware/laser-setters/mctex/doc/tour.tex« 

TextFile

\begingroup
\raggedbottom

The driver we shall examine
is a simplified version of the one for the Imagen imPRESS language.
The description below assumes that you are at least somewhat familiar
with the format of a \dvi\ file.
An exact description can be found in Donald Knuth's {\em \TeX: The Program}.

\subsection{The \dvi\ library}
Our program begins with a number of inclusions
that refer to things found in the \dvi\ library:
\begin{quote}
\begin{verbatim}
#include <stdio.h>
#include "types.h"
#include "conv.h"
#include "dviclass.h"
#include "dvicodes.h"
#include "error.h"
#include "fio.h"
#include "font.h"
#include "gripes.h"
#include "dvistate.h"

#include "imagen.h"
#include "imPcodes.h"
\end{verbatim}
\end{quote}
The first, of course, refers to the system's standard I/O package,
which the driver uses to read the \dvi\ file and write imPRESS codes.
Of the rest,
all but the last two were covered in the previous section.
(We get away without the {\tt ../h/}
by using {\tt cc}'s {\tt -I} option.)
The files {\tt imagen.h} and {\tt imPcodes.h}
define the hardware characteristics---in this case,
the number of pixels per inch---and
the programming codes used in the imPRESS language.

The next section of code defines the global variables for the program:
\begin{quote}
\begin{verbatim}
char *ProgName;
extern char *optarg;
extern int optind;

#define f_family f_un.f_int

int     LFlag;                  /* -l => landscape mode */
int     ReversePages;           /* -p => [no] page reversal */

int     ImHH;                   /* Imagen horizontal position */
int     ImVV;                   /* Imagen vertical position */
int     ImFamily;               /* Imagen current-font number */

char    *PrintEngine;           /* e.g., canon, ricoh */

char    *DVIFileName;           /* name of input DVI file */
\end{verbatim}
\end{quote}
{\tt ProgName} is used by {\tt error};
we will set it to {\tt argv[0]} in {\tt main}.
The variables {\tt optarg} and {\tt optind} hold return values
from {\tt getopt} ({\tt getopt} is in many C libraries,
but a version from Henry Spencer of the University of Toronto
is optionally included in the \mctex\ library).

Like most devices, the Imagen needs the fonts renumbered
(as `font family' numbers),
and we use the {\tt f_un.f_int} field of a {\tt struct font}
to hold this.

Next come the global flags:
{\tt LFlag}, for landscape mode, and
{\tt ReversePages}, which holds the state of the {\tt -p} option
(recall from the \mctex\ User's Guide
that, on the Imagen, {\tt -p} is self-cancelling).
After these are three global variables
which retain the global state of the Imagen software itself:
{\tt ImHH}, the Imagen's current horizonal position on the page,
{\tt ImVV}, the Imagen's current vertical position, and
{\tt ImFamily}, the Imagen's current font family number.
Then we have the {\tt PrintEngine} from the {\tt -e} flag.
This is set to {\tt "canon"} in {\tt main}
in case no print engine is named.
Last is {\tt DVIFileName},
which the `gripe' routines use to tell the user which file is faulty.

After this is a forward declaration for a function,
two macros to put eight and sixteen bit values to the Imagen,
and a macro to set the Imagen's horizontal and vertical position.
The latter is used once for every character on every page,
and should therefore be fast;
a subroutine call involves too much overhead.
\begin{quote}
\begin{verbatim}
void    DownLoadGlyph();

#undef putbyte
#define putbyte(b) ((void) putchar(b))
#define putword(w) (putbyte((int)(w) >> 8), putbyte((int)(w)))

#define SetPosition(h, v) { \
        if (ImHH != (h)) { \
                if (ImHH == (h) - 1) \
                        putbyte(imP_Forw); \
                else if (ImHH == (h) + 1) \
                        putbyte(imP_Backw); \
                else { \
                        putbyte(imP_SetHAbs); \
                        putword(h); \
                } \
                ImHH = (h); \
        } \
        if (ImVV != (v)) { \
                putbyte(imP_SetVAbs); \
                putword(v); \
                ImVV = (v); \
        } \
}
\end{verbatim}
\end{quote}
(The {\tt \#undef putbyte} gets rid of the usual definition
from {\tt fio.h};
this driver does not need that version.)

At this point, the real driver code begins.
First we have a routine for {\tt DVISetState} to call
to acquire fonts:
\begin{quote}
\begin{verbatim}
struct font *DefineFont(char *name, i32 dvimag, i32 dvidsz) {
        register struct font *f;
        char *path;
        static int nextfamily;

        f = GetFont(name, dvimag, dvidsz, PrintEngine, &path);
        if (f == NULL) {
                GripeCannotGetFont(name, dvimag, dvidsz, PrintEngine, path);
                return (NULL);
        }
        if (nextfamily == MaxImFamily)
                error(0, 0, "\
WARNING: out of Imagen font family indicies;\n\
\toutput will probably resemble freshly scrambled eggs.");
        f->f_family = nextfamily++;
        return (f);
}
\end{verbatim}
\end{quote}
The {\tt DefineFont} routine---which we will later
hand to {\tt DVISetState}---receives the name of the font
and the magnification and design size
from the font definition found in the \dvi\ file postamble,
and must return a pointer to a font structure.
The Imagen itself uses the usual bitmap fonts
of the sort found in GF and PK files,
so we call {\tt GetFont}.
If {\tt GetFont} fails,
we call {\tt GripeCannotGetFont},
which produces an appropriate error message,
and we return {\tt NULL}
(that is, a nil pointer of type {\tt struct font *})
to indicate that at least one font is missing.
(The `blank' and `box' fonts come back from {\tt GetFont}
as ordinary fonts,
and {\tt GetFont} takes care of issuing the warning;
it is not necessary to tell here that this has happened.)
If we did get a font,
we must assign it a font family number.
The Imagen has a hard limit on the number of fonts;
{\tt MaxImFamily} is defined in {\tt imagen.h} as this limit.
If the next available font family number is equal to {\tt MaxImFamily},
we have run out of font numbers
and we generate a complaint here
(but continue on anyway,
since most of the output pages will probably be useful).
Having assigned a family number,
we return the font.
{\tt DVISetState} will take care of recording the \dvi\ index
and later translating the same number to the font pointer {\tt f}
that we returned here.

Next we have two tiny routines to handle the {\em bop\/} and {\em eop\/}
\dvi\ opcodes:
\begin{quote}
\begin{verbatim}
/* ARGSUSED */
void BeginPage(i32 *count) {
        putbyte(imP_Page);
        ImHH = ImVV = 0;
}

void EndPage() {
        putbyte(imP_EndPage);
}
\end{verbatim}
\end{quote}
{\tt BeginPage} puts out an Imagen `begin page' opcode;
this resets the Imagen's idea of its current point to $(0,0)$,
so we reset our idea at the same time.
{\tt EndPage} puts out an Imagen `end page' opcode,
which make the Imagen actually print the page
with whatever marks we have put on it in the meantime.
(The {\tt ARGSUSED} comment merely keeps lint happy
about the unused {\tt count} parameter in {\tt BeginPage},
which we must declare since {\tt DVIBeginPage} passes it.)

To handle a \verb|\special|, we simply print a warning and skip over it:
\begin{quote}
\begin{verbatim}
void DoSpecial(i32 len) {
        error(0, 0, "warning: ignoring \\special");
        (void) fseek(ds.ds_fp, (long)len, 1);
}
\end{verbatim}
\end{quote}
We must use {\tt ds.ds_fp},
not {\tt stdin},
because {\tt DVISetState} may have copied the input elsewhere.

We have finally reached code that performs some typesetting:
\begin{quote}
\begin{verbatim}
void SetRule(i32 h, i32 w) {
        SetPosition(dvi_hh, dvi_vv);
        (void) putchar(imP_Rule);
        putword(w);
        putword(h);
        putword(-h + 1);
}
\end{verbatim}
\end{quote}
{\tt SetRule} (called from {\tt DVIRule})
plots a rule at the current position
as recorded in the global \dvi\ state.
To do this on the Imagen,
we must set its h and v position,
then put out a `set rule' opcode,
followed by the width and height of the rule in pixels,
and then the start of that rule below the baseline (also in pixels).
The Imagen draws the rule extending downwards from this position.
\TeX\ always moves {\tt dvi_vv} before setting rules
so that they will sit right on the baseline.
This means that the `depth' of the rule should be negative or zero,
as the rule begins at $h-1$ pixels {\em above\/} the baseline.
A one-pixel high rule will sit right on the baseline,
or zero pixels above it;
a two-pixel high rule will sit one pixel above it;
and so forth.

The next routine, {\tt CheckChar},
ensures that the character codes being sent to the Imagen
are within its software limits.
The Imagen can only plot glyphs whose character code values
are in the range $0 \le c \le 127$.
It would be possible to print glyphs from fonts with more codes
by assigning several Imagen family numbers to such fonts,
but this driver does not do it,
since none of the standard \TeX\ fonts have such glyphs.
\begin{quote}
\begin{verbatim}
int CheckChar(i32 c) {
        if ((ui32)c > 127) {
                error(0, 0,
                    "Warning: character code %ld too big for Imagen",
                    (long)c);
                return (1);
        }
        return (0);
}
\end{verbatim}
\end{quote}

At long last we reach the main page loop,
which reads one page of the input \dvi\ file,
plotting character glyphs and rules as needed
in the appropriate positions.
A form of this loop (along with the device-specific functions above)
appears in every driver,
but it changes sufficiently
that abstracting it out into the \mctex\ library
is harder than it first appears.
The straightforward way to do this
would be to move all the device-specific code out of the loop itself
and into functions that can be called from this loop.
This approach has been tested.
It works, but it makes the drivers about 15\% slower.
Moreover, it only works for one-pass drivers
like that for the Imagen.
The \ps\ driver uses a two-pass approach,
and in fact has two separate page loops;
one of these (the character scan pass)
does much less work than the other,
and calling a general subroutine for it
slows the program even more.
A `generic' version of the loop appears in {\tt dvi/pageloop};
those writing new drivers can often start with this,
and add the device driver code around it to build a working converter.

\begin{quote}
\begin{verbatim}
int PageLoop() {
        static struct font NoFont;      /* font with zero pspace, etc */
        register int c;
        register i32 p;
        register struct font *f = &NoFont;
        register FILE *fp = ds.ds_fp;
        int doingpage = 0, advance;

        if (ReversePages)
                if (ds.ds_prevpage == -1)
                        return (0);     /* Luke 13:30 (kilroy was here) */
                else
                        (void) fseek(fp, ds.ds_prevpage, 0);
\end{verbatim}
\end{quote}
The loop begins with declarations for local variables:
{\tt c} holds an input character, or {\tt EOF};
{\tt p} holds an {\tt i32} parameter; and
{\tt f} is a pointer to the current font,
initially the special font {\tt NoFont}
in case the \dvi\ file has a motion opcode
before a font selection opcode.
(Each \dvi\ page is required to have a font selection opcode
before placing any glyphs,
but not before motions.)
Putting the input file pointer into the register variable {\tt fp}
offers a small speed advantage in older compilers
which do not do their own register allocation,
and does not hurt in smarter compilers;
and it is a bit more convenient to type and to read.
The variable {\tt doingpage} is initially zero,
meaning that we have not found a {\em bop\/} opcode.
The variable {\tt advance} is used later
to remember whether a glyph-setting command
came from a `set' opcode or a `put' opcode:
the former advances the \dvi\ position variables
by the glyph's width,
and the latter does not.

If the program is reversing pages,
the next page begins at byte number {\tt ds.ds_prevpage}.
If, however, {\tt ds.ds_prevpage} is {\tt -1},
we have reached the first page of the \dvi\ file,
which (since we are reading backwards) is the last to be printed,
and {\tt PageLoop} signals that the last page is done
by returning 0.
Otherwise we seek to that position
so that the next {\tt fgetbyte(fp)}
will start at the previous page.

After this we enter the loop.
(This loop would use {\tt for~(;;)}
rather than labels and {\tt goto},
but that makes the function crawl off the right hand side
of the screen---here, of the page.)
The loop handles ordinary characters early,
as they are the most common opcodes in \dvi\ files,
and doing so makes the code run faster.
\begin{quote}
\begin{verbatim}
loop:
        c = fgetbyte(fp);
        if (DVI_IsChar(c)) {
                register struct glyph *g;

                p = c;
                advance = 1;
do_char:
                g = GLYPH(f, p);
                if (!GVALID(g)) {
                        GripeBadGlyph(p, f);
                        goto loop;
                }
                if ((g->g_flags & GF_SEEN) == 0)
                        DownLoadGlyph((int)p, f, g);
                if (HASRASTER(g)) {     /* workaround for Imagen bug */
                        SetPosition(dvi_hh, dvi_vv);
                        if (ImFamily != f->f_family) {
                                putbyte(imP_SetFamily);
                                putbyte(f->f_family);
                                ImFamily = f->f_family;
                        }
                        putbyte((int)p);
                        ImHH += g->g_pixwidth;
                }
                if (advance) {
                        dvi_h += g->g_tfmwidth;
                        dvi_hh += g->g_pixwidth;
                        p = fromSP(dvi_h);
                        FIXDRIFT(dvi_hh, p);
                }
                goto loop;
        }
\end{verbatim}
\end{quote}
At the top of the loop,
we set {\tt c} to the next opcode from the \dvi\ file,
or to {\tt EOF} if the file ends prematurely.
If the code in {\tt c} is a character setting command
(value in $[0..127]$),
we make available the variable {\tt g},
copy {\tt c} to the parameter variable {\tt p},
set {\tt advance} to {\tt 1},
and enter the `plot a glyph' code
(which is re\"entered from below for `set' and `put' \dvi\ opcodes;
this is the reason {\tt c} must be copied into {\tt p}).
Next we obtain the glyph data for character {\tt p} in font {\tt f},
and if it is invalid, complain and ignore the character.
(If {\tt f} is {\tt \&NoFont}, {\tt g} will never be valid,
so the program will continue
even if the \dvi\ file is corrupt,
at least in this particular case.)
If {\tt g} is valid, but has not yet been loaded into the Imagen,
we do that here,
computing {\tt g->g_pixwidth} and setting {\tt GF_SEEN}
as a side effect of downloading the glyph itself.
Next we send the code for the glyph to the Imagen,
making it plot the character.
Old versions of the Imagen software have a bug
that causes terrible things to happen
if you ask it to plot a glyph that has no bits,
so we avoid this situation entirely;
but if there is a raster,
we set the Imagen's position to the current \dvi\ pixel position,
set the printer's family if necessary,
and then put the character code for the glyph.
The Imagen then advances by the glyph's {\tt g_pixwidth}
(as instructed during the download).
Then, whether or not the glyph has a raster,
if the character came from anything other than a `put' opcode,
we advance the current \dvi\ position
(in both the \dvi\ units---scaled points---and pixels),
and correct for any overlarge drifting that may have occurred.
We have now finished with the character opcode
(whether it was a regular character in $[0..127]$
or from a `set' or `put' opcode
whose parameter was not rejected by {\tt CheckChar})
and we return to the loop.

If the \dvi\ opcode was not a simple character command,
we first check for an unexpected end of file,
and if this has occurred, complain and abort.
Otherwise, we look up the opcode in the parameter length table
(see Table~\ref{tab:paramlen}, p.~\pageref{tab:paramlen})
and get a parameter, if any.
(If the \dvi\ file ends at this point,
the parameter value may be incorrect,
but using the signed and unsigned extension macros
forces the value to be in range,
and the next {\tt fgetbyte(fp)} will get another {\tt EOF}
which we will detect on the next trip around the loop.
If the parameter length code from {\tt DVI_OpLen}
is not a proper value,
the program panics.)
Having obtained a parameter, if possible,
we switch on the class code (Table~\ref{tab:classcodes}).
\begin{quote}
\begin{verbatim}
        if (c == EOF)
                GripeUnexpectedDVIEOF();
        switch (DVI_OpLen(c)) {
        case DPL_NONE:
                break;
        case DPL_SGN1:
                p = fgetbyte(fp);
                p = Sign8(p);
                break;
        case DPL_SGN2:
                fGetWord(fp, p);
                p = Sign16(p);
                break;
        case DPL_SGN3:
                fGet3Byte(fp, p);
                p = Sign24(p);
                break;
        case DPL_SGN4:
                fGetLong(fp, p);
                break;
        case DPL_UNS1:
                p = fgetbyte(fp);
                p = UnSign8(p);
                break;
        case DPL_UNS2:
                fGetWord(fp, p);
                p = UnSign16(p);
                break;
        case DPL_UNS3:
                fGet3Byte(fp, p);
                p = UnSign24(p);
                break;
        default:
                panic("DVI_OpLen(%d) = %d", c, DVI_OpLen(c));
                /* NOTREACHED */
        }
        switch (DVI_DT(c)) {
\end{verbatim}
\end{quote}

For `set' and `put' opcodes,
we set {\tt advance} appropriately,
then check that the character
(already obtained as our single simple parameter)
can be handled.
If not, we ignore the character entirely
({\tt CheckChar} having complained already);
otherwise, we draw it just like any other character:
\begin{quote}
\begin{verbatim}
        case DT_SET:
                advance = 1;
                if (CheckChar(p))
                        goto loop;
                goto do_char;
        case DT_PUT:
                advance = 0;
                if (CheckChar(p))
                        goto loop;
                goto do_char;
\end{verbatim}
\end{quote}

For the {\em setrule\/} and {\em putrule\/} opcodes,
we call the function {\tt DVIRule},
passing it the address of the function that will set the rule
and a 1 if it should advance (set) or 0 if not (put).
{\tt DVIRule} hands the rule's width and height to {\tt SetRule},
having read these from the input and converted them to pixels
using the global conversion.
{\tt DVIRule} then adjusts the \dvi\ positions
(again in both scaled points and pixels) and fixes over-drift,
and then we return to the loop.
\begin{quote}
\begin{verbatim}
        case DT_SETRULE:
                DVIRule(SetRule, 1);
                goto loop;

        case DT_PUTRULE:
                DVIRule(SetRule, 0);
                goto loop;
\end{verbatim}
\end{quote}

Any {\em nop\/} opcodes are ignored, resuming the loop.
\begin{quote}
\begin{verbatim}
        case DT_NOP:
                goto loop;
\end{verbatim}
\end{quote}

On encountering a {\em bop},
we first make sure that we are not already working on a page.
If so, we gripe about the \dvi\ file and abort.
Otherwise we call {\tt DVIBeginPage},
which reads the ten \cccount\ registers
and the previous page pointer,
resets the \dvi\ position and values to {\tt ds.ds_fresh},
clears the \dvi\ stack,
and calls our {\tt BeginPage} function.
\begin{quote}
\begin{verbatim}
        case DT_BOP:
                if (doingpage)
                        GripeUnexpectedOp("BOP (already in page)");
                DVIBeginPage(BeginPage);
                doingpage = 1;
                goto loop;
\end{verbatim}
\end{quote}

For an {\em eop},
we make sure that we are working on a page,
and if not, gripe and abort.
Otherwise we call our EndPage function,
then return 1,
signifying that there may be more pages in the \dvi\ file.
\begin{quote}
\begin{verbatim}
        case DT_EOP:
                if (!doingpage)
                        GripeUnexpectedOp("EOP (no BOP)");
                EndPage();
                return (1);
\end{verbatim}
\end{quote}

{\em Push\/} and {\em pop\/} push and pop the current \dvi\ values
to and from the \dvi\ stack:
\begin{quote}
\begin{verbatim}
        case DT_PUSH:
                *ds.ds_sp++ = ds.ds_cur;
                goto loop;
        case DT_POP:
                ds.ds_cur = *--ds.ds_sp;
                goto loop;
\end{verbatim}
\end{quote}
These do not check for stack overruns or underruns;
a bad \dvi\ file could crash the program here,
but we leave this check to \dvitype.

All the opcodes that move right and down
are handled in a device-independent fashion,
and are done according to the \dvi\ positioning rules
described in \S\ref{sec:fonts}:
\begin{quote}
\begin{verbatim}
        case DT_W0:
                p = dvi_w;
                goto right;
        case DT_W:
                dvi_w = p;
                goto right;
        case DT_X0:
                p = dvi_x;
                goto right;
        case DT_X:
                dvi_x = p;
                goto right;
        case DT_RIGHT:
right:
                dvi_h += p;
                if (F_SMALLH(f, p)) {
                        dvi_hh += fromSP(p);
                        p = fromSP(dvi_h);
                        FIXDRIFT(dvi_hh, p);
                } else
                        dvi_hh = fromSP(dvi_h);
                goto loop;

        case DT_Y0:
                p = dvi_y;
                goto down;
        case DT_Y:
                dvi_y = p;
                goto down;
        case DT_Z0:
                p = dvi_z;
                goto down;
        case DT_Z:
                dvi_z = p;
                goto down;
        case DT_DOWN:
down:
                dvi_v += p;
                if (F_SMALLV(f, p)) {
                        dvi_vv += fromSP(p);
                        p = fromSP(dvi_v);
                        FIXDRIFT(dvi_vv, p);
                } else
                        dvi_vv = fromSP(dvi_v);
                goto loop;
\end{verbatim}
\end{quote}

Font selection codes (both the 64 one-byte codes
and the four parameterised versions)
use {\tt DVIFindFont} to find the new current font:
\begin{quote}
\begin{verbatim}
        case DT_FNTNUM:
                f = DVIFindFont((i32)(c - DVI_FNTNUM0));
                goto loop;
        case DT_FNT:
                f = DVIFindFont(p);
                goto loop;
\end{verbatim}
\end{quote}

Specials are passed to {\tt DoSpecial};
fonts are skipped via {\tt SkipFontDef};
and {\em pre\/} and {\em postpost\/} opcodes should not appear.
{\em Post\/} codes
may appear outside pages (i.e., before a {\em bop\/})
and signal the end of the \dvi\ file.
\begin{quote}
\begin{verbatim}
        case DT_XXX:
                DoSpecial(p);
                goto loop;
        case DT_FNTDEF:
                SkipFontDef(fp);
                goto loop;
        case DT_PRE:
                GripeUnexpectedOp("PRE");
                /* NOTREACHED */
        case DT_POST:
                if (doingpage) {
                        GripeUnexpectedOp("POST (no EOP)");
                        /* NOTREACHED */
                }
                return (0);
        case DT_POSTPOST:
                GripeUnexpectedOp("POSTPOST");
                /* NOTREACHED */
\end{verbatim}
\end{quote}

The only remaining possibilities are the six undefined \dvi\ opcodes
(values in $[250..255]$),
or a program bug.
These are handled by aborting and panicking, respectively.
\begin{quote}
\begin{verbatim}
        case DT_UNDEF:
                GripeUndefinedOp(c);
                /* NOTREACHED */
        default:
                panic("DVI_DT(%d) = %d", c, DVI_DT(c));
                /* NOTREACHED */
        }
        /* NOTREACHED */
}
\end{verbatim}
\end{quote}

We have nearly reached the end of the driver.
All that is left are the main program and the glyph downloading code.
First we have {\tt main},
which begins by setting various defaults.
The $x$ and $y$ margin offsets (in $1/1000$ths of an inch)
are set to zero,
and the printer's resolution (in dots per inch, or pixels per inch)
is set to the default from {\tt imagen.h} (normally 300).
{\tt ProgName} is set to {\tt argv[0]},
{\tt ds.ds_usermag} is set to 1000 (unmagnified),
{\tt ds.ds_maxdrift} is set to the default for the device
(3 when {\tt dpi} is 300).
The {\tt ReversePages} flag is set
({\tt -p} will then clear it, and another {\tt -p} will set it again)
and {\tt PrintEngine} and {\tt DVIFileName} are set
to {\tt "canon"} and {\tt "`stdin'"} respectively,
all as defaults.
\begin{quote}
\begin{verbatim}
main(int argc, register char **argv) {
        register int c;
        int xoff = 0, yoff = 0; /* margins from -X and -Y */
        int dpi = DefaultDPI;   /* resolution from -r */

        ProgName = *argv;
        ds.ds_usermag = 1000;
        ds.ds_maxdrift = DefaultMaxDrift;
        ReversePages = 1;       /* default is to reverse pages */
        PrintEngine = "canon";
        DVIFileName = "`stdin'";
\end{verbatim}
\end{quote}

We then get options from the program's arguments, if any,
until we run out ({\tt c == EOF}).
Each option changes some default as set above:
{\tt -d} sets {\tt ds.ds_maxdrift} to its integer argument,
{\tt -e} sets {\tt PrintEngine} to its string argument,
{\tt -l} sets {\tt LFlag},
{\tt -m} sets {\tt ds.ds_usermag} to its integer argument,
{\tt -p} toggles {\tt ReversePages},
and {\tt -r}, {\tt -X}, and {\tt -Y}
set {\tt dpi}, {\tt xoff}, and {\tt yoff}
to their integer arguments respectively.
If any other option letter is given,
the program prints a short usage message
and aborts.
\begin{quote}
\begin{verbatim}
        while ((c = getopt(argc, argv, "d:e:lm:pr:X:Y:")) != EOF) {
                switch (c) {
                case 'd':       /* max drift value */
                        ds.ds_maxdrift = atoi(optarg);
                        break;
                case 'e':       /* engine */
                        PrintEngine = optarg;
                        break;
                case 'l':       /* landscape mode */
                        LFlag++;
                        break;
                case 'm':       /* magnification */
                        ds.ds_usermag = atoi(optarg);
                        break;
                case 'p':       /* toggle page reversal */
                        ReversePages = !ReversePages;
                        break;
                case 'r':       /* resolution */
                        dpi = atoi(optarg);
                        break;
                case 'X':       /* x offset, in 1/1000 inch increments */
                        xoff = atoi(optarg);
                        break;
                case 'Y':       /* y offset */
                        yoff = atoi(optarg);
                        break;
                case '?':
                        (void) fprintf(stderr,
        "Usage: %s [-l] [more options, see manual] [file]\n", ProgName);
                        (void) fflush(stderr);
                        exit(1);
                }
        }
\end{verbatim}
\end{quote}

Having processed all options, we then open the named input file
if there are any arguments remaining, retaining the name for later.
If this fails,
the program aborts with an error message
that includes the \Unix\ error (such as `No such file or directory'
or `Permission denied').
Otherwise,
we call {\tt DVISetState},
passing it the input \dvi\ file pointer ({\tt stdin}),
the font-obtaining routine ({\tt DefineFont}),
the printer's resolution ({\tt dpi}),
and the x and y offsets.
\begin{quote}
\begin{verbatim}
        if (optind < argc)
                if (freopen(DVIFileName = argv[optind], "r", stdin) == NULL)
                        error(1, -1, "can't open %s", DVIFileName);
        DVISetState(stdin, DefineFont, dpi, xoff, yoff);
\end{verbatim}
\end{quote}
{\tt DVISetState} makes {\tt ds.ds_fp}
point to a seekable copy of {\tt stdin}
(possibly a different {\tt stdio} file),
reads through the postamble,
defines each font,
and then reads through the preamble
and performs a few sanity checks on the file.
It also sets the global conversion factor
and the values for {\tt ds.ds_fresh}.
The values in the latter are computed by adding one inch
({\tt dpi} pixels) to $\var*{offset}/1000$ for
each of the x and~y offsets.
Most Imagens are accomodating devices,
allowing the point $(0,0)$ to be at the top left of the page,
so that an offset of 300 pixels on a 300 dot per inch printer
really comes out as a one inch margin.
Some printers, however, put $(0,0)$ a bit further down or to the right;
this can be corrected by adjusting the last two parameters
to {\tt DVISetState}.
For instance,
if the devices always adds a $2/10$~inch margin to the top of the page,
but none to the side,
we could use a call like this:
\begin{quote}
\begin{verbatim}
DVISetState(stdin, DefineFont, dpi, xoff, yoff - 200);
\end{verbatim}
\end{quote}
which would subtract off the `extra' margin,
leaving the \dvi\ coordinate $(0,0)$ at the right place.

In any case, if there are missing fonts---that is, if {\tt DefineFont}
returns at least one nil pointer---{\tt DVISetState} aborts
with an appropriate apology.
But if we make it through {\tt DVISetState},
all is well and we can begin setting pages.

Well, almost: first we have to set up the device.
The Imagen requires a short declaration string
starting with {\tt @document}, giving the language to be used
({\tt imPRESS}) and the file's name.
By default, the point $(0,0)$ is at the top left
and one writes to the right and then moves down to the next line,
but if we are producing landscape output,
we need to change this:
we want to start at the top right
and write down the page.
The {\tt SetHVSystem} and {\tt SetAdvDirs} codes found here
program the Imagen to produce properly landscaped pages.
\begin{quote}
\begin{verbatim}
        ImFamily = -1;          /* probably 0, but this is safer */
        /* All set! */
        (void) printf("@document(language imPRESS, name \"%s\")",
            DVIFileName);
        if (LFlag) {
                putbyte(imP_SetHVSystem);
                putbyte(0x55);  /* origin=ulc, h:v=90, h:x=90 */
                putbyte(imP_SetAdvDirs);
                putbyte(0);     /* main=0 (degrees), secondary=90 */
        }
\end{verbatim}
\end{quote}
Now we set pages, and continue to do so
until we have set the very last one.
\begin{quote}
\begin{verbatim}
        while (PageLoop())
                /* void */;
\end{verbatim}
\end{quote}
We then tell the Imagen that we are done,
force any pending output to be flushed,
and check for errors.
Although proper programming practise
would include error checking on every output byte,
this is overly expensive
and does not gain much;
this program compromises by checking only at the last minute.
A better approach is to check after each output page.
With large documents, this may at least keep the `disk full' messages
at bay.
But if all goes well,
we exit with the success code (0).
(The {\tt NOTREACHED} comment tells lint that main ends here,
preventing `main returns random value to invocation environment'
diagnostics.)
\begin{quote}
\begin{verbatim}
        putbyte(imP_EOF);
        (void) fflush(stdout);
        if (ferror(stdout))
                error(1, -1, "error writing imPRESS output");

        exit(0);
        /* NOTREACHED */
}
\end{verbatim}
\end{quote}

We are almost done---all that is left is the routine to download glyphs.
Here it is:
\begin{quote}
\begin{verbatim}
void DownLoadGlyph(int c, struct font *f, register struct glyph *g) {
        register char *p;
        register int i, j, w;

        g->g_pixwidth = fromSP(g->g_tfmwidth);
        g->g_flags |= GF_SEEN;
        if (!HASRASTER(g))
                return;

        if (!LFlag) {
                w = 0;
                p = RASTER(g, f, ROT_NORM);
        } else {
                w = 1 << 14;
                p = RASTER(g, f, ROT_RIGHT);
        }
        putbyte(imP_DefGlyph);
        w |= (f->f_family << 7) | c;
        putword(w);
        putword(g->g_pixwidth);
        putword(g->g_width);
        putword(g->g_xorigin);
        putword(g->g_height);
        putword(g->g_yorigin);

        w = (g->g_width + 7) >> 3;
        for (i = g->g_height; --i >= 0;)
                for (j = w; --j >= 0;)
                        putbyte(*p++);

        FreeRaster(g);
}
\end{verbatim}
\end{quote}
This routine downloads the bitmap and width values
for the glyph {\tt g} in the font {\tt f};
{\tt g}'s character code has value {\tt c}
(which is guaranteed to be in $[0..127]$,
since we throw out other codes early).
The first step---and, due to the bug in early Imagen software,
if the glyph has no raster, the only step---is to compute
{\tt g->g_pixwidth}
and set the {\tt GF_SEEN} flag.
If the character does have a raster,
we get the Imagen's {\em rotation code\/}
(0 for normal `upright' characters,
1 for characters printed on landscaped pages;
this value goes in the top two bits of a 16 bit word)
and the raster bits for the glyph in that orientation.
We then tell the Imagen that we are defining a glyph
with that rotation code
for font family {\tt f->f_family}, character {\tt c},
whose advance-width is {\tt g->g_pixwidth}
and whose {\em bounding box\/} is {\tt g->g_width}
and {\tt g->g_height},
with x and y offsets of {\tt g->g_xorigin} and {\tt g->g_yorigin}.
It `just happens' that the Imagen's idea of offsets and bounding boxes
is the same as that of the various \TeX\ fonts;\footnote
{Imagen was founded by a group from Stanford,
after some of the early work on \TeX\ was already done,
so this is not exactly coincidence.}
most device drivers have to do various arithmetic gyrations
to calculate the values the device wants.
After this come the bits for the character,
packed into bytes,
with zero bits added to round each row to a whole number of bytes.
Again the \TeX\ fonts `just happen' to match the Imagen's requirements,
and we can simply put out the bytes in order.\footnote
{This one {\em is\/} a coincidence,
or at least, was not intended for compatiblity with \TeX;
the original \TeX\ (\TeX78) padded font bitmaps to 36-bit words.}
(This bitmap format is not uncommon on other devices;
it is mainly the baseline computations that vary.)
After putting out the bits for the raster,
we no longer need the raster itself,
so we discard it with {\tt FreeRaster}.

That is it!
Most of the `grunt work',
aside from the page loop,
is taken care of by library routines.
Unpacking PK fonts,
rasterising GF fonts,
and locating fonts in general
are all done by the \mctex\ library,
so that drivers can concentrate on driving their devices.

\endgroup % undo \raggedbottom