/* Copyright (C) 1997, 1999 artofcode LLC.  All rights reserved.
  
  This program is free software; you can redistribute it and/or modify it
  under the terms of the GNU General Public License as published by the
  Free Software Foundation; either version 2 of the License, or (at your
  option) any later version.

  This program is distributed in the hope that it will be useful, but
  WITHOUT ANY WARRANTY; without even the implied warranty of
  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
  General Public License for more details.

  You should have received a copy of the GNU General Public License along
  with this program; if not, write to the Free Software Foundation, Inc.,
  59 Temple Place, Suite 330, Boston, MA, 02111-1307.

*/

/*$Id: gdevlj56.c,v 1.3.2.1.2.1 2003/01/17 00:49:00 giles Exp $ */
/* H-P LaserJet 5 & 6 drivers for Ghostscript */
#include "gdevprn.h"
#include "stream.h"
#include "gdevpcl.h"
#include "gdevpxat.h"
#include "gdevpxen.h"
#include "gdevpxop.h"
#include "gdevpxut.h"

/* Define the default resolution. */
#ifndef X_DPI
#  define X_DPI 600
#endif
#ifndef Y_DPI
#  define Y_DPI 600
#endif

/* Define the number of blank lines that make it worthwhile to */
/* start a new image. */
#define MIN_SKIP_LINES 2

/* We round up the LINE_SIZE to a multiple of a ulong for faster scanning. */
#define W sizeof(word)

private dev_proc_open_device(ljet5_open);
private dev_proc_close_device(ljet5_close);
private dev_proc_print_page(ljet5_print_page);

private const gx_device_procs ljet5_procs =
prn_procs(ljet5_open, gdev_prn_output_page, ljet5_close);

const gx_device_printer gs_lj5mono_device =
prn_device(ljet5_procs, "lj5mono",
	   DEFAULT_WIDTH_10THS, DEFAULT_HEIGHT_10THS,
	   X_DPI, Y_DPI,
	   0, 0, 0, 0,
	   1, ljet5_print_page);

private const gx_device_procs lj5gray_procs =
prn_color_procs(ljet5_open, gdev_prn_output_page, ljet5_close,
		gx_default_gray_map_rgb_color,
		gx_default_gray_map_color_rgb);

const gx_device_printer gs_lj5gray_device = {
    prn_device_body(gx_device_printer, lj5gray_procs, "lj5gray",
		    DEFAULT_WIDTH_10THS, DEFAULT_HEIGHT_10THS,
		    X_DPI, Y_DPI,
		    0, 0, 0, 0,
		    1, 8, 255, 0, 256, 1, ljet5_print_page)
};

/* Open the printer, writing the stream header. */
private int
ljet5_open(gx_device * pdev)
{
    int code = gdev_prn_open(pdev);

    if (code < 0)
	return code;
    code = gdev_prn_open_printer(pdev, true);
    if (code < 0)
	return code;
    {
	gx_device_printer *const ppdev = (gx_device_printer *)pdev;
	stream fs;
	stream *const s = &fs;
	byte buf[50];		/* arbitrary */

	swrite_file(s, ppdev->file, buf, sizeof(buf));
	px_write_file_header(s, pdev);
	sflush(s);		/* don't close */
    }
    return 0;
}

/* Close the printer, writing the stream trailer. */
private int
ljet5_close(gx_device * pdev)
{
    gx_device_printer *const ppdev = (gx_device_printer *)pdev;
    int code = gdev_prn_open_printer(pdev, true);

    if (code < 0)
	return code;
    px_write_file_trailer(ppdev->file);
    return gdev_prn_close(pdev);
}

/* Send the page to the printer.  For now, just send the whole image. */
private int
ljet5_print_page(gx_device_printer * pdev, FILE * prn_stream)
{
    gs_memory_t *mem = pdev->memory;
    uint line_size = gdev_mem_bytes_per_scan_line((gx_device *) pdev);
    uint line_size_words = (line_size + W - 1) / W;
    uint out_size = line_size + (line_size / 127) + 1;
    word *line = (word *)gs_alloc_byte_array(mem, line_size_words, W, "ljet5(line)");
    byte *out = gs_alloc_bytes(mem, out_size, "ljet5(out)");
    int code = 0;
    int lnum;
    stream fs;
    stream *const s = &fs;
    byte buf[200];		/* arbitrary */

    if (line == 0 || out == 0) {
	code = gs_note_error(gs_error_VMerror);
	goto done;
    }
    swrite_file(s, prn_stream, buf, sizeof(buf));

    /* Write the page header. */
    {
	static const byte page_header[] = {
	    pxtBeginPage,
	    DUSP(0, 0), DA(pxaPoint),
	    pxtSetCursor
	};
	static const byte mono_header[] = {
	    DUB(eGray), DA(pxaColorSpace),
	    DUB(e8Bit), DA(pxaPaletteDepth),
	    pxt_ubyte_array, pxt_ubyte, 2, 0xff, 0x00, DA(pxaPaletteData),
	    pxtSetColorSpace
	};
	static const byte gray_header[] = {
	    DUB(eGray), DA(pxaColorSpace),
	    pxtSetColorSpace
	};

	px_write_page_header(s, (gx_device *)pdev);
	px_write_select_media(s, (gx_device *)pdev, NULL);
	PX_PUT_LIT(s, page_header);
	if (pdev->color_info.depth == 1)
	    PX_PUT_LIT(s, mono_header);
	else
	    PX_PUT_LIT(s, gray_header);
    }

    /* Write the image header. */
    {
	static const byte mono_image_header[] = {
	    DA(pxaDestinationSize),
	    DUB(eIndexedPixel), DA(pxaColorMapping),
	    DUB(e1Bit), DA(pxaColorDepth),
	    pxtBeginImage
	};
	static const byte gray_image_header[] = {
	    DA(pxaDestinationSize),
	    DUB(eDirectPixel), DA(pxaColorMapping),
	    DUB(e8Bit), DA(pxaColorDepth),
	    pxtBeginImage
	};

	px_put_us(s, pdev->width);
	px_put_a(s, pxaSourceWidth);
	px_put_us(s, pdev->height);
	px_put_a(s, pxaSourceHeight);
	px_put_usp(s, pdev->width, pdev->height);
	if (pdev->color_info.depth == 1)
	    PX_PUT_LIT(s, mono_image_header);
	else
	    PX_PUT_LIT(s, gray_image_header);
    }

    /* Write the image data, compressing each line. */
    for (lnum = 0; lnum < pdev->height; ++lnum) {
	int ncompr;
	static const byte line_header[] = {
	    DA(pxaStartLine),
	    DUS(1), DA(pxaBlockHeight),
	    DUB(eRLECompression), DA(pxaCompressMode),
	    pxtReadImage
	};

	code = gdev_prn_copy_scan_lines(pdev, lnum, (byte *) line, line_size);
	if (code < 0)
	    goto fin;
	px_put_us(s, lnum);
	PX_PUT_LIT(s, line_header);
	ncompr = gdev_pcl_mode2compress_padded(line, line + line_size_words,
					       out, true);
	px_put_data_length(s, ncompr);
	px_put_bytes(s, out, ncompr);
    }

    /* Finish up. */
  fin:
    spputc(s, pxtEndImage);
    spputc(s, pxtEndPage);
    sflush(s);
  done:
    gs_free_object(mem, out, "ljet5(out)");
    gs_free_object(mem, line, "ljet5(line)");
    return code;
}
