DviContext *mdvi_init_context(DviParams *par, DviPageSpec *spec, const char *file)
{
	FILE	*p;
	Int32	arg;
	int	op;
	long	offset;
	int	n;
	DviContext *dvi;
	char	*filename;
	int	pagecount;

	/*
	 * 1. Open the file and initialize the DVI context
	 */	

	filename = opendvi(file);
	if(filename == NULL) {
		perror(file);
		return NULL;
	}
	p = fopen(filename, "rb");
	if(p == NULL) {
		perror(file);
		mdvi_free(filename);
		return NULL;
	}
	dvi = xalloc(DviContext);
	memzero(dvi, sizeof(DviContext));
	dvi->pagemap = NULL;
	dvi->filename = filename;
	dvi->stack = NULL;
	dvi->modtime = get_mtime(fileno(p));	
	dvi->buffer.data = NULL;
	dvi->pagesel = spec;
	dvi->in = p; /* now we can use the dget*() functions */

	/* 
	 * 2. Read the preamble, extract scaling information, and 
	 *    setup the DVI parameters.
	 */

	if(fuget1(p) != DVI_PRE)
		goto bad_dvi;
	if((arg = fuget1(p)) != DVI_ID) {
		mdvi_error(_("%s: unsupported DVI format (version %u)\n"),
			   file, arg);
		goto error; /* jump to the end of this routine, 
			     * where we handle errors */
	}
	/* get dimensions */
	dvi->num = fuget4(p);
	dvi->den = fuget4(p);
	dvi->dvimag = fuget4(p);

	/* check that these numbers make sense */
	if(!dvi->num || !dvi->den || !dvi->dvimag)
		goto bad_dvi;
	
	dvi->params.mag = 
		(par->mag > 0 ? par->mag : (double)dvi->dvimag / 1000.0);
	dvi->params.hdrift  = par->hdrift;
	dvi->params.vdrift  = par->vdrift;
	dvi->params.dpi     = par->dpi ? par->dpi : MDVI_DPI;
	dvi->params.vdpi    = par->vdpi ? par->vdpi : par->dpi;
	dvi->params.hshrink = par->hshrink;
	dvi->params.vshrink = par->vshrink;
	dvi->params.density = par->density;
	dvi->params.gamma   = par->gamma;
	dvi->params.conv    = (double)dvi->num / dvi->den;
	dvi->params.conv   *= (dvi->params.dpi / 254000.0) * dvi->params.mag;
	dvi->params.vconv   = (double)dvi->num / dvi->den;
	dvi->params.vconv  *= (dvi->params.vdpi / 254000.0) * dvi->params.mag;
	dvi->params.tfm_conv = (25400000.0 / dvi->num) *
				((double)dvi->den / 473628672) / 16.0;
	dvi->params.flags = par->flags;
	dvi->params.orientation = par->orientation;
	dvi->params.fg = par->fg;
	dvi->params.bg = par->bg;

	/* initialize colors */
	dvi->curr_fg = par->fg;
	dvi->curr_bg = par->bg;
	dvi->color_stack = NULL;
	dvi->color_top = 0;
	dvi->color_size = 0;

	/* pixel conversion factors */
	dvi->dviconv = dvi->params.conv;
	dvi->dvivconv = dvi->params.vconv;
	if(dvi->params.hshrink)
		dvi->params.conv /= dvi->params.hshrink;
	if(dvi->params.vshrink)
		dvi->params.vconv /= dvi->params.vshrink;

	/* get the comment from the preamble */
	n = fuget1(p);
	dvi->fileid = mdvi_malloc(n + 1);
	fread(dvi->fileid, 1, n, p);
	dvi->fileid[n] = 0;
	DEBUG((DBG_DVI, "%s: %s\n", filename, dvi->fileid));
	
	/*
	 * 3. Read postamble, extract page information (number of
	 *    pages, dimensions) and stack depth.
	 */

	/* jump to the end of the file */
	if(fseek(p, (long)-1, SEEK_END) == -1)
		goto error;	
	for(n = 0; (op = fuget1(p)) == DVI_TRAILER; n++)
		if(fseek(p, (long)-2, SEEK_CUR) < 0)
			break;
	if(op != arg || n < 4)
		goto bad_dvi;
	/* get the pointer to postamble */
	fseek(p, (long)-5, SEEK_CUR);
	arg = fuget4(p);
	/* jump to it */
	fseek(p, (long)arg, SEEK_SET);
	if(fuget1(p) != DVI_POST)
		goto bad_dvi;
	offset = fuget4(p);
	if(dvi->num != fuget4(p) || dvi->den != fuget4(p) ||
	   dvi->dvimag != fuget4(p))
		goto bad_dvi;
	dvi->dvi_page_h = fuget4(p);
	dvi->dvi_page_w = fuget4(p);
	dvi->stacksize = fuget2(p);
	dvi->npages = fuget2(p);
	DEBUG((DBG_DVI, "%s: from postamble: stack depth %d, %d page%s\n",
		filename, dvi->stacksize, dvi->npages, dvi->npages > 1 ? "s" : ""));
	
	/*
	 * 4. Process font definitions.
	 */

	/* process font definitions */
	dvi->nfonts = 0;
	dvi->fontmap = NULL;
	/* 
	 * CAREFUL: here we need to use the dvi->buffer, but it might leave the
	 * the file cursor in the wrong position after reading fonts (because of
	 * buffering). It's ok, though, because after the font definitions we read
	 * the page offsets, and we fseek() to the relevant part of the file with
	 * SEEK_SET. Nothing is read after the page offsets.
	 */
	while((op = duget1(dvi)) != DVI_POST_POST) {
		DviFontRef *ref;
		
		if(op == DVI_NOOP)
			continue;
		else if(op < DVI_FNT_DEF1 || op > DVI_FNT_DEF4)
			goto error;
		ref = define_font(dvi, op);
		if(ref == NULL)
			goto error;
		ref->next = dvi->fonts;
		dvi->fonts = ref;
		dvi->nfonts++;
	}
	/* we don't need the buffer anymore */
	dreset(dvi);
	
	if(op != DVI_POST_POST)
		goto bad_dvi;
	font_finish_definitions(dvi);
	DEBUG((DBG_DVI, "%s: %d font%s required by this job\n",
		filename, dvi->nfonts, dvi->nfonts > 1 ? "s" : ""));
	dvi->findref = font_find_mapped;

	/*
	 * 5. Build the page map.
	 */

	dvi->pagemap = xnalloc(PageNum, dvi->npages);
	memzero(dvi->pagemap, sizeof(PageNum) * dvi->npages);
		
	n = dvi->npages - 1;
	pagecount = n;
	while(offset != -1) {
		int	i;
		PageNum page;

		fseek(p, offset, SEEK_SET);		
		op = fuget1(p);
		if(op != DVI_BOP || n < 0)
			goto bad_dvi;
		for(i = 1; i <= 10; i++)
			page[i] = fsget4(p);
		page[0] = offset;
		offset = fsget4(p);
		/* check if the page is selected */
		if(spec && mdvi_page_selected(spec, page, n) == 0) {
			DEBUG((DBG_DVI, "Page %d (%ld.%ld.%ld.%ld.%ld.%ld.%ld.%ld.%ld.%ld) ignored by request\n",
			n, page[1], page[2], page[3], page[4], page[5],
			   page[6], page[7], page[8], page[9], page[10]));
		} else {
			memcpy(&dvi->pagemap[pagecount], page, sizeof(PageNum));
			pagecount--;
		}
		n--;
	}
	pagecount++;
	if(pagecount >= dvi->npages) {
		mdvi_error(_("no pages selected\n"));
		goto error;
	}
	if(pagecount) {
		DEBUG((DBG_DVI, "%d of %d pages selected\n",
			dvi->npages - pagecount, dvi->npages));
		dvi->npages -= pagecount;
		memmove(dvi->pagemap, &dvi->pagemap[pagecount], 
			dvi->npages * sizeof(PageNum));
	}

	/*
	 * 6. Setup stack, initialize device functions
	 */

	dvi->curr_layer = 0;
	dvi->stack = xnalloc(DviState, dvi->stacksize + 8);

	dvi->device.draw_glyph   = dummy_draw_glyph;
	dvi->device.draw_rule    = dummy_draw_rule;
	dvi->device.alloc_colors = dummy_alloc_colors;
	dvi->device.create_image = dummy_create_image;
	dvi->device.free_image   = dummy_free_image;
	dvi->device.dev_destroy  = dummy_dev_destroy;
	dvi->device.put_pixel    = dummy_dev_putpixel;
	dvi->device.refresh      = dummy_dev_refresh;
	dvi->device.set_color    = dummy_dev_set_color;
	dvi->device.device_data  = NULL;

	DEBUG((DBG_DVI, "%s read successfully\n", filename));
	return dvi;

bad_dvi:
	mdvi_error(_("%s: File corrupted, or not a DVI file\n"), file);
error:
	/* if we came from the font definitions, this will be non-trivial */
	dreset(dvi);
	mdvi_destroy_context(dvi);
	return NULL;
}