1 #ident "$Id: g32pbm.c,v 4.3 1998/06/01 12:00:09 gert Exp $ (c) Gert Doering, Chris Lewis et.al." 2 3 /* G32pbm.c 4 * 5 * convert raw G3 data (optionally with a digifax header) to PBM or 6 * Laserjet, optionally scaling the image in the process. 7 * 8 * G3 and PBM code by Gert Doering 9 * HP Laserjet and scaling code by Chris Lewis 10 */ 11 12 #include <stdio.h> 13 #include <unistd.h> 14 #include "syslibs.h" 15 #include <string.h> 16 #include <fcntl.h> 17 18 #include "ugly.h" 19 20 #include "g3.h" 21 22 int pixblock _PROTO((char *start, char *end)); 23 int nullscan _PROTO((char *start, char *end)); 24 void emitlj _PROTO((int resolution, int numx, int numy, char *image)); 25 26 void emitpbm _PROTO((int hcol, int row, char *bitmap, int bperrow )); 27 28 29 #ifdef DEBUG 30 void putbin _P1( (d), unsigned long d ) 31 { 32 unsigned long i = 0x80000000; 33 34 while ( i!=0 ) 35 { 36 putc( ( d & i ) ? '1' : '0', stderr ); 37 i >>= 1; 38 } 39 putc( '\n', stderr ); 40 } 41 #endif 42 43 static int byte_tab[ 256 ]; 44 static int o_stretch; /* -stretch: double each line */ 45 static int o_stretch_force=-1; /* -1: guess from filename */ 46 static int o_lj; /* -l: LJ output */ 47 static int o_turn; /* -t: turn 90 degrees right */ 48 49 struct g3_tree * black, * white; 50 51 #define CHUNK 2048; 52 static char rbuf[2048]; /* read buffer */ 53 static int rp; /* read pointer */ 54 static int rs; /* read buffer size */ 55 56 #define MAX_ROWS 4300 57 #define MAX_COLS 1728 /* !! FIXME - command line parameter */ 58 59 #define BASERES 200 /* resolution of G3 */ 60 61 #define MVTYPE int 62 63 /* scale the bitmap */ 64 65 char *scalebm _P5( (res, cols, rows, map, bperrow), 66 int res, int *cols, int *rows, char *map, int *bperrow) 67 { 68 int nc, nr, i, newbperrow; 69 register char *orp, *nrp; 70 char *newmap; 71 MVTYPE *mulvec; 72 73 if ( res == BASERES ) /* don't do anything of not scaled */ 74 { 75 return map; 76 } 77 78 /* do scaling, from "BASERES" to "res" dpi */ 79 80 nr = (*rows * res) / BASERES; 81 nc = (((*cols * res) / BASERES) + 7) & ~7; 82 newbperrow = (nc + 7) >> 3; 83 84 85 newmap = malloc(nr * newbperrow ); 86 if (!newmap) 87 { 88 fprintf (stderr, "g32pbm: cannot allocate %d bytes for scale raster\n", 89 nr * newbperrow ); 90 exit(1); 91 } 92 memset( newmap, 0, nr * newbperrow ); 93 94 { 95 int max = ( *cols > *rows ) ? *cols: *rows; 96 MVTYPE *mv; 97 98 mulvec = (MVTYPE *) malloc(max * sizeof(MVTYPE)); 99 if (!mulvec) 100 { 101 fprintf (stderr, "g32pbm: cannot allocate multiplier vector\n"); 102 exit(1); 103 } 104 for (mv = mulvec, i = 0; i < max; i++) 105 { 106 *mv++ = (i * res) / BASERES; 107 } 108 } 109 110 orp = map; 111 112 for (i = 0; i < *rows; i++) 113 { 114 register MVTYPE *mv; 115 register int j; 116 nrp = newmap + (mulvec[i] * newbperrow); 117 118 for (j = 0, mv = mulvec; j < *cols; j++, mv++) 119 { 120 if (!(j & 0x7) && !orp[j >> 3]) 121 { 122 j += 8; 123 mv += 8; 124 continue; 125 } 126 127 if (orp[j >> 3] & (0x80 >> (j & 0x7))) 128 nrp[(*mv) >> 3] |= (0x80 >> ((*mv) & 0x7)); 129 } 130 131 orp += *bperrow; 132 } 133 134 free (map); 135 *rows = nr; 136 *cols = nc; 137 *bperrow = newbperrow; 138 return (newmap); 139 } 140 141 /* turn the bitmap */ 142 143 char * turnbm _P4 (( cols, rows, map, bperrow ), 144 int * cols, int * rows, char * map, int * bperrow ) 145 { 146 char * newmap; 147 int newbperrow, nr, nc, nx, ny; 148 149 register int obit; 150 register char * newbp, * obyte; 151 152 char * oldbp; 153 int byte, bit; 154 155 o_turn &= 3; 156 if ( o_turn == 0 ) return map; 157 158 /* turn right */ 159 nc = *rows; /* new columns */ 160 nr = *cols; /* new rows */ 161 newbperrow = ( nc+7 ) / 8; 162 163 newmap = malloc( nr * newbperrow ); 164 165 if ( newmap == NULL ) 166 { 167 fprintf( stderr, "g32pbm: cannot allocate %d bytes for turn bitmap", 168 nr * newbperrow ); 169 exit(1); 170 } 171 172 memset( newmap, 0, nr * newbperrow ); 173 174 for( nx = 0; nx<nc; nx++ ) /* new X coordinate */ 175 { 176 bit = 0x80 >> (nx&7); 177 byte = nx >> 3; 178 179 oldbp = &map[ (*rows - nx - 1) * *bperrow ]; 180 181 for ( ny = nr, newbp= &newmap[byte], /* new y */ 182 obyte = oldbp, obit=0x80; 183 ny>0; ny--, newbp += newbperrow ) 184 { 185 if ( (*obyte) & obit ) 186 { 187 *newbp |= bit; 188 } 189 obit >>= 1; if ( obit == 0 ) { obit=0x80; obyte++; } 190 } 191 } 192 193 free( map ); 194 *rows = nr; 195 *cols = nc; 196 *bperrow = newbperrow; 197 return newmap; 198 } 199 200 int main _P2( (argc, argv), int argc, char ** argv ) 201 { 202 int data; 203 int hibit; 204 struct g3_tree * p; 205 int nr_pels; 206 int fd; 207 int color; 208 int i; 209 int cons_eol; 210 211 int bperrow = MAX_COLS/8; /* bytes per bit row */ 212 char * bitmap; /* MAX_ROWS by (bperrow) bytes */ 213 char * bp; /* bitmap pointer */ 214 int row; 215 int max_rows; /* max. rows allocated */ 216 int col, hcol; /* column, highest column ever used */ 217 extern int optind; 218 extern char *optarg; 219 int resolution = BASERES; 220 221 /* initialize lookup trees */ 222 build_tree( &white, t_white ); 223 build_tree( &white, m_white ); 224 build_tree( &black, t_black ); 225 build_tree( &black, m_black ); 226 227 init_byte_tab( 0, byte_tab ); 228 229 while((i = getopt(argc, argv, "rsnfld:t")) != EOF) 230 { 231 switch (i) 232 { 233 case 'r': 234 init_byte_tab( 1, byte_tab ); 235 break; 236 case 's': /* stretch */ 237 case 'n': /* "n"ormal */ 238 o_stretch_force=1; 239 break; 240 case 'f': /* "f"ine */ 241 o_stretch_force=0; 242 break; 243 case 'l': 244 o_lj=1; 245 break; 246 case 'd': 247 resolution = atoi(optarg); 248 if ( resolution != 75 && resolution != 150 && 249 resolution != 300 ) 250 { 251 fprintf( stderr, "g32pbm: only supports 75, 150, or 300 dpi\n"); 252 exit(1); 253 } 254 break; 255 case 't': 256 o_turn++; 257 break; 258 case '?': 259 fprintf( stderr, "usage: g32pbm [-l|-r|-n/f|-d <dpi>|-t] [g3 file]\n"); 260 exit(1); 261 } 262 } 263 264 if (o_lj && resolution == BASERES) 265 resolution = 150; 266 267 o_stretch = (o_stretch_force<0) ? 0: o_stretch_force; 268 269 if ( optind < argc ) /* read from file */ 270 { 271 if ( o_stretch_force < 0 ) /* no resolution given yet */ 272 { 273 char * p = argv[optind]; 274 if ( strrchr( p, '/' ) != NULL ) p = strrchr( p, '/' )+1; 275 o_stretch = ( p[1] == 'n' ); /* 'fn...' -> stretch */ 276 } 277 278 fd = open( argv[optind], O_RDONLY ); 279 if ( fd == -1 ) 280 { perror( argv[optind] ); exit( 1 ); } 281 } 282 else 283 fd = 0; 284 285 hibit = 0; 286 data = 0; 287 288 cons_eol = 0; /* consecutive EOLs read - zero yet */ 289 290 color = 0; /* start with white */ 291 292 rs = read( fd, rbuf, sizeof(rbuf) ); 293 if ( rs < 0 ) { perror( "read" ); close( rs ); exit(8); } 294 295 /* skip GhostScript header */ 296 rp = ( rs >= 64 && strcmp( rbuf+1, "PC Research, Inc" ) == 0 ) ? 64 : 0; 297 298 /* initialize bitmap */ 299 300 row = col = hcol = 0; 301 bitmap = (char *) malloc( ( max_rows = MAX_ROWS ) * MAX_COLS / 8 ); 302 if ( bitmap == NULL ) 303 { 304 fprintf( stderr, "cannot allocate %d bytes for bitmap", 305 max_rows * MAX_COLS/8 ); 306 close( fd ); 307 exit(9); 308 } 309 memset( bitmap, 0, max_rows * MAX_COLS/8 ); 310 bp = &bitmap[ row * MAX_COLS/8 ]; 311 312 while ( rs > 0 && cons_eol < 4 ) /* i.e., while (!EOF) */ 313 { 314 #ifdef DEBUG 315 fprintf( stderr, "hibit=%2d, data=", hibit ); 316 putbin( data ); 317 #endif 318 while ( hibit < 20 ) 319 { 320 data |= ( byte_tab[ (int) (unsigned char) rbuf[ rp++] ] << hibit ); 321 hibit += 8; 322 323 if ( rp >= rs ) 324 { 325 rs = read( fd, rbuf, sizeof( rbuf ) ); 326 if ( rs < 0 ) { perror( "read2"); break; } 327 rp = 0; 328 if ( rs == 0 ) { goto do_write; } 329 } 330 #ifdef DEBUG 331 fprintf( stderr, "hibit=%2d, data=", hibit ); 332 putbin( data ); 333 #endif 334 } 335 336 if ( color == 0 ) /* white */ 337 p = white->nextb[ data & BITM ]; 338 else /* black */ 339 p = black->nextb[ data & BITM ]; 340 341 while ( p != NULL && ! ( p->nr_bits ) ) 342 { 343 data >>= BITS; 344 hibit -= BITS; 345 p = p->nextb[ data & BITM ]; 346 } 347 348 if ( p == NULL ) /* invalid code */ 349 { 350 fprintf( stderr, "invalid code, row=%d, col=%d, file offset=%lx, skip to eol\n", 351 row, col, (unsigned long) lseek( fd, 0, 1 ) - rs + rp ); 352 while ( ( data & 0x03f ) != 0 ) 353 { 354 data >>= 1; hibit--; 355 if ( hibit < 20 ) 356 { 357 data |= ( byte_tab[ (int) (unsigned char) rbuf[ rp++] ] << hibit ); 358 hibit += 8; 359 360 if ( rp >= rs ) /* buffer underrun */ 361 { rs = read( fd, rbuf, sizeof( rbuf ) ); 362 if ( rs < 0 ) { perror( "read4"); break; } 363 rp = 0; 364 if ( rs == 0 ) goto do_write; 365 } 366 } 367 } 368 nr_pels = -1; /* handle as if eol */ 369 } 370 else /* p != NULL <-> valid code */ 371 { 372 data >>= p->nr_bits; 373 hibit -= p->nr_bits; 374 375 nr_pels = ( (struct g3_leaf *) p ) ->nr_pels; 376 #ifdef DEBUG 377 fprintf( stderr, "PELs: %d (%c)\n", nr_pels, '0'+color ); 378 #endif 379 } 380 381 /* handle EOL (including fill bits) */ 382 if ( nr_pels == -1 ) 383 { 384 #ifdef DEBUG 385 fprintf( stderr, "hibit=%2d, data=", hibit ); 386 putbin( data ); 387 #endif 388 /* skip filler 0bits -> seek for "1"-bit */ 389 while ( ( data & 0x01 ) != 1 ) 390 { 391 if ( ( data & 0xf ) == 0 ) /* nibble optimization */ 392 { 393 hibit-= 4; data >>= 4; 394 } 395 else 396 { 397 hibit--; data >>= 1; 398 } 399 /* fill higher bits */ 400 if ( hibit < 20 ) 401 { 402 data |= ( byte_tab[ (int) (unsigned char) rbuf[ rp++] ] << hibit ); 403 hibit += 8; 404 405 if ( rp >= rs ) /* buffer underrun */ 406 { rs = read( fd, rbuf, sizeof( rbuf ) ); 407 if ( rs < 0 ) { perror( "read3"); break; } 408 rp = 0; 409 if ( rs == 0 ) goto do_write; 410 } 411 } 412 #ifdef DEBUG 413 fprintf( stderr, "hibit=%2d, data=", hibit ); 414 putbin( data ); 415 #endif 416 } /* end skip 0bits */ 417 hibit--; data >>=1; 418 419 color=0; 420 421 if ( col == 0 ) 422 cons_eol++; /* consecutive EOLs */ 423 else 424 { 425 if ( col > hcol && col <= MAX_COLS ) hcol = col; 426 row++; 427 428 /* bitmap memory full? make it larger! */ 429 if ( row >= max_rows ) 430 { 431 char * p = realloc( bitmap, 432 ( max_rows += 500 ) * MAX_COLS/8 ); 433 if ( p == NULL ) 434 { 435 perror( "realloc() failed, page truncated" ); 436 rs = 0; 437 } 438 else 439 { 440 bitmap = p; 441 memset( &bitmap[ row * MAX_COLS/8 ], 0, 442 ( max_rows - row ) * MAX_COLS/8 ); 443 } 444 } 445 446 col=0; bp = &bitmap[ row * MAX_COLS/8 ]; 447 cons_eol = 0; 448 } 449 } 450 else /* not eol */ 451 { 452 if ( col+nr_pels > MAX_COLS ) nr_pels = MAX_COLS - col; 453 454 if ( color == 0 ) /* white */ 455 col += nr_pels; 456 else /* black */ 457 { 458 register int bit = ( 0x80 >> ( col & 07 ) ); 459 register char *w = & bp[ col>>3 ]; 460 461 for ( i=nr_pels; i > 0; i-- ) 462 { 463 *w |= bit; 464 bit >>=1; if ( bit == 0 ) { bit = 0x80; w++; } 465 col++; 466 } 467 } 468 if ( nr_pels < 64 ) color = !color; /* terminating code */ 469 } 470 } /* end main loop */ 471 472 do_write: /* write pbm (or whatever) file */ 473 474 if( fd != 0 ) close(fd); /* close input file */ 475 476 #ifdef DEBUG 477 fprintf( stderr, "consecutive EOLs: %d, max columns: %d\n", cons_eol, hcol ); 478 #endif 479 480 bitmap = scalebm(resolution, &hcol, &row, bitmap, &bperrow ); 481 482 bitmap = turnbm( &hcol, &row, bitmap, &bperrow ); 483 484 if (o_lj) 485 emitlj(resolution, hcol, row, bitmap); 486 else 487 emitpbm(hcol, row, bitmap, bperrow ); 488 489 490 return 0; 491 } 492 493 /* hcol is the number of columns, row the number of rows 494 * bperrow is the number of bytes actually used by hcol, which may 495 * be greater than (hcol+7)/8 [in case of an unscaled g3 image less 496 * than 1728 pixels wide] 497 */ 498 499 void emitpbm _P4(( hcol, row, bitmap, bperrow), 500 int hcol, int row, char *bitmap, int bperrow ) 501 { 502 register int i; 503 504 sprintf( rbuf, "P4\n%d %d\n", hcol, ( o_stretch? row*2 : row ) ); 505 write( 1, rbuf, strlen( rbuf )); 506 507 if ( hcol == (bperrow*8) && !o_stretch ) 508 write( 1, bitmap, row * bperrow ); 509 else 510 { 511 if ( !o_stretch ) 512 for ( i=0; i<row; i++ ) 513 { 514 write( 1, &bitmap[ i*bperrow ], (hcol+7)/8 ); 515 } 516 else /* Double each row */ 517 for ( i=0; i<row; i++ ) 518 { 519 write( 1, &bitmap[ i*bperrow ], (hcol+7)/8 ); 520 write( 1, &bitmap[ i*bperrow ], (hcol+7)/8 ); 521 } 522 } 523 } 524 525 /* The following code is copyright 1994, Chris Lewis. Permission is hereby 526 permanently granted to Gert Doering to include this software in his 527 g32pbm program, whether distributed as freeware or commercially. 528 */ 529 530 #define ESCLEN 25 /* avg # bytes in raster escape prolog/epilog */ 531 532 int nullscan _P2((start, end), char *start, char *end) 533 { 534 register char *cur; 535 for (cur = start; cur < end && !*cur; cur++); 536 return(cur - start); 537 } 538 539 int pixblock _P2((start, end), char *start, char *end) 540 { 541 register char *cur; 542 register int numnulls; 543 if (end - start <= ESCLEN * 2) /* no point optimizing */ 544 return(end - start); 545 cur = start; 546 547 while(cur < end) { 548 549 for(; *cur && cur < end; cur++); 550 551 numnulls = nullscan(cur, end); 552 553 if (numnulls > ESCLEN) 554 return(cur - start); 555 else 556 cur += numnulls; 557 } 558 return(end - start); 559 } 560 561 void emitlj _P4((resolution, numx, numy, image), 562 int resolution, int numx, int numy, char *image) 563 { 564 int bperline; 565 int resmult = 300/resolution; 566 register char *ip, *lineanch, *nip; 567 register int currow, bcount; 568 bperline = ((numx + 7) / 8); 569 570 /* some spoolers use a "cut" to do printer-type selection 571 (eg: "%!" processing). The newline is to prevent cut 572 (or other line-length-limited UNIX utilities) dying. */ 573 printf("\033*t%dR\n", resolution); 574 575 for(currow = 0; currow < numy; currow++) 576 { 577 lineanch = ip = &image[bperline * currow]; 578 nip = ip + bperline; 579 while (ip < nip && !*ip) ip++; 580 if (ip >= nip) 581 continue; /* line has no pixels */ 582 while (!*(nip - 1)) nip--; /* truncate trailing nulls */ 583 while (ip < nip) { /* inv: !*ip && !*nip */ 584 bcount = pixblock(ip, nip); 585 printf("\033*p%dx%dY\033*r1A\033*b%dW", 586 (int) ((ip - lineanch) * 8 * resmult), (currow * resmult) << o_stretch, bcount); 587 fwrite(ip, 1, bcount, stdout); 588 if (o_stretch) { 589 printf("\033*b%dW", bcount); 590 fwrite(ip, 1, bcount, stdout); 591 } 592 fputs("\033*rB", stdout); 593 for(ip += bcount; ip < nip && !*ip; ip++); 594 } 595 } 596 putchar('\f'); 597 } 598