#ifndef lint
static char rcsid[] = "$Header: /usr/people/sam/tiff/tools/RCS/fax2tiff.c,v 1.15 92/02/10 19:04:17 sam Exp $";
#endif

/*
 * Copyright (c) 1990, 1991, 1992 Sam Leffler
 * Copyright (c) 1991, 1992 Silicon Graphics, Inc.
 *
 * Permission to use, copy, modify, distribute, and sell this software and 
 * its documentation for any purpose is hereby granted without fee, provided
 * that (i) the above copyright notices and this permission notice appear in
 * all copies of the software and related documentation, and (ii) the names of
 * Sam Leffler and Silicon Graphics may not be used in any advertising or
 * publicity relating to the software without the specific, prior written
 * permission of Sam Leffler and Silicon Graphics.
 * 
 * THE SOFTWARE IS PROVIDED "AS-IS" AND WITHOUT WARRANTY OF ANY KIND, 
 * EXPRESS, IMPLIED OR OTHERWISE, INCLUDING WITHOUT LIMITATION, ANY 
 * WARRANTY OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE.  
 * 
 * IN NO EVENT SHALL SAM LEFFLER OR SILICON GRAPHICS BE LIABLE FOR
 * ANY SPECIAL, INCIDENTAL, INDIRECT OR CONSEQUENTIAL DAMAGES OF ANY KIND,
 * OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,
 * WHETHER OR NOT ADVISED OF THE POSSIBILITY OF DAMAGE, AND ON ANY THEORY OF 
 * LIABILITY, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE 
 * OF THIS SOFTWARE.
 */

/* 
 * Convert a CCITT Group 3 FAX file to TIFF Group 3 format.
 */
#include <stdio.h>
#include <math.h>		/* for atof declaration */
#include "tiffioP.h"

TIFF	faxTIFF;
#define XSIZE		1728
u_char	rowbuf[howmany(XSIZE,8)];
u_char	refbuf[howmany(XSIZE,8)];

int	verbose;
int	stretch;
u_short	badfaxrun;
u_long	badfaxlines;

usage()
{
	fprintf(stderr, "usage: fax2tiff [-2BLMW] [-R inres] [-14cfmpsv] [-o out.tif] faxfile ...\n");
	exit(-1);
}

main(argc, argv)
	int argc;
	char *argv[];
{
	FILE *in;
	TIFF *out = NULL;
	int compression = COMPRESSION_CCITTFAX3;
	int fillorder = FILLORDER_LSB2MSB;
	long group3options = GROUP3OPT_FILLBITS;
	int photometric = PHOTOMETRIC_MINISWHITE;
	int isClassF = 1;
	int rows;
	int c;
	int pn, npages;
	extern int optind;
	extern char *optarg;

	bzero(&faxTIFF, sizeof (faxTIFF));
	TIFFSetField(&faxTIFF, TIFFTAG_IMAGEWIDTH,	XSIZE);
	TIFFSetField(&faxTIFF, TIFFTAG_SAMPLESPERPIXEL,	1);
	TIFFSetField(&faxTIFF, TIFFTAG_BITSPERSAMPLE,	1);
	TIFFSetField(&faxTIFF, TIFFTAG_FILLORDER,	FILLORDER_LSB2MSB);
	TIFFSetField(&faxTIFF, TIFFTAG_PLANARCONFIG,	PLANARCONFIG_CONTIG);
	TIFFSetField(&faxTIFF, TIFFTAG_PHOTOMETRIC,	PHOTOMETRIC_MINISWHITE);
	TIFFSetField(&faxTIFF, TIFFTAG_YRESOLUTION,	196.);
	TIFFSetField(&faxTIFF, TIFFTAG_RESOLUTIONUNIT,	RESUNIT_INCH);
	/* NB: this is normally setup when a directory is read */
	faxTIFF.tif_scanlinesize = TIFFScanlineSize(&faxTIFF);

	while ((c = getopt(argc, argv, "R:o:2BLMW14cflmpsvwz")) != -1)
		switch (c) {
			/* input-related options */
		case '2':		/* input is 2d-encoded */
			TIFFSetField(&faxTIFF,
			    TIFFTAG_GROUP3OPTIONS, GROUP3OPT_2DENCODING);
			break;
		case 'B':		/* input has 0 mean black */
			TIFFSetField(&faxTIFF,
			    TIFFTAG_PHOTOMETRIC, PHOTOMETRIC_MINISBLACK);
			break;
		case 'L':		/* input has lsb-to-msb fillorder */
			TIFFSetField(&faxTIFF,
			    TIFFTAG_FILLORDER, FILLORDER_LSB2MSB);
			break;
		case 'M':		/* input has msb-to-lsb fillorder */
			TIFFSetField(&faxTIFF,
			    TIFFTAG_FILLORDER, FILLORDER_MSB2LSB);
			break;
		case 'R':		/* input resolution */
			TIFFSetField(&faxTIFF,
			    TIFFTAG_YRESOLUTION, atof(optarg));
			break;
		case 'W':		/* input has 0 mean white */
			TIFFSetField(&faxTIFF,
			    TIFFTAG_PHOTOMETRIC, PHOTOMETRIC_MINISWHITE);
			break;

			/* output-related options */
		case '1':		/* generate 1d-encoded output */
			group3options &= ~GROUP3OPT_2DENCODING;
			break;
		case '4':		/* generate g4-encoded output */
			compression = COMPRESSION_CCITTFAX4;
			break;
		case 'c':		/* generate "classic" g3 format */
			isClassF = 0;
			break;
		case 'f':		/* generate Class F format */
			isClassF = 1;
			break;
		case 'm':		/* output's fillorder is msb-to-lsb */
			fillorder = FILLORDER_MSB2LSB;
			break;
		case 'o':
			out = TIFFOpen(optarg, "w");
			if (out == NULL)
				exit(-2);
			break;
		case 'p':		/* zero pad output scanline EOLs */
			group3options &= ~GROUP3OPT_FILLBITS;
			break;
		case 's':		/* stretch image by dup'ng scanlines */
			stretch = 1;
			break;
		case 'w':		/* undocumented -- for testing */
			photometric = PHOTOMETRIC_MINISBLACK;
			break;

		case 'z':		/* undocumented -- for testing */
			compression = COMPRESSION_LZW;
			break;

		case 'v':		/* -v for info */
			verbose++;
			break;
		case '?':
			usage();
			/*NOTREACHED*/
		}
	if (out == NULL) {
		out = TIFFOpen("fax.tif", "w");
		if (out == NULL)
			exit(-2);
	}
	faxTIFF.tif_fillorder = out->tif_fillorder;	/* XXX */

	npages = argc - optind;
	if (npages < 1)
		usage();

	/* NB: this must be done after directory info is setup */
	TIFFSetField(&faxTIFF, TIFFTAG_COMPRESSION, COMPRESSION_CCITTFAX3);
	for (pn = 0; optind < argc; pn++, optind++) {
		in = fopen(argv[optind], "r");
		if (in == NULL) {
			fprintf(stderr,
			    "%s: %s: Can not open\n", argv[0], argv[optind]);
			continue;
		}
		faxTIFF.tif_fd = fileno(in);
		faxTIFF.tif_name = argv[optind];
		TIFFSetField(out, TIFFTAG_IMAGEWIDTH, XSIZE);
		TIFFSetField(out, TIFFTAG_BITSPERSAMPLE, 1);
		TIFFSetField(out, TIFFTAG_COMPRESSION, compression);
		TIFFSetField(out, TIFFTAG_PHOTOMETRIC, photometric);
		TIFFSetField(out, TIFFTAG_ORIENTATION, ORIENTATION_TOPLEFT);
		TIFFSetField(out, TIFFTAG_SAMPLESPERPIXEL, 1);
		if (compression == COMPRESSION_CCITTFAX3) {
			TIFFSetField(out, TIFFTAG_GROUP3OPTIONS, group3options);
			TIFFModeCCITTFax3(out, isClassF);
		}
		if (compression == COMPRESSION_CCITTFAX3 ||
		    compression == COMPRESSION_CCITTFAX4)
			TIFFSetField(out, TIFFTAG_ROWSPERSTRIP, -1L);
		else
			TIFFSetField(out, TIFFTAG_ROWSPERSTRIP,
			    (u_long) (8*1024)/TIFFScanlineSize(out));
		TIFFSetField(out, TIFFTAG_PLANARCONFIG, PLANARCONFIG_CONTIG);
		TIFFSetField(out, TIFFTAG_FILLORDER, fillorder);
		TIFFSetField(out, TIFFTAG_SOFTWARE, "fax2tiff");
		TIFFSetField(out, TIFFTAG_XRESOLUTION, 204.0);
		if (!stretch) {
			float yres;
			TIFFGetField(&faxTIFF, TIFFTAG_YRESOLUTION, &yres);
			TIFFSetField(out, TIFFTAG_YRESOLUTION, yres);
		} else
			TIFFSetField(out, TIFFTAG_YRESOLUTION, 196.);
		TIFFSetField(out, TIFFTAG_RESOLUTIONUNIT, RESUNIT_INCH);
		TIFFSetField(out, TIFFTAG_PAGENUMBER, pn+1, npages);

		rows = copyFaxFile(&faxTIFF, out);
		fclose(in);

		TIFFSetField(out, TIFFTAG_IMAGELENGTH, rows);

		if (verbose) {
			fprintf(stderr, "%s:\n", argv[optind]);
			fprintf(stderr, "%d rows in input\n", rows);
			fprintf(stderr, "%d total bad rows\n", badfaxlines);
			fprintf(stderr, "%d max consecutive bad rows\n", badfaxrun);
		}
		if (compression == COMPRESSION_CCITTFAX3 && isClassF) {
			TIFFSetField(out, TIFFTAG_BADFAXLINES, badfaxlines);
			TIFFSetField(out, TIFFTAG_CLEANFAXDATA, badfaxlines ?
			    CLEANFAXDATA_REGENERATED : CLEANFAXDATA_CLEAN);
			TIFFSetField(out, TIFFTAG_CONSECUTIVEBADFAXLINES, badfaxrun);
		}
		TIFFWriteDirectory(out);
	}
	TIFFClose(out);
	exit(0);
}

copyFaxFile(tifin, tifout)
	TIFF *tifin, *tifout;
{
	long row;
	u_short badrun;
	int ok;

	tifin->tif_rawdatasize = TIFFGetFileSize(tifin->tif_fd);
	tifin->tif_rawdata = malloc(tifin->tif_rawdatasize);
	if (!ReadOK(tifin->tif_fd, tifin->tif_rawdata, tifin->tif_rawdatasize)) {
		TIFFError(tifin->tif_name, "%s: Read error at scanline 0");
		return (0);
	}
	tifin->tif_rawcp = tifin->tif_rawdata;
	tifin->tif_rawcc = tifin->tif_rawdatasize;

	(*tifin->tif_predecode)(tifin);
	tifin->tif_row = 0;
	badfaxlines = 0;
	badfaxrun = 0;

	bzero(refbuf, sizeof (refbuf));
	row = 0;
	badrun = 0;		/* current run of bad lines */
	while (tifin->tif_rawcc > 0) {
		ok = (*tifin->tif_decoderow)(tifin, rowbuf, sizeof (rowbuf), 0);
		if (!ok) {
			badfaxlines++;
			badrun++;
			/* regenerate line from previous good line */
			bcopy(refbuf, rowbuf, sizeof (rowbuf));
		} else {
			if (badrun > badfaxrun)
				badfaxrun = badrun;
			badrun = 0;
			bcopy(rowbuf, refbuf, sizeof (rowbuf));
		}
		tifin->tif_row++;

		if (TIFFWriteScanline(tifout, rowbuf, row, 0) < 0) {
			fprintf(stderr, "%s: Write error at row %ld.\n",
			    tifout->tif_name, row);
			break;
		}
		row++;
		if (stretch) {
			if (TIFFWriteScanline(tifout, rowbuf, row, 0) < 0) {
				fprintf(stderr, "%s: Write error at row %ld.\n",
				    tifout->tif_name, row);
				break;
			}
			row++;
		}
	}
	if (badrun > badfaxrun)
		badfaxrun = badrun;
	free(tifin->tif_rawdata);
	return (row);
}
