"Initial commit to Gerrit"
[profile/ivi/libtiff.git] / tools / fax2tiff.c
1 /* $Id: fax2tiff.c,v 1.19.2.1 2010-06-08 18:50:43 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 #include "tiffiop.h"
48
49 #ifndef EXIT_SUCCESS
50 # define EXIT_SUCCESS   0
51 #endif
52 #ifndef EXIT_FAILURE
53 # define EXIT_FAILURE   1
54 #endif
55
56 #define TIFFhowmany8(x) (((x)&0x07)?((uint32)(x)>>3)+1:(uint32)(x)>>3)
57
58 TIFF    *faxTIFF;
59 char    *rowbuf;
60 char    *refbuf;
61
62 uint32  xsize = 1728;
63 int     verbose;
64 int     stretch;
65 uint16  badfaxrun;
66 uint32  badfaxlines;
67
68 int     copyFaxFile(TIFF* tifin, TIFF* tifout);
69 static  void usage(void);
70
71 int
72 main(int argc, char* argv[])
73 {
74         FILE *in;
75         TIFF *out = NULL;
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;
86         uint32 rowsperstrip;
87         int photometric_in = PHOTOMETRIC_MINISWHITE;
88         int photometric_out = PHOTOMETRIC_MINISWHITE;
89         int mode = FAXMODE_CLASSF;
90         int rows;
91         int c;
92         int pn, npages;
93         float resY = 196.0;
94         extern int optind;
95         extern char* optarg;
96
97
98         while ((c = getopt(argc, argv, "R:X:o:1234ABLMPUW5678abcflmprsuvwz?")) != -1)
99                 switch (c) {
100                         /* input-related options */
101                 case '3':               /* input is g3-encoded */
102                         compression_in = COMPRESSION_CCITTFAX3;
103                         break;
104                 case '4':               /* input is g4-encoded */
105                         compression_in = COMPRESSION_CCITTFAX4;
106                         break;
107                 case 'U':               /* input is uncompressed (g3 and g4) */
108                         group3options_in |= GROUP3OPT_UNCOMPRESSED;
109                         group4options_in |= GROUP4OPT_UNCOMPRESSED;
110                         break;
111                 case '1':               /* input is 1d-encoded (g3 only) */
112                         group3options_in &= ~GROUP3OPT_2DENCODING;
113                         break;
114                 case '2':               /* input is 2d-encoded (g3 only) */
115                         group3options_in |= GROUP3OPT_2DENCODING;
116                         break;
117                 case 'P':       /* input has not-aligned EOL (g3 only) */
118                         group3options_in &= ~GROUP3OPT_FILLBITS;
119                         break;
120                 case 'A':               /* input has aligned EOL (g3 only) */
121                         group3options_in |= GROUP3OPT_FILLBITS;
122                         break;
123                 case 'W':               /* input has 0 mean white */
124                         photometric_in = PHOTOMETRIC_MINISWHITE;
125                         break;
126                 case 'B':               /* input has 0 mean black */
127                         photometric_in = PHOTOMETRIC_MINISBLACK;
128                         break;
129                 case 'L':               /* input has lsb-to-msb fillorder */
130                         fillorder_in = FILLORDER_LSB2MSB;
131                         break;
132                 case 'M':               /* input has msb-to-lsb fillorder */
133                         fillorder_in = FILLORDER_MSB2LSB;
134                         break;
135                 case 'R':               /* input resolution */
136                         resY = (float) atof(optarg);
137                         break;
138                 case 'X':               /* input width */
139                         xsize = (uint32) atoi(optarg);
140                         break;
141
142                         /* output-related options */
143                 case '7':               /* generate g3-encoded output */
144                         compression_out = COMPRESSION_CCITTFAX3;
145                         break;
146                 case '8':               /* generate g4-encoded output */
147                         compression_out = COMPRESSION_CCITTFAX4;
148                         break;
149                 case 'u':       /* generate uncompressed output (g3 and g4) */
150                         group3options_out |= GROUP3OPT_UNCOMPRESSED;
151                         group4options_out |= GROUP4OPT_UNCOMPRESSED;
152                         break;
153                 case '5':       /* generate 1d-encoded output (g3 only) */
154                         group3options_out &= ~GROUP3OPT_2DENCODING;
155                         break;
156                 case '6':       /* generate 2d-encoded output (g3 only) */
157                         group3options_out |= GROUP3OPT_2DENCODING;
158                         break;
159                 case 'c':               /* generate "classic" g3 format */
160                         mode = FAXMODE_CLASSIC;
161                         break;
162                 case 'f':               /* generate Class F format */
163                         mode = FAXMODE_CLASSF;
164                         break;
165                 case 'm':               /* output's fillorder is msb-to-lsb */
166                         fillorder_out = FILLORDER_MSB2LSB;
167                         break;
168                 case 'l':               /* output's fillorder is lsb-to-msb */
169                         fillorder_out = FILLORDER_LSB2MSB;
170                         break;
171                 case 'o':
172                         out = TIFFOpen(optarg, "w");
173                         if (out == NULL) {
174                                 fprintf(stderr,
175                                     "%s: Can not create or open %s\n",
176                                     argv[0], optarg);
177                                 return EXIT_FAILURE;
178                         }
179                         break;
180                 case 'a':       /* generate EOL-aligned output (g3 only) */
181                         group3options_out |= GROUP3OPT_FILLBITS;
182                         break;
183                 case 'p':       /* generate not EOL-aligned output (g3 only) */
184                         group3options_out &= ~GROUP3OPT_FILLBITS;
185                         break;
186                 case 'r':               /* rows/strip */
187                         defrowsperstrip = atol(optarg);
188                         break;
189                 case 's':               /* stretch image by dup'ng scanlines */
190                         stretch = 1;
191                         break;
192                 case 'w':               /* undocumented -- for testing */
193                         photometric_out = PHOTOMETRIC_MINISWHITE;
194                         break;
195                 case 'b':               /* undocumented -- for testing */
196                         photometric_out = PHOTOMETRIC_MINISBLACK;
197                         break;
198                 case 'z':               /* undocumented -- for testing */
199                         compression_out = COMPRESSION_LZW;
200                         break;
201                 case 'v':               /* -v for info */
202                         verbose++;
203                         break;
204                 case '?':
205                         usage();
206                         /*NOTREACHED*/
207                 }
208         npages = argc - optind;
209         if (npages < 1)
210                 usage();
211
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);
217         }
218
219         if (out == NULL) {
220                 out = TIFFOpen("fax.tif", "w");
221                 if (out == NULL) {
222                         fprintf(stderr, "%s: Can not create fax.tif\n",
223                             argv[0]);
224                         return (EXIT_FAILURE);
225                 }
226         }
227                 
228         faxTIFF = TIFFClientOpen("(FakeInput)", "w",
229         /* TIFFClientOpen() fails if we don't set existing value here */
230                                  TIFFClientdata(out),
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",
237                     argv[0]);
238                 return (EXIT_FAILURE);
239         }
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);
249         
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");
258                 if (in == NULL) {
259                         fprintf(stderr,
260                             "%s: %s: Can not open\n", argv[0], argv[optind]);
261                         continue;
262                 }
263 #if defined(_WIN32) && defined(USE_WIN32_FILEIO)
264                 TIFFSetClientdata(faxTIFF, (thandle_t)_get_osfhandle(fileno(in)));
265 #else
266                 TIFFSetClientdata(faxTIFF, (thandle_t)fileno(in));
267 #endif
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) {
276                         /* g3 */
277                         case COMPRESSION_CCITTFAX3:
278                         TIFFSetField(out, TIFFTAG_GROUP3OPTIONS,
279                                      group3options_out);
280                         TIFFSetField(out, TIFFTAG_FAXMODE, mode);
281                         rowsperstrip =
282                                 (defrowsperstrip)?defrowsperstrip:(uint32)-1L;
283                         break;
284
285                         /* g4 */
286                         case COMPRESSION_CCITTFAX4:
287                         TIFFSetField(out, TIFFTAG_GROUP4OPTIONS,
288                                      group4options_out);
289                         TIFFSetField(out, TIFFTAG_FAXMODE, mode);
290                         rowsperstrip =
291                                 (defrowsperstrip)?defrowsperstrip:(uint32)-1L;
292                         break;
293
294                         default:
295                         rowsperstrip = (defrowsperstrip) ?
296                                 defrowsperstrip : TIFFDefaultStripSize(out, 0);
297                 }
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);
303                 if (!stretch) {
304                         TIFFGetField(faxTIFF, TIFFTAG_YRESOLUTION, &resY);
305                         TIFFSetField(out, TIFFTAG_YRESOLUTION, resY);
306                 } else
307                         TIFFSetField(out, TIFFTAG_YRESOLUTION, 196.);
308                 TIFFSetField(out, TIFFTAG_RESOLUTIONUNIT, RESUNIT_INCH);
309                 TIFFSetField(out, TIFFTAG_PAGENUMBER, pn, npages);
310
311                 if (!verbose)
312                     whandler = TIFFSetWarningHandler(NULL);
313                 rows = copyFaxFile(faxTIFF, out);
314                 fclose(in);
315                 if (!verbose)
316                     (void) TIFFSetWarningHandler(whandler);
317
318                 TIFFSetField(out, TIFFTAG_IMAGELENGTH, rows);
319
320                 if (verbose) {
321                         fprintf(stderr, "%s:\n", argv[optind]);
322                         fprintf(stderr, "%d rows in input\n", rows);
323                         fprintf(stderr, "%ld total bad rows\n",
324                             (long) badfaxlines);
325                         fprintf(stderr, "%d max consecutive bad rows\n", badfaxrun);
326                 }
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);
333                 }
334                 TIFFWriteDirectory(out);
335         }
336         TIFFClose(out);
337         _TIFFfree(rowbuf);
338         _TIFFfree(refbuf);
339         return (EXIT_SUCCESS);
340 }
341
342 int
343 copyFaxFile(TIFF* tifin, TIFF* tifout)
344 {
345         uint32 row;
346         uint32 linesize = TIFFhowmany8(xsize);
347         uint16 badrun;
348         int ok;
349
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");
354                 return (0);
355         }
356         if (!ReadOK(tifin, tifin->tif_rawdata, tifin->tif_rawdatasize)) {
357                 TIFFError(tifin->tif_name, "Read error at scanline 0");
358                 return (0);
359         }
360         tifin->tif_rawcp = tifin->tif_rawdata;
361         tifin->tif_rawcc = tifin->tif_rawdatasize;
362
363         (*tifin->tif_setupdecode)(tifin);
364         (*tifin->tif_predecode)(tifin, (tsample_t) 0);
365         tifin->tif_row = 0;
366         badfaxlines = 0;
367         badfaxrun = 0;
368
369         _TIFFmemset(refbuf, 0, linesize);
370         row = 0;
371         badrun = 0;             /* current run of bad lines */
372         while (tifin->tif_rawcc > 0) {
373                 ok = (*tifin->tif_decoderow)(tifin, (tdata_t) rowbuf, 
374                                              linesize, 0);
375                 if (!ok) {
376                         badfaxlines++;
377                         badrun++;
378                         /* regenerate line from previous good line */
379                         _TIFFmemcpy(rowbuf, refbuf, linesize);
380                 } else {
381                         if (badrun > badfaxrun)
382                                 badfaxrun = badrun;
383                         badrun = 0;
384                         _TIFFmemcpy(refbuf, rowbuf, linesize);
385                 }
386                 tifin->tif_row++;
387
388                 if (TIFFWriteScanline(tifout, rowbuf, row, 0) < 0) {
389                         fprintf(stderr, "%s: Write error at row %ld.\n",
390                             tifout->tif_name, (long) row);
391                         break;
392                 }
393                 row++;
394                 if (stretch) {
395                         if (TIFFWriteScanline(tifout, rowbuf, row, 0) < 0) {
396                                 fprintf(stderr, "%s: Write error at row %ld.\n",
397                                     tifout->tif_name, (long) row);
398                                 break;
399                         }
400                         row++;
401                 }
402         }
403         if (badrun > badfaxrun)
404                 badfaxrun = badrun;
405         _TIFFfree(tifin->tif_rawdata);
406         return (row);
407 }
408
409 char* stuff[] = {
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]",
425 "",
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",
442 NULL
443 };
444
445 static void
446 usage(void)
447 {
448         char buf[BUFSIZ];
449         int i;
450
451         setbuf(stderr, buf);
452         fprintf(stderr, "%s\n\n", TIFFGetVersion());
453         for (i = 0; stuff[i] != NULL; i++)
454                 fprintf(stderr, "%s\n", stuff[i]);
455         exit(EXIT_FAILURE);
456 }
457
458 /* vim: set ts=8 sts=8 sw=8 noet: */
459 /*
460  * Local Variables:
461  * mode: c
462  * c-basic-offset: 8
463  * fill-column: 78
464  * End:
465  */