1 #ident "$Id: pbm2g3.c,v 4.2 1998/05/07 10:37:38 gert Exp $ Copyright (C) 1994 Gert Doering"
2 
3 /* pbm2g3
4  *
5  * convert a "portable bitmap" file into CCITT T.4 fax format
6  * the output can directly be sent with mgetty+sendfax
7  *
8  * options: -d     output digifax header
9  *          -w xxx use a page width of xxx pels (default 1728)
10  *          -h xxx start page with xxx blank lines (default 0)
11  *	    -a     byte-align EOLs
12  *	    -r     reverse bytes
13  */
14 
15 #include <stdio.h>
16 #include <unistd.h>
17 #include <fcntl.h>
18 #include <string.h>
19 #include "syslibs.h"
20 #include <ctype.h>
21 
22 #include "ugly.h"
23 
24 #include "g3.h"
25 
26 #ifndef TRUE
27 #define TRUE 1
28 #define FALSE 0
29 #endif
30 
31 /* g3 stuff */
32 
33 int byte_align = FALSE;
34 
35 static unsigned char buf[2048];
36 static int buflen = 0;
37 static unsigned int out_data = 0;
38 static unsigned int out_hibit = 0;
39 
40 static int out_byte_tab[ 256 ];			/* for g3 byte reversal */
41 
42 #ifdef __GNUC__
43 inline
44 #endif
45 void putcode _P2( (code, len), int code, int len )
46 {
47     out_data |= ( code << out_hibit );
48     out_hibit += len;
49 
50     while( out_hibit >= 8 )
51     {
52 	buf[ buflen++ ] = out_byte_tab[( out_data ) & 0xff];
53 	out_data >>= 8;
54 	out_hibit -= 8;
55 	if ( buflen >= sizeof( buf ) )
56 	{
57 	    write( 1, buf, buflen ); buflen = 0;
58 	}
59     }
60 }
61 
62 #ifdef __GNUC__
63 inline
64 #endif
_P0(void)65 void puteol _P0( void )			/* write byte-aligned EOL */
66 {
67     if ( byte_align ) while( out_hibit != 4 ) putcode( 0, 1 );
68     putcode( 0x800, 12 );
69 }
70 
71 #ifdef __GNUC__
72 inline
73 #endif
74 void putwhitespan _P1( (l), int l )
75 {
76     if ( l >= 64 )
77     {
78 	int mkup = ( l & ~63 );
79 	int idx = (mkup / 64) -1;
80 
81 	if ( mkup > 1728 )	/* extended makeup table */
82 	{
83 	    fprintf( stderr,
84 		    "run length too long (%d) - not yet implemented\n",  l );
85 	    exit(99);
86 	}
87 	else
88 	{
89 	    if ( m_white[idx].nr_pels != mkup )	/* paranoia alert */
90 	    {
91 		fprintf( stderr, "no match: idx=%d, mkup=%d", idx, mkup );
92 		exit(99);
93 	    }
94 	    putcode( m_white[idx].bit_code, m_white[idx].bit_length );
95 	}
96 	l -= mkup;
97     }
98 
99     putcode( t_white[l].bit_code, t_white[l].bit_length );
100 }
101 
102 #ifdef __GNUC__
103 inline
104 #endif
105 void putblackspan _P1( (l), int l )
106 {
107     if ( l >= 64 )
108     {
109 	int mkup = ( l & ~63 );
110 	int idx = (mkup / 64) -1;
111 
112 	if ( mkup > 1728 )	/* extended makeup table */
113 	{
114 	    fprintf( stderr,
115 		    "run length too long (%d) - not yet implemented\n",  l );
116 	    exit(99);
117 	}
118 	else
119 	{
120 	    if ( m_black[idx].nr_pels != mkup )	/* paranoia alert */
121 	    {
122 		fprintf( stderr, "no match: idx=%d, mkup=%d", idx, mkup );
123 		exit(99);
124 	    }
125 	    putcode( m_black[idx].bit_code, m_black[idx].bit_length );
126 	}
127 	l -= mkup;
128     }
129 
130     putcode( t_black[l].bit_code, t_black[l].bit_length );
131 }
132 
133 /* pbm file header stuff */
134 
135 typedef enum { unknown,
136 	       pbm, pgm, ppm,
137 	       pbm_raw, pgm_raw, ppm_raw
138 	   } pbm_file_types;
139 
140 pbm_file_types	pbm_type;
141 int		pbm_xsize;
142 int		pbm_ysize;
143 
144 int pbm_getint _P1( (fd), int fd )
145 {
146     char buf[50];
147     int i;
148 
149     /* skip leading whitespace */
150     do
151     {
152 	if ( read( fd, buf, 1 ) != 1 ) return -1;
153 
154 	if ( buf[0] == '#' )
155 	{
156 	    while( buf[0] != '\n' && read( fd, buf, 1 ) == 1 ) {}
157 	}
158     }
159     while ( isspace( buf[0] ) );
160 
161     i = 1;
162     while ( i < sizeof( buf ) -1 &&
163 	    read( fd, &buf[i], 1 ) == 1 &&
164 	    ! isspace( buf[i] ) )
165     {
166 	i++;
167     }
168 
169     if ( ! isspace( buf[i] ) ) return -1;
170 
171     buf[i] = 0;
172 
173     return ( atoi( buf ) );
174 }
175 
176 void pbm_getheader _P1( (fd), int fd )
177 {
178     char buf[10];
179 
180     if ( read( fd, buf, 2 ) != 2 || buf[0] != 'P' )
181     {
182 	pbm_type = unknown; return;
183     }
184 
185     switch( buf[1] )
186     {
187       case '1': pbm_type = pbm; break;
188       case '2': pbm_type = pgm; break;
189       case '3': pbm_type = ppm; break;
190       case '4': pbm_type = pbm_raw; break;
191       case '5': pbm_type = pgm_raw; break;
192       case '6': pbm_type = ppm_raw; break;
193       default:  pbm_type = unknown; return;
194     }
195 
196     pbm_xsize = pbm_getint( fd );
197     pbm_ysize = pbm_getint( fd );
198 }
199 
200 
201 
202 void exit_usage _P1( (name), char * name )
203 {
204     fprintf( stderr,
205 	     "usage: %s [-w width] [-h blank_lines] [-d] [-a] [-r] [pbm file]\n",
206 	      name );
207     exit(1);
208 }
209 
210 
211 void convert_pbm_raw _P2( (fd, g3_page_width), int fd, int
212 			 g3_page_width )
213 {
214     extern void make_run_tables _PROTO((void));
215     extern char w_rtab[8][256],
216                 b_rtab[8][256];
217 
218     int x, y, maxx;
219     int run, c;
220     int bit;
221 
222     int ll;
223     unsigned char * linebuf, * r;
224 
225     /* initialize run length tables */
226     make_run_tables();
227 
228     /* round up page width to byte boundary */
229     pbm_xsize = ( pbm_xsize + 7 ) & ~7;
230 
231     /* malloc memory for line buffer */
232     ll = pbm_xsize / 8;
233     linebuf = (unsigned char *) malloc( ll );
234 
235     if ( linebuf == NULL )
236     {
237 	fprintf( stderr, "cannot malloc %d bytes: ", ll );
238 	perror( "" );
239 	exit(5);
240     }
241 
242     /* maximum number of PELs to write pbm -> g3 */
243     if ( g3_page_width > pbm_xsize ) maxx = pbm_xsize;
244                                 else maxx = g3_page_width;
245 
246     for ( y=0; y<pbm_ysize; y++ )
247     {
248 	int h;
249 
250 	c = 0;					/* start with white */
251 	run = 0;
252 	x = 0;
253 	bit = 7;
254 
255 	/* read line into buffer (pipe -> multiple tries may be necessary!) */
256 	h = 0;
257 	while ( h < ll )
258 	{
259 	    int h2;
260 
261 	    h2 = read( fd, linebuf+h, ll-h );
262 	    if ( h2 == 0 )
263 	    {
264 		fprintf( stderr, "line %d: want %d, got %d bytes, EOF", y, ll, h );
265 		return;		/* the page will be short... */
266 	    }
267 	    h += h2;
268 	}
269 
270 	r = linebuf;
271 
272 	while ( x+run < maxx )
273 	{
274 #ifdef NOISY
275 fprintf( stderr, "c=%d, bit=%d, x=%d, *r(%d)=%03o ", c, bit, x, r-linebuf, *r);
276 #endif
277 	    if ( c == 0 )	/* white run */
278 	    {
279 		run += w_rtab[ bit ][ *r ];
280 		bit -= w_rtab[ bit ][ *r ];
281 	    }
282 	    else		/* black run */
283 	    {
284 		run += b_rtab[ bit ][ *r ];
285 		bit -= b_rtab[ bit ][ *r ];
286 	    }
287 #ifdef NOISY
288 fprintf( stderr, "-> run=%d, bit=%d\n", run, bit );
289 #endif
290 	    if ( bit < 0 )	/* continue in next byte */
291 	    {
292 		if ( bit != -1 ) fprintf( stderr, "bit panic: %d\n", bit );
293 		bit = 7;
294 		r++;
295 	    }
296 	    else		/* write out run, change color */
297 	    {
298 		if ( c == 0 )	/* white */
299 		    putwhitespan( run );
300 		else		/* black */
301 		    putblackspan( run );
302 
303                 x += run;
304 		run = 0;
305 		c = !c;
306 	    }
307 	}			/* end while ( x+run < maxx ) */
308 #ifdef NOISY
309 fprintf( stderr, "end of line, c=%d, run=%d, bit=%d, x=%d\n", c, run, bit, x );
310 #endif
311 
312 	/* write rest of line */
313 	if ( c == 0 ) putwhitespan( run + (g3_page_width - maxx) );
314 	else
315 	{
316 	    putblackspan( run );
317 	    putwhitespan( g3_page_width - maxx );
318 	}
319         puteol();
320     }				/* end for ( all y ) */
321 }
322 
323 void convert_pbm _P2( (fd, g3_page_width), int fd, int g3_page_width )
324 {
325     int x, y;
326     int c, ch = 0;
327     int run;
328     FILE * fp;
329     int maxx;
330 
331     if ( ( fp = fdopen( fd, "r" ) ) == NULL )
332     {
333 	perror( "cannot fdopen: " );
334 	exit( 5 );
335     }
336 
337     /* maximum size of PELs to write pbm -> g3 */
338     if ( g3_page_width > pbm_xsize ) maxx = pbm_xsize;
339                                 else maxx = g3_page_width;
340 
341     for ( y = 0; y < pbm_ysize; y++ )
342     {
343 	c = '0';	/* start with white run length */
344 	run = 0;
345 
346 	x = 0;
347 	while ( x < maxx && ch != EOF )
348 	{
349 	    ch = fgetc( fp );
350 	    if ( ch == '#' )			/* comment lines */
351 	    {
352 		while ( ch != '\n' ) ch = fgetc( fp );
353 	    }
354 	    if ( ch == '0' || ch == '1' )	/* bits */
355 	    {
356 		x++;
357 		if ( ch == c ) run++;
358 		else
359 		{
360 		    if ( c == '0' ) putwhitespan( run );
361 		               else putblackspan( run );
362 		    c = ch;
363 		    run = 1;
364 		}
365 	    }
366 	}					/* end while (x<maxx) */
367 
368 	/* read remainder of line (if pbm was wider than the G3 page) */
369 	while ( x < pbm_xsize && ch != EOF )
370 	{
371 	    ch = fgetc( fp );
372 	    if ( ch == '#' )			/* comment lines */
373 	    { while ( ch != '\n' ) ch = fgetc( fp ); }
374 	    if ( ch == '0' || ch == '1' )	/* bits */
375 	    { x++; }
376 	}
377 
378 	if ( c == '0' ) putwhitespan( run + (g3_page_width - maxx) );
379 	else
380 	{
381 	    putblackspan( run );
382 	    putwhitespan( g3_page_width - maxx );
383 	}
384 
385 	puteol();
386     }
387 }
388 
389 
390 extern int	optind;
391 extern char *	optarg;
392 
393 int main _P2( (argc, argv), int argc, char ** argv )
394 {
395     int c, fd, i;
396     int empty_lines = 0;
397     int g3_page_width = 1728;
398     int digifax_header = FALSE;
399 
400     init_byte_tab( FALSE, out_byte_tab );
401 
402     while ( (c = getopt(argc, argv, "h:w:dar") ) != EOF)
403     {
404 	switch (c)
405 	{
406 	  case 'h': empty_lines = atoi( optarg ); break;
407 	  case 'w': g3_page_width = atoi( optarg ); break;
408 	  case 'd': digifax_header = TRUE; break;
409 	  case 'a': byte_align = TRUE; break;
410 	  case 'r': init_byte_tab( TRUE, out_byte_tab ); break;
411 
412 	  default: exit_usage( argv[0] );
413 	}
414     }
415 
416     if ( optind == argc	||
417 	 strcmp( argv[optind], "-" ) == 0 )	/* read from stdin */
418     {
419 	fd = 0;
420     }
421     else					/* read from file */
422     {
423 	if ( optind != argc -1 ) exit_usage( argv[0] );
424 
425 	fd = open( argv[optind], O_RDONLY );
426 	if ( fd == -1 )
427 	{
428 	    fprintf( stderr, "%s: cannot open %s: ", argv[0], argv[optind] );
429 	    perror( "" );
430 	    exit(2);
431 	}
432     }
433 
434     /* ok, open succeeded. now get file type */
435     pbm_getheader( fd );
436 
437     /* reject unknown file types */
438     if ( pbm_type == unknown )
439     {
440 	fprintf( stderr, "%s: input file type unknown\n", argv[0] );
441 	exit(3);
442     }
443 
444     /* barf if problems reading the header occured */
445     if ( pbm_xsize == -1 || pbm_ysize == -1 )
446     {
447 	fprintf( stderr, "%s: error reading PBM header\n", argv[0] );
448 	exit(4);
449     }
450 
451     /* unsupported bitmap types */
452 
453     if ( pbm_type == pgm || pbm_type == pgm_raw )
454     {
455 	fprintf( stderr, "%s: portable greymaps (pgm) not supported, use ``pgmtopbm'' first.\n", argv[0] );
456 	exit(3);
457     }
458     if ( pbm_type == ppm || pbm_type == ppm_raw )
459     {
460 	fprintf( stderr, "%s: portable pixmap (ppm) not supported, use ``ppmtopgm | pgmtopbm'' first.\n", argv[0] );
461 	exit(3);
462     }
463 
464     /* the only remaing types are PBM and PBM RawBits */
465 
466 
467     /* if the g3_page_width is 0, use the width of the pbm file */
468     if ( g3_page_width == 0 ) g3_page_width = pbm_xsize;
469 
470     /* if it's a RAW pbm file, round up the g3_page_width to
471      * multiples of 8 (to avoid byte-boundary problems)
472      */
473     if ( pbm_type == pbm_raw && g3_page_width < ( ( pbm_xsize+7 ) & ~7) )
474     {
475 	g3_page_width = ( g3_page_width + 7 ) & ~7;
476     }
477 
478     /* output leading EOL and possibly leading blank lines */
479     puteol();
480     for ( i=0; i<empty_lines; i++ )
481     {
482 	putwhitespan( g3_page_width ); puteol();
483     }
484 
485     /* convert file */
486     if ( pbm_type == pbm )
487     {
488 	convert_pbm( fd, g3_page_width );
489     }
490     else
491     {
492 	convert_pbm_raw( fd, g3_page_width );
493     }
494 
495     /* over & out */
496     close( fd );
497 
498     /* output final RTC */
499     for ( i=0; i<6; i++ ) puteol();
500 
501     /* flush buffer */
502     if ( out_hibit != 0 )
503         buf[buflen++] = out_byte_tab[out_data & 0xff];
504     write( 1, buf, buflen );
505 
506     exit(0);
507 }
508