/** * jbig2_add_page_result: composite a decoding result onto a page * * this is called to add the results of segment decode (when it * is an image) to a page image buffer **/ int jbig2_page_add_result(Jbig2Ctx *ctx, Jbig2Page *page, Jbig2Image *image, int x, int y, Jbig2ComposeOp op) { /* ensure image exists first */ if (page->image == NULL) { jbig2_error(ctx, JBIG2_SEVERITY_WARNING, -1, "page info possibly missing, no image defined"); return 0; } /* grow the page to accomodate a new stripe if necessary */ if (page->striped) { int new_height = y + image->height + page->end_row; if (page->image->height < new_height) { jbig2_error(ctx, JBIG2_SEVERITY_DEBUG, -1, "growing page buffer to %d rows " "to accomodate new stripe", new_height); jbig2_image_resize(ctx, page->image, page->image->width, new_height); } } jbig2_image_compose(ctx, page->image, image, x, y + page->end_row, op); return 0; }
/** * jbig2_get_page: return the next available page image buffer * * the client can call this at any time to check if any pages * have been decoded. If so, it returns the first available * one. The client should then call jbig2_release_page() when * it no longer needs to refer to the image buffer. * * since this is a public routine for the library clients, we * return an image structure pointer, even though the function * name refers to a page; the page structure is private. **/ Jbig2Image *jbig2_page_out(Jbig2Ctx *ctx) { int index; /* search for a completed page */ for (index=0; index < ctx->max_page_index; index++) { if (ctx->pages[index].state == JBIG2_PAGE_COMPLETE) { Jbig2Image *img = ctx->pages[index].image; uint32_t page_number = ctx->pages[index].number; ctx->pages[index].state = JBIG2_PAGE_RETURNED; if (img != NULL) { jbig2_error(ctx, JBIG2_SEVERITY_DEBUG, -1, "page %d returned to the client", page_number); return jbig2_image_clone(ctx, img); } else { jbig2_error(ctx, JBIG2_SEVERITY_WARNING, -1, "page %d returned with no associated image", page_number); ; /* continue */ } } } /* no pages available */ return NULL; }
/** * jbig2_complete_page: complete a page image * * called upon seeing an 'end of page' segment, this routine * marks a page as completed so it can be returned. * compositing will have already happened in the previous * segment handlers. **/ int jbig2_complete_page (Jbig2Ctx *ctx) { int code = 0; /* check for unfinished segments */ if (ctx->segment_index != ctx->n_segments) { Jbig2Segment *segment = ctx->segments[ctx->segment_index]; /* Some versions of Xerox Workcentre generate PDF files with the segment data length field of the last segment set to -1. Try to cope with this here. */ if ((segment->data_length & 0xffffffff) == 0xffffffff) { jbig2_error(ctx, JBIG2_SEVERITY_WARNING, segment->number, "File has an invalid segment data length!" " Trying to decode using the available data."); segment->data_length = ctx->buf_wr_ix - ctx->buf_rd_ix; code = jbig2_parse_segment(ctx, segment, ctx->buf + ctx->buf_rd_ix); ctx->buf_rd_ix += segment->data_length; ctx->segment_index++; } } /* ensure image exists before marking page as complete */ if (ctx->pages[ctx->current_page].image != NULL) { ctx->pages[ctx->current_page].state = JBIG2_PAGE_COMPLETE; } return code; }
static char *jbig2_strndup(Jbig2Ctx *ctx, const char *c, const int len) { char *s = jbig2_alloc(ctx->allocator, len*sizeof(char)); if (s == NULL) { jbig2_error(ctx, JBIG2_SEVERITY_FATAL, -1, "unable to duplicate comment string"); } else { memcpy(s, c, len); } return s; }
/** * jbig2_release_page: tell the library a page can be freed **/ int jbig2_release_page(Jbig2Ctx *ctx, Jbig2Image *image) { int index; /* find the matching page struct and mark it released */ for (index = 0; index < ctx->max_page_index; index++) { if (ctx->pages[index].image == image) { jbig2_image_release(ctx, image); ctx->pages[index].state = JBIG2_PAGE_RELEASED; jbig2_error(ctx, JBIG2_SEVERITY_DEBUG, -1, "page %d released by the client", ctx->pages[index].number); return 0; } } /* no matching pages */ jbig2_error(ctx, JBIG2_SEVERITY_WARNING, -1, "jbig2_release_page called on unknown page"); return 1; }
/** * jbig2_end_of_stripe: parse and implement an end of stripe segment **/ int jbig2_end_of_stripe(Jbig2Ctx *ctx, Jbig2Segment *segment, const uint8_t *segment_data) { Jbig2Page page = ctx->pages[ctx->current_page]; int end_row; end_row = jbig2_get_int32(segment_data); if (end_row < page.end_row) { jbig2_error(ctx, JBIG2_SEVERITY_WARNING, segment->number, "end of stripe segment with non-positive end row advance" " (new end row %d vs current end row %d)", end_row, page.end_row); } else { jbig2_error(ctx, JBIG2_SEVERITY_INFO, segment->number, "end of stripe: advancing end row to %d", end_row); } page.end_row = end_row; return 0; }
void jbig2_dump_symbol_dict(Jbig2Ctx *ctx, Jbig2Segment *segment) { Jbig2SymbolDict *dict = (Jbig2SymbolDict *)segment->result; int index; char filename[24]; if (dict == NULL) return; jbig2_error(ctx, JBIG2_SEVERITY_INFO, segment->number, "dumping symbol dict as %d individual png files\n", dict->n_symbols); for (index = 0; index < dict->n_symbols; index++) { snprintf(filename, sizeof(filename), "symbol_%02d-%04d.png", segment->number, index); jbig2_error(ctx, JBIG2_SEVERITY_DEBUG, segment->number, "dumping symbol %d/%d as '%s'", index, dict->n_symbols, filename); #ifdef HAVE_LIBPNG jbig2_image_write_png_file(dict->glyphs[index], filename); #else jbig2_image_write_pbm_file(dict->glyphs[index], filename); #endif } }
Jbig2ArithIntCtx * jbig2_arith_int_ctx_new(Jbig2Ctx *ctx) { Jbig2ArithIntCtx *result = jbig2_new(ctx, Jbig2ArithIntCtx, 1); if (result == NULL) { jbig2_error(ctx, JBIG2_SEVERITY_FATAL, -1, "failed to allocate Jbig2ArithIntCtx in jbig2_arith_int_ctx_new"); } else { memset(result->IAx, 0, sizeof(result->IAx)); } return result; }
/** * jbig2_end_of_page: parse and implement an end of page segment **/ int jbig2_end_of_page(Jbig2Ctx *ctx, Jbig2Segment *segment, const uint8_t *segment_data) { uint32_t page_number = ctx->pages[ctx->current_page].number; if (segment->page_association != page_number) { jbig2_error(ctx, JBIG2_SEVERITY_WARNING, segment->number, "end of page marker for page %d doesn't match current page number %d", segment->page_association, page_number); } jbig2_error(ctx, JBIG2_SEVERITY_INFO, segment->number, "end of page %d", page_number); jbig2_complete_page(ctx); #ifdef OUTPUT_PBM jbig2_image_write_pbm(ctx->pages[ctx->current_page].image, stdout); #endif return 0; }
/* dump the page struct info */ static void dump_page_info(Jbig2Ctx *ctx, Jbig2Segment *segment, Jbig2Page *page) { if (page->x_resolution == 0) { jbig2_error(ctx, JBIG2_SEVERITY_INFO, segment->number, "page %d image is %dx%d (unknown res)", page->number, page->width, page->height); } else if (page->x_resolution == page->y_resolution) { jbig2_error(ctx, JBIG2_SEVERITY_INFO, segment->number, "page %d image is %dx%d (%d ppm)", page->number, page->width, page->height, page->x_resolution); } else { jbig2_error(ctx, JBIG2_SEVERITY_INFO, segment->number, "page %d image is %dx%d (%dx%d ppm)", page->number, page->width, page->height, page->x_resolution, page->y_resolution); } if (page->striped) { jbig2_error(ctx, JBIG2_SEVERITY_INFO, segment->number, "\tmaximum stripe size: %d", page->stripe_size); } }
/* metadata key,value list object */ Jbig2Metadata *jbig2_metadata_new(Jbig2Ctx *ctx, Jbig2Encoding encoding) { Jbig2Metadata *md = jbig2_new(ctx, Jbig2Metadata, 1); if (md != NULL) { md->encoding = encoding; md->entries = 0; md->max_entries = 4; md->keys = jbig2_new(ctx, char*, md->max_entries); md->values = jbig2_new(ctx, char*, md->max_entries); if (md->keys == NULL || md->values == NULL) { jbig2_error(ctx, JBIG2_SEVERITY_FATAL, -1, "failed to allocate storage for metadata keys/values"); jbig2_metadata_free(ctx, md); md = NULL; } } else {
/** * jbig2_get_page: return the next available page image buffer * * the client can call this at any time to check if any pages * have been decoded. If so, it returns the first available * one. The client should then call jbig2_release_page() when * it no longer needs to refer to the image buffer. * * since this is a public routine for the library clients, we * return an image structure pointer, even though the function * name refers to a page; the page structure is private. **/ Jbig2Image *jbig2_page_out(Jbig2Ctx *ctx) { int index; /* search for a completed page */ for (index=0; index < ctx->max_page_index; index++) { if (ctx->pages[index].state == JBIG2_PAGE_COMPLETE) { ctx->pages[index].state = JBIG2_PAGE_RETURNED; jbig2_error(ctx, JBIG2_SEVERITY_DEBUG, -1, "page %d returned to the client", ctx->pages[index].number); return ctx->pages[index].image; } } /* no pages available */ return NULL; }
int jbig2_metadata_add(Jbig2Ctx *ctx, Jbig2Metadata *md, const char *key, const int key_length, const char *value, const int value_length) { char **keys, **values; /* grow the array if necessary */ if (md->entries == md->max_entries) { md->max_entries >>= 2; keys = jbig2_realloc(ctx->allocator, md->keys, md->max_entries); values = jbig2_realloc(ctx->allocator, md->values, md->max_entries); if (keys == NULL || values == NULL) { jbig2_error(ctx, JBIG2_SEVERITY_FATAL, -1, "unable to resize metadata structure"); return -1; } md->keys = keys; md->values = values; }
/** * jbig2_add_page_result: composite a decoding result onto a page * * this is called to add the results of segment decode (when it * is an image) to a page image buffer **/ int jbig2_page_add_result(Jbig2Ctx *ctx, Jbig2Page *page, Jbig2Image *image, int x, int y, Jbig2ComposeOp op) { /* grow the page to accomodate a new stripe if necessary */ if (page->striped) { int new_height = y + image->height + page->end_row; if (page->image->height < new_height) { jbig2_error(ctx, JBIG2_SEVERITY_DEBUG, -1, "growing page buffer to %d rows " "to accomodate new stripe", new_height); jbig2_image_resize(ctx, page->image, page->image->width, new_height); } } jbig2_image_compose(ctx, page->image, image, x, y + page->end_row, JBIG2_COMPOSE_OR); return 0; }
/** * jbig2_page_info: parse page info segment * * Parse the page info segment data and fill out a corresponding * Jbig2Page struct and ready it for subsequent rendered data, * including allocating an image buffer for the page (or the first stripe) **/ int jbig2_page_info (Jbig2Ctx *ctx, Jbig2Segment *segment, const uint8_t *segment_data) { Jbig2Page *page; /* a new page info segment implies the previous page is finished */ page = &(ctx->pages[ctx->current_page]); if ((page->number != 0) && ((page->state == JBIG2_PAGE_NEW) || (page->state == JBIG2_PAGE_FREE))) { page->state = JBIG2_PAGE_COMPLETE; jbig2_error(ctx, JBIG2_SEVERITY_WARNING, segment->number, "unexpected page info segment, marking previous page finished"); } /* find a free page */ { int index, j; index = ctx->current_page; while (ctx->pages[index].state != JBIG2_PAGE_FREE) { index++; if (index >= ctx->max_page_index) { /* grow the list */ ctx->pages = jbig2_renew(ctx, ctx->pages, Jbig2Page, (ctx->max_page_index <<= 2)); for (j=index; j < ctx->max_page_index; j++) { ctx->pages[j].state = JBIG2_PAGE_FREE; ctx->pages[j].number = 0; ctx->pages[j].image = NULL; } } } page = &(ctx->pages[index]); ctx->current_page = index; page->state = JBIG2_PAGE_NEW; page->number = segment->page_association; } /* FIXME: would be nice if we tried to work around this */ if (segment->data_length < 19) { return jbig2_error(ctx, JBIG2_SEVERITY_FATAL, segment->number, "segment too short"); } /* 7.4.8.x */ page->width = jbig2_get_uint32(segment_data); page->height = jbig2_get_uint32(segment_data + 4); page->x_resolution = jbig2_get_uint32(segment_data + 8); page->y_resolution = jbig2_get_uint32(segment_data + 12); page->flags = segment_data[16]; /* 7.4.8.6 */ { int16_t striping = jbig2_get_int16(segment_data +17); if (striping & 0x8000) { page->striped = TRUE; page->stripe_size = striping & 0x7FFF; } else { page->striped = FALSE; page->stripe_size = 0; /* would page->height be better? */ } } if (page->height == 0xFFFFFFFF && page->striped == FALSE) { jbig2_error(ctx, JBIG2_SEVERITY_WARNING, segment->number, "height is unspecified but page is not markes as striped"); page->striped = TRUE; } page->end_row = 0; if (segment->data_length > 19) { jbig2_error(ctx, JBIG2_SEVERITY_WARNING, segment->number, "extra data in segment"); } dump_page_info(ctx, segment, page); /* allocate an approprate page image buffer */ /* 7.4.8.2 */ if (page->height == 0xFFFFFFFF) { page->image = jbig2_image_new(ctx, page->width, page->stripe_size); } else { page->image = jbig2_image_new(ctx, page->width, page->height); } if (page->image == NULL) { return jbig2_error(ctx, JBIG2_SEVERITY_FATAL, segment->number, "failed to allocate buffer for page image"); } else { /* 8.2 (3) fill the page with the default pixel value */ jbig2_image_clear(ctx, page->image, (page->flags & 4)); jbig2_error(ctx, JBIG2_SEVERITY_DEBUG, segment->number, "allocated %dx%d page image (%d bytes)", page->image->width, page->image->height, page->image->stride*page->image->height); } return 0; }
/** * jbig2_text_region: read a text region segment header **/ int jbig2_text_region(Jbig2Ctx *ctx, Jbig2Segment *segment, const byte *segment_data) { int offset = 0; Jbig2RegionSegmentInfo region_info; Jbig2TextRegionParams params; Jbig2Image *image = NULL; Jbig2SymbolDict **dicts = NULL; int n_dicts = 0; uint16_t flags = 0; uint16_t huffman_flags = 0; Jbig2ArithCx *GR_stats = NULL; int code = 0; Jbig2WordStream *ws = NULL; Jbig2ArithState *as = NULL; int table_index = 0; const Jbig2HuffmanParams *huffman_params = NULL; /* 7.4.1 */ if (segment->data_length < 17) goto too_short; jbig2_get_region_segment_info(®ion_info, segment_data); offset += 17; /* 7.4.3.1.1 */ flags = jbig2_get_uint16(segment_data + offset); offset += 2; jbig2_error(ctx, JBIG2_SEVERITY_DEBUG, segment->number, "text region header flags 0x%04x", flags); /* zero params to ease cleanup later */ memset(¶ms, 0, sizeof(Jbig2TextRegionParams)); params.SBHUFF = flags & 0x0001; params.SBREFINE = flags & 0x0002; params.LOGSBSTRIPS = (flags & 0x000c) >> 2; params.SBSTRIPS = 1 << params.LOGSBSTRIPS; params.REFCORNER = (Jbig2RefCorner)((flags & 0x0030) >> 4); params.TRANSPOSED = flags & 0x0040; params.SBCOMBOP = (Jbig2ComposeOp)((flags & 0x0180) >> 7); params.SBDEFPIXEL = flags & 0x0200; /* SBDSOFFSET is a signed 5 bit integer */ params.SBDSOFFSET = (flags & 0x7C00) >> 10; if (params.SBDSOFFSET > 0x0f) params.SBDSOFFSET -= 0x20; params.SBRTEMPLATE = flags & 0x8000; if (params.SBDSOFFSET) { jbig2_error(ctx, JBIG2_SEVERITY_DEBUG, segment->number, "text region has SBDSOFFSET %d", params.SBDSOFFSET); } if (params.SBHUFF) /* Huffman coding */ { /* 7.4.3.1.2 */ huffman_flags = jbig2_get_uint16(segment_data + offset); offset += 2; if (huffman_flags & 0x8000) jbig2_error(ctx, JBIG2_SEVERITY_WARNING, segment->number, "reserved bit 15 of text region huffman flags is not zero"); } else /* arithmetic coding */ { /* 7.4.3.1.3 */ if ((params.SBREFINE) && !(params.SBRTEMPLATE)) { params.sbrat[0] = segment_data[offset]; params.sbrat[1] = segment_data[offset + 1]; params.sbrat[2] = segment_data[offset + 2]; params.sbrat[3] = segment_data[offset + 3]; offset += 4; } } /* 7.4.3.1.4 */ params.SBNUMINSTANCES = jbig2_get_uint32(segment_data + offset); offset += 4; if (params.SBHUFF) { /* 7.4.3.1.5 - Symbol ID Huffman table */ /* ...this is handled in the segment body decoder */ /* 7.4.3.1.6 - Other Huffman table selection */ switch (huffman_flags & 0x0003) { case 0: /* Table B.6 */ params.SBHUFFFS = jbig2_build_huffman_table(ctx, &jbig2_huffman_params_F); break; case 1: /* Table B.7 */ params.SBHUFFFS = jbig2_build_huffman_table(ctx, &jbig2_huffman_params_G); break; case 3: /* Custom table from referred segment */ huffman_params = jbig2_find_table(ctx, segment, table_index); if (huffman_params == NULL) { code = jbig2_error(ctx, JBIG2_SEVERITY_FATAL, segment->number, "Custom FS huffman table not found (%d)", table_index); goto cleanup1; } params.SBHUFFFS = jbig2_build_huffman_table(ctx, huffman_params); ++table_index; break; case 2: /* invalid */ default: code = jbig2_error(ctx, JBIG2_SEVERITY_FATAL, segment->number, "text region specified invalid FS huffman table"); goto cleanup1; break; } if (params.SBHUFFFS == NULL) { code = jbig2_error(ctx, JBIG2_SEVERITY_FATAL, segment->number, "failed to allocate text region specified FS huffman table"); goto cleanup1; } switch ((huffman_flags & 0x000c) >> 2) { case 0: /* Table B.8 */ params.SBHUFFDS = jbig2_build_huffman_table(ctx, &jbig2_huffman_params_H); break; case 1: /* Table B.9 */ params.SBHUFFDS = jbig2_build_huffman_table(ctx, &jbig2_huffman_params_I); break; case 2: /* Table B.10 */ params.SBHUFFDS = jbig2_build_huffman_table(ctx, &jbig2_huffman_params_J); break; case 3: /* Custom table from referred segment */ huffman_params = jbig2_find_table(ctx, segment, table_index); if (huffman_params == NULL) { code = jbig2_error(ctx, JBIG2_SEVERITY_FATAL, segment->number, "Custom DS huffman table not found (%d)", table_index); goto cleanup1; } params.SBHUFFDS = jbig2_build_huffman_table(ctx, huffman_params); ++table_index; break; } if (params.SBHUFFDS == NULL) { code = jbig2_error(ctx, JBIG2_SEVERITY_FATAL, segment->number, "failed to allocate text region specified DS huffman table"); goto cleanup1; } switch ((huffman_flags & 0x0030) >> 4) { case 0: /* Table B.11 */ params.SBHUFFDT = jbig2_build_huffman_table(ctx, &jbig2_huffman_params_K); break; case 1: /* Table B.12 */ params.SBHUFFDT = jbig2_build_huffman_table(ctx, &jbig2_huffman_params_L); break; case 2: /* Table B.13 */ params.SBHUFFDT = jbig2_build_huffman_table(ctx, &jbig2_huffman_params_M); break; case 3: /* Custom table from referred segment */ huffman_params = jbig2_find_table(ctx, segment, table_index); if (huffman_params == NULL) { code = jbig2_error(ctx, JBIG2_SEVERITY_FATAL, segment->number, "Custom DT huffman table not found (%d)", table_index); goto cleanup1; } params.SBHUFFDT = jbig2_build_huffman_table(ctx, huffman_params); ++table_index; break; } if (params.SBHUFFDT == NULL) { code = jbig2_error(ctx, JBIG2_SEVERITY_FATAL, segment->number, "failed to allocate text region specified DT huffman table"); goto cleanup1; } switch ((huffman_flags & 0x00c0) >> 6) { case 0: /* Table B.14 */ params.SBHUFFRDW = jbig2_build_huffman_table(ctx, &jbig2_huffman_params_N); break; case 1: /* Table B.15 */ params.SBHUFFRDW = jbig2_build_huffman_table(ctx, &jbig2_huffman_params_O); break; case 3: /* Custom table from referred segment */ huffman_params = jbig2_find_table(ctx, segment, table_index); if (huffman_params == NULL) { code = jbig2_error(ctx, JBIG2_SEVERITY_FATAL, segment->number, "Custom RDW huffman table not found (%d)", table_index); goto cleanup1; } params.SBHUFFRDW = jbig2_build_huffman_table(ctx, huffman_params); ++table_index; break; case 2: /* invalid */ default: code = jbig2_error(ctx, JBIG2_SEVERITY_FATAL, segment->number, "text region specified invalid RDW huffman table"); goto cleanup1; break; } if (params.SBHUFFRDW == NULL) { code = jbig2_error(ctx, JBIG2_SEVERITY_FATAL, segment->number, "failed to allocate text region specified RDW huffman table"); goto cleanup1; } switch ((huffman_flags & 0x0300) >> 8) { case 0: /* Table B.14 */ params.SBHUFFRDH = jbig2_build_huffman_table(ctx, &jbig2_huffman_params_N); break; case 1: /* Table B.15 */ params.SBHUFFRDH = jbig2_build_huffman_table(ctx, &jbig2_huffman_params_O); break; case 3: /* Custom table from referred segment */ huffman_params = jbig2_find_table(ctx, segment, table_index); if (huffman_params == NULL) { code = jbig2_error(ctx, JBIG2_SEVERITY_FATAL, segment->number, "Custom RDH huffman table not found (%d)", table_index); goto cleanup1; } params.SBHUFFRDH = jbig2_build_huffman_table(ctx, huffman_params); ++table_index; break; case 2: /* invalid */ default: code = jbig2_error(ctx, JBIG2_SEVERITY_FATAL, segment->number, "text region specified invalid RDH huffman table"); goto cleanup1; break; } if (params.SBHUFFRDH == NULL) { code = jbig2_error(ctx, JBIG2_SEVERITY_FATAL, segment->number, "failed to allocate text region specified RDH huffman table"); goto cleanup1; } switch ((huffman_flags & 0x0c00) >> 10) { case 0: /* Table B.14 */ params.SBHUFFRDX = jbig2_build_huffman_table(ctx, &jbig2_huffman_params_N); break; case 1: /* Table B.15 */ params.SBHUFFRDX = jbig2_build_huffman_table(ctx, &jbig2_huffman_params_O); break; case 3: /* Custom table from referred segment */ huffman_params = jbig2_find_table(ctx, segment, table_index); if (huffman_params == NULL) { code = jbig2_error(ctx, JBIG2_SEVERITY_FATAL, segment->number, "Custom RDX huffman table not found (%d)", table_index); goto cleanup1; } params.SBHUFFRDX = jbig2_build_huffman_table(ctx, huffman_params); ++table_index; break; case 2: /* invalid */ default: code = jbig2_error(ctx, JBIG2_SEVERITY_FATAL, segment->number, "text region specified invalid RDX huffman table"); goto cleanup1; break; } if (params.SBHUFFRDX == NULL) { code = jbig2_error(ctx, JBIG2_SEVERITY_FATAL, segment->number, "failed to allocate text region specified RDX huffman table"); goto cleanup1; } switch ((huffman_flags & 0x3000) >> 12) { case 0: /* Table B.14 */ params.SBHUFFRDY = jbig2_build_huffman_table(ctx, &jbig2_huffman_params_N); break; case 1: /* Table B.15 */ params.SBHUFFRDY = jbig2_build_huffman_table(ctx, &jbig2_huffman_params_O); break; case 3: /* Custom table from referred segment */ huffman_params = jbig2_find_table(ctx, segment, table_index); if (huffman_params == NULL) { code = jbig2_error(ctx, JBIG2_SEVERITY_FATAL, segment->number, "Custom RDY huffman table not found (%d)", table_index); goto cleanup1; } params.SBHUFFRDY = jbig2_build_huffman_table(ctx, huffman_params); ++table_index; break; case 2: /* invalid */ default: code = jbig2_error(ctx, JBIG2_SEVERITY_FATAL, segment->number, "text region specified invalid RDY huffman table"); goto cleanup1; break; } if (params.SBHUFFRDY == NULL) { code = jbig2_error(ctx, JBIG2_SEVERITY_FATAL, segment->number, "failed to allocate text region specified RDY huffman table"); goto cleanup1; } switch ((huffman_flags & 0x4000) >> 14) { case 0: /* Table B.1 */ params.SBHUFFRSIZE = jbig2_build_huffman_table(ctx, &jbig2_huffman_params_A); break; case 1: /* Custom table from referred segment */ huffman_params = jbig2_find_table(ctx, segment, table_index); if (huffman_params == NULL) { code = jbig2_error(ctx, JBIG2_SEVERITY_FATAL, segment->number, "Custom RSIZE huffman table not found (%d)", table_index); goto cleanup1; } params.SBHUFFRSIZE = jbig2_build_huffman_table(ctx, huffman_params); ++table_index; break; } if (params.SBHUFFRSIZE == NULL) { code = jbig2_error(ctx, JBIG2_SEVERITY_FATAL, segment->number, "failed to allocate text region specified RSIZE huffman table"); goto cleanup1; } if (huffman_flags & 0x8000) { jbig2_error(ctx, JBIG2_SEVERITY_WARNING, segment->number, "text region huffman flags bit 15 is set, contrary to spec"); } /* 7.4.3.1.7 */ /* For convenience this is done in the body decoder routine */ } jbig2_error(ctx, JBIG2_SEVERITY_INFO, segment->number, "text region: %d x %d @ (%d,%d) %d symbols", region_info.width, region_info.height, region_info.x, region_info.y, params.SBNUMINSTANCES); /* 7.4.3.2 (2) - compose the list of symbol dictionaries */ n_dicts = jbig2_sd_count_referred(ctx, segment); if (n_dicts != 0) { dicts = jbig2_sd_list_referred(ctx, segment); } else { code = jbig2_error(ctx, JBIG2_SEVERITY_FATAL, segment->number, "text region refers to no symbol dictionaries!"); goto cleanup1; } if (dicts == NULL) { code = jbig2_error(ctx, JBIG2_SEVERITY_FATAL, segment->number, "unable to retrive symbol dictionaries! previous parsing error?"); goto cleanup1; } else { int index; if (dicts[0] == NULL) { code =jbig2_error(ctx, JBIG2_SEVERITY_WARNING, segment->number, "unable to find first referenced symbol dictionary!"); goto cleanup1; } for (index = 1; index < n_dicts; index++) if (dicts[index] == NULL) { jbig2_error(ctx, JBIG2_SEVERITY_WARNING, segment->number, "unable to find all referenced symbol dictionaries!"); n_dicts = index; } } /* 7.4.3.2 (3) */ if (!params.SBHUFF && params.SBREFINE) { int stats_size = params.SBRTEMPLATE ? 1 << 10 : 1 << 13; GR_stats = jbig2_new(ctx, Jbig2ArithCx, stats_size); if (GR_stats == NULL) { code = jbig2_error(ctx, JBIG2_SEVERITY_FATAL, segment->number, "could not allocate GR_stats"); goto cleanup1; } memset(GR_stats, 0, stats_size); } image = jbig2_image_new(ctx, region_info.width, region_info.height); if (image == NULL) { code =jbig2_error(ctx, JBIG2_SEVERITY_FATAL, segment->number, "couldn't allocate text region image"); goto cleanup1; } ws = jbig2_word_stream_buf_new(ctx, segment_data + offset, segment->data_length - offset); if (ws == NULL) { code = jbig2_error(ctx, JBIG2_SEVERITY_FATAL, segment->number, "couldn't allocate text region image"); jbig2_image_release(ctx, image); goto cleanup2; } if (!params.SBHUFF) { int SBSYMCODELEN, index; int SBNUMSYMS = 0; for (index = 0; index < n_dicts; index++) { SBNUMSYMS += dicts[index]->n_symbols; } as = jbig2_arith_new(ctx, ws); params.IADT = jbig2_arith_int_ctx_new(ctx); params.IAFS = jbig2_arith_int_ctx_new(ctx); params.IADS = jbig2_arith_int_ctx_new(ctx); params.IAIT = jbig2_arith_int_ctx_new(ctx); if ((as == NULL) || (params.IADT == NULL) || (params.IAFS == NULL) || (params.IADS == NULL) || (params.IAIT == NULL)) { code = jbig2_error(ctx, JBIG2_SEVERITY_FATAL, segment->number, "couldn't allocate text region image data"); goto cleanup3; } /* Table 31 */ for (SBSYMCODELEN = 0; (1 << SBSYMCODELEN) < SBNUMSYMS; SBSYMCODELEN++); params.IAID = jbig2_arith_iaid_ctx_new(ctx, SBSYMCODELEN); params.IARI = jbig2_arith_int_ctx_new(ctx); params.IARDW = jbig2_arith_int_ctx_new(ctx); params.IARDH = jbig2_arith_int_ctx_new(ctx); params.IARDX = jbig2_arith_int_ctx_new(ctx); params.IARDY = jbig2_arith_int_ctx_new(ctx); if ((params.IAID == NULL) || (params.IARI == NULL) || (params.IARDW == NULL) || (params.IARDH == NULL) || (params.IARDX == NULL) || (params.IARDY == NULL)) { code = jbig2_error(ctx, JBIG2_SEVERITY_FATAL, segment->number, "couldn't allocate text region image data"); goto cleanup4; } } code = jbig2_decode_text_region(ctx, segment, ¶ms, (const Jbig2SymbolDict * const *)dicts, n_dicts, image, segment_data + offset, segment->data_length - offset, GR_stats, as, as ? NULL : ws); if (code < 0) { jbig2_error(ctx, JBIG2_SEVERITY_FATAL, segment->number, "failed to decode text region image data"); jbig2_image_release(ctx, image); goto cleanup4; } if ((segment->flags & 63) == 4) { /* we have an intermediate region here. save it for later */ segment->result = image; } else { /* otherwise composite onto the page */ jbig2_error(ctx, JBIG2_SEVERITY_DEBUG, segment->number, "composing %dx%d decoded text region onto page at (%d, %d)", region_info.width, region_info.height, region_info.x, region_info.y); jbig2_page_add_result(ctx, &ctx->pages[ctx->current_page], image, region_info.x, region_info.y, region_info.op); jbig2_image_release(ctx, image); } cleanup4: if (!params.SBHUFF) { jbig2_arith_iaid_ctx_free(ctx, params.IAID); jbig2_arith_int_ctx_free(ctx, params.IARI); jbig2_arith_int_ctx_free(ctx, params.IARDW); jbig2_arith_int_ctx_free(ctx, params.IARDH); jbig2_arith_int_ctx_free(ctx, params.IARDX); jbig2_arith_int_ctx_free(ctx, params.IARDY); } cleanup3: if (!params.SBHUFF) { jbig2_arith_int_ctx_free(ctx, params.IADT); jbig2_arith_int_ctx_free(ctx, params.IAFS); jbig2_arith_int_ctx_free(ctx, params.IADS); jbig2_arith_int_ctx_free(ctx, params.IAIT); jbig2_free(ctx->allocator, as); } jbig2_word_stream_buf_free(ctx, ws); cleanup2: if (!params.SBHUFF && params.SBREFINE) { jbig2_free(ctx->allocator, GR_stats); } cleanup1: if (params.SBHUFF) { jbig2_release_huffman_table(ctx, params.SBHUFFFS); jbig2_release_huffman_table(ctx, params.SBHUFFDS); jbig2_release_huffman_table(ctx, params.SBHUFFDT); jbig2_release_huffman_table(ctx, params.SBHUFFRDX); jbig2_release_huffman_table(ctx, params.SBHUFFRDY); jbig2_release_huffman_table(ctx, params.SBHUFFRDW); jbig2_release_huffman_table(ctx, params.SBHUFFRDH); jbig2_release_huffman_table(ctx, params.SBHUFFRSIZE); } jbig2_free(ctx->allocator, dicts); return code; too_short: return jbig2_error(ctx, JBIG2_SEVERITY_FATAL, segment->number, "Segment too short"); }
/** * jbig2_decode_text_region: decode a text region segment * * @ctx: jbig2 decoder context * @segment: jbig2 segment (header) structure * @params: parameters from the text region header * @dicts: an array of referenced symbol dictionaries * @n_dicts: the number of referenced symbol dictionaries * @image: image structure in which to store the decoded region bitmap * @data: pointer to text region data to be decoded * @size: length of text region data * * Implements the text region decoding procedure * described in section 6.4 of the JBIG2 spec. * * returns: 0 on success **/ int jbig2_decode_text_region(Jbig2Ctx *ctx, Jbig2Segment *segment, const Jbig2TextRegionParams *params, const Jbig2SymbolDict * const *dicts, const int n_dicts, Jbig2Image *image, const byte *data, const size_t size, Jbig2ArithCx *GR_stats, Jbig2ArithState *as, Jbig2WordStream *ws) { /* relevent bits of 6.4.4 */ uint32_t NINSTANCES; uint32_t ID; int32_t STRIPT; int32_t FIRSTS; int32_t DT; int32_t DFS; int32_t IDS; int32_t CURS; int32_t CURT; int S,T; int x,y; bool first_symbol; uint32_t index, SBNUMSYMS; Jbig2Image *IB = NULL; Jbig2HuffmanState *hs = NULL; Jbig2HuffmanTable *SBSYMCODES = NULL; int code = 0; int RI; SBNUMSYMS = 0; for (index = 0; index < n_dicts; index++) { SBNUMSYMS += dicts[index]->n_symbols; } jbig2_error(ctx, JBIG2_SEVERITY_DEBUG, segment->number, "symbol list contains %d glyphs in %d dictionaries", SBNUMSYMS, n_dicts); if (params->SBHUFF) { Jbig2HuffmanTable *runcodes = NULL; Jbig2HuffmanParams runcodeparams; Jbig2HuffmanLine runcodelengths[35]; Jbig2HuffmanLine *symcodelengths = NULL; Jbig2HuffmanParams symcodeparams; int err, len, range, r; jbig2_error(ctx, JBIG2_SEVERITY_DEBUG, segment->number, "huffman coded text region"); hs = jbig2_huffman_new(ctx, ws); if (hs == NULL) { jbig2_error(ctx, JBIG2_SEVERITY_WARNING, segment->number, "failed to allocate storage for text region"); return -1; } /* 7.4.3.1.7 - decode symbol ID Huffman table */ /* this is actually part of the segment header, but it is more convenient to handle it here */ /* parse and build the runlength code huffman table */ for (index = 0; index < 35; index++) { runcodelengths[index].PREFLEN = jbig2_huffman_get_bits(hs, 4); runcodelengths[index].RANGELEN = 0; runcodelengths[index].RANGELOW = index; jbig2_error(ctx, JBIG2_SEVERITY_DEBUG, segment->number, " read runcode%d length %d", index, runcodelengths[index].PREFLEN); } runcodeparams.HTOOB = 0; runcodeparams.lines = runcodelengths; runcodeparams.n_lines = 35; runcodes = jbig2_build_huffman_table(ctx, &runcodeparams); if (runcodes == NULL) { jbig2_error(ctx, JBIG2_SEVERITY_FATAL, segment->number, "error constructing symbol id runcode table!"); code = -1; goto cleanup1; } /* decode the symbol id codelengths using the runlength table */ symcodelengths = jbig2_new(ctx, Jbig2HuffmanLine, SBNUMSYMS); if (symcodelengths == NULL) { jbig2_error(ctx, JBIG2_SEVERITY_FATAL, segment->number, "memory allocation failure reading symbol ID huffman table!"); code = -1; goto cleanup1; } index = 0; while (index < SBNUMSYMS) { code = jbig2_huffman_get(hs, runcodes, &err); if (err != 0 || code < 0 || code >= 35) { jbig2_error(ctx, JBIG2_SEVERITY_FATAL, segment->number, "error reading symbol ID huffman table!"); code = err ? err : -1; goto cleanup1; } if (code < 32) { len = code; range = 1; } else { if (code == 32) { len = symcodelengths[index-1].PREFLEN; if (index < 1) { jbig2_error(ctx, JBIG2_SEVERITY_FATAL, segment->number, "error decoding symbol id table: run length with no antecedent!"); code = -1; goto cleanup1; } } else { len = 0; /* code == 33 or 34 */ } if (code == 32) range = jbig2_huffman_get_bits(hs, 2) + 3; else if (code == 33) range = jbig2_huffman_get_bits(hs, 3) + 3; else if (code == 34) range = jbig2_huffman_get_bits(hs, 7) + 11; } jbig2_error(ctx, JBIG2_SEVERITY_DEBUG, segment->number, " read runcode%d at index %d (length %d range %d)", code, index, len, range); if (index+range > SBNUMSYMS) { jbig2_error(ctx, JBIG2_SEVERITY_WARNING, segment->number, "runlength extends %d entries beyond the end of symbol id table!", index+range - SBNUMSYMS); range = SBNUMSYMS - index; } for (r = 0; r < range; r++) { symcodelengths[index+r].PREFLEN = len; symcodelengths[index+r].RANGELEN = 0; symcodelengths[index+r].RANGELOW = index + r; } index += r; } if (index < SBNUMSYMS) { jbig2_error(ctx, JBIG2_SEVERITY_WARNING, segment->number, "runlength codes do not cover the available symbol set"); } symcodeparams.HTOOB = 0; symcodeparams.lines = symcodelengths; symcodeparams.n_lines = SBNUMSYMS; /* skip to byte boundary */ jbig2_huffman_skip(hs); /* finally, construct the symbol id huffman table itself */ SBSYMCODES = jbig2_build_huffman_table(ctx, &symcodeparams); cleanup1: jbig2_free(ctx->allocator, symcodelengths); jbig2_release_huffman_table(ctx, runcodes); if (SBSYMCODES == NULL) { jbig2_error(ctx, JBIG2_SEVERITY_FATAL, segment->number, "could not construct Symbol ID huffman table!"); jbig2_huffman_free(ctx, hs); return ((code != 0) ? code : -1); } } /* 6.4.5 (1) */ jbig2_image_clear(ctx, image, params->SBDEFPIXEL); /* 6.4.6 */ if (params->SBHUFF) { STRIPT = jbig2_huffman_get(hs, params->SBHUFFDT, &code); } else { code = jbig2_arith_int_decode(params->IADT, as, &STRIPT); if (code < 0) goto cleanup2; } /* 6.4.5 (2) */ STRIPT *= -(params->SBSTRIPS); FIRSTS = 0; NINSTANCES = 0; /* 6.4.5 (3) */ while (NINSTANCES < params->SBNUMINSTANCES) { /* (3b) */ if (params->SBHUFF) { DT = jbig2_huffman_get(hs, params->SBHUFFDT, &code); } else { code = jbig2_arith_int_decode(params->IADT, as, &DT); if (code < 0) goto cleanup2; } DT *= params->SBSTRIPS; STRIPT += DT; first_symbol = TRUE; /* 6.4.5 (3c) - decode symbols in strip */ for (;;) { /* (3c.i) */ if (first_symbol) { /* 6.4.7 */ if (params->SBHUFF) { DFS = jbig2_huffman_get(hs, params->SBHUFFFS, &code); } else { code = jbig2_arith_int_decode(params->IAFS, as, &DFS); if (code < 0) goto cleanup2; } FIRSTS += DFS; CURS = FIRSTS; first_symbol = FALSE; } else { /* (3c.ii) / 6.4.8 */ if (params->SBHUFF) { IDS = jbig2_huffman_get(hs, params->SBHUFFDS, &code); } else { code = jbig2_arith_int_decode(params->IADS, as, &IDS); } if (code) { break; } CURS += IDS + params->SBDSOFFSET; } /* (3c.iii) / 6.4.9 */ if (params->SBSTRIPS == 1) { CURT = 0; } else if (params->SBHUFF) { CURT = jbig2_huffman_get_bits(hs, params->LOGSBSTRIPS); } else { code = jbig2_arith_int_decode(params->IAIT, as, &CURT); if (code < 0) goto cleanup2; } T = STRIPT + CURT; /* (3b.iv) / 6.4.10 - decode the symbol id */ if (params->SBHUFF) { ID = jbig2_huffman_get(hs, SBSYMCODES, &code); } else { code = jbig2_arith_iaid_decode(params->IAID, as, (int *)&ID); if (code < 0) goto cleanup2; } if (ID >= SBNUMSYMS) { return jbig2_error(ctx, JBIG2_SEVERITY_FATAL, segment->number, "symbol id out of range! (%d/%d)", ID, SBNUMSYMS); } /* (3c.v) / 6.4.11 - look up the symbol bitmap IB */ { uint32_t id = ID; index = 0; while (id >= dicts[index]->n_symbols) id -= dicts[index++]->n_symbols; IB = jbig2_image_clone(ctx, dicts[index]->glyphs[id]); } if (params->SBREFINE) { if (params->SBHUFF) { RI = jbig2_huffman_get_bits(hs, 1); } else { code = jbig2_arith_int_decode(params->IARI, as, &RI); if (code < 0) goto cleanup2; } } else { RI = 0; } if (RI) { Jbig2RefinementRegionParams rparams; Jbig2Image *IBO; int32_t RDW, RDH, RDX, RDY; Jbig2Image *refimage; int BMSIZE = 0; int code1 = 0; int code2 = 0; int code3 = 0; int code4 = 0; int code5 = 0; /* 6.4.11 (1, 2, 3, 4) */ if (!params->SBHUFF) { code1 = jbig2_arith_int_decode(params->IARDW, as, &RDW); code2 = jbig2_arith_int_decode(params->IARDH, as, &RDH); code3 = jbig2_arith_int_decode(params->IARDX, as, &RDX); code4 = jbig2_arith_int_decode(params->IARDY, as, &RDY); } else { RDW = jbig2_huffman_get(hs, params->SBHUFFRDW, &code1); RDH = jbig2_huffman_get(hs, params->SBHUFFRDH, &code2); RDX = jbig2_huffman_get(hs, params->SBHUFFRDX, &code3); RDY = jbig2_huffman_get(hs, params->SBHUFFRDY, &code4); BMSIZE = jbig2_huffman_get(hs, params->SBHUFFRSIZE, &code5); jbig2_huffman_skip(hs); } if ((code1 < 0) || (code2 < 0) || (code3 < 0) || (code4 < 0) || (code5 < 0)) { code = jbig2_error(ctx, JBIG2_SEVERITY_FATAL, segment->number, "failed to decode data"); goto cleanup2; } /* 6.4.11 (6) */ IBO = IB; refimage = jbig2_image_new(ctx, IBO->width + RDW, IBO->height + RDH); if (refimage == NULL) { jbig2_image_release(ctx, IBO); if (params->SBHUFF) { jbig2_release_huffman_table(ctx, SBSYMCODES); } return jbig2_error(ctx, JBIG2_SEVERITY_FATAL, segment->number, "couldn't allocate reference image"); } /* Table 12 */ rparams.GRTEMPLATE = params->SBRTEMPLATE; rparams.reference = IBO; rparams.DX = (RDW >> 1) + RDX; rparams.DY = (RDH >> 1) + RDY; rparams.TPGRON = 0; memcpy(rparams.grat, params->sbrat, 4); jbig2_decode_refinement_region(ctx, segment, &rparams, as, refimage, GR_stats); IB = refimage; jbig2_image_release(ctx, IBO); /* 6.4.11 (7) */ if (params->SBHUFF) { jbig2_huffman_advance(hs, BMSIZE); } } /* (3c.vi) */ if ((!params->TRANSPOSED) && (params->REFCORNER > 1)) { CURS += IB->width - 1; } else if ((params->TRANSPOSED) && !(params->REFCORNER & 1)) { CURS += IB->height - 1; } /* (3c.vii) */ S = CURS; /* (3c.viii) */ if (!params->TRANSPOSED) { switch (params->REFCORNER) { case JBIG2_CORNER_TOPLEFT: x = S; y = T; break; case JBIG2_CORNER_TOPRIGHT: x = S - IB->width + 1; y = T; break; case JBIG2_CORNER_BOTTOMLEFT: x = S; y = T - IB->height + 1; break; case JBIG2_CORNER_BOTTOMRIGHT: x = S - IB->width + 1; y = T - IB->height + 1; break; } } else { /* TRANSPOSED */ switch (params->REFCORNER) { case JBIG2_CORNER_TOPLEFT: x = T; y = S; break; case JBIG2_CORNER_TOPRIGHT: x = T - IB->width + 1; y = S; break; case JBIG2_CORNER_BOTTOMLEFT: x = T; y = S - IB->height + 1; break; case JBIG2_CORNER_BOTTOMRIGHT: x = T - IB->width + 1; y = S - IB->height + 1; break; } } /* (3c.ix) */ #ifdef JBIG2_DEBUG jbig2_error(ctx, JBIG2_SEVERITY_DEBUG, segment->number, "composing glyph id %d: %dx%d @ (%d,%d) symbol %d/%d", ID, IB->width, IB->height, x, y, NINSTANCES + 1, params->SBNUMINSTANCES); #endif jbig2_image_compose(ctx, image, IB, x, y, params->SBCOMBOP); /* (3c.x) */ if ((!params->TRANSPOSED) && (params->REFCORNER < 2)) { CURS += IB->width -1 ; } else if ((params->TRANSPOSED) && (params->REFCORNER & 1)) { CURS += IB->height - 1; } /* (3c.xi) */ NINSTANCES++; jbig2_image_release(ctx, IB); } /* end strip */ } /* 6.4.5 (4) */ cleanup2: if (params->SBHUFF) { jbig2_release_huffman_table(ctx, SBSYMCODES); } jbig2_huffman_free(ctx, hs); return code; }