/*
		Treiber fr Canon BJ200 Drucker fr Atari DVI-Treiber (Version 2.30):
		Fr Quer- und Hochkant!

		- DVI-Druckertreiber fr Canon BJ200 Drucker. Der Sourcecode entspricht
			(immer noch) gr”átenteils der EPSON-Stylus Version. 

		- WICHTIG: Dieser Treiber erwartet das der Canon BJ in der
											____BJ-Emulation____
			betrieben wird! Fr die EPSON-Emulation kann man den EPSON LQ800
			Treiber benutzen. Letzterer bietet nur keine echten 360*360 dpi
			Aufl”sung (Eine Nadel wird in der EPSON-Emulation immer durch zwei
			Dsen emuliert) und auch keinen 48 Dsen Druck :-(.

		- Bemerkung: Ich habe diesen Treiber nur teilweise getestet!
								 (Nicht nur) Deshalb:

			BENUTZUNG AUF EIGENE GEFAHR. ICH šBERNEHME KEINE VERANTWORTUNG
			FšR EVENTUELLE SCHŽDEN, DIE DURCH DIE BENUTZUNG DIESES TREIBERS
			VERURSACHT WURDEN. ICH BEHAUPTE AUCH NICHT, DASS DIESES PROGRAMM
			IRGENDEINEN ZWECK ERFšLLT.
			WER HIERMIT NICHT EINVERSTANDEN IST, DARF DIESEN TREIBER NICHT
			BENUTZEN!!

			Verbesserungsvorschl„ge sind willkommen:

			Derzeitige Adresse:
			
			Dietmar Herrend”rfer,
			Department of Physics,
			Trinity College,
			Dublin 2
			Ireland
			email: dhrrndrf@tcd.ie
			
		- Version 1.2

		½ 1995, Dietmar Herrend”rfer.
		½ Markus Pristovsek, fr die Teile, die aus anderen Treibern wurden.
		
		Bem.: Tabulatorabstand auf 4 Zeichen setzen.
*/

/* Weitere Bemerkung: Der Treiber wurde von mir (Markus Pristovsek) in neue Form 
eingepažt. Theoretisch sollte er funktionieren, kann jedoch von mir mangels Drucker 
nicht getestet werden. */

#include <limits.h>
#include <stdlib.h>
#include <string.h>
#include <portab.h>
#include "treiber.h"



/*** Ein paar Parameter: ***/
/*++ Seitenabmessungen - alle Pixelgr”áen bezogen auf 360dpi*360dpi: ++*/
#define TOP 				43L 														/* Oberer Rand in Pixeln. */
#define BOTTOM			90L 														/* Unterer Rand in Pixeln. */
#define LEFT				47L 														/* Linker Rand in Pixeln. */
#define RIGHT 			49L 														/* Rechter Rand in Pixel. */
#define A4WIDTH 		2976L 													/* A4 Papierbreite in Pixeln. */
#define A4HEIGHT		4209L 													/* A4 Papierh”he in Pixeln. */
#define WIDTH 			(A4WIDTH-LEFT-RIGHT)						/* max. Breite einer Grafikseite */
#define HEIGHT			(A4HEIGHT-TOP-BOTTOM) 					/* max. Bitmap H”he */
#define BUF_SIZE		WIDTH*6L+6L+256L								/* Gr”áe des ben”tigten Zeilenpuffers. */

/*** Neudefinierte Typen: ***/
typedef struct {
	UBYTE 	id; 												/* Druckmodusnummer. */
	WORD		h_dpi;											/* Horizontale Aufl”sung. */
	WORD		v_dpi;											/* Vertikale Aufl”sung. */
	WORD		bytes;											/* Bytes pro Druckkopf-Spalte. */
} MODI;

/*** Lokale Variablen: ***/
/*++ Drucker Steuercodes und „hnliches Druckerspezifisches: ++*/
static UBYTE		init[]		= "\033[K\002\000\000\045"/* Reset & 1/360 dpi spacing. */
														"\033[\\\004\000\000\000\150\001";
static UBYTE		h_step[]	= "\033d__";							/* Horizontale Druckkopfbewegung. */
static UBYTE		v_step[]	= "\034CJ\004_\015";			/* Zeilenvorschub in 360 dpi. */
static UBYTE		v_skip[]	= "\034CJ\000\200\015"; 	/* 256/360 Inch Zeilenvorschub. */
static UBYTE		g_print[] = "\033[g"; 							/* Bitmap-Graphik drucken. */
static MODI 		cbj_mode[]= {{	0,	60,  60, 1},		/* Canon BJ200 Druckermodi: */
														 {	1, 120,  60, 1},
														 {	2, 120,  60, 1},
														 {	3, 240,  60, 1},
														 {	8,	60, 180, 3},
														 {	9, 120, 180, 3},
														 { 11, 180, 180, 3},
														 { 12, 360, 180, 3},
														 { 13, 120, 360, 6},
														 { 14, 180, 360, 6},
														 { 15, 240, 360, 6},
														 { 16, 360, 360, 6},
														 {	0,	 0, 	0, 0}}; 	/* Markiert das Listenende !*/

/****************************************************************************************/

/*** Globale Variablen: ***/
LONG	max_puffer_laenge = BUF_SIZE; 	/* Obergrenze der Gr”áe des Druckpuffers. */

/*** Lokale Variable: ***/
static UBYTE		prt_buffer[BUF_SIZE]; 							/* Zwischenspeicher fr eine Zeile */


/* šberspringt "Rows" leerzeilen */
void	skip_rows( WORD out_channel, LONG rows )
{
	v_step[4] = (UBYTE)255;	/* Maximal 255 Zeilen pro Vorschub: */
	while(  rows>255L  )
	{
		print_block( 6L, v_step, out_channel );
		rows -= 255L;
	}
	v_step[4] = (UBYTE)rows;	/* und den Rest */
	print_block( 6L, v_skip, out_channel );
}
/* 7.10.95 */


/****************************************************************************************/
/* Ausdruck einer Seite als Landscape */

WORD drucke_quer(UBYTE *bitmap_ptr, LONG h_offset, LONG hsize_p, LONG vsize_p, LONG h_dpi, LONG v_dpi, WORD out_channel, WORD flag )
{
	UBYTE					*bit_ptr, *prt_ptr;				/* Pointer in die Bitmap und in den Druckpuffer. */
	MODI					*mode, *tmp_mode;					/* Zeiger auf die Modus-Tabelle. */
	LONG					hdpi_diff, vdpi_diff;			/* Zum Festlegen der Aufl”sung. */
	register LONG	tmp;											/* Puffer fr alles m”gliche. */
	register LONG	hsize_b, hsize_m;					/* Druckseitenbreite in Bytes, Bitmapbreite in Bytes. */
	register LONG	h_pos, rows_to_skip;			/* Derzeitige Druck-Zeile, Anzahl leerer Zeilen. */
	LONG					l_margin, right, left;		/* Linker Rand, Zeilenr„nder. */
	LONG					prt_columns, prt_bytes;		/* Druckspalten pro Zeile, Spaltenh”he in Bytes. */
	int						active;										/* Ein flag. */	

	(void)h_offset;	/* ignorieren */

	/*** Initialisierung: ***/
	/*++ Gewnschte Aufl”sung an vorhandene Aufl”sungen anpassen: ++*/
	/*++ Bem.: Die optimale Anpassung der vertikalen Aufl”sung geht vor. ++*/
	tmp_mode = cbj_mode;
	hdpi_diff = vdpi_diff = LONG_MAX; /* Zun„chst maximale Abweichung. */
	do
	{
		if(  labs( tmp_mode->v_dpi - h_dpi )<hdpi_diff  )
		{
			hdpi_diff = labs(tmp_mode->v_dpi - h_dpi);
			vdpi_diff = labs(tmp_mode->h_dpi - v_dpi);
			mode = tmp_mode;
		}
		if(  labs( tmp_mode->v_dpi - h_dpi )==hdpi_diff  )
		{
			if(  labs( tmp_mode->h_dpi - v_dpi )<vdpi_diff  )
			{
				mode = tmp_mode;
				vdpi_diff = labs(tmp_mode->v_dpi - v_dpi);
			}
		}
	}
	while(  (++tmp_mode)->bytes  );	/* bytes == 0 => Ende. */
	h_dpi = mode->v_dpi;								/* Aufl”sung ggf. ab„ndern. */
	v_dpi = mode->h_dpi;
	prt_bytes = mode->bytes;

	/*++ Diverse Variablen setzen: ++*/
	strcpy(prt_buffer, g_print);						/* Bitmap-Graphik Steuerkode kopieren */

	hsize_b = (hsize_p < (HEIGHT*h_dpi)/360L) ? (hsize_p + 7L) >> 3 : (HEIGHT*h_dpi)/2880L;
	hsize_m = ((hsize_p + 15L) >> 4) << 1;				/* -> die 'Bitmap' ist eine gerade Anzahl von Bytes hoch. */

	/* Nicht druckbare R„nder berspringen. */
	if(  flag&1  )
	{
		bitmap_ptr += (tmp = (((TOP*v_dpi)/360L + 7L) >> 3)) - hsize_m*((LEFT*h_dpi)/360L);
		hsize_b -= tmp;		/* vertikale Gr”áe entsprechend korrigieren. */
	}

	/* Breite der Seite berprfen: */
	if(  vsize_p>(tmp = (WIDTH*v_dpi)/360L)  )
		vsize_p = tmp;

	
	/*++ Testen ob die Seite leer ist: ++*/
	rows_to_skip = 0;		/* Zun„chst keine leeren Zeilen. */
	for(  l_margin=0;  
	      ist_next_leer( bitmap_ptr + l_margin, hsize_m, vsize_p)  &&  l_margin < hsize_b; 
	      l_margin++  )
	  ;
	/* Leere Druckzeile ? */
	if(  l_margin==hsize_b  )
	{
		rows_to_skip = hsize_m;
		h_pos = hsize_m;
	}

	/*** Begin des Druckvorgangs: ***/
	/*++ Reset: ++*/
	if (flag & 1)
		print_block( 16L, init, out_channel );
	h_pos = 0;

	/*++ Bytes im Puffer an den Drucker ausgeben: ++*/
	active = TRUE;
	while(  active  &&  flag&2  )
	{
		/*++ Leerzeilen gesondert abarbeiten: ++*/
		while(  h_pos+rows_to_skip < hsize_m  &&  ist_next_leer( bitmap_ptr + rows_to_skip, hsize_b, vsize_p)  )
			++rows_to_skip;
		h_pos += rows_to_skip;
		if(  h_pos > hsize_b  )
			rows_to_skip = hsize_b - (h_pos - rows_to_skip);

		/* Ist diese Seite ist schon fertig? */
		if(  TRUE == (active = (h_pos < hsize_b))  )
		{
			/* Leerzeilen zu berspringen ? */
			if(  rows_to_skip>0L  )
				skip_rows( out_channel, (rows_to_skip*2880L)/h_dpi );

			/*++ R„nder einstellen: ++*/
			for( right=0;
			     right < vsize_p - 1L  &&  ist_leerzeile(bitmap_ptr + right*hsize_m, prt_bytes);
			     right++  )
			  ;
			--right;

			for( left = vsize_p - 1L;
			     left > right  &&  ist_leerzeile(bitmap_ptr + left*hsize_m, prt_bytes);
           left--  )
        ;
			++left;

			/* Linker Rand ? */
			if(  left>0  )
			{
				left = (left/3)*3;			/* Da in (ganzen) 120 dpi Schritten. */
				tmp = ((vsize_p - left)*120)/v_dpi;
				h_step[2] = (UBYTE)tmp;
				h_step[3] = (UBYTE)(tmp >> 8);
				print_block(4L, h_step, out_channel);
			}

			/*++ Daten drucken: ++*/
			prt_ptr = prt_buffer + 6;
			for(  prt_columns=left-right;  prt_columns!=0;  prt_columns--  )
			{
				bit_ptr = bitmap_ptr+(prt_columns)*hsize_m;
				for(  tmp=1; tmp<=prt_bytes; tmp++  )
					*prt_ptr++ = *bit_ptr++;
			}
			
			prt_columns = left - right;
			prt_buffer[3] = (UBYTE)(prt_columns*prt_bytes + 1L);
			prt_buffer[4] = (UBYTE)((prt_columns*prt_bytes + 1L) >> 8);
			prt_buffer[5] = (UBYTE)mode->id;

			/* šberagt Zeile unteren Rand ? */
			if(  h_pos+prt_bytes>hsize_b  )
			{
				register WORD	bit, byte, and_it;	/*  => Abschneiden. */

				bit = (WORD)(h_pos + prt_bytes - hsize_b);
				byte = bit >> 3;
				and_it = (0x00FF << (bit & 7));
				for(  rows_to_skip=0L;  rows_to_skip<prt_columns*prt_bytes;  rows_to_skip++  )
				{
					if(  rows_to_skip%prt_bytes == byte  )
						prt_buffer[6L + rows_to_skip] &= and_it;
					else
						if(  rows_to_skip%prt_bytes > byte  )
							prt_buffer[6 + rows_to_skip] = 0;
				}
			}
			print_block( 6L+prt_columns*prt_bytes, prt_buffer, out_channel );
			rows_to_skip = prt_bytes;
			bitmap_ptr += prt_bytes;
		}
	}

	/*** Seitenende bearbeiten: ***/
	if (flag & 4)
		print_block(1L, "\014\007", out_channel);

	/*** Das war es: ***/
	return 0;
}
/* Žnderungen 7.10.95, M. Pristovsek */


/****************************************************************************************/

/***		Druckpuffer ausdrucken (fr Querdruck wird drucke_quer aufgerufen):
****
****		 > bitmap_ptr 	(UBYTE *) 	; Puffer mit den zu druckenden Bytes.
****		 > v_offset 		(LONG)			; vertikale (horizontale) Startposition in der Bitmap.
****		 > hsize_p			(LONG)			; Breite der Bitmap in Pixeln.
****		 > vsize_p			(LONG)			; H”he der Bitmap in Pixeln.
****		 > h_dpi				(LONG)			; gewnschte horizontale Aufl”sung.
****		 > v_dpi				(LONG)			; gewnschte vertikale Aufl”sung.
****		 > out_channel	(WORD)			; Ausgabekanal (siehe TOS: 'Fopen')
****		 > flag 				(WORD)			; Bitfeld: 1=Header, 2=Daten, 4=Ende
****		 > quer					(WORD)			; Querdruck
****		 > opt					(UBYTE *)		; String mit zus. Optionen (max. 16 Bytes)
***/

WORD drucke( UBYTE *bitmap_ptr, LONG v_offset, LONG hsize_p, LONG vsize_p,
						 LONG h_dpi, LONG v_dpi, WORD out_channel, WORD flag, WORD quer, UBYTE *opt )
{
	/*** Lokale Variablen: ***/
	MODI					*mode, *tmp_mode; 			/* Zeiger auf die Modus-Tabelle. */
	LONG					hdpi_diff, vdpi_diff; 	/* Zum Festlegen der Aufl”sung. */
	register LONG tmp;										/* Puffer fr alles m”gliche. */
	register LONG hsize_b, hsize_m; 			/* Druckseitenbreite in Bytes, Bitmapbreite in Bytes. */
	register LONG v_pos, rows_to_skip;		/* Derzeitige Druck-Zeile, Anzahl leerer Zeilen. */
	LONG					l_margin, right, left;	/* Linker Rand, Zeilenr„nder. */
	LONG					prt_columns, prt_bits;	/* Druckspalten pro Zeile, Spaltenh”he in Bits. */
	int 					active; 								/* Ein flag. */

	(void)opt;	/* ignorieren */
	if(  quer  )
		return drucke_quer( bitmap_ptr, v_offset, hsize_p, vsize_p, h_dpi, v_dpi, out_channel, flag );

	/*** Initialisierung: ***/

	/*++ Gewnschte Aufl”sung an vorhandene Aufl”sungen anpassen: ++*/
	/*++ Bem.: Die optimale Anpassung der horizontalen Aufl”sung geht vor. ++*/
	tmp_mode = cbj_mode;
	hdpi_diff = vdpi_diff = LONG_MAX; /* Zun„chst maximale Abweichung. */
	do
	{
		if(  labs( tmp_mode->h_dpi - h_dpi )<hdpi_diff  )
		{
			hdpi_diff = labs(tmp_mode->h_dpi - h_dpi);
			vdpi_diff = labs(tmp_mode->v_dpi - v_dpi);
			mode = tmp_mode;
		}
		if(  labs( tmp_mode->h_dpi - h_dpi )==hdpi_diff  )
		{
			if(  labs( tmp_mode->v_dpi - v_dpi )<vdpi_diff  )
			{
				mode = tmp_mode;
				vdpi_diff = labs(tmp_mode->v_dpi - v_dpi);
			}
		}
	}
	while(  (++tmp_mode)->bytes  );	/* bytes == 0 => Ende. */
	h_dpi = mode->h_dpi;						/* Aufl”sung ggf. ab„ndern. */
	v_dpi = mode->v_dpi;
	prt_bits = mode->bytes << 3;

	/*++ Diverse Variablen setzen: ++*/
	strcpy(prt_buffer, g_print);	/* Bitmap-Graphik Steuerkode kopieren */

	hsize_b = (hsize_p < (WIDTH*h_dpi)/360L) ? (hsize_p + 7L) >> 3 : (WIDTH*h_dpi)/2880L;
	hsize_m = ((hsize_p + 15L) >> 4) << 1;		/* -> die 'Bitmap' ist eine gerade Anzahl von Bytes weit. */
	if(  flag&1  )
	{ 																				/* Nicht druckbare R„nder berspringen. */
		bitmap_ptr += hsize_m*(tmp = (TOP*v_dpi)/360L) + (((LEFT*h_dpi)/360L + 7L) >> 3);
		vsize_p -= tmp; 												/* vertikale Gr”áe entsprechend korrigieren. */
	}

	v_pos = v_offset;
	vsize_p += v_offset;		/* H”he der Seite berprfen: */
	if(  vsize_p > (tmp = (HEIGHT*v_dpi)/360L)  )
		vsize_p = tmp;
	vsize_p -= v_offset;
	--vsize_p;
	

	/*++ Testen ob die Seite leer ist: ++*/
	for (l_margin = 0; ist_next_leer(bitmap_ptr + l_margin, hsize_m, vsize_p)  &&  l_margin < hsize_b; l_margin++);
	rows_to_skip = 0; 										/* Zun„chst keine leeren Zeilen. */
	if(  l_margin == hsize_b  )
	{	/* Leere Druckseite ? */
		rows_to_skip = vsize_p - v_pos; 	/* So tun, als ob das Ende der Seite erreicht ist. */
		v_pos = vsize_p;
	}


	/*** Begin des Druckvorgangs: ***/
	/*++ Reset: ++*/
	if(  flag&1  )
		print_block(16L, init, out_channel);

	/*++ Bytes im Puffer an den Drucker ausgeben: ++*/
	active = TRUE;
	while(  active  &&  v_pos<vsize_p  &&  flag&2  )
	{
		/*++ Leerzeilen gesondert abarbeiten: ++*/
		while(  v_pos < vsize_p  &&  ist_leerzeile(bitmap_ptr, hsize_b)  )
		{
			rows_to_skip++;
			v_pos++;
			bitmap_ptr += hsize_m;
		}

		/* Ist diese Seite ist schon fertig? */
		if(  TRUE==(active=(v_pos<vsize_p))  )
		{
			/* Leerzeilen zu berspringen ? */
			if(  rows_to_skip!=0L  )
				skip_rows( out_channel, (rows_to_skip*360L)/v_dpi );

			/*++ R„nder einstellen: ++*/
			for(  right=hsize_b - 1L;
					  right > l_margin  &&  ist_next_leer(bitmap_ptr + right, hsize_m, prt_bits);
						right--  )
				;
			++right;

			for(  left=l_margin;
						ist_next_leer(bitmap_ptr + left, hsize_m, prt_bits) && left < right;
						left++  )
				;
			/* Linker Rand ? */
			if(  left>0  )
			{
				left = (left/3)*3;							/* Da in (ganzen) 120 dpi Schritten. */
				tmp = ((left << 3)*120)/h_dpi;
				h_step[2] = (UBYTE)tmp;
				h_step[3] = (UBYTE)(tmp >> 8);
				print_block( 4L, h_step, out_channel );
			}
			prt_columns = right - left;

			/*++ Daten drucken: ++*/
			block_it( prt_buffer+6L, bitmap_ptr+left, prt_columns, hsize_m, mode->bytes );
			prt_buffer[3] = (UBYTE)(prt_columns*prt_bits + 1L);
			prt_buffer[4] = (UBYTE)((prt_columns*prt_bits + 1L) >> 8);
			prt_buffer[5] = (UBYTE)mode->id;

			/* šberagt Zeile unteren Rand ? */
			if (v_pos + prt_bits > vsize_p)
			{
				register WORD 	bit, byte, and_it;	/*	=> Abschneiden. */

				bit = (WORD)(v_pos + prt_bits - vsize_p);
				byte = bit >> 3;
				and_it = (0x00FF << (bit & 7));
				for(  rows_to_skip = 0L;  rows_to_skip < prt_columns*prt_bits;  rows_to_skip++  )
				{
					if(  rows_to_skip%mode->bytes == byte  )
						prt_buffer[6L + rows_to_skip] &= and_it;
					else
						if(  rows_to_skip%mode->bytes > byte  )
							prt_buffer[6 + rows_to_skip] = 0;
				}
				print_block( 6L+prt_columns*prt_bits, prt_buffer, out_channel);
				rows_to_skip = bit;
				active = FALSE;
			}
			else
			{
				rows_to_skip = prt_bits;
				print_block( 6L+prt_columns*prt_bits, prt_buffer, out_channel );
				bitmap_ptr += hsize_m * prt_bits;
				v_pos += rows_to_skip;
			}
		}
	}

	/*** Seitenende bearbeiten: ***/
	/* Seite beenden ? */
	if(  flag&4  )
		print_block( 1L, "\014\007", out_channel );
	else
	{
		/* Evt. Rest ausgeben: Nur noch Leerzeilen */
		/* Leerzeilen zu berspringen ? */
		if(  rows_to_skip != 0L  )
			skip_rows( out_channel, (rows_to_skip*360L)/v_dpi );
	}

	/*** Das war es: ***/
	flush_block( out_channel );
	return 0; /* Fehler sind nicht vorgesehen. */
}
/* Žnderungen 7.10.95, M. Pristovsek */
