/* * jbgtopbm - JBIG to Portable Bitmap converter * * Markus Kuhn - http://www.cl.cam.ac.uk/~mgk25/jbigkit/ */ #include #include #include #include #include "jbig.h" char *progname; /* global pointer to argv[0] */ /* * Print usage message and abort */ static void usage(void) { fprintf(stderr, "JBIGtoPBM converter " JBG_VERSION " -- " "reads a bi-level image entity (BIE) as input file\n\n" "usage: %s [] [ | - []]\n\n" "options:\n\n", progname); fprintf(stderr, " -x number\tif possible decode only up to a resolution layer not\n" "\t\twider than the given number of pixels\n" " -y number\tif possible decode only up to a resolution layer not\n" "\t\thigher than the given number of pixels\n" " -m\t\tdecode a progressive sequence of multiple concatenated BIEs\n" " -b\t\tuse binary code for multiple bit planes (default: Gray code)\n" " -d\t\tdiagnose single BIE, print header, list marker sequences\n" " -p number\tdecode only one single bit plane (0 = first plane)\n\n"); exit(1); } /* * Call-back routine for merged image output */ void write_it(unsigned char *data, size_t len, void *file) { fwrite(data, len, 1, (FILE *) file); } /* * Remalloc a buffer and append a file f into its content. * If *buflen == 0, then malloc a buffer first. */ void read_file(unsigned char **buf, size_t *buflen, size_t *len, FILE *f) { if (*buflen == 0) { *buflen = 4000; *len = 0; *buf = (unsigned char *) malloc(*buflen); if (!*buf) { fprintf(stderr, "Sorry, not enough memory available!\n"); exit(1); } } do { *len += fread(*buf + *len, 1, *buflen - *len, f); if (*len == *buflen) { *buflen *= 2; *buf = (unsigned char *) realloc(*buf, *buflen); if (!*buf) { fprintf(stderr, "Sorry, not enough memory available!\n"); exit(1); } } if (ferror(f)) { perror("Problem while reading input file"); exit(1); } } while (!feof(f)); if (!*len) return; *buflen = *len; *buf = (unsigned char *) realloc(*buf, *buflen); if (!*buf) { fprintf(stderr, "Oops, realloc failed when shrinking buffer!\n"); exit(1); } return; } /* marker codes */ #define MARKER_STUFF 0x00 #define MARKER_SDNORM 0x02 #define MARKER_SDRST 0x03 #define MARKER_ABORT 0x04 #define MARKER_NEWLEN 0x05 #define MARKER_ATMOVE 0x06 #define MARKER_COMMENT 0x07 #define MARKER_ESC 0xff /* * Output (prefix of) a short byte sequence in hexadecimal * for diagnostic purposes */ void fprint_bytes(FILE *f, unsigned char *p, size_t len, int width) { size_t i; size_t max = width / 3; if (len > max) max -= 7; for (i = 0; i < len && i < max; i++) fprintf(f, "%02x ", p[i]); if (len > i) fprintf(f, "... %lu bytes total", (unsigned long) len); fprintf(f, "\n"); } /* * Read BIE and output human readable description of content */ void diagnose_bie(FILE *fin) { unsigned char *bie, *p, *pnext; size_t buflen = 0, len; unsigned long xd, yd, l0; int dl, d; FILE *f = stdout; extern unsigned char *jbg_next_pscdms(unsigned char *p, size_t len); extern unsigned long jbg_stripes(unsigned long l0, unsigned long yd, unsigned long d); unsigned long stripes; int layers, planes; unsigned long sdes, sde = 0; double size; /* read BIH */ read_file(&bie, &buflen, &len, fin); if (len < 20) { fprintf(f, "Error: Input file is %lu < 20 bytes long and therefore " "does not contain an intact BIE header!\n", (unsigned long) len); free(bie); return; } /* parse BIH */ fprintf(f, "BIH:\n\n DL = %d\n D = %d\n P = %d\n" " - = %d\n XD = %lu\n YD = %lu\n L0 = %lu\n MX = %d\n" " MY = %d\n", dl = bie[0], d = bie[1], planes = bie[2], bie[3], xd = ((unsigned long)bie[ 4] << 24) | ((unsigned long)bie[ 5] << 16)| ((unsigned long) bie[ 6] << 8) | ((unsigned long) bie[ 7]), yd = ((unsigned long)bie[ 8] << 24) | ((unsigned long)bie[ 9] << 16)| ((unsigned long) bie[10] << 8) | ((unsigned long) bie[11]), l0 = ((unsigned long)bie[12] << 24) | ((unsigned long)bie[13] << 16)| ((unsigned long) bie[14] << 8) | ((unsigned long) bie[15]), bie[16], bie[17]); fprintf(f, " order = %d %s%s%s%s%s\n", bie[18], bie[18] & JBG_HITOLO ? " HITOLO" : "", bie[18] & JBG_SEQ ? " SEQ" : "", bie[18] & JBG_ILEAVE ? " ILEAVE" : "", bie[18] & JBG_SMID ? " SMID" : "", bie[18] & 0xf0 ? " other" : ""); fprintf(f, " options = %d %s%s%s%s%s%s%s%s\n\n", bie[19], bie[19] & JBG_LRLTWO ? " LRLTWO" : "", bie[19] & JBG_VLENGTH ? " VLENGTH" : "", bie[19] & JBG_TPDON ? " TPDON" : "", bie[19] & JBG_TPBON ? " TPBON" : "", bie[19] & JBG_DPON ? " DPON" : "", bie[19] & JBG_DPPRIV ? " DPPRIV" : "", bie[19] & JBG_DPLAST ? " DPLAST" : "", bie[19] & 0x80 ? " other" : ""); if (d < dl) { fprintf(f, "D >= DL required!\n"); return; } if (l0 == 0) { fprintf(f, "L0 > 0 required!\n"); return; } stripes = jbg_stripes(l0, yd, d); layers = d - dl + 1; fprintf(f, " %lu stripes, %d layers, %d planes => ", stripes, layers, planes); if (planes == 0 || (ULONG_MAX / layers) / planes >= stripes) { sdes = stripes * layers * planes; fprintf(f, "%lu SDEs\n", sdes); } else { /* handle integer overflow */ fprintf(f, ">%lu SDEs!\n", ULONG_MAX); return; } size = (double) planes * (double) yd * (double) jbg_ceil_half(xd, 3); fprintf(f, " decompressed %.15g bytes\n\n", size); if (planes == 0) { fprintf(f, "P > 0 required!\n\n"); } /* parse BID */ fprintf(f, "BID:\n\n"); p = bie + 20; /* skip BIH */ if ((bie[19] & (JBG_DPON | JBG_DPPRIV | JBG_DPLAST)) == (JBG_DPON | JBG_DPPRIV)) p += 1728; /* skip DPTABLE */ if (p > bie + len) { fprintf(f, "Error: Input file is %lu < 20+1728 bytes long and therefore " "does not contain an intact BIE header with DPTABLE!\n", (unsigned long) len); return; } while (p != bie + len) { if (p > bie + len - 2) { fprintf(f, "%06lx: Error: single byte 0x%02x left\n", (long) (p - bie), *p); return; } pnext = jbg_next_pscdms(p, len - (p - bie)); if (p[0] != MARKER_ESC || p[1] == MARKER_STUFF) { fprintf(f, "%06lx: PSCD: ", (long) (p - bie)); fprint_bytes(f, p, pnext ? (size_t) (pnext - p) : len - (p - bie), 60); if (!pnext) { fprintf(f, "Error: PSCD not terminated by SDNORM or SDRST marker\n"); return; } } else switch (p[1]) { case MARKER_SDNORM: case MARKER_SDRST: fprintf(f, "%06lx: ESC %s, ending SDE #%lu", (long) (p - bie), (p[1] == MARKER_SDNORM) ? "SDNORM" : "SDRST", ++sde); if (sde == sdes) fprintf(f, " (final SDE)"); else if (sde == sdes + 1) fprintf(f, " (first surplus SDE, VLENGTH = %d)", (bie[19] & JBG_VLENGTH) > 0); fprintf(f, "\n"); break; case MARKER_ABORT: fprintf(f, "%06lx: ESC ABORT\n", (long) (p - bie)); break; case MARKER_NEWLEN: fprintf(f, "%06lx: ESC NEWLEN ", (long) (p - bie)); if (p + 5 < bie + len) { fprintf(f, "YD = %lu\n", yd = (((long) p[2] << 24) | ((long) p[3] << 16) | ((long) p[4] << 8) | (long) p[5])); stripes = jbg_stripes(l0, yd, d); fprintf(f, " %lu stripes, %d layers, %d planes => ", stripes, layers, planes); if ((ULONG_MAX / layers) / planes >= stripes) { sdes = stripes * layers * planes; fprintf(f, "%lu SDEs\n", sdes); } else { /* handle integer overflow */ fprintf(f, ">%lu SDEs!\n", ULONG_MAX); return; } } else fprintf(f, "unexpected EOF\n"); break; case MARKER_ATMOVE: fprintf(f, "%06lx: ESC ATMOVE ", (long) (p - bie)); if (p + 7 < bie + len) fprintf(f, "YAT = %lu, tX = %d, tY = %d\n", (((long) p[2] << 24) | ((long) p[3] << 16) | ((long) p[4] << 8) | (long) p[5]), p[6], p[7]); else fprintf(f, "unexpected EOF\n"); break; case MARKER_COMMENT: fprintf(f, "%06lx: ESC COMMENT ", (long) (p - bie)); if (p + 5 < bie + len) fprintf(f, "LC = %lu\n", (((long) p[2] << 24) | ((long) p[3] << 16) | ((long) p[4] << 8) | (long) p[5])); else fprintf(f, "unexpected EOF\n"); break; default: fprintf(f, "%06lx: ESC 0x%02x\n", (long) (p - bie), p[1]); } if (!pnext) { fprintf(f, "Error encountered!\n"); return; } p = pnext; } free(bie); return; } int main (int argc, char **argv) { FILE *fin = stdin, *fout = stdout; const char *fnin = NULL, *fnout = NULL; int i, j, result; int all_args = 0, files = 0; struct jbg_dec_state s; unsigned char *buffer, *p; size_t buflen, len, cnt; size_t bytes_read = 0; unsigned long xmax = 4294967295UL, ymax = 4294967295UL, max; int plane = -1, use_graycode = 1, diagnose = 0, multi = 0; buflen = 8000; buffer = (unsigned char *) malloc(buflen); if (!buffer) { printf("Sorry, not enough memory available!\n"); exit(1); } /* parse command line arguments */ progname = argv[0]; for (i = 1; i < argc; i++) { if (!all_args && argv[i][0] == '-') if (argv[i][1] == 0) { if (files++) usage(); } else for (j = 1; j > 0 && argv[i][j]; j++) switch(argv[i][j]) { case '-' : all_args = 1; break; case 'b': use_graycode = 0; break; case 'm': multi = 1; break; case 'd': diagnose = 1; break; case 'x': if (++i >= argc) usage(); xmax = atol(argv[i]); j = -1; break; case 'y': if (++i >= argc) usage(); ymax = atol(argv[i]); j = -1; break; case 'p': if (++i >= argc) usage(); plane = atoi(argv[i]); j = -1; break; default: usage(); } else switch (files++) { case 0: fnin = argv[i]; break; case 1: fnout = argv[i]; break; default: usage(); } } if (fnin) { fin = fopen(fnin, "rb"); if (!fin) { fprintf(stderr, "Can't open input file '%s", fnin); perror("'"); exit(1); } } else fnin = ""; if (diagnose) { diagnose_bie(fin); exit(0); } if (fnout) { fout = fopen(fnout, "wb"); if (!fout) { fprintf(stderr, "Can't open input file '%s", fnout); perror("'"); exit(1); } } else fnout = ""; /* send input file to decoder */ jbg_dec_init(&s); jbg_dec_maxsize(&s, xmax, ymax); /* read BIH first to check VLENGTH */ len = fread(buffer, 1, 20, fin); if (len < 20) { fprintf(stderr, "Input file '%s' (%lu bytes) must be at least " "20 bytes long\n", fnin, (unsigned long) len); if (fout != stdout) { fclose(fout); remove(fnout); } exit(1); } if (buffer[19] & JBG_VLENGTH) { /* VLENGTH = 1 => we might encounter a NEWLEN, therefore read entire * input file into memory and run two passes over it */ read_file(&buffer, &buflen, &len, fin); /* scan for NEWLEN marker segments and update BIE header accordingly */ result = jbg_newlen(buffer, len); /* feed data to decoder */ if (result == JBG_EOK) { p = (unsigned char *) buffer; result = JBG_EAGAIN; while (len > 0 && (result == JBG_EAGAIN || (result == JBG_EOK && multi))) { result = jbg_dec_in(&s, p, len, &cnt); p += cnt; len -= cnt; bytes_read += cnt; } } } else { /* VLENGTH = 0 => we can simply pass the input file directly to decoder */ result = JBG_EAGAIN; do { cnt = 0; p = (unsigned char *) buffer; while (len > 0 && (result == JBG_EAGAIN || (result == JBG_EOK && multi))) { result = jbg_dec_in(&s, p, len, &cnt); p += cnt; len -= cnt; bytes_read += cnt; } if (!(result == JBG_EAGAIN || (result == JBG_EOK && multi))) break; len = fread(buffer, 1, buflen, fin); } while (len > 0); if (ferror(fin)) { fprintf(stderr, "Problem while reading input file '%s", fnin); perror("'"); if (fout != stdout) { fclose(fout); remove(fnout); } exit(1); } } if (result != JBG_EOK && result != JBG_EOK_INTR) { fprintf(stderr, "Problem with input file '%s': %s\n" "(error code 0x%02x, %lu = 0x%04lx BIE bytes processed)\n", fnin, jbg_strerror(result), result, (unsigned long) bytes_read, (unsigned long) bytes_read); if (fout != stdout) { fclose(fout); remove(fnout); } exit(1); } if (plane >= 0 && jbg_dec_getplanes(&s) <= plane) { fprintf(stderr, "Image has only %d planes!\n", jbg_dec_getplanes(&s)); if (fout != stdout) { fclose(fout); remove(fnout); } exit(1); } if (jbg_dec_getplanes(&s) == 1 || plane >= 0) { /* write PBM output file */ fprintf(fout, "P4\n%10lu\n%10lu\n", jbg_dec_getwidth(&s), jbg_dec_getheight(&s)); fwrite(jbg_dec_getimage(&s, plane < 0 ? 0 : plane), 1, jbg_dec_getsize(&s), fout); } else { /* write PGM output file */ if ((size_t) jbg_dec_getplanes(&s) > sizeof(unsigned long) * 8) { fprintf(stderr, "Image has too many planes (%d)!\n", jbg_dec_getplanes(&s)); if (fout != stdout) { fclose(fout); remove(fnout); } exit(1); } max = 0; for (i = jbg_dec_getplanes(&s); i > 0; i--) max = (max << 1) | 1; fprintf(fout, "P5\n%10lu\n%10lu\n%lu\n", jbg_dec_getwidth(&s), jbg_dec_getheight(&s), max); jbg_dec_merge_planes(&s, use_graycode, write_it, fout); } /* check for file errors and close fout */ if (ferror(fout) || fclose(fout)) { fprintf(stderr, "Problem while writing output file '%s", fnout); perror("'"); exit(1); } jbg_dec_free(&s); return 0; }