shithub: jbig2

Download patch

ref: 625abbded569d5ffde7301d497158b0116313f21
parent: 107138382e590d3851ba1b5e897a33f2cf27ee4b
author: raph <raph@ded80894-8fb9-0310-811b-c03f3676ab4d>
date: Sat Feb 16 02:25:36 EST 2002

Lots of changes: Added template 0 generic decoder. Added "results"
mechanism to Jbig2Ctx for storing the decoded results, but not yet
any segments which store their results there. Changed generic region
API slightly so callers could pass in more context (needed for
generic region decoding other than generic). Added arithmetic integer
decoder. Started symbol dictionary decoder. Currently, this decodes
about 200 symbols from the cvision global dict, then goes haywire.


git-svn-id: http://svn.ghostscript.com/jbig2dec/trunk@43 ded80894-8fb9-0310-811b-c03f3676ab4d

--- a/jbig2.c
+++ b/jbig2.c
@@ -4,7 +4,9 @@
 #include <stdio.h>
 #include "jbig2.h"
 #include "jbig2_priv.h"
+#include "jbig2_arith.h"
 #include "jbig2_generic.h"
+#include "jbig2_symbol_dict.h"
 
 static void *
 jbig2_default_alloc (Jbig2Allocator *allocator, size_t size)
@@ -56,6 +58,7 @@
   char buf[1024];
   va_list ap;
   int n;
+  int code;
 
   va_start (ap, fmt);
   n = vsnprintf (buf, sizeof(buf), fmt, ap);
@@ -62,7 +65,10 @@
   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);
+  code = ctx->error_callback (ctx->error_callback_data, buf, severity, seg_idx);
+  if (severity == JBIG2_SEVERITY_FATAL)
+    code = -1;
+  return code;
 }
 
 Jbig2Ctx *
@@ -77,7 +83,7 @@
   if (allocator == NULL)
       allocator = &jbig2_default_allocator;
 
-  result = (Jbig2Ctx *)jbig2_alloc (allocator, sizeof(Jbig2Ctx));
+  result = (Jbig2Ctx *)jbig2_alloc(allocator, sizeof(Jbig2Ctx));
   result->allocator = allocator;
   result->options = options;
   result->global_ctx = (const Jbig2Ctx *)global_ctx;
@@ -93,6 +99,11 @@
   result->n_sh = 0;
   result->n_sh_max = 1;
   result->sh_ix = 0;
+
+  result->n_results = 0;
+  result->n_results_max = 16;
+  result->results = (const Jbig2Result **)jbig2_alloc(allocator, result->n_results_max * sizeof(Jbig2Result *));
+
   return result;
 }
 
@@ -102,6 +113,12 @@
   return (buf[0] << 24) | (buf[1] << 16) | (buf[2] << 8) | buf[3];
 }
 
+int16_t
+jbig2_get_int16 (const byte *buf)
+{
+  return (buf[0] << 8) | buf[1];
+}
+
 static Jbig2SegmentHeader *
 jbig2_parse_segment_header (Jbig2Ctx *ctx, uint8_t *buf, size_t buf_size,
 			    size_t *p_header_size)
@@ -240,11 +257,8 @@
 	  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;
-	    }
+	    return jbig2_error(ctx, JBIG2_SEVERITY_FATAL, -1,
+			       "Not a JBIG2 file header");
 	  ctx->file_header_flags = ctx->buf[ctx->buf_rd_ix + 8];
 	  if (!(ctx->file_header_flags & 2))
 	    {
@@ -322,9 +336,8 @@
 	case JBIG2_FILE_EOF:
 	  if (ctx->buf_rd_ix == ctx->buf_wr_ix)
 	    return 0;
-	  jbig2_error(ctx, JBIG2_SEVERITY_WARNING, -1,
+	  return jbig2_error(ctx, JBIG2_SEVERITY_WARNING, -1,
 		      "Garbage beyond end of file");
-	  return -1;
 	}
     }
   return 0;
@@ -337,6 +350,7 @@
 {
   Jbig2Allocator *ca = ctx->allocator;
   int i;
+  int32_t seg_ix;
 
   jbig2_free(ca, ctx->buf);
   if (ctx->sh_list != NULL)
@@ -345,6 +359,16 @@
 	jbig2_free_segment_header(ctx, ctx->sh_list[i]);
       jbig2_free(ca, ctx->sh_list);
     }
+
+  for (seg_ix = 0; seg_ix < ctx->n_results; seg_ix++)
+    {
+      const Jbig2Result *result = ctx->results[seg_ix];
+
+      if (result)
+	result->free(result, ctx);
+    }
+  jbig2_free(ca, ctx->results);
+
   jbig2_free(ca, ctx);
 }
 
@@ -367,8 +391,101 @@
 	      sh->data_length);
   switch (sh->flags & 63)
     {
+    case 0:
+      return jbig2_symbol_dictionary(ctx, sh, segment_data);
     case 38:
       return jbig2_immediate_generic_region(ctx, sh, segment_data);
     }
   return 0;
 }
+
+const Jbig2Result *
+jbig2_get_result(Jbig2Ctx *ctx, int32_t segment_number)
+{
+  if (segment_number < 0 || segment_number >= ctx->n_results)
+    {
+      jbig2_error(ctx, JBIG2_SEVERITY_WARNING, segment_number,
+		  "Attempting to get invalid segment number");
+      return NULL;
+    }
+  return ctx->results[segment_number];
+}
+
+int
+jbig2_put_result(Jbig2Ctx *ctx, const Jbig2Result *result)
+{
+  int32_t segment_number = result->segment_number;
+  int32_t i;
+
+  if (ctx->n_results_max <= segment_number)
+    {
+      const Jbig2Result **new_results;
+      do
+	ctx->n_results_max <<= 1;
+      while (ctx->n_results_max <= segment_number);
+      new_results = (const Jbig2Result **)jbig2_realloc(ctx->allocator,
+							  ctx->results,
+							  ctx->n_results_max * sizeof(Jbig2Result *));
+      if (new_results == NULL)
+	return jbig2_error(ctx, JBIG2_SEVERITY_FATAL, segment_number,
+			   "Allocation failure");
+      ctx->results = new_results;
+    }
+  for (i = ctx->n_results; i < segment_number; i++)
+    ctx->results[i] = NULL;
+  ctx->results[segment_number] = result;
+  if (ctx->n_results < segment_number + 1)
+    ctx->n_results = segment_number + 1;
+  return 0;
+}
+
+typedef struct {
+  Jbig2WordStream super;
+  const byte *data;
+  size_t size;
+} Jbig2WordStreamBuf;
+
+/* I'm not committed to keeping the word stream interface. It's handy
+   when you think you may be streaming your input, but if you're not
+   (as is currently the case), it just adds complexity.
+*/
+
+static uint32_t
+jbig2_word_stream_buf_get_next_word(Jbig2WordStream *self, int offset)
+{
+  Jbig2WordStreamBuf *z = (Jbig2WordStreamBuf *)self;
+  const byte *data = z->data;
+  uint32_t result;
+
+  if (offset + 4 < z->size)
+    result = (data[offset] << 24) | (data[offset + 1] << 16) |
+      (data[offset + 2] << 8) | data[offset + 3];
+  else
+    {
+      int i;
+
+      result = 0;
+      for (i = 0; i < z->size - offset; i++)
+	result |= data[offset + i] << ((3 - i) << 3);
+    }
+  return result;
+}
+
+Jbig2WordStream *
+jbig2_word_stream_buf_new(Jbig2Ctx *ctx, const byte *data, size_t size)
+{
+  Jbig2WordStreamBuf *result = (Jbig2WordStreamBuf *)jbig2_alloc(ctx->allocator, sizeof(Jbig2WordStreamBuf));
+
+  result->super.get_next_word = jbig2_word_stream_buf_get_next_word;
+  result->data = data;
+  result->size = size;
+
+  return &result->super;
+}
+
+void
+jbig2_word_stream_buf_free(Jbig2Ctx *ctx, Jbig2WordStream *ws)
+{
+  jbig2_free(ctx->allocator, ws);
+}
+
--- a/jbig2_arith.c
+++ b/jbig2_arith.c
@@ -8,13 +8,15 @@
     the Free Software Foundation; either version 2 of the License, or
     (at your option) any later version.
 
-    $Id: jbig2_arith.c,v 1.6 2002/02/15 20:46:30 raph Exp $
+    $Id: jbig2_arith.c,v 1.7 2002/02/16 07:25:36 raph Exp $
 */
 
 #include <stdio.h>
 #include <stdlib.h>
+#include <stdint.h>
 
-#include "jbig2dec.h"
+#include "jbig2.h"
+#include "jbig2_priv.h"
 #include "jbig2_arith.h"
 
 #ifdef DEBUG
@@ -22,12 +24,12 @@
 #endif
 
 struct _Jbig2ArithState {
-  uint32 C;
+  uint32_t C;
   int A;
 
   int CT;
 
-  uint32 next_word;
+  uint32_t next_word;
   int next_word_bytes;
 
   Jbig2WordStream *ws;
@@ -170,7 +172,7 @@
 #endif
 
 Jbig2ArithState *
-jbig2_arith_new (Jbig2WordStream *ws)
+jbig2_arith_new (Jbig2Ctx *ctx, Jbig2WordStream *ws)
 {
   Jbig2ArithState *result = (Jbig2ArithState *)malloc (sizeof(Jbig2ArithState));
 
@@ -334,7 +336,7 @@
 
 #ifdef TEST
 
-static int32
+static int32_t
 test_get_word (Jbig2WordStream *self, int offset)
 {
   byte stream[] = {
@@ -359,7 +361,7 @@
   Jbig2ArithCx cx = 0;
 
   ws.get_next_word = test_get_word;
-  as = jbig2_arith_new (&ws);
+  as = jbig2_arith_new (NULL, &ws);
   jbig2_arith_trace (as, cx);
 
   for (i = 0; i < 256; i++)
--- a/jbig2_arith.h
+++ b/jbig2_arith.h
@@ -8,7 +8,7 @@
     the Free Software Foundation; either version 2 of the License, or
     (at your option) any later version.
 
-    $Id: jbig2_arith.h,v 1.3 2001/06/10 07:09:18 giles Exp $
+    $Id: jbig2_arith.h,v 1.4 2002/02/16 07:25:36 raph Exp $
 */
 
 typedef struct _Jbig2ArithState Jbig2ArithState;
@@ -19,7 +19,7 @@
 typedef unsigned char Jbig2ArithCx;
 
 Jbig2ArithState *
-jbig2_arith_new (Jbig2WordStream *ws);
+jbig2_arith_new (Jbig2Ctx *ctx, Jbig2WordStream *ws);
 
 bool
 jbig2_arith_decode (Jbig2ArithState *as, Jbig2ArithCx *pcx);
--- /dev/null
+++ b/jbig2_arith_int.c
@@ -1,0 +1,103 @@
+/* Annex A */
+
+#include <stddef.h>
+#include <stdint.h>
+#include "jbig2.h"
+#include "jbig2_priv.h"
+#include "jbig2_arith.h"
+#include "jbig2_arith_int.h"
+
+struct _Jbig2ArithIntCtx {
+  Jbig2ArithCx IAx[512];
+};
+
+Jbig2ArithIntCtx *
+jbig2_arith_int_ctx_new(Jbig2Ctx *ctx)
+{
+  Jbig2ArithIntCtx *result = jbig2_new(ctx, Jbig2ArithIntCtx, 1);
+
+  memset(result->IAx, 0, sizeof(result->IAx));
+
+  return result;
+}
+
+/* A.2 */
+/* Return value: -1 on error, 0 on normal value, 1 on OOB return. */
+int
+jbig2_arith_int_decode(Jbig2ArithIntCtx *ctx, Jbig2ArithState *as,
+		       int32_t *p_result)
+{
+  Jbig2ArithCx *IAx = ctx->IAx;
+  int PREV = 1;
+  int S, V;
+  int bit;
+  int n_tail, offset;
+  int i;
+
+  S = jbig2_arith_decode(as, &IAx[PREV]);
+  PREV = (PREV << 1) | S;
+
+  bit = jbig2_arith_decode(as, &IAx[PREV]);
+  PREV = (PREV << 1) | bit;
+  if (bit)
+    {
+      bit = jbig2_arith_decode(as, &IAx[PREV]);
+      PREV = (PREV << 1) | bit;
+
+      if (bit)
+	{
+	  bit = jbig2_arith_decode(as, &IAx[PREV]);
+	  PREV = (PREV << 1) | bit;
+	  if (bit)
+	    {
+	      bit = jbig2_arith_decode(as, &IAx[PREV]);
+	      PREV = (PREV << 1) | bit;
+	      
+	      if (bit)
+		{
+		  n_tail = 32;
+		  offset = 4436;
+		}
+	      else
+		{
+		  n_tail = 12;
+		  offset = 340;
+		}
+	    }
+	  else
+	    {
+	      n_tail = 6;
+	      offset = 20;
+	    }
+	}
+      else
+	{
+	  n_tail = 4;
+	  offset = 4;
+	}
+    }
+  else
+    {
+      n_tail = 2;
+      offset = 0;
+    }
+
+  V = 0;
+  for (i = 0; i < n_tail; i++)
+    {
+      bit = jbig2_arith_decode(as, &IAx[PREV]);
+      PREV = ((PREV << 1) & 511) | (PREV & 256) | bit;
+      V = (V << 1) | bit;
+    }
+
+  V += offset;
+  V = S ? -V : V;
+  *p_result = V;
+  return S && V == 0 ? 1 : 0;
+}
+
+void
+jbig2_arith_int_ctx_free(Jbig2Ctx *ctx, Jbig2ArithIntCtx *iax)
+{
+  jbig2_free(ctx->allocator, iax);
+}
--- /dev/null
+++ b/jbig2_arith_int.h
@@ -1,0 +1,11 @@
+typedef struct _Jbig2ArithIntCtx Jbig2ArithIntCtx;
+
+Jbig2ArithIntCtx *
+jbig2_arith_int_ctx_new(Jbig2Ctx *ctx);
+
+int
+jbig2_arith_int_decode(Jbig2ArithIntCtx *ctx, Jbig2ArithState *as,
+		       int32_t *p_result);
+
+void
+jbig2_arith_int_ctx_free(Jbig2Ctx *ctx, Jbig2ArithIntCtx *iax);
--- a/jbig2_generic.c
+++ b/jbig2_generic.c
@@ -14,19 +14,7 @@
 #include "jbig2_arith.h"
 #include "jbig2_generic.h"
 
-/* Table 2 */
 typedef struct {
-  bool MMR;
-  int32_t GBW;
-  int32_t GBH;
-  int GBTEMPLATE;
-  bool TPGDON;
-  bool USESKIP;
-  /* SKIP */
-  byte gbat[8];
-} Jbig2GenericRegionParams;
-
-typedef struct {
   int32_t width;
   int32_t height;
   int32_t x;
@@ -34,49 +22,72 @@
   byte flags;
 } Jbig2RegionSegmentInfo;
 
-typedef struct {
-  Jbig2WordStream super;
-  const byte *data;
-  size_t size;
-} Jbig2WordStreamBuf;
-
-static uint32_t
-jbig2_word_stream_buf_get_next_word(Jbig2WordStream *self, int offset)
+static int
+jbig2_decode_generic_template0(Jbig2Ctx *ctx,
+			       int32_t seg_number,
+			       const Jbig2GenericRegionParams *params,
+			       Jbig2ArithState *as,
+			       byte *gbreg,
+			       Jbig2ArithCx *GB_stats)
 {
-  Jbig2WordStreamBuf *z = (Jbig2WordStreamBuf *)self;
-  const byte *data = z->data;
-  uint32_t result;
+  int GBW = params->GBW;
+  int rowstride = (GBW + 7) >> 3;
+  int x, y;
+  byte *gbreg_line = gbreg;
+  bool LTP = 0;
 
-  if (offset + 4 < z->size)
-    result = (data[offset] << 24) | (data[offset + 1] << 16) |
-      (data[offset + 2] << 8) | data[offset + 3];
-  else
+  /* todo: currently we only handle the nominal gbat location */
+
+#ifdef OUTPUT_PBM
+  printf("P4\n%d %d\n", GBW, params->GBH);
+#endif
+
+  for (y = 0; y < params->GBH; y++)
     {
-      int i;
+      uint32_t CONTEXT;
+      uint32_t line_m1;
+      uint32_t line_m2;
+      int padded_width = (GBW + 7) & -8;
 
-      result = 0;
-      for (i = 0; i < z->size - offset; i++)
-	result |= data[offset + i] << ((3 - i) << 3);
-    }
-  return result;
-}
+      line_m1 = (y >= 1) ? gbreg_line[-rowstride] : 0;
+      line_m2 = (y >= 2) ? gbreg_line[-(rowstride << 1)] << 6 : 0;
+      CONTEXT = (line_m1 & 0x7f0) | (line_m2 & 0xf800);
 
-Jbig2WordStream *
-jbig2_word_stream_buf_new(Jbig2Ctx *ctx, const byte *data, size_t size)
-{
-  Jbig2WordStreamBuf *result = (Jbig2WordStreamBuf *)jbig2_alloc(ctx->allocator, sizeof(Jbig2WordStreamBuf));
+      /* 6.2.5.7 3d */
+      for (x = 0; x < padded_width; x += 8)
+	{
+	  byte result = 0;
+	  int x_minor;
+	  int minor_width = GBW - x > 8 ? 8 : GBW - x;
 
-  result->super.get_next_word = jbig2_word_stream_buf_get_next_word;
-  result->data = data;
-  result->size = size;
+	  if (y >= 1)
+	    line_m1 = (line_m1 << 8) |
+	      (x + 8 < GBW ? gbreg_line[-rowstride + (x >> 3) + 1] : 0);
 
-  return &result->super;
-}
+	  if (y >= 2)
+	    line_m2 = (line_m2 << 8) |
+	      (x + 8 < GBW ? gbreg_line[-(rowstride << 1) + (x >> 3) + 1] << 6: 0);
 
-void
-jbig2_word_stream_buf_free(Jbig2Ctx *ctx, Jbig2WordStream *ws)
-{
-  jbig2_free(ctx->allocator, ws);
+	  /* This is the speed-critical inner loop. */
+	  for (x_minor = 0; x_minor < minor_width; x_minor++)
+	    {
+	      bool bit;
+
+	      bit = jbig2_arith_decode(as, &GB_stats[CONTEXT]);
+	      result |= bit << (7 - x_minor);
+	      CONTEXT = ((CONTEXT & 0x7bf7) << 1) | bit |
+		((line_m1 >> (7 - x_minor)) & 0x10) |
+		((line_m2 >> (7 - x_minor)) & 0x800);
+	    }
+	  gbreg_line[x >> 3] = result;
+	}
+#ifdef OUTPUT_PBM
+      fwrite(gbreg_line, 1, rowstride, stdout);
+#endif
+      gbreg_line += rowstride;
+    }
+
+  return 0;
 }
 
 static int
@@ -83,13 +94,10 @@
 jbig2_decode_generic_template1(Jbig2Ctx *ctx,
 			       int32_t seg_number,
 			       const Jbig2GenericRegionParams *params,
-			       const byte *data, size_t size,
-			       byte *gbreg)
+			       Jbig2ArithState *as,
+			       byte *gbreg,
+			       Jbig2ArithCx *GB_stats)
 {
-  Jbig2ArithCx GB_stats[8192];
-  Jbig2WordStream *ws = jbig2_word_stream_buf_new(ctx, data, size);
-  /* todo: ctx needs to be an argument; fix up memory allocation */
-  Jbig2ArithState *as = jbig2_arith_new(ws);
   int GBW = params->GBW;
   int rowstride = (GBW + 7) >> 3;
   int x, y;
@@ -96,7 +104,6 @@
   byte *gbreg_line = gbreg;
   bool LTP = 0;
 
-  memset(GB_stats, 0, sizeof(GB_stats));
   /* todo: currently we only handle the nominal gbat location */
 
 #ifdef OUTPUT_PBM
@@ -112,7 +119,7 @@
 
       line_m1 = (y >= 1) ? gbreg_line[-rowstride] : 0;
       line_m2 = (y >= 2) ? gbreg_line[-(rowstride << 1)] << 5 : 0;
-      CONTEXT = ((line_m1 >> 1) & 0x1f8) | ((line_m2 >> 4) & 0x1e00);
+      CONTEXT = ((line_m1 >> 1) & 0x1f8) | ((line_m2 >> 1) & 0x1e00);
 
       /* 6.2.5.7 3d */
       for (x = 0; x < padded_width; x += 8)
@@ -171,12 +178,21 @@
 jbig2_decode_generic_region(Jbig2Ctx *ctx,
 			    int32_t seg_number,
 			    const Jbig2GenericRegionParams *params,
-			    const byte *data, size_t size,
-			    byte *gbreg)
+			    Jbig2ArithState *as,
+			    byte *gbreg,
+			    Jbig2ArithCx *GB_stats)
 {
-  if (!params->MMR && params->GBTEMPLATE == 1)
+  if (!params->MMR && params->GBTEMPLATE == 0)
+    return jbig2_decode_generic_template0(ctx, seg_number,
+					  params, as, gbreg, GB_stats);
+  else if (!params->MMR && params->GBTEMPLATE == 1)
     return jbig2_decode_generic_template1(ctx, seg_number,
-					  params, data, size, gbreg);
+					  params, as, gbreg, GB_stats);
+  {
+    int i;
+    for (i = 0; i < 8; i++)
+      printf ("gbat[%d] = %d\n", i, params->gbat[i]);
+  }
   jbig2_error(ctx, JBIG2_SEVERITY_WARNING, seg_number,
 	      "decode_generic_region: MMR=%d, GBTEMPLATE=%d NYI",
 	      params->MMR, params->GBTEMPLATE);
@@ -207,14 +223,14 @@
   Jbig2GenericRegionParams params;
   int code;
   byte *gbreg;
+  Jbig2WordStream *ws;
+  Jbig2ArithState *as;
+  Jbig2ArithCx *GB_stats = NULL;
 
   /* 7.4.6 */
   if (sh->data_length < 18)
-    {
-      jbig2_error(ctx, JBIG2_SEVERITY_FATAL, sh->segment_number,
-		  "Segment too short");
-      return -1;
-    }
+    return jbig2_error(ctx, JBIG2_SEVERITY_FATAL, sh->segment_number,
+		       "Segment too short");
 
   jbig2_get_region_segment_info(&rsi, segment_data);
   jbig2_error(ctx, JBIG2_SEVERITY_INFO, sh->segment_number,
@@ -235,7 +251,8 @@
     {
       gbat_bytes = (seg_flags & 6) ? 2 : 8;
       if (18 + gbat_bytes > sh->data_length)
-	return -1;
+	return jbig2_error(ctx, JBIG2_SEVERITY_FATAL, sh->segment_number,
+			   "Segment too short");
       memcpy(gbat, segment_data + 18, gbat_bytes);
       jbig2_error(ctx, JBIG2_SEVERITY_INFO, sh->segment_number,
 		  "gbat: %d, %d", gbat[0], gbat[1]);
@@ -254,12 +271,26 @@
 
   gbreg = jbig2_alloc(ctx->allocator, ((rsi.width + 7) >> 3) * rsi.height);
 
+  ws = jbig2_word_stream_buf_new(ctx,
+				 segment_data + offset,
+				 sh->data_length - offset);
+  as = jbig2_arith_new(ctx, ws);
+
+  if (!params.MMR)
+    {
+      int stats_size = params.GBTEMPLATE == 0 ? 65536 :
+	params.GBTEMPLATE == 1 ? 8192 : 1024;
+      GB_stats = jbig2_alloc(ctx->allocator, stats_size);
+      memset(GB_stats, 0, stats_size);
+    }
+
   code = jbig2_decode_generic_region(ctx, sh->segment_number, &params,
-				     segment_data + offset,
-				     sh->data_length - offset,
-				     gbreg);
+				     as, gbreg, GB_stats);
 
   /* todo: stash gbreg as segment result */
+  /* todo: free ws, as */
+
+  jbig2_free(ctx->allocator, GB_stats);
 
   return code;
 }
--- a/jbig2_generic.h
+++ b/jbig2_generic.h
@@ -1,3 +1,25 @@
+/* Table 2 */
+typedef struct {
+  bool MMR;
+  int32_t GBW;
+  int32_t GBH;
+  int GBTEMPLATE;
+  bool TPGDON;
+  bool USESKIP;
+  /* SKIP */
+  byte gbat[8];
+} Jbig2GenericRegionParams;
+
+/* 6.2 */
+int
+jbig2_decode_generic_region(Jbig2Ctx *ctx,
+			    int32_t seg_number,
+			    const Jbig2GenericRegionParams *params,
+			    Jbig2ArithState *as,
+			    byte *gbreg,
+			    Jbig2ArithCx *GB_stats);
+
+/* 7.4 */
 int
 jbig2_immediate_generic_region(Jbig2Ctx *ctx, Jbig2SegmentHeader *sh,
 			       const uint8_t *segment_data);
--- a/jbig2_priv.h
+++ b/jbig2_priv.h
@@ -7,6 +7,10 @@
 void *
 jbig2_realloc (Jbig2Allocator *allocator, void *p, size_t size);
 
+#define jbig2_new(ctx, t, size) ((t *)jbig2_alloc(ctx->allocator, (size) * sizeof(t)))
+
+#define jbig2_renew(ctx, p, t, size) ((t *)jbig2_realloc(ctx->allocator, (p), (size) * sizeof(t)))
+
 int
 jbig2_error (Jbig2Ctx *ctx, Jbig2Severity severity, int32_t seg_idx,
 	     const char *fmt, ...);
@@ -14,6 +18,15 @@
 typedef uint8_t byte;
 typedef int bool;
 
+typedef struct _Jbig2Result Jbig2Result;
+
+/* The result of decoding a segment. See 0.1.5 */
+struct _Jbig2Result {
+  int32_t segment_number;
+  int segment_type;
+  void (*free)(const Jbig2Result *self, Jbig2Ctx *ctx);
+};
+
 typedef enum {
   JBIG2_FILE_HEADER,
   JBIG2_FILE_SEQUENTIAL_HEADER,
@@ -44,11 +57,20 @@
   int n_sh_max;
   Jbig2SegmentHeader **sh_list;
   int sh_ix;
+
+  /* The map from segment numbers to decoding results, currently
+     stored as a contiguous, 0-indexed array. */
+  int n_results;
+  int n_results_max;
+  const Jbig2Result **results;
 };
 
 int32_t
 jbig2_get_int32 (const byte *buf);
 
+int16_t
+jbig2_get_int16 (const byte *buf);
+
 /* The word stream design is a compromise between simplicity and
    trying to amortize the number of method calls. Each ::get_next_word
    invocation pulls 4 bytes from the stream, packed big-endian into a
@@ -59,3 +81,16 @@
 struct _Jbig2WordStream {
   uint32_t (*get_next_word) (Jbig2WordStream *self, int offset);
 };
+
+Jbig2WordStream *
+jbig2_word_stream_buf_new(Jbig2Ctx *ctx, const byte *data, size_t size);
+
+void
+jbig2_word_stream_buf_free(Jbig2Ctx *ctx, Jbig2WordStream *ws);
+
+const Jbig2Result *
+jbig2_get_result(Jbig2Ctx *ctx, int32_t segment_number);
+
+int
+jbig2_put_result(Jbig2Ctx *ctx, const Jbig2Result *result);
+
--- /dev/null
+++ b/jbig2_symbol_dict.c
@@ -1,0 +1,227 @@
+#include <stddef.h>
+#include <stdint.h>
+#include "jbig2.h"
+#include "jbig2_priv.h"
+#include "jbig2_arith.h"
+#include "jbig2_arith_int.h"
+#include "jbig2_generic.h"
+#include "jbig2_symbol_dict.h"
+
+/* Table 13 */
+typedef struct {
+  bool SDHUFF;
+  bool SDREFAGG;
+  int32_t SDNUMINSYMS;
+  /* SDINSYMS */
+  uint32_t SDNUMNEWSYMS;
+  uint32_t SDNUMEXSYMS;
+  /* SDHUFFDH */
+  /* SDHUFFDW */
+  /* SDHUFFBMSIZE */
+  /* SDHUFFAGGINST */
+  int SDTEMPLATE;
+  int8_t sdat[8];
+  bool SDRTEMPLATE;
+  int8_t sdrat[4];
+} Jbig2SymbolDictParams;
+
+/* 6.5 */
+int
+jbig2_decode_symbol_dict(Jbig2Ctx *ctx,
+			 int32_t seg_number,
+			 const Jbig2SymbolDictParams *params,
+			 const byte *data, size_t size,
+			 Jbig2ArithCx *GB_stats)
+{
+  int32_t HCHEIGHT;
+  uint32_t NSYMSDECODED;
+  int32_t SYMWIDTH, TOTWIDTH;
+  uint32_t HCFIRSTSYM;
+  Jbig2ArithState *as = NULL;
+  Jbig2ArithIntCtx *IADH = NULL;
+  Jbig2ArithIntCtx *IADW = NULL;
+  int code;
+
+  /* 6.5.5 (3) */
+  HCHEIGHT = 0;
+  NSYMSDECODED = 0;
+
+  if (!params->SDHUFF)
+    {
+      Jbig2WordStream *ws = jbig2_word_stream_buf_new(ctx, data, size);
+      as = jbig2_arith_new(ctx, ws);
+      IADH = jbig2_arith_int_ctx_new(ctx);
+      IADW = jbig2_arith_int_ctx_new(ctx);
+    }
+
+  /* 6.5.5 (4a) */
+  while (NSYMSDECODED < params->SDNUMNEWSYMS)
+    {
+      int32_t HCDH, DW;
+
+      /* 6.5.6 */
+      if (params->SDHUFF)
+	; /* todo */
+      else
+	{
+	  code = jbig2_arith_int_decode(IADH, as, &HCDH);
+	}
+
+      /* 6.5.5 (4b) */
+      HCHEIGHT = HCHEIGHT + HCDH;
+      SYMWIDTH = 0;
+      TOTWIDTH = 0;
+      HCFIRSTSYM = NSYMSDECODED;
+
+      if (HCHEIGHT < 0)
+	/* todo: mem cleanup */
+	return jbig2_error(ctx, JBIG2_SEVERITY_FATAL, seg_number,
+			   "Invalid HCHEIGHT value");
+
+      printf ("HCHEIGHT = %d\n", HCHEIGHT);
+      for (;;)
+	{
+	  /* 6.5.7 */
+	  if (params->SDHUFF)
+	    ; /* todo */
+	  else
+	    {
+	      code = jbig2_arith_int_decode(IADW, as, &DW);
+	    }
+
+	  /* 6.5.5 (4c.i) */
+	  if (code == 1)
+	    break;
+	  SYMWIDTH = SYMWIDTH + DW;
+	  TOTWIDTH = TOTWIDTH + SYMWIDTH;
+	  if (SYMWIDTH < 0)
+	    /* todo: mem cleanup */
+	    return jbig2_error(ctx, JBIG2_SEVERITY_FATAL, seg_number,
+			       "Invalid SYMWIDTH value");
+	  printf ("SYMWIDTH = %d\n", SYMWIDTH);
+
+	  /* 6.5.5 (4c.ii) */
+	  if (!params->SDHUFF || params->SDREFAGG)
+	    {
+	      /* 6.5.8 */
+	      if (!params->SDREFAGG)
+		{
+		  Jbig2GenericRegionParams region_params;
+		  int sdat_bytes;
+		  byte *gbreg;
+
+		  /* Table 16 */
+		  region_params.MMR = 0;
+		  region_params.GBW = SYMWIDTH;
+		  region_params.GBH = HCHEIGHT;
+		  region_params.GBTEMPLATE = params->SDTEMPLATE;
+		  region_params.TPGDON = 0;
+		  region_params.USESKIP = 0;
+		  sdat_bytes = params->SDTEMPLATE == 0 ? 8 : 2;
+		  memcpy(region_params.gbat, params->sdat, sdat_bytes);
+
+		  gbreg = jbig2_alloc(ctx->allocator,
+				      ((SYMWIDTH + 7) >> 3) * HCHEIGHT);
+
+		  code = jbig2_decode_generic_region(ctx, seg_number,
+						     &region_params,
+						     as,
+						     gbreg, GB_stats);
+		  /* todo: handle errors */
+		  /* todo: stash gbreg in SDNEWSYMS */
+						     
+		}
+	    }
+
+	  /* 6.5.5 (4c.iv) */
+	  NSYMSDECODED = NSYMSDECODED + 1;
+	}
+    }
+
+  jbig2_free(ctx->allocator, GB_stats);
+
+  return 0;
+}
+
+/* 7.4.2 */
+int
+jbig2_symbol_dictionary(Jbig2Ctx *ctx, Jbig2SegmentHeader *sh,
+			const byte *segment_data)
+{
+  Jbig2SymbolDictParams params;
+  uint16_t flags;
+  int sdat_bytes;
+  int offset;
+  Jbig2ArithCx *GB_stats = NULL;
+
+  if (sh->data_length < 10)
+    goto too_short;
+
+  /* 7.4.2.1.1 */
+  flags = jbig2_get_int16(segment_data);
+  params.SDHUFF = flags & 1;
+  params.SDREFAGG = (flags >> 1) & 1;
+  params.SDTEMPLATE = (flags >> 10) & 3;
+  params.SDRTEMPLATE = (flags >> 12) & 1;
+
+  /* FIXME: there are quite a few of these conditions to check */
+  /* maybe #ifdef CONFORMANCE and a separate routine */
+  if(!params.SDHUFF && (flags & 0x0006))
+    {
+      jbig2_error(ctx, JBIG2_SEVERITY_WARNING, sh->segment_number,
+		  "SDHUFF is zero, but contrary to spec SDHUFFDH is not.");
+    }
+  if(!params.SDHUFF && (flags & 0x0018))
+    {
+      jbig2_error(ctx, JBIG2_SEVERITY_WARNING, sh->segment_number,
+		  "SDHUFF is zero, but contrary to spec SDHUFFDW is not.");
+    }
+
+  /* 7.4.2.1.2 */
+  sdat_bytes = params.SDHUFF ? 0 : params.SDTEMPLATE == 0 ? 8 : 2;
+  memcpy(params.sdat, segment_data + 2, sdat_bytes);
+  offset = 2 + sdat_bytes;
+
+  /* 7.4.2.1.3 */
+  if (params.SDREFAGG && !params.SDRTEMPLATE)
+    {
+      if (offset + 4 > sh->data_length)
+	goto too_short;
+      memcpy(params.sdrat, segment_data + offset, 4);
+      offset += 4;
+    }
+
+  if (offset + 8 > sh->data_length)
+    goto too_short;
+
+  /* 7.4.2.1.4 */
+  params.SDNUMEXSYMS = jbig2_get_int32(segment_data + offset);
+  /* 7.4.2.1.5 */
+  params.SDNUMNEWSYMS = jbig2_get_int32(segment_data + offset + 4);
+  offset += 8;
+
+  jbig2_error(ctx, JBIG2_SEVERITY_INFO, sh->segment_number,
+	      "symbol dictionary, flags=%04x, %d exported syms, %d new syms",
+	      flags, params.SDNUMEXSYMS, params.SDNUMNEWSYMS);
+
+  /* 7.4.2.2 (4) */
+  if (!params.SDHUFF)
+    {
+      int stats_size = params.SDTEMPLATE == 0 ? 65536 :
+	params.SDTEMPLATE == 1 ? 8192 : 1024;
+      GB_stats = jbig2_alloc(ctx->allocator, stats_size);
+      memset(GB_stats, 0, stats_size);
+    }
+
+  return jbig2_decode_symbol_dict(ctx, sh->segment_number,
+				  &params,
+				  segment_data + offset,
+				  sh->data_length - offset,
+				  GB_stats);
+
+  /* todo: retain or free GB_stats */
+  
+ too_short:
+  return jbig2_error(ctx, JBIG2_SEVERITY_FATAL, sh->segment_number,
+		     "Segment too short");
+}
--- /dev/null
+++ b/jbig2_symbol_dict.h
@@ -1,0 +1,3 @@
+int
+jbig2_symbol_dictionary(Jbig2Ctx *ctx, Jbig2SegmentHeader *sh,
+			const byte *segment_data);
--- a/jbig2dec.c
+++ b/jbig2dec.c
@@ -8,7 +8,7 @@
     the Free Software Foundation; either version 2 of the License, or
     (at your option) any later version.
 
-    $Id: jbig2dec.c,v 1.13 2002/02/13 08:47:18 raph Exp $
+    $Id: jbig2dec.c,v 1.14 2002/02/16 07:25:36 raph Exp $
 */
 
 #include <stdio.h>
@@ -215,6 +215,7 @@
   Jbig2SymbolDictionary *result = (Jbig2SymbolDictionary *)malloc(sizeof(Jbig2SymbolDictionary));
   int offset = ctx->offset;
   bool SDHUFF, SDREFAGG, SDRTEMPLATE;
+  int SDTEMPLATE;
   int sdat_bytes;
 
   /* 7.4.2.1.1 */
@@ -223,6 +224,7 @@
 
   SDHUFF = result->flags & 1;
   SDREFAGG = (result->flags >> 1) & 1;
+  SDTEMPLATE = (result->flags >> 10) & 3;
   SDRTEMPLATE = (result->flags >> 12) & 1;
 
   /* FIXME: there are quite a few of these conditions to check */
--- a/makefile
+++ b/makefile
@@ -4,7 +4,7 @@
 
 all:	$(APPS)
 
-jbig2dec:	jbig2.o jbig2dec.o jbig2_huffman.o jbig2_arith.o jbig2_image.o jbig2_generic.o
+jbig2dec:	jbig2.o jbig2dec.o jbig2_huffman.o jbig2_arith.o jbig2_image.o jbig2_generic.o jbig2_symbol_dict.o jbig2_arith_int.o
 
 test_huffman:	jbig2_huffman.c
 	gcc $(CFLAGS) -DTEST jbig2_huffman.c -o test_huffman