1 
2 /*
3  * Copyright (c) 1990-1997 Sam Leffler
4  * Copyright (c) 1991-1997 Silicon Graphics, Inc.
5  *
6  * Permission to use, copy, modify, distribute, and sell this software and
7  * its documentation for any purpose is hereby granted without fee, provided
8  * that (i) the above copyright notices and this permission notice appear in
9  * all copies of the software and related documentation, and (ii) the names of
10  * Sam Leffler and Silicon Graphics may not be used in any advertising or
11  * publicity relating to the software without the specific, prior written
12  * permission of Sam Leffler and Silicon Graphics.
13  *
14  * THE SOFTWARE IS PROVIDED "AS-IS" AND WITHOUT WARRANTY OF ANY KIND,
15  * EXPRESS, IMPLIED OR OTHERWISE, INCLUDING WITHOUT LIMITATION, ANY
16  * WARRANTY OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE.
17  *
18  * IN NO EVENT SHALL SAM LEFFLER OR SILICON GRAPHICS BE LIABLE FOR
19  * ANY SPECIAL, INCIDENTAL, INDIRECT OR CONSEQUENTIAL DAMAGES OF ANY KIND,
20  * OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,
21  * WHETHER OR NOT ADVISED OF THE POSSIBILITY OF DAMAGE, AND ON ANY THEORY OF
22  * LIABILITY, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE
23  * OF THIS SOFTWARE.
24  */
25 
26 /*
27  * Convert a CCITT Group 3 or 4 FAX file to TIFF Group 3 or 4 format.
28  */
29 #include "tif_config.h"
30 
31 #include <stdio.h>
32 #include <stdlib.h>		/* should have atof & getopt */
33 
34 #ifdef HAVE_UNISTD_H
35 # include <unistd.h>
36 #endif
37 
38 #ifdef HAVE_FCNTL_H
39 # include <fcntl.h>
40 #endif
41 
42 #ifdef HAVE_IO_H
43 # include <io.h>
44 #endif
45 
46 #ifdef NEED_LIBPORT
47 # include "libport.h"
48 #endif
49 
50 #include "tiffiop.h"
51 
52 #ifndef EXIT_SUCCESS
53 # define EXIT_SUCCESS	0
54 #endif
55 #ifndef EXIT_FAILURE
56 # define EXIT_FAILURE	1
57 #endif
58 
59 #define TIFFhowmany8(x) (((x)&0x07)?((uint32)(x)>>3)+1:(uint32)(x)>>3)
60 
61 TIFF	*faxTIFF;
62 char	*rowbuf;
63 char	*refbuf;
64 
65 uint32	xsize = 1728;
66 int	verbose;
67 int	stretch;
68 uint16	badfaxrun;
69 uint32	badfaxlines;
70 
71 int	copyFaxFile(TIFF* tifin, TIFF* tifout);
72 static	void usage(void);
73 
74 int
main(int argc,char * argv[])75 main(int argc, char* argv[])
76 {
77 	FILE *in;
78 	TIFF *out = NULL;
79 	TIFFErrorHandler whandler = NULL;
80 	int compression_in = COMPRESSION_CCITTFAX3;
81 	int compression_out = COMPRESSION_CCITTFAX3;
82 	int fillorder_in = FILLORDER_LSB2MSB;
83 	int fillorder_out = FILLORDER_LSB2MSB;
84 	uint32 group3options_in = 0;	/* 1d-encoded */
85 	uint32 group3options_out = 0;	/* 1d-encoded */
86 	uint32 group4options_in = 0;	/* compressed */
87 	uint32 group4options_out = 0;	/* compressed */
88 	uint32 defrowsperstrip = (uint32) 0;
89 	uint32 rowsperstrip;
90 	int photometric_in = PHOTOMETRIC_MINISWHITE;
91 	int photometric_out = PHOTOMETRIC_MINISWHITE;
92 	int mode = FAXMODE_CLASSF;
93 	int rows;
94 	int c;
95 	int pn, npages;
96 	float resY = 196.0;
97 	extern int optind;
98 	extern char* optarg;
99 
100 
101 	while ((c = getopt(argc, argv, "R:X:o:1234ABLMPUW5678abcflmprsuvwz?")) != -1)
102 		switch (c) {
103 			/* input-related options */
104 		case '3':		/* input is g3-encoded */
105 			compression_in = COMPRESSION_CCITTFAX3;
106 			break;
107 		case '4':		/* input is g4-encoded */
108 			compression_in = COMPRESSION_CCITTFAX4;
109 			break;
110 		case 'U':		/* input is uncompressed (g3 and g4) */
111 			group3options_in |= GROUP3OPT_UNCOMPRESSED;
112 			group4options_in |= GROUP4OPT_UNCOMPRESSED;
113 			break;
114 		case '1':		/* input is 1d-encoded (g3 only) */
115 			group3options_in &= ~GROUP3OPT_2DENCODING;
116 			break;
117 		case '2':		/* input is 2d-encoded (g3 only) */
118 			group3options_in |= GROUP3OPT_2DENCODING;
119 			break;
120 		case 'P':	/* input has not-aligned EOL (g3 only) */
121 			group3options_in &= ~GROUP3OPT_FILLBITS;
122 			break;
123 		case 'A':		/* input has aligned EOL (g3 only) */
124 			group3options_in |= GROUP3OPT_FILLBITS;
125 			break;
126 		case 'W':		/* input has 0 mean white */
127 			photometric_in = PHOTOMETRIC_MINISWHITE;
128 			break;
129 		case 'B':		/* input has 0 mean black */
130 			photometric_in = PHOTOMETRIC_MINISBLACK;
131 			break;
132 		case 'L':		/* input has lsb-to-msb fillorder */
133 			fillorder_in = FILLORDER_LSB2MSB;
134 			break;
135 		case 'M':		/* input has msb-to-lsb fillorder */
136 			fillorder_in = FILLORDER_MSB2LSB;
137 			break;
138 		case 'R':		/* input resolution */
139 			resY = (float) atof(optarg);
140 			break;
141 		case 'X':		/* input width */
142 			xsize = (uint32) atoi(optarg);
143 			break;
144 
145 			/* output-related options */
146 		case '7':		/* generate g3-encoded output */
147 			compression_out = COMPRESSION_CCITTFAX3;
148 			break;
149 		case '8':		/* generate g4-encoded output */
150 			compression_out = COMPRESSION_CCITTFAX4;
151 			break;
152 		case 'u':	/* generate uncompressed output (g3 and g4) */
153 			group3options_out |= GROUP3OPT_UNCOMPRESSED;
154 			group4options_out |= GROUP4OPT_UNCOMPRESSED;
155 			break;
156 		case '5':	/* generate 1d-encoded output (g3 only) */
157 			group3options_out &= ~GROUP3OPT_2DENCODING;
158 			break;
159 		case '6':	/* generate 2d-encoded output (g3 only) */
160 			group3options_out |= GROUP3OPT_2DENCODING;
161 			break;
162 		case 'c':		/* generate "classic" g3 format */
163 			mode = FAXMODE_CLASSIC;
164 			break;
165 		case 'f':		/* generate Class F format */
166 			mode = FAXMODE_CLASSF;
167 			break;
168 		case 'm':		/* output's fillorder is msb-to-lsb */
169 			fillorder_out = FILLORDER_MSB2LSB;
170 			break;
171 		case 'l':		/* output's fillorder is lsb-to-msb */
172 			fillorder_out = FILLORDER_LSB2MSB;
173 			break;
174 		case 'o':
175 			out = TIFFOpen(optarg, "w");
176 			if (out == NULL) {
177 				fprintf(stderr,
178 				    "%s: Can not create or open %s\n",
179 				    argv[0], optarg);
180 				return EXIT_FAILURE;
181 			}
182 			break;
183 		case 'a':	/* generate EOL-aligned output (g3 only) */
184 			group3options_out |= GROUP3OPT_FILLBITS;
185 			break;
186 		case 'p':	/* generate not EOL-aligned output (g3 only) */
187 			group3options_out &= ~GROUP3OPT_FILLBITS;
188 			break;
189 		case 'r':		/* rows/strip */
190 			defrowsperstrip = atol(optarg);
191 			break;
192 		case 's':		/* stretch image by dup'ng scanlines */
193 			stretch = 1;
194 			break;
195 		case 'w':		/* undocumented -- for testing */
196 			photometric_out = PHOTOMETRIC_MINISWHITE;
197 			break;
198 		case 'b':		/* undocumented -- for testing */
199 			photometric_out = PHOTOMETRIC_MINISBLACK;
200 			break;
201 		case 'z':		/* undocumented -- for testing */
202 			compression_out = COMPRESSION_LZW;
203 			break;
204 		case 'v':		/* -v for info */
205 			verbose++;
206 			break;
207 		case '?':
208 			usage();
209 			/*NOTREACHED*/
210 		}
211 	npages = argc - optind;
212 	if (npages < 1)
213 		usage();
214 
215 	rowbuf = _TIFFmalloc(TIFFhowmany8(xsize));
216 	refbuf = _TIFFmalloc(TIFFhowmany8(xsize));
217 	if (rowbuf == NULL || refbuf == NULL) {
218 		fprintf(stderr, "%s: Not enough memory\n", argv[0]);
219 		return (EXIT_FAILURE);
220 	}
221 
222 	if (out == NULL) {
223 		out = TIFFOpen("fax.tif", "w");
224 		if (out == NULL) {
225 			fprintf(stderr, "%s: Can not create fax.tif\n",
226 			    argv[0]);
227 			return (EXIT_FAILURE);
228 		}
229 	}
230 
231 	faxTIFF = TIFFClientOpen("(FakeInput)", "w",
232 	/* TIFFClientOpen() fails if we don't set existing value here */
233 				 TIFFClientdata(out),
234 				 TIFFGetReadProc(out), TIFFGetWriteProc(out),
235 				 TIFFGetSeekProc(out), TIFFGetCloseProc(out),
236 				 TIFFGetSizeProc(out), TIFFGetMapFileProc(out),
237 				 TIFFGetUnmapFileProc(out));
238 	if (faxTIFF == NULL) {
239 		fprintf(stderr, "%s: Can not create fake input file\n",
240 		    argv[0]);
241 		return (EXIT_FAILURE);
242 	}
243 	TIFFSetMode(faxTIFF, O_RDONLY);
244 	TIFFSetField(faxTIFF, TIFFTAG_IMAGEWIDTH,	xsize);
245 	TIFFSetField(faxTIFF, TIFFTAG_SAMPLESPERPIXEL,	1);
246 	TIFFSetField(faxTIFF, TIFFTAG_BITSPERSAMPLE,	1);
247 	TIFFSetField(faxTIFF, TIFFTAG_FILLORDER,	fillorder_in);
248 	TIFFSetField(faxTIFF, TIFFTAG_PLANARCONFIG,	PLANARCONFIG_CONTIG);
249 	TIFFSetField(faxTIFF, TIFFTAG_PHOTOMETRIC,	photometric_in);
250 	TIFFSetField(faxTIFF, TIFFTAG_YRESOLUTION,	resY);
251 	TIFFSetField(faxTIFF, TIFFTAG_RESOLUTIONUNIT,	RESUNIT_INCH);
252 
253 	/* NB: this must be done after directory info is setup */
254 	TIFFSetField(faxTIFF, TIFFTAG_COMPRESSION, compression_in);
255 	if (compression_in == COMPRESSION_CCITTFAX3)
256 		TIFFSetField(faxTIFF, TIFFTAG_GROUP3OPTIONS, group3options_in);
257 	else if (compression_in == COMPRESSION_CCITTFAX4)
258 		TIFFSetField(faxTIFF, TIFFTAG_GROUP4OPTIONS, group4options_in);
259 	for (pn = 0; optind < argc; pn++, optind++) {
260 		in = fopen(argv[optind], "rb");
261 		if (in == NULL) {
262 			fprintf(stderr,
263 			    "%s: %s: Can not open\n", argv[0], argv[optind]);
264 			continue;
265 		}
266 #if defined(_WIN32) && defined(USE_WIN32_FILEIO)
267                 TIFFSetClientdata(faxTIFF, (thandle_t)_get_osfhandle(fileno(in)));
268 #else
269                 TIFFSetClientdata(faxTIFF, (thandle_t)fileno(in));
270 #endif
271 		TIFFSetFileName(faxTIFF, (const char*)argv[optind]);
272 		TIFFSetField(out, TIFFTAG_IMAGEWIDTH, xsize);
273 		TIFFSetField(out, TIFFTAG_BITSPERSAMPLE, 1);
274 		TIFFSetField(out, TIFFTAG_COMPRESSION, compression_out);
275 		TIFFSetField(out, TIFFTAG_PHOTOMETRIC, photometric_out);
276 		TIFFSetField(out, TIFFTAG_ORIENTATION, ORIENTATION_TOPLEFT);
277 		TIFFSetField(out, TIFFTAG_SAMPLESPERPIXEL, 1);
278 		switch (compression_out) {
279 			/* g3 */
280 			case COMPRESSION_CCITTFAX3:
281 			TIFFSetField(out, TIFFTAG_GROUP3OPTIONS,
282 				     group3options_out);
283 			TIFFSetField(out, TIFFTAG_FAXMODE, mode);
284 			rowsperstrip =
285 				(defrowsperstrip)?defrowsperstrip:(uint32)-1L;
286 			break;
287 
288 			/* g4 */
289 			case COMPRESSION_CCITTFAX4:
290 			TIFFSetField(out, TIFFTAG_GROUP4OPTIONS,
291 				     group4options_out);
292 			TIFFSetField(out, TIFFTAG_FAXMODE, mode);
293 			rowsperstrip =
294 				(defrowsperstrip)?defrowsperstrip:(uint32)-1L;
295 			break;
296 
297 			default:
298 			rowsperstrip = (defrowsperstrip) ?
299 				defrowsperstrip : TIFFDefaultStripSize(out, 0);
300 		}
301 		TIFFSetField(out, TIFFTAG_ROWSPERSTRIP, rowsperstrip);
302 		TIFFSetField(out, TIFFTAG_PLANARCONFIG, PLANARCONFIG_CONTIG);
303 		TIFFSetField(out, TIFFTAG_FILLORDER, fillorder_out);
304 		TIFFSetField(out, TIFFTAG_SOFTWARE, "fax2tiff");
305 		TIFFSetField(out, TIFFTAG_XRESOLUTION, 204.0);
306 		if (!stretch) {
307 			TIFFGetField(faxTIFF, TIFFTAG_YRESOLUTION, &resY);
308 			TIFFSetField(out, TIFFTAG_YRESOLUTION, resY);
309 		} else
310 			TIFFSetField(out, TIFFTAG_YRESOLUTION, 196.);
311 		TIFFSetField(out, TIFFTAG_RESOLUTIONUNIT, RESUNIT_INCH);
312 		TIFFSetField(out, TIFFTAG_PAGENUMBER, pn, npages);
313 
314 		if (!verbose)
315 		    whandler = TIFFSetWarningHandler(NULL);
316 		rows = copyFaxFile(faxTIFF, out);
317 		fclose(in);
318 		if (!verbose)
319 		    (void) TIFFSetWarningHandler(whandler);
320 
321 		TIFFSetField(out, TIFFTAG_IMAGELENGTH, rows);
322 
323 		if (verbose) {
324 			fprintf(stderr, "%s:\n", argv[optind]);
325 			fprintf(stderr, "%d rows in input\n", rows);
326 			fprintf(stderr, "%ld total bad rows\n",
327 			    (long) badfaxlines);
328 			fprintf(stderr, "%d max consecutive bad rows\n", badfaxrun);
329 		}
330 		if (compression_out == COMPRESSION_CCITTFAX3 &&
331 		    mode == FAXMODE_CLASSF) {
332 			TIFFSetField(out, TIFFTAG_BADFAXLINES, badfaxlines);
333 			TIFFSetField(out, TIFFTAG_CLEANFAXDATA, badfaxlines ?
334 			    CLEANFAXDATA_REGENERATED : CLEANFAXDATA_CLEAN);
335 			TIFFSetField(out, TIFFTAG_CONSECUTIVEBADFAXLINES, badfaxrun);
336 		}
337 		TIFFWriteDirectory(out);
338 	}
339 	TIFFClose(out);
340 	_TIFFfree(rowbuf);
341 	_TIFFfree(refbuf);
342 	return (EXIT_SUCCESS);
343 }
344 
345 int
copyFaxFile(TIFF * tifin,TIFF * tifout)346 copyFaxFile(TIFF* tifin, TIFF* tifout)
347 {
348 	uint32 row;
349 	uint32 linesize = TIFFhowmany8(xsize);
350 	uint16 badrun;
351 	int ok;
352 
353 	tifin->tif_rawdatasize = (tmsize_t)TIFFGetFileSize(tifin);
354 	tifin->tif_rawdata = _TIFFmalloc(tifin->tif_rawdatasize);
355 	if (tifin->tif_rawdata == NULL) {
356 		TIFFError(tifin->tif_name, "Not enough memory");
357 		return (0);
358 	}
359 	if (!ReadOK(tifin, tifin->tif_rawdata, tifin->tif_rawdatasize)) {
360 		TIFFError(tifin->tif_name, "Read error at scanline 0");
361 		return (0);
362 	}
363 	tifin->tif_rawcp = tifin->tif_rawdata;
364 	tifin->tif_rawcc = tifin->tif_rawdatasize;
365 
366 	(*tifin->tif_setupdecode)(tifin);
367 	(*tifin->tif_predecode)(tifin, (tsample_t) 0);
368 	tifin->tif_row = 0;
369 	badfaxlines = 0;
370 	badfaxrun = 0;
371 
372 	_TIFFmemset(refbuf, 0, linesize);
373 	row = 0;
374 	badrun = 0;		/* current run of bad lines */
375 	while (tifin->tif_rawcc > 0) {
376 		ok = (*tifin->tif_decoderow)(tifin, (tdata_t) rowbuf,
377 					     linesize, 0);
378 		if (!ok) {
379 			badfaxlines++;
380 			badrun++;
381 			/* regenerate line from previous good line */
382 			_TIFFmemcpy(rowbuf, refbuf, linesize);
383 		} else {
384 			if (badrun > badfaxrun)
385 				badfaxrun = badrun;
386 			badrun = 0;
387 			_TIFFmemcpy(refbuf, rowbuf, linesize);
388 		}
389 		tifin->tif_row++;
390 
391 		if (TIFFWriteScanline(tifout, rowbuf, row, 0) < 0) {
392 			fprintf(stderr, "%s: Write error at row %ld.\n",
393 			    tifout->tif_name, (long) row);
394 			break;
395 		}
396 		row++;
397 		if (stretch) {
398 			if (TIFFWriteScanline(tifout, rowbuf, row, 0) < 0) {
399 				fprintf(stderr, "%s: Write error at row %ld.\n",
400 				    tifout->tif_name, (long) row);
401 				break;
402 			}
403 			row++;
404 		}
405 	}
406 	if (badrun > badfaxrun)
407 		badfaxrun = badrun;
408 	_TIFFfree(tifin->tif_rawdata);
409 	return (row);
410 }
411 
412 char* stuff[] = {
413 "usage: fax2tiff [options] input.raw...",
414 "where options are:",
415 " -3		input data is G3-encoded		[default]",
416 " -4		input data is G4-encoded",
417 " -U		input data is uncompressed (G3 or G4)",
418 " -1		input data is 1D-encoded (G3 only)	[default]",
419 " -2		input data is 2D-encoded (G3 only)",
420 " -P		input is not EOL-aligned (G3 only)	[default]",
421 " -A		input is EOL-aligned (G3 only)",
422 " -M		input data has MSB2LSB bit order",
423 " -L		input data has LSB2MSB bit order	[default]",
424 " -B		input data has min 0 means black",
425 " -W		input data has min 0 means white	[default]",
426 " -R #		input data has # resolution (lines/inch) [default is 196]",
427 " -X #		input data has # width			[default is 1728]",
428 "",
429 " -o out.tif	write output to out.tif",
430 " -7		generate G3-encoded output		[default]",
431 " -8		generate G4-encoded output",
432 " -u		generate uncompressed output (G3 or G4)",
433 " -5		generate 1D-encoded output (G3 only)",
434 " -6		generate 2D-encoded output (G3 only)	[default]",
435 " -p		generate not EOL-aligned output (G3 only)",
436 " -a		generate EOL-aligned output (G3 only)	[default]",
437 " -c		generate \"classic\" TIFF format",
438 " -f		generate TIFF Class F (TIFF/F) format	[default]",
439 " -m		output fill order is MSB2LSB",
440 " -l		output fill order is LSB2MSB		[default]",
441 " -r #		make each strip have no more than # rows",
442 " -s		stretch image by duplicating scanlines",
443 " -v		print information about conversion work",
444 " -z		generate LZW compressed output",
445 NULL
446 };
447 
448 static void
usage(void)449 usage(void)
450 {
451 	char buf[BUFSIZ];
452 	int i;
453 
454 	setbuf(stderr, buf);
455         fprintf(stderr, "%s\n\n", TIFFGetVersion());
456 	for (i = 0; stuff[i] != NULL; i++)
457 		fprintf(stderr, "%s\n", stuff[i]);
458 	exit(EXIT_FAILURE);
459 }
460 
461 /* vim: set ts=8 sts=8 sw=8 noet: */
462 /*
463  * Local Variables:
464  * mode: c
465  * c-basic-offset: 8
466  * fill-column: 78
467  * End:
468  */
469