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