]> git.saurik.com Git - wxWidgets.git/blob - src/tiff/tools/fax2tiff.c
Remove all lines containing cvs/svn "$Id$" keyword.
[wxWidgets.git] / src / tiff / tools / fax2tiff.c
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
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
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
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 */