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 v

⟦f8d282fce⟧ TextFile

    Length: 24084 (0x5e14)
    Types: TextFile
    Names: »verser1.c«

Derivation

└─⟦060c9c824⟧ Bits:30007080 DKUUG TeX 2/12/89
    └─⟦this⟧ »./DVIware/laser-setters/umd-dvi/dev/verser1.c« 

TextFile

/*
 * 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: verser1.c,v 2.4 87/06/16 17:14:51 chris Exp $";
#endif

/*
 * Verser1 -- First half of DVI to Versatec driver
 *
 * Reads DVI version 2 files and converts to an intermediate form that
 * is read by verser2.  Most of the work consists of converting DVI units
 * to Versatec units, sorting pages, and rotating positions if the -h
 * flag is given.
 *
 * TODO:
 *	think about fonts with characters outside [0..127]
 */

#include <stdio.h>
#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 "verser.h"

char	*ProgName;
extern char *optarg;
extern int optind;

/* Globals */
char	serrbuf[BUFSIZ];	/* buffer for stderr */

/*
 * According to DVItype, we have to plot characters using two simultaneous
 * pieces of information:  the character's TFM width modified by the scale
 * factor from the DVI file (this is the width in scaled points), and the
 * character's width in pixels (converted from scaled points).  The former
 * is obtained by the glyph routines; the latter we put in the g_pixwidth
 * field of each glyph.
 *
 * Whenever we move 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 followed for vertical positioning.
 */
struct fontinfo {
	struct font *f;		/* the font */
	int index;		/* our index number */
	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 */
};

/*
 * However, there's an exception to the rule (natch!): certain fonts
 * tend to produce a constant "drift" away from the "correct" position
 * which eventually builds up to an intolerable amount.  So, if hh and
 * fromSP(dvi_h) become more than MaxDrift units apart, hh must be adjusted
 * by the smallest amount that will preserve the invariant.  (The same
 * applies to vv and dvi_v.)
 */
int	MaxDrift;		/* the maximum allowable difference between
				   hh and fromSP(dvi_h) */

struct fontinfo *CurrentFont;	/* the current font (if any) */
int	NextFont;		/* during font definition, we assign
				   sequential indicies; verser2 knows this */

struct fontinfo NoFont;		/* a fake font, to help get things started */

char	*TeXFontDesc;		/* getenv(CONFENV) */

/* arrays containing page info */
int	Chars;			/* number of chars {left,} on page */
int	MaxChars;		/* total space for chars */
int	*yx;			/* contains (y<<16|x) for each char */
int	*fcp;			/* contains (font<<14|char<<7|part) */
int	*nextyx;		/* pointer to next yx area */
int	*nextfcp;		/* pointer to next fcp area */

/*
 * A few experiments showed that the typical job uses less than 3200
 * characters per page.  This is an extensible array, so the initial value
 * affects only efficiency.
 */
#ifndef InitialChars
#define InitialChars 4000	/* initial number of chars to allocate */
#endif

int	ExpectBOP;		/* true => BOP ok */
int	ExpectEOP;		/* true => EOP ok */

/*
 * If there are lots of characters and rules that don't fit on a page,
 * these flags help reduce error output.
 */
int	TopEMsg;		/* true => gave error message about top */
int	BottomEMsg;		/* true => gave error message about bottom */
int	LeftEMsg;		/* true => gave error message about left */
int	RightEMsg;		/* true => gave error message about right */
int	RuleEMsg;		/* true => gave error message about rule */

int	CFlag;			/* -c => center output */
int	HFlag;			/* -h => horizontal (sideways) output */
int	SFlag;			/* -s => silent (no page #s) */
int	Debug;			/* -D => debug flag */

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	BottomMargin;		/* bottom margin (in DEVs) */
int	TopMargin;		/* top margin (in DEVs) */
int	WidestPageWidth;	/* width of widest page (in DEVs) */
int	TallestPageHeight;	/* height of tallest page (in DEVs) */
int	HHMargin;		/* horizontal margin (in DEVs) */
int	VVMargin;		/* vertical margin (in DEVs) */

i32	Numerator;		/* numerator from DVI file */
i32	Denominator;		/* denominator from DVI file */
i32	DVIMag;			/* magnification from the DVI file */
int	UserMag;		/* user specified magnification */

struct search *FontFinder;	/* maps from DVI index to internal info */
int	FontErrors;		/* true => error(s) occurred during font
				   definitions from DVI postamble */

char	*malloc(), *realloc(), *getenv();

/* Absolute value */
#define ABS(n) ((n) >= 0 ? (n) : -(n))

/* Correct devpos (the actual device position) to be within MaxDrift pixels
   of dvipos (the virtual DVI position). */
#define FIXDRIFT(devpos, dvipos) \
	if (ABS((devpos) - (dvipos)) <= MaxDrift) \
		/* void */; \
	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 struct font *f;
	register struct glyph *g;
	register int i;

	f = fi->f;
	for (i = 0; i < 128; i++) {
		g = GLYPH(f, i);
		if (GVALID(g))
			g->g_pixwidth = fromSP(g->g_tfmwidth);
	}
}

SelectFont(n)
	i32 n;
{
	int x = S_LOOKUP;

	if ((CurrentFont = (struct fontinfo *)SSearch(FontFinder, n, &x)) == 0)
		error(1, 0, "font %d not in finder table", n);
}

/*
 * Have run out of room in the current yx and fcp arrays, so expand them.
 */
ExpandArrays()
{
	register unsigned newsize;

	MaxChars <<= 1;
	newsize = MaxChars * sizeof *yx;
	if ((yx = (int *) realloc((char *) yx, newsize)) == NULL)
		GripeOutOfMemory(newsize, "yx array");
	if ((fcp = (int *) realloc((char *) fcp, newsize)) == NULL)
		GripeOutOfMemory(newsize, "fcp array");
	Chars = MaxChars >> 1;
	nextyx = &yx[Chars];
	nextfcp = &fcp[Chars];
	--Chars;		/* because we're about to use one */
}

/*
 * Sort the page arrays so that the values in yx are in ascending order.  We
 * use a Shell sort.
 */
SortPage()
{
	register int i, j, k, delta, *y, *f;

	/*
	 * Chars is currently the number of chars on the page, not the number
	 * of chars left in the array.
	 */
	y = yx;
	f = fcp;
	delta = 1;
	while (9 * delta + 4 < Chars)
		delta = 3 * delta + 1;
	while (delta > 0) {
		for (i = delta; i < Chars; i++) {
			if (y[j = i - delta] > y[i]) {
				register int t1 = y[i];
				register int t2 = f[i];

				k = i;
				do {
					y[k] = y[j];
					f[k] = f[j];
					k = j;
					j -= delta;
				} while (j >= 0 && y[j] > t1);
				y[k] = t1;
				f[k] = t2;
			}
		}
		delta /= 3;
	}
}

/*
 * 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[11];	/* the 10 counters, plus the previous page
				   pointer (which we ignore anyway) */

	if (!ExpectBOP)
		GripeUnexpectedOp("BOP");

	if (nextyx && !SFlag)
		putc(' ', stderr);

	/* Chars now becomes "number of characters left" */
	Chars = MaxChars;	/* empty the arrays */
	nextyx = yx;
	nextfcp = fcp;

	dvi_stackp = dvi_stack;

	ExpectBOP = 0;
	ExpectEOP++;		/* set the new "expect" state */

	TopEMsg = 0;
	BottomEMsg = 0;
	LeftEMsg = 0;
	RightEMsg = 0;
	RuleEMsg = 0;

	for (i = count; i < &count[sizeof count / sizeof *count]; i++)
		fGetLong(stdin, *i);

	if (!SFlag) {
		(void) fprintf(stderr, "[%d", count[0]);
		(void) fflush(stderr);
	}
	hh = HHMargin;
	vv = VVMargin;
	dvi_h = toSP(hh);	/* `0' */
	dvi_v = toSP(vv);	/* `0' */
	dvi_w = 0;
	dvi_x = 0;
	dvi_y = 0;
	dvi_z = 0;
}

/*
 * End a page (process a DVI_EOP).
 */
EndPage()
{
	register int i, *y, *f, t, v, oldv;

	if (!ExpectEOP)
		GripeUnexpectedOp("EOP");

	/* Chars now becomes "number of characters on page" */
	i = Chars = MaxChars - Chars;

	ExpectBOP++;
	ExpectEOP = 0;		/* set the new "expect" state */

	SortPage();

	if (!SFlag) {
		putc(']', stderr);
		(void) fflush(stderr);
	}
	y = yx;
	f = fcp;
	oldv = 0;
	while (--i >= 0) {
		v = *y >> 16;
		t = v - oldv;
		if (*f >= 0) {	/* setting a character */
			t = (t << 16) | (*y++ & 0xffff);
			PutLong(stdout, t);
			t = *f++;
			PutLong(stdout, t);	/* move down & place char */
		} else {	/* setting a rule */
			y++;
			if (t > 0) {	/* need to move down first */
				t = -t;
				PutLong(stdout, -1);
				PutLong(stdout, t);
			}
			t = *f++ & 0x7fffffff;
			PutLong(stdout, -1);
			PutLong(stdout, t);	/* place rule */
		}
		oldv = v;
	}

	/* Make all pages the same length */
	if (HFlag)
		t = -WidestPageWidth;
	else
		t = -TallestPageHeight;
	t -= BottomMargin + TopMargin;
	if (t) {
		PutLong(stdout, -1);	/* move down */
		PutLong(stdout, t);
	}
	PutLong(stdout, -1);
	PutLong(stdout, 0);	/* end of page */
}

/*
 * Store the relevant information from the DVI postamble, copying some of it
 * to stdout for verser2, and set up variables.
 */
PostAmbleHeader(p)
	register struct PostAmbleInfo *p;
{
	register int n;

	Numerator = p->pai_Numerator;
	Denominator = p->pai_Denominator;
	DVIMag = p->pai_DVIMag;

	/*
	 * Set the conversion factor.
	 */
	SetConversion(200, UserMag, Numerator, Denominator, DVIMag);

	TallestPageHeight = fromSP(p->pai_TallestPageHeight);
	WidestPageWidth = fromSP(p->pai_WidestPageWidth);

	n = p->pai_DVIStackSize * sizeof *dvi_stack;
	dvi_stack = (struct localstack *) malloc((unsigned) n);
	if ((dvi_stackp = dvi_stack) == 0)
		GripeOutOfMemory(n, "DVI stack");

	/* verser2 needs to set the conversion factor too */
	PutLong(stdout, 200);	/* keep raster density knowledge all here */
	PutLong(stdout, UserMag);
	PutLong(stdout, Numerator);
	PutLong(stdout, Denominator);
	PutLong(stdout, DVIMag);
}

/*
 * Handle one of the font definitions from the DVI postamble.
 */
PostAmbleFontDef(p)
	register struct PostAmbleFont *p;
{
	register struct fontinfo *fi;
	register struct font *f;
	register char *s;
	register int n;
	char *fname;
	int def = S_CREATE | S_EXCL;

	if (NextFont >= NFONTS)	/* fix this later */
		error(1, 0, "too many fonts (%d) used", NextFont);

	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 = GetRasterlessFont(p->paf_name, p->paf_DVIMag,
		p->paf_DVIDesignSize, "versatec", &fname);
	if ((fi->f = f) == NULL) {
		GripeCannotGetFont(p->paf_name, p->paf_DVIMag,
			p->paf_DVIDesignSize, "versatec", 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);

	putchar(1);		/* signal another font */
	PutLong(stdout, p->paf_DVIChecksum);
	PutLong(stdout, p->paf_DVIMag);
	PutLong(stdout, p->paf_DVIDesignSize);
	n = p->paf_n1 + p->paf_n2;
	PutLong(stdout, n);
	fputs(p->paf_name, stdout);

	ComputeCWidths(fi);

	fi->index = NextFont++;
	fi->pspace = p->paf_DVIMag / 6;	/* a three-unit "thin space" */
	fi->nspace = -4 * fi->pspace;
	fi->vspace = 5 * fi->pspace;
}

/*
 * 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);
	putchar(0);		/* mark end of fonts */
}

/*
 * 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;
	register char *s;
	int lmargin;

	setbuf(stderr, serrbuf);

	ProgName = *argv;
	UserMag = 1000;
	MaxDrift = DefaultMaxDrift;

	while ((c = getopt(argc, argv, "cd:hm:sD")) != EOF) {
		switch (c) {

		case 'c':
			CFlag++;/* centered output */
			break;

		case 'd':	/* max drift value */
			MaxDrift = atoi(optarg);
			break;

		case 'h':	/* horizontal output */
			HFlag++;
			break;

		case 'm':	/* magnification */
			UserMag = atoi(optarg);
			break;

		case 's':	/* silent */
			SFlag++;
			break;

		case 'D':
			Debug++;
			break;

		case '?':
			(void) fprintf(stderr, "\
Usage: %s [-c] [-h] [-m mag] [-s] [file]\n",
				ProgName);
			(void) fflush(stderr);
			exit(1);
		}
	}
	if (optind < argc)
		if (freopen(argv[optind], "r", stdin) == NULL)
			error(1, -1, "can't open %s", argv[optind]);

	if (MakeSeekable(stdin))
		error(1, 0,
			"unable to copy input to temp file (see the manual)");

	if ((TeXFontDesc = getenv(CONFENV)) == NULL)
		TeXFontDesc = "";

	c = (VERSION << 1) + (HFlag ? 1 : 0);
	putchar(c);
	c = strlen(s = TeXFontDesc);
	PutLong(stdout, c);
	while (--c >= 0)
		putchar(*s++);

	ReadPostAmble();

	/*
	 * The character plotter must check each character to ensure it
	 * is on the page, because the widest and tallest page values from
	 * the DVI file are not always accurate; so these tests do little
	 * save to keep one from finding the offending object.  Accordingly,
	 * I have disabled them.  20 May 1984 ACT.
	 */
#ifdef notdef
	if (HFlag) {
		if (MaxPageWidth - MinimumLeftMargin < TallestPageHeight)
			error(1, 0, "text object too high!");
		if (MaxPageHeight - MinimumTopMargin < WidestPageWidth)
			error(1, 0, "text object too wide!");
	} else {		/* page height can be safely ignored */
		if (MaxPageWidth - MinimumLeftMargin < WidestPageWidth)
			error(1, 0, "text object too wide!");
	}
#endif

	/* Determine margins */
	/* THIS CODE NEEDS WORK */
	if (CFlag) {
		lmargin = HFlag ? TallestPageHeight : WidestPageWidth;
		lmargin = (MaxPageWidth - lmargin) >> 1;
		if (lmargin < MinimumLeftMargin) {
			lmargin = MinimumLeftMargin;
			error(0, 0, "\
cannot center (page too wide); flush left instead");
		}
	} else
		lmargin = HFlag ? DefaultTopMargin : DefaultLeftMargin;

	if (HFlag) {
		TopMargin = (MaxPageHeight - WidestPageWidth) >> 1;
		if (TopMargin < 0)
			TopMargin = 0;
		BottomMargin = MaxPageHeight - TopMargin - WidestPageWidth;
		if (BottomMargin < 0)
			BottomMargin = 0;
	} else {
		TopMargin = DefaultTopMargin;
		BottomMargin = DefaultBottomMargin;
	}

	HHMargin = lmargin;
	VVMargin = TopMargin;

	ReadPreAmble();
	ExpectBOP++;

	/* Allocate arrays */
	MaxChars = InitialChars;
	if ((yx = (int *) malloc(InitialChars * sizeof *yx)) == NULL)
		GripeOutOfMemory(InitialChars * sizeof *yx, "yx array");
	if ((fcp = (int *) malloc(InitialChars * sizeof *fcp)) == NULL)
		GripeOutOfMemory(InitialChars * sizeof *fcp, "fcp array");

	/*
	 * 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();

	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);
}

/*
 * Perform a \special - right now ignore all of them.
 */
DoSpecial(len)
	i32 len;		/* length of the \special string */
{

	error(0, 0, "warning: ignoring \\special");
	(void) fseek (stdin, (long) len, 1);
}

#ifndef lint
#define maxysize min(MaxCharHeight, 255)
#else
#define maxysize 255
#endif

/*
 * 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 */
	i32 rheight;		/* rule height from DVI file */
	register int y;		/* temporary y value */
	register int x;		/* temporary x value */
	register int h, w;	/* temporaries */
	register int ymax;	/* bottommost (versatec-wise) y coord */
	register int xmax;	/* rightmost (versatec-wise) x coord */
	register int ymin;	/* topmost y coord */
	register int xmin;	/* leftmost x coord */
	int anybad = 0;

	fGetLong(stdin, rheight);
	fGetLong(stdin, rwidth);

	h = ConvRule(rheight);
	w = ConvRule(rwidth);

	if (!HFlag) {
		xmin = hh;
		ymax = vv;
		ymin = ymax - h;
		xmax = xmin + w;
	} else {
		ymin = hh - FFMargin;
		xmin = MaxPageWidth - vv - 1;	/* ???DO I NEED -1 ANYMORE?? */
		xmax = xmin + h;
		ymax = ymin + w;
		if (ymax > MaxPageHeight - FFMargin)
			ymax = MaxPageHeight - FFMargin, anybad++;
	}
	if (ymin < 0)
		ymin = 0, anybad++;
	if (xmin < 0)
		xmin = 0, anybad++;
	if (xmax > MaxPageWidth)
		xmax = MaxPageWidth, anybad++;
	if (anybad && !RuleEMsg) {
		error(0, 0, "WARNING: rule(s) off page edge; clipped to fit");
		RuleEMsg++;
	}
	if (advance) {
		hh += w;
		dvi_h += rwidth;
		x = fromSP(dvi_h);
		FIXDRIFT(hh, x);
	}
	for (y = ymin; y < ymax; y += h) {
		for (x = xmin; x < xmax; x += w) {
			h = ymax - y;
			h = min(h, maxysize);
			w = xmax - x;
			w = min(w, 255);
			if (--Chars < 0)
				ExpandArrays();
			*nextyx++ = (y << 16) | x;
			*nextfcp++ = (1 << 31) | (x << 16) | (h << 8) | w;
		}
	}
}

/* this driver needs work for codes > 127 */
char	chartoobig[] = "Character code %d is too big; ignored";

/*
 * Used inside the dump-char code to ensure chars are within page limits:
 */
#define CHECK(cond, msg, flag) \
	if (cond) { \
		if (!flag) { \
			error(0, 0, warn, msg); \
			flag++; \
		} \
		goto ignore; \
	}

/*
 * 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;
	register struct glyph *g;
	register i32 p;
	int advance;
	static char warn[] = "\
WARNING: text object(s) run off %s of page; ignored";

	/*
	 * 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 hte
	 * program run a bit faster.
	 */
	if (DVI_IsChar(c)) {
		advance = 1;
do_char:
		{
			register struct font *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;
			}
		}
		{
			register int ulcx, ulcy, height;

			if (!HFlag) {
				ulcy = vv - g->g_yorigin;
				ulcx = hh - g->g_xorigin;
				height = g->g_height;
				CHECK(ulcy < 0, "top", TopEMsg);
				CHECK(ulcx < 0, "left", LeftEMsg);
				CHECK(ulcx + g->g_width >= MaxPageWidth,
					"right", RightEMsg);
			} else {/* rotate & translate */
				ulcy = hh - g->g_xorigin - FFMargin;
				ulcx = MaxPageWidth -
					(vv + g->g_height - g->g_yorigin);
				height = g->g_width;
				CHECK(ulcy < 0, "left", LeftEMsg);
				CHECK(ulcy+height >= MaxPageHeight-FFMargin,
					"right", RightEMsg);
				CHECK(ulcx < 0, "bottom", BottomEMsg);
				CHECK(ulcx + g->g_height >= MaxPageWidth,
					"top", TopEMsg);
			}
			p = 0;
			while (height > 0) {
				if (--Chars < 0)
					ExpandArrays();
				*nextyx++ = (ulcy << 16) | ulcx;
				*nextfcp++ = (CurrentFont->index<<FONTSHIFT) |
					((c & CHARMASK) << CHARSHIFT) | p;
				height -= MaxCharHeight;
				ulcy += MaxCharHeight;
				p++;
			}
		}
ignore:
		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 */
	}

	/*
	 * Now that we have the parameter, perform the
	 * command.
	 */
	switch (DVI_DT(c)) {

	case DT_SET:
		advance = 1;
		c = p;
		if (c > 127) {
			error(0, 0, chartoobig, c);
			break;
		}
		goto do_char;

	case DT_PUT:
		advance = 0;
		c = p;
		if (c > 127) {
			error(0, 0, chartoobig, c);
			break;
		}
		advance = 0;
		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 (!ExpectBOP)
			GripeUnexpectedOp("POST");
		if (!SFlag) {
			(void) fprintf(stderr, "\n");
			(void) fflush(stderr);
		}
		return;

	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;
}