/*
 * $Id: cpostscript.cxx,v 4.6 2004/06/21 08:18:29 hut66au Exp $
 *
 * Imview, the portable image analysis application
 * http://www.cmis.csiro.au/Hugues.Talbot/imview
 * ----------------------------------------------------------
 *
 *  Imview is an attempt to provide an image display application
 *  suitable for professional image analysis. It was started in
 *  1997 and is mostly the result of the efforts of Hugues Talbot,
 *  Image Analysis Project, CSIRO Mathematical and Information
 *  Sciences, with help from others (see the CREDITS files for
 *  more information)
 *
 *  Imview is Copyrighted (C) 1997-2001 by Hugues Talbot and was
 *  supported in parts by the Australian Commonwealth Science and 
 *  Industry Research Organisation. Please see the COPYRIGHT file 
 *  for full details. Imview also includes the contributions of 
 *  many others. Please see the CREDITS file for full details.
 *
 *  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, USA.
 * */



/* $Id: cpostscript.cxx,v 4.6 2004/06/21 08:18:29 hut66au Exp $ */
/*------------------------------------------------------------------------
 *      cpostscript.c
 *
 *	This file contains functions used to produce a page or an
 *	encapsulated postscript file from a list of images in the
 * 	form of buffers.
 *
 *	by Hugues Talbot	16 Feb 1995
 *      modified Hugues Talbot	23 Apr 1998 for ImView
 *     
*-----------------------------------------------------------------------*/



#include <stdio.h>
#include <string.h>
#include <math.h>
#include <time.h>
#include <assert.h>
#include "imview.hxx"
#include "cpostscript.hxx"

extern int dbgprintf(const char *msg, ...);
extern const char *patchlevel;
extern char  appName[];

/* globals */
static char errMsg[300];
/* page size in points */

extern float page_formats[][2];

static margin_size currentMarginIndex = MARGIN_SIZE_STANDARD;

/* various allowable margins in points */
static float page_margins[MARGIN_SIZE_NB][MARGIN_INDEX_NB] = {
    {25.0, 25.0, 30.0, 75.0, 40.0, 10.0, 10.0}, // standard
    {10.0, 10.0, 10.0, 20.0, 10.0,  5.0,  5.0}, // small
    {40.0, 40.0, 40.0, 80.0, 40.0, 10.0, 10.0}, // large
    { 0.0,  0.0,  0.0,  0.0,  0.0,  0.0,  0.0}, // zero (the title will never be printed!)
    { 0.0,  0.0,  0.0, 35.0,  0.0,  5.0,  5.0}  // custom
};

static void ps_getWindowBounds(byte papertype,
			byte win_type,
			byte orientation,
			float *boundOx,
			float *boundOy,
			float *boundW,
			float *boundH);

static void ps_arrange_images(HIMAGE *theImageList,
		       int nbIm,
		       double aspectRatio,
		       float boundOx,
		       float boundOy,
		       float boundW,
		       float boundH,
		       int nbAcross,
		       int nbDown,
		       byte drawFrame,
		       FILE *tbp);

static void ps_start(byte papertype, byte orientation, int nbCopies, char *fontName, FILE *tbp);
static void ps_end( FILE *tbp);
static void arrange_page(int nbIm, float trueW, float trueH, long maxRows, long maxCols, double aspectRatio,
		  int *nacross, int *ndown);
static void ps_printAnImage(HIMAGE theimage, BOX *subbox, double theScale, double aspectRatio, byte drawFrame, FILE *tbp);
static void getMinMax(PIX_TYPE *buf, long nbpix, PIX_TYPE *theMin, PIX_TYPE *theMax);
static void ps_printTitle(float boundOx, float boundOy, float boundW, float boundH, char *theTitle, char *fontName, FILE *tbp);
static void ps_drawRibbon(float boundOx, float boundOy, float boundW, float boundH, FILE *tbp);
static void eps_start(HIMAGE anImage, double aspect, FILE *tbp);
static void eps_save(HIMAGE anImage, double aspect, FILE *tbp);
static void eps_end(FILE *tbp);
static void myDebug(char *string);

/* 
 *
 * margin management 
 *
 */

void postscript_set_margin_index(margin_size whichMargins)
{
    currentMarginIndex = whichMargins;
}

void postscript_set_custom_margins(float allmargings[MARGIN_INDEX_NB])
{
    for (int i = 0 ; i < MARGIN_INDEX_NB ; ++i) {
	page_margins[MARGIN_SIZE_CUSTOM][i] = allmargings[i];
    }
}

void postscript_get_margins(margin_size whichMargin, float allmargings[MARGIN_INDEX_NB])
{
    assert((whichMargin >= 0) && (whichMargin < MARGIN_SIZE_NB));
    for (int i = 0 ; i < MARGIN_INDEX_NB ; ++i) {
	allmargings[i] = page_margins[whichMargin][i];
    }
}

/*
 *
 * compose_PostScript_Code()
 *
 * this function takes a list of images and a flurry of options, and
 *  output that on a PostScript Device
 *
 */

int compose_PostScript_code(HIMAGE *theImageList,	/* image list */
			    int	nbIm, /* real number of images */
			    double aspectRatio, /* aspect of the image */
			    byte paperType, /* type of the paper (a4, etc) */
			    byte win_type, /* portion of the window covered */
			    byte orientation, /* landscape or portrait */
			    int nbCopies, /* nb of copies */
			    int nbAcross, /* nb of images across */
			    int nbDown, /* nb of images down */
			    char *theTitle, /* title of the page */
			    char *fontName, /* name of the font */
			    byte hasRibbon,
			    byte drawFrame,
			    FILE *tbp)
{
    float boundOx, boundOy, boundW, boundH;

    /* case of the EPS */
    if (win_type != WIN_EPS) {
	myDebug("Standart ps code to eventually print on a page");
	/* get the actual bounds of the window */
	ps_getWindowBounds(paperType, win_type, orientation, &boundOx, &boundOy, &boundW, &boundH);
	/* startup */
	ps_start(paperType, orientation, nbCopies, fontName, tbp);
	/* arrange images on the page */
	ps_arrange_images(theImageList, nbIm, aspectRatio, boundOx, boundOy, boundW, boundH,
			  nbAcross, nbDown, drawFrame, tbp);
	/* title */
	if (theTitle != NULL) {
	    ps_printTitle(boundOx, boundOy, boundW, boundH, theTitle, fontName, tbp);
	}
	/* ribbon if asked for */
	if (hasRibbon) {
	    ps_drawRibbon(boundOx, boundOy, boundW, boundH, tbp);
	}

	/* the end */
	ps_end(tbp);
    } else {
	/* when eps is required, everything is different : we consider */
	/* only the first image, and output only this one with the tightest bounding box */
	/* this image can't be printed, a file name must be supplied */
	myDebug("Saving first image on command line into Encapsulated Postscript");
	if (nbIm > 1) {
	    myDebug("** Only the first image will be saved to file **");
	}
	if (tbp == NULL) { /* a file name must be supplied */
	    return EPS_NOFILE;
	}
	/* real stuff now */
	eps_start(theImageList[0], aspectRatio, tbp);
	eps_save(theImageList[0], aspectRatio, tbp); /* extremely simple... */
	eps_end(tbp);
    }
    
    return 0;
}


/* computes the correct bounds */
void ps_getWindowBounds(byte papertype, byte win_type, byte orientation, float *boundOx, float *boundOy, float *boundW,
			float *boundH)
{
    if (orientation == ORI_PRT) {
	myDebug("image is oriented as portrait");
	if (win_type == WIN_ALL) {
	    myDebug("the whole page is used");
	    *boundOx = *boundOy = 0.0;
	    *boundW = page_formats[papertype][0];
	    *boundH = page_formats[papertype][1];
	} else	if (win_type == WIN_UP) {
	    myDebug("Only the top-half of the page is used");
	    *boundOx = 0.0;
	    *boundOy = page_formats[papertype][1]/2;
	    *boundW = page_formats[papertype][0];
	    *boundH = page_formats[papertype][1];
	} else if (win_type == WIN_LOW) {
	    myDebug("Only the bottom-half of the page is used");
	    *boundOx = *boundOy = 0.0;
	    *boundW = page_formats[papertype][0];
	    *boundH = page_formats[papertype][1]/2;
	}
    } else if (orientation == ORI_LDS) {
	myDebug("image is oriented as landscape");
	if (win_type == WIN_ALL) {
	    myDebug("the whole page is used");
	    *boundOx = *boundOy = 0.0;
	    *boundW = page_formats[papertype][1];
	    *boundH = page_formats[papertype][0];
	} else	if (win_type == WIN_UP) {
	    myDebug("Only the top-half of the page is used");
	    *boundOx = 0.0;
	    *boundOy = page_formats[papertype][0]/2;
	    *boundW = page_formats[papertype][1];
	    *boundH = page_formats[papertype][0];
	} else if (win_type == WIN_LOW) {
	    myDebug("Only the bottom-half of the page is used");
	    *boundOx = *boundOy = 0.0;
	    *boundW = page_formats[papertype][1];
	    *boundH = page_formats[papertype][0]/2;
	}
    }
    
    return;
}
    
/* the blurb at the beginning of the PS file */
void ps_start(byte papertype, byte orientation, int nbCopies, char *fontName, FILE *tbp)
{
    time_t theTime;

    myDebug("Writing ps header");
    /* first the comments */
    theTime = time(0);
    fprintf(tbp, "%%!PS-Adobe-3.0 EPSF-3.0\n"); /* this comment is compulsery, the rest is mostly makeup */
    fprintf(tbp, "%%%%Creator: %s-%s by CSIRO-MIS\n", appName, patchlevel);
    fprintf(tbp, "%%%%Title: Image printout\n");
    fprintf(tbp, "%%%%CreationDate: %s", ctime(&theTime));
    fprintf(tbp, "%%%%DocumentFonts: %s\n", fontName);
    fprintf(tbp, "%%%%Pages:1\n");
    fprintf(tbp, "%%%%Origin: 0 0\n");
    fprintf(tbp, "%%%%BoundingBox: 0 0 %d %d\n", (int)page_formats[papertype][0],(int)page_formats[papertype][1]);
    fprintf(tbp,
	    "%%%%EndComments\n"
	    "%%%%EndProlog\n"
	    "%%%%Page: 1 1\n");

    /* now the environnent */
    fprintf(tbp, "%%%%BeginSetup\n/#copies %d def\n%%%%EndSetup\n", nbCopies);
    fprintf(tbp, "\n\n/origstate save def\n");
    fprintf(tbp, "20 dict begin\n");	/* for local procedures */
    if (orientation == ORI_LDS) {
	/* translate and rotate the coordinate system */
	fprintf(tbp, "%d 0 translate\n90 rotate", (int)page_formats[papertype][0]);
    }

    return;
}

/* same thing for eps */
void eps_start(HIMAGE anImage, double aspect, FILE *tbp)
{
    time_t 	theTime;
    long	w, h;

    myDebug("Writing eps header");
    /* first the comments */
    theTime = time(0);
    fprintf(tbp, "%%!PS-Adobe-3.0 EPSF-3.0\n"); /* this comment is compulsery, the rest is mostly makeup */
    fprintf(tbp, "%%%%Creator: %s-%s by CSIRO-MIS\n", appName, patchlevel);
    fprintf(tbp, "%%%%Title: %s EPS file\n", appName);
    fprintf(tbp, "%%%%CreationDate: %s", ctime(&theTime));
    fprintf(tbp, "%%%%Pages:1\n");
    fprintf(tbp, "%%%%DocumentFonts:\n");
    fprintf(tbp, "%%%%Origin: 0 0\n");

    /* computes bounding box */
    w = anImage.nbcols;
    h = anImage.nbrows;

    if (aspect > 1.0) {
	w = (long)(w/aspect);
    } else {
	h = (long)(h*aspect);
    }
    fprintf(tbp, "%%%%BoundingBox: 0 0 %ld %ld\n", w, h);
    fprintf(tbp,
	    "%%%%EndComments\n"
	    "%%%%EndProlog\n");

    fprintf(tbp, "%%%%Page: 1 1\n");

    /* now the environnent */
    fprintf(tbp, "\n\n/origstate save def\n");
    fprintf(tbp, "20 dict begin\n");	/* for local procedures */
    fprintf(tbp, "0 0 translate\n");

    return;
}

void ps_end( FILE *tbp)
{
    fprintf(tbp, "\n\nshowpage\n"); /* otherwise it won't print... */
    fprintf(tbp, "\nend\n");	/* of the dictionary */
    fprintf(tbp, "origstate restore\n"); /* get back to previous state */
    
    fprintf(tbp, "%%%%Trailer\n");
    myDebug("Finished writing ps footer");
    return;
}

/* same thing for eps */
void eps_end( FILE *tbp)
{
    fprintf(tbp, "\n\nshowpage\n"); /* In theory we shouldn't need that, */
				/* in fact, we do. Some programs need */
				/* it. For example xv... */

    fprintf(tbp, "\nend\n");	/* of the dictionary */
    fprintf(tbp, "origstate restore\n"); /* get back to previous state */
    fprintf(tbp, "%%%%Trailer\n");
    myDebug("Finished writing eps footer");
    return;
}

/* This function finds its place for each image,
   and then call the postscript generator for each of those
   That sounds complex, but is it ?
 */
void ps_arrange_images(HIMAGE *theImageList,
		       int nbIm,
		       double aspectRatio,
		       float boundOx,
		       float boundOy,
		       float boundW,
		       float boundH,
		       int nbAcross,
		       int nbDown,
		       byte drawFrame,
		       FILE *tbp)
{
    int i, j, n;
    int	boxWidth, boxHeight;
    long maxRows, maxCols, aRow, aCol;
    float trueOx, trueOy, trueW, trueH;
    double scaleW, scaleH, theScale;
    BOX	subbox;

    myDebug("Arranging images on page");
    /* get the dimensions of the page */
    trueOx = boundOx + page_margins[currentMarginIndex][MARGIN_LEFT];
    trueOy = boundOy + page_margins[currentMarginIndex][MARGIN_BOTTOM];
    trueW = boundW-boundOx - \
	page_margins[currentMarginIndex][MARGIN_LEFT] - \
	page_margins[currentMarginIndex][MARGIN_RIGHT];
    trueH = boundH-boundOy - \
	page_margins[currentMarginIndex][MARGIN_BOTTOM] - \
	page_margins[currentMarginIndex][MARGIN_TOP];

    /* find the "minimum enclosing rectangle" for all images */
    maxRows = maxCols = 0;
    for (i = 0 ; i < nbIm ; i++) {
	aRow = theImageList[i].nbrows;
	aCol = theImageList[i].nbcols;
	if (aRow > maxRows)
	    maxRows = aRow;
	if (aCol > maxCols)
	    maxCols = aCol;
    }

    if ((nbAcross > 0) && (nbDown > 0) && ((nbAcross * nbDown) < nbIm)) {
	sprintf(errMsg, "The arrangement supplied does not make sense (only %d slots for %d images).\n\
We shall supply our own arrangement", nbAcross*nbDown, nbIm);
	myDebug(errMsg);
	nbAcross = nbDown = 0;
    }

    arrange_page(nbIm,trueW, trueH, maxRows, maxCols, aspectRatio, &nbAcross, &nbDown);

    sprintf(errMsg, "Arrangement found : %d image(s) across, %d image(s) down", nbAcross, nbDown);
    myDebug(errMsg);

    boxWidth = (int)((trueW - (nbAcross+1)*page_margins[currentMarginIndex][MARGIN_BETWEEN_X]) / nbAcross);
    boxHeight = (int)((trueH - (nbDown +1)*page_margins[currentMarginIndex][MARGIN_BETWEEN_Y]) / nbDown);

    /* find the scale (all images are rescaled to the same dimension )*/
    scaleW = (double) boxWidth / maxCols;
    scaleH = (double) boxHeight / maxRows;
    theScale = MYMIN(scaleW, scaleH);
    
    subbox.wide = boxWidth;
    subbox.high = boxHeight;
    /* print image per image */
    for (j = 0, n = 0 ; j < nbDown ; j++) {
	for (i = 0; i < nbAcross ; i++, n++) {
	    if (n >= nbIm)
		break;		/* everything has been printed...*/
            subbox.xmin = (int)(trueOx + (float)(i+1)*page_margins[currentMarginIndex][MARGIN_BETWEEN_X] + (float)i*boxWidth);
	    subbox.xmax = (int)(subbox.xmin + boxWidth - 1);
	    subbox.ymax = (int)(trueOy + trueH - (float)(j+1) * page_margins[currentMarginIndex][MARGIN_BETWEEN_Y] - (float)j * boxHeight);
	    subbox.ymin = (int)(subbox.ymax - boxHeight +1);
	    sprintf(errMsg,"Printing image %d", n);
	    myDebug(errMsg);
	    ps_printAnImage(theImageList[n], &subbox, theScale, aspectRatio, drawFrame, tbp);
	}
    }
    
    return;
}

void arrange_page(int n, float trueW, float trueH, long maxRows, long maxCols, double aspectRatio,
		  int *nacross, int *ndown)
{
    double 	ratio, across, down;
    int		na, nd;

    /* computed in any case */
    ratio = ((double)maxRows * trueW * aspectRatio) /  ((double)maxCols * trueH);

    if (*nacross != 0) {
	na = *nacross;
	if (*ndown == 0) {
	    nd = n / na;
	    if (nd * na < n)
		nd++; /* once is enough */
	} else {
	    nd = *ndown;
	}
    } else if (*ndown != 0) {
	nd = *ndown;
	na = n / nd;	/* no need to test again if *nacross == 0 */
	if (na * nd < n)
	    na++; /* once is enough*/
    } else {
	across = sqrt(n*ratio);
	down = across/ratio;
	na = (int)floor(across);
	nd = (int)floor(down);
    }
    
    if (na <= 0) {
	na = 1;
	nd = n ;
    } else if (nd <= 0) {
	nd = 1;
	na = n;
    } else if (ratio <= 1.0) {
	while (na * nd < n) {
	    if ((++nd) * na < n) {
		nd--;
		++na;
	    }
	}
    } else {
	while (na * nd < n) {
	    if ((++na) * nd < n) {
		na--;
		nd++;
	    }
	}
    }

    *nacross = na;
    *ndown = nd;
    
    return;
}

/* this procedure saves an image under the EPS format */
void eps_save(HIMAGE theImage, double aspectRatio, FILE *tbp)
{
    int		i, j, k;
    byte	theByte, imMin, imMax;
    byte	doAnamorphosis;
    long	nbw, nbBytes;
    long   	imW, imH, bufferLength;
    PIX_TYPE	*p, *r, *g, *b;

    /* we can change that anytime */
    doAnamorphosis = NO;
    
    /* get the dimensions of the picture */
    imH = theImage.nbrows;
    imW = theImage.nbcols;
        /* computes bounding box */
    if (aspectRatio > 1.0) {
	imW = (int)(imW/aspectRatio);
    } else {
	imH = (int)(imH*aspectRatio);
    }
    /* set up a string to hold up the information */
    bufferLength = MYMIN(PLINE_LENGTH, theImage.nbcols);
    fprintf(tbp, "%%%% new image\n");
    fprintf(tbp, "/picstr %ld string def\n", (long)PLINE_LENGTH);
     /* set the right scale */
    fprintf(tbp, "%ld %ld scale\n", imW, imH);
    /* Now it depends on # of colors and byte per pixels */
    
    if (theImage.type == PS_BINARY) {
	myDebug("Saving binary image to EPS");
	/* set up the arguments of the postscript image operator */
	fprintf(tbp, "%ld %ld %d\n[ %ld 0 0 -%ld 0 %ld ]",
		theImage.nbcols, theImage.nbrows, 1,
		theImage.nbcols, theImage.nbrows, theImage.nbrows);

	/* we are not considering RGB binaries */
	/* writing the small bit of program that will read the data */
	fprintf(tbp, "{currentfile\npicstr readhexstring pop}\nimage\n");
	/* printing the data now */
	p = theImage.buf0;
	for (j = 0, nbw = 0 ; j < theImage.nbrows ; j++ ) {
	    for (i = 0 ; i < theImage.nbcols ; i += 8, nbw++) {
		for (k = 0, theByte = 0x00; ((k < 8) && (i+k < theImage.nbcols))  ; k++,p++) {
		    theByte <<= 1;
		    theByte |= ((!*p) & 0x01);
		}
		/* make up the rest of the byte */
		for (; k < 8 ; k++) {
		    theByte <<= 1;
		}
		/* print it */
		fprintf(tbp, "%2.2x", theByte);
		if ((nbw % PLINE_LENGTH) == PLINE_LENGTH-1)
		    fprintf(tbp, "\n");
	    }
	}
	if (nbw % PLINE_LENGTH != 0) {
	    /* complete the scanline */
	    for (i = nbw % PLINE_LENGTH ; i < PLINE_LENGTH ; i++)
		fprintf(tbp, "%2.2x", 0);
	}
    } else if (theImage.nbcomp == 1) {
	myDebug("Saving gray-level image to EPS");
	/* standard Gray-level image */
	getMinMax(theImage.buf0, theImage.nbrows*theImage.nbcols, &imMin, &imMax);
	/* set up the arguments of the postscript image operator */
	fprintf(tbp, "%ld %ld %d\n[ %ld 0 0 -%ld 0 %ld ]\n",
		theImage.nbcols, theImage.nbrows, 8,
		theImage.nbcols, theImage.nbrows, theImage.nbrows);
	/* writing the small bit of program that will read the data */
	fprintf(tbp, "{currentfile picstr readhexstring pop}\nimage\n");
	/* printing the data now */
	p = theImage.buf0;
	nbBytes = theImage.nbrows*theImage.nbcols;
	if (!doAnamorphosis || (imMax-imMin == 0)) {
	    for (j = 0 ; j < nbBytes ; j++, p++) {
		fprintf(tbp, "%2.2x", *p);
		if ((j % PLINE_LENGTH) == PLINE_LENGTH-1)
		    fprintf(tbp, "\n");
	    }
	    if (j % PLINE_LENGTH != 0) {
		/* complete the scanline */
		for (i = j % PLINE_LENGTH ; i < PLINE_LENGTH ; i++)
		    fprintf(tbp, "%2.2x", 0);
	    }
	} else { /* gray-level anamorphosis */
	    for (j = 0 ; j < nbBytes ; j++, p++) {
		fprintf(tbp, "%2.2x", (int)((*p-imMin)*((double)PIX_TYPE_MAX/(double)(imMax-imMin))));
		if ((j % PLINE_LENGTH) == PLINE_LENGTH-1)
		    fprintf(tbp, "\n");
	    }
	    if (j % PLINE_LENGTH != 0) {
		/* complete the scanline */
		for (i = j % PLINE_LENGTH ; i < PLINE_LENGTH ; i++)
		    fprintf(tbp, "%2.2x", 0);
	    }
	}
    } else if (theImage.nbcomp == 3) {
	myDebug("saving RGB image to EPS");
	/* RGB image */
	/* set up the arguments for the ColorImage operator */
	fprintf(tbp, "%ld %ld %d\n[ %ld 0 0 -%ld 0 %ld ]",
		theImage.nbcols, theImage.nbrows, 8,
		theImage.nbcols, theImage.nbrows, theImage.nbrows);
	/* writing the small bit of program that will read the data */
	fprintf(tbp, "{currentfile\npicstr readhexstring pop}\nfalse 3 colorimage\n");
	/* the data will be written in interleaved format (RGBRGBRGB...) */
	r = theImage.buf0; g = theImage.buf1; b = theImage.buf2;
	nbBytes = theImage.nbrows*theImage.nbcols;
	for (j = 0 ; j < nbBytes ; j++, r++, g++, b++) {
	    fprintf(tbp, "%2.2x%2.2x%2.2x", *r, *g, *b);
	    if ((j % (PLINE_LENGTH/3)) == PLINE_LENGTH/3-1)
		fprintf(tbp, "\n");
	}
	/* complete the scanline */
	if (j % (PLINE_LENGTH/3) != 0) {
	    for (i = j % (PLINE_LENGTH/3) ; i < (PLINE_LENGTH/3) ; i++)
		fprintf(tbp, "%2.2x%2.2x%2.2x", 0, 0, 0);
	}
    }
    
    return;
}

/* this procedure prints an image placed within a box */
/* the box itself is not drawn */
void ps_printAnImage(HIMAGE theImage, BOX *subbox, double theScale, double aspectRatio, byte drawFrame, FILE *tbp)
{
    int		i, j, k;
    byte	theByte, imMin, imMax;
    byte	doAnamorphosis;
    long    	woff, hoff, nbBytes, nbw;
    long   	imW, imH, bufferLength;
    double 	scaledW, scaledH;
    PIX_TYPE	*p, *r, *g, *b;
    

    /* can be changed anytime */
    doAnamorphosis = NO;
    
    myDebug("Actual printing of the image");
    /* first save the graphic state yet again */
    fprintf(tbp, "\ngsave\n");

    /* get the dimensions of the picture */
    imH = theImage.nbrows;
    imW = theImage.nbcols;
    scaledW = imW * theScale;	/* now in points */
    scaledH = imH * theScale;
    if (aspectRatio > 1.0) {
	scaledW /= aspectRatio;
    } else {
	scaledH *= aspectRatio;
    }
    woff = (int)((subbox->wide - scaledW)/2);
    hoff = (int)((subbox->high - scaledH)/2);

    
    
    /* set up a string to hold up the information */
    bufferLength = MYMIN(PLINE_LENGTH, theImage.nbcols);
    fprintf(tbp, "%%%% printing new image\n");
    fprintf(tbp, "/picstr %ld string def\n", (long)PLINE_LENGTH);
    /* move to the right point (centering the picture) */
    fprintf(tbp, "%d %d translate\n", (int)(subbox->xmin+woff), (int)(subbox->ymin+hoff));
    /* set the right scale */
    fprintf(tbp, "%d %d scale\n", (int)(scaledW), (int)(scaledH)); 
    
    /* Now it depends on # of colors and byte per pixels */
    if (theImage.type == PS_BINARY) {
	myDebug("Image printed as binary");
	/* set up the arguments of the postscript image operator */
	fprintf(tbp, "%ld %ld %d\n[ %ld 0 0 -%ld 0 %ld ]",
		theImage.nbcols, theImage.nbrows, 1,
		theImage.nbcols, theImage.nbrows, theImage.nbrows);

	/* we are not considering RGB binaries */
	/* writing the small bit of program that will read the data */
	fprintf(tbp, "{currentfile\npicstr readhexstring pop}\nimage\n");
	/* printing the data now */
	p = theImage.buf0;
	for (j = 0, nbw = 0 ; j < theImage.nbrows ; j++ ) {
	    for (i = 0 ; i < theImage.nbcols ; i += 8, nbw++) {
		for (k = 0, theByte = 0x00; ((k < 8) && (i+k < theImage.nbcols))  ; k++,p++) {
		    theByte <<= 1;
		    theByte |= ((!*p) & 0x01);
		}
		/* make up the rest of the byte */
		for (; k < 8 ; k++) {
		    theByte <<= 1;
		}
		/* print it */
		fprintf(tbp, "%2.2x", theByte);
		if ((nbw % PLINE_LENGTH) == PLINE_LENGTH-1)
		    fprintf(tbp, "\n");
	    }
	}
	if (nbw % PLINE_LENGTH != 0) {
	    /* complete the scanline */
	    for (i = nbw % PLINE_LENGTH ; i < PLINE_LENGTH ; i++)
		fprintf(tbp, "%2.2x", 0);
	}
    } else if (theImage.nbcomp == 1) {
	myDebug("Image printed as gray-level");
	/* standard Gray-level image */
	getMinMax(theImage.buf0, theImage.nbrows*theImage.nbcols, &imMin, &imMax);
	/* set up the arguments of the postscript image operator */
	fprintf(tbp, "%ld %ld %d\n[ %ld 0 0 -%ld 0 %ld ]\n",
		theImage.nbcols, theImage.nbrows, 8,
		theImage.nbcols, theImage.nbrows, theImage.nbrows);
	/* writing the small bit of program that will read the data */
	fprintf(tbp, "{currentfile picstr readhexstring pop}\nimage\n");
	/* printing the data now */
	p = theImage.buf0;
	nbBytes = theImage.nbrows*theImage.nbcols;
	if (!doAnamorphosis || (imMax-imMin == 0)) {
	    for (j = 0 ; j < nbBytes ; j++, p++) {
		fprintf(tbp, "%2.2x", *p);
		if ((j % PLINE_LENGTH) == PLINE_LENGTH-1)
		    fprintf(tbp, "\n");
	    }
	    if (j % PLINE_LENGTH != 0) {
		/* complete the scanline */
		for (i = j % PLINE_LENGTH ; i < PLINE_LENGTH ; i++)
		    fprintf(tbp, "%2.2x", 0);
	    }
	} else { /* gray-level anamorphosis */
	    for (j = 0 ; j < nbBytes ; j++, p++) {
		fprintf(tbp, "%2.2x", (int)((*p-imMin)*((double)PIX_TYPE_MAX/(double)(imMax-imMin))));
		if ((j % PLINE_LENGTH) == PLINE_LENGTH-1)
		    fprintf(tbp, "\n");
	    }
	    if (j % PLINE_LENGTH != 0) {
		/* complete the scanline */
		for (i = j % PLINE_LENGTH ; i < PLINE_LENGTH ; i++)
		    fprintf(tbp, "%2.2x", 0);
	    }
	}
    } else if (theImage.nbcomp == 3) {
	myDebug("image printed as RGB image");
	/* RGB image */
	/* set up the arguments for the ColorImage operator */
	fprintf(tbp, "%ld %ld %d\n[ %ld 0 0 -%ld 0 %ld ]",
		theImage.nbcols, theImage.nbrows, 8,
		theImage.nbcols, theImage.nbrows, theImage.nbrows);
	/* writing the small bit of program that will read the data */
	fprintf(tbp, "{currentfile\npicstr readhexstring pop}\nfalse 3 colorimage\n");
	/* the data will be written in interleaved format (RGBRGBRGB...) */
	r = theImage.buf0; g = theImage.buf1; b = theImage.buf2;
	nbBytes = theImage.nbrows*theImage.nbcols;
	for (j = 0 ; j < nbBytes ; j++, r++, g++, b++) {
	    fprintf(tbp, "%2.2x%2.2x%2.2x", *r, *g, *b);
	    if ((j % (PLINE_LENGTH/3)) == PLINE_LENGTH/3-1)
		fprintf(tbp, "\n");
	}
	/* complete the scanline */
	if (j % (PLINE_LENGTH/3) != 0) {
	    for (i = j % (PLINE_LENGTH/3) ; i < (PLINE_LENGTH/3) ; i++)
		fprintf(tbp, "%2.2x%2.2x%2.2x", 0, 0, 0);
	}
    }

    /* if asked for, draw a very thin small frame around the image */
    if (drawFrame) {
	myDebug("Small frame drawn around image");
	fprintf(tbp, "\n\n%%%% frame around image\n");
	fprintf(tbp, "0 setlinewidth\n");
	fprintf(tbp, "0 setgray\n");
	fprintf(tbp, "newpath\n");
	fprintf(tbp, "0 0 moveto\n");
	fprintf(tbp, "1 0 lineto\n");
	fprintf(tbp, "1 1 lineto\n");
	fprintf(tbp, "0 1 lineto\n");
	fprintf(tbp, "closepath\n");
	fprintf(tbp, "stroke\n");
    }
    
    /* last restore the graphic state yet again */
    fprintf(tbp, "\n\ngrestore\n%%%%\n\n");    
    
    return ;
}

/* get min and max of image */
void getMinMax(PIX_TYPE *buf, long nbpix, PIX_TYPE *theMin, PIX_TYPE *theMax)
{
    PIX_TYPE *p, *end;

    p = buf;
    end = buf + nbpix;
    *theMin = PIX_TYPE_MAX;
    *theMax = PIX_TYPE_MIN;

    while (p != end) { 
	if (*p > *theMax)
	    *theMax = *p;

	if (*p < *theMin)
	    *theMin = *p;

	p++;
    }
    
    return;
}

void ps_printTitle(float boundOx, float boundOy, float boundW, float boundH, char *theTitle, char *fontName, FILE *tbp)
{
    int startXpoint, startYpoint, startsize, minsize;
    int titleBoxHeight;
    int lengthlimit;

    startXpoint = (int)(boundOx + page_margins[currentMarginIndex][MARGIN_RIGHT]);
    startYpoint = (int)(boundOy + page_margins[currentMarginIndex][MARGIN_TITLE]);
    titleBoxHeight = (int)(page_margins[currentMarginIndex][MARGIN_BOTTOM] - page_margins[currentMarginIndex][MARGIN_TITLE]);
    
    if (titleBoxHeight > 0) {
	// then do print
	startsize = (int)(MYMIN(TEXT_START_SIZE, titleBoxHeight));
	minsize = (int)(TEXT_MIN_SIZE);

	lengthlimit = (int)(boundW -					\
			    page_margins[currentMarginIndex][MARGIN_LEFT] - \
			    startXpoint);

	sprintf(errMsg, 
		"box: (boundOx=%g, boundOy=%g, boundW=%g, boundH=%g);\n"
		"startXpoint=%d, startYpoint= %d\n",
		boundOx, boundOy, boundW, boundH, startXpoint, startYpoint);
	myDebug(errMsg);	
    
	/* start the small ps program */
	/* variables first */
	myDebug("Writing title");
	fprintf(tbp, "\n%%%% Small ps program to write title at bottom of page\n");
	fprintf(tbp, "/startpoint {%d %d} def\n", startXpoint, startYpoint);
	fprintf(tbp, "/startsize %d def\n", startsize);
	fprintf(tbp, "/minsize %d def\n", minsize);
	fprintf(tbp, "/myfont {/%s} def\n", fontName);
	fprintf(tbp, "/lengthlimit %d def\n", lengthlimit);
	fprintf(tbp, "/mytext (%s) def\n\n", theTitle);

	/* the program itself now */
	fprintf(tbp, "myfont findfont startsize scalefont setfont\n"); /* font selection */
	fprintf(tbp, "startsize -1 minsize {\n"); /* start of the for loop */
	fprintf(tbp, "    /newsize exch def\n");  /* get the new font size from the loop */
	fprintf(tbp, "    /textlength mytext stringwidth pop def\n"); /* compute the length of the title */
	fprintf(tbp, "    textlength lengthlimit le {\n"); /* verifies if it is shorter than the available space */
	fprintf(tbp, "        exit\n");	/* if yes, we have found the right size for the font */
	fprintf(tbp, "    } {\n");		/* that's an else */
	fprintf(tbp, "        myfont newsize selectfont\n"); /* selects the new font size (smaller) as current size */
	fprintf(tbp, "    } ifelse\n"); 	/* there is the logical operator */
	fprintf(tbp, "} for\n\n");	/* and the whole thing is a loop, from biggest size to smallest, we chose the best */
	fprintf(tbp, "lengthlimit textlength sub 2 div\n"); /* now we will center the sentence */
	fprintf(tbp, "startpoint pop add\n"); /* some calculation */
	fprintf(tbp, "startpoint exch pop\n"); /* some more trivial stuff */
	fprintf(tbp, "moveto\n");	/* it's making its move now... */
	fprintf(tbp, "mytext show"); /* lo and behold, the text appears... */
    } else {
	myDebug("Title not printed because margins are too tight\n");
    }
    /* and this is it */
    return;
}

/* this function draws a black to white strip (+ R, G, B, C, Y, M blocks) */
/* serves for comparisons and checking */
void ps_drawRibbon(float boundOx, float boundOy, float boundW, float boundH, FILE *tbp)
{
    int height, width, realwidth, pagewidth;
    int swatchwidth;
    int xpos, ypos;
    int	i;

    myDebug("printing colour-test ribbon");
    /* print ribbon in right-hand margin */
    pagewidth = (int)(boundW - boundOx - \
		      page_margins[currentMarginIndex][MARGIN_RIGHT] - \
		      page_margins[currentMarginIndex][MARGIN_LEFT]);
    width = pagewidth/3;
    swatchwidth = width/6;
    height = (int)(page_margins[currentMarginIndex][MARGIN_TOP]/3 + 0.5);
    realwidth = 2*width;
    height = (int)(page_margins[currentMarginIndex][MARGIN_TOP]/3 + 0.5);
    xpos = (int)((pagewidth-realwidth)/2 + \
		 page_margins[currentMarginIndex][MARGIN_RIGHT] \
		 + boundOx);
    ypos = (int)(boundH - \
		 page_margins[currentMarginIndex][MARGIN_TOP] \
		 + 1);

    if (ypos < boundOy) {
	sprintf(errMsg, "** Not enough room on page **");
	myDebug(errMsg);
    }


    /* variable */
    fprintf(tbp, "\n\n%%%% Optionnal ribbon\n");
    fprintf(tbp, "/nbgray 256 def\n");		/* that's a constant.. */
    fprintf(tbp, "/wherex %d def\n", xpos);
    fprintf(tbp, "/wherey %d def\n", ypos);
    fprintf(tbp, "/striplength %d def\n", width);
    fprintf(tbp, "/stripheight %d def\n", height);
    fprintf(tbp, "/swatchwidth %d def\n", swatchwidth);
    fprintf(tbp, "/huelength striplength nbgray div def\n");
    fprintf(tbp, "/grayinc 1 nbgray div def\n");
    fprintf(tbp, "/tstriplength striplength swatchwidth add def\n");
    fprintf(tbp, "\ngsave\n"); /* start of the important stuff */
    fprintf(tbp, "wherex wherey translate\n");
    fprintf(tbp, "1 1 nbgray {\n");
    fprintf(tbp, "    /index exch def\n");
    fprintf(tbp, "    newpath\n");
    fprintf(tbp, "    0 0 moveto\n");
    fprintf(tbp, "    huelength 0 lineto\n");
    fprintf(tbp, "    huelength stripheight lineto\n");
    fprintf(tbp, "    0 stripheight lineto\n");
    fprintf(tbp, "    closepath\n");
    fprintf(tbp, "    fill\n");
    fprintf(tbp, "    huelength 0 translate\n");
    fprintf(tbp, "    index grayinc mul setgray\n");
    fprintf(tbp, "} for\n\n");
    fprintf(tbp, "%%%% hairline around the ribbon\n");
    fprintf(tbp, "0 striplength sub 0 translate\n");
    fprintf(tbp, "newpath\n");
    fprintf(tbp, "0 0 moveto\n");
    fprintf(tbp, "striplength 0 lineto\n");
    fprintf(tbp, "striplength stripheight lineto\n");
    fprintf(tbp, "0 stripheight lineto\n");
    fprintf(tbp, "closepath\n");
    fprintf(tbp, "0 setgray\n");
    fprintf(tbp, "0 setlinewidth\n");
    fprintf(tbp, "stroke\n");
    fprintf(tbp, "\n%%%% color swatches\n");
    fprintf(tbp, "striplength  0 translate\n");
    for (i = 1 ; i < 7 ; i++) {
	fprintf(tbp, "newpath\n");
	fprintf(tbp, "0 0 moveto\n");
	fprintf(tbp, "swatchwidth 0 lineto\n");
	fprintf(tbp, "swatchwidth stripheight lineto\n");
	fprintf(tbp, "0 stripheight lineto\n");
	fprintf(tbp, "closepath\n");
	fprintf(tbp, "%d %d %d setrgbcolor\n", (i >> 2 ) & 1, (i >> 1) & 1, i &1);
	fprintf(tbp, "fill\n");
	fprintf(tbp, "swatchwidth  0 translate\n");
    }

    fprintf(tbp, "grestore\n");
    fprintf(tbp, "%%%% end of ribbon\n\n");
    return ;
}

void myDebug(char *string)
{
    char myMess[300];
    strcpy(myMess, "    ");
    strcat(myMess, string);
    strcat(myMess, "\n");
    dbgprintf(myMess);
}


