/** * 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_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; }