// This program will convert MetaPost output to a PDF and modify,
// if desired, the Bounding Box.
//
// This utility is licensed under the GNU GPL.
//
// Author: Troy Henderson
// Email : thenders@math.tamu.edu
// ICQ   : 4551170

#include <stdio.h>
#include <getopt.h>
#include <stdbool.h>
#include <stdlib.h>
//#include <unistd.h>

const char* progname;

void usage (FILE* stream, int exit_code) {
	fprintf(stream,"Usage: %s options [ inputfile ]\n",progname);
	fprintf(stream,
			"  -h  --help                                Display this usage information.\n"
			"  -o  --output filename                     Write output to file.\n"
			"  -t  --tighten-bbox <Dl>[,<Db>,<Dr>,<Dt>]  Tighten bounding box.\n");
	fprintf(stream,"\nNote: <Dl>,  <Db>,  <Dr>,  and <Dt> represent the number of (integer) PostScript\n");
	fprintf(stream,"      points to tighten the left, bottom, right, and top, respectively.  If only\n");
	fprintf(stream,"      <Dl> is specfied,  then  <Db>,  <Dr>,  and <Dt> are all assumed to be this\n");
	fprintf(stream,"      value of <Dl>.  Furthermore, negative values of <Dl>, <Db>, <Dr>, and <Dt>\n");
	fprintf(stream,"      will actually \"loosen\" the left, bottom, right, and top, respectively.\n");
	exit(exit_code);
}

int main(int argc,char* argv[]) {
	int n,nextopt,numargs=1,numcommas=0,MAXLINELEN=0,FOUNDBB=0;
	long int xl,yb,xr,yt,Dl=0,Db=0,Dr=0,Dt=0,width,height,tmplong;
	char c,*endptr,*line,*BBOX="%%BoundingBox:",BBOX2[15],*GS;
	char dL[80],dB[80],dR[80],dT[80];
	const char* const shortopts="ho:t:";
	const struct option longopts[] = {
		{ "help" , 0, NULL, 'h' },
		{ "output" , 1, NULL, 'o' },
		{ "tighten-bbox" ,1, NULL, 't' },
		{ NULL , 0, NULL, 0 }
	};
	const char* PDF = NULL;
	const char* PS = NULL;
	const char* DELTA = "0,0,0,0";
	bool valid=true;
	FILE* fp,*pipe;
	
	progname = argv[0];
// ********************* Read in the command line arguments *******************
	do {
		nextopt = getopt_long(argc,argv,shortopts,longopts,NULL);
		switch(nextopt) {
			case 'h':						// -h or --help
				usage(stdout,0);
			case 'o':						// -o or --output
				numargs=numargs+2;
				PDF=optarg;
				break;
			case 't':						// -t or --tighten-bbox
				numargs=numargs+2;
				DELTA=optarg;
				break;
			case '?':						// Invalid Option
				usage(stderr,1);
			case -1:							// Done with options
				break;
			default:							// Something else: unexpected
				abort();
		}
	}
	while (nextopt != -1);

	if(argc != (numargs+1))
		usage(stderr,1);
	PS=argv[optind];

// ******************** Read in BoundingBox changes ***************************
	for(n=0;n<strlen(DELTA);n++) {
		if(DELTA[n]==',')
			numcommas++;
	}
	if(numcommas != 0 && numcommas != 3) {
		fprintf(stderr,"-t (or --tighten-bbox) syntax is incorrect\n\n");
		usage(stderr,1);
	}
	if(numcommas == 0) {
		sscanf(DELTA,"%s",&dL);
		strcpy(dB,dL);
		strcpy(dR,dL);
		strcpy(dT,dL);
	} else {
		sscanf(DELTA,"%[^,],%[^,],%[^,],%s",&dL,&dB,&dR,&dT);
	}
	// ********** Convert <Dl> (as a string) to an integer ***********
	tmplong=strtol(dL,&endptr,10);
	if (*endptr!='\0') {
		fprintf(stderr,"<Dl>=%s cannot be converted to an integer\n",dL);
		valid=false;
	} else {
		Dl=tmplong;
	}
	// ********** Convert <Db> (as a string) to an integer ***********
	tmplong=strtol(dB,&endptr,10);
	if (*endptr!='\0') {
		fprintf(stderr,"<Db>=%s cannot be converted to an integer\n",dB);
		valid=false;
	} else {
		Db=tmplong;
	}
	// ********** Convert <Dr> (as a string) to an integer ***********//
	tmplong=strtol(dR,&endptr,10);
	if (*endptr!='\0') {
		fprintf(stderr,"<Dr>=%s cannot be converted to an integer\n",dR);
		valid=false;
	} else {
		Dr=tmplong;
	}
	// ********** Convert <Dt> (as a string) to an integer ***********
	tmplong=strtol(dT,&endptr,10);
	if (*endptr!='\0') {
		fprintf(stderr,"<Dt>=%s cannot be converted to an integer\n",dT);
		valid=false;
	} else {
		Dt=tmplong;
	}
 
	// *** If <Dl>, <Db>, <Dr>, or <Dt> could not be converted ... show usage **
	if(valid==false)
		usage(stderr,1);

// ********* Make sure that the input file can be open for reading ************
	//	if ( access(PS,R_OK) == -1) {
	//		fprintf(stderr,"Cannot read (or doesn't exist) file <%s>\n",PS);
	//		exit(0);
	//	}
	if ( (fp=fopen(PS,"r")) == NULL ) {
		fprintf(stderr,"Cannot read file <%s>\n",PS);
		exit(0);
	}

// ********** Determine the length of the longest line in the input file ******
	n=0;
	while((c=fgetc(fp)) != EOF) {
		if(c=='\n') {
			if(n>MAXLINELEN)
				MAXLINELEN=n;
			n=0;
		} else {
			n++;
		}
	}
	fclose(fp);

// ******* Read in data and pipe it directly to the Ghostscript command. ******
// ******* If we find "%%BoundingBox", then change it (if necessary) and ******
// ******* append the PageSize.                                          ******
	
	fp=fopen(PS,"r");
	line=(char*)malloc((MAXLINELEN+320)*sizeof(char));
	GS=(char*)malloc((strlen(PDF)+61)*sizeof(char));
//	sprintf(GS,"gs -q -dUseCropBox -sDEVICE=pdfwrite -sOutputFile=%s - -c quit",PDF);
	sprintf(GS,"gs -q -dEPSCrop -sDEVICE=pdfwrite -sOutputFile=%s - -c quit",PDF);
   pipe=popen(GS,"w");
	free(GS);
	BBOX2[14]='\0';
	while(fgets(line,MAXLINELEN+1,fp) != NULL) {
		strncpy(BBOX2,line,14);
		if(FOUNDBB==0 && strcmp(BBOX,BBOX2)==0) {
			sscanf(line,"%s %ld %ld %ld %ld",&BBOX2,&xl,&yb,&xr,&yt);
			width=xr-xl-Dr-Dl;
			height=yt-yb-Dt-Db;
			sprintf(line,"%s 0 0 %ld %ld\n<</PageSize [%d %d]>>setpagedevice\nmark /MediaBox [0 0 %ld %ld] /PAGE pdfmark\nmark /CropBox [0 0 %ld %ld] /PAGE pdfmark\ngsave %ld %ld translate\n",BBOX2,width,height,width,height,width,height,width,height,-xl-Dl,-yb-Db);
			FOUNDBB=1;
      }
		fprintf(pipe,"%s",line);
	}
	free(line);
	pclose(pipe);
	fclose(fp);
}
