diff --git a/pbmtools/pbmtojbg.c b/pbmtools/pbmtojbg.c index 90c4581..3efefb9 100644 --- a/pbmtools/pbmtojbg.c +++ b/pbmtools/pbmtojbg.c @@ -86,7 +86,11 @@ static unsigned long getint(FILE *f) while ((c = getc(f)) != EOF && !(c == 13 || c == 10)) ; if (c != EOF) { ungetc(c, f); - fscanf(f, "%lu", &i); + if (fscanf(f, "%lu", &i) != 1) { + /* should never fail, since c must be a digit */ + fprintf(stderr, "Unexpected failure reading digit '%c'\n", c); + exit(1); + } } return i; @@ -300,7 +304,9 @@ int main (int argc, char **argv) break; case '4': /* PBM raw binary format */ - fread(bitmap[0], bitmap_size, 1, fin); + if (fread(bitmap[0], bitmap_size, 1, fin) != 1) { + /* silence compiler warnings; ferror/feof checked below */ + } break; case '2': case '5': @@ -312,8 +318,18 @@ int main (int argc, char **argv) for (j = 0; j < bpp; j++) image[x * bpp + (bpp - 1) - j] = v >> (j * 8); } - } else - fread(image, width * height, bpp, fin); + } else { + if (fread(image, width * height, bpp, fin) != (size_t) bpp) { + if (ferror(fin)) { + fprintf(stderr, "Problem while reading input file '%s", fnin); + perror("'"); + exit(1); + } else { + fprintf(stderr, "Unexpected end of input file '%s'!\n", fnin); + exit(1); + } + } + } jbg_split_planes(width, height, planes, encode_planes, image, bitmap, use_graycode); free(image); diff --git a/pbmtools/pbmtojbg.c.warnings b/pbmtools/pbmtojbg.c.warnings new file mode 100644 index 0000000..90c4581 --- /dev/null +++ b/pbmtools/pbmtojbg.c.warnings @@ -0,0 +1,433 @@ +/* + * pbmtojbg - Portable Bitmap to JBIG 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] */ +unsigned long total_length = 0; /* used for determining output file length */ + + +/* + * Print usage message and abort + */ +static void usage(void) +{ + fprintf(stderr, + "PBMtoJBIG converter " JBG_VERSION " -- " + "creates bi-level image entity (BIE) as output file\n\n" + "usage: %s [] [ | - []]\n\n" + "options:\n\n", progname); + fprintf(stderr, + " -q\t\tsequential coding, no differential layers (like -d 0)\n" + " -x number\tmaximum width of lowest resolution layer (default 640)\n" + " -y number\tmaximum height of lowest resolution layer (default 480)\n" + " -l number\tlowest layer written to output file (default 0)\n" + " -h number\thighest layer written to output file (default max)\n" + " -b\t\tuse binary code for multiple bitplanes (default: Gray code)\n" + " -d number\ttotal number of differential layers (overrides -x and -y)\n" + " -s number\theight of a stripe in layer 0\n"); + fprintf(stderr, + " -m number\tmaximum adaptive template pixel horizontal offset (default 8)\n" + " -t number\tencode only that many most significant planes\n" + " -o number\torder byte value: add 1=SMID, 2=ILEAVE, 4=SEQ, 8=HITOLO\n" + "\t\t(default 3 = ILEAVE+SMID)\n" + " -p number\toptions byte value: add DPON=4, TPBON=8, TPDON=16, LRLTWO=64\n" + "\t\t(default 28 = DPON+TPBON+TPDON)\n"); + fprintf(stderr, + " -C string\tadd the provided string as a comment marker segment\n" + " -c\t\tdelay adaptive template changes to first line of next stripe\n" + "\t\t(only provided for a conformance test)\n" + " -r\t\tterminate each stripe with SDRST marker\n" + "\t\t(only intended for decoder testing)\n" ); + fprintf(stderr, + " -Y number\tannounce in header initially this larger image height\n" + "\t\t(only for generating test files with NEWLEN and VLENGTH=1)\n" + " -f\t\tchose encoding options for T.85 fax profile complianance\n" + " -v\t\tverbose output\n\n"); + exit(1); +} + + +/* + * malloc() with exception handler + */ +void *checkedmalloc(size_t n) +{ + void *p; + + if ((p = malloc(n)) == NULL) { + fprintf(stderr, "Sorry, not enough memory available!\n"); + exit(1); + } + + return p; +} + + +/* + * Read an ASCII integer number from file f and skip any PBM + * comments which are encountered. + */ +static unsigned long getint(FILE *f) +{ + int c; + unsigned long i; + + while ((c = getc(f)) != EOF && !isdigit(c)) + if (c == '#') + while ((c = getc(f)) != EOF && !(c == 13 || c == 10)) ; + if (c != EOF) { + ungetc(c, f); + fscanf(f, "%lu", &i); + } + + return i; +} + + +/* + * Callback procedure which is used by JBIG encoder to deliver the + * encoded data. It simply sends the bytes to the output file. + */ +static void data_out(unsigned char *start, size_t len, void *file) +{ + fwrite(start, len, 1, (FILE *) file); + total_length += len; + return; +} + + +int main (int argc, char **argv) +{ + FILE *fin = stdin, *fout = stdout; + const char *fnin = NULL, *fnout = NULL; + int i, j, c; + int all_args = 0, files = 0; + unsigned long x, y; + unsigned long width, height, max, v; + unsigned long bpl; + int bpp, planes, encode_planes = -1; + size_t bitmap_size; + char type; + unsigned char **bitmap, *p, *image; + struct jbg_enc_state s; + int verbose = 0, delay_at = 0, reset = 0, use_graycode = 1; + long mwidth = 640, mheight = 480; + int dl = -1, dh = -1, d = -1, mx = -1; + unsigned long l0 = 0, y1 = 0; + char *comment = NULL; + int options = JBG_TPDON | JBG_TPBON | JBG_DPON; + int order = JBG_ILEAVE | JBG_SMID; + + /* 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 0 : + if (files++) usage(); + break; + case 'v': + verbose = 1; + break; + case 'b': + use_graycode = 0; + break; + case 'c': + delay_at = 1; + break; + case 'r': + reset = 1; + break; + case 'f': + d = 0; + order = 0; + options = 8; + l0 = 128; + encode_planes = 1; + mx = 127; + break; + case 'x': + if (++i >= argc) usage(); + j = -1; + mwidth = atol(argv[i]); + break; + case 'y': + if (++i >= argc) usage(); + j = -1; + mheight = atol(argv[i]); + break; + case 'Y': + if (++i >= argc) usage(); + j = -1; + y1 = atol(argv[i]); + break; + case 'o': + if (++i >= argc) usage(); + j = -1; + order = atoi(argv[i]); + break; + case 'p': + if (++i >= argc) usage(); + j = -1; + options = atoi(argv[i]); + break; + case 'l': + if (++i >= argc) usage(); + j = -1; + dl = atoi(argv[i]); + break; + case 'h': + if (++i >= argc) usage(); + j = -1; + dh = atoi(argv[i]); + break; + case 'q': + d = 0; + break; + case 'd': + if (++i >= argc) usage(); + j = -1; + d = atoi(argv[i]); + break; + case 's': + if (++i >= argc) usage(); + j = -1; + l0 = atol(argv[i]); + break; + case 't': + if (++i >= argc) usage(); + j = -1; + encode_planes = atoi(argv[i]); + break; + case 'm': + if (++i >= argc) usage(); + j = -1; + mx = atoi(argv[i]); + break; + case 'C': + if (++i >= argc) usage(); + j = -1; + comment = argv[i]; + 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 (fnout) { + fout = fopen(fnout, "wb"); + if (!fout) { + fprintf(stderr, "Can't open input file '%s", fnout); + perror("'"); + exit(1); + } + } else + fnout = ""; + + /* read PBM header */ + while ((c = getc(fin)) != EOF && (isspace(c) || c == '#')) + if (c == '#') + while ((c = getc(fin)) != EOF && !(c == 13 || c == 10)) ; + if (c != 'P') { + fprintf(stderr, "Input file '%s' does not look like a PBM file!\n", fnin); + exit(1); + } + type = getc(fin); + width = getint(fin); + height = getint(fin); + if (type == '2' || type == '5' || + type == '3' || type == '6') + max = getint(fin); + else + max = 1; + for (planes = 0, v = max; v; planes++, v >>= 1) ; + bpp = (planes + 7) / 8; + if (encode_planes < 0 || encode_planes > planes) + encode_planes = planes; + fgetc(fin); /* skip line feed */ + + /* read PBM image data */ + bpl = (width + 7) / 8; /* bytes per line */ + bitmap_size = bpl * (size_t) height; + bitmap = (unsigned char **) checkedmalloc(sizeof(unsigned char *) * + encode_planes); + for (i = 0; i < encode_planes; i++) + bitmap[i] = (unsigned char *) checkedmalloc(bitmap_size); + switch (type) { + case '1': + /* PBM text format */ + p = bitmap[0]; + for (y = 0; y < height; y++) + for (x = 0; x <= ((width-1) | 7); x++) { + *p <<= 1; + if (x < width) + *p |= getint(fin) & 1; + if ((x & 7) == 7) + ++p; + } + break; + case '4': + /* PBM raw binary format */ + fread(bitmap[0], bitmap_size, 1, fin); + break; + case '2': + case '5': + /* PGM */ + image = (unsigned char *) checkedmalloc(width * height * bpp); + if (type == '2') { + for (x = 0; x < width * height; x++) { + v = getint(fin); + for (j = 0; j < bpp; j++) + image[x * bpp + (bpp - 1) - j] = v >> (j * 8); + } + } else + fread(image, width * height, bpp, fin); + jbg_split_planes(width, height, planes, encode_planes, image, bitmap, + use_graycode); + free(image); + break; + default: + fprintf(stderr, "Unsupported PBM type P%c!\n", type); + exit(1); + } + if (ferror(fin)) { + fprintf(stderr, "Problem while reading input file '%s", fnin); + perror("'"); + exit(1); + } + if (feof(fin)) { + fprintf(stderr, "Unexpected end of input file '%s'!\n", fnin); + exit(1); + } + + /* Test for valid parameters */ + if (width < 1 || height < 1) { + fprintf(stderr, "Image dimensions must be positive!\n"); + exit(1); + } + if (encode_planes < 1 || encode_planes > 255) { + fprintf(stderr, "Number of planes must be in range 1-255!\n"); + exit(1); + } + + /* Test the final byte in each image line for correct zero padding */ + if ((width & 7) && type == '4') { + for (y = 0; y < height; y++) + if (bitmap[0][y * bpl + bpl - 1] & ((1 << (8 - (width & 7))) - 1)) { + fprintf(stderr, "Warning: No zero padding in last byte (0x%02x) of " + "line %lu!\n", bitmap[0][y * bpl + bpl - 1], y + 1); + break; + } + } + + /* Apply JBIG algorithm and write BIE to output file */ + + /* initialize parameter struct for JBIG encoder*/ + jbg_enc_init(&s, width, height, encode_planes, bitmap, data_out, fout); + + /* Select number of resolution layers either directly or based + * on a given maximum size for the lowest resolution layer */ + if (d >= 0) + jbg_enc_layers(&s, d); + else + jbg_enc_lrlmax(&s, mwidth, mheight); + + /* Specify a few other options (each is ignored if negative) */ + if (delay_at) + options |= JBG_DELAY_AT; + if (reset) + options |= JBG_SDRST; + if (comment) { + s.comment_len = strlen(comment); + s.comment = (unsigned char *) comment; + } + if (y1) + s.yd1 = y1; + jbg_enc_lrange(&s, dl, dh); + jbg_enc_options(&s, order, options, l0, mx, -1); + + /* now encode everything and send it to data_out() */ + jbg_enc_out(&s); + + /* give encoder a chance to free its temporary data structures */ + jbg_enc_free(&s); + + /* check for file errors and close fout */ + if (ferror(fout) || fclose(fout)) { + fprintf(stderr, "Problem while writing output file '%s", fnout); + perror("'"); + exit(1); + } + + /* In case the user wants to know all the gory details ... */ + if (verbose) { + fprintf(stderr, "Information about the created JBIG bi-level image entity " + "(BIE):\n\n"); + fprintf(stderr, " input image size: %lu x %lu pixel\n", + s.xd, s.yd); + fprintf(stderr, " bit planes: %d\n", s.planes); + if (s.planes > 1) + fprintf(stderr, " encoding: %s code, MSB first\n", + use_graycode ? "Gray" : "binary"); + fprintf(stderr, " stripes: %lu\n", s.stripes); + fprintf(stderr, " lines per stripe in layer 0: %lu\n", s.l0); + fprintf(stderr, " total number of diff. layers: %d\n", s.d); + fprintf(stderr, " lowest layer in BIE: %d\n", s.dl); + fprintf(stderr, " highest layer in BIE: %d\n", s.dh); + fprintf(stderr, " lowest layer size: %lu x %lu pixel\n", + jbg_ceil_half(s.xd, s.d - s.dl), jbg_ceil_half(s.yd, s.d - s.dl)); + fprintf(stderr, " highest layer size: %lu x %lu pixel\n", + jbg_ceil_half(s.xd, s.d - s.dh), jbg_ceil_half(s.yd, s.d - s.dh)); + fprintf(stderr, " option bits:%s%s%s%s%s%s%s\n", + s.options & JBG_LRLTWO ? " LRLTWO" : "", + s.options & JBG_VLENGTH ? " VLENGTH" : "", + s.options & JBG_TPDON ? " TPDON" : "", + s.options & JBG_TPBON ? " TPBON" : "", + s.options & JBG_DPON ? " DPON" : "", + s.options & JBG_DPPRIV ? " DPPRIV" : "", + s.options & JBG_DPLAST ? " DPLAST" : ""); + fprintf(stderr, " order bits:%s%s%s%s\n", + s.order & JBG_HITOLO ? " HITOLO" : "", + s.order & JBG_SEQ ? " SEQ" : "", + s.order & JBG_ILEAVE ? " ILEAVE" : "", + s.order & JBG_SMID ? " SMID" : ""); + fprintf(stderr, " AT maximum x-offset: %d\n" + " AT maximum y-offset: %d\n", s.mx, s.my); + fprintf(stderr, " length of output file: %lu byte\n\n", + total_length); + } + + return 0; +} diff --git a/pbmtools/pbmtojbg85.c b/pbmtools/pbmtojbg85.c index 27bdbbd..651c075 100644 --- a/pbmtools/pbmtojbg85.c +++ b/pbmtools/pbmtojbg85.c @@ -70,9 +70,12 @@ static unsigned long getint(FILE *f) while ((c = getc(f)) != EOF && !(c == 13 || c == 10)) ; if (c != EOF) { ungetc(c, f); - fscanf(f, "%lu", &i); + if (fscanf(f, "%lu", &i) != 1) { + /* should never fail, since c must be a digit */ + fprintf(stderr, "Unexpected failure reading digit '%c'\n", c); + exit(1); + } } - return i; } @@ -237,7 +240,9 @@ int main (int argc, char **argv) break; case '4': /* PBM raw binary format */ - fread(next_line, bpl, 1, fin); + if (fread(next_line, bpl, 1, fin) != 1) { + /* silence compiler warnings; ferror/feof checked below */ + } break; default: fprintf(stderr, "Unsupported PBM type P%c!\n", type); diff --git a/pbmtools/pbmtojbg85.c.warnings b/pbmtools/pbmtojbg85.c.warnings new file mode 100644 index 0000000..27bdbbd --- /dev/null +++ b/pbmtools/pbmtojbg85.c.warnings @@ -0,0 +1,274 @@ +/* + * pbmtojbg85 - Portable Bitmap to JBIG converter (T.85 version) + * + * Markus Kuhn - http://www.cl.cam.ac.uk/~mgk25/jbigkit/ + */ + +#include +#include +#include +#include +#include "jbig85.h" + + +char *progname; /* global pointer to argv[0] */ + + +/* + * Print usage message and abort + */ +static void usage(void) +{ + fprintf(stderr, + "PBMtoJBIG converter " JBG85_VERSION " (T.85 version) --\n" + "creates bi-level image entity (BIE) as output file\n\n" + "usage: %s [] [ | - []]\n\n" + "options:\n\n", progname); + fprintf(stderr, + " -s number\theight of a stripe\n"); + fprintf(stderr, + " -m number\tmaximum adaptive template pixel horizontal offset (default 8)\n" + " -p number\toptions byte value: add TPBON=8, LRLTWO=64\n" + "\t\t(default 8 = TPBON)\n"); + fprintf(stderr, + " -C string\tadd the provided string as a comment marker segment\n"); + fprintf(stderr, + " -Y yi yr\tannounce in header initially the larger image height yi\n" + "\t\tand then announce after line yr has been encoded the real height\n" + "\t\tusing NEWLEN marker (for testing NEWLEN and VLENGTH=1 function)\n\n"); + exit(1); +} + + +/* + * malloc() with exception handler + */ +void *checkedmalloc(size_t n) +{ + void *p; + + if ((p = malloc(n)) == NULL) { + fprintf(stderr, "Sorry, not enough memory available!\n"); + exit(1); + } + + return p; +} + + +/* + * Read an ASCII integer number from file f and skip any PBM + * comments which are encountered. + */ +static unsigned long getint(FILE *f) +{ + int c; + unsigned long i; + + while ((c = getc(f)) != EOF && !isdigit(c)) + if (c == '#') + while ((c = getc(f)) != EOF && !(c == 13 || c == 10)) ; + if (c != EOF) { + ungetc(c, f); + fscanf(f, "%lu", &i); + } + + return i; +} + + +/* + * Callback procedure which is used by JBIG encoder to deliver the + * encoded data. It simply sends the bytes to the output file. + */ +static void data_out(unsigned char *start, size_t len, void *file) +{ + fwrite(start, len, 1, (FILE *) file); + return; +} + + +int main (int argc, char **argv) +{ + FILE *fin = stdin, *fout = stdout; + const char *fnin = NULL, *fnout = NULL; + int i, j, c; + int all_args = 0, files = 0; + unsigned long x, y; + unsigned long width, height; + size_t bpl; + char type; + unsigned char *p, *lines, *next_line; + unsigned char *prev_line = NULL, *prevprev_line = NULL; + struct jbg85_enc_state s; + int mx = -1; + unsigned long l0 = 0, yi = 0, yr = 0; + char *comment = NULL; + int options = JBG_TPBON; + + /* 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 0 : + if (files++) usage(); + break; + case 'Y': + if (i+2 >= argc) usage(); + j = -1; + yi = atol(argv[++i]); + yr = atol(argv[++i]); + break; + case 'p': + if (++i >= argc) usage(); + j = -1; + options = atoi(argv[i]); + break; + case 's': + if (++i >= argc) usage(); + j = -1; + l0 = atol(argv[i]); + break; + case 'm': + if (++i >= argc) usage(); + j = -1; + mx = atoi(argv[i]); + break; + case 'C': + if (++i >= argc) usage(); + j = -1; + comment = argv[i]; + break; + default: + usage(); + } + else + switch (files++) { + case 0: fnin = argv[i]; break; + case 1: fnout = argv[i]; break; + default: + usage(); + } + } + + /* open input file */ + if (fnin) { + fin = fopen(fnin, "rb"); + if (!fin) { + fprintf(stderr, "Can't open input file '%s", fnin); + perror("'"); + exit(1); + } + } else + fnin = ""; + + /* read PBM header */ + while ((c = getc(fin)) != EOF && (isspace(c) || c == '#')) + if (c == '#') + while ((c = getc(fin)) != EOF && !(c == 13 || c == 10)) ; + type = getc(fin); + if (c != 'P' || (type != '1' && type != '4')) { + fprintf(stderr, "Input file '%s' does not look like a PBM file!\n", fnin); + exit(1); + } + width = getint(fin); + height = getint(fin); + fgetc(fin); /* skip line feed */ + + /* Test for valid parameters */ + if (width < 1 || height < 1) { + fprintf(stderr, "Image dimensions must be positive!\n"); + exit(1); + } + + /* allocate buffer for a single image line */ + bpl = (width >> 3) + !!(width & 7); /* bytes per line */ + lines = (unsigned char *) checkedmalloc(bpl * 3); + + /* open output file */ + if (fnout) { + fout = fopen(fnout, "wb"); + if (!fout) { + fprintf(stderr, "Can't open input file '%s", fnout); + perror("'"); + exit(1); + } + } else + fnout = ""; + + /* initialize parameter struct for JBIG encoder*/ + jbg85_enc_init(&s, width, yi ? yi : height, data_out, fout); + + /* Specify a few other options (each is ignored if negative) */ + if (yi) + options |= JBG_VLENGTH; + if (comment) { + s.comment_len = strlen(comment); + s.comment = (unsigned char *) comment; + } + jbg85_enc_options(&s, options, l0, mx); + + for (y = 0; y < height; y++) { + + /* Use a 3-line ring buffer, because the encoder requires that the two + * previously supplied lines are still in memory when the next line is + * processed. */ + next_line = lines + (y%3)*bpl; + + switch (type) { + case '1': + /* PBM text format */ + p = next_line; + for (x = 0; x <= ((width-1) | 7); x++) { + *p <<= 1; + if (x < width) + *p |= getint(fin) & 1; + if ((x & 7) == 7) + ++p; + } + break; + case '4': + /* PBM raw binary format */ + fread(next_line, bpl, 1, fin); + break; + default: + fprintf(stderr, "Unsupported PBM type P%c!\n", type); + exit(1); + } + if (ferror(fin)) { + fprintf(stderr, "Problem while reading input file '%s", fnin); + perror("'"); + exit(1); + } + if (feof(fin)) { + fprintf(stderr, "Unexpected end of input file '%s'!\n", fnin); + exit(1); + } + + /* JBIG compress another line and write out result via callback */ + jbg85_enc_lineout(&s, next_line, prev_line, prevprev_line); + prevprev_line = prev_line; + prev_line = next_line; + + /* adjust final image height via NEWLEN */ + if (yi && y == yr) + jbg85_enc_newlen(&s, height); + } + + /* check for file errors and close fout */ + if (ferror(fout) || fclose(fout)) { + fprintf(stderr, "Problem while writing output file '%s", fnout); + perror("'"); + exit(1); + } + + return 0; +}