]> git.saurik.com Git - wxWidgets.git/blob - src/tiff/tools/fax2tiff.c
modification to fix in r70597, has-window check is unnecessary and perhaps incorrect
[wxWidgets.git] / src / tiff / tools / fax2tiff.c
1 /* $Id$ */
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 #include "tiffiop.h"
48
49 #ifndef BINMODE
50 # define BINMODE
51 #endif
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 int
76 main(int argc, char* argv[])
77 {
78 FILE *in;
79 TIFF *out = NULL;
80 TIFFErrorHandler whandler = NULL;
81 int compression_in = COMPRESSION_CCITTFAX3;
82 int compression_out = COMPRESSION_CCITTFAX3;
83 int fillorder_in = FILLORDER_LSB2MSB;
84 int fillorder_out = FILLORDER_LSB2MSB;
85 uint32 group3options_in = 0; /* 1d-encoded */
86 uint32 group3options_out = 0; /* 1d-encoded */
87 uint32 group4options_in = 0; /* compressed */
88 uint32 group4options_out = 0; /* compressed */
89 uint32 defrowsperstrip = (uint32) 0;
90 uint32 rowsperstrip;
91 int photometric_in = PHOTOMETRIC_MINISWHITE;
92 int photometric_out = PHOTOMETRIC_MINISWHITE;
93 int mode = FAXMODE_CLASSF;
94 int rows;
95 int c;
96 int pn, npages;
97 float resY = 196.0;
98 extern int optind;
99 extern char* optarg;
100
101
102 while ((c = getopt(argc, argv, "R:X:o:1234ABLMPUW5678abcflmprsuvwz?")) != -1)
103 switch (c) {
104 /* input-related options */
105 case '3': /* input is g3-encoded */
106 compression_in = COMPRESSION_CCITTFAX3;
107 break;
108 case '4': /* input is g4-encoded */
109 compression_in = COMPRESSION_CCITTFAX4;
110 break;
111 case 'U': /* input is uncompressed (g3 and g4) */
112 group3options_in |= GROUP3OPT_UNCOMPRESSED;
113 group4options_in |= GROUP4OPT_UNCOMPRESSED;
114 break;
115 case '1': /* input is 1d-encoded (g3 only) */
116 group3options_in &= ~GROUP3OPT_2DENCODING;
117 break;
118 case '2': /* input is 2d-encoded (g3 only) */
119 group3options_in |= GROUP3OPT_2DENCODING;
120 break;
121 case 'P': /* input has not-aligned EOL (g3 only) */
122 group3options_in &= ~GROUP3OPT_FILLBITS;
123 break;
124 case 'A': /* input has aligned EOL (g3 only) */
125 group3options_in |= GROUP3OPT_FILLBITS;
126 break;
127 case 'W': /* input has 0 mean white */
128 photometric_in = PHOTOMETRIC_MINISWHITE;
129 break;
130 case 'B': /* input has 0 mean black */
131 photometric_in = PHOTOMETRIC_MINISBLACK;
132 break;
133 case 'L': /* input has lsb-to-msb fillorder */
134 fillorder_in = FILLORDER_LSB2MSB;
135 break;
136 case 'M': /* input has msb-to-lsb fillorder */
137 fillorder_in = FILLORDER_MSB2LSB;
138 break;
139 case 'R': /* input resolution */
140 resY = (float) atof(optarg);
141 break;
142 case 'X': /* input width */
143 xsize = (uint32) atoi(optarg);
144 break;
145
146 /* output-related options */
147 case '7': /* generate g3-encoded output */
148 compression_out = COMPRESSION_CCITTFAX3;
149 break;
150 case '8': /* generate g4-encoded output */
151 compression_out = COMPRESSION_CCITTFAX4;
152 break;
153 case 'u': /* generate uncompressed output (g3 and g4) */
154 group3options_out |= GROUP3OPT_UNCOMPRESSED;
155 group4options_out |= GROUP4OPT_UNCOMPRESSED;
156 break;
157 case '5': /* generate 1d-encoded output (g3 only) */
158 group3options_out &= ~GROUP3OPT_2DENCODING;
159 break;
160 case '6': /* generate 2d-encoded output (g3 only) */
161 group3options_out |= GROUP3OPT_2DENCODING;
162 break;
163 case 'c': /* generate "classic" g3 format */
164 mode = FAXMODE_CLASSIC;
165 break;
166 case 'f': /* generate Class F format */
167 mode = FAXMODE_CLASSF;
168 break;
169 case 'm': /* output's fillorder is msb-to-lsb */
170 fillorder_out = FILLORDER_MSB2LSB;
171 break;
172 case 'l': /* output's fillorder is lsb-to-msb */
173 fillorder_out = FILLORDER_LSB2MSB;
174 break;
175 case 'o':
176 out = TIFFOpen(optarg, "w");
177 if (out == NULL) {
178 fprintf(stderr,
179 "%s: Can not create or open %s\n",
180 argv[0], optarg);
181 return EXIT_FAILURE;
182 }
183 break;
184 case 'a': /* generate EOL-aligned output (g3 only) */
185 group3options_out |= GROUP3OPT_FILLBITS;
186 break;
187 case 'p': /* generate not EOL-aligned output (g3 only) */
188 group3options_out &= ~GROUP3OPT_FILLBITS;
189 break;
190 case 'r': /* rows/strip */
191 defrowsperstrip = atol(optarg);
192 break;
193 case 's': /* stretch image by dup'ng scanlines */
194 stretch = 1;
195 break;
196 case 'w': /* undocumented -- for testing */
197 photometric_out = PHOTOMETRIC_MINISWHITE;
198 break;
199 case 'b': /* undocumented -- for testing */
200 photometric_out = PHOTOMETRIC_MINISBLACK;
201 break;
202 case 'z': /* undocumented -- for testing */
203 compression_out = COMPRESSION_LZW;
204 break;
205 case 'v': /* -v for info */
206 verbose++;
207 break;
208 case '?':
209 usage();
210 /*NOTREACHED*/
211 }
212 npages = argc - optind;
213 if (npages < 1)
214 usage();
215
216 rowbuf = _TIFFmalloc(TIFFhowmany8(xsize));
217 refbuf = _TIFFmalloc(TIFFhowmany8(xsize));
218 if (rowbuf == NULL || refbuf == NULL) {
219 fprintf(stderr, "%s: Not enough memory\n", argv[0]);
220 return (EXIT_FAILURE);
221 }
222
223 if (out == NULL) {
224 out = TIFFOpen("fax.tif", "w");
225 if (out == NULL) {
226 fprintf(stderr, "%s: Can not create fax.tif\n",
227 argv[0]);
228 return (EXIT_FAILURE);
229 }
230 }
231
232 faxTIFF = TIFFClientOpen("(FakeInput)", "w",
233 /* TIFFClientOpen() fails if we don't set existing value here */
234 TIFFClientdata(out),
235 TIFFGetReadProc(out), TIFFGetWriteProc(out),
236 TIFFGetSeekProc(out), TIFFGetCloseProc(out),
237 TIFFGetSizeProc(out), TIFFGetMapFileProc(out),
238 TIFFGetUnmapFileProc(out));
239 if (faxTIFF == NULL) {
240 fprintf(stderr, "%s: Can not create fake input file\n",
241 argv[0]);
242 return (EXIT_FAILURE);
243 }
244 TIFFSetMode(faxTIFF, O_RDONLY);
245 TIFFSetField(faxTIFF, TIFFTAG_IMAGEWIDTH, xsize);
246 TIFFSetField(faxTIFF, TIFFTAG_SAMPLESPERPIXEL, 1);
247 TIFFSetField(faxTIFF, TIFFTAG_BITSPERSAMPLE, 1);
248 TIFFSetField(faxTIFF, TIFFTAG_FILLORDER, fillorder_in);
249 TIFFSetField(faxTIFF, TIFFTAG_PLANARCONFIG, PLANARCONFIG_CONTIG);
250 TIFFSetField(faxTIFF, TIFFTAG_PHOTOMETRIC, photometric_in);
251 TIFFSetField(faxTIFF, TIFFTAG_YRESOLUTION, resY);
252 TIFFSetField(faxTIFF, TIFFTAG_RESOLUTIONUNIT, RESUNIT_INCH);
253
254 /* NB: this must be done after directory info is setup */
255 TIFFSetField(faxTIFF, TIFFTAG_COMPRESSION, compression_in);
256 if (compression_in == COMPRESSION_CCITTFAX3)
257 TIFFSetField(faxTIFF, TIFFTAG_GROUP3OPTIONS, group3options_in);
258 else if (compression_in == COMPRESSION_CCITTFAX4)
259 TIFFSetField(faxTIFF, TIFFTAG_GROUP4OPTIONS, group4options_in);
260 for (pn = 0; optind < argc; pn++, optind++) {
261 in = fopen(argv[optind], "r" BINMODE);
262 if (in == NULL) {
263 fprintf(stderr,
264 "%s: %s: Can not open\n", argv[0], argv[optind]);
265 continue;
266 }
267 #if defined(_WIN32) && defined(USE_WIN32_FILEIO)
268 TIFFSetClientdata(faxTIFF, (thandle_t)_get_osfhandle(fileno(in)));
269 #else
270 TIFFSetClientdata(faxTIFF, (thandle_t)fileno(in));
271 #endif
272 TIFFSetFileName(faxTIFF, (const char*)argv[optind]);
273 TIFFSetField(out, TIFFTAG_IMAGEWIDTH, xsize);
274 TIFFSetField(out, TIFFTAG_BITSPERSAMPLE, 1);
275 TIFFSetField(out, TIFFTAG_COMPRESSION, compression_out);
276 TIFFSetField(out, TIFFTAG_PHOTOMETRIC, photometric_out);
277 TIFFSetField(out, TIFFTAG_ORIENTATION, ORIENTATION_TOPLEFT);
278 TIFFSetField(out, TIFFTAG_SAMPLESPERPIXEL, 1);
279 switch (compression_out) {
280 /* g3 */
281 case COMPRESSION_CCITTFAX3:
282 TIFFSetField(out, TIFFTAG_GROUP3OPTIONS,
283 group3options_out);
284 TIFFSetField(out, TIFFTAG_FAXMODE, mode);
285 rowsperstrip =
286 (defrowsperstrip)?defrowsperstrip:(uint32)-1L;
287 break;
288
289 /* g4 */
290 case COMPRESSION_CCITTFAX4:
291 TIFFSetField(out, TIFFTAG_GROUP4OPTIONS,
292 group4options_out);
293 TIFFSetField(out, TIFFTAG_FAXMODE, mode);
294 rowsperstrip =
295 (defrowsperstrip)?defrowsperstrip:(uint32)-1L;
296 break;
297
298 default:
299 rowsperstrip = (defrowsperstrip) ?
300 defrowsperstrip : TIFFDefaultStripSize(out, 0);
301 }
302 TIFFSetField(out, TIFFTAG_ROWSPERSTRIP, rowsperstrip);
303 TIFFSetField(out, TIFFTAG_PLANARCONFIG, PLANARCONFIG_CONTIG);
304 TIFFSetField(out, TIFFTAG_FILLORDER, fillorder_out);
305 TIFFSetField(out, TIFFTAG_SOFTWARE, "fax2tiff");
306 TIFFSetField(out, TIFFTAG_XRESOLUTION, 204.0);
307 if (!stretch) {
308 TIFFGetField(faxTIFF, TIFFTAG_YRESOLUTION, &resY);
309 TIFFSetField(out, TIFFTAG_YRESOLUTION, resY);
310 } else
311 TIFFSetField(out, TIFFTAG_YRESOLUTION, 196.);
312 TIFFSetField(out, TIFFTAG_RESOLUTIONUNIT, RESUNIT_INCH);
313 TIFFSetField(out, TIFFTAG_PAGENUMBER, pn, npages);
314
315 if (!verbose)
316 whandler = TIFFSetWarningHandler(NULL);
317 rows = copyFaxFile(faxTIFF, out);
318 fclose(in);
319 if (!verbose)
320 (void) TIFFSetWarningHandler(whandler);
321
322 TIFFSetField(out, TIFFTAG_IMAGELENGTH, rows);
323
324 if (verbose) {
325 fprintf(stderr, "%s:\n", argv[optind]);
326 fprintf(stderr, "%d rows in input\n", rows);
327 fprintf(stderr, "%ld total bad rows\n",
328 (long) badfaxlines);
329 fprintf(stderr, "%d max consecutive bad rows\n", badfaxrun);
330 }
331 if (compression_out == COMPRESSION_CCITTFAX3 &&
332 mode == FAXMODE_CLASSF) {
333 TIFFSetField(out, TIFFTAG_BADFAXLINES, badfaxlines);
334 TIFFSetField(out, TIFFTAG_CLEANFAXDATA, badfaxlines ?
335 CLEANFAXDATA_REGENERATED : CLEANFAXDATA_CLEAN);
336 TIFFSetField(out, TIFFTAG_CONSECUTIVEBADFAXLINES, badfaxrun);
337 }
338 TIFFWriteDirectory(out);
339 }
340 TIFFClose(out);
341 _TIFFfree(rowbuf);
342 _TIFFfree(refbuf);
343 return (EXIT_SUCCESS);
344 }
345
346 int
347 copyFaxFile(TIFF* tifin, TIFF* tifout)
348 {
349 uint32 row;
350 uint32 linesize = TIFFhowmany8(xsize);
351 uint16 badrun;
352 int ok;
353
354 tifin->tif_rawdatasize = TIFFGetFileSize(tifin);
355 tifin->tif_rawdata = _TIFFmalloc(tifin->tif_rawdatasize);
356 if (tifin->tif_rawdata == NULL) {
357 TIFFError(tifin->tif_name, "Not enough memory");
358 return (0);
359 }
360 if (!ReadOK(tifin, tifin->tif_rawdata, tifin->tif_rawdatasize)) {
361 TIFFError(tifin->tif_name, "Read error at scanline 0");
362 return (0);
363 }
364 tifin->tif_rawcp = tifin->tif_rawdata;
365 tifin->tif_rawcc = tifin->tif_rawdatasize;
366
367 (*tifin->tif_setupdecode)(tifin);
368 (*tifin->tif_predecode)(tifin, (tsample_t) 0);
369 tifin->tif_row = 0;
370 badfaxlines = 0;
371 badfaxrun = 0;
372
373 _TIFFmemset(refbuf, 0, linesize);
374 row = 0;
375 badrun = 0; /* current run of bad lines */
376 while (tifin->tif_rawcc > 0) {
377 ok = (*tifin->tif_decoderow)(tifin, (tdata_t) rowbuf,
378 linesize, 0);
379 if (!ok) {
380 badfaxlines++;
381 badrun++;
382 /* regenerate line from previous good line */
383 _TIFFmemcpy(rowbuf, refbuf, linesize);
384 } else {
385 if (badrun > badfaxrun)
386 badfaxrun = badrun;
387 badrun = 0;
388 _TIFFmemcpy(refbuf, rowbuf, linesize);
389 }
390 tifin->tif_row++;
391
392 if (TIFFWriteScanline(tifout, rowbuf, row, 0) < 0) {
393 fprintf(stderr, "%s: Write error at row %ld.\n",
394 tifout->tif_name, (long) row);
395 break;
396 }
397 row++;
398 if (stretch) {
399 if (TIFFWriteScanline(tifout, rowbuf, row, 0) < 0) {
400 fprintf(stderr, "%s: Write error at row %ld.\n",
401 tifout->tif_name, (long) row);
402 break;
403 }
404 row++;
405 }
406 }
407 if (badrun > badfaxrun)
408 badfaxrun = badrun;
409 _TIFFfree(tifin->tif_rawdata);
410 return (row);
411 }
412
413 char* stuff[] = {
414 "usage: fax2tiff [options] input.raw...",
415 "where options are:",
416 " -3 input data is G3-encoded [default]",
417 " -4 input data is G4-encoded",
418 " -U input data is uncompressed (G3 or G4)",
419 " -1 input data is 1D-encoded (G3 only) [default]",
420 " -2 input data is 2D-encoded (G3 only)",
421 " -P input is not EOL-aligned (G3 only) [default]",
422 " -A input is EOL-aligned (G3 only)",
423 " -M input data has MSB2LSB bit order",
424 " -L input data has LSB2MSB bit order [default]",
425 " -B input data has min 0 means black",
426 " -W input data has min 0 means white [default]",
427 " -R # input data has # resolution (lines/inch) [default is 196]",
428 " -X # input data has # width [default is 1728]",
429 "",
430 " -o out.tif write output to out.tif",
431 " -7 generate G3-encoded output [default]",
432 " -8 generate G4-encoded output",
433 " -u generate uncompressed output (G3 or G4)",
434 " -5 generate 1D-encoded output (G3 only)",
435 " -6 generate 2D-encoded output (G3 only) [default]",
436 " -p generate not EOL-aligned output (G3 only)",
437 " -a generate EOL-aligned output (G3 only) [default]",
438 " -c generate \"classic\" TIFF format",
439 " -f generate TIFF Class F (TIFF/F) format [default]",
440 " -m output fill order is MSB2LSB",
441 " -l output fill order is LSB2MSB [default]",
442 " -r # make each strip have no more than # rows",
443 " -s stretch image by duplicating scanlines",
444 " -v print information about conversion work",
445 " -z generate LZW compressed output",
446 NULL
447 };
448
449 static void
450 usage(void)
451 {
452 char buf[BUFSIZ];
453 int i;
454
455 setbuf(stderr, buf);
456 fprintf(stderr, "%s\n\n", TIFFGetVersion());
457 for (i = 0; stuff[i] != NULL; i++)
458 fprintf(stderr, "%s\n", stuff[i]);
459 exit(EXIT_FAILURE);
460 }
461
462 /* vim: set ts=8 sts=8 sw=8 noet: */