/* Copyright (c) 1993 Neal Becker
 * All rights reserved.
 * Permission to copy for any purpose is hereby granted
 * so long as this copyright notice remains intact.
*/

/*
 * Copyright (c) 1987, 1989 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 const char rcsid[] = "$Header: c:/mctex/imagen/rcs/eps.c 1.1 91/08/05 23:40:48 ROOT_DOS Exp Locker: ROOT_DOS $";
#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]
 */

/*
$Log:	eps.c $
 * Revision 1.1  91/08/05  23:40:48  ROOT_DOS
 * Initial revision
 * 
*/

#include "PROTO.h"
#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 "version.h"

#include "epson.h"
#include "Bset.h"

#include <stdlib.h> /* for atoi, exit */

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

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

/*
 * We store Imagen font family numbers in the f_int field the font
 * routines allocate for us.
 */

int	LFlag;			/* -l => landscape mode */
int	ReversePages;		/* -p => [no] page reversal */
int	SFlag;			/* -s => silent (no page numbers) */
int	Debug;			/* -D => debug flag */

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

char	*DVIFileName;		/* name of input DVI file */

epson* Printer;

BitMap* theBitMap;

#ifdef TWOPASS
static int Pass = 0;
#endif

static void LoadGlyph(struct glyph* g);
static void SetGlyph( int c, struct font* f, struct glyph* g);

/*
 * Assign a unique Imagen family to each font in the DVI file.
 */
struct font *
DefineFont(char* name, i32 dvimag, i32 dvidsz)
{
	register struct font *f;
	char *path;

	f = GetFont(name, dvimag, dvidsz, PrintEngine, &path);
	if (f == NULL) {
		GripeCannotGetFont(name, dvimag, dvidsz, PrintEngine, path);
		return (NULL);
	}
	if (Debug) {
		(void) fprintf(stderr, "[%s -> %s]\n", Font_TeXName(f), path);
		(void) fflush(stderr);
	}
	return (f);
}

/*
 * Start a new page (interpreter found a DVI_BOP).
 */
void
BeginPage(i32* count)
{

	if (!SFlag) {
		static int beenhere;

		if (beenhere)
			(void) putc(' ', stderr);
		else
			beenhere = 1;
		(void) fprintf(stderr, "[%ld", (long)count[0]);
		(void) fflush(stderr);
	}
  theBitMap->Clear();
  Printer->Reset();
}

/*
 * End the page (interpreter found a DVI_EOP).
 */
void
EndPage( )
{

	if (!SFlag) {
		(void) putc(']', stderr);
		(void) fflush(stderr);
	}
#ifdef TWOPASS
  Printer->PrintBitMap( *theBitMap );
  if( Pass % 2 == 1 ) /* finished 2nd pass */
    Printer->EoP();
  else /* finished first pass */
    Printer->SyncVpos();
#else
	Printer->PrintBitMap( *theBitMap );
	Printer->EoP();
#endif /* TWOPASS */
}

/*
 * Perform a \special.
 * This version ignores all, with a warning.
 */
void
DoSpecial(i32 len)
{

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

/*
 * Set a rule at dvi_hh, dvi_vv.
 */
void
SetRule(i32 h, i32 w)
{
  if( LFlag == 0 ) {
    for( int i = 0; i < h; i++ ) {
      int Row = dvi_vv - i;
#ifdef TWOPASS
      if( Pass % 2 == 1 )
        Row -= BitMapRows;
#endif
      int Pos = dvi_hh - LeftMarginPixels;
      if( Pos >= 0 ) {
        if( Row >= 0 && Row < BitMapRows )
          theBitMap->SetBits( w, Row, Pos );
      }
    }
  }
  else {
    for( int i = 0; i < w; i++ ) {
      int Row = dvi_hh + i;
#ifdef TWOPASS
      if( Pass % 2 == 1 )
        Row -= BitMapRows;
#endif
      if( Row >= 0 && Row < BitMapRows ) {
        int Pos = HPixels - dvi_vv - LeftMarginPixels;
        if( Pos >= 0 )
          theBitMap->SetBits( h, Row, Pos );
      }
    }
  }
}

/*
 * Main
 page loop.  This reads one page of the DVI file.
 * Returns 1 for EOP and 0 for end of last page (POST).
 */
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;
#ifdef TWOPASS
	int Pass = 0;
	long PageStartPos;
#endif

	if (ReversePages)
		if (ds.ds_prevpage == -1)
			return (0);	/* Mark 10:31 (kilroy was here) */
		else
			(void) fseek(fp, ds.ds_prevpage, 0);
#ifdef TWOPASS
	PageStartPos = ftell( fp );
#endif

	/*
	 * This would be a `for (;;)', but that makes the function
	 * crawl off the right of the screen.
	 *
	 * We handle ordinary characters early, as they are the
	 * most common opcodes in DVI files, and doing so makes the
	 * loop run faster.
	 */
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)
			LoadGlyph(g);
                SetGlyph( (int)p, f, g);
		if (advance) {
			dvi_h += g->g_tfmwidth;
			dvi_hh += g->g_pixwidth;
			p = fromSPh(dvi_h);
			FIXDRIFT(dvi_hh, p);
		}
		goto loop;
	}

	if (c == EOF)		/* unexpected end of DVI file */
		GripeUnexpectedDVIEOF();

	/*
	 * Gather up a parameter, if known.
	 */
	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 */
	}

	/*
	 * Now switch on the type.
	 */
	switch (DVI_DT(c)) {

	case DT_SET:
		advance = 1;
		goto do_char;

	case DT_PUT:
		advance = 0;
		goto do_char;

	case DT_SETRULE:
		DVIRule(SetRule, 1);
		goto loop;

	case DT_PUTRULE:
		DVIRule(SetRule, 0);
		goto loop;

	case DT_NOP:
		goto loop;

	case DT_BOP:
		if (doingpage)
			GripeUnexpectedOp("BOP (already in page)");
		DVIBeginPage(BeginPage);
		doingpage = 1;
		goto loop;

	case DT_EOP:
		if (!doingpage)
			GripeUnexpectedOp("EOP (no BOP)");
		EndPage();
#ifdef TWOPASS
		Pass++;
		if( Pass % 2 == 1 ) {
		  fseek( fp, PageStartPos, 0 );
		  doingpage = 0;
		  goto loop;
		}
		else
#endif
		return (1);

	case DT_PUSH:
		*ds.ds_sp++ = ds.ds_cur;
		goto loop;

	case DT_POP:
		ds.ds_cur = *--ds.ds_sp;
		goto loop;

	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 += fromSPh(p);
			p = fromSPh(dvi_h);
			FIXDRIFT(dvi_hh, p);
		} else
			dvi_hh = fromSPh(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 += fromSPv(p);
			p = fromSPv(dvi_v);
			FIXDRIFT(dvi_vv, p);
		} else
			dvi_vv = fromSPv(dvi_v);
		goto loop;

	case DT_FNTNUM:
		f = DVIFindFont((i32)(c - DVI_FNTNUM0));
		goto loop;

	case DT_FNT:
		f = DVIFindFont(p);
		goto loop;

	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 */

	case DT_UNDEF:
		GripeUndefinedOp(c);
		/* NOTREACHED */

	default:
		panic("DVI_DT(%d) = %d", c, DVI_DT(c));
		/* NOTREACHED */
	}
	/* NOTREACHED */
}

#if defined( __MSDOS__ )
#  if !defined( __GNUC__ )
#    include <io.h>
#    include <alloc.h>
#  endif /* __GNUC__ */
#  include <fcntl.h>
extern int _fmode;
#endif

void ShowUse( const char* ProgName ) {
  fprintf( stderr, "Usage: %s [delmpHVsDXYfv]\n", ProgName );
  fprintf( stderr, "\
  \t-d maxdrift\n\
  \t-e engine\n\
  \t-l landscape\n\
  \t-m mag\n\
  \t-p toggle page reversal\n\
  \t-H horiz. res. (dpi)\n\
  \t-V vert. res. (dpi)\n\
  \t-s silent\n\
  \t-D debug\n\
  \t-X x offset in 0.001 in\n\
  \t-Y y offset in 0.001 in\n\
  \t-f fast horiz space\n\
  \t-v show version number\n\
  ");
  exit( 2 );
}

main(int argc, char **argv)
{
	register int c;
	int xoff = 0, yoff = 0;	/* margins from -X and -Y */
	int hdpi = DefaultDPIh;	/* resolution from -r */
	int vdpi = DefaultDPIv;	/* resolution from -r */
	FILE *fp = stdin;
  int FastSpace = 0;

	setbuf(stderr, serrbuf);
#ifdef __MSDOS__
	_fmode = O_BINARY;
  setmode( fileno( stdout ), O_BINARY );
  setmode( fileno( stdin ), O_BINARY );
#endif

	ProgName = *argv;
	ds.ds_usermag = 1000;
	ds.ds_maxdrift = DefaultMaxDrift;
	ReversePages = 0;	/* default is to reverse pages */
	PrintEngine = "epson";
	DVIFileName = "`stdin'";

	while ((c = getopt(argc, argv, "d:e:lm:pH:V:sDX:Y:fv")) != 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 'H':	/* resolution */
			hdpi = atoi(optarg);
			break;

		case 'V':	/* resolution */
			vdpi = atoi(optarg);
			break;

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

		case 'D':
			Debug++;
			break;

		case 'X':	/* x offset, in 1/1000 inch increments */
			xoff = atoi(optarg);
			break;

		case 'Y':	/* y offset */
			yoff = atoi(optarg);
			break;
  case 'f':				/* fast spaces */
		  FastSpace = 1;
		  break;
		case 'v':
			fprintf( stderr, "eps version %s\n", EpsVersion );
		case '?':
  ShowUse( ProgName );
		}
	}

	if (optind < argc)
		if ((fp = fopen(DVIFileName = argv[optind], "r")) == NULL)
			error(1, errno, "can't open %s", DVIFileName);

#if defined( __MSDOS__ ) && !defined( __GNUC__ )
  if( Debug )
    fprintf( stderr, "Start: brk(0)=%p,coreleft=%lx\n",
             sbrk(0), coreleft() );
#endif
	DVISetState(fp, DefineFont, hdpi, vdpi, xoff, yoff);
#if defined( __MSDOS__ ) && !defined( __GNUC__ )
  if( Debug )
    fprintf( stderr, "After DVISetState: brk(0)=%p,coreleft=%lx\n",
             sbrk(0), coreleft() );
#endif
  theBitMap = new BitMap;
#if defined( __MSDOS__ ) && !defined( __GNUC__ )
  if( Debug )
    fprintf( stderr, "After BitMap: brk(0)=%p,coreleft=%lx\n",
             sbrk(0), coreleft() );
#endif

  Printer = new epson( stdout, FastSpace );

	/* All set! */
	while (PageLoop())
		/* void */;
	if (!SFlag) {
		(void) fprintf(stderr, "\n");
		(void) fflush(stderr);
	}
	(void) fflush(stdout);
	if (ferror(stdout))
		error(1, -1, "error writing imPRESS output");

	exit(0);
	/* NOTREACHED */
}

static void ShowBits( char x )
{
  for( int i = 7; i >= 0; --i ) {
    if( x & BitMask[ i ] )
      putc( '*', stdout );
    else
      putc( ' ', stdout );
  }
}


/*
 * Download the character c of font f (glyph=g).
 * The value of c is guaranteed to be in [0..127].
 */
static void
LoadGlyph(struct glyph* g)
{
	g->g_pixwidth = fromSPh(g->g_tfmwidth);
	g->g_flags |= GF_SEEN;
}

static void
SetGlyph(int c, struct font* f, struct glyph* g)
{
	register char *p;
	register int i, j, w;

	if (!HASRASTER(g))	/* never load dull glyphs */
		return;		/* (imagen cannot handle them) */

	if (!LFlag) {
		p = RASTER(g, f, ROT_NORM);
	} else {
		p = RASTER(g, f, ROT_RIGHT);
	}


	/* Define the character */

	/*
	 * Now put out the bitmap.
	 */
/*  fprintf( stderr, "adv:%ld width:%ld xor:%ld height:%ld yor:%ld\n",
    (long)(g->g_pixwidth), (long)(g->g_width), (long)(g->g_xorigin), (long)(g->g_height), (long)(g->g_yorigin) );
*/
	w = (g->g_width + 7) >> 3;
	for (i = 0; i < g->g_height; i++ ) {
          int Row = ( LFlag ) ?
	    ( dvi_hh - g->g_yorigin + i ) :
	    ( dvi_vv - g->g_yorigin + i ) ;
#ifdef TWOPASS
	  if( Pass % 2 == 1 )
	    Row -= BitMapRows;
#endif
	  if( Row >= 0 && Row < BitMapRows ) {
	    int Pos = ( LFlag ) ?
	        HPixels - dvi_vv - g->g_xorigin :
	        dvi_hh - g->g_xorigin;
	    Pos -= LeftMarginPixels;
	    if( Pos >= 0 )
	      theBitMap->Set( p, w, Row, Pos );
	  }
  p += w;
	}
/*
	for (i = 0; i < g->g_height; i++ ) {
		for (j = 0; j < w; j++ ) {
                        ShowBits( BitMap[ dvi_vv - g->g_yorigin + i ][(dvi_hh - g->g_xorigin)/8 + j] );
		}
	  putc( '\n', stdout );
	}
  putc( '\n', stdout );
*/
}
