/*
 *----------------------------------------------------------------------
 * plotHP.c --
 *
 * This file contains the procedures that generate plots in
 * HP Raster Transfer Language (HPRTL) and HPGL2.
 *
 *----------------------------------------------------------------------
 */

#include <stdio.h>
#include <string.h>
#include <errno.h>

#include "utils/malloc.h"
#include "plot/plotInt.h"

/* C99 compat */
#include "textio/textio.h"

#ifdef VERSATEC

extern int PlotRTLCompress();
extern void PlotHPRTLTrailer();

extern int rasFileByteCount;

/*
 * ----------------------------------------------------------------------------
 *
 * PlotHPRTLHeader --
 *
 *	Header for HP RTL format (standard for, e.g., HP color laser printers)
 *
 * ----------------------------------------------------------------------------
 */

void
PlotHPRTLHeader(width, height, density, hpfile)
    int width, height, density;
    FILE *hpfile;
{
    fprintf(hpfile, "\033*r-3U\n");	   /* Simple CMY color space  */
    fprintf(hpfile, "\033*r%dS", width);   /* Image width in pixels. */
    fprintf(hpfile, "\033*r%dT", height);  /* Image height in pixels.*/
    fprintf(hpfile, "\033&a1N");	   /* No negative motion. */
    fprintf(hpfile, "\033*b2M");	   /* Mode 2 row compression */
    fprintf(hpfile, "\033*t%dR", density); /* Plotting density in DPI. */
    fprintf(hpfile, "\033*r0A");	   /* Start raster data. */
}

/*
 * ----------------------------------------------------------------------------
 *
 * PlotHPGL2Header --
 *
 *	Header for HPGL2 (plotter) format
 *
 * ----------------------------------------------------------------------------
 */

#define	LABEL_SPACER 200	/* Height of the label area, in pixels	  */
#define IN_TO_HPGL  1016	/* HPGL2 coordinates are in 0.025mm units */
#define THIN_MARGIN   40	/* thin spacer (1mm) in HPGL2 coordinates */

void
PlotHPGL2Header(width, height, density, scale, hpfile)
    int width, height, density, scale;
    FILE *hpfile;
{
    fprintf(hpfile, "\033%%-12345X");	   /* Universal Command Language. */
    fprintf(hpfile, "@PJL ENTER LANGUAGE=HPGL2\r\n");
    fprintf(hpfile, "\033E\033%%0B");	   /* Reset printer; set HPGL2 mode. */
    fprintf(hpfile, "BP1,\"MAGIC\",5,1;"); /* Declare name; disable auto-rotate */

    fprintf(hpfile, "PS%d,%d;", ((height + LABEL_SPACER) * IN_TO_HPGL / density)
		+ THIN_MARGIN, (width * IN_TO_HPGL / density) + THIN_MARGIN);
    /* Move the pen to the right edge of the paper. */
    fprintf(hpfile, "SP1PA%d,0", (width * IN_TO_HPGL / density));
    /* Plot upside down with a 10-point font. */
    fprintf(hpfile, "DI-1,0SD3,10;");
    /* Plot a label. */
    fprintf(hpfile, "LB\r\nMagic Plot (%dX)\r\n\003SP0;", scale);

    fprintf(hpfile, "\033%%0A");			/* Enter RTL mode. */
    fprintf(hpfile, "\033*v1N");			/* Source mode opaque */

    /* Make room for the label string by moving vertically. */
    fprintf(hpfile, "\033*b%dY", LABEL_SPACER);

    /* HPRTL Color reference guide:						*/
    /* h20000.www2.hp.com/bc/docs/support/SupportManual/bpl13212/bpl13212.pdf	*/

    /* Configure Image Data (CID) command		*/
    /* Color space (1) = CMY				*/
    /* Pixel encoding mode (0) = Index by plane		*/
    /* Bits per index (3) = 8-index (2^3) colormap	*/
    /* Color planes (1, 1, 1) = 1 bit each		*/
    /* Bit planes are cyan / magenta / yellow and must	*/
    /* be output in that order.				*/

    /* fwrite("\033*v6W\1\0\3\1\1\1", 11, 1, hpfile); */

    /* Apparently some plotters don't support plane-indexed CMY mode.	*/
    /* Due to this oversight, it is necessary to build the CMY color	*/
    /* table by hand by reversing the RGB color table.			*/

    fwrite("\033*v6W\0\0\3\1\1\1", 11, 1, hpfile);
    fprintf(hpfile, "\033*v255a255b255c0I\n");
    fprintf(hpfile, "\033*v0a255b255c1I\n");
    fprintf(hpfile, "\033*v255a0b255c2I\n");
    fprintf(hpfile, "\033*v0a0b255c3I\n");
    fprintf(hpfile, "\033*v255a255b0c4I\n");
    fprintf(hpfile, "\033*v0a255b0c5I\n");
    fprintf(hpfile, "\033*v255a0b0c6I\n");
    fprintf(hpfile, "\033*v0a0b0c7I\n");

    fprintf(hpfile, "\033*r%dS", width);   /* Set the image width in pixels. */
    fprintf(hpfile, "\033*r%dT", height);  /* Set the image height in pixels.*/
    fprintf(hpfile, "\033&a1N");	   /* No negative motion. */
    fprintf(hpfile, "\033*b2M");	   /* Mode 2 row compression */
    fprintf(hpfile, "\033*t%dR", density); /* Plot density, in DPI. */
    fprintf(hpfile, "\033*r1A");	   /* Start sending raster data. */
}

/*
 * ----------------------------------------------------------------------------
 *
 * PlotHPRTLTrailer --
 *
 * ----------------------------------------------------------------------------
 */

void
PlotHPRTLTrailer(hpfile)
    FILE *hpfile;
{
    fprintf(hpfile, "\033*r0B\014\n");		/* End raster graphics. */
}

/*
 * ----------------------------------------------------------------------------
 *
 * PlotHPGL2Trailer --
 *
 * ----------------------------------------------------------------------------
 */

void
PlotHPGL2Trailer(hpfile)
    FILE *hpfile;
{
    fprintf(hpfile, "\033*rC");		/* End raster graphics. */
    fprintf(hpfile, "\033%%0B");	/* HPGL2 mode. */
    fprintf(hpfile, "PG;");		/* Terminate plot. */
    fprintf(hpfile, "\033%%-12345X");	/* Universal command language reset. */
    fprintf(hpfile, "@PJL\r\n");
}

/*
 * ----------------------------------------------------------------------------
 * HP Run-length compression algorithm #2
 * This code has been taken from the "p3" PNM-to-HPRTL conversion tool.
 * Subroutine written by Noel Gorelick (ngorelic@speclab.cr.usgs.gov)
 * Author's comments below:
 *
 * The HP Paintjet 300-XL and DesignJet 650C both have the option of receiving
 * data using TIFF packbits encoding.  This seemed like a good idea to me, so I
 * ran to the libtiff package to see if there was anything useful there to do
 * packbits for me.  I was sorely disappointed to find a large (225 lines)
 * routine that will do this, but replys heavily on the internal data
 * structures used by the rest of the libtiff library.
 *
 * So, I wrote my own.  Here is a much simpler pair of routines to do the same
 * thing.  These were tested on both the PaintJet-300XL and DesignJet 650C,
 * having been compiled using an HP9000s800.  If anyone is actually interested
 * in the driver program that converts a PPM image for use on these printers, I
 * could probably post that as well.
 *
 * Noel (ngorelic@speclab.cr.usgs.gov)
 *-----------------------------------------------------------------------
 */

/*
 *-----------------------------------------------------------------------
 *
 * PlotRTLCompress --
 *
 * This routine encodes a string using the TIFF packbits encoding
 * scheme.  This encoding method is used by several HP peripherals to
 * reduce the size of incoming raster images.  Both routines convert s1
 * into s2.  The len parameter indicates the size of the incoming string.
 * The return value is the size of the output string (in s2).
 *
 * Results:
 *	Returns the length of the compressed data.  Note that compression
 *	is not guaranteed, and the output may be larger than the input.
 *	However, it is bounded by N + (N / 127) + 1.
 *
 * Side effects:
 *	Output data placed in s2, which must be large enough to hold it.
 *
 *-----------------------------------------------------------------------
 */

int
PlotRTLCompress(s1, s2, len)
    unsigned char *s1, *s2;
    int len;
{
    /*
     * Pack s1 using TIFF packbits encoding into s2
     */
    int count = 0;
    int i;
    int base, newbase, size, outp;

    base = newbase = outp = 0;
    for (i = 1; i < len; i++)
    {
	if (s1[newbase] == s1[i])
	    count++;
	else
	{
	    if (count < 2)
	    {
		newbase = i;
		count = 0;
	    }
	    else
	    {
		/*
		 * Put any backed up literals first.
		 */
		while ((newbase - base) > 0)
		{
		    size = MIN(127, newbase - base - 1);
		    s2[outp++] = size;
		    memcpy(s2 + outp, s1 + base, size + 1);
		    outp += size + 1;
		    base += size + 1;
		}
		/*
		 * Now put -count and repeated string.
		 */
		count++;
		while (count > 0)
		{
		    size = MIN(128, count);
		    s2[outp++] = -(size - 1);
		    s2[outp++] = s1[newbase];
		    count -= size;
		}
		base = newbase = i;
	    }
	}
    }

    /*
     * Output any trailing literals.
     */
    newbase = i;
    while ((newbase - base) > 0)
    {
	size = MIN(127, newbase - base - 1);
	s2[outp++] = size;
	memcpy(s2 + outp, s1 + base, size + 1);
	outp += size + 1;
	base += size + 1;
    }
    return (outp);
}

/*
 * ----------------------------------------------------------------------------
 *
 * PlotDumpHPRTL  --
 *
 *	Combines the four (CMYK) swath buffers by ORing the black image
 *	into the primary color swaths.  The swath is compressed using
 *	HP run-length row compression mode 2 (TIFF compression).
 *
 * Results:
 *	Returns 0 if all was well.  Returns non-zero if there was
 *	an I/O error.  In this event, this procedure prints an
 *	error message before returning.
 *
 * Side effects:
 *	Information is added to the file.
 *
 * ----------------------------------------------------------------------------
 */

int
PlotDumpHPRTL(hpfile, kRaster, cRaster, mRaster, yRaster)
    FILE *hpfile;		/* File in which to dump it. */
    Raster *kRaster;		/* Rasters to be dumped. */
    Raster *cRaster;
    Raster *mRaster;
    Raster *yRaster;
{
    int line, count, line_offset = 0;
    int ipl, bpl;
    register int *c, *m, *y, *k;
    unsigned char *obytes;	/* bytes to output (compressed) */
    int size;

    ipl = kRaster->ras_intsPerLine;
    bpl = kRaster->ras_bytesPerLine;

    c = cRaster->ras_bits;
    m = mRaster->ras_bits;
    y = yRaster->ras_bits;
    k = kRaster->ras_bits;

    /* Mode 2 row compression has a worst-case length of N + (N / 127) + 1 */
    obytes = (unsigned char *)mallocMagic(bpl + (bpl / 127) + 1);

    for (line = 0; line < kRaster->ras_height; line++)
    {
	/* Merge the black plane into C, M, and Y */
	for (count = 0; count < ipl; count++)
	{
	    *c = (*c | *k);
	    *m = (*m | *k);
	    *y = (*y | *k);
	    c++;
	    m++;
	    y++;
	    k++;
	}

	/* Compress each plane (C, M, and Y) and output */

	size = PlotRTLCompress((unsigned char *)(c - ipl), obytes, bpl);
	fprintf(hpfile, "\033*b%dV", size);
	fwrite(obytes, size, 1, hpfile);

	size = PlotRTLCompress((unsigned char *)(m - ipl), obytes, bpl);
	fprintf(hpfile, "\033*b%dV", size);
	fwrite(obytes, size, 1, hpfile);

	size = PlotRTLCompress((unsigned char *)(y - ipl), obytes, bpl);
	fprintf(hpfile, "\033*b%dW", size);
	fwrite(obytes, size, 1, hpfile);
    }
    freeMagic(obytes);

    if (count < 0)
    {
	TxError("I/O error in writing HPRTL file:  %s.\n", strerror(errno));
	return 1;
    }
    rasFileByteCount += count;
    return 0;
}

#endif /* VERSATEC */
