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