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
], "rb");
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
= (tmsize_t
)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: */