comparison mupdf-source/thirdparty/jbig2dec/jbig2dec.c @ 2:b50eed0cc0ef upstream

ADD: MuPDF v1.26.7: the MuPDF source as downloaded by a default build of PyMuPDF 1.26.4. The directory name has changed: no version number in the expanded directory now.
author Franz Glasner <fzglas.hg@dom66.de>
date Mon, 15 Sep 2025 11:43:07 +0200
parents
children
comparison
equal deleted inserted replaced
1:1d09e1dec1d9 2:b50eed0cc0ef
1 /* Copyright (C) 2001-2023 Artifex Software, Inc.
2 All Rights Reserved.
3
4 This software is provided AS-IS with no warranty, either express or
5 implied.
6
7 This software is distributed under license and may not be copied,
8 modified or distributed except as expressly authorized under the terms
9 of the license contained in the file LICENSE in this distribution.
10
11 Refer to licensing information at http://www.artifex.com or contact
12 Artifex Software, Inc., 39 Mesa Street, Suite 108A, San Francisco,
13 CA 94129, USA, for further information.
14 */
15
16 /*
17 jbig2dec
18 */
19
20 #ifdef HAVE_CONFIG_H
21 #include "config.h"
22 #endif
23
24 #include <stdio.h>
25 #include <stdlib.h>
26 #include <stddef.h>
27 #include <string.h>
28
29 #ifdef HAVE_GETOPT_H
30 # include <getopt.h>
31 #else
32 # include "getopt.h"
33 #endif
34
35 #include "os_types.h"
36 #include "sha1.h"
37
38 #ifdef JBIG_EXTERNAL_MEMENTO_H
39 #include JBIG_EXTERNAL_MEMENTO_H
40 #else
41 #include "memento.h"
42 #endif
43
44 #include "jbig2.h"
45 #include "jbig2_priv.h"
46 #include "jbig2_image.h"
47 #include "jbig2_image_rw.h"
48
49 typedef enum {
50 usage, dump, render
51 } jbig2dec_mode;
52
53 typedef enum {
54 jbig2dec_format_none,
55 jbig2dec_format_jbig2,
56 jbig2dec_format_pbm,
57 #ifdef HAVE_LIBPNG
58 jbig2dec_format_png,
59 #endif
60 } jbig2dec_format;
61
62 typedef struct {
63 jbig2dec_mode mode;
64 int verbose, hash, embedded;
65 SHA1_CTX *hash_ctx;
66 char *output_filename;
67 jbig2dec_format output_format;
68 size_t memory_limit;
69 } jbig2dec_params_t;
70
71 typedef struct {
72 int verbose;
73 char *last_message;
74 Jbig2Severity severity;
75 char *type;
76 long repeats;
77 } jbig2dec_error_callback_state_t;
78
79 typedef struct {
80 Jbig2Allocator super;
81 Jbig2Ctx *ctx;
82 size_t memory_limit;
83 size_t memory_used;
84 size_t memory_peak;
85 } jbig2dec_allocator_t;
86
87 static int print_version(void);
88 static int print_usage(void);
89
90 #define ALIGNMENT 16
91 #define KBYTE 1024
92 #define MBYTE (1024 * KBYTE)
93
94 static void *jbig2dec_reached_limit(jbig2dec_allocator_t *allocator, size_t oldsize, size_t size)
95 {
96 size_t limit_mb = allocator->memory_limit / MBYTE;
97 size_t used_mb = allocator->memory_used / MBYTE;
98 size_t oldsize_mb = oldsize / MBYTE;
99 size_t size_mb = size / MBYTE;
100
101 if (oldsize == 0)
102 jbig2_error(allocator->ctx, JBIG2_SEVERITY_FATAL, -1, "memory: limit reached: limit: %zu (%zu Mbyte) used: %zu (%zu Mbyte) allocation: %zu (%zu Mbyte)",
103 allocator->memory_limit, limit_mb,
104 allocator->memory_used, used_mb,
105 size, size_mb);
106 else
107 jbig2_error(allocator->ctx, JBIG2_SEVERITY_FATAL, -1, "memory: limit reached: limit: %zu (%zu Mbyte) used: %zu (%zu Mbyte) reallocation: %zu (%zu Mbyte) -> %zu (%zu Mbyte)",
108 allocator->memory_limit, limit_mb,
109 allocator->memory_used, used_mb,
110 oldsize, oldsize_mb,
111 size, size_mb);
112
113 return NULL;
114 }
115
116 static void jbig2dec_peak(jbig2dec_allocator_t *allocator)
117 {
118 size_t limit_mb = allocator->memory_limit / MBYTE;
119 size_t peak_mb = allocator->memory_peak / MBYTE;
120 size_t used_mb = allocator->memory_used / MBYTE;
121
122 if (allocator->ctx == NULL)
123 return;
124 if (used_mb <= peak_mb)
125 return;
126
127 allocator->memory_peak = allocator->memory_used;
128
129 jbig2_error(allocator->ctx, JBIG2_SEVERITY_DEBUG, JBIG2_UNKNOWN_SEGMENT_NUMBER, "memory: limit: %lu %sbyte used: %lu %sbyte, peak: %lu %sbyte",
130 limit_mb > 0 ? limit_mb : allocator->memory_limit, limit_mb > 0 ? "M" : "",
131 used_mb > 0 ? used_mb : allocator->memory_used, used_mb > 0 ? "M" : "",
132 peak_mb > 0 ? peak_mb : allocator->memory_peak, peak_mb > 0 ? "M" : "");
133 }
134
135 static void *jbig2dec_alloc(Jbig2Allocator *allocator_, size_t size)
136 {
137 jbig2dec_allocator_t *allocator = (jbig2dec_allocator_t *) allocator_;
138 void *ptr;
139
140 if (size == 0)
141 return NULL;
142 if (size > SIZE_MAX - ALIGNMENT)
143 return NULL;
144
145 if (size + ALIGNMENT > allocator->memory_limit - allocator->memory_used)
146 return jbig2dec_reached_limit(allocator, 0, size + ALIGNMENT);
147
148 ptr = malloc(size + ALIGNMENT);
149 if (ptr == NULL)
150 return NULL;
151 memcpy(ptr, &size, sizeof(size));
152 allocator->memory_used += size + ALIGNMENT;
153
154 jbig2dec_peak(allocator);
155
156 return (unsigned char *) ptr + ALIGNMENT;
157 }
158
159 static void jbig2dec_free(Jbig2Allocator *allocator_, void *p)
160 {
161 jbig2dec_allocator_t *allocator = (jbig2dec_allocator_t *) allocator_;
162 size_t size;
163
164 if (p == NULL)
165 return;
166
167 memcpy(&size, (unsigned char *) p - ALIGNMENT, sizeof(size));
168 allocator->memory_used -= size + ALIGNMENT;
169 free((unsigned char *) p - ALIGNMENT);
170 }
171
172 static void *jbig2dec_realloc(Jbig2Allocator *allocator_, void *p, size_t size)
173 {
174 jbig2dec_allocator_t *allocator = (jbig2dec_allocator_t *) allocator_;
175 unsigned char *oldp;
176 size_t oldsize;
177
178 if (p == NULL)
179 return jbig2dec_alloc(allocator_, size);
180 if (p < (void *) ALIGNMENT)
181 return NULL;
182
183 if (size == 0) {
184 jbig2dec_free(allocator_, p);
185 return NULL;
186 }
187 if (size > SIZE_MAX - ALIGNMENT)
188 return NULL;
189
190 oldp = (unsigned char *) p - ALIGNMENT;
191 memcpy(&oldsize, oldp, sizeof(oldsize));
192
193 if (size + ALIGNMENT > allocator->memory_limit - allocator->memory_used + oldsize + ALIGNMENT)
194 return jbig2dec_reached_limit(allocator, oldsize + ALIGNMENT, size + ALIGNMENT);
195
196 p = realloc(oldp, size + ALIGNMENT);
197 if (p == NULL)
198 return NULL;
199
200 allocator->memory_used -= oldsize + ALIGNMENT;
201 memcpy(p, &size, sizeof(size));
202 allocator->memory_used += size + ALIGNMENT;
203
204 jbig2dec_peak(allocator);
205
206 return (unsigned char *) p + ALIGNMENT;
207 }
208
209 /* page hashing functions */
210 static void
211 hash_init(jbig2dec_params_t *params)
212 {
213 params->hash_ctx = (SHA1_CTX *) malloc(sizeof(SHA1_CTX));
214 if (params->hash_ctx == NULL) {
215 fprintf(stderr, "unable to allocate hash state\n");
216 params->hash = 0;
217 return;
218 } else {
219 SHA1_Init(params->hash_ctx);
220 }
221 }
222
223 static void
224 hash_image(jbig2dec_params_t *params, Jbig2Image *image)
225 {
226 unsigned int N = image->stride * image->height;
227
228 SHA1_Update(params->hash_ctx, image->data, N);
229 }
230
231 static void
232 hash_print(jbig2dec_params_t *params, FILE *out)
233 {
234 unsigned char md[SHA1_DIGEST_SIZE];
235 char digest[2 * SHA1_DIGEST_SIZE + 1];
236 int i;
237
238 SHA1_Final(params->hash_ctx, md);
239 for (i = 0; i < SHA1_DIGEST_SIZE; i++) {
240 snprintf(&(digest[2 * i]), 3, "%02x", md[i]);
241 }
242 fprintf(out, "%s", digest);
243 }
244
245 static void
246 hash_free(jbig2dec_params_t *params)
247 {
248 free(params->hash_ctx);
249 params->hash_ctx = NULL;
250 }
251
252 static int
253 set_output_format(jbig2dec_params_t *params, const char *format)
254 {
255 #ifdef HAVE_LIBPNG
256 /* this should really by strncasecmp()
257 TODO: we need to provide our own for portability */
258 if (!strncmp(format, "png", 3) || !strncmp(format, "PNG", 3)) {
259 params->output_format = jbig2dec_format_png;
260 return 0;
261 }
262 #endif
263 /* default to pbm */
264 params->output_format = jbig2dec_format_pbm;
265
266 return 0;
267 }
268
269 static int
270 parse_options(int argc, char *argv[], jbig2dec_params_t *params)
271 {
272 static struct option long_options[] = {
273 {"version", 0, NULL, 'V'},
274 {"help", 0, NULL, 'h'},
275 {"quiet", 0, NULL, 'q'},
276 {"verbose", 2, NULL, 'v'},
277 {"dump", 0, NULL, 'd'},
278 {"hash", 0, NULL, 'm'},
279 {"output", 1, NULL, 'o'},
280 {"format", 1, NULL, 't'},
281 {"embedded", 0, NULL, 'e'},
282 {NULL, 0, NULL, 0}
283 };
284 int option_idx = 1;
285 int option;
286 int ret;
287
288 while (1) {
289 option = getopt_long(argc, argv, "Vh?qv:do:t:eM:", long_options, &option_idx);
290 if (option == -1)
291 break;
292
293 switch (option) {
294 case 0: /* unknown long option */
295 if (!params->verbose)
296 fprintf(stdout, "unrecognized option: --%s\n", long_options[option_idx].name);
297 break;
298 case 'q':
299 params->verbose = 0;
300 break;
301 case 'v':
302 if (optarg)
303 params->verbose = atoi(optarg);
304 else
305 params->verbose = 2;
306 break;
307 case 'h':
308 case '?':
309 params->mode = usage;
310 break;
311 case 'V':
312 /* the GNU Coding Standards suggest --version
313 should override all other options */
314 print_version();
315 exit(0);
316 break;
317 case 'd':
318 params->mode = dump;
319 break;
320 case 'm':
321 params->hash = 1;
322 break;
323 case 'o':
324 params->output_filename = strdup(optarg);
325 break;
326 case 't':
327 set_output_format(params, optarg);
328 break;
329 case 'e':
330 params->embedded = 1;
331 break;
332 case 'M':
333 ret = sscanf(optarg, "%zu", &params->memory_limit);
334 if (ret != 1)
335 fprintf(stderr, "could not parse memory limit argument\n");
336 break;
337 default:
338 if (!params->verbose)
339 fprintf(stderr, "unrecognized option: -%c\n", option);
340 break;
341 }
342 }
343 return (optind);
344 }
345
346 static int
347 print_version(void)
348 {
349 fprintf(stdout, "jbig2dec %d.%d\n", JBIG2_VERSION_MAJOR, JBIG2_VERSION_MINOR);
350 return 0;
351 }
352
353 static int
354 print_usage(void)
355 {
356 fprintf(stderr,
357 "Usage: jbig2dec [options] <file.jbig2>\n"
358 " or jbig2dec [options] <global_stream> <page_stream>\n"
359 "\n"
360 " When invoked with a single file, it attempts to parse it as\n"
361 " a normal jbig2 file. Invoked with two files, it treats the\n"
362 " first as the global segments, and the second as the segment\n"
363 " stream for a particular page. This is useful for examining\n"
364 " embedded streams.\n"
365 "\n"
366 " available options:\n"
367 " -h --help this usage summary\n"
368 " -q --quiet suppress diagnostic output\n"
369 " -v --verbose set the verbosity level\n"
370 " -d --dump print the structure of the jbig2 file\n"
371 " rather than explicitly decoding\n"
372 " -V --version program name and version information\n"
373 " -m --hash print a hash of the decoded document\n"
374 " -e --embedded expect embedded bit stream without file header\n"
375 " -M <limit> memory limit expressed in bytes\n"
376 " -o <file>\n"
377 " --output <file> send decoded output to <file>\n"
378 " Defaults to the the input with a different\n"
379 " extension. Pass '-' for stdout.\n"
380 " -t <type>\n"
381 " --format <type> force a particular output file format\n"
382 #ifdef HAVE_LIBPNG
383 " supported options are 'png' and 'pbm'\n"
384 #else
385 " the only supported option is 'pbm'\n"
386 #endif
387 "\n");
388
389 return 1;
390 }
391
392 static void
393 error_callback(void *error_callback_data, const char *message, Jbig2Severity severity, uint32_t seg_idx)
394 {
395 jbig2dec_error_callback_state_t *state = (jbig2dec_error_callback_state_t *) error_callback_data;
396 char *type;
397 int ret;
398
399 switch (severity) {
400 case JBIG2_SEVERITY_DEBUG:
401 if (state->verbose < 3)
402 return;
403 type = "DEBUG";
404 break;
405 case JBIG2_SEVERITY_INFO:
406 if (state->verbose < 2)
407 return;
408 type = "info";
409 break;
410 case JBIG2_SEVERITY_WARNING:
411 if (state->verbose < 1)
412 return;
413 type = "WARNING";
414 break;
415 case JBIG2_SEVERITY_FATAL:
416 type = "FATAL ERROR";
417 break;
418 default:
419 type = "unknown message";
420 break;
421 }
422
423 if (state->last_message != NULL && !strcmp(message, state->last_message) && state->severity == severity && state->type == type) {
424 state->repeats++;
425 if (state->repeats % 1000000 == 0) {
426 ret = fprintf(stderr, "jbig2dec %s last message repeated %ld times so far\n", state->type, state->repeats);
427 if (ret < 0)
428 goto printerror;
429 }
430 } else {
431 if (state->repeats > 1) {
432 ret = fprintf(stderr, "jbig2dec %s last message repeated %ld times\n", state->type, state->repeats);
433 if (ret < 0)
434 goto printerror;
435 }
436
437 if (seg_idx == JBIG2_UNKNOWN_SEGMENT_NUMBER)
438 ret = fprintf(stderr, "jbig2dec %s %s\n", type, message);
439 else
440 ret = fprintf(stderr, "jbig2dec %s %s (segment 0x%08x)\n", type, message, seg_idx);
441 if (ret < 0)
442 goto printerror;
443
444 state->repeats = 0;
445 state->severity = severity;
446 state->type = type;
447 free(state->last_message);
448 state->last_message = NULL;
449
450 if (message) {
451 state->last_message = strdup(message);
452 if (state->last_message == NULL) {
453 ret = fprintf(stderr, "jbig2dec WARNING could not duplicate message\n");
454 if (ret < 0)
455 goto printerror;
456 }
457 }
458 }
459
460 return;
461
462 printerror:
463 fprintf(stderr, "jbig2dec WARNING could not print message\n");
464 state->repeats = 0;
465 free(state->last_message);
466 state->last_message = NULL;
467 }
468
469 static void
470 flush_errors(jbig2dec_error_callback_state_t *state)
471 {
472 if (state->repeats > 1) {
473 fprintf(stderr, "jbig2dec last message repeated %ld times\n", state->repeats);
474 }
475 }
476
477 static char *
478 make_output_filename(const char *input_filename, const char *extension)
479 {
480 char *output_filename;
481 const char *c, *e;
482 size_t extlen, len;
483
484 if (extension == NULL) {
485 fprintf(stderr, "no filename extension; cannot create output filename!\n");
486 exit(1);
487 }
488
489 if (input_filename == NULL)
490 c = "out";
491 else {
492 /* strip any leading path */
493 c = strrchr(input_filename, '/'); /* *nix */
494 if (c == NULL)
495 c = strrchr(input_filename, '\\'); /* win32/dos */
496 if (c != NULL)
497 c++; /* skip the path separator */
498 else
499 c = input_filename; /* no leading path */
500 }
501
502 /* make sure we haven't just stripped the last character */
503 if (*c == '\0')
504 c = "out";
505
506 /* strip the extension */
507 len = strlen(c);
508 e = strrchr(c, '.');
509 if (e != NULL)
510 len -= strlen(e);
511
512 extlen = strlen(extension);
513
514 /* allocate enough space for the base + ext */
515 output_filename = (char *)malloc(len + extlen + 1);
516 if (output_filename == NULL) {
517 fprintf(stderr, "failed to allocate memory for output filename\n");
518 exit(1);
519 }
520
521 memcpy(output_filename, c, len);
522 memcpy(output_filename + len, extension, extlen);
523 *(output_filename + len + extlen) = '\0';
524
525 /* return the new string */
526 return (output_filename);
527 }
528
529 static int
530 write_page_image(jbig2dec_params_t *params, FILE *out, Jbig2Image *image)
531 {
532 switch (params->output_format) {
533 #ifdef HAVE_LIBPNG
534 case jbig2dec_format_png:
535 return jbig2_image_write_png(image, out);
536 #endif
537 case jbig2dec_format_pbm:
538 return jbig2_image_write_pbm(image, out);
539 default:
540 fprintf(stderr, "unsupported output format.\n");
541 return 1;
542 }
543
544 return 0;
545 }
546
547 static int
548 write_document_hash(jbig2dec_params_t *params)
549 {
550 FILE *out;
551
552 if (!strncmp(params->output_filename, "-", 2)) {
553 out = stderr;
554 } else {
555 out = stdout;
556 }
557
558 fprintf(out, "Hash of decoded document: ");
559 hash_print(params, out);
560 fprintf(out, "\n");
561
562 return 0;
563 }
564
565 int
566 main(int argc, char **argv)
567 {
568 jbig2dec_params_t params;
569 jbig2dec_error_callback_state_t error_callback_state;
570 jbig2dec_allocator_t allocator_;
571 jbig2dec_allocator_t *allocator = &allocator_;
572 Jbig2Ctx *ctx = NULL;
573 FILE *f = NULL, *f_page = NULL;
574 uint8_t buf[4096];
575 int filearg;
576 int result = 1;
577 int code;
578
579 /* set defaults */
580 params.mode = render;
581 params.verbose = 1;
582 params.hash = 0;
583 params.output_filename = NULL;
584 params.output_format = jbig2dec_format_none;
585 params.embedded = 0;
586 params.memory_limit = 0;
587
588 filearg = parse_options(argc, argv, &params);
589
590 error_callback_state.verbose = params.verbose;
591 error_callback_state.severity = JBIG2_SEVERITY_DEBUG;
592 error_callback_state.type = NULL;
593 error_callback_state.last_message = NULL;
594 error_callback_state.repeats = 0;
595
596 if (params.hash)
597 hash_init(&params);
598
599 switch (params.mode) {
600 case usage:
601 print_usage();
602 exit(0);
603 break;
604 case dump:
605 fprintf(stderr, "Sorry, segment dump not yet implemented\n");
606 break;
607 case render:
608
609 if ((argc - filearg) == 1) {
610 /* only one argument--open as a jbig2 file */
611 char *fn = argv[filearg];
612
613 f = fopen(fn, "rb");
614 if (f == NULL) {
615 fprintf(stderr, "error opening %s\n", fn);
616 goto cleanup;
617 }
618 } else if ((argc - filearg) == 2) {
619 /* two arguments open as separate global and page streams */
620 char *fn = argv[filearg];
621 char *fn_page = argv[filearg + 1];
622
623 f = fopen(fn, "rb");
624 if (f == NULL) {
625 fprintf(stderr, "error opening %s\n", fn);
626 goto cleanup;
627 }
628
629 f_page = fopen(fn_page, "rb");
630 if (f_page == NULL) {
631 fclose(f);
632 fprintf(stderr, "error opening %s\n", fn_page);
633 goto cleanup;
634 }
635 } else {
636 /* any other number of arguments */
637 result = print_usage();
638 goto cleanup;
639 }
640
641 if (params.memory_limit == 0)
642 allocator = NULL;
643 else
644 {
645 allocator->super.alloc = jbig2dec_alloc;
646 allocator->super.free = jbig2dec_free;
647 allocator->super.realloc = jbig2dec_realloc;
648 allocator->ctx = NULL;
649 allocator->memory_limit = params.memory_limit;
650 allocator->memory_used = 0;
651 allocator->memory_peak = 0;
652 }
653
654 ctx = jbig2_ctx_new((Jbig2Allocator *) allocator, (Jbig2Options)(f_page != NULL || params.embedded ? JBIG2_OPTIONS_EMBEDDED : 0), NULL, error_callback, &error_callback_state);
655 if (ctx == NULL) {
656 fclose(f);
657 if (f_page)
658 fclose(f_page);
659 goto cleanup;
660 }
661
662 if (allocator != NULL)
663 allocator->ctx = ctx;
664
665 /* pull the whole file/global stream into memory */
666 for (;;) {
667 int n_bytes = fread(buf, 1, sizeof(buf), f);
668 if (n_bytes < 0) {
669 if (f_page != NULL)
670 jbig2_error(ctx, JBIG2_SEVERITY_WARNING, JBIG2_UNKNOWN_SEGMENT_NUMBER, "unable to read jbig2 global stream");
671 else
672 jbig2_error(ctx, JBIG2_SEVERITY_WARNING, JBIG2_UNKNOWN_SEGMENT_NUMBER, "unable to read jbig2 page stream");
673 }
674 if (n_bytes <= 0)
675 break;
676
677 if (jbig2_data_in(ctx, buf, (size_t) n_bytes) < 0)
678 {
679 if (f_page != NULL)
680 jbig2_error(ctx, JBIG2_SEVERITY_WARNING, JBIG2_UNKNOWN_SEGMENT_NUMBER, "unable to process jbig2 global stream");
681 else
682 jbig2_error(ctx, JBIG2_SEVERITY_WARNING, JBIG2_UNKNOWN_SEGMENT_NUMBER, "unable to process jbig2 page stream");
683 break;
684 }
685 }
686 fclose(f);
687
688 /* if there's a local page stream read that in its entirety */
689 if (f_page != NULL) {
690 Jbig2GlobalCtx *global_ctx = jbig2_make_global_ctx(ctx);
691
692 ctx = jbig2_ctx_new((Jbig2Allocator *) allocator, JBIG2_OPTIONS_EMBEDDED, global_ctx, error_callback, &error_callback_state);
693 if (ctx != NULL) {
694 if (allocator != NULL)
695 allocator->ctx = ctx;
696
697 for (;;) {
698 int n_bytes = fread(buf, 1, sizeof(buf), f_page);
699 if (n_bytes < 0)
700 jbig2_error(ctx, JBIG2_SEVERITY_WARNING, JBIG2_UNKNOWN_SEGMENT_NUMBER, "unable to read jbig2 page stream");
701 if (n_bytes <= 0)
702 break;
703
704 if (jbig2_data_in(ctx, buf, (size_t) n_bytes) < 0)
705 {
706 jbig2_error(ctx, JBIG2_SEVERITY_WARNING, JBIG2_UNKNOWN_SEGMENT_NUMBER, "unable to process jbig2 page stream");
707 break;
708 }
709 }
710 }
711 fclose(f_page);
712 jbig2_global_ctx_free(global_ctx);
713 }
714
715 /* retrieve and output the returned pages */
716 {
717 Jbig2Image *image;
718 FILE *out;
719
720 /* always complete a page, working around streams that lack end of
721 page segments: broken CVision streams, embedded streams or streams
722 with parse errors. */
723 code = jbig2_complete_page(ctx);
724 if (code < 0) {
725 jbig2_error(ctx, JBIG2_SEVERITY_WARNING, JBIG2_UNKNOWN_SEGMENT_NUMBER, "unable to complete page");
726 goto cleanup;
727 }
728
729 if (params.output_filename == NULL) {
730 switch (params.output_format) {
731 #ifdef HAVE_LIBPNG
732 case jbig2dec_format_png:
733 params.output_filename = make_output_filename(argv[filearg], ".png");
734 break;
735 #endif
736 case jbig2dec_format_pbm:
737 params.output_filename = make_output_filename(argv[filearg], ".pbm");
738 break;
739 default:
740 fprintf(stderr, "unsupported output format: %d\n", params.output_format);
741 goto cleanup;
742 }
743 } else {
744 int len = strlen(params.output_filename);
745
746 if ((len >= 3) && (params.output_format == jbig2dec_format_none))
747 /* try to set the output type by the given extension */
748 set_output_format(&params, params.output_filename + len - 3);
749 }
750
751 if (!strncmp(params.output_filename, "-", 2)) {
752 out = stdout;
753 } else {
754 if (params.verbose > 1)
755 fprintf(stderr, "saving decoded page as '%s'\n", params.output_filename);
756 if ((out = fopen(params.output_filename, "wb")) == NULL) {
757 fprintf(stderr, "unable to open '%s' for writing\n", params.output_filename);
758 goto cleanup;
759 }
760 }
761
762 /* retrieve and write out all the completed pages */
763 while ((image = jbig2_page_out(ctx)) != NULL) {
764 write_page_image(&params, out, image);
765 if (params.hash)
766 hash_image(&params, image);
767 jbig2_release_page(ctx, image);
768 }
769
770 if (out != stdout)
771 fclose(out);
772 if (params.hash)
773 write_document_hash(&params);
774 }
775
776 } /* end params.mode switch */
777
778 if (allocator != NULL && allocator->ctx != NULL) {
779 size_t limit_mb = allocator->memory_limit / MBYTE;
780 size_t peak_mb = allocator->memory_peak / MBYTE;
781 jbig2_error(allocator->ctx, JBIG2_SEVERITY_DEBUG, JBIG2_UNKNOWN_SEGMENT_NUMBER, "memory: limit: %lu Mbyte peak usage: %lu Mbyte", limit_mb, peak_mb);
782 }
783
784 /* fin */
785 result = 0;
786
787 cleanup:
788 flush_errors(&error_callback_state);
789 jbig2_ctx_free(ctx);
790 if (params.output_filename)
791 free(params.output_filename);
792 if (error_callback_state.last_message)
793 free(error_callback_state.last_message);
794 if (params.hash)
795 hash_free(&params);
796
797 return result;
798 }