/* +-------------------------------------------------------------------+ */
/* | Copyright 1993, David Koblas (koblas@netcom.com)		       | */
/* |								       | */
/* | Permission to use, copy, modify, and to distribute this software  | */
/* | and its documentation for any purpose is hereby granted without   | */
/* | fee, provided that the above copyright notice appear in all       | */
/* | copies and that both that copyright notice and this permission    | */
/* | notice appear in supporting documentation.	 There is no	       | */
/* | representations about the suitability of this software for	       | */
/* | any purpose.  this software is provided "as is" without express   | */
/* | or implied warranty.					       | */
/* |								       | */
/* +-------------------------------------------------------------------+ */

/* $Id: writePS.c,v 1.5 2000/10/02 20:53:00 torsten Exp $ */

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include "image.h"
#include "libpnmrw.h"

#if defined(sco) || defined(__CYGWIN__)
#include <time.h>
#else
#include <time.h>
#include <sys/time.h>
#endif
#include <string.h>

typedef struct {
    float wpercent, hpercent;
    int wbbox, hbbox, width, height, rx, ry, wsubdiv, hsubdiv, orient;
} PageInfo;

extern void * xmalloc(size_t n);
extern FILE * openTemp(char **np);
extern void removeTemp(void);

#ifndef TRUE
#define TRUE	1
#define FALSE	0
#endif

/*
static void writePreview(FILE * fd, Image * image)
{
}
*/

#if 0
static void writeCmap(FILE * fd, Image * image)
{
    int runlen, maxCount;
    int col, used;
    int i, x, y;
    unsigned char *ucp;
    char buf[280];
    short map[256];
    int isRLE;

    /*
    **	Remap the colors to the number used
     */
    for (i = 0; i < 256; i++)
	map[i] = -1;
    for (ucp = image->data, y = 0; y < image->width * image->height; y++, ucp++)
	map[*ucp] = 1;
    for (used = i = 0; i < 256; i++)
	if (map[i] != -1)
	    map[i] = used++;

    /*
    **	If compression will help
     */
    fprintf(fd, "/rgbmap %d string def\n", used * 3);
    fputs("% RLE drawing routine\n\
/buffer 2 string def							\n\
/rgb (000) def								\n\
/drawcolormappedimage {							\n\
  /buffer  1   string def						\n\
  /rgbval  3   string def						\n\
  /npixels 384 string def	% 128 * 3				\n\
  { currentfile buffer readhexstring pop  % read run information	\n\
    /len exch 0 get store						\n\
    len 128 ge								\n\
    { 0 1 len 128 sub	 % this is a run of raw data			\n\
      { currentfile buffer readhexstring pop pop			\n\
	/rgbval rgbmap buffer 0 get 3 mul 3 getinterval store		\n\
	npixels exch 3 mul rgbval putinterval				\n\
      } for								\n\
      npixels 0 len 127 sub 3 mul getinterval				\n\
    }									\n\
    { % else it's run data						\n\
      currentfile buffer readhexstring pop pop				\n\
      /rgbval rgbmap buffer 0 get 3 mul 3 getinterval store		\n\
      0 1 len { npixels exch 3 mul rgval putinterval } for		\n\
      npixels 0 len 1 add 3 mul getinterval				\n\
    } ifelse								\n\
  }									\n\
  false 3 colorimage							\n\
} bind def\n", fd);

    fprintf(fd, "%%%% get the rgb map\n");
    fprintf(fd, "currentfile rgbmap readhexstring\n");

    ucp = image->cmapData;
    for (col = i = 0; i < image->cmapSize; i++, ucp += 3) {
	if (map[i] == -1)
	    continue;

	fprintf(fd, "%02.2x%02.2x%02.2x ", ucp[0], ucp[1], ucp[2]);
	if (++col == 10) {
	    putc('\n', fd);
	    col = 0;
	}
    }
    if (col != 0)
	putc('\n', fd);
    fprintf(fd, "pop pop\n\n");

    fprintf(fd, "%d %d 8\n", image->width, image->height);
    fprintf(fd, "[ %d 0 0 -%d 0 %d ]\n",
	    image->width, image->height, image->height);
    fprintf(fd, "drawcolormappedimage\n");

    ucp = image->data;
    maxCount = 0x7f;

#define PUT(fd, val) do {			\
	fprintf(fd, "%02.2x", val);		\
	if (++col == 35)			\
		{ col = 0; putc('\n', fd); }	\
	} while (0)

#define	WRITE(fd, flg, buf, len) do { int _i;			\
	PUT(fd, (len) + (flg ? 0 : 128));			\
	if (flg)						\
		PUT(fd, map[buf[0]]);				\
	else							\
		for (_i = 0; _i < (len); _i++)			\
			PUT(fd, map[buf[_i]]);			\
	putc(' ', fd);						\
	} while (0)

    col = 0;
    for (y = 0; y < image->height; y++) {
	int prev;

	runlen = 0;
	for (x = 0; x < image->width; x++, ucp++) {
	    int v = *ucp;

	    if (runlen == 0)
		isRLE = TRUE;
	    else if (runlen == maxCount) {
		WRITE(fd, isRLE, buf, runlen);
		runlen = 0;
		isRLE = TRUE;
	    } else if (isRLE && v != prev) {
		if (runlen == 1)
		    isRLE = FALSE;
		else {
		    WRITE(fd, isRLE, buf, runlen);
		    runlen = 0;
		}
	    } else if (!isRLE && v == prev) {
		WRITE(fd, isRLE, buf, runlen);
		buf[0] = prev;
		runlen = 1;
		isRLE = TRUE;
	    }
	    buf[runlen++] = v;
	    prev = v;
	}

	if (runlen != 0)
	    WRITE(fd, isRLE, buf, runlen);
	col = 0;
	putc('\n', fd);
    }
    putc('\n', fd);
#undef WRITE
#undef PUT
}
#endif

static void writeRGB(FILE * fd, Image * image, PageInfo *pinfo)
{
    unsigned char *ucp, *vcp, *wcp, *tcp;
    unsigned char r, g, b, rp, gp, bp, wp, hp, i, j;
    int x, y, xp, yp;
    int col = 0;

    wp = (unsigned char) pinfo->wsubdiv;
    hp = (unsigned char) pinfo->hsubdiv;
    x = image->width * pinfo->wsubdiv;
    y = image->height * pinfo->hsubdiv;

    fprintf(fd, "/line %d string def\n", x * 3);
    fprintf(fd, "%d %d 8\n", x, y);
    fprintf(fd, "[ %d 0 0 -%d 0 %d ]\n", x, y, y);
    fprintf(fd, "{currentfile line readhexstring pop}\n");
    fprintf(fd, "false 3 colorimage\n");

    if (wp == 1 && hp == 1)
    for (y = 0; y < image->height; y++)
	for (x = 0; x < image->width; x++) {
	    ucp = ImagePixel(image, x, y);
	    fprintf(fd, "%02x%02x%02x", ucp[0], ucp[1], ucp[2]);
	    if (++col == 12) {
		putc('\n', fd);
		col = 0;
	    }
	}
    else
    for (y = 0; y < image->height; y++) {
	if (y+1 < image->height) yp = y+1; else yp = y;
        for (j = 0; j < hp; j++)
	for (x = 0; x < image->width; x++) {
	    ucp = ImagePixel(image, x, y);
	    if (x+1 < image->width) xp = x+1; else xp = x;
	    vcp = ImagePixel(image, xp, y);
	    if (j==0) {
                r = ucp[0];
		g = ucp[1];
		b = ucp[2];
		rp = vcp[0];
		gp = vcp[1];
		bp = vcp[2];
	    } else {
	        wcp = ImagePixel(image, x, yp);
	        tcp = ImagePixel(image, xp, yp);
                r = ((hp-j)*ucp[0] + j*wcp[0])/hp;
                g = ((hp-j)*ucp[1] + j*wcp[1])/hp;
                b = ((hp-j)*ucp[2] + j*wcp[2])/hp;
                rp = ((hp-j)*vcp[0] + j*tcp[0])/hp;
                gp = ((hp-j)*vcp[1] + j*tcp[1])/hp;
                bp = ((hp-j)*vcp[2] + j*tcp[2])/hp;
	    }
            for (i = 0; i < wp; i++) {
                if (i==0)
	            fprintf(fd, "%02x%02x%02x", r, g, b);
		else
	            fprintf(fd, "%02x%02x%02x", 
			    ((wp-i)*r+i*rp)/wp, 
			    ((wp-i)*g+i*gp)/wp, 
			    ((wp-i)*b+i*bp)/wp);
	        if (++col == 12) {
		    putc('\n', fd);
		    col = 0;
	        }
	    }
	}
    }

    if (col != 0)
	putc('\n', fd);
}

int WriteResizedPS(char *file, Image * image, void *data)
{
    FILE *fd;
    PageInfo *pinfo = (PageInfo *)data;
    char *cp, buf[256];
    time_t t;

    if ((fd = fopen(file, "w")) == NULL)
	return 1;

    if ((cp = strrchr(file, '/')) == NULL)
	cp = file;
    else
	cp++;
    strcpy(buf, cp);
    if ((cp = strrchr(buf, '.')) != NULL)
	*cp = '\0';

    time(&t);
    fprintf(fd, "%%!PS-Adobe-2.0 EPSF-2.0\n");
    fprintf(fd, "%%%%Creator: XPaint\n%%%%Title: %s\n%%%%Pages: 1\n", buf);
    fprintf(fd, "%%%%BoundingBox: %d %d %d %d\n", 0, 0,
	    pinfo->wbbox, pinfo->hbbox);
    fprintf(fd, "%%%%CreationDate: %s", ctime(&t));
    fprintf(fd, "%%%%EndComments\n");

    /*
    **  Write a preview image
    */

    /* Void routine. Don't know what other people had in mind for this...
    writePreview(fd, image);
    */

    fprintf(fd, "%%%%EndProlog\n");
    fprintf(fd, "%%%%Page: 1 1\n");
    fprintf(fd, "\n\ngsave\n\n");

    /*
    **	Write the actual image
     */

    fprintf(fd, "/inch {72 mul} def\n");

    if (pinfo->rx || pinfo->ry)
        fprintf(fd, "%d %d translate\n", pinfo->rx, pinfo->ry);

    if (pinfo->orient)
        fprintf(fd, "%d %d translate  90 rotate\n",
	        (int)(0.01 * image->height * pinfo->hpercent), 0);

    fprintf(fd, "%g %g scale\n", 
       0.01 * image->width * pinfo->wpercent, 
       0.01 * image->height * pinfo->hpercent );

#if 0
    if (image->cmapSize == 0 || image->cmapSize > 256)
	writeRGB(fd, image, pinfo);
    else
	writeCmap(fd, image);
#else
    writeRGB(fd, image, pinfo);
#endif

    fprintf(fd, "%%\n\ngrestore\nshowpage\n%%%%Trailer\n");

    fclose(fd);

    return 0;
}

Image *
ReadPS(char *file)
{
    char *tmp;
    char buf[512];
    FILE *fp;
    int format;
    int wth, hth, w, h, pnm_type, bytes_per_pixel;
    int x, y, i, j;
    int top, bottom, left, right;
    int *lborder, *rborder;
    xel **els = NULL;
    xel *curel;
    xelval maxval;
    char filled, newchr;
    char *data, *dp;
    Image *image;

    fp = fopen(file, "rb");
    if (!fp) return NULL;
    fclose(fp);

    fp = openTemp(&tmp);
    if (!fp) return NULL;
    fclose(fp);

    sprintf(buf, 
      "gs -g2000x2000 -sDEVICE=ppmraw -r72 -q -dNOPAUSE -sOutputFile=%s -- %s",
      tmp, file);

    system(buf);

    pnm_init2(tmp);
    if ((fp = fopen(tmp, "r")) == NULL) 
        return NULL;
    if ((els = pnm_readpnm(fp, &wth, &hth, &maxval, &format)) == NULL)
        return NULL;
    pnm_type = PNM_FORMAT_TYPE(format);
    fclose(fp);
    removeTemp();

    if (pnm_type == PPM_TYPE)
        bytes_per_pixel = 3;
    else
        bytes_per_pixel = 1;

    data = (char *)xmalloc(bytes_per_pixel*wth*hth);
    lborder = (int *)xmalloc(hth*sizeof(int));
    rborder = (int *)xmalloc(hth*sizeof(int));
    if (!data || !lborder || !rborder) return NULL;

    dp = data;

    for (y = 0; y < hth; y++) {
        lborder[y] = 0;
	rborder[y] = -1;
	for (curel = els[y], x = 0; x < wth; x++, curel++) {
	    if (pnm_type == PPM_TYPE) {
		newchr = *dp++ = 255 * PPM_GETR(*curel) / maxval;
		filled = 255-newchr;
		newchr = *dp++ = 255 * PPM_GETG(*curel) / maxval;
		filled |= 255-newchr;
		newchr = *dp++ = 255 * PPM_GETB(*curel) / maxval;
		filled |= 255-newchr;
	    } else if (pnm_type == PGM_TYPE) {
		newchr = *dp++ = 255 * PNM_GET1(*curel) / maxval;
		filled = 255-newchr;
	    } else {
		newchr = *dp++ = (PNM_GET1(*curel) != PBM_WHITE) ? 1 : 0;
		filled = 1-newchr;
	    }
	    if (filled)
	        rborder[y] = x;
	    else {
                if (lborder[y] == x) ++lborder[y];
	    }
	}
    }

    top = 0;
    bottom = -1;
    left = wth-1;
    right = 0;
    for (y = 0; y < hth; y++) {
        if (rborder[y] == -1) {
	     if (top == y) ++top;
	} else
	     bottom = y;
	if (lborder[y]<left) left = lborder[y];
	if (rborder[y]>right) right = rborder[y];
    }
    if (top>bottom || left>right) return NULL;
    w = right-left+1;
    h = bottom-top+1;

    if (pnm_type == PBM_TYPE)
	image = ImageNewBW(w, h);
    else if (pnm_type == PGM_TYPE)
	image = ImageNewGrey(w, h);
    else
	image = ImageNew(w, h);
    
    if (pnm_type == PPM_TYPE) {
        for (y = top; y <= bottom; y++)
	    for (x = left; x <= right; x++) {
                i = 3*(y*wth + x);
                j = 3*((y-top)*w + x-left);
	        memcpy(&image->data[j], &data[i], 3);
	    }
    } else {
        for (y = top; y <= bottom; y++)
	    for (x = left; x <= right; x++) {
                i = y*wth + x;
                j = (y-top)*w + x-left;
	        image->data[j] = data[i];        
	    }
    }

    free(data);
    free(lborder);
    free(rborder);
    pnm_freearray(els, hth);

    return image;
}

int WritePS(char *file, Image * image)
{
    PageInfo pinfo;

    pinfo.wbbox = image->width;
    pinfo.hbbox = image->height;
    pinfo.width = image->width;
    pinfo.height = image->height;
    pinfo.rx = 0;
    pinfo.ry = 0;
    pinfo.wsubdiv = 1;
    pinfo.hsubdiv = 1;
    pinfo.orient = 0;
    pinfo.wpercent = 100.0;
    pinfo.hpercent = 100.0;

    return
        WriteResizedPS(file, image, &pinfo);
}

int WritePDF(char *file, Image * image)
{
    char *tmp;
    char buf[512];
    FILE *fp;

    fp = openTemp(&tmp);
    if (!fp) return 1;
    fclose(fp);

    if (WritePS(tmp, image)) return 1;
    fp = fopen(tmp, "rb");
    if (!fp) return 1;
    fclose(fp);
    
    sprintf(buf, "gs -sDEVICE=pdfwrite -q -dNOPAUSE -sOutputFile=%s -- %s", 
            file, tmp);
    system(buf);
    removeTemp();
    return 0;
}

int
TestPS(char *file)
{
    char header[8];
    FILE *fp = fopen(file, "rb");   /* libpng requires ANSI; so do we */

    if (!fp)
        return 0;

    fread(header, 1, 8, fp);
    fclose(fp);

    return !strncasecmp(header, "%!PS", 4);
}

int
TestPDF(char *file)
{
    char header[8];
    FILE *fp = fopen(file, "rb");   /* libpng requires ANSI; so do we */

    if (!fp)
        return 0;

    fread(header, 1, 8, fp);
    fclose(fp);

    return !strncasecmp(header, "%PDF", 4);
}

