/* $XConsortium: xpr.c,v 1.59 94/10/14 21:22:08 kaleb Exp $ */
/*

Copyright (c) 1985  X Consortium

Permission is hereby granted, free of charge, to any person obtaining
a copy of this software and associated documentation files (the
"Software"), to deal in the Software without restriction, including
without limitation the rights to use, copy, modify, merge, publish,
distribute, sublicense, and/or sell copies of the Software, and to
permit persons to whom the Software is furnished to do so, subject to
the following conditions:

The above copyright notice and this permission notice shall be included
in all copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
IN NO EVENT SHALL THE X CONSORTIUM BE LIABLE FOR ANY CLAIM, DAMAGES OR
OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
OTHER DEALINGS IN THE SOFTWARE.

Except as contained in this notice, the name of the X Consortium shall
not be used in advertising or otherwise to promote the sale, use or
other dealings in this Software without prior written authorization
from the X Consortium.

*/

/*
 * XPR - process xwd(1) files for various printers
 *
 * Author: Michael R. Gretzinger, MIT Project Athena
 *
 * Modified by Marvin Solomon, Univeristy of Wisconsin, to handle Apple
 * Laserwriter (PostScript) devices (-device ps).
 * Also accepts the -compact flag that produces more compact output
 * by using run-length encoding on white (1) pixels.
 * This version does not (yet) support the following options
 *   -append -dump -noff -nosixopt -split
 * 
 * Changes
 * Copyright 1986 by Marvin Solomon and the University of Wisconsin
 * 
 * Permission to use, copy, modify, and distribute this
 * software and its documentation for any purpose and without
 * fee is hereby granted, provided that the above copyright
 * notice appear in all copies and that both that copyright
 * notice and this permission notice appear in supporting
 * documentation, and that the names of Marvin Solomon and
 * the University of Wisconsin not be used in
 * advertising or publicity pertaining to distribution of the
 * software without specific, written prior permission.
 * Neither Marvin Solomon nor the University of Wisconsin
 * makes any representations about the suitability of
 * this software for any purpose.  It is provided "as is"
 * without express or implied warranty.
 * 
 * Modified by Bob Scheifler for 2x2 grayscale, then ...
 * Modified by Angela Bock and E. Mike Durbin, Rich Inc., to produce output
 * using 2x2, 3x3, or 4x4 grayscales. This version modifies the grayscale
 * conversion option of -gray to accept an input of  2, 3, or 4 to signify
 * the gray level desired.  The output is produced, using 5, 10, or 17-level
 * gray scales, respectively.
 *
 * Modifications by Larry Rupp, Hewlett-Packard Company, to support HP
 * LaserJet, PaintJet, and other PCL printers.  Added "ljet" and "pjet"
 * to devices recognized.  Also added -density, -cutoff, and -noposition
 * command line options.
 *
 */

#include <X11/Xos.h>
#include <X11/Xfuncs.h>
#include <X11/Xlib.h>
#include <X11/Xutil.h>
#include <stdio.h>
#ifndef WIN32
#include <pwd.h>
#endif
#include "lncmd.h"
#include "xpr.h"
#include <X11/XWDFile.h>
#include <X11/Xmu/SysUtil.h>
#ifndef O_BINARY
#define O_BINARY 0
#endif

#ifdef	NLS16
#ifndef NLS
#define	NLS
#endif
#endif

#ifndef NLS
#define catgets(i, sn,mn,s) (s)
#else /* NLS */
#define NL_SETN 1	/* set number */
#include <nl_types.h>

nl_catd nlmsg_fd;
#endif /* NLS */

int debug = 0;

#define W_MAX 2400
#define H_MAX 3150
#define W_MARGIN 75
#define H_MARGIN 37
#define W_PAGE 2550
#define H_PAGE 3225

#ifdef NOINLINE
#define min(x,y) (((x)<(y))?(x):(y))
#endif /* NOINLINE */

#define F_PORTRAIT 1
#define F_LANDSCAPE 2
#define F_DUMP 4
#define F_NOSIXOPT 8
#define F_APPEND 16
#define F_NOFF 32
#define F_REPORT 64
#define F_COMPACT 128
#define F_INVERT 256
#define F_GRAY 512
#define F_NPOSITION 1024
#define F_SLIDE 2048

#define DEFAULT_CUTOFF ((unsigned int) (0xFFFF * 0.50))

char *infilename = NULL;
char *progname   = NULL;

typedef struct _grayRec {
    int level;
    int sizeX, sizeY;		/* 2x2, 3x3, 4x4 */
    unsigned long *grayscales;	/* pointer to the encoded pixels */
} GrayRec, *GrayPtr;

unsigned long grayscale2x2[] =
	{0, 1, 9, 11, 15};
unsigned long grayscale3x3[] =
	{0, 16, 68, 81, 325, 341, 349, 381, 383, 511};
unsigned long grayscale4x4[] =
	{0, 64, 4160, 4161, 20545, 21057, 23105,
	 23113, 23145, 24169, 24171, 56939, 55275, 55279,
	 57327, 65519, 65535};

GrayRec gray2x2 = {sizeof(grayscale2x2)/sizeof(long), 2, 2, grayscale2x2};
GrayRec gray3x3 = {sizeof(grayscale3x3)/sizeof(long), 3, 3, grayscale3x3};
GrayRec gray4x4 = {sizeof(grayscale4x4)/sizeof(long), 4, 4, grayscale4x4};

/* mapping tables to map a byte in to the hex representation of its
 * bit-reversal
 */
const
char hex1[]="084c2a6e195d3b7f084c2a6e195d3b7f084c2a6e195d3b7f084c2a6e195d3b7f\
084c2a6e195d3b7f084c2a6e195d3b7f084c2a6e195d3b7f084c2a6e195d3b7f\
084c2a6e195d3b7f084c2a6e195d3b7f084c2a6e195d3b7f084c2a6e195d3b7f\
084c2a6e195d3b7f084c2a6e195d3b7f084c2a6e195d3b7f084c2a6e195d3b7f";

const
char hex2[]="000000000000000088888888888888884444444444444444cccccccccccccccc\
2222222222222222aaaaaaaaaaaaaaaa6666666666666666eeeeeeeeeeeeeeee\
111111111111111199999999999999995555555555555555dddddddddddddddd\
3333333333333333bbbbbbbbbbbbbbbb7777777777777777ffffffffffffffff";


/* Local prototypes */
static void usage(void);
static
void parse_args(
  int argc,
  char **argv,
  int *scale,
  int *width,
  int *height,
  int *left,
  int *top,
  enum device *device,
  int *flags,
  int *split,
  char **header,
  char **trailer,
  int *plane,
  GrayPtr *gray,
  int *density,
  unsigned int *cutoff,
  float *gamma,
  int *render);
static
void setup_layout(
  enum device device,
  int win_width,
  int win_height,
  int flags,
  int width,
  int height,
  char *header,
  char *trailer,
  int *scale,
  enum orientation *orientation);
static
char *convert_data(
    XWDFileHeader *win,
    char *data,
    int plane,
    GrayPtr gray,
    XColor *colors,
    int flags);
static
void dump_sixmap(
  register unsigned char (*sixmap)[],
  int iw,
  int ih);
static
void build_sixmap(
  int ih,
  int iw,
  unsigned char (*sixmap)[],
  int hpad,
  XWDFileHeader *win,
  const char *data);
static void build_output_filename(const char *name, enum device device, char *oname);
static
void ln03_setup(
  int iw,
  int ih,
  enum orientation orientation,
  int scale,
  int left,
  int top,
  int *left_margin,
  int *top_margin,
  int flags,
  const char *header,
  const char *trailer);
static void ln03_finish(void);
static void la100_setup(int iw, int ih, int scale);
static void la100_finish(void);
static void dump_prolog(int flags);
static int points(int n);
static char *escape(const char *s);
static
void ps_setup(
  int iw,
  int ih,
  enum orientation orientation,
  int scale,
  int left,
  int top,
  int flags,
  const char *header,
  const char *trailer,
  const char *name);
static void ps_finish(void);
static void ln03_alter_background(
  unsigned char (*sixmap)[],
  int iw,
  int ih);
static
void ln03_output_sixels(
  unsigned char (*sixmap)[],
  int iw,
  int ih,
  int nosixopt,
  int split,
  int scale,
  int top_margin,
  int left_margin);
static void la100_output_sixels(
  unsigned char (*sixmap)[],
  int iw,
  int ih,
  int nosixopt);
static void ps_output_bits(
  int iw,
  int ih,
  int flags,
  enum orientation orientation,
  XWDFileHeader *win,
  const char *data);
static int ps_putbuf(
  register unsigned char *s,
  register int n,	
  register int ocount,	
  int compact);
static void ps_bitrot(
  unsigned char *s,
  register int n,
  int col,
  register int owidth,
  char *obuf);
static void fullread (
  int file,
  char *data,
  int nbytes);  
  
int main(int argc, char **argv)
{
    unsigned long swaptest = 1;
    XWDFileHeader win;
    register unsigned char (*sixmap)[];
    register int i;
    register int iw;
    register int ih;
    register int sixel_count;
    char *w_name;
    int scale, width, height, flags, split;
    int left, top;
    int top_margin, left_margin;
    int hpad;
    char *header, *trailer;
    int plane;
    int density, render;
    unsigned int cutoff;
    float gamma;
    GrayPtr gray;
    char *data;
    long size;
    enum orientation orientation;
    enum device device;
    XColor *colors = (XColor *)NULL;
    
    if (!(progname = argv[0]))
      progname = "xpr";
#ifdef	NLS
    nlmsg_fd = catopen("xpr", 0);
#endif
    parse_args (argc, argv, &scale, &width, &height, &left, &top, &device, 
		&flags, &split, &header, &trailer, &plane, &gray,
		&density, &cutoff, &gamma, &render);
    
    if (device == PP) {
	x2pmp(stdin, stdout, scale,
	      width >= 0? inch2pel((float)width/300.0): X_MAX_PELS,
	      height >= 0? inch2pel((float)height/300.0): Y_MAX_PELS,
	      left >= 0? inch2pel((float)left/300.0): inch2pel(0.60),
	      top >= 0? inch2pel((float)top/300.0): inch2pel(0.70),
	      header, trailer,
	      (flags & F_PORTRAIT)? PORTRAIT:
	        ((flags & F_LANDSCAPE)? LANDSCAPE: UNSPECIFIED),
	      (flags & F_INVERT));
	exit(0);
    } else if ((device == LJET) || (device == PJET) || (device == PJETXL)) {
        x2jet(stdin, stdout, scale, density, width, height, left, top,
	      header, trailer,
	      (flags & F_PORTRAIT)? PORTRAIT:
	      ((flags & F_LANDSCAPE)? LANDSCAPE: UNSPECIFIED),
	      (flags & F_INVERT),
	      ((flags & F_APPEND) && !(flags & F_NOFF)),
	      !(flags & F_NPOSITION),
	      (flags & F_SLIDE),
	      device, cutoff, gamma, render);
	exit(0);
    }

    /* read in window header */
    fullread(0, (char *)&win, sizeof win);
    if (*(char *) &swaptest)
	_swaplong((char *) &win, (long)sizeof(win));

    if (win.file_version != XWD_FILE_VERSION) {
	fprintf(stderr,"xpr: file format version missmatch.\n");
	exit(1);
    }
    if (win.header_size < sizeof(win)) {
	fprintf(stderr,"xpr: header size is too small.\n");
	exit(1);
    }

    w_name = malloc((unsigned)(win.header_size - sizeof win));
    fullread(0, w_name, (int) (win.header_size - sizeof win));
    
    if(win.ncolors) {
	XWDColor xwdcolor;
	colors = (XColor *)malloc((unsigned) (win.ncolors * sizeof(XColor)));
	for (i = 0; i < win.ncolors; i++) {
	    fullread(0, (char*)&xwdcolor, (int) sizeof xwdcolor);
	    colors[i].pixel = xwdcolor.pixel;
	    colors[i].red = xwdcolor.red;
	    colors[i].green = xwdcolor.green;
	    colors[i].blue = xwdcolor.blue;
	    colors[i].flags = xwdcolor.flags;
 	}
	if (*(char *) &swaptest) {
	    for (i = 0; i < win.ncolors; i++) {
		_swaplong((char *) &colors[i].pixel, (long)sizeof(long));
		_swapshort((char *) &colors[i].red, (long) (3 * sizeof(short)));
	    }
	}
	if ((win.ncolors == 2) &&
	    (INTENSITY(&colors[0]) > INTENSITY(&colors[1])))
	    flags ^= F_INVERT;
    }
    if (plane >= (long)win.pixmap_depth) {
	fprintf(stderr,"xpr: plane number exceeds image depth\n");
	exit(1);
    }
    size = win.bytes_per_line * win.pixmap_height;
    if (win.pixmap_format == XYPixmap)
	size *= win.pixmap_depth;
    data = malloc((unsigned)size);
    fullread(0, data, (int)size);
    if ((win.pixmap_depth > 1) || (win.byte_order != win.bitmap_bit_order)) {
	data = convert_data(&win, data, plane, gray, colors, flags);
	size = win.bytes_per_line * win.pixmap_height;
    }
    if (win.bitmap_bit_order == MSBFirst) {
	_swapbits((unsigned char *)data, size);
	win.bitmap_bit_order = LSBFirst;
    }
    if (flags & F_INVERT)
	_invbits((unsigned char *)data, size);

    /* calculate orientation and scale */
    setup_layout(device, (int) win.pixmap_width, (int) win.pixmap_height,
		 flags, width, height, header, trailer, &scale, &orientation);

    if (device == PS) {
	iw = win.pixmap_width;
	ih = win.pixmap_height;
    } else {
	/* calculate w and h cell count */
	iw = win.pixmap_width;
	ih = (win.pixmap_height + 5) / 6;
	hpad = (ih * 6) - win.pixmap_height;

	/* build pixcells from input file */
	sixel_count = iw * ih;
	sixmap = (unsigned char (*)[])malloc((unsigned)sixel_count);
	build_sixmap(iw, ih, sixmap, hpad, &win, data);
    }

    /* output commands and sixel graphics */
    if (device == LN03) {
/*	ln03_grind_fonts(sixmap, iw, ih, scale, &pixmap); */
	ln03_setup(iw, ih, orientation, scale, left, top,
		   &left_margin, &top_margin, flags, header, trailer);
	ln03_output_sixels(sixmap, iw, ih, (flags & F_NOSIXOPT), split, 
			   scale, top_margin, left_margin);
	ln03_finish();
    } else if (device == LA100) {
	la100_setup(iw, ih, scale);
	la100_output_sixels(sixmap, iw, ih, (flags & F_NOSIXOPT));
	la100_finish();
    } else if (device == PS) {
	ps_setup(iw, ih, orientation, scale, left, top,
		   flags, header, trailer, w_name);
	ps_output_bits(iw, ih, flags, orientation, &win, data);
	ps_finish();
    } else {
	fprintf(stderr, "xpr: device not supported\n");
    }
    
    /* print some statistics */
    if (flags & F_REPORT) {
	fprintf(stderr, "Name: %s\n", w_name);
	fprintf(stderr, "Width: %d, Height: %d\n", win.pixmap_width, 
		win.pixmap_height);
	fprintf(stderr, "Orientation: %s, Scale: %d\n", 
		(orientation==PORTRAIT) ? "Portrait" : "Landscape", scale);
    }
    if (((device == LN03) || (device == LA100)) && (flags & F_DUMP))
	dump_sixmap(sixmap, iw, ih);
    exit(EXIT_SUCCESS);
}

static
void usage(void)
{
    fprintf(stderr, "usage: %s [options] [file]\n", progname);
    fprintf(stderr, "    -append <file>  -noff  -output <file>\n");
    fprintf(stderr, "    -compact\n");
    fprintf(stderr, "    -device {ln03 | la100 | ps | lw | pp | ljet | pjet | pjetxl}\n");
    fprintf(stderr, "    -dump\n");
    fprintf(stderr, "    -gamma <correction>\n");
    fprintf(stderr, "    -gray {2 | 3 | 4}\n");
    fprintf(stderr, "    -height <inches>  -width <inches>\n");
    fprintf(stderr, "    -header <string>  -trailer <string>\n");
    fprintf(stderr, "    -landscape  -portrait\n");
    fprintf(stderr, "    -left <inches>  -top <inches>\n");
    fprintf(stderr, "    -noposition\n");
    fprintf(stderr, "    -nosixopt\n");
    fprintf(stderr, "    -plane <n>\n");
    fprintf(stderr, "    -psfig\n");
    fprintf(stderr, "    -render <type>\n");
    fprintf(stderr, "    -report\n");
    fprintf(stderr, "    -rv\n");
    fprintf(stderr, "    -scale <scale>\n");
    fprintf(stderr, "    -slide\n");
    fprintf(stderr, "    -split <n-pages>\n");
    exit(EXIT_FAILURE);
}

static
void parse_args(
  int argc,
  char **argv,
  int *scale,
  int *width,
  int *height,
  int *left,
  int *top,
  enum device *device,
  int *flags,
  int *split,
  char **header,
  char **trailer,
  int *plane,
  GrayPtr *gray,
  int *density,
  unsigned int *cutoff,
  float *gamma,
  int *render)
{
    register char *output_filename;
    register int f;
    register int len;
    register int pos;

    output_filename = NULL;
    *device = PS;	/* default */
    *flags = 0;
    *scale = 0;
    *split = 1;
    *width = -1;
    *height = -1;
    *top = -1;
    *left = -1;
    *header = NULL;
    *trailer = NULL;
    *plane = -1;
    *gray = (GrayPtr)NULL;
    *density = 0;
    *cutoff = DEFAULT_CUTOFF;
    *gamma = -1.0;
    *render = 0;

    for (argc--, argv++; argc > 0; argc--, argv++) {
	if (argv[0][0] != '-') {
	    infilename = *argv;
	    continue;
	}
	len = strlen(*argv);
	switch (argv[0][1]) {
	case 'a':		/* -append <filename> */
	    if (!bcmp(*argv, "-append", len)) {
		argc--; argv++;
		if (argc == 0) usage();
		output_filename = *argv;
		*flags |= F_APPEND;
	    } else
		usage();
	    break;

	case 'c':		/* -compact | -cutoff <intensity> */
	    if (len <= 2 )
		usage();
	    if (!bcmp(*argv, "-compact", len)) {
		*flags |= F_COMPACT;
	    } else if (!bcmp(*argv, "-cutoff", len)) {
		argc--; argv++;
		if (argc == 0) usage();
		*cutoff = min((atof(*argv) / 100.0 * 0xFFFF), 0xFFFF);
	    } else
		usage();
	    break;

	case 'd':	/* -density <num> | -device <dev> | -dump */
	    if (len <= 2)
		usage();
	    if (!bcmp(*argv, "-dump", len)) {
		*flags |= F_DUMP;
	    } else if (len <= 3) {
		usage();
	    } else if (!bcmp(*argv, "-density", len)) {
		argc--; argv++;
		if (argc == 0) usage();
		*density = atoi(*argv);
	    } else if (!bcmp(*argv, "-device", len)) {
		argc--; argv++;
		if (argc == 0) usage();
		len = strlen(*argv);
		if (len < 2)
		    usage();
		if (!bcmp(*argv, "ln03", len)) {
		    *device = LN03;
		} else if (!bcmp(*argv, "la100", len)) {
		    *device = LA100;
		} else if (!bcmp(*argv, "ps", len)) {
		    *device = PS;
		} else if (!bcmp(*argv, "lw", len)) {
		    *device = PS;
		} else if (!bcmp(*argv, "pp", len)) {
		    *device = PP;
		} else if (!bcmp(*argv, "ljet", len)) {
		    *device = LJET;
		} else if (!bcmp(*argv, "pjet", len)) {
		    *device = PJET;
		} else if (!bcmp(*argv, "pjetxl", len)) {
		    *device = PJETXL;
		} else
		    usage();
	    } else
		usage();
	    break;

	case 'g':		/* -gamma <float> | -gray <num> */
	    if (len <= 2)
		usage();
	    if (!bcmp(*argv, "-gamma", len)) {
		argc--; argv++;
		if (argc == 0) usage();
		*gamma = atof(*argv);
	    } else if (!bcmp(*argv, "-gray", len) ||
		!bcmp(*argv, "-grey", len)) {
		argc--; argv++;
		if (argc == 0) usage();
		switch (atoi(*argv)) {
		case 2:
		    *gray = &gray2x2;
		    break;
		case 3:
		    *gray = &gray3x3;
		    break;
		case 4:
		    *gray = &gray4x4;
		    break;
		default:
		    usage();
		}
		*flags |= F_GRAY;
	    } else
		usage();
	    break;

	case 'h':		/* -height <inches> | -header <string> */
	    if (len <= 3)
		usage();
	    if (!bcmp(*argv, "-height", len)) {
		argc--; argv++;
		if (argc == 0) usage();
		*height = (int)(300.0 * atof(*argv));
	    } else if (!bcmp(*argv, "-header", len)) {
		argc--; argv++;
		if (argc == 0) usage();
		*header = *argv;
	    } else
		usage();
	    break;

	case 'l':		/* -landscape | -left <inches> */
	    if (len <= 2)
		usage();
	    if (!bcmp(*argv, "-landscape", len)) {
		*flags |= F_LANDSCAPE;
	    } else if (!bcmp(*argv, "-left", len)) {
		argc--; argv++;
		if (argc == 0) usage();
		*left = (int)(300.0 * atof(*argv));
	    } else
		usage();
	    break;

	case 'n':		/* -nosixopt | -noff | -noposition */
	    if (len <= 3)
		usage();
	    if (!bcmp(*argv, "-nosixopt", len)) {
		*flags |= F_NOSIXOPT;
	    } else if (!bcmp(*argv, "-noff", len)) {
		*flags |= F_NOFF;
	    } else if (!bcmp(*argv, "-noposition", len)) {
		*flags |= F_NPOSITION;
	    } else
		usage();
	    break;

	case 'o':		/* -output <filename> */
	    if (!bcmp(*argv, "-output", len)) {
		argc--; argv++;
		if (argc == 0) usage();
		output_filename = *argv;
	    } else
		usage();
	    break;

	case 'p':		/* -portrait | -plane <n> */
	    if (len <= 2)
		usage();
	    if (!bcmp(*argv, "-portrait", len)) {
		*flags |= F_PORTRAIT;
	    } else if (!bcmp(*argv, "-plane", len)) {
		argc--; argv++;
		if (argc == 0) usage();
		*plane = atoi(*argv);
	    } else if (!bcmp(*argv, "-psfig", len)) {
		*flags |= F_NPOSITION;
	    } else
		usage();
	    break;

	case 'r':		/* -render <type> | -report | -rv */
	    if (len <= 2)
		usage();
	    if (!bcmp(*argv, "-rv", len)) {
		*flags |= F_INVERT;
	    } else if (len <= 3) {
		usage();
	    } else if (!bcmp(*argv, "-render", len)) {
		argc--; argv++;
		if (argc == 0) usage();
		*render = atoi(*argv);
	    } else if (!bcmp(*argv, "-report", len)) {
		*flags |= F_REPORT;
	    } else
		usage();
	    break;

	case 's':	/* -scale <scale> | -slide | -split <n-pages> */
	    if (len <= 2)
		usage();
	    if (!bcmp(*argv, "-scale", len)) {
		argc--; argv++;
		if (argc == 0) usage();
		*scale = atoi(*argv);
	    } else if (!bcmp(*argv, "-slide", len)) {
		*flags |= F_SLIDE;
	    } else if (!bcmp(*argv, "-split", len)) {
		argc--; argv++;
		if (argc == 0) usage();
		*split = atoi(*argv);
	    } else
		usage();
	    break;

	case 't':		/* -top <inches> | -trailer <string> */
	    if (len <= 2)
		usage();
	    if (!bcmp(*argv, "-top", len)) {
		argc--; argv++;
		if (argc == 0) usage();
		*top = (int)(300.0 * atof(*argv));
	    } else if (!bcmp(*argv, "-trailer", len)) {
		argc--; argv++;
		if (argc == 0) usage();
		*trailer = *argv;
	    } else
		usage();
	    break;

	case 'w':		/* -width <inches> */
	    if (!bcmp(*argv, "-width", len)) {
		argc--; argv++;
		if (argc == 0) usage();
		*width = (int)(300.0 * atof(*argv));
	    } else
		usage();
	    break;

	default:
	    usage();
	    break;
	}
    }

    if (infilename) {
	f = open(infilename, O_RDONLY|O_BINARY, 0);
	if (f < 0) {
	    fprintf(stderr, "xpr: error opening \"%s\" for input\n",
		    infilename);
	    perror("");
	    exit(1);
	}
	dup2(f, 0);
	close(f);
    } else
	infilename = "stdin";

    if (output_filename != NULL) {
	if (!(*flags & F_APPEND)) {
	    f = open(output_filename, O_CREAT|O_WRONLY|O_TRUNC, 0664);
	} else {
	    f = open(output_filename, O_WRONLY, 0);
	}
	if (f < 0) {
	    fprintf(stderr, "xpr: error opening \"%s\" for output\n", 
		    output_filename);
	    perror("xpr");
	    exit(1);
	}
	if (*flags & F_APPEND) {
	    pos = lseek(f, 0, 2);          /* get eof position */
	    if ((*flags & F_NOFF) &&
		!(*device == LJET || *device == PJET || *device == PJETXL))
		pos -= 3; /* set position before trailing */
	    		  /*     formfeed and reset */
	    lseek(f, pos, 0);              /* set pointer */
	}
	dup2(f, 1);
	close(f);
    }
}

static
void setup_layout(
  enum device device,
  int win_width,
  int win_height,
  int flags,
  int width,
  int height,
  char *header,
  char *trailer,
  int *scale,
  enum orientation *orientation)
{
    register int w_scale;
    register int h_scale;
    register int iscale = *scale;
    register int w_max;
    register int h_max;

    if (header != NULL) win_height += 75;
    if (trailer != NULL) win_height += 75;

    /* check maximum width and height; set orientation and scale*/
    if (device == LN03 || device == PS) {
	if ((win_width < win_height || (flags & F_PORTRAIT)) && 
	    !(flags & F_LANDSCAPE)) {
	    *orientation = PORTRAIT;
	    w_max = (width > 0)? width : W_MAX;
	    h_max = (height > 0)? height : H_MAX;
	    w_scale = w_max / win_width;
	    h_scale = h_max / win_height;
	    *scale = min(w_scale, h_scale);
	} else {
	    *orientation = LANDSCAPE;
	    w_max = (width > 0)? width : H_MAX;
	    h_max = (height > 0)? height : W_MAX;
	    w_scale = w_max / win_width;
	    h_scale = h_max / win_height;
	    *scale = min(w_scale, h_scale);
	}
    } else {			/* device == LA100 */
	*orientation = PORTRAIT;
	*scale = W_MAX / win_width;
    }
    if (*scale == 0) *scale = 1;
    if (*scale > 6) *scale = 6;
    if (iscale > 0 && iscale < *scale) *scale = iscale;
}

static
char *convert_data(
    XWDFileHeader *win,
    char *data,
    int plane,
    GrayPtr gray,
    XColor *colors,
    int flags)
{
    XImage in_image_struct, out_image_struct;
    register XImage *in_image, *out_image;
    register int x, y;

    if ((win->pixmap_format == XYPixmap) && (plane >= 0)) {
	data += win->bytes_per_line * win->pixmap_height *
		(win->pixmap_depth - (plane + 1));
	win->pixmap_format = XYBitmap;
	win->pixmap_depth = 1;
	return data;
    }

    /* initialize the input image */

    in_image = &in_image_struct;
    in_image->byte_order = win->byte_order;
    in_image->bitmap_unit = win->bitmap_unit;
    in_image->bitmap_bit_order = win->bitmap_bit_order;
    in_image->depth = win->pixmap_depth;
    in_image->bits_per_pixel = win->bits_per_pixel;
    in_image->format = win->pixmap_format,
    in_image->xoffset = win->xoffset,
    in_image->data = data;
    in_image->width = win->pixmap_width;
    in_image->height = win->pixmap_height;
    in_image->bitmap_pad = win->bitmap_pad;
    in_image->bytes_per_line = win->bytes_per_line;
    in_image->red_mask = win->red_mask;
    in_image->green_mask = win->green_mask;
    in_image->blue_mask = win->blue_mask;
    in_image->obdata = NULL;
    if (!XInitImage(in_image)) {
	fprintf(stderr,"xpr: bad input image header data.\n");
	exit(1);
    }
    if ((flags & F_GRAY) && (in_image->depth > 1) && (plane < 0)) {
	win->pixmap_width *= gray->sizeX;
	win->pixmap_height *= gray->sizeY;
    }
    win->xoffset = 0;
    win->pixmap_format = XYBitmap;
    win->byte_order = LSBFirst;
    win->bitmap_unit = 8;
    win->bitmap_bit_order = LSBFirst;
    win->bitmap_pad = 8;
    win->pixmap_depth = 1;
    win->bits_per_pixel = 1;
    win->bytes_per_line = (win->pixmap_width + 7) >> 3;

    out_image = &out_image_struct;
    out_image->byte_order = win->byte_order;
    out_image->bitmap_unit = win->bitmap_unit;
    out_image->bitmap_bit_order = win->bitmap_bit_order;
    out_image->depth = win->pixmap_depth;
    out_image->bits_per_pixel = win->bits_per_pixel;
    out_image->format = win->pixmap_format;
    out_image->xoffset = win->xoffset,
    out_image->width = win->pixmap_width;
    out_image->height = win->pixmap_height;
    out_image->bitmap_pad = win->bitmap_pad;
    out_image->bytes_per_line = win->bytes_per_line;
    out_image->red_mask = 0;
    out_image->green_mask = 0;
    out_image->blue_mask = 0;
    out_image->obdata = NULL;
    out_image->data = malloc((unsigned)out_image->bytes_per_line *
				      out_image->height);
    if (!XInitImage(out_image)) {
	fprintf(stderr,"xpr: bad output image header data.\n");
	exit(1);
    }

    if ((in_image->depth > 1) && (plane > 0)) {
	for (y = 0; y < in_image->height; y++)
	    for (x = 0; x < in_image->width; x++)
		XPutPixel(out_image, x, y,
			  (XGetPixel(in_image, x, y) >> plane) & 1);
    } else if (plane == 0) {
	for (y = 0; y < in_image->height; y++)
	    for (x = 0; x < in_image->width; x++)
		XPutPixel(out_image, x, y, XGetPixel(in_image, x, y));
    } else if ((in_image->depth > 1) &&
	       ((win->visual_class == TrueColor) ||
		(win->visual_class == DirectColor))) {
	XColor color;
	int direct = 0;
	unsigned long rmask, gmask, bmask;
	int rshift = 0, gshift = 0, bshift = 0;

	rmask = win->red_mask;
	while (!(rmask & 1)) {
	    rmask >>= 1;
	    rshift++;
	}
	gmask = win->green_mask;
	while (!(gmask & 1)) {
	    gmask >>= 1;
	    gshift++;
	}
	bmask = win->blue_mask;
	while (!(bmask & 1)) {
	    bmask >>= 1;
	    bshift++;
	}
	if ((win->ncolors == 0) || (win->visual_class == DirectColor))
	    direct = 1;
	if (flags & F_GRAY) {
	    register int ox, oy;
	    int ix, iy;
	    unsigned long bits;
	    for (y = 0, oy = 0; y < in_image->height; y++, oy += gray->sizeY)
		for (x = 0, ox = 0; x < in_image->width; x++, ox += gray->sizeX)
		{
		    color.pixel = XGetPixel(in_image, x, y);
		    color.red = (color.pixel >> rshift) & rmask;
		    color.green = (color.pixel >> gshift) & gmask;
		    color.blue = (color.pixel >> bshift) & bmask;
		    if (!direct) {
			color.red = colors[color.red].red;
			color.green = colors[color.green].green;
			color.blue = colors[color.blue].blue;
		    }
		    bits = gray->grayscales[(int)(gray->level *
						  INTENSITY(&color)) /
					    (INTENSITYPER(100) + 1)];
		    for (iy = 0; iy < gray->sizeY; iy++)
			for (ix = 0; ix < gray->sizeX; ix++, bits >>= 1)
			    XPutPixel(out_image, ox + ix, oy + iy, bits);
		}
	} else {
	    for (y = 0; y < in_image->height; y++)
		for (x = 0; x < in_image->width; x++) {
		    color.pixel = XGetPixel(in_image, x, y);
		    color.red = (color.pixel >> rshift) & rmask;
		    color.green = (color.pixel >> gshift) & gmask;
		    color.blue = (color.pixel >> bshift) & bmask;
		    if (!direct) {
			color.red = colors[color.red].red;
			color.green = colors[color.green].green;
			color.blue = colors[color.blue].blue;
		    }
		    XPutPixel(out_image, x, y,
			      INTENSITY(&color) > HALFINTENSITY);
		}
	}
    } else if (flags & F_GRAY) {
	register int ox, oy;
	int ix, iy;
	unsigned long bits;

	if (win->ncolors == 0) {
	    fprintf(stderr, "no colors in data, can't remap\n");
	    exit(1);
	}
	for (x = 0; x < win->ncolors; x++) {
	    register XColor *color = &colors[x];

	    color->pixel = gray->grayscales[(gray->level * INTENSITY(color)) /
					    (INTENSITYPER(100) + 1)];
	}
	for (y = 0, oy = 0; y < in_image->height; y++, oy += gray->sizeY)
	    for (x = 0, ox = 0; x < in_image->width; x++, ox += gray->sizeX) {
		bits = colors[XGetPixel(in_image, x, y)].pixel;
	        for (iy = 0; iy < gray->sizeY; iy++)
		    for (ix = 0; ix < gray->sizeX; ix++, bits >>= 1)
			XPutPixel(out_image, ox + ix, oy + iy, bits);
	    }
    } else {
	if (win->ncolors == 0) {
	    fprintf(stderr, "no colors in data, can't remap\n");
	    exit(1);
	}
	for (x = 0; x < win->ncolors; x++) {
	    register XColor *color = &colors[x];
	    color->pixel = (INTENSITY(color) > HALFINTENSITY);
	}
	for (y = 0; y < in_image->height; y++)
	    for (x = 0; x < in_image->width; x++)
		XPutPixel(out_image, x, y,
			  colors[XGetPixel(in_image, x, y)].pixel);
    }
    free(data);
    return (out_image->data);
}

static
void dump_sixmap(
  register unsigned char (*sixmap)[],
  int iw,
  int ih)
{
    register int i, j;
    register unsigned char *c;

    c = (unsigned char *)sixmap;
    fprintf(stderr, "Sixmap:\n");
    for (i = 0; i < ih; i++) {
	for (j = 0; j < iw; j++) {
	    fprintf(stderr, "%02X ", *c++);
	}
	fprintf(stderr, "\n\n");
    }
}

static
void build_sixmap(
  int ih,
  int iw,
  unsigned char (*sixmap)[],
  int hpad,
  XWDFileHeader *win,
  const char *data)
{
    int iwb = win->bytes_per_line;
    unsigned char *line[6];
    register unsigned char *c;
    register int i, j;
#ifdef NOINLINE
    register int w;
#endif
    register int sixel;
    unsigned char *buffer = (unsigned char *)data;

    c = (unsigned char *)sixmap;


    while (--ih >= 0) {
        for (i = 0; i <= 5; i++) {
	    line[i] = buffer;
	    buffer += iwb;
        }
	if ((ih == 0) && (hpad > 0)) {
	    unsigned char *ffbuf;

	    ffbuf = (unsigned char *)malloc((unsigned)iwb);
	    for (j = 0; j < iwb; j++)
		ffbuf[j] = 0xFF;
	    for (; --hpad >= 0; i--)
		line[i] = ffbuf;
	}

#ifndef NOINLINE
	for (i = 0; i < iw; i++) {
	    sixel =  extzv(line[0], i, 1);
	    sixel |= extzv(line[1], i, 1) << 1;
	    sixel |= extzv(line[2], i, 1) << 2;
	    sixel |= extzv(line[3], i, 1) << 3;
	    sixel |= extzv(line[4], i, 1) << 4;
	    sixel |= extzv(line[5], i, 1) << 5;
	    *c++ = sixel;
	}
#else
	for (i = 0, w = iw; w > 0; i++) {
	    for (j = 0; j <= 7; j++) {
		sixel =  ((line[0][i] >> j) & 1);
		sixel |= ((line[1][i] >> j) & 1) << 1;
		sixel |= ((line[2][i] >> j) & 1) << 2;
		sixel |= ((line[3][i] >> j) & 1) << 3;
		sixel |= ((line[4][i] >> j) & 1) << 4;
		sixel |= ((line[5][i] >> j) & 1) << 5;
		*c++ = sixel;
		if (--w == 0) break;
	    }
	}
#endif
    }
}

static
void build_output_filename(const char *name, enum device device, char *oname)
{
    while (*name && *name != '.') *oname++ = *name++;
    switch (device) {
    case LN03:	bcopy(".ln03", oname, 6); break;
    case LA100:	bcopy(".la100", oname, 7); break;
    }
}

/*
ln03_grind_fonts(sixmap, iw, ih, scale, pixmap)
unsigned char (*sixmap)[];
int iw;
int ih;
int scale;
struct pixmap (**pixmap)[];
{
}
*/

static
void ln03_setup(
  int iw,
  int ih,
  enum orientation orientation,
  int scale,
  int left,
  int top,
  int *left_margin,
  int *top_margin,
  int flags,
  const char *header,
  const char *trailer)
{
    register int i;
    register int lm, tm, xm;
    char buf[256];
    register char *bp = buf;
	
    if (!(flags & F_APPEND)) {
	sprintf(bp, LN_STR); bp += 4;
	sprintf(bp, LN_SSU, 7); bp += 5;
	sprintf(bp, LN_PUM_SET); bp += sizeof LN_PUM_SET - 1;
    }

    if (orientation == PORTRAIT) {
	lm = (left > 0)? left : (((W_MAX - scale * iw) / 2) + W_MARGIN);
	tm = (top > 0)? top : (((H_MAX - scale * ih * 6) / 2) + H_MARGIN);
	sprintf(bp, LN_PFS, "?20"); bp += 7;
	sprintf(bp, LN_DECOPM_SET); bp += sizeof LN_DECOPM_SET - 1;
	sprintf(bp, LN_DECSLRM, lm, W_PAGE - lm); bp += strlen(bp);
    } else {
	lm = (left > 0)? left : (((H_MAX - scale * iw) / 2) + H_MARGIN);
	tm = (top > 0)? top : (((W_MAX - scale * ih * 6) / 2) + W_MARGIN);
	sprintf(bp, LN_PFS, "?21"); bp += 7;
	sprintf(bp, LN_DECOPM_SET); bp += sizeof LN_DECOPM_SET - 1;
	sprintf(bp, LN_DECSLRM, lm, H_PAGE - lm); bp += strlen(bp);
    }

    if (header != NULL) {
	sprintf(bp, LN_VPA, tm - 100); bp += strlen(bp);
	i = strlen(header);
	xm = (((scale * iw) - (i * 30)) / 2) + lm;
	sprintf(bp, LN_HPA, xm); bp += strlen(bp);
	sprintf(bp, LN_SGR, 3); bp += strlen(bp);
	bcopy(header, bp, i);
	bp += i;
    }
    if (trailer != NULL) {
	sprintf(bp, LN_VPA, tm + (scale * ih * 6) + 75); bp += strlen(bp);
	i = strlen(trailer);
	xm = (((scale * iw) - (i * 30)) / 2) + lm;
	sprintf(bp, LN_HPA, xm); bp += strlen(bp);
	sprintf(bp, LN_SGR, 3); bp += strlen(bp);
	bcopy(trailer, bp, i);
	bp += i;
    }

    sprintf(bp, LN_HPA, lm); bp += strlen(bp);
    sprintf(bp, LN_VPA, tm); bp += strlen(bp);
    sprintf(bp, LN_SIXEL_GRAPHICS, 9, 0, scale); bp += strlen(bp);
    sprintf(bp, "\"1;1"); bp += 4; /* Pixel aspect ratio */
    write(1, buf, bp-buf);
    *top_margin = tm;
    *left_margin = lm;
}

static
void ln03_finish(void)
{
    char buf[256];
    register char *bp = buf;

    sprintf(bp, LN_DECOPM_RESET); bp += sizeof LN_DECOPM_SET - 1;
    sprintf(bp, LN_LNM); bp += 5;
    sprintf(bp, LN_PUM); bp += 5;
    sprintf(bp, LN_PFS, "?20"); bp += 7; 
    sprintf(bp, LN_SGR, 0); bp += strlen(bp);   
    sprintf(bp, LN_HPA, 1); bp += strlen(bp);
    sprintf(bp, LN_VPA, 1); bp += strlen(bp);


    write(1, buf, bp-buf);    
}

/*ARGSUSED*/
static
void la100_setup(int iw, int ih, int scale)
{
    char buf[256];
    register char *bp;
    int lm, tm;

    bp = buf;
    lm = ((80 - (int)((double)iw / 6.6)) / 2) - 1;
    if (lm < 1) lm = 1;
    tm = ((66 - (int)((double)ih / 2)) / 2) - 1;
    if (tm < 1) tm = 1;
    sprintf(bp, "\033[%d;%ds", lm, 81-lm); bp += strlen(bp);
    sprintf(bp, "\033[?7l"); bp += 5;
    sprintf(bp, "\033[%dd", tm); bp += strlen(bp);
    sprintf(bp, "\033[%d`", lm); bp += strlen(bp);
    sprintf(bp, "\033P0q"); bp += 4;
    write(1, buf, bp-buf);
}

#define LA100_RESET "\033[1;80s\033[?7h"

static
void la100_finish(void)
{
    write(1, LA100_RESET, sizeof LA100_RESET - 1);
}

#define COMMENTVERSION "PS-Adobe-1.0"

#ifdef XPROLOG
/* for debugging, get the prolog from a file */
static
void dump_prolog(int flags)
{
    char *fname=(flags & F_COMPACT) ? "prolog.compact" : "prolog";
    FILE *fi = fopen(fname,"r");
    char buf[1024];

    if (fi==NULL) {
	perror(fname);
	exit(1);
    }
    while (fgets(buf,1024,fi)) fputs(buf,stdout);
    fclose(fi);
}

#else /* XPROLOG */
/* postscript "programs" to unpack and print the bitmaps being sent */

const
char *ps_prolog_compact[] = {
    "%%Pages: 1",
    "%%EndProlog",
    "%%Page: 1 1",
    "",
    "/bitgen",
    "	{",
    "		/nextpos 0 def",
    "		currentfile bufspace readhexstring pop % get a chunk of input",
    "		% interpret each byte of the input",
    "		{",
    "			flag { % if the previous byte was FF",
    "				/len exch def % this byte is a count",
    "				result",
    "				nextpos",
    "				FFstring 0 len getinterval % grap a chunk of FF's",
    "					putinterval % and stuff them into the result",
    "				/nextpos nextpos len add def",
    "				/flag false def",
    "			}{ % otherwise",
    "				dup 255 eq { % if this byte is FF",
    "					/flag true def % just set the flag",
    "					pop % and toss the FF",
    "				}{ % otherwise",
    "					% move this byte to the result",
    "					result nextpos",
    "						3 -1 roll % roll the current byte back to the top",
    "						put",
    "					/nextpos nextpos 1 add def",
    "				} ifelse",
    "			} ifelse",
    "		} forall",
    "		% trim unused space from end of result",
    "		result 0 nextpos getinterval",
    "	} def",
    "",
    "",
    "/bitdump % stk: width, height, iscale",
    "	% dump a bit image with lower left corner at current origin,",
    "	% scaling by iscale (iscale=1 means 1/300 inch per pixel)",
    "	{",
    "		% read arguments",
    "		/iscale exch def",
    "		/height exch def",
    "		/width exch def",
    "",
    "		% scale appropriately",
    "		width iscale mul height iscale mul scale",
    "",
    "		% data structures:",
    "",
    "		% allocate space for one line of input",
    "		/bufspace 36 string def",
    "",
    "		% string of FF's",
    "		/FFstring 256 string def",
    "		% for all i FFstring[i]=255",
    "		0 1 255 { FFstring exch 255 put } for",
    "",
    "		% 'escape' flag",
    "		/flag false def",
    "",
    "		% space for a chunk of generated bits",
    "		/result 4590 string def",
    "",
    "		% read and dump the image",
    "		width height 1 [width 0 0 height neg 0 height]",
    "			{ bitgen }",
    "			image",
    "	} def",
    0
};

const 
char *ps_prolog[] = {
    "%%Pages: 1",
    "%%EndProlog",
    "%%Page: 1 1",
    "",
    "/bitdump % stk: width, height, iscale",
    "% dump a bit image with lower left corner at current origin,",
    "% scaling by iscale (iscale=1 means 1/300 inch per pixel)",
    "{",
    "	% read arguments",
    "	/iscale exch def",
    "	/height exch def",
    "	/width exch def",
    "",
    "	% scale appropriately",
    "	width iscale mul height iscale mul scale",
    "",
    "	% allocate space for one scanline of input",
    "	/picstr % picstr holds one scan line",
    "		width 7 add 8 idiv % width of image in bytes = ceiling(width/8)",
    "		string",
    "		def",
    "",
    "	% read and dump the image",
    "	width height 1 [width 0 0 height neg 0 height]",
    "	{ currentfile picstr readhexstring pop }",
    "	image",
    "} def",
    0
};

static
void dump_prolog(int flags) {
    const char **p = (flags & F_COMPACT) ? ps_prolog_compact : ps_prolog;
    while (*p)
        printf("%s\n", *p++);
}
#endif /* XPROLOG */

#define PAPER_WIDTH 85*30 /* 8.5 inches */
#define PAPER_LENGTH 11*300 /* 11 inches */

static
int points(int n)
{
    /* scale n from pixels (1/300 inch) to points (1/72 inch) */
    n *= 72;
    return n/300;
}

static
char *escape(const char *s)
{
    /* make a version of s in which control characters are deleted and
     * special characters are escaped.
     */
    static char buf[200];
    char *p = buf;

    for (;*s;s++) {
	if (*s < ' ' || *s > 0176) continue;
	if (*s==')' || *s=='(' || *s == '\\') {
	    sprintf(p,"\\%03o",*s);
	    p += 4;
	}
	else *p++ = *s;
    }
    *p = 0;
    return buf;
}

static
void ps_setup(
  int iw,
  int ih,
  enum orientation orientation,
  int scale,
  int left,
  int top,
  int flags,
  const char *header,
  const char *trailer,
  const char *name)
{
    char    hostname[256];
#ifdef WIN32
    char *username;
#else
    struct passwd  *pswd;
#endif
    long    clock;
    int lm, bm; /* left (bottom) margin */

    /* calculate margins */
    if (orientation==PORTRAIT) {
	lm = (left > 0)? left : ((PAPER_WIDTH - scale * iw) / 2);
	bm = (top > 0)? (PAPER_LENGTH - top - scale * ih)
		: ((PAPER_LENGTH - scale * ih) / 2);
    } else { /* orientation == LANDSCAPE */
	lm = (top > 0)? (PAPER_WIDTH - top - scale * ih)
		: ((PAPER_WIDTH - scale * ih) / 2);
	bm = (left > 0)? (PAPER_LENGTH - left - scale * iw)
		: ((PAPER_LENGTH - scale * iw) / 2);
    }
    printf ("%%!%s\n", COMMENTVERSION);
    printf ("%%%%BoundingBox: %d %d %d %d\n",
	    (flags & F_NPOSITION) ? points(lm) : 0,
	    (flags & F_NPOSITION) ? points(bm) : 0,
	    points(iw * scale), points(ih * scale));
    (void) XmuGetHostname (hostname, sizeof hostname);
#ifdef WIN32
    username = getenv("USERNAME");
    printf ("%%%%Creator: %s:%s\n", hostname,
	    username ? username : "unknown");
#else
    pswd = getpwuid (getuid ());
    printf ("%%%%Creator: %s:%s (%s)\n", hostname,
	    pswd->pw_name, pswd->pw_gecos);
#endif
    printf ("%%%%Title: %s (%s)\n", infilename,name);
    printf ("%%%%CreationDate: %s",
		(time (&clock), ctime (&clock)));
    printf ("%%%%EndComments\n");

    dump_prolog(flags);

    if (orientation==PORTRAIT) {
	if (header || trailer) {
	    printf("gsave\n");
	    printf("/Times-Roman findfont 15 scalefont setfont\n");
	    /* origin at bottom left corner of image */
	    printf("%d %d translate\n",points(lm),points(bm));
	    if (header) {
		char *label = escape(header);
		printf("%d (%s) stringwidth pop sub 2 div %d moveto\n",
		    points(iw*scale), label, points(ih*scale) + 10);
		printf("(%s) show\n",label);
	    }
	    if (trailer) {
		char *label = escape(trailer);
		printf("%d (%s) stringwidth pop sub 2 div -20 moveto\n",
		    points(iw*scale), label);
		printf("(%s) show\n",label);
	    }
	    printf("grestore\n");
	}
	/* set resolution to device units (300/inch) */
	printf("72 300 div dup scale\n");
	/* move to lower left corner of image */
	if (!(flags & F_NPOSITION))
	    printf("%d %d translate\n",lm,bm);
	/* dump the bitmap */
	printf("%d %d %d bitdump\n",iw,ih,scale);
    } else { /* orientation == LANDSCAPE */
	if (header || trailer) {
	    printf("gsave\n");
	    printf("/Times-Roman findfont 15 scalefont setfont\n");
	    /* origin at top left corner of image */
	    printf("%d %d translate\n",points(lm),points(bm + scale * iw));
	    /* rotate to print the titles */
	    printf("-90 rotate\n");
	    if (header) {
		char *label = escape(header);
		printf("%d (%s) stringwidth pop sub 2 div %d moveto\n",
		    points(iw*scale), label, points(ih*scale) + 10);
		printf("(%s) show\n",label);
	    }
	    if (trailer) {
		char *label = escape(trailer);
		printf("%d (%s) stringwidth pop sub 2 div -20 moveto\n",
		    points(iw*scale), label);
		printf("(%s) show\n",label);
	    }
	    printf("grestore\n");
	}
	/* set resolution to device units (300/inch) */
	printf("72 300 div dup scale\n");
	/* move to lower left corner of image */
	if (!(flags & F_NPOSITION))
	    printf("%d %d translate\n",lm,bm);
	/* dump the bitmap */
	printf("%d %d %d bitdump\n",ih,iw,scale);
    }
}

const
char *ps_epilog[] = {
	"",
	"showpage",
	"%%Trailer",
	0
};

static
void ps_finish(void)
{
	char **p = ps_epilog;

	while (*p) printf("%s\n",*p++);
}

static
void ln03_alter_background(
  unsigned char (*sixmap)[],
  int iw,
  int ih)
{
    register unsigned char *c, *stopc;
    register unsigned char *startc;
    register int n;

    c = (unsigned char *)sixmap;
    stopc = c + (iw * ih);
    n = 0;
    while (c < stopc) {
	switch (*c) {
	case 0x08: case 0x11: case 0x04: case 0x22:
	case 0x20: case 0x21: case 0x24: case 0x00:
	    if (n == 0) startc = c;
	    n++;
	    break;

	default:
	    if (n >= 2) {
		while (n-- > 0) *startc++ = 0x00;
	    } else {
		n = 0;
	    }
	    break;
	}
	c++;
    }
}

static
void ln03_output_sixels(
  unsigned char (*sixmap)[],
  int iw,
  int ih,
  int nosixopt,
  int split,
  int scale,
  int top_margin,
  int left_margin)
{
    unsigned char *buf;
    register unsigned char *bp;
    int i;
    int j;
    register int k;
    register unsigned char *c;
    register int lastc;
    register int count;
    char snum[6];
    register char *snp;

    bp = (unsigned char *)malloc((unsigned)(iw*ih+512));
    buf = bp;
    count = 0;
    lastc = -1;
    c = (unsigned char *)sixmap;
    split = ih / split;		/* number of lines per page */

    iw--;			/* optimization */
    for (i = 0; i < ih; i++) {
	for (j = 0; j <= iw; j++) {
	    if (!nosixopt) {
		if (*c == lastc && j < iw) {
		    count++;
		    c++;
		    continue;
		}
		if (count >= 3) {
		    bp--;
		    count++;
		    *bp++ = '!';
		    snp = snum;
		    while (count > 0) {
			k = count / 10;
			*snp++ = count - (k * 10) + '0';
			count = k;
		    }
		    while (--snp >= snum) *bp++ = *snp;
		    *bp++ = (~lastc & 0x3F) + 0x3F;
		} else if (count > 0) {
		    lastc = (~lastc & 0x3F) + 0x3F;
		    do {
			*bp++ = lastc;
		    } while (--count > 0);
		}
	    }
	    lastc = *c++;
	    *bp++ = (~lastc & 0x3F) + 0x3F;
	}
	*bp++ = '-';		/* New line */
	lastc = -1;
	if ((i % split) == 0 && i != 0) {
	    sprintf((char *)bp, LN_ST); bp += sizeof LN_ST - 1;
	    *bp++ = '\f';
	    sprintf((char *)bp, LN_VPA, top_margin + (i * 6 * scale));
	    bp += strlen((char *)bp);
	    sprintf((char *)bp, LN_HPA, left_margin);
	    bp += strlen((char *)bp);
	    sprintf((char *)bp, LN_SIXEL_GRAPHICS, 9, 0, scale);
	    bp += strlen((char *)bp);
	    sprintf((char *)bp, "\"1;1"); bp += 4;
	}
    }

    sprintf((char *)bp, LN_ST); bp += sizeof LN_ST - 1;
    write(1, (char *)buf, bp-buf);
}

/*ARGSUSED*/
static
void la100_output_sixels(
  unsigned char (*sixmap)[],
  int iw,
  int ih,
  int nosixopt)
{
    unsigned char *buf;
    register unsigned char *bp;
    int i;
    register int j, k;
    register unsigned char *c;
    register int lastc;
    register int count;
    char snum[6];

    bp = (unsigned char *)malloc((unsigned)(iw*ih+512));
    buf = bp;
    count = 0;
    lastc = -1;
    c = (unsigned char *)sixmap;

    for (i = 0; i < ih; i++) {
	for (j = 0; j < iw; j++) {
	    if (*c == lastc && (j+1) < iw) {
		count++;
		c++;
		continue;
	    }
	    if (count >= 2) {
		bp -= 2;
		count = 2 * (count + 1);
		*bp++ = '!';
		k = 0;
		while (count > 0) {
		    snum[k++] = (count % 10) + '0';
		    count /= 10;
		}
		while (--k >= 0) *bp++ = snum[k];
		*bp++ = (~lastc & 0x3F) + 0x3F;
		count = 0;
	    } else if (count > 0) {
		lastc = (~lastc & 0x3F) + 0x3F;
		do {
		    *bp++ = lastc;
		    *bp++ = lastc;
		} while (--count > 0);
	    }
	    lastc = (~*c & 0x3F) + 0x3F;
	    *bp++ = lastc;
	    *bp++ = lastc;
	    lastc = *c++;
	}
	*bp++ = '-';		/* New line */
	lastc = -1;
    }

    sprintf((char *)bp, LN_ST); bp += sizeof LN_ST - 1;
    *bp++ = '\f';
    write(1, (char *)buf, bp-buf);
}

#define LINELEN 72 /* number of CHARS (bytes*2) per line of bitmap output */

static
void ps_output_bits(
  int iw,
  int ih,
  int flags,
  enum orientation orientation,
  XWDFileHeader *win,
  const char *data)
{
    unsigned long swaptest = 1;
    int iwb = win->bytes_per_line;
    register int i;
    int bytes;
    unsigned char *buffer = (unsigned char *)data;
    register int ocount=0;
    static char hex[] = "0123456789abcdef";

    if (orientation == LANDSCAPE) {
	/* read in and rotate the entire image */
	/* The Postscript language has a rotate operator, but using it
	 * seem to make printing (at least on the Apple Laserwriter
	 * take about 10 times as long (40 minutes for a 1024x864 full-screen
	 * dump)!  Therefore, we rotate the image here.
	 */
	int ocol = ih;
	int owidth = (ih+31)/32; /* width of rotated image, in bytes */
	int oheight = (iw+31)/32; /* height of rotated image, in scanlines */
	register char *p, *q;
	char *obuf;
	unsigned char *ibuf;
	owidth *= 4;
	oheight *= 32;

	/* Allocate buffer for the entire rotated image (output).
	 * Owidth and Oheight are rounded up to a multiple of 32 bits,
	 * to avoid special cases at the boundaries
	 */
	obuf = malloc((unsigned)(owidth*oheight));
	if (obuf==0) {
	    fprintf(stderr,"xpr: cannot allocate %d bytes\n",owidth*oheight);
	    exit(1);
	}
	bzero(obuf,owidth*oheight);

	ibuf = (unsigned char *)malloc((unsigned)(iwb + 3));
	for (i=0;i<ih;i++) {
	    bcopy((char *)buffer, (char *)ibuf, iwb);
	    buffer += iwb;
	    if (!(*(char *) &swaptest))
		_swaplong((char *)ibuf,(long)iwb);
	    ps_bitrot(ibuf,iw,--ocol,owidth,obuf);
	}
	if (!(*(char *) &swaptest))
	    _swaplong(obuf,(long)(iw*owidth));
	q = &obuf[iw*owidth];
	bytes = (ih+7)/8;
	for (p=obuf;p<q;p+=owidth)
	    ocount = ps_putbuf((unsigned char *)p,bytes,ocount,flags&F_COMPACT);
    }
    else {
	for (i=0;i<ih;i++) {
	    ocount = ps_putbuf(buffer,(iw+7)/8,ocount,flags&F_COMPACT);
	    buffer += iwb;
	}
    }
    if (flags & F_COMPACT) {
	if (ocount) {
	    /* pad to an integral number of lines */
	    while (ocount++ < LINELEN)
		/* for debugging, pad with a "random" value */
		putchar(hex[ocount&15]);
	    putchar('\n');
	}
    }
}

const
unsigned char _reverse_byte[0x100] = {
	0x00, 0x80, 0x40, 0xc0, 0x20, 0xa0, 0x60, 0xe0,
	0x10, 0x90, 0x50, 0xd0, 0x30, 0xb0, 0x70, 0xf0,
	0x08, 0x88, 0x48, 0xc8, 0x28, 0xa8, 0x68, 0xe8,
	0x18, 0x98, 0x58, 0xd8, 0x38, 0xb8, 0x78, 0xf8,
	0x04, 0x84, 0x44, 0xc4, 0x24, 0xa4, 0x64, 0xe4,
	0x14, 0x94, 0x54, 0xd4, 0x34, 0xb4, 0x74, 0xf4,
	0x0c, 0x8c, 0x4c, 0xcc, 0x2c, 0xac, 0x6c, 0xec,
	0x1c, 0x9c, 0x5c, 0xdc, 0x3c, 0xbc, 0x7c, 0xfc,
	0x02, 0x82, 0x42, 0xc2, 0x22, 0xa2, 0x62, 0xe2,
	0x12, 0x92, 0x52, 0xd2, 0x32, 0xb2, 0x72, 0xf2,
	0x0a, 0x8a, 0x4a, 0xca, 0x2a, 0xaa, 0x6a, 0xea,
	0x1a, 0x9a, 0x5a, 0xda, 0x3a, 0xba, 0x7a, 0xfa,
	0x06, 0x86, 0x46, 0xc6, 0x26, 0xa6, 0x66, 0xe6,
	0x16, 0x96, 0x56, 0xd6, 0x36, 0xb6, 0x76, 0xf6,
	0x0e, 0x8e, 0x4e, 0xce, 0x2e, 0xae, 0x6e, 0xee,
	0x1e, 0x9e, 0x5e, 0xde, 0x3e, 0xbe, 0x7e, 0xfe,
	0x01, 0x81, 0x41, 0xc1, 0x21, 0xa1, 0x61, 0xe1,
	0x11, 0x91, 0x51, 0xd1, 0x31, 0xb1, 0x71, 0xf1,
	0x09, 0x89, 0x49, 0xc9, 0x29, 0xa9, 0x69, 0xe9,
	0x19, 0x99, 0x59, 0xd9, 0x39, 0xb9, 0x79, 0xf9,
	0x05, 0x85, 0x45, 0xc5, 0x25, 0xa5, 0x65, 0xe5,
	0x15, 0x95, 0x55, 0xd5, 0x35, 0xb5, 0x75, 0xf5,
	0x0d, 0x8d, 0x4d, 0xcd, 0x2d, 0xad, 0x6d, 0xed,
	0x1d, 0x9d, 0x5d, 0xdd, 0x3d, 0xbd, 0x7d, 0xfd,
	0x03, 0x83, 0x43, 0xc3, 0x23, 0xa3, 0x63, 0xe3,
	0x13, 0x93, 0x53, 0xd3, 0x33, 0xb3, 0x73, 0xf3,
	0x0b, 0x8b, 0x4b, 0xcb, 0x2b, 0xab, 0x6b, 0xeb,
	0x1b, 0x9b, 0x5b, 0xdb, 0x3b, 0xbb, 0x7b, 0xfb,
	0x07, 0x87, 0x47, 0xc7, 0x27, 0xa7, 0x67, 0xe7,
	0x17, 0x97, 0x57, 0xd7, 0x37, 0xb7, 0x77, 0xf7,
	0x0f, 0x8f, 0x4f, 0xcf, 0x2f, 0xaf, 0x6f, 0xef,
	0x1f, 0x9f, 0x5f, 0xdf, 0x3f, 0xbf, 0x7f, 0xff
};

void _invbits (
  register unsigned char *b,
  register long n)
{
	do {
		*b = ~*b;
		b++;
	    } while (--n > 0);
	
}

/* copied from lib/X/XPutImage.c */

void _swapbits (
  register unsigned char *b,
  register long n)
{
	do {
		*b = _reverse_byte[*b];
		b++;
	    } while (--n > 0);
	
}

void _swapshort (
  register char *bp,
  register long n)
{
	register char c;
	register char *ep = bp + n;
	do {
		c = *bp;
		*bp = *(bp + 1);
		bp++;
		*bp = c;
		bp++;
	}
	while (bp < ep);
}

void _swaplong (
     register char *bp,
     register long n)
{
	register char c;
	register char *ep = bp + n;
	register char *sp;
	do {
	  	sp = bp + 3;
		c = *sp;
		*sp = *bp;
		*bp++ = c;
		sp = bp + 1;
		c = *sp;
		*sp = *bp;
		*bp++ = c;
		bp += 2;
	}
	while (bp < ep);
}

/* Dump some bytes in hex, with bits in each byte reversed
 * Ocount is number of chacters that have been written to the current
 * output line.  It's new value is returned as the result of the function.
 * Ocount is ignored (and the return value is meaningless) if compact==0.
 */
static
int ps_putbuf(
  register unsigned char *s,	/* buffer to dump */
  register int n,		/* number of BITS to dump */
  register int ocount,		/* position on output line for next char */
  int compact)			/* if non-zero, do compaction (see below) */
{
    register int ffcount = 0;
    static char hex[] = "0123456789abcdef";
#define PUT(c) { putchar(c); if (++ocount>=LINELEN) \
	{ putchar('\n'); ocount=0; }}

    if (compact) {
	/* The following loop puts out the bits of the image in hex,
	 * compressing runs of white space (represented by one bits)
	 * according the the following simple algorithm:  A run of n
	 * 'ff' bytes (i.e., bytes with value 255--all ones), where
	 * 1<=n<=255, is represented by a single 'ff' byte followed by a
	 * byte containing n.
	 * On a typical dump of a full screen pretty much covered by
	 * black-on-white text windows, this compression decreased the
	 * size of the file from 223 Kbytes to 63 Kbytes.
	 * Of course, another factor of two could be saved by sending
	 * the bytes 'as is' rather than in hex, using some sort of
	 * escape convention to avoid problems with control characters.
	 * Another popular encoding is to pack three bytes into 4 'sixels'
	 * as in the LN03, etc, but I'm too lazy to write the necessary
	 * PostScript code to unpack fancier representations.
	 */
	while (n--) {
	    if (*s == 0xff) {
		if (++ffcount == 255) {
		    PUT('f'); PUT('f');
		    PUT('f'); PUT('f');
		    ffcount = 0;
		}
	    }
	    else {
		if (ffcount) {
		    PUT('f'); PUT('f');
		    PUT(hex[ffcount >> 4]);
		    PUT(hex[ffcount & 0xf]);
		    ffcount = 0;
		}
		PUT(hex1[*s]);
		PUT(hex2[*s]);
	    }
	    s++;
	}
	if (ffcount) {
	    PUT('f'); PUT('f');
		PUT(hex[ffcount >> 4]);
	    PUT(hex[ffcount & 0xf]);
	    ffcount = 0;
	}
    }
    else { /* no compaction: just dump the image in hex (bits reversed) */
	while (n--) {
	    putchar(hex1[*s]);
	    putchar(hex2[*s++]);
	}
	putchar('\n');
    }
    return ocount;
}

static
void ps_bitrot(
  unsigned char *s,
  register int n,
  int col,
  register int owidth,
  char *obuf)
/* s points to a chunk of memory and n is its width in bits.
 * The algorithm is, roughly,
 *    for (i=0;i<n;i++) {
 *        OR the ith bit of s into the ith row of the
 *        (col)th column of obuf
 *    }
 * Assume VAX bit and byte ordering for s:
 *	The ith bit of s is s[j]&(1<<k) where i=8*j+k.
 *	It can also be retrieved as t[j]&(1<<k), where t=(int*)s and i=32*j+k.
 * Also assume VAX bit and byte ordering for each row of obuf.
 * Ps_putbuf() takes care of converting to Motorola 68000 byte and bit
 * ordering.  The following code is very carefully tuned to yield a very
 * tight loop on the VAX, since it easily dominates the entire running
 * time of this program.  In particular, iwordp is declared last, since
 * there aren't enough registers, and iwordp is referenced only once
 * every 32 times through the loop.
 */
{
    register int mask = 1<<(col%32);
    register int iword; /* current input word (*iwordp) */
    register int b = 0; /* number of bits in iword left to examine */
    register char *opos = obuf + (col/32)*4;
	/* pointer to word of obuf to receive next output bit */
    register int *iwordp = (int *) s; /* pointer to next word of s */

    while (--n>=0) {
	if (--b < 0) {
	    iword = *iwordp++;
	    b = 31;
	}
	if (iword & 1) {
	    *(int *)opos |= mask;
	}
	opos += owidth;
	iword >>= 1;
    }
}

/* fullread() is the same as read(), except that it guarantees to 
   read all the bytes requested. */

static
void fullread (
  int file,
  char *data,
  int nbytes)
{
    int bytes_read;
    while ((bytes_read = read(file, data, nbytes)) != nbytes) {
	if (bytes_read < 0) {
	    perror ("error while reading standard input");
	    return;
	    }
	else if (bytes_read == 0) {
	    fprintf (stderr, "xpr: premature end of file\n");
	    return;
	    }
	nbytes -= bytes_read;
	data += bytes_read;
	}
}
