e00de5277bc2a4a8aa35fc80699b45bd49de16b1
[platform/upstream/tiff.git] / tools / fax2tiff.c
1 /* $Id: fax2tiff.c,v 1.25 2016-10-25 22:22:45 erouault 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
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
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         tifin->tif_rawdata = _TIFFmalloc(tifin->tif_rawdatasize);
374         if (tifin->tif_rawdata == NULL) {
375                 TIFFError(tifin->tif_name, "Not enough memory");
376                 return (0);
377         }
378         if (!ReadOK(tifin, tifin->tif_rawdata, tifin->tif_rawdatasize)) {
379                 TIFFError(tifin->tif_name, "Read error at scanline 0");
380                 return (0);
381         }
382         tifin->tif_rawcp = tifin->tif_rawdata;
383         tifin->tif_rawcc = tifin->tif_rawdatasize;
384
385         (*tifin->tif_setupdecode)(tifin);
386         (*tifin->tif_predecode)(tifin, (tsample_t) 0);
387         tifin->tif_row = 0;
388         badfaxlines = 0;
389         badfaxrun = 0;
390
391         _TIFFmemset(refbuf, 0, linesize);
392         row = 0;
393         badrun = 0;             /* current run of bad lines */
394         while (tifin->tif_rawcc > 0) {
395                 ok = (*tifin->tif_decoderow)(tifin, (tdata_t) rowbuf, 
396                                              linesize, 0);
397                 if (!ok) {
398                         badfaxlines++;
399                         badrun++;
400                         /* regenerate line from previous good line */
401                         _TIFFmemcpy(rowbuf, refbuf, linesize);
402                 } else {
403                         if (badrun > badfaxrun)
404                                 badfaxrun = badrun;
405                         badrun = 0;
406                         _TIFFmemcpy(refbuf, rowbuf, linesize);
407                 }
408                 tifin->tif_row++;
409
410                 if (TIFFWriteScanline(tifout, rowbuf, row, 0) < 0) {
411                         fprintf(stderr, "%s: Write error at row %ld.\n",
412                             tifout->tif_name, (long) row);
413                         break;
414                 }
415                 row++;
416                 if (stretch) {
417                         if (TIFFWriteScanline(tifout, rowbuf, row, 0) < 0) {
418                                 fprintf(stderr, "%s: Write error at row %ld.\n",
419                                     tifout->tif_name, (long) row);
420                                 break;
421                         }
422                         row++;
423                 }
424         }
425         if (badrun > badfaxrun)
426                 badfaxrun = badrun;
427         _TIFFfree(tifin->tif_rawdata);
428         return (row);
429 }
430
431 char* stuff[] = {
432 "usage: fax2tiff [options] input.raw...",
433 "where options are:",
434 " -3            input data is G3-encoded                [default]",
435 " -4            input data is G4-encoded",
436 " -U            input data is uncompressed (G3 or G4)",
437 " -1            input data is 1D-encoded (G3 only)      [default]",
438 " -2            input data is 2D-encoded (G3 only)",
439 " -P            input is not EOL-aligned (G3 only)      [default]",
440 " -A            input is EOL-aligned (G3 only)",
441 " -M            input data has MSB2LSB bit order",
442 " -L            input data has LSB2MSB bit order        [default]",
443 " -B            input data has min 0 means black",
444 " -W            input data has min 0 means white        [default]",
445 " -R #          input data has # resolution (lines/inch) [default is 196]",
446 " -X #          input data has # width                  [default is 1728]",
447 "",
448 " -o out.tif    write output to out.tif",
449 " -7            generate G3-encoded output              [default]",
450 " -8            generate G4-encoded output",
451 " -u            generate uncompressed output (G3 or G4)",
452 " -5            generate 1D-encoded output (G3 only)",
453 " -6            generate 2D-encoded output (G3 only)    [default]",
454 " -p            generate not EOL-aligned output (G3 only)",
455 " -a            generate EOL-aligned output (G3 only)   [default]",
456 " -c            generate \"classic\" TIFF format",
457 " -f            generate TIFF Class F (TIFF/F) format   [default]",
458 " -m            output fill order is MSB2LSB",
459 " -l            output fill order is LSB2MSB            [default]",
460 " -r #          make each strip have no more than # rows",
461 " -s            stretch image by duplicating scanlines",
462 " -v            print information about conversion work",
463 " -z            generate LZW compressed output",
464 NULL
465 };
466
467 static void
468 usage(void)
469 {
470         char buf[BUFSIZ];
471         int i;
472
473         setbuf(stderr, buf);
474         fprintf(stderr, "%s\n\n", TIFFGetVersion());
475         for (i = 0; stuff[i] != NULL; i++)
476                 fprintf(stderr, "%s\n", stuff[i]);
477         exit(EXIT_FAILURE);
478 }
479
480 /* vim: set ts=8 sts=8 sw=8 noet: */
481 /*
482  * Local Variables:
483  * mode: c
484  * c-basic-offset: 8
485  * fill-column: 78
486  * End:
487  */