1/* $Id: fax2tiff.c 276 2010-06-30 12:18:30Z nijtmans $ */ 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 58TIFF *faxTIFF; 59char *rowbuf; 60char *refbuf; 61 62uint32 xsize = 1728; 63int verbose; 64int stretch; 65uint16 badfaxrun; 66uint32 badfaxlines; 67 68int copyFaxFile(TIFF* tifin, TIFF* tifout); 69static void usage(void); 70 71int 72main(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 342int 343copyFaxFile(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 409char* 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", 442NULL 443}; 444 445static void 446usage(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 */ 466