/** * 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_parse_text_region: read a text region segment header **/ int jbig2_parse_text_region(Jbig2Ctx *ctx, Jbig2Segment *segment, const byte *segment_data) { int offset = 0; Jbig2RegionSegmentInfo region_info; Jbig2TextRegionParams params; Jbig2Image *image; Jbig2SymbolDict **dicts; int n_dicts; uint16_t flags; uint16_t huffman_flags = 0; Jbig2ArithCx *GR_stats = NULL; int code = 0; Jbig2WordStream *ws = NULL; Jbig2ArithState *as = 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_int16(segment_data + offset); offset += 2; jbig2_error(ctx, JBIG2_SEVERITY_DEBUG, segment->number, "text region header flags 0x%04x", flags); params.SBHUFF = flags & 0x0001; params.SBREFINE = flags & 0x0002; params.LOGSBSTRIPS = (flags & 0x000c) >> 2; params.SBSTRIPS = 1 << params.LOGSBSTRIPS; params.REFCORNER = (flags & 0x0030) >> 4; params.TRANSPOSED = flags & 0x0040; params.SBCOMBOP = (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_int16(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_int32(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 */ /* We handle this case later by leaving the table as NULL */ return jbig2_error(ctx, JBIG2_SEVERITY_FATAL, segment->number, "text region uses custom FS huffman table (NYI)"); break; case 2: /* invalid */ default: return jbig2_error(ctx, JBIG2_SEVERITY_FATAL, segment->number, "text region specified invalid FS huffman table"); break; } 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 */ /* We handle this case later by leaving the table as NULL */ return jbig2_error(ctx, JBIG2_SEVERITY_FATAL, segment->number, "text region uses custom DS huffman table (NYI)"); break; } 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 */ /* We handle this case later by leaving the table as NULL */ return jbig2_error(ctx, JBIG2_SEVERITY_FATAL, segment->number, "text region uses custom DT huffman table (NYI)"); break; } 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 */ /* We handle this case later by leaving the table as NULL */ return jbig2_error(ctx, JBIG2_SEVERITY_FATAL, segment->number, "text region uses custom RDW huffman table (NYI)"); break; case 2: /* invalid */ default: return jbig2_error(ctx, JBIG2_SEVERITY_FATAL, segment->number, "text region specified invalid RDW huffman table"); break; } 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 */ /* We handle this case later by leaving the table as NULL */ return jbig2_error(ctx, JBIG2_SEVERITY_FATAL, segment->number, "text region uses custom RDH huffman table (NYI)"); break; case 2: /* invalid */ default: return jbig2_error(ctx, JBIG2_SEVERITY_FATAL, segment->number, "text region specified invalid RDH huffman table"); break; } 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 */ /* We handle this case later by leaving the table as NULL */ return jbig2_error(ctx, JBIG2_SEVERITY_FATAL, segment->number, "text region uses custom RDX huffman table (NYI)"); break; case 2: /* invalid */ default: return jbig2_error(ctx, JBIG2_SEVERITY_FATAL, segment->number, "text region specified invalid RDX huffman table"); break; } 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 */ /* We handle this case later by leaving the table as NULL */ return jbig2_error(ctx, JBIG2_SEVERITY_FATAL, segment->number, "text region uses custom RDY huffman table (NYI)"); break; case 2: /* invalid */ default: return jbig2_error(ctx, JBIG2_SEVERITY_FATAL, segment->number, "text region specified invalid RDY huffman table"); break; } 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 */ /* We handle this case later by leaving the table as NULL */ return jbig2_error(ctx, JBIG2_SEVERITY_FATAL, segment->number, "text region uses custom RSIZE huffman table (NYI)"); break; } 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 { return jbig2_error(ctx, JBIG2_SEVERITY_FATAL, segment->number, "text region refers to no symbol dictionaries!"); } if (dicts == NULL) { return jbig2_error(ctx, JBIG2_SEVERITY_FATAL, segment->number, "unable to retrive symbol dictionaries!" " previous parsing error?"); } else { int index; if (dicts[0] == NULL) { return jbig2_error(ctx, JBIG2_SEVERITY_WARNING, segment->number, "unable to find first referenced symbol dictionary!"); } 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_alloc(ctx->allocator, stats_size); memset(GR_stats, 0, stats_size); } image = jbig2_image_new(ctx, region_info.width, region_info.height); ws = jbig2_word_stream_buf_new(ctx, segment_data + offset, segment->data_length - offset); 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); ws = 0; 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); /* 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); } 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, ws); if (!params.SBHUFF && params.SBREFINE) { jbig2_free(ctx->allocator, GR_stats); } 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); } else { 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_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); jbig2_free(ctx->allocator, as); jbig2_word_stream_buf_free(ctx, ws); } jbig2_free(ctx->allocator, dicts); /* todo: check errors */ 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); } /* success */ return 0; too_short: return jbig2_error(ctx, JBIG2_SEVERITY_FATAL, segment->number, "Segment too short"); }