shithub: jbig2

ref: 0a9304dad738268b27556717bf83936c15618506
dir: /jbig2.c/

View raw version
#include <stdint.h>
#include <stdlib.h>
#include <stdarg.h>
#include <stdio.h>
#include "jbig2.h"
#include "jbig2_priv.h"

static void *
jbig2_default_alloc (Jbig2Allocator *allocator, size_t size)
{
  return malloc (size);
}

static void
jbig2_default_free (Jbig2Allocator *allocator, void *p)
{
  free (p);
}

static void *
jbig2_default_realloc (Jbig2Allocator *allocator, void *p, size_t size)
{
  return realloc (p, size);
}

static Jbig2Allocator jbig2_default_allocator =
{
  jbig2_default_alloc,
  jbig2_default_free,
  jbig2_default_realloc
};

void *
jbig2_alloc (Jbig2Allocator *allocator, size_t size)
{
  return allocator->alloc (allocator, size);
}

void
jbig2_free (Jbig2Allocator *allocator, void *p)
{
  allocator->free (allocator, p);
}

void *
jbig2_realloc (Jbig2Allocator *allocator, void *p, size_t size)
{
  return allocator->realloc (allocator, p, size);
}

int
jbig2_error (Jbig2Ctx *ctx, Jbig2Severity severity, int seg_idx,
	     const char *fmt, ...)
{
  char buf[1024];
  va_list ap;
  int n;

  va_start (ap, fmt);
  n = vsnprintf (buf, sizeof(buf), fmt, ap);
  va_end (ap);
  if (n < 0 || n == sizeof(buf))
    strcpy (buf, "jbig2_error: error in generating error string");
  return ctx->error_callback (ctx->error_callback_data, buf, severity, seg_idx);
}

Jbig2Ctx *
jbig2_ctx_new (Jbig2Allocator *allocator,
	       Jbig2Options options,
	       Jbig2GlobalCtx *global_ctx,
	       Jbig2ErrorCallback error_callback,
	       void *error_callback_data)
{
  Jbig2Ctx *result;

  if (allocator == NULL)
      allocator = &jbig2_default_allocator;

  result = (Jbig2Ctx *)jbig2_alloc (allocator, sizeof(Jbig2Ctx));
  result->allocator = allocator;
  result->options = options;
  result->global_ctx = (const Jbig2Ctx *)global_ctx;
  result->error_callback = error_callback;
  result->error_callback_data = error_callback_data;

  result->state = (options & JBIG2_OPTIONS_EMBEDDED) ?
    JBIG2_FILE_SEQUENTIAL_HEADER :
    JBIG2_FILE_HEADER;

  result->buf = NULL;
  result->sh_list = NULL;
  result->n_sh = 0;
  result->n_sh_max = 1;
  result->sh_ix = 0;
  return result;
}

static int32_t
jbig2_get_int32 (uint8_t *buf)
{
  return (buf[0] << 24) | (buf[1] << 16) | (buf[2] << 8) | buf[3];
}

static Jbig2SegmentHeader *
jbig2_parse_segment_header (Jbig2Ctx *ctx, uint8_t *buf, size_t buf_size,
			    size_t *p_header_size)
{
  Jbig2SegmentHeader *result;
  byte	rtscarf;
  int32_t rtscarf_long;
  int referred_to_segment_count;
  int referred_to_segment_size;
  int pa_size;
  int offset;

  /* minimum possible size of a jbig2 segment header */
  if (buf_size < 11)
    return NULL;

  result = (Jbig2SegmentHeader *)jbig2_alloc(ctx->allocator,
					     sizeof(Jbig2SegmentHeader));

  /* 7.2.2 */
  result->segment_number = jbig2_get_int32 (buf);

  /* 7.2.3 */
  result->flags = buf[4];

  /* 7.2.4 */
  rtscarf = buf[5];
  if ((rtscarf & 0xe0) == 0xe0)
    {
      rtscarf_long = jbig2_get_int32(buf + 5);
      referred_to_segment_count = rtscarf_long & 0x1fffffff;
      offset = 5 + 4 + (referred_to_segment_count + 1) / 8;
    }
  else
    {
      referred_to_segment_count = (rtscarf >> 5);
      offset = 5 + 1;
    }
  result->referred_to_segment_count = referred_to_segment_count;

  /* 7.2.5 */
  /* todo: read referred to segment numbers */
  /* For now, we skip them. */
  referred_to_segment_size = result->segment_number <= 256 ? 1:
    result->segment_number <= 65536 ? 2:
    4;
  offset += referred_to_segment_count * referred_to_segment_size;

  /* 7.2.6 */
  pa_size = result->flags & 0x40 ? 4 : 1;

  if (offset + pa_size + 4 > buf_size)
    {
      jbig2_free (ctx->allocator, result);
      return NULL;
    }

  if (result->flags & 0x40) {
	result->page_association = jbig2_get_int32(buf + offset);
	offset += 4;
  } else {
	result->page_association = buf[offset++];
  }
  
  /* 7.2.7 */
  result->data_length = jbig2_get_int32 (buf + offset);
  *p_header_size = offset + 4;

  return result;
}

void
jbig2_free_segment_header (Jbig2Ctx *ctx, Jbig2SegmentHeader *sh)
{
  jbig2_free (ctx->allocator, sh);
}

int
jbig2_write (Jbig2Ctx *ctx, const unsigned char *data, size_t size)
{
  const int initial_buf_size = 1024;

  if (ctx->buf == NULL)
    {
      int buf_size = initial_buf_size;

      do
	buf_size <<= 1;
      while (buf_size < size);
      ctx->buf = (byte *)jbig2_alloc (ctx->allocator, size);
      ctx->buf_size = buf_size;
      ctx->buf_rd_ix = 0;
      ctx->buf_wr_ix = 0;
    }
  else if (ctx->buf_wr_ix + size > ctx->buf_size)
    {
      if (ctx->buf_rd_ix <= (ctx->buf_size >> 1) &&
	  ctx->buf_wr_ix - ctx->buf_rd_ix + size <= ctx->buf_size)
	{
	  memcpy (ctx->buf, ctx->buf + ctx->buf_rd_ix,
		  ctx->buf_wr_ix - ctx->buf_rd_ix);
	}
      else
	{
	  byte *buf;
	  int buf_size = initial_buf_size;
	  
	  do
	    buf_size <<= 1;
	  while (buf_size < ctx->buf_wr_ix - ctx->buf_rd_ix + size);
	  buf = (byte *)jbig2_alloc (ctx->allocator, buf_size);
	  memcpy (buf, ctx->buf + ctx->buf_rd_ix,
		  ctx->buf_wr_ix - ctx->buf_rd_ix);
	  jbig2_free (ctx->allocator, ctx->buf);
	  ctx->buf = buf;
	  ctx->buf_size = buf_size;
	}
      ctx->buf_wr_ix -= ctx->buf_rd_ix;
      ctx->buf_rd_ix = 0;
    }
  memcpy (ctx->buf + ctx->buf_wr_ix, data, size);
  ctx->buf_wr_ix += size;

  /* data has now been added to buffer */

  for (;;)
    {
      const byte jbig2_id_string[8] = { 0x97, 0x4a, 0x42, 0x32, 0x0d, 0x0a, 0x1a, 0x0a };
      Jbig2SegmentHeader *sh;
      size_t header_size;
      int code;

      switch (ctx->state)
	{
	case JBIG2_FILE_HEADER:
	  if (ctx->buf_wr_ix - ctx->buf_rd_ix < 9)
	    return 0;
	  if (memcmp(ctx->buf + ctx->buf_rd_ix, jbig2_id_string, 8))
	    {
	      jbig2_error(ctx, JBIG2_SEVERITY_FATAL, -1,
			  "Not a JBIG2 file header");
	      return -1;
	    }
	  ctx->file_header_flags = ctx->buf[ctx->buf_rd_ix + 8];
	  if (!(ctx->file_header_flags & 2))
	    {
	      if (ctx->buf_wr_ix - ctx->buf_rd_ix < 13)
		return 0;
	      ctx->n_pages = jbig2_get_int32(ctx->buf + ctx->buf_rd_ix + 9);
	      ctx->buf_rd_ix += 13;
	    }
	  else
	    ctx->buf_rd_ix += 9;
	  if (ctx->file_header_flags & 1)
	    {
	      ctx->state = JBIG2_FILE_SEQUENTIAL_HEADER;
	    }
	  else
	    {
	      ctx->state = JBIG2_FILE_RANDOM_HEADERS;
	      ctx->n_sh_max = 16;
	    }
	  break;
	case JBIG2_FILE_SEQUENTIAL_HEADER:
	case JBIG2_FILE_RANDOM_HEADERS:
	  sh = jbig2_parse_segment_header(ctx, ctx->buf + ctx->buf_rd_ix,
					  ctx->buf_wr_ix - ctx->buf_rd_ix,
					  &header_size);
	  if (sh == NULL)
	    return 0;
	  ctx->buf_rd_ix += header_size;

	  if (ctx->sh_list == NULL)
	      ctx->sh_list = jbig2_alloc(ctx->allocator, ctx->n_sh_max *
				     sizeof(Jbig2SegmentHeader *));
	  else if (ctx->n_sh == ctx->n_sh_max)
	    /* Note to rillian: I usually define a macro to make this
	       less ungainly. */
	    ctx->sh_list = (Jbig2SegmentHeader **)jbig2_realloc(ctx->allocator,
								ctx->sh_list,
								(ctx->n_sh_max <<= 2) * sizeof(Jbig2SegmentHeader *));

	  ctx->sh_list[ctx->n_sh++] = sh;
	  if (ctx->state == JBIG2_FILE_RANDOM_HEADERS)
	    {
	      if ((sh->flags & 63) == 51) /* end of file */
		ctx->state = JBIG2_FILE_RANDOM_BODIES;
	    }
	  else
	    ctx->state = JBIG2_FILE_SEQUENTIAL_BODY;
	  break;
	case JBIG2_FILE_SEQUENTIAL_BODY:
	case JBIG2_FILE_RANDOM_BODIES:
	  sh = ctx->sh_list[ctx->sh_ix];
	  if (sh->data_length > ctx->buf_wr_ix - ctx->buf_rd_ix)
	    return 0;
	  code = jbig2_write_segment(ctx, sh, ctx->buf + ctx->buf_rd_ix);
	  ctx->buf_rd_ix += sh->data_length;
	  jbig2_free_segment_header(ctx, sh);
	  ctx->sh_list[ctx->sh_ix] = NULL;
	  if (ctx->state == JBIG2_FILE_RANDOM_BODIES)
	    {
	      ctx->sh_ix++;
	      if (ctx->sh_ix == ctx->n_sh)
		ctx->state = JBIG2_FILE_EOF;
	    }
	  else
	    {
	      ctx->n_sh = 0;
	      ctx->state = JBIG2_FILE_SEQUENTIAL_HEADER;
	    }
	  if (code < 0)
	    {
	      ctx->state = JBIG2_FILE_EOF;
	      return code;
	    }
	  break;
	case JBIG2_FILE_EOF:
	  if (ctx->buf_rd_ix == ctx->buf_wr_ix)
	    return 0;
	  jbig2_error(ctx, JBIG2_SEVERITY_WARNING, -1,
		      "Garbage beyond end of file");
	  return -1;
	}
    }
  return 0;
}

/* get_bits */

void
jbig2_ctx_free (Jbig2Ctx *ctx)
{
  Jbig2Allocator *ca = ctx->allocator;
  int i;

  jbig2_free(ca, ctx->buf);
  if (ctx->sh_list != NULL)
    {
      for (i = ctx->sh_ix; i < ctx->n_sh; i++)
	jbig2_free_segment_header(ctx, ctx->sh_list[i]);
      jbig2_free(ca, ctx->sh_list);
    }
  jbig2_free(ca, ctx);
}

Jbig2GlobalCtx *jbig2_make_global_ctx (Jbig2Ctx *ctx)
{
  return (Jbig2GlobalCtx *)ctx;
}

void jbig2_global_ctx_free(Jbig2GlobalCtx *global_ctx)
{
  jbig2_ctx_free((Jbig2Ctx *)global_ctx);
}

int jbig2_write_segment (Jbig2Ctx *ctx, Jbig2SegmentHeader *sh,
			 const uint8_t *segment_data)
{
  jbig2_error(ctx, JBIG2_SEVERITY_INFO, sh->segment_number,
	      "Segment %d, flags=%x, type=%d, data_length=%d",
	      sh->segment_number, sh->flags, sh->flags & 63,
	      sh->data_length);
  return 0;
}