ref: dabd594f325487412c44b593b8aae566c3fc9eab
dir: /jbig2dec.c/
/* jbig2dec Copyright (C) 2001-2002 artofcode LLC. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. $Id: jbig2dec.c,v 1.32 2002/07/15 20:01:01 giles Exp $ */ #ifdef HAVE_CONFIG_H #include "config.h" #include "config_types.h" #elif _WIN32 #include "config_win32.h" #endif #ifdef HAVE_STDINT_H #include <stdint.h> #endif #include <stdio.h> #include <stdlib.h> #include <stddef.h> #include <string.h> #include "jbig2.h" #include "jbig2_image.h" #ifdef HAVE_GETOPT_H # include <getopt.h> #else # include "getopt.h" #endif #ifdef HAVE_OPENSSL_SHA1 # include <openssl/sha.h> #endif typedef enum { usage,dump,render } jbig2dec_mode; typedef struct { jbig2dec_mode mode; int verbose, hash; #ifdef HAVE_OPENSSL_SHA1 SHA_CTX *hash_ctx; #endif char *output_file; } jbig2dec_params_t; /* page hashing functions */ static void hash_init(jbig2dec_params_t *params) { #ifdef HAVE_OPENSSL_SHA1 params->hash_ctx = malloc(sizeof(SHA_CTX)); if (params->hash == NULL) { fprintf(stderr, "unable to allocate hash state\n"); params->hash = 0; return; } else { SHA1_Init(params->hash_ctx); } #endif /* HAVE_OPENSSL_SHA1 */ } static void hash_image(jbig2dec_params_t *params, Jbig2Image *image) { int N = image->stride * image->height; #ifdef HAVE_OPENSSL_SHA1 SHA1_Update(params->hash_ctx, image->data, N); #endif } static void hash_print(jbig2dec_params_t *params, FILE *out) { #ifdef HAVE_OPENSSL_SHA1 char md[SHA_DIGEST_LENGTH]; char digest[2*SHA_DIGEST_LENGTH + 1]; int i; SHA1_Final(md, params->hash_ctx); for (i = 0; i < SHA_DIGEST_LENGTH; i++) { snprintf(&(digest[2*i]), 3, "%02x", md[i]); } fprintf(out, "%s", digest); #else fprintf(out, "sorry, hash function unimplemented"); #endif } static void hash_free(jbig2dec_params_t *params) { #ifdef HAVE_OPENSSL_SHA1 // FIXME: need to check for SHA1 finalization? free(params->hash_ctx); params->hash_ctx = NULL; #endif } static int parse_options(int argc, char *argv[], jbig2dec_params_t *params) { static struct option long_options[] = { {"quiet", 0, NULL, 'q'}, {"help", 0, NULL, 'h'}, {"dump", 0, NULL, 'd'}, {"hash", 0, NULL, 'm'}, {"output", 1, NULL, 'o'}, {NULL, 0, NULL, 0} }; int option_idx = 1; int option; while (1) { option = getopt_long(argc, argv, "qhdo:", long_options, &option_idx); if (option == -1) break; fprintf(stderr, "option '%c' value '%s'\n", option, optarg); switch (option) { case 0: // unknown long option if (!params->verbose) fprintf(stdout, "unrecognized option: --%s\n", long_options[option_idx].name); break; case 'q': params->verbose = 0; break; case 'h': case '?': params->mode = usage; break; case 'd': params->mode=dump; break; case 'm': params->hash = 1; break; case 'o': params->output_file = strdup(optarg); break; default: if (!params->verbose) fprintf(stdout, "unrecognized option: -%c\n", option); break; } } return (optind); } static int print_usage (void) { fprintf(stderr, "Usage: jbig2dec [options] <file.jbig2>\n" " or jbig2dec [options] <global_stream> <page_stream>\n" "\n" " When invoked with a single file, it attempts to parse it as\n" " a normal jbig2 file. Invoked with two files, it treats the\n" " first as the global segments, and the second as the segment\n" " stream for a particular page. This is useful for examining\n" " embedded streams.\n" "\n" " available options:\n" " -h --help this usage summary\n" " -q --quiet suppress diagnostic output\n" " -d --dump print the structure of the jbig2 file\n" " rather than explicitly decoding\n" " --hash print a hash of the decode document\n" " -o <file> send decoded output to <file>\n" " Defaults to the the input with a different\n" " extension. Pass '-' for stdout.\n" "\n" ); return 1; } static int error_callback(void *error_callback_data, const char *buf, Jbig2Severity severity, int32_t seg_idx) { char *type; char segment[22]; switch (severity) { case JBIG2_SEVERITY_DEBUG: type = "DEBUG"; break;; case JBIG2_SEVERITY_INFO: type = "info"; break;; case JBIG2_SEVERITY_WARNING: type = "WARNING"; break;; case JBIG2_SEVERITY_FATAL: type = "FATAL ERROR"; break;; default: type = "unknown message"; break;; } if (seg_idx == -1) segment[0] = '\0'; else snprintf(segment, sizeof(segment), "(segment 0x%02x)", seg_idx); fprintf(stderr, "jbig2dec %s %s %s\n", type, buf, segment); return 0; } static char * make_output_filename(const char *input_filename, const char *extension) { char *output_filename; const char *c, *e; int len; if (extension == NULL) { fprintf(stderr, "make_output_filename called with no extension!\n"); exit (1); } if (input_filename == NULL) c = "out"; else { /* strip any leading path */ c = strrchr(input_filename, '/'); /* *nix */ if (c == NULL) c = strrchr(input_filename, '\\'); /* win32/dos */ if (c != NULL) c++; /* skip the path separator */ else c = input_filename; /* no leading path */ } /* make sure we haven't just stripped the last character */ if (*c == '\0') c = "out"; /* strip the extension */ len = strlen(c); e = strrchr(c, '.'); if (e != NULL) len -= strlen(e); /* allocate enough space for the base + ext */ output_filename = malloc(len + strlen(extension) + 1); if (output_filename == NULL) { fprintf(stderr, "couldn't allocate memory for output_filename\n"); exit (1); } strncpy(output_filename, c, len); strncpy(output_filename + len, extension, strlen(extension)); *(output_filename + len + strlen(extension)) = '\0'; /* return the new string */ return (output_filename); } static int write_page_image(jbig2dec_params_t *params, Jbig2Image *image) { if (!strncmp(params->output_file, "-", 2)) { fprintf(stderr, "writing decoded page to stdout\n"); #ifdef HAVE_LIBPNG jbig2_image_write_png(image, stdout); #else jbig2_image_write_pbm(image, stdout); #endif } else { fprintf(stderr, "saving decoded page as '%s'\n", params->output_file); #ifdef HAVE_LIBPNG jbig2_image_write_png_file(image, params->output_file); #else jbig2_image_write_pbm_file(image, params->output_file); #endif } return 0; } static int write_document_hash(jbig2dec_params_t *params) { FILE *out; if (!strncmp(params->output_file, "-", 2)) { out = stderr; } else { out = stdout; } fprintf(out, "Hash of decoded document: "); hash_print(params, out); fprintf(out, "\n"); return 0; } int main (int argc, char **argv) { FILE *f = NULL, *f_page = NULL; Jbig2Ctx *ctx; uint8_t buf[4096]; jbig2dec_params_t params; int filearg; // set defaults params.mode = render; params.verbose = 1; params.hash = 0; params.output_file = NULL; filearg = parse_options(argc, argv, ¶ms); if (params.hash) hash_init(¶ms); switch (params.mode) { case usage: print_usage(); exit (0); break; case dump: fprintf(stderr, "Sorry, segment dump not yet implemented\n"); break; case render: if ((argc - filearg) == 1) // only one argument--open as a jbig2 file { char *fn = argv[filearg]; f = fopen(fn, "rb"); if (f == NULL) { fprintf(stderr, "error opening %s\n", fn); return 1; } } else if ((argc - filearg) == 2) // two arguments open as separate global and page streams { char *fn = argv[filearg]; char *fn_page = argv[filearg+1]; f = fopen(fn, "rb"); if (f == NULL) { fprintf(stderr, "error opening %s\n", fn); return 1; } f_page = fopen(fn_page, "rb"); if (f_page == NULL) { fprintf(stderr, "error opening %s\n", fn_page); return 1; } } else // any other number of arguments return print_usage(); ctx = jbig2_ctx_new(NULL, f_page != NULL ? JBIG2_OPTIONS_EMBEDDED : 0, NULL, error_callback, NULL); // pull the whole file/global stream into memory for (;;) { int n_bytes = fread(buf, 1, sizeof(buf), f); if (n_bytes <= 0) break; jbig2_data_in(ctx, buf, n_bytes); } fclose(f); // if there's a local page stream read that in its entirety if (f_page != NULL) { Jbig2GlobalCtx *global_ctx = jbig2_make_global_ctx(ctx); ctx = jbig2_ctx_new(NULL, JBIG2_OPTIONS_EMBEDDED, global_ctx, error_callback, NULL); for (;;) { int n_bytes = fread(buf, 1, sizeof(buf), f_page); if (n_bytes <= 0) break; jbig2_data_in(ctx, buf, n_bytes); } fclose(f_page); jbig2_global_ctx_free(global_ctx); } // retrieve and output the returned pages { Jbig2Image *image; /* work around broken CVision embedded streams */ if (f_page != NULL) jbig2_complete_page(ctx); if (params.output_file == NULL) { #ifdef HAVE_LIBPNG params.output_file = make_output_filename(argv[filearg], ".png"); #else params.output_file = make_output_filename(argv[filearg], ".pbm"); #endif } /* retrieve and write out all the completed pages */ while ((image = jbig2_page_out(ctx)) != NULL) { write_page_image(¶ms, image); if (params.hash) hash_image(¶ms, image); jbig2_release_page(ctx, image); } if (params.hash) write_document_hash(¶ms); } jbig2_ctx_free(ctx); } /* end params.mode switch */ if (params.hash) hash_free(¶ms); // fin return 0; }