3 * Copyright (c) 1990-1997 Sam Leffler
4 * Copyright (c) 1991-1997 Silicon Graphics, Inc.
6 * Permission to use, copy, modify, distribute, and sell this software and
7 * its documentation for any purpose is hereby granted without fee, provided
8 * that (i) the above copyright notices and this permission notice appear in
9 * all copies of the software and related documentation, and (ii) the names of
10 * Sam Leffler and Silicon Graphics may not be used in any advertising or
11 * publicity relating to the software without the specific, prior written
12 * permission of Sam Leffler and Silicon Graphics.
14 * THE SOFTWARE IS PROVIDED "AS-IS" AND WITHOUT WARRANTY OF ANY KIND,
15 * EXPRESS, IMPLIED OR OTHERWISE, INCLUDING WITHOUT LIMITATION, ANY
16 * WARRANTY OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE.
18 * IN NO EVENT SHALL SAM LEFFLER OR SILICON GRAPHICS BE LIABLE FOR
19 * ANY SPECIAL, INCIDENTAL, INDIRECT OR CONSEQUENTIAL DAMAGES OF ANY KIND,
20 * OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,
21 * WHETHER OR NOT ADVISED OF THE POSSIBILITY OF DAMAGE, AND ON ANY THEORY OF
22 * LIABILITY, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE
27 * Convert a CCITT Group 3 or 4 FAX file to TIFF Group 3 or 4 format.
29 #include "tif_config.h"
32 #include <stdlib.h> /* should have atof & getopt */
53 # define EXIT_SUCCESS 0
56 # define EXIT_FAILURE 1
59 #define TIFFhowmany8(x) (((x)&0x07)?((uint32)(x)>>3)+1:(uint32)(x)>>3)
71 int copyFaxFile(TIFF
* tifin
, TIFF
* tifout
);
72 static void usage(void);
75 main(int argc
, char* argv
[])
79 TIFFErrorHandler whandler
= NULL
;
80 int compression_in
= COMPRESSION_CCITTFAX3
;
81 int compression_out
= COMPRESSION_CCITTFAX3
;
82 int fillorder_in
= FILLORDER_LSB2MSB
;
83 int fillorder_out
= FILLORDER_LSB2MSB
;
84 uint32 group3options_in
= 0; /* 1d-encoded */
85 uint32 group3options_out
= 0; /* 1d-encoded */
86 uint32 group4options_in
= 0; /* compressed */
87 uint32 group4options_out
= 0; /* compressed */
88 uint32 defrowsperstrip
= (uint32
) 0;
90 int photometric_in
= PHOTOMETRIC_MINISWHITE
;
91 int photometric_out
= PHOTOMETRIC_MINISWHITE
;
92 int mode
= FAXMODE_CLASSF
;
101 while ((c
= getopt(argc
, argv
, "R:X:o:1234ABLMPUW5678abcflmprsuvwz?")) != -1)
103 /* input-related options */
104 case '3': /* input is g3-encoded */
105 compression_in
= COMPRESSION_CCITTFAX3
;
107 case '4': /* input is g4-encoded */
108 compression_in
= COMPRESSION_CCITTFAX4
;
110 case 'U': /* input is uncompressed (g3 and g4) */
111 group3options_in
|= GROUP3OPT_UNCOMPRESSED
;
112 group4options_in
|= GROUP4OPT_UNCOMPRESSED
;
114 case '1': /* input is 1d-encoded (g3 only) */
115 group3options_in
&= ~GROUP3OPT_2DENCODING
;
117 case '2': /* input is 2d-encoded (g3 only) */
118 group3options_in
|= GROUP3OPT_2DENCODING
;
120 case 'P': /* input has not-aligned EOL (g3 only) */
121 group3options_in
&= ~GROUP3OPT_FILLBITS
;
123 case 'A': /* input has aligned EOL (g3 only) */
124 group3options_in
|= GROUP3OPT_FILLBITS
;
126 case 'W': /* input has 0 mean white */
127 photometric_in
= PHOTOMETRIC_MINISWHITE
;
129 case 'B': /* input has 0 mean black */
130 photometric_in
= PHOTOMETRIC_MINISBLACK
;
132 case 'L': /* input has lsb-to-msb fillorder */
133 fillorder_in
= FILLORDER_LSB2MSB
;
135 case 'M': /* input has msb-to-lsb fillorder */
136 fillorder_in
= FILLORDER_MSB2LSB
;
138 case 'R': /* input resolution */
139 resY
= (float) atof(optarg
);
141 case 'X': /* input width */
142 xsize
= (uint32
) atoi(optarg
);
145 /* output-related options */
146 case '7': /* generate g3-encoded output */
147 compression_out
= COMPRESSION_CCITTFAX3
;
149 case '8': /* generate g4-encoded output */
150 compression_out
= COMPRESSION_CCITTFAX4
;
152 case 'u': /* generate uncompressed output (g3 and g4) */
153 group3options_out
|= GROUP3OPT_UNCOMPRESSED
;
154 group4options_out
|= GROUP4OPT_UNCOMPRESSED
;
156 case '5': /* generate 1d-encoded output (g3 only) */
157 group3options_out
&= ~GROUP3OPT_2DENCODING
;
159 case '6': /* generate 2d-encoded output (g3 only) */
160 group3options_out
|= GROUP3OPT_2DENCODING
;
162 case 'c': /* generate "classic" g3 format */
163 mode
= FAXMODE_CLASSIC
;
165 case 'f': /* generate Class F format */
166 mode
= FAXMODE_CLASSF
;
168 case 'm': /* output's fillorder is msb-to-lsb */
169 fillorder_out
= FILLORDER_MSB2LSB
;
171 case 'l': /* output's fillorder is lsb-to-msb */
172 fillorder_out
= FILLORDER_LSB2MSB
;
175 out
= TIFFOpen(optarg
, "w");
178 "%s: Can not create or open %s\n",
183 case 'a': /* generate EOL-aligned output (g3 only) */
184 group3options_out
|= GROUP3OPT_FILLBITS
;
186 case 'p': /* generate not EOL-aligned output (g3 only) */
187 group3options_out
&= ~GROUP3OPT_FILLBITS
;
189 case 'r': /* rows/strip */
190 defrowsperstrip
= atol(optarg
);
192 case 's': /* stretch image by dup'ng scanlines */
195 case 'w': /* undocumented -- for testing */
196 photometric_out
= PHOTOMETRIC_MINISWHITE
;
198 case 'b': /* undocumented -- for testing */
199 photometric_out
= PHOTOMETRIC_MINISBLACK
;
201 case 'z': /* undocumented -- for testing */
202 compression_out
= COMPRESSION_LZW
;
204 case 'v': /* -v for info */
211 npages
= argc
- optind
;
215 rowbuf
= _TIFFmalloc(TIFFhowmany8(xsize
));
216 refbuf
= _TIFFmalloc(TIFFhowmany8(xsize
));
217 if (rowbuf
== NULL
|| refbuf
== NULL
) {
218 fprintf(stderr
, "%s: Not enough memory\n", argv
[0]);
219 return (EXIT_FAILURE
);
223 out
= TIFFOpen("fax.tif", "w");
225 fprintf(stderr
, "%s: Can not create fax.tif\n",
227 return (EXIT_FAILURE
);
231 faxTIFF
= TIFFClientOpen("(FakeInput)", "w",
232 /* TIFFClientOpen() fails if we don't set existing value here */
234 TIFFGetReadProc(out
), TIFFGetWriteProc(out
),
235 TIFFGetSeekProc(out
), TIFFGetCloseProc(out
),
236 TIFFGetSizeProc(out
), TIFFGetMapFileProc(out
),
237 TIFFGetUnmapFileProc(out
));
238 if (faxTIFF
== NULL
) {
239 fprintf(stderr
, "%s: Can not create fake input file\n",
241 return (EXIT_FAILURE
);
243 TIFFSetMode(faxTIFF
, O_RDONLY
);
244 TIFFSetField(faxTIFF
, TIFFTAG_IMAGEWIDTH
, xsize
);
245 TIFFSetField(faxTIFF
, TIFFTAG_SAMPLESPERPIXEL
, 1);
246 TIFFSetField(faxTIFF
, TIFFTAG_BITSPERSAMPLE
, 1);
247 TIFFSetField(faxTIFF
, TIFFTAG_FILLORDER
, fillorder_in
);
248 TIFFSetField(faxTIFF
, TIFFTAG_PLANARCONFIG
, PLANARCONFIG_CONTIG
);
249 TIFFSetField(faxTIFF
, TIFFTAG_PHOTOMETRIC
, photometric_in
);
250 TIFFSetField(faxTIFF
, TIFFTAG_YRESOLUTION
, resY
);
251 TIFFSetField(faxTIFF
, TIFFTAG_RESOLUTIONUNIT
, RESUNIT_INCH
);
253 /* NB: this must be done after directory info is setup */
254 TIFFSetField(faxTIFF
, TIFFTAG_COMPRESSION
, compression_in
);
255 if (compression_in
== COMPRESSION_CCITTFAX3
)
256 TIFFSetField(faxTIFF
, TIFFTAG_GROUP3OPTIONS
, group3options_in
);
257 else if (compression_in
== COMPRESSION_CCITTFAX4
)
258 TIFFSetField(faxTIFF
, TIFFTAG_GROUP4OPTIONS
, group4options_in
);
259 for (pn
= 0; optind
< argc
; pn
++, optind
++) {
260 in
= fopen(argv
[optind
], "rb");
263 "%s: %s: Can not open\n", argv
[0], argv
[optind
]);
266 #if defined(_WIN32) && defined(USE_WIN32_FILEIO)
267 TIFFSetClientdata(faxTIFF
, (thandle_t
)_get_osfhandle(fileno(in
)));
269 TIFFSetClientdata(faxTIFF
, (thandle_t
)fileno(in
));
271 TIFFSetFileName(faxTIFF
, (const char*)argv
[optind
]);
272 TIFFSetField(out
, TIFFTAG_IMAGEWIDTH
, xsize
);
273 TIFFSetField(out
, TIFFTAG_BITSPERSAMPLE
, 1);
274 TIFFSetField(out
, TIFFTAG_COMPRESSION
, compression_out
);
275 TIFFSetField(out
, TIFFTAG_PHOTOMETRIC
, photometric_out
);
276 TIFFSetField(out
, TIFFTAG_ORIENTATION
, ORIENTATION_TOPLEFT
);
277 TIFFSetField(out
, TIFFTAG_SAMPLESPERPIXEL
, 1);
278 switch (compression_out
) {
280 case COMPRESSION_CCITTFAX3
:
281 TIFFSetField(out
, TIFFTAG_GROUP3OPTIONS
,
283 TIFFSetField(out
, TIFFTAG_FAXMODE
, mode
);
285 (defrowsperstrip
)?defrowsperstrip
:(uint32
)-1L;
289 case COMPRESSION_CCITTFAX4
:
290 TIFFSetField(out
, TIFFTAG_GROUP4OPTIONS
,
292 TIFFSetField(out
, TIFFTAG_FAXMODE
, mode
);
294 (defrowsperstrip
)?defrowsperstrip
:(uint32
)-1L;
298 rowsperstrip
= (defrowsperstrip
) ?
299 defrowsperstrip
: TIFFDefaultStripSize(out
, 0);
301 TIFFSetField(out
, TIFFTAG_ROWSPERSTRIP
, rowsperstrip
);
302 TIFFSetField(out
, TIFFTAG_PLANARCONFIG
, PLANARCONFIG_CONTIG
);
303 TIFFSetField(out
, TIFFTAG_FILLORDER
, fillorder_out
);
304 TIFFSetField(out
, TIFFTAG_SOFTWARE
, "fax2tiff");
305 TIFFSetField(out
, TIFFTAG_XRESOLUTION
, 204.0);
307 TIFFGetField(faxTIFF
, TIFFTAG_YRESOLUTION
, &resY
);
308 TIFFSetField(out
, TIFFTAG_YRESOLUTION
, resY
);
310 TIFFSetField(out
, TIFFTAG_YRESOLUTION
, 196.);
311 TIFFSetField(out
, TIFFTAG_RESOLUTIONUNIT
, RESUNIT_INCH
);
312 TIFFSetField(out
, TIFFTAG_PAGENUMBER
, pn
, npages
);
315 whandler
= TIFFSetWarningHandler(NULL
);
316 rows
= copyFaxFile(faxTIFF
, out
);
319 (void) TIFFSetWarningHandler(whandler
);
321 TIFFSetField(out
, TIFFTAG_IMAGELENGTH
, rows
);
324 fprintf(stderr
, "%s:\n", argv
[optind
]);
325 fprintf(stderr
, "%d rows in input\n", rows
);
326 fprintf(stderr
, "%ld total bad rows\n",
328 fprintf(stderr
, "%d max consecutive bad rows\n", badfaxrun
);
330 if (compression_out
== COMPRESSION_CCITTFAX3
&&
331 mode
== FAXMODE_CLASSF
) {
332 TIFFSetField(out
, TIFFTAG_BADFAXLINES
, badfaxlines
);
333 TIFFSetField(out
, TIFFTAG_CLEANFAXDATA
, badfaxlines
?
334 CLEANFAXDATA_REGENERATED
: CLEANFAXDATA_CLEAN
);
335 TIFFSetField(out
, TIFFTAG_CONSECUTIVEBADFAXLINES
, badfaxrun
);
337 TIFFWriteDirectory(out
);
342 return (EXIT_SUCCESS
);
346 copyFaxFile(TIFF
* tifin
, TIFF
* tifout
)
349 uint32 linesize
= TIFFhowmany8(xsize
);
353 tifin
->tif_rawdatasize
= (tmsize_t
)TIFFGetFileSize(tifin
);
354 tifin
->tif_rawdata
= _TIFFmalloc(tifin
->tif_rawdatasize
);
355 if (tifin
->tif_rawdata
== NULL
) {
356 TIFFError(tifin
->tif_name
, "Not enough memory");
359 if (!ReadOK(tifin
, tifin
->tif_rawdata
, tifin
->tif_rawdatasize
)) {
360 TIFFError(tifin
->tif_name
, "Read error at scanline 0");
363 tifin
->tif_rawcp
= tifin
->tif_rawdata
;
364 tifin
->tif_rawcc
= tifin
->tif_rawdatasize
;
366 (*tifin
->tif_setupdecode
)(tifin
);
367 (*tifin
->tif_predecode
)(tifin
, (tsample_t
) 0);
372 _TIFFmemset(refbuf
, 0, linesize
);
374 badrun
= 0; /* current run of bad lines */
375 while (tifin
->tif_rawcc
> 0) {
376 ok
= (*tifin
->tif_decoderow
)(tifin
, (tdata_t
) rowbuf
,
381 /* regenerate line from previous good line */
382 _TIFFmemcpy(rowbuf
, refbuf
, linesize
);
384 if (badrun
> badfaxrun
)
387 _TIFFmemcpy(refbuf
, rowbuf
, linesize
);
391 if (TIFFWriteScanline(tifout
, rowbuf
, row
, 0) < 0) {
392 fprintf(stderr
, "%s: Write error at row %ld.\n",
393 tifout
->tif_name
, (long) row
);
398 if (TIFFWriteScanline(tifout
, rowbuf
, row
, 0) < 0) {
399 fprintf(stderr
, "%s: Write error at row %ld.\n",
400 tifout
->tif_name
, (long) row
);
406 if (badrun
> badfaxrun
)
408 _TIFFfree(tifin
->tif_rawdata
);
413 "usage: fax2tiff [options] input.raw...",
414 "where options are:",
415 " -3 input data is G3-encoded [default]",
416 " -4 input data is G4-encoded",
417 " -U input data is uncompressed (G3 or G4)",
418 " -1 input data is 1D-encoded (G3 only) [default]",
419 " -2 input data is 2D-encoded (G3 only)",
420 " -P input is not EOL-aligned (G3 only) [default]",
421 " -A input is EOL-aligned (G3 only)",
422 " -M input data has MSB2LSB bit order",
423 " -L input data has LSB2MSB bit order [default]",
424 " -B input data has min 0 means black",
425 " -W input data has min 0 means white [default]",
426 " -R # input data has # resolution (lines/inch) [default is 196]",
427 " -X # input data has # width [default is 1728]",
429 " -o out.tif write output to out.tif",
430 " -7 generate G3-encoded output [default]",
431 " -8 generate G4-encoded output",
432 " -u generate uncompressed output (G3 or G4)",
433 " -5 generate 1D-encoded output (G3 only)",
434 " -6 generate 2D-encoded output (G3 only) [default]",
435 " -p generate not EOL-aligned output (G3 only)",
436 " -a generate EOL-aligned output (G3 only) [default]",
437 " -c generate \"classic\" TIFF format",
438 " -f generate TIFF Class F (TIFF/F) format [default]",
439 " -m output fill order is MSB2LSB",
440 " -l output fill order is LSB2MSB [default]",
441 " -r # make each strip have no more than # rows",
442 " -s stretch image by duplicating scanlines",
443 " -v print information about conversion work",
444 " -z generate LZW compressed output",
455 fprintf(stderr
, "%s\n\n", TIFFGetVersion());
456 for (i
= 0; stuff
[i
] != NULL
; i
++)
457 fprintf(stderr
, "%s\n", stuff
[i
]);
461 /* vim: set ts=8 sts=8 sw=8 noet: */