Boston Linux & Unix (BLU) Home | Calendar | Mail Lists | List Archives | Desktop SIG | Hardware Hacking SIG
Wiki | Flickr | PicasaWeb | Video | Maps & Directions | Installfests | Keysignings
Linux Cafe | Meeting Notes | Blog | Linux Links | Bling | About BLU

BLU Discuss list archive


[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

Duplex (double sided) printing to an HP 8000



I use this filter for the HP4000, equipped with a duplex unit.  It senses the
file type (text, Postscript, PCL), and sends the commands appropriate the 
modes.  I compile two editions, one for normal use, and one with -DDUPLEX
to turn on the duplexer, and then use a flag to use the filter that turns the
duplexer on.  The printcap entry on our Sparc 5 is:

lz0|lps0|corridor_hp: \
        :lp=/dev/bpp0:sh:ff=/f:sd=/var/spool/lz0:ms=-opost,crtscts:pl#60:mx#0\
        :lf=/var/spool/lz0/errs:\
        :if=/usr/local/bin/hplaser:df=/usr/local/bin/hplaser2sides:

so 

	lpr -Plz0 -d <filename>

will be printed with the duplexer on.

There's also a compile time mode that will invoke another filter 'hpps' that
uses Ghostscript to generate PCL code for non-Postscript printers.  It has only
one line in it:

/usr/local/bin/gs -q -dNOPAUSE -sDEVICE=laserjet -sOutputFile=- -

Referencing it as a separate command allows changes, e.g., device driver, 
without re-compilation.
-------------- next part --------------
/* @(#)hplaser.c	1.9 4/14/99

   Taste the file to determine file type: PCL, text, Postscript.   For
   text files, expand tabs and put a CR before each LF.

   For reasons unknown, executing 'write' with 0 bytes will hang the Aurora 
   parallel port - avoid it...

 */
#include <stdio.h>
#include <signal.h>
#include <errno.h>

enum FILETYPE { text, PCL, Postscript };
static enum FILETYPE mode;
static int psDup_off;
static int gs_pid;

void duplex_off(void)
{
#ifdef DUPLEX
    char cmd[80];

    if (mode == Postscript) {
	if (!psDup_off) {
	    strcpy(cmd, "<< /Duplex false >> setpagedevice\n");
	    write(1, cmd, strlen(cmd));
	    psDup_off = 1;
	}
    } else 
      write(1, "\033&l0S", 5);	/* duplex off */
#endif
}

/* when 'lprm' is executed, clean up the job before terminating.
 */
void sigint(int s)
{
#ifdef GHOSTSCRIPT
    /* If postscript, we've started a subprocess to run gs which may
       have gone away when a job is removed - just exit in this case,
       so we don't hang on the write.
     */
    if (mode == Postscript) {
	if (gs_pid) kill(gs_pid, SIGINT);
	exit(0);
    }
#else
    duplex_off();
    write(1, "\033E", 2);	/* reset */
#endif

    exit(0);
}

void gs_sig(int s)
{
    int n;

    wait(&n);	/* pick up gs subprocess exit status */
    
    if ((n & 0xff) != 0) exit(-1);
    else exit((n >> 8) & 0xff);
}

int main(int ac, char **av)
{
    int nbytes, col, i, nb, n, sd[2];
    char buff[307], *p, *s, *eob;
    char cmd[80];

    signal(SIGINT, sigint);

    /* Get some characters, and determine its type */
    nbytes = read(0, buff, sizeof(buff));
    p = s = buff;

    if ((nbytes > 0) && (buff[0] == '\033')) {
	mode = PCL;
	write(1, "\033E", 2);
	/* Scan for initial \033E reset sequence at beginning of buffer.
	   This can turn off other modes (e.g., duplex) if sent later..
	 */
	if (buff[1] == 'E') s = &buff[2];

    } else if ((nbytes >= 10) && !strncmp(buff, "%!PS-Adobe", 10)) {
	mode = Postscript;
    } else {
	mode = text;
	/* Check for first character ^L; omit since we're already at 
	   top of form.
	*/
	if (*p == '\f') {
	    p++;
	    s = p;
	}

	write(1, "\033E", 2);

#ifdef LAND132
        strcpy(cmd, "\033&l2A\033(s13H\033&l1O");
        write(1, cmd, strlen(cmd));	
#endif
    }

#ifdef GHOSTSCRIPT
    if (mode == Postscript) {
	/* fork subprocess, make pipe, run 'hpps' script */
	pipe(sd);

	if (!(gs_pid = fork())) {
	    /* subprocess */
	    close(sd[1]);
	    dup2(sd[0], 0);	/* pick up stdin */
	    
	    if (execl("/usr/local/bin/hpps", "hpps", 0) < 0)
		perror("can't exec /usr/local/bin/hpps");
	    
	    exit(0);

	} else {
	    dup2(sd[1], 1);	/* pick up stdout */

	    /* we want to be notified of abnormal terminations, so we can
	       quit....
	    */
	    signal(SIGCHLD, gs_sig);
	}
    }
#endif

#ifdef DUPLEX
    if (mode == Postscript) {

	/* Scan to end of first line; insert duplex command */
	eob = &buff[nbytes];
	while (p < eob) {
	    if (*p == '\n') {
		p++;
		write(1, buff, p - buff);
		s = p;

		strcpy(cmd, "<< /Duplex true >> setpagedevice\n");
		write(1, cmd, strlen(cmd));
		break;
	    } else p++;
	}
    } else {
	write(1, "\033&l1S", 5);	/* duplex, long edge binding */
    }
#endif

    col = 0;
    do {
	if (mode == text) {
	    /* expand tabs and add CR to LF */
	    for (eob = &buff[nbytes]; p < eob; i++, p++) {
		
		switch (*p) {
		    /* Line feeds expand to CR LF */
		  case '\n':
		    n = p - s;
		    if (n > 0) write(1, s, n);
		    write(1, "\r\n", 2);
		    s = p + 1;
		    col = 0;
		    continue;
		    
		  case '\r':  col = 0;
		    break;
		    
		    /* Tabs expand by 8
		       012345678901234567890
		       x	x
		       */
		  case '\t':
		    n = p - s;
		    if (n > 0) write(1, s, n);
		    nb = 8 - (col % 8);
		    write(1, "        ", nb);
		    col += nb;
		    s = p + 1;
		    continue;
		    
		  default:    col++;
		    break;
		}
	    }
#ifdef DUPLEX
	} else if (mode == Postscript) {
	    /* Scan for %%EOF; put in end of duplex if found.  Note that
	       if not found, p will equal &buff[nbytes], and entire buffer
	       will be written out, below.
	    */
	    eob = &buff[nbytes];
	    for (p=buff; p<eob; p++) {
		if (*p == '%') {
		    /* Check for enough chars to compare;
		       get more if need more */
		    if (eob - p < 5) {
			n = p - s;
			if (n > 0) write(1, s, n);
			nbytes = eob - p;
			memcpy(s, p, nbytes);
			n = read(0, &buff[nbytes], sizeof(buff)-nbytes);
			if (n < 0) break;
			nbytes += n;
			eob = &buff[nbytes];
			p = buff;
		    }
		    if (eob - p >= 5 && !strncmp(p, "%%EOF", 5)) {
			n = p - s;
			if (n) write(1, s, n);
			duplex_off();
			s = p;
			p = eob;
			break;
		    }
		}
	    }
#endif
	} else {
	    /* PCL and non-Duplex Postscript modes are just pass thru */
	    p = &buff[nbytes];
	}

	/* Flush buffer */
	n = p - s;
	if (n > 0) write(1, s, n);

	nbytes = read(0, buff, sizeof(buff));
	p = s = buff;
    } while (nbytes > 0);
    
    duplex_off();

    if (mode != Postscript)
        write(1, "\033E", 2);

#ifdef GHOSTSCRIPT
    close(sd[1]);
    close(1);

    if (mode == Postscript) {
	/* wait for Ghostscript subprocess to terminate */
	signal(SIGCHLD, SIG_DFL);
	wait(&n);
	
	if ((n & 0xff) != 0) return -1;
	else return (n >> 8) & 0xff;
    } else {
	return 0;
    }
#else
    close(1);
    return 0;
#endif
}



BLU is a member of BostonUserGroups
BLU is a member of BostonUserGroups
We also thank MIT for the use of their facilities.

Valid HTML 4.01! Valid CSS!



Boston Linux & Unix / webmaster@blu.org