1 /* $Id: fax2tiff.c,v 1.19.2.1 2010-06-08 18:50:43 bfriesen Exp $ */
4 * Copyright (c) 1990-1997 Sam Leffler
5 * Copyright (c) 1991-1997 Silicon Graphics, Inc.
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.
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.
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
28 * Convert a CCITT Group 3 or 4 FAX file to TIFF Group 3 or 4 format.
30 #include "tif_config.h"
33 #include <stdlib.h> /* should have atof & getopt */
50 # define EXIT_SUCCESS 0
53 # define EXIT_FAILURE 1
56 #define TIFFhowmany8(x) (((x)&0x07)?((uint32)(x)>>3)+1:(uint32)(x)>>3)
68 int copyFaxFile(TIFF* tifin, TIFF* tifout);
69 static void usage(void);
72 main(int argc, char* argv[])
76 TIFFErrorHandler whandler = NULL;
77 int compression_in = COMPRESSION_CCITTFAX3;
78 int compression_out = COMPRESSION_CCITTFAX3;
79 int fillorder_in = FILLORDER_LSB2MSB;
80 int fillorder_out = FILLORDER_LSB2MSB;
81 uint32 group3options_in = 0; /* 1d-encoded */
82 uint32 group3options_out = 0; /* 1d-encoded */
83 uint32 group4options_in = 0; /* compressed */
84 uint32 group4options_out = 0; /* compressed */
85 uint32 defrowsperstrip = (uint32) 0;
87 int photometric_in = PHOTOMETRIC_MINISWHITE;
88 int photometric_out = PHOTOMETRIC_MINISWHITE;
89 int mode = FAXMODE_CLASSF;
98 while ((c = getopt(argc, argv, "R:X:o:1234ABLMPUW5678abcflmprsuvwz?")) != -1)
100 /* input-related options */
101 case '3': /* input is g3-encoded */
102 compression_in = COMPRESSION_CCITTFAX3;
104 case '4': /* input is g4-encoded */
105 compression_in = COMPRESSION_CCITTFAX4;
107 case 'U': /* input is uncompressed (g3 and g4) */
108 group3options_in |= GROUP3OPT_UNCOMPRESSED;
109 group4options_in |= GROUP4OPT_UNCOMPRESSED;
111 case '1': /* input is 1d-encoded (g3 only) */
112 group3options_in &= ~GROUP3OPT_2DENCODING;
114 case '2': /* input is 2d-encoded (g3 only) */
115 group3options_in |= GROUP3OPT_2DENCODING;
117 case 'P': /* input has not-aligned EOL (g3 only) */
118 group3options_in &= ~GROUP3OPT_FILLBITS;
120 case 'A': /* input has aligned EOL (g3 only) */
121 group3options_in |= GROUP3OPT_FILLBITS;
123 case 'W': /* input has 0 mean white */
124 photometric_in = PHOTOMETRIC_MINISWHITE;
126 case 'B': /* input has 0 mean black */
127 photometric_in = PHOTOMETRIC_MINISBLACK;
129 case 'L': /* input has lsb-to-msb fillorder */
130 fillorder_in = FILLORDER_LSB2MSB;
132 case 'M': /* input has msb-to-lsb fillorder */
133 fillorder_in = FILLORDER_MSB2LSB;
135 case 'R': /* input resolution */
136 resY = (float) atof(optarg);
138 case 'X': /* input width */
139 xsize = (uint32) atoi(optarg);
142 /* output-related options */
143 case '7': /* generate g3-encoded output */
144 compression_out = COMPRESSION_CCITTFAX3;
146 case '8': /* generate g4-encoded output */
147 compression_out = COMPRESSION_CCITTFAX4;
149 case 'u': /* generate uncompressed output (g3 and g4) */
150 group3options_out |= GROUP3OPT_UNCOMPRESSED;
151 group4options_out |= GROUP4OPT_UNCOMPRESSED;
153 case '5': /* generate 1d-encoded output (g3 only) */
154 group3options_out &= ~GROUP3OPT_2DENCODING;
156 case '6': /* generate 2d-encoded output (g3 only) */
157 group3options_out |= GROUP3OPT_2DENCODING;
159 case 'c': /* generate "classic" g3 format */
160 mode = FAXMODE_CLASSIC;
162 case 'f': /* generate Class F format */
163 mode = FAXMODE_CLASSF;
165 case 'm': /* output's fillorder is msb-to-lsb */
166 fillorder_out = FILLORDER_MSB2LSB;
168 case 'l': /* output's fillorder is lsb-to-msb */
169 fillorder_out = FILLORDER_LSB2MSB;
172 out = TIFFOpen(optarg, "w");
175 "%s: Can not create or open %s\n",
180 case 'a': /* generate EOL-aligned output (g3 only) */
181 group3options_out |= GROUP3OPT_FILLBITS;
183 case 'p': /* generate not EOL-aligned output (g3 only) */
184 group3options_out &= ~GROUP3OPT_FILLBITS;
186 case 'r': /* rows/strip */
187 defrowsperstrip = atol(optarg);
189 case 's': /* stretch image by dup'ng scanlines */
192 case 'w': /* undocumented -- for testing */
193 photometric_out = PHOTOMETRIC_MINISWHITE;
195 case 'b': /* undocumented -- for testing */
196 photometric_out = PHOTOMETRIC_MINISBLACK;
198 case 'z': /* undocumented -- for testing */
199 compression_out = COMPRESSION_LZW;
201 case 'v': /* -v for info */
208 npages = argc - optind;
212 rowbuf = _TIFFmalloc(TIFFhowmany8(xsize));
213 refbuf = _TIFFmalloc(TIFFhowmany8(xsize));
214 if (rowbuf == NULL || refbuf == NULL) {
215 fprintf(stderr, "%s: Not enough memory\n", argv[0]);
216 return (EXIT_FAILURE);
220 out = TIFFOpen("fax.tif", "w");
222 fprintf(stderr, "%s: Can not create fax.tif\n",
224 return (EXIT_FAILURE);
228 faxTIFF = TIFFClientOpen("(FakeInput)", "w",
229 /* TIFFClientOpen() fails if we don't set existing value here */
231 TIFFGetReadProc(out), TIFFGetWriteProc(out),
232 TIFFGetSeekProc(out), TIFFGetCloseProc(out),
233 TIFFGetSizeProc(out), TIFFGetMapFileProc(out),
234 TIFFGetUnmapFileProc(out));
235 if (faxTIFF == NULL) {
236 fprintf(stderr, "%s: Can not create fake input file\n",
238 return (EXIT_FAILURE);
240 TIFFSetMode(faxTIFF, O_RDONLY);
241 TIFFSetField(faxTIFF, TIFFTAG_IMAGEWIDTH, xsize);
242 TIFFSetField(faxTIFF, TIFFTAG_SAMPLESPERPIXEL, 1);
243 TIFFSetField(faxTIFF, TIFFTAG_BITSPERSAMPLE, 1);
244 TIFFSetField(faxTIFF, TIFFTAG_FILLORDER, fillorder_in);
245 TIFFSetField(faxTIFF, TIFFTAG_PLANARCONFIG, PLANARCONFIG_CONTIG);
246 TIFFSetField(faxTIFF, TIFFTAG_PHOTOMETRIC, photometric_in);
247 TIFFSetField(faxTIFF, TIFFTAG_YRESOLUTION, resY);
248 TIFFSetField(faxTIFF, TIFFTAG_RESOLUTIONUNIT, RESUNIT_INCH);
250 /* NB: this must be done after directory info is setup */
251 TIFFSetField(faxTIFF, TIFFTAG_COMPRESSION, compression_in);
252 if (compression_in == COMPRESSION_CCITTFAX3)
253 TIFFSetField(faxTIFF, TIFFTAG_GROUP3OPTIONS, group3options_in);
254 else if (compression_in == COMPRESSION_CCITTFAX4)
255 TIFFSetField(faxTIFF, TIFFTAG_GROUP4OPTIONS, group4options_in);
256 for (pn = 0; optind < argc; pn++, optind++) {
257 in = fopen(argv[optind], "rb");
260 "%s: %s: Can not open\n", argv[0], argv[optind]);
263 #if defined(_WIN32) && defined(USE_WIN32_FILEIO)
264 TIFFSetClientdata(faxTIFF, (thandle_t)_get_osfhandle(fileno(in)));
266 TIFFSetClientdata(faxTIFF, (thandle_t)fileno(in));
268 TIFFSetFileName(faxTIFF, (const char*)argv[optind]);
269 TIFFSetField(out, TIFFTAG_IMAGEWIDTH, xsize);
270 TIFFSetField(out, TIFFTAG_BITSPERSAMPLE, 1);
271 TIFFSetField(out, TIFFTAG_COMPRESSION, compression_out);
272 TIFFSetField(out, TIFFTAG_PHOTOMETRIC, photometric_out);
273 TIFFSetField(out, TIFFTAG_ORIENTATION, ORIENTATION_TOPLEFT);
274 TIFFSetField(out, TIFFTAG_SAMPLESPERPIXEL, 1);
275 switch (compression_out) {
277 case COMPRESSION_CCITTFAX3:
278 TIFFSetField(out, TIFFTAG_GROUP3OPTIONS,
280 TIFFSetField(out, TIFFTAG_FAXMODE, mode);
282 (defrowsperstrip)?defrowsperstrip:(uint32)-1L;
286 case COMPRESSION_CCITTFAX4:
287 TIFFSetField(out, TIFFTAG_GROUP4OPTIONS,
289 TIFFSetField(out, TIFFTAG_FAXMODE, mode);
291 (defrowsperstrip)?defrowsperstrip:(uint32)-1L;
295 rowsperstrip = (defrowsperstrip) ?
296 defrowsperstrip : TIFFDefaultStripSize(out, 0);
298 TIFFSetField(out, TIFFTAG_ROWSPERSTRIP, rowsperstrip);
299 TIFFSetField(out, TIFFTAG_PLANARCONFIG, PLANARCONFIG_CONTIG);
300 TIFFSetField(out, TIFFTAG_FILLORDER, fillorder_out);
301 TIFFSetField(out, TIFFTAG_SOFTWARE, "fax2tiff");
302 TIFFSetField(out, TIFFTAG_XRESOLUTION, 204.0);
304 TIFFGetField(faxTIFF, TIFFTAG_YRESOLUTION, &resY);
305 TIFFSetField(out, TIFFTAG_YRESOLUTION, resY);
307 TIFFSetField(out, TIFFTAG_YRESOLUTION, 196.);
308 TIFFSetField(out, TIFFTAG_RESOLUTIONUNIT, RESUNIT_INCH);
309 TIFFSetField(out, TIFFTAG_PAGENUMBER, pn, npages);
312 whandler = TIFFSetWarningHandler(NULL);
313 rows = copyFaxFile(faxTIFF, out);
316 (void) TIFFSetWarningHandler(whandler);
318 TIFFSetField(out, TIFFTAG_IMAGELENGTH, rows);
321 fprintf(stderr, "%s:\n", argv[optind]);
322 fprintf(stderr, "%d rows in input\n", rows);
323 fprintf(stderr, "%ld total bad rows\n",
325 fprintf(stderr, "%d max consecutive bad rows\n", badfaxrun);
327 if (compression_out == COMPRESSION_CCITTFAX3 &&
328 mode == FAXMODE_CLASSF) {
329 TIFFSetField(out, TIFFTAG_BADFAXLINES, badfaxlines);
330 TIFFSetField(out, TIFFTAG_CLEANFAXDATA, badfaxlines ?
331 CLEANFAXDATA_REGENERATED : CLEANFAXDATA_CLEAN);
332 TIFFSetField(out, TIFFTAG_CONSECUTIVEBADFAXLINES, badfaxrun);
334 TIFFWriteDirectory(out);
339 return (EXIT_SUCCESS);
343 copyFaxFile(TIFF* tifin, TIFF* tifout)
346 uint32 linesize = TIFFhowmany8(xsize);
350 tifin->tif_rawdatasize = TIFFGetFileSize(tifin);
351 tifin->tif_rawdata = _TIFFmalloc(tifin->tif_rawdatasize);
352 if (tifin->tif_rawdata == NULL) {
353 TIFFError(tifin->tif_name, "Not enough memory");
356 if (!ReadOK(tifin, tifin->tif_rawdata, tifin->tif_rawdatasize)) {
357 TIFFError(tifin->tif_name, "Read error at scanline 0");
360 tifin->tif_rawcp = tifin->tif_rawdata;
361 tifin->tif_rawcc = tifin->tif_rawdatasize;
363 (*tifin->tif_setupdecode)(tifin);
364 (*tifin->tif_predecode)(tifin, (tsample_t) 0);
369 _TIFFmemset(refbuf, 0, linesize);
371 badrun = 0; /* current run of bad lines */
372 while (tifin->tif_rawcc > 0) {
373 ok = (*tifin->tif_decoderow)(tifin, (tdata_t) rowbuf,
378 /* regenerate line from previous good line */
379 _TIFFmemcpy(rowbuf, refbuf, linesize);
381 if (badrun > badfaxrun)
384 _TIFFmemcpy(refbuf, rowbuf, linesize);
388 if (TIFFWriteScanline(tifout, rowbuf, row, 0) < 0) {
389 fprintf(stderr, "%s: Write error at row %ld.\n",
390 tifout->tif_name, (long) row);
395 if (TIFFWriteScanline(tifout, rowbuf, row, 0) < 0) {
396 fprintf(stderr, "%s: Write error at row %ld.\n",
397 tifout->tif_name, (long) row);
403 if (badrun > badfaxrun)
405 _TIFFfree(tifin->tif_rawdata);
410 "usage: fax2tiff [options] input.raw...",
411 "where options are:",
412 " -3 input data is G3-encoded [default]",
413 " -4 input data is G4-encoded",
414 " -U input data is uncompressed (G3 or G4)",
415 " -1 input data is 1D-encoded (G3 only) [default]",
416 " -2 input data is 2D-encoded (G3 only)",
417 " -P input is not EOL-aligned (G3 only) [default]",
418 " -A input is EOL-aligned (G3 only)",
419 " -M input data has MSB2LSB bit order",
420 " -L input data has LSB2MSB bit order [default]",
421 " -B input data has min 0 means black",
422 " -W input data has min 0 means white [default]",
423 " -R # input data has # resolution (lines/inch) [default is 196]",
424 " -X # input data has # width [default is 1728]",
426 " -o out.tif write output to out.tif",
427 " -7 generate G3-encoded output [default]",
428 " -8 generate G4-encoded output",
429 " -u generate uncompressed output (G3 or G4)",
430 " -5 generate 1D-encoded output (G3 only)",
431 " -6 generate 2D-encoded output (G3 only) [default]",
432 " -p generate not EOL-aligned output (G3 only)",
433 " -a generate EOL-aligned output (G3 only) [default]",
434 " -c generate \"classic\" TIFF format",
435 " -f generate TIFF Class F (TIFF/F) format [default]",
436 " -m output fill order is MSB2LSB",
437 " -l output fill order is LSB2MSB [default]",
438 " -r # make each strip have no more than # rows",
439 " -s stretch image by duplicating scanlines",
440 " -v print information about conversion work",
441 " -z generate LZW compressed output",
452 fprintf(stderr, "%s\n\n", TIFFGetVersion());
453 for (i = 0; stuff[i] != NULL; i++)
454 fprintf(stderr, "%s\n", stuff[i]);
458 /* vim: set ts=8 sts=8 sw=8 noet: */