Ejemplo n.º 1
0
/**
 * 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;
}
Ejemplo n.º 2
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;
}
Ejemplo n.º 3
0
/**
 * 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;
}
Ejemplo n.º 4
0
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;
}
Ejemplo n.º 5
0
/**
 * 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;
}
Ejemplo n.º 6
0
/**
 * 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
    }
}
Ejemplo n.º 8
0
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;
}
Ejemplo n.º 9
0
/**
 * 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;
}
Ejemplo n.º 10
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);
    }
}
Ejemplo n.º 11
0
/* 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 {
Ejemplo n.º 12
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) {
            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;
}
Ejemplo n.º 13
0
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;
    }
Ejemplo n.º 14
0
/**
 * 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;
}
Ejemplo n.º 15
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;
}
Ejemplo n.º 16
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(&region_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(&params, 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, &params,
        (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");
}
Ejemplo n.º 17
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;
}