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 */ 
  54 # define EXIT_SUCCESS   0 
  57 # define EXIT_FAILURE   1 
  60 #define TIFFhowmany8(x) (((x)&0x07)?((uint32)(x)>>3)+1:(uint32)(x)>>3) 
  72 int     copyFaxFile(TIFF
* tifin
, TIFF
* tifout
); 
  73 static  void usage(void); 
  76 main(int argc
, char* argv
[]) 
  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; 
  91         int photometric_in 
= PHOTOMETRIC_MINISWHITE
; 
  92         int photometric_out 
= PHOTOMETRIC_MINISWHITE
; 
  93         int mode 
= FAXMODE_CLASSF
; 
 102         while ((c 
= getopt(argc
, argv
, "R:X:o:1234ABLMPUW5678abcflmprsuvwz?")) != -1) 
 104                         /* input-related options */ 
 105                 case '3':               /* input is g3-encoded */ 
 106                         compression_in 
= COMPRESSION_CCITTFAX3
; 
 108                 case '4':               /* input is g4-encoded */ 
 109                         compression_in 
= COMPRESSION_CCITTFAX4
; 
 111                 case 'U':               /* input is uncompressed (g3 and g4) */ 
 112                         group3options_in 
|= GROUP3OPT_UNCOMPRESSED
; 
 113                         group4options_in 
|= GROUP4OPT_UNCOMPRESSED
; 
 115                 case '1':               /* input is 1d-encoded (g3 only) */ 
 116                         group3options_in 
&= ~GROUP3OPT_2DENCODING
; 
 118                 case '2':               /* input is 2d-encoded (g3 only) */ 
 119                         group3options_in 
|= GROUP3OPT_2DENCODING
; 
 121                 case 'P':       /* input has not-aligned EOL (g3 only) */ 
 122                         group3options_in 
&= ~GROUP3OPT_FILLBITS
; 
 124                 case 'A':               /* input has aligned EOL (g3 only) */ 
 125                         group3options_in 
|= GROUP3OPT_FILLBITS
; 
 127                 case 'W':               /* input has 0 mean white */ 
 128                         photometric_in 
= PHOTOMETRIC_MINISWHITE
; 
 130                 case 'B':               /* input has 0 mean black */ 
 131                         photometric_in 
= PHOTOMETRIC_MINISBLACK
; 
 133                 case 'L':               /* input has lsb-to-msb fillorder */ 
 134                         fillorder_in 
= FILLORDER_LSB2MSB
; 
 136                 case 'M':               /* input has msb-to-lsb fillorder */ 
 137                         fillorder_in 
= FILLORDER_MSB2LSB
; 
 139                 case 'R':               /* input resolution */ 
 140                         resY 
= (float) atof(optarg
); 
 142                 case 'X':               /* input width */ 
 143                         xsize 
= (uint32
) atoi(optarg
); 
 146                         /* output-related options */ 
 147                 case '7':               /* generate g3-encoded output */ 
 148                         compression_out 
= COMPRESSION_CCITTFAX3
; 
 150                 case '8':               /* generate g4-encoded output */ 
 151                         compression_out 
= COMPRESSION_CCITTFAX4
; 
 153                 case 'u':       /* generate uncompressed output (g3 and g4) */ 
 154                         group3options_out 
|= GROUP3OPT_UNCOMPRESSED
; 
 155                         group4options_out 
|= GROUP4OPT_UNCOMPRESSED
; 
 157                 case '5':       /* generate 1d-encoded output (g3 only) */ 
 158                         group3options_out 
&= ~GROUP3OPT_2DENCODING
; 
 160                 case '6':       /* generate 2d-encoded output (g3 only) */ 
 161                         group3options_out 
|= GROUP3OPT_2DENCODING
; 
 163                 case 'c':               /* generate "classic" g3 format */ 
 164                         mode 
= FAXMODE_CLASSIC
; 
 166                 case 'f':               /* generate Class F format */ 
 167                         mode 
= FAXMODE_CLASSF
; 
 169                 case 'm':               /* output's fillorder is msb-to-lsb */ 
 170                         fillorder_out 
= FILLORDER_MSB2LSB
; 
 172                 case 'l':               /* output's fillorder is lsb-to-msb */ 
 173                         fillorder_out 
= FILLORDER_LSB2MSB
; 
 176                         out 
= TIFFOpen(optarg
, "w"); 
 179                                     "%s: Can not create or open %s\n", 
 184                 case 'a':       /* generate EOL-aligned output (g3 only) */ 
 185                         group3options_out 
|= GROUP3OPT_FILLBITS
; 
 187                 case 'p':       /* generate not EOL-aligned output (g3 only) */ 
 188                         group3options_out 
&= ~GROUP3OPT_FILLBITS
; 
 190                 case 'r':               /* rows/strip */ 
 191                         defrowsperstrip 
= atol(optarg
); 
 193                 case 's':               /* stretch image by dup'ng scanlines */ 
 196                 case 'w':               /* undocumented -- for testing */ 
 197                         photometric_out 
= PHOTOMETRIC_MINISWHITE
; 
 199                 case 'b':               /* undocumented -- for testing */ 
 200                         photometric_out 
= PHOTOMETRIC_MINISBLACK
; 
 202                 case 'z':               /* undocumented -- for testing */ 
 203                         compression_out 
= COMPRESSION_LZW
; 
 205                 case 'v':               /* -v for info */ 
 212         npages 
= argc 
- optind
; 
 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
); 
 224                 out 
= TIFFOpen("fax.tif", "w"); 
 226                         fprintf(stderr
, "%s: Can not create fax.tif\n", 
 228                         return (EXIT_FAILURE
); 
 232         faxTIFF 
= TIFFClientOpen("(FakeInput)", "w", 
 233         /* TIFFClientOpen() fails if we don't set existing value here */ 
 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", 
 242                 return (EXIT_FAILURE
); 
 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
); 
 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
); 
 264                             "%s: %s: Can not open\n", argv
[0], argv
[optind
]); 
 267 #if defined(_WIN32) && defined(USE_WIN32_FILEIO) 
 268                 TIFFSetClientdata(faxTIFF
, (thandle_t
)_get_osfhandle(fileno(in
))); 
 270                 TIFFSetClientdata(faxTIFF
, (thandle_t
)fileno(in
)); 
 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
) { 
 281                         case COMPRESSION_CCITTFAX3
: 
 282                         TIFFSetField(out
, TIFFTAG_GROUP3OPTIONS
, 
 284                         TIFFSetField(out
, TIFFTAG_FAXMODE
, mode
); 
 286                                 (defrowsperstrip
)?defrowsperstrip
:(uint32
)-1L; 
 290                         case COMPRESSION_CCITTFAX4
: 
 291                         TIFFSetField(out
, TIFFTAG_GROUP4OPTIONS
, 
 293                         TIFFSetField(out
, TIFFTAG_FAXMODE
, mode
); 
 295                                 (defrowsperstrip
)?defrowsperstrip
:(uint32
)-1L; 
 299                         rowsperstrip 
= (defrowsperstrip
) ? 
 300                                 defrowsperstrip 
: TIFFDefaultStripSize(out
, 0); 
 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); 
 308                         TIFFGetField(faxTIFF
, TIFFTAG_YRESOLUTION
, &resY
); 
 309                         TIFFSetField(out
, TIFFTAG_YRESOLUTION
, resY
); 
 311                         TIFFSetField(out
, TIFFTAG_YRESOLUTION
, 196.); 
 312                 TIFFSetField(out
, TIFFTAG_RESOLUTIONUNIT
, RESUNIT_INCH
); 
 313                 TIFFSetField(out
, TIFFTAG_PAGENUMBER
, pn
, npages
); 
 316                     whandler 
= TIFFSetWarningHandler(NULL
); 
 317                 rows 
= copyFaxFile(faxTIFF
, out
); 
 320                     (void) TIFFSetWarningHandler(whandler
); 
 322                 TIFFSetField(out
, TIFFTAG_IMAGELENGTH
, rows
); 
 325                         fprintf(stderr
, "%s:\n", argv
[optind
]); 
 326                         fprintf(stderr
, "%d rows in input\n", rows
); 
 327                         fprintf(stderr
, "%ld total bad rows\n", 
 329                         fprintf(stderr
, "%d max consecutive bad rows\n", badfaxrun
); 
 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
); 
 338                 TIFFWriteDirectory(out
); 
 343         return (EXIT_SUCCESS
); 
 347 copyFaxFile(TIFF
* tifin
, TIFF
* tifout
) 
 350         uint32 linesize 
= TIFFhowmany8(xsize
); 
 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"); 
 360         if (!ReadOK(tifin
, tifin
->tif_rawdata
, tifin
->tif_rawdatasize
)) { 
 361                 TIFFError(tifin
->tif_name
, "Read error at scanline 0"); 
 364         tifin
->tif_rawcp 
= tifin
->tif_rawdata
; 
 365         tifin
->tif_rawcc 
= tifin
->tif_rawdatasize
; 
 367         (*tifin
->tif_setupdecode
)(tifin
); 
 368         (*tifin
->tif_predecode
)(tifin
, (tsample_t
) 0); 
 373         _TIFFmemset(refbuf
, 0, linesize
); 
 375         badrun 
= 0;             /* current run of bad lines */ 
 376         while (tifin
->tif_rawcc 
> 0) { 
 377                 ok 
= (*tifin
->tif_decoderow
)(tifin
, (tdata_t
) rowbuf
,  
 382                         /* regenerate line from previous good line */ 
 383                         _TIFFmemcpy(rowbuf
, refbuf
, linesize
); 
 385                         if (badrun 
> badfaxrun
) 
 388                         _TIFFmemcpy(refbuf
, rowbuf
, linesize
); 
 392                 if (TIFFWriteScanline(tifout
, rowbuf
, row
, 0) < 0) { 
 393                         fprintf(stderr
, "%s: Write error at row %ld.\n", 
 394                             tifout
->tif_name
, (long) row
); 
 399                         if (TIFFWriteScanline(tifout
, rowbuf
, row
, 0) < 0) { 
 400                                 fprintf(stderr
, "%s: Write error at row %ld.\n", 
 401                                     tifout
->tif_name
, (long) row
); 
 407         if (badrun 
> badfaxrun
) 
 409         _TIFFfree(tifin
->tif_rawdata
); 
 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]", 
 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", 
 456         fprintf(stderr
, "%s\n\n", TIFFGetVersion()); 
 457         for (i 
= 0; stuff
[i
] != NULL
; i
++) 
 458                 fprintf(stderr
, "%s\n", stuff
[i
]); 
 462 /* vim: set ts=8 sts=8 sw=8 noet: */