/*--------------------------------------------------------------------
 *    $Id: ps2raster.c,v 1.10 2007/10/10 14:11:17 remko Exp $
 *
 *    Copyright (c) 2006-2007 by J. Luis
 *    See README file for copying and redistribution conditions.
 *--------------------------------------------------------------------*/
/*
 *
 * ps2raster converts one or several PostScript file(s) to other formats using GhostScript.
 * It works by modifying the page size in order that the image will have a size
 * which is specified by the BoundingBox.
 * As an option, a tight BoundingBox may be computed.
 * ps2raster uses the ideas of the EPS2XXX.m from Primoz Cermelj published in MatLab Central
 * and of psbbox.sh of Remko Scharroo.
 *
 *
 *--------------------------------------------------------------------*/
/*
 * Authors:	Joaquim Luis and Remko Scharroo
 * Created:	15-FEB-2005
 * Modified:	15-NOV-2005 (It used to fail too many times in detecting if is GMT_ps)
 *		01-DEC-2005 Changed the generated script name to not overlap with -N
 *		27-DEC-2005 Integrated into GMT's misc supplement
 *		07-SEP-2006 Added EPS format; no more GMT specific; high-quality PDF output
 *		 1-APR-2007 Moved to GMT src directory
 *		 6-JUN-2007 Added -P option to force Portrait mode
 *		14-SEP-2007 Reduced bbox rendering from the default 4000 to 720 DPI. This
 *			    produces about 50% speedup for the -A option.
 */

#include "gmt.h"

int main (int argc, char **argv) {
	int i, j, k, len, w, h, r, xt, yt, x0 = 0, x1 = 612, y0 = 0, y1 = 828, dpi = 0, n_files = 0, n_alloc = 100;
	int error = FALSE, adjust_BB = FALSE, no_rem = FALSE, have_list = FALSE, do_eps = FALSE, do_portrait = FALSE;
	int atend, got_BB, got_orient, landscape, setup;

	char ps_file[BUFSIZ];
	char **ps_names;
	char line[BUFSIZ], c[BUFSIZ], c1[20], c2[20], c3[20], c4[20], c5[20], cmd[BUFSIZ];
	char *script_file = NULL, *tmp_file = NULL, *out_file = NULL, *BB_file = NULL;
	char *fullGsPath = NULL, *gs_params = NULL, *gs_BB = NULL;
	char *ext = ".jpg", *device = "jpeg";
	FILE *fp = NULL, *fpo = NULL, *fpb = NULL, *fpl = NULL;
	void run_cmd (char *name, char *cmd, int rem1, int rem2);

	/* Parameters for all the formats available */

	gs_params = "-q -dNOPAUSE -dBATCH -dUseFlateCompression=true -dPDFSETTINGS=/prepress -dEmbedAllFonts=true -dMonoImageFilter=/FlateEncode -dAutoFilterGrayImages=false -dGrayImageFilter=/FlateEncode -dAutoFilterColorImages=false -dColorImageFilter=/FlateEncode";
	gs_BB = "-q -dNOPAUSE -dBATCH -sDEVICE=bbox -r720";
#ifdef WIN32
	fullGsPath = "gswin32c";
#else
	fullGsPath = "gs";
#endif

	/* Check and interpret the command line arguments */

	GMT_program = argv[0];
	for (i = 1; i < argc; i++) {
		if (argv[i][0] == '-') {
			switch(argv[i][1]) {
				case '\0':
					GMT_give_synopsis_and_exit = TRUE;
					break;

				/* Supplemental parameters */
				case 'A':
					adjust_BB = TRUE;
					break;
				case 'G':
					fullGsPath = &argv[i][2];
					break;
				case 'E':
					dpi = atoi(&argv[i][2]);
					break;
				case 'L':
					if ((fpl = fopen (&argv[i][2], "r")) == NULL) {
						fprintf (stderr, "%s: GMT ERROR: Cannot to open list file %s\n", GMT_program, argv[i]);
						error = TRUE;
					}
					have_list = TRUE;
					break;
				case 'N':
					no_rem = TRUE;
					break;
				case 'P':
					do_portrait = TRUE;
					break;
				case 'T':	/* Select output format */
					for (j = 2; argv[i][j]; j++) {
						switch (argv[i][j]) {
							case 'e':	/* EPS */
								ext = ".eps";
								do_eps = TRUE;
								device = CNULL;
								break;
							case 'f':	/* PDF */
								ext = ".pdf";
								device = "pdfwrite";
								break;
							case 'j':	/* JPEG */
								ext = ".jpg";
								device = "jpeg";
								break;
							case 'g':	/* PNG */
								ext = ".png";
								device = "png16m";
								break;
							case 'm':	/* PPM */
								ext = ".ppm";
								device = "ppmraw";
								break;
							case 't':	/* TIFF */
								ext = ".tif";
								device = "tiff24nc";
								break;
							default:
								fprintf (stderr, "%s: GMT ERROR: Unrecognized option %s\n", GMT_program, argv[i]);
								error = TRUE;
								break;
						}
					}
					break;
				/* Options not recognized */
				default:
					error = TRUE;
					break;
			}
		}
		else {
			strcpy (ps_file, argv[i]);
			n_files++;
		}
	}

	if (argc == 1 || GMT_give_synopsis_and_exit || error) {
		fprintf (stderr,"ps2raster %s - Converts one or several PS file(s) to raster formats using GhostScript.\n\n", GMT_VERSION);
		fprintf(stderr,"usage: ps2raster <psfile1> <psfile2> <...> [-A] [-E<resolution>]\n");
		fprintf(stderr,"       [-G<ghost_path>] [-L<listfile>] [-N] [-P] [-Te|f|g|j|m|t]\n\n");
		if (GMT_give_synopsis_and_exit) exit (EXIT_FAILURE);

		fprintf (stderr,"Works by modifying the page size in order that the resulting\n");
		fprintf (stderr,"image will have the size specified by the BoundingBox.\n");
		fprintf (stderr,"As an option, a tight BoundingBox may be computed.\n\n");
		fprintf (stderr,"	<psfile(s)> postscript file(s) to be converted.\n");
		fprintf(stderr,"\n\tOPTIONS:\n");
		fprintf (stderr,"\t-A Adjust the BoundingBox to the minimum required by the image contents\n");
		fprintf (stderr,"\t-E Set raster resolution in dpi [default = 720 for PDF, 300 for others]\n");
		fprintf (stderr,"\t-G Full path to your ghostscript executable.\n");
		fprintf (stderr,"\t   NOTE: Under Unix systems this is generally not necessary.\n");
		fprintf (stderr,"\t   Under Windows, ghostscript is not added to the system's path.\n");
		fprintf (stderr,"\t   So either you do it yourself, or give the full path here.\n");
		fprintf (stderr,"\t   (e.g. -Gc:\\programs\\gs\\gs7.05\\bin\\gswin32c).\n");
		fprintf (stderr,"\t-L The <listfile> is an ASCII file with names of ps files to be converted\n");
		fprintf (stderr,"\t-N Do Not remove auxiliary files [default, does]. Auxiliary files are\n");
		fprintf (stderr,"\t   build using as base the input ps files and consist of:\n");
		fprintf (stderr,"\t   psfile_tmp.eps -> PostScript with a modified BoundingBox\n");
		fprintf (stderr,"\t   psfile_tmp.bat -> script with the ghostscript command that does the job\n");
		fprintf (stderr,"\t   Use this option to save the script and run it later with different settings\n");
		fprintf (stderr,"\t-P Force Portrait mode. All Landscape mode plots will be rotated back\n");
		fprintf (stderr,"\t   so that they show unrotated in Portrait mode.\n");
		fprintf (stderr,"\t   This is practical when converting to image formats or preparing\n");
		fprintf (stderr,"\t   EPS or PDF plots for inclusion in documents.\n");
		fprintf (stderr,"\t-T Set output format [default is jpeg]\n");
		fprintf (stderr,"\t   e means EPS\n");
		fprintf (stderr,"\t   f means PDF\n");
		fprintf (stderr,"\t   g means PNG\n");
		fprintf (stderr,"\t   j means JPEG\n");
		fprintf (stderr,"\t   m means PPM\n");
		fprintf (stderr,"\t   t means TIF\n");
		fprintf (stderr,"\t   The EPS format can be combined with any of the other formats.\n");
		fprintf (stderr,"\t   For example, -Tef creates both an EPS and PDF file.\n");
		exit (EXIT_FAILURE);
	}

	if (n_files > 1 && have_list) {
		fprintf (stderr, "%s: GMT SYNTAX ERROR: Cannot handle both a file list and multiple ps files in input\n", GMT_program);
		error = TRUE;
	}

	if (error) exit (EXIT_FAILURE);

	/* Use default DPI if not already set */
	if (dpi <= 0) {
		dpi = 300;
		if (!strcmp(ext,".pdf")) dpi = 720;
	}

	/* Multiple files in a file with their names */
	if (have_list) {
		ps_names = (char **) calloc ((size_t)(n_alloc), sizeof (char *));
		while (fgets (line, BUFSIZ, fpl) != NULL) {
			ps_names[n_files] = (char *) calloc (BUFSIZ, sizeof (char));
			if (line[0] == '#' || line[0] == '\n' || line[0] == '\r') continue;
			sscanf (line, "%s",ps_names[n_files]);
			n_files++;
			if (n_files > n_alloc) {
				n_alloc += 100;
				ps_names = realloc (ps_names, n_alloc * sizeof (char *));
			}
		}
		fclose (fpl);
	}

	/* Multiple files given on command line */

	else if (n_files > 1) {
		ps_names = (char **) calloc ((size_t)(n_alloc), sizeof (char *));
		j = 0;
		for (k = 1; k < argc; k++) {
			if (argv[k][0] == '-') continue;
			ps_names[j] = (char *) calloc (BUFSIZ, sizeof (char));
			ps_names[j] = argv[k];
			j++;
			if (n_files > n_alloc) {
				n_alloc += 100;
				ps_names = realloc (ps_names, n_alloc * sizeof (char *));
			}
		}
	}
	else {				/* Single file */
		ps_names = (char **) calloc ((size_t)(1), sizeof (char *));
		ps_names[0] = ps_file;
	}

	/* Loop over all input files */

	for (k = 0; k < n_files; k++) {
		strcpy(ps_file,ps_names[k]);
		if ((fp = fopen (ps_file, "r")) == NULL) {
			fprintf (stderr, "%s: Cannot to open file %s\n", GMT_program, ps_file);
			continue;
		}
		atend = got_BB = got_orient = landscape = setup = FALSE;

		len = strlen(ps_file);
		j = len - 1;
		for (i = 0; i < len; i++, j--) {
			if (ps_file[j] == '.') break;
		}
		script_file = (char *) calloc ((size_t)(j+10), sizeof(char));
		for (i = 0; i < j; i++) script_file[i] = ps_file[i];
		strcat(script_file,"_tmp.bat");

		/* Adjust to a tight BoundingBox if user requested so */

		if (adjust_BB) {
			BB_file = (char *) calloc ((size_t)(j+7), sizeof(char));
			for (i = 0; i < j; i++) BB_file[i] = ps_file[i];
			strcat (BB_file,".bbox");
			sprintf (cmd,"%s %s %s 2> %s",fullGsPath,gs_BB,ps_file,BB_file);
			run_cmd (script_file, cmd, 0, 0);	/* Execute the script that computes the tight BB */
			if ((fpb = fopen (BB_file, "r")) == NULL) {
				fprintf (stderr, "%s: Unable to open file %s\n", GMT_program, BB_file);
				exit (EXIT_FAILURE);
			}
			while (fgets (line, BUFSIZ, fpb) != NULL) {
				sscanf (line, "%s",c);
				if (strcmp(c,"%%BoundingBox:") == 0) {
					sscanf (line, "%s %s %s %s %s",c1,c2,c3,c4,c5);
					x0 = atoi (c2);		y0 = atoi (c3);
					x1 = atoi (c4);		y1 = atoi (c5);
					got_BB = TRUE;
				}
			}
			fclose (fpb);
			run_cmd (BB_file, cmd, 1, 0);	/* Remove the file with BB info */
			free ((void *)BB_file);
		}

		/* Open temporary file to be processed by ghostscript. When just creating EPS, tmp_file is for keeps */

		tmp_file = (char *) calloc ((size_t)(j+10), sizeof(char));
		out_file = (char *) calloc ((size_t)(j+6), sizeof(char));
		for (i = 0; i < j; i++) tmp_file[i] = ps_file[i];
		for (i = 0; i < j; i++) out_file[i] = ps_file[i];
		if (do_eps)
			strcat(tmp_file,".eps");
		else
			strcat(tmp_file,"_tmp.eps");
		if ((fpo = fopen (tmp_file, "w")) == NULL) {
			fprintf (stderr, "%s: Unable to open file %s for writing\n", GMT_program, tmp_file);
			exit (EXIT_FAILURE);
		}
		strcat(out_file,ext);

		/* Scan first 20 lines of input file for BoundingBox and Orientation statements */

		i = 0;
		while ((fgets (line, BUFSIZ, fp) != NULL) && i < 20 && !(got_BB && got_orient)) {
			i++;
			if (!line[0] || line[0] != '%') continue;/* Skip empty and non-comment lines */
			sscanf (line, "%s", c);
			if (!got_BB && !strncmp (c, "%%BoundingBox:", 14)) {
				sscanf (line, "%s %s %s %s %s", c1, c2, c3, c4, c5);
				if (strncmp (c2, "(atend)", 7)) {
					x0 = atoi (c2);		y0 = atoi (c3);
					x1 = atoi (c4);		y1 = atoi (c5);
					got_BB = TRUE;
				}
			}
			else if (!got_orient && !strncmp (c, "%%Orientation:", 14)) {
				sscanf (line, "%s %s", c1, c2);
				if (!strncmp (c2, "Landscape", 9)) landscape = TRUE;
				got_orient = TRUE;
			}
		}

		/* Can not proceed without knowing the BoundingBox */

		if (!got_BB) {
			fprintf (stderr, "%s: GMT FATAL ERROR: The file %s has no BoundingBox in the first 20 lines. Use -A option.\n", GMT_program, ps_file);
			exit (EXIT_FAILURE);
		}

		/* Do the math on the BoundingBox and translation coordinates */

		if (do_eps && !device)
			xt = yt = r = 0, w = x1-x0, h = y1-y0;
		else if (do_portrait && landscape)
			xt = -x1, yt = -y0, w = y1-y0, h = x1-x0, x0 = y0 = 0, r = -90;
		else
			xt = -x0, yt = -y0, w = x1-x0, h = y1-y0, x0 = y0 = r = 0;

		/* Rewind the input file and start copying and replacing */

		rewind (fp);
		while (fgets (line, BUFSIZ, fp) != NULL) {
			if (line[0] != '%') {	/* Copy any non-comment line, except one containing /PageSize in the Setup block */
				if (setup && strstr(line,"/PageSize") != NULL) continue;
				fprintf (fpo, "%s", line);
				continue;
			}
			sscanf (line, "%s",c);
			if (!strncmp(c, "%%BoundingBox:", 14)) {
				if (got_BB) fprintf (fpo, "%%%%BoundingBox: %d %d %d %d\n", x0, y0, x0+w, y0+h);
				got_BB = FALSE;
				continue;
			}
			else if (do_portrait && landscape && !strncmp(c, "%%Orientation:", 14)) {
				fprintf (fpo, "%%%%Orientation: Portrait\n");
				landscape = FALSE;
				continue;
			}
			else if (!strncmp(c, "%%BeginSetup", 12))
				setup=TRUE;
			else if (!strncmp(c, "%%EndSetup", 10))
				setup=FALSE;
			else if (!strncmp(c, "%%EndComments", 13)) {
				fprintf (fpo, "%s", line);
				if (r != 0) fprintf (fpo, "%d rotate\n", r);
				if (xt != 0 || yt != 0) fprintf (fpo, "%d %d translate\n", xt, yt);
				xt = yt = r = 0;
				continue;
			}
			fprintf (fpo, "%s", line);
		}

		fclose (fpo);
		fclose (fp);

		/* Build the converting ghostscript command and execute it */

		if (device) {
			w = irint (w / 72.0 * dpi);
			h = irint (h / 72.0 * dpi);
			sprintf (cmd,"%s %s -sDEVICE=%s -g%dx%d -r%d -sOutputFile=%s -f%s",fullGsPath,gs_params,device,w,h,dpi,out_file,tmp_file);
			run_cmd (script_file, cmd, 0, no_rem);
			if (!no_rem && !do_eps) run_cmd (tmp_file, cmd, 1, 0);
		}

		free ((void *)script_file);
		free ((void *)tmp_file);
		free ((void *)out_file);
	}

	free ((void *)ps_names);

	exit (EXIT_SUCCESS);
}

void run_cmd(char *name, char *cmd, int rem1, int rem2) {
	FILE *fp;

	if (rem1) {
#ifdef WIN32
		sprintf (cmd, "del %s\n", name);
#else
		sprintf (cmd, "rm -f %s\n", name);
#endif
		system(cmd);
		return;
	}

	fp = fopen (name, "w");
#ifdef WIN32
	fprintf (fp, "@echo off\n");
#endif
	fprintf (fp, "%s\n", cmd);
	fclose (fp);

#ifdef WIN32
	sprintf (cmd, "%s",name);
#else
	sprintf (cmd, "sh %s", name);
#endif
	system(cmd);

	if (!rem2) {
#ifdef WIN32
		sprintf (cmd, "del %s\n", name);
#else
		sprintf (cmd, "rm -f %s\n", name);
#endif
		system(cmd);
	}
}
