1 /*===========================================================================
2 g3topbm
3 =============================================================================
4
5 This program reads a Group 3 FAX file and produces a PBM image.
6
7 Bryan Henderson wrote this on August 5, 2004 and contributed it to
8 the public domain.
9
10 This program is designed to be a drop-in replacement for the program
11 of the same name that was distributed with Pbmplus and Netpbm since
12 1989, written by Paul Haeberli <paul@manray.sgi.com>.
13
14 Bryan used ideas on processing G3 data from Haeberli's code, but did
15 not use any of the code.
16
17 Others have modified the program since Bryan's initial work, each
18 contributing their work to the public domain.
19 ===========================================================================*/
20
21 #define _DEFAULT_SOURCE /* New name for SVID & BSD source defines */
22 #define _BSD_SOURCE /* Make nstring.h define strcaseeq() */
23
24 #include "pm_c_util.h"
25 #include "pbm.h"
26 #include "shhopt.h"
27 #include "nstring.h"
28 #include "mallocvar.h"
29 #include "g3.h"
30 #include "g3ttable.h"
31 #include "bitreverse.h"
32 #include "bitarith.h"
33
34 #define LEFTBITS pm_byteLeftBits
35 #define RIGHTBITS pm_byteRightBits
36
37 #define MAXCOLS 10800
38 #define MAXROWS 14400 /* this allows up to two pages of image */
39
40 #define WHASHA 3510
41 #define WHASHB 1178
42
43 #define BHASHA 293
44 #define BHASHB 2695
45
46 #define HASHSIZE 1021
47
48 #define MAXFILLBITS (5 * 9600)
49
50 /*
51 Fill bits are for flow control. This was important when DRAM was
52 expensive and fax machines came with small buffers.
53
54 If data arrives too quickly it may overflow the buffer of the
55 receiving device. On sending devices transmission time of compressed
56 data representing a single row can be shorter than the time required
57 to scan and encode. The CCITT standard allows sending devices to
58 insert fill bits to put communication on hold in these cases.
59
60 By the CCITT standard, the maximum transmission time for one row is:
61
62 100 - 400 pixels/inch 13 seconds
63 (standard mode: 200 pixels/inch)
64 600 pixels/inch 19 seconds
65 1200 pixels/inch 37 seconds
66
67 If one row is not received within the above limits, the receiving
68 machine must disconnect the line.
69
70 The receiver may be less patient. It may opt to disconnect if one row
71 is not received within 5 seconds.
72 */
73
74 static G3TableEntry * whash[HASHSIZE];
75 static G3TableEntry * bhash[HASHSIZE];
76
77
78 struct CmdlineInfo {
79 /* All the information the user supplied in the command line,
80 in a form easy for the program to use.
81 */
82 const char * inputFilespec; /* Filespec of input file */
83 unsigned int reversebits;
84 unsigned int kludge;
85 unsigned int stretch;
86 unsigned int stop_error;
87 unsigned int expectedLineSize;
88 };
89
90
91
92 static void
parseCommandLine(int argc,const char ** const argv,struct CmdlineInfo * const cmdlineP)93 parseCommandLine(int argc, const char ** const argv,
94 struct CmdlineInfo * const cmdlineP) {
95 /*----------------------------------------------------------------------------
96 Note that the file spec array we return is stored in the storage that
97 was passed to us as the argv array.
98 -----------------------------------------------------------------------------*/
99 optEntry * option_def; /* malloc'ed */
100 /* Instructions to OptParseOptions3 on how to parse our options. */
101 optStruct3 opt;
102
103 unsigned int option_def_index;
104
105 unsigned int widthSpec, paper_sizeSpec;
106 const char * paperSize;
107
108 MALLOCARRAY_NOFAIL(option_def, 100);
109
110 option_def_index = 0; /* incremented by OPTENTRY */
111 OPTENT3(0, "reversebits", OPT_FLAG, NULL, &cmdlineP->reversebits,
112 0);
113 OPTENT3(0, "kludge", OPT_FLAG, NULL, &cmdlineP->kludge,
114 0);
115 OPTENT3(0, "stretch", OPT_FLAG, NULL, &cmdlineP->stretch,
116 0);
117 OPTENT3(0, "stop_error", OPT_FLAG, NULL, &cmdlineP->stop_error,
118 0);
119 OPTENT3(0, "width", OPT_UINT, &cmdlineP->expectedLineSize,
120 &widthSpec, 0);
121 OPTENT3(0, "paper_size", OPT_STRING, &paperSize,
122 &paper_sizeSpec, 0);
123
124 opt.opt_table = option_def;
125 opt.short_allowed = FALSE; /* We have no short (old-fashioned) options */
126 opt.allowNegNum = FALSE; /* We may have parms that are negative numbers */
127
128 pm_optParseOptions3(&argc, (char**)argv, opt, sizeof(opt), 0);
129 /* Uses and sets argc, argv, and some of *cmdlineP and others. */
130
131 if (widthSpec && paper_sizeSpec)
132 pm_error("You can't specify both -width and -paper_size");
133
134 if (widthSpec) {
135 if (cmdlineP->expectedLineSize < 1)
136 pm_error("-width must be at least 1");
137 } else if (paper_sizeSpec) {
138 if (strcaseeq(paperSize, "A6"))
139 cmdlineP->expectedLineSize = 864;
140 else if (strcaseeq(paperSize, "A5"))
141 cmdlineP->expectedLineSize = 1216;
142 else if (strcaseeq(paperSize, "A4"))
143 cmdlineP->expectedLineSize = 1728;
144 else if (strcaseeq(paperSize, "B4"))
145 cmdlineP->expectedLineSize = 2048;
146 else if (strcaseeq(paperSize, "A3"))
147 cmdlineP->expectedLineSize = 2432;
148 else
149 pm_error("Unrecognized value for -paper_size '%s'. "
150 "We recognize only A3, A4, A5, A6, and B4.",
151 paperSize);
152 } else
153 cmdlineP->expectedLineSize = 0;
154
155 if (argc-1 == 0)
156 cmdlineP->inputFilespec = "-";
157 else if (argc-1 != 1)
158 pm_error("Program takes zero or one argument (filename). You "
159 "specified %d", argc-1);
160 else
161 cmdlineP->inputFilespec = argv[1];
162 }
163
164
165
166 struct BitStream {
167
168 FILE * fileP;
169 bool reversebits;
170 int shdata;
171 /* 8-bit buffer for rawgetbit(). */
172 unsigned int shbit;
173 /* single bit mask for the bit of 'shdata' that is next in the stream.
174 zero when 'shdata' is empty.
175 */
176 unsigned int zeroBitCount;
177 /* Number of consecutive zero bits the stream has seen. Note that
178 because an EOL mark ends in a one bit, this starts over for each
179 line.
180 */
181 };
182
183
184
185 static void
readBit(struct BitStream * const bitStreamP,unsigned int * const bitP,const char ** const errorP)186 readBit(struct BitStream * const bitStreamP,
187 unsigned int * const bitP,
188 const char ** const errorP) {
189 /*----------------------------------------------------------------------------
190 Return the next raw bit from the G3 input stream.
191
192 Do not call this outside of the bit stream object; Caller is responsible
193 for maintaining object state.
194 -----------------------------------------------------------------------------*/
195 *errorP = NULL; /* initial assumption */
196
197 if ((bitStreamP->shbit & 0xff) == 0) {
198 bitStreamP->shdata = getc(bitStreamP->fileP);
199 if (bitStreamP->shdata == EOF)
200 pm_asprintf(errorP, "EOF or error reading file");
201 else {
202 bitStreamP->shbit = 0x80;
203 if ( bitStreamP->reversebits )
204 bitStreamP->shdata = bitreverse[ bitStreamP->shdata ];
205 }
206 }
207
208 if (bitStreamP->shdata & bitStreamP->shbit)
209 *bitP = 1;
210 else
211 *bitP = 0;
212
213 bitStreamP->shbit >>= 1;
214 }
215
216
217
218 static void
readBitAndDetectEol(struct BitStream * const bitStreamP,unsigned int * const bitP,bool * const eolP,const char ** const errorP)219 readBitAndDetectEol(struct BitStream * const bitStreamP,
220 unsigned int * const bitP,
221 bool * const eolP,
222 const char ** const errorP) {
223 /*----------------------------------------------------------------------------
224 Same as readBit(), but iff the bit read is the final bit of an EOL
225 mark, return *eolP == TRUE.
226
227 An EOL mark is 11 zero bits followed by a one.
228 -----------------------------------------------------------------------------*/
229 readBit(bitStreamP, bitP, errorP);
230 if (!*errorP) {
231 bool eol;
232
233 eol = FALSE; /* initial assumption */
234 if (*bitP == 0)
235 ++bitStreamP->zeroBitCount;
236 else {
237 if (bitStreamP->zeroBitCount >= 11)
238 eol = TRUE;
239 bitStreamP->zeroBitCount = 0;
240 }
241 *eolP = eol;
242 }
243 }
244
245
246 static void
initBitStream(struct BitStream * const bitStreamP,FILE * const fileP,bool const reversebits)247 initBitStream(struct BitStream * const bitStreamP,
248 FILE * const fileP,
249 bool const reversebits) {
250
251 bitStreamP->fileP = fileP;
252 bitStreamP->reversebits = reversebits;
253 bitStreamP->shbit = 0x00;
254 bitStreamP->zeroBitCount = 0;
255 }
256
257
258
259 static void
skipToNextLine(struct BitStream * const bitStreamP)260 skipToNextLine(struct BitStream * const bitStreamP) {
261
262 bool eol;
263 const char * error;
264
265 eol = FALSE;
266 error = NULL;
267
268 while (!eol && !error) {
269 unsigned int bit;
270
271 readBitAndDetectEol(bitStreamP, &bit, &eol, &error);
272 }
273 }
274
275
276
277 static void
addtohash(G3TableEntry * hash[],G3TableEntry table[],unsigned int const n,int const a,int const b)278 addtohash(G3TableEntry * hash[],
279 G3TableEntry table[],
280 unsigned int const n,
281 int const a,
282 int const b) {
283
284 unsigned int i;
285
286 for (i = 0; i < n; ++i) {
287 G3TableEntry * const teP = &table[i*2];
288 unsigned int const pos =
289 ((teP->length + a) * (teP->code + b)) % HASHSIZE;
290 if (hash[pos])
291 pm_error("internal error: addtohash fatal hash collision");
292 hash[pos] = teP;
293 }
294 }
295
296
297
298 static G3TableEntry *
hashfind(G3TableEntry * hash[],int const length,int const code,int const a,int const b)299 hashfind(G3TableEntry * hash[],
300 int const length,
301 int const code,
302 int const a,
303 int const b) {
304
305 unsigned int pos;
306 G3TableEntry * te;
307
308 pos = ((length + a) * (code + b)) % HASHSIZE;
309 te = hash[pos];
310 return ((te && te->length == length && te->code == code) ? te : NULL);
311 }
312
313
314
315 static void
buildHashes(G3TableEntry * (* whashP)[HASHSIZE],G3TableEntry * (* bhashP)[HASHSIZE])316 buildHashes(G3TableEntry * (*whashP)[HASHSIZE],
317 G3TableEntry * (*bhashP)[HASHSIZE]) {
318
319 unsigned int i;
320
321 for (i = 0; i < HASHSIZE; ++i)
322 (*whashP)[i] = (*bhashP)[i] = NULL;
323
324 addtohash(*whashP, &g3ttable_table[0], 64, WHASHA, WHASHB);
325 addtohash(*whashP, &g3ttable_mtable[2], 40, WHASHA, WHASHB);
326
327 addtohash(*bhashP, &g3ttable_table[1], 64, BHASHA, BHASHB);
328 addtohash(*bhashP, &g3ttable_mtable[3], 40, BHASHA, BHASHB);
329
330 }
331
332
333
334 static void
makeRowWhite(unsigned char * const packedBitrow,unsigned int const cols)335 makeRowWhite(unsigned char * const packedBitrow,
336 unsigned int const cols) {
337
338 unsigned int colByte;
339 for (colByte = 0; colByte < pbm_packed_bytes(cols); ++colByte)
340 packedBitrow[colByte] = PBM_WHITE * 0xff;
341 }
342
343
344
345 static G3TableEntry *
g3code(unsigned int const curcode,unsigned int const curlen,bit const color)346 g3code(unsigned int const curcode,
347 unsigned int const curlen,
348 bit const color) {
349 /*----------------------------------------------------------------------------
350 Return the position in the code tables mtable and ttable of the
351 G3 code which is the 'curlen' bits long with value 'curcode'.
352
353 Note that it is the _position_ in the table that determines the meaning
354 of the code. The contents of the table entry do not.
355 -----------------------------------------------------------------------------*/
356 G3TableEntry * retval;
357
358 switch (color) {
359 case PBM_WHITE:
360 if (curlen < 4)
361 retval = NULL;
362 else
363 retval = hashfind(whash, curlen, curcode, WHASHA, WHASHB);
364 break;
365 case PBM_BLACK:
366 if (curlen < 2)
367 retval = NULL;
368 else
369 retval = hashfind(bhash, curlen, curcode, BHASHA, BHASHB);
370 break;
371 default:
372 pm_error("INTERNAL ERROR: color is not black or white");
373 }
374 return retval;
375 }
376
377
378
379 static void
writeBlackBitSpan(unsigned char * const packedBitrow,int const cols,int const offset)380 writeBlackBitSpan(unsigned char * const packedBitrow,
381 int const cols,
382 int const offset) {
383 /*----------------------------------------------------------------------------
384 Write black (="1") bits into packedBitrow[], starting at 'offset',
385 length 'cols'.
386 -----------------------------------------------------------------------------*/
387 unsigned char * const dest = & packedBitrow[offset/8];
388 unsigned int const rs = offset % 8;
389 unsigned int const trs = (cols + rs) % 8;
390 unsigned int const colBytes = pbm_packed_bytes(cols + rs);
391 unsigned int const last = colBytes - 1;
392
393 unsigned char const origHead = dest[0];
394 unsigned char const origEnd = 0x00;
395
396 unsigned int i;
397
398 for( i = 0; i < colBytes; ++i)
399 dest[i] = PBM_BLACK * 0xff;
400
401 if (rs > 0)
402 dest[0] = LEFTBITS(origHead, rs) | RIGHTBITS(dest[0], 8-rs);
403
404 if (trs > 0)
405 dest[last] = LEFTBITS(dest[last], trs) | RIGHTBITS(origEnd, 8-trs);
406 }
407
408
409
410 enum g3tableId {TERMWHITE, TERMBLACK, MKUPWHITE, MKUPBLACK};
411
412 static void
processG3Code(const G3TableEntry * const teP,unsigned char * const packedBitrow,unsigned int * const colP,bit * const colorP,unsigned int * const countP)413 processG3Code(const G3TableEntry * const teP,
414 unsigned char * const packedBitrow,
415 unsigned int * const colP,
416 bit * const colorP,
417 unsigned int * const countP) {
418 /*----------------------------------------------------------------------------
419 'teP' is a pointer into the mtable/ttable. Note that the thing it points
420 to is irrelevant to us; it is only the position in the table that
421 matters.
422 -----------------------------------------------------------------------------*/
423 enum g3tableId const teId =
424 (teP > g3ttable_mtable ? 2 : 0) + (teP - g3ttable_table) % 2;
425
426 unsigned int teCount;
427
428 switch(teId) {
429 case TERMWHITE: teCount = (teP - g3ttable_table ) / 2; break;
430 case TERMBLACK: teCount = (teP - g3ttable_table - 1) / 2; break;
431 case MKUPWHITE: teCount = (teP - g3ttable_mtable ) / 2 * 64; break;
432 case MKUPBLACK: teCount = (teP - g3ttable_mtable - 1) / 2 * 64; break;
433 }
434
435 switch (teId) {
436 case TERMWHITE:
437 case TERMBLACK: {
438 unsigned int totalRunLength;
439 unsigned int col;
440
441 col = *colP;
442 totalRunLength = MIN(*countP + teCount, MAXCOLS - col);
443
444 if (totalRunLength > 0) {
445 if (*colorP == PBM_BLACK)
446 writeBlackBitSpan(packedBitrow, totalRunLength, col);
447 /* else : Row was initialized to white, so we just skip */
448 col += totalRunLength;
449 }
450 *colorP = !*colorP;
451 *countP = 0;
452 *colP = col;
453 } break;
454 case MKUPWHITE:
455 case MKUPBLACK:
456 *countP += teCount;
457 break;
458 default:
459 pm_error("Can't happen");
460 }
461 }
462
463
464
465 static void
formatBadCodeException(const char ** const exceptionP,unsigned int const col,unsigned int const curlen,unsigned int const curcode)466 formatBadCodeException(const char ** const exceptionP,
467 unsigned int const col,
468 unsigned int const curlen,
469 unsigned int const curcode) {
470
471 pm_asprintf(exceptionP,
472 "bad code word at Column %u. "
473 "No prefix of the %u bits 0x%x matches any recognized "
474 "code word and no code words longer than 13 bits are "
475 "defined. ",
476 col, curlen, curcode);
477 }
478
479
480
481 static void
readFaxRow(struct BitStream * const bitStreamP,unsigned char * const packedBitrow,unsigned int * const lineLengthP,const char ** const exceptionP,const char ** const errorP)482 readFaxRow(struct BitStream * const bitStreamP,
483 unsigned char * const packedBitrow,
484 unsigned int * const lineLengthP,
485 const char ** const exceptionP,
486 const char ** const errorP) {
487 /*----------------------------------------------------------------------------
488 Read one line of G3 fax from the bit stream *bitStreamP into
489 packedBitrow[]. Return the length of the line, in pixels, as *lineLengthP.
490
491 If there's a problem with the line, return as much of it as we can,
492 advance the input stream past the next EOL mark, and put a text
493 description of the problem in newly malloc'ed storage at
494 *exceptionP. If there's no problem, return *exceptionP = NULL.
495
496 We guarantee that we make progress through the input stream.
497
498 Iff there is an error, return a text description of it in newly
499 malloc'ed storage at *errorP and all other specified behavior
500 (including return values) is unspecified.
501 -----------------------------------------------------------------------------*/
502 unsigned int col;
503 unsigned int curlen;
504 /* Number of bits we've read so far for the code we're currently
505 reading
506 */
507 unsigned int fillbits;
508 /* Number of consecutive 0 bits. Can precede EOL codes */
509 unsigned int curcode;
510 /* What we've assembled so far of the code we're currently reading */
511 unsigned int count;
512 /* Number of consecutive pixels of the same color */
513 bit currentColor;
514 /* The color of the current run of pixels */
515 bool done;
516
517 makeRowWhite(packedBitrow, MAXCOLS); /* initialize row */
518
519 col = 0;
520 curlen = 0;
521 curcode = 0;
522 fillbits = 0;
523 currentColor = PBM_WHITE;
524 count = 0;
525 *exceptionP = NULL;
526 *errorP = NULL;
527 done = FALSE;
528
529 while (!done) {
530 if (col >= MAXCOLS) {
531 pm_asprintf(exceptionP, "Line is too long for this program to "
532 "handle -- longer than %u columns", MAXCOLS);
533 done = TRUE;
534 } else {
535 unsigned int bit;
536 bool eol;
537 const char * error;
538
539 readBitAndDetectEol(bitStreamP, &bit, &eol, &error);
540 if (error) {
541 if (col > 0)
542 /* We got at least some of the row, so it's only an
543 exception, not a fatal error.
544 */
545 *exceptionP = error;
546 else
547 *errorP = error;
548 done = TRUE;
549 } else if (eol)
550 done = TRUE;
551 else {
552 curcode = (curcode << 1) | bit;
553 ++curlen;
554
555 if (curlen > 11 && curcode == 0x00) {
556 if (++fillbits > MAXFILLBITS)
557 pm_error("Encountered %u consecutive fill bits. "
558 "Aborting", fillbits);
559 }
560 else if (curlen - fillbits > 13) {
561 formatBadCodeException(exceptionP, col, curlen, curcode);
562 done = TRUE;
563 } else if (curcode != 0) {
564 const G3TableEntry * const teP =
565 g3code(curcode, curlen, currentColor);
566 /* Address of structure that describes the
567 current G3 code. Null means 'curcode' isn't
568 a G3 code yet (probably just the beginning of one)
569 */
570 if (teP) {
571 processG3Code(teP, packedBitrow,
572 &col, ¤tColor, &count);
573
574 curcode = 0;
575 curlen = 0;
576 }
577 }
578 }
579 }
580 }
581 if (*exceptionP)
582 skipToNextLine(bitStreamP);
583
584 *lineLengthP = col;
585 }
586
587
588
589 static void
freeBits(unsigned char ** const packedBits,unsigned int const rows,bool const stretched)590 freeBits(unsigned char ** const packedBits,
591 unsigned int const rows,
592 bool const stretched) {
593
594 unsigned int row;
595
596 for (row = 0; row < rows; ++row) {
597 if (stretched && row % 2 == 1) {
598 /* This is just a pointer to the previous row; don't want to
599 free it twice.
600 */
601 } else
602 pbm_freerow_packed(packedBits[row]);
603 }
604 free(packedBits);
605 }
606
607
608
609 static void
handleRowException(const char * const exception,const char * const error,unsigned int const row,bool const tolerateErrors)610 handleRowException(const char * const exception,
611 const char * const error,
612 unsigned int const row,
613 bool const tolerateErrors) {
614
615
616 if (exception) {
617 if (tolerateErrors)
618 pm_message("Problem reading Row %u. Skipping rest of row. %s",
619 row, exception);
620 else
621 pm_error("Problem reading Row %u. Aborting. %s", row, exception);
622 pm_strfree(exception);
623 }
624
625 if (error) {
626 if (tolerateErrors)
627 pm_message("Unable to read Row %u. Skipping rest of image. %s",
628 row, error);
629 else
630 pm_error("Unable to read Row %u. Aborting. %s", row, error);
631 pm_strfree(error);
632 }
633 }
634
635
636
637 typedef struct {
638 unsigned int expectedLineSize;
639 /* The size that lines are supposed to be. Zero means we're happy
640 with any size.
641 */
642 unsigned int maxLineSize;
643 /* The maximum line size we have seen so far, or zero if we have
644 not seen any lines yet.
645 */
646 bool warned;
647 /* We have warned the user that he has a line length problem */
648 bool tolerateErrors;
649 /* Try to continue when we detect a line size error, as opposed to
650 aborting the program.
651 */
652 } lineSizeAnalyzer;
653
654
655
656 static void
initializeLineSizeAnalyzer(lineSizeAnalyzer * const analyzerP,unsigned int const expectedLineSize,bool const tolerateErrors)657 initializeLineSizeAnalyzer(lineSizeAnalyzer * const analyzerP,
658 unsigned int const expectedLineSize,
659 bool const tolerateErrors) {
660
661 analyzerP->expectedLineSize = expectedLineSize;
662 analyzerP->tolerateErrors = tolerateErrors;
663
664 analyzerP->maxLineSize = 0;
665 analyzerP->warned = FALSE;
666 }
667
668
669
670 static void
analyzeLineSize(lineSizeAnalyzer * const analyzerP,unsigned int const thisLineSize)671 analyzeLineSize(lineSizeAnalyzer * const analyzerP,
672 unsigned int const thisLineSize) {
673
674 const char * error;
675
676 if (analyzerP->expectedLineSize &&
677 thisLineSize != analyzerP->expectedLineSize)
678 pm_asprintf(&error, "Image contains a line of %u pixels. "
679 "You specified lines should be %u pixels.",
680 thisLineSize, analyzerP->expectedLineSize);
681 else {
682 if (analyzerP->maxLineSize && thisLineSize != analyzerP->maxLineSize)
683 pm_asprintf(&error, "There are at least two different "
684 "line lengths in this image, "
685 "%u pixels and %u pixels. "
686 "This is a violation of the G3 standard. ",
687 thisLineSize, analyzerP->maxLineSize);
688 else
689 error = NULL;
690 }
691
692 if (error) {
693 if (analyzerP->tolerateErrors) {
694 if (!analyzerP->warned) {
695 pm_message("Warning: %s.", error);
696 analyzerP->warned = TRUE;
697 }
698 } else
699 pm_error("%s", error);
700
701 pm_strfree(error);
702 }
703 analyzerP->maxLineSize = MAX(thisLineSize, analyzerP->maxLineSize);
704 }
705
706
707
708 /* An empty line means EOF. An ancient comment in the code said there
709 is supposed to be 6 EOL marks in a row to indicate EOF, but the code
710 checked for 3 and considered 2 in a row just to mean a zero length
711 line. Starting in Netpbm 10.24 (August 2004), we assume there is
712 no valid reason to have an empty line and recognize EOF as any
713 empty line. Alternatively, we could read off and ignore two empty
714 lines without a 3rd.
715 */
716
717 static void
readFax(struct BitStream * const bitStreamP,bool const stretch,unsigned int const expectedLineSize,bool const tolerateErrors,unsigned char *** const packedBitsP,unsigned int * const colsP,unsigned int * const rowsP)718 readFax(struct BitStream * const bitStreamP,
719 bool const stretch,
720 unsigned int const expectedLineSize,
721 bool const tolerateErrors,
722 unsigned char *** const packedBitsP,
723 unsigned int * const colsP,
724 unsigned int * const rowsP) {
725
726 lineSizeAnalyzer lineSizeAnalyzer;
727 unsigned char ** packedBits;
728 const char * error;
729 bool eof;
730 unsigned int row;
731
732 MALLOCARRAY_NOFAIL(packedBits, MAXROWS);
733
734 initializeLineSizeAnalyzer(&lineSizeAnalyzer,
735 expectedLineSize, tolerateErrors);
736
737 eof = FALSE;
738 error = NULL;
739 row = 0;
740
741 while (!eof && !error) {
742 unsigned int lineSize;
743
744 if (row >= MAXROWS)
745 pm_asprintf(&error, "Image is too tall. This program can "
746 "handle at most %u rows", MAXROWS);
747 else {
748 const char * exception;
749
750 packedBits[row] = pbm_allocrow_packed(MAXCOLS);
751 readFaxRow(bitStreamP, packedBits[row],
752 &lineSize, &exception, &error);
753
754 handleRowException(exception, error, row, tolerateErrors);
755
756 if (!error) {
757 if (lineSize == 0) {
758 /* EOF. See explanation above */
759 eof = TRUE;
760 } else {
761 analyzeLineSize(&lineSizeAnalyzer, lineSize);
762
763 if (stretch) {
764 ++row;
765 if (row >= MAXROWS)
766 pm_asprintf(&error, "Image is too tall. This "
767 "program can handle at most %u rows "
768 "after stretching", MAXROWS);
769 else
770 packedBits[row] = packedBits[row-1];
771 }
772 ++row;
773 }
774 }
775 }
776 }
777 *rowsP = row;
778 *colsP = lineSizeAnalyzer.maxLineSize;
779 *packedBitsP = packedBits;
780 }
781
782
783
784 int
main(int argc,const char * argv[])785 main(int argc, const char * argv[]) {
786
787 struct CmdlineInfo cmdline;
788 FILE * ifP;
789 struct BitStream bitStream;
790 unsigned int rows, cols;
791 unsigned char ** packedBits;
792
793 pm_proginit(&argc, argv);
794
795 parseCommandLine(argc, argv, &cmdline);
796
797 ifP = pm_openr(cmdline.inputFilespec);
798
799 initBitStream(&bitStream, ifP, cmdline.reversebits);
800
801 if (cmdline.kludge) {
802 /* Skip extra lines to get in sync. */
803 skipToNextLine(&bitStream);
804 skipToNextLine(&bitStream);
805 skipToNextLine(&bitStream);
806 }
807 skipToNextLine(&bitStream);
808
809 buildHashes(&whash, &bhash);
810
811 readFax(&bitStream, cmdline.stretch, cmdline.expectedLineSize,
812 !cmdline.stop_error,
813 &packedBits, &cols, &rows);
814
815 pm_close(ifP);
816
817 if (cols > 0 && rows > 0) {
818 unsigned int row;
819 pbm_writepbminit(stdout, cols, rows, 0);
820 for (row = 0; row < rows; ++row)
821 pbm_writepbmrow_packed(stdout, packedBits[row], cols, 0);
822 } else
823 pm_error("No image data in input");
824
825 pm_close(stdout);
826
827 freeBits(packedBits, rows, cmdline.stretch);
828
829 return 0;
830 }
831