shithub: jbig2

Download patch

ref: b536be35e4f10d15b973800351e9e5e1dc17e79d
author: giles <giles@ded80894-8fb9-0310-811b-c03f3676ab4d>
date: Wed May 30 16:58:04 EDT 2001

Initial revision


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

--- /dev/null
+++ b/jbig2.c
@@ -1,0 +1,242 @@
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "jbig2.h"
+
+typedef struct _Jbig2SegmentHeader Jbig2SegmentHeader;
+typedef struct _Jbig2SymbolDictionary Jbig2SymbolDictionary;
+
+struct _Jbig2Ctx {
+  FILE *f;
+  int offset;
+
+  byte flags;
+  int32 n_pages;
+};
+
+struct _Jbig2SegmentHeader {
+  int32 segment_number;
+  byte flags;
+  int referred_to_segment_count;
+  int32 page_association;
+  int data_length;
+};
+
+struct _Jbig2SymbolDictionary {
+  int16 flags;
+  int8 SDAT_flags[8];
+  byte SDRAT_flags[4];
+  int32 SDNUMEXSYMS;
+  int32 SDNUMNEWSYMS;
+};
+
+int32
+get_bytes (Jbig2Ctx *ctx, byte *buf, int size, int off)
+{
+  fseek (ctx->f, off, SEEK_SET);
+  return fread (buf, 1, size, ctx->f);
+}
+
+int16
+get_int16 (Jbig2Ctx *ctx, int off)
+{
+  byte buf[2];
+
+  get_bytes (ctx, buf, 2, off);
+  return (buf[0] << 8) | buf[1];
+}
+
+int32
+get_int32 (Jbig2Ctx *ctx, int off)
+{
+  byte buf[4];
+
+  get_bytes (ctx, buf, 4, off);
+  return (buf[0] << 24) | (buf[1] << 16) | (buf[2] << 8) | buf[3];
+}
+
+static Jbig2Ctx *
+jbig2_open (FILE *f)
+{
+  byte buf[9];
+  const byte header[8] = { 0x97, 0x4a, 0x42, 0x32, 0x0d, 0x0a, 0x1a, 0x0a };
+  Jbig2Ctx *ctx;
+
+  /* Annex D.4 */
+  ctx = (Jbig2Ctx *)malloc (sizeof(Jbig2Ctx));
+  ctx->f = f;
+  get_bytes (ctx, buf, 9, 0);
+  if (memcmp (buf, header, 8))
+    {
+      printf ("not a JBIG2 file\n");
+      return NULL;
+    }
+  ctx->flags = buf[8];
+  if (ctx->flags & 2)
+    {
+      ctx->offset = 9;
+    }
+  else
+    {
+      ctx->offset = 13;
+      ctx->n_pages = get_int32 (ctx, 9);
+    }
+  return ctx;
+}
+
+static Jbig2SegmentHeader *
+jbig2_read_segment_header (Jbig2Ctx *ctx)
+{
+  Jbig2SegmentHeader *result = (Jbig2SegmentHeader *)malloc(sizeof(Jbig2SegmentHeader));
+  int32 offset = ctx->offset;
+  byte rtscarf;
+  int referred_to_segment_count;
+  byte spa;
+
+  /* 7.2.2 */
+  result->segment_number = get_int32 (ctx, offset);
+
+  /* 7.2.3 */
+  get_bytes (ctx, &result->flags, 1, offset + 4);
+
+  /* 7.2.4 */
+  get_bytes (ctx, &rtscarf, 1, offset + 5);
+  if ((rtscarf & 0xe0) == 0xe0)
+    {
+      printf ("long form of rtscarf, can't handle!\n");
+    }
+  else
+    {
+      referred_to_segment_count = (rtscarf >> 5);
+      offset += 6 + referred_to_segment_count;
+    }
+  result->referred_to_segment_count = referred_to_segment_count;
+  /* todo: read referred to segment numbers */
+
+  /* 7.2.6 */
+  get_bytes (ctx, &spa, 1, offset);
+  if (result->flags & 64)
+    {
+      printf ("long form of spa, can't handle!\n");
+    }
+  result->page_association = spa;
+
+  /* 7.2.7 */
+  result->data_length = get_int32 (ctx, offset + 1);
+  ctx->offset = offset + 5;
+
+  return result;
+}
+
+static Jbig2SymbolDictionary *
+jbig2_read_symbol_dictionary (Jbig2Ctx *ctx)
+{
+  Jbig2SymbolDictionary *result = (Jbig2SymbolDictionary *)malloc(sizeof(Jbig2SymbolDictionary));
+  int32 offset = ctx->offset;
+  bool SDHUFF, SDREFAGG, SDRTEMPLATE;
+  int sdat_bytes;
+
+  /* 7.4.2.1.1 */
+  result->flags = get_int16 (ctx, offset);
+  offset += 2;
+
+  SDHUFF = result->flags & 1;
+  SDREFAGG = (result->flags >> 1) & 1;
+  SDRTEMPLATE = (result->flags >> 12) & 1;
+
+  /* 7.4.2.1.2 */
+  if (!SDHUFF)
+    {
+      int SDTEMPLATE = (result->flags >> 10) & 3;
+      if (SDTEMPLATE == 0)
+	sdat_bytes = 8;
+      else
+	sdat_bytes = 2;
+    }
+  else
+    sdat_bytes = 0;
+  get_bytes (ctx, result->SDAT_flags, sdat_bytes, offset);
+  memset (&result->SDAT_flags + sdat_bytes, 0, 8 - sdat_bytes);
+  offset += sdat_bytes;
+
+  /* 7.4.2.1.3 */
+  if (SDREFAGG && !SDRTEMPLATE)
+    {
+      get_bytes (ctx, result->SDRAT_flags, 4, offset);
+      offset += 4;
+    }
+
+  /* 7.4.2.1.4 */
+  result->SDNUMEXSYMS = get_int32 (ctx, offset);
+
+  /* 7.4.2.1.5 */
+  result->SDNUMNEWSYMS = get_int32 (ctx, offset + 4);
+  offset += 8;
+
+  return result;
+}
+
+static void
+dump_symbol_dictionary (Jbig2SymbolDictionary *sd)
+{
+  printf ("segment type = symbol dictionary, flags = %04x, numexsyms = %d, numnewsyms = %d\n",
+	  sd->flags, sd->SDNUMEXSYMS, sd->SDNUMNEWSYMS);
+}
+
+static bool
+dump_segment (Jbig2Ctx *ctx)
+{
+  Jbig2SegmentHeader *sh;
+  int32 offset;
+  Jbig2SymbolDictionary *sd;
+
+  sh = jbig2_read_segment_header (ctx);
+  offset = ctx->offset;
+  printf ("segment number = %d, flags = %02x, page %d, %d bytes\n",
+	  sh->segment_number, sh->flags, sh->page_association, sh->data_length);
+  switch (sh->flags & 63)
+    {
+    case 0:
+      sd = jbig2_read_symbol_dictionary (ctx);
+      dump_symbol_dictionary (sd);
+      break;
+    case 51:
+      printf ("segment type = end of file\n");
+      return TRUE;
+    }
+  ctx->offset = offset + sh->data_length;
+  return FALSE;
+}
+
+static void
+dump_jbig2 (FILE *f)
+{
+  Jbig2Ctx *ctx;
+  bool last;
+
+  ctx = jbig2_open (f);
+  printf ("Number of pages = %d\n", ctx->n_pages);
+  for (;;)
+    {
+      last = dump_segment (ctx);
+      if (last)
+	break;
+    }
+}
+
+int
+main (int argc, char **argv)
+{
+  FILE *f;
+
+  if (argc < 2)
+    return -1;
+  f = fopen (argv[1], "rb");
+  if (f == NULL)
+    return -1;
+
+  dump_jbig2 (f);
+  fclose (f);
+  return 0;
+}
--- /dev/null
+++ b/jbig2.h
@@ -1,0 +1,31 @@
+typedef unsigned char byte;
+typedef int bool;
+typedef unsigned int uint32;
+typedef int int32;
+typedef short int16;
+typedef signed char int8;
+
+#define TRUE 1
+#define FALSE 0
+
+typedef struct _Jbig2Ctx Jbig2Ctx;
+
+int32
+get_bytes (Jbig2Ctx *ctx, byte *buf, int size, int off);
+
+int16
+get_int16 (Jbig2Ctx *ctx, int off);
+
+int32
+get_int32 (Jbig2Ctx *ctx, int off);
+
+/* 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
+   32 bit word. The offset argument is provided as a convenience. It
+   begins at 0 and increments by 4 for each successive invocation. */
+typedef struct _Jbig2WordStream Jbig2WordStream;
+
+struct _Jbig2WordStream {
+  int32 (*get_next_word) (Jbig2WordStream *self, int offset);
+};
--- /dev/null
+++ b/jbig2_arith.c
@@ -1,0 +1,327 @@
+#include <stdlib.h>
+
+#include "jbig2.h"
+#include "jbig2_arith.h"
+
+struct _Jbig2ArithState {
+  uint32 C;
+  int A;
+  int I_CX;
+  int MPS_CX;
+
+  int CT;
+
+  uint32 next_word;
+  int next_word_bytes;
+
+  Jbig2WordStream *ws;
+  int offset;
+};
+
+#define SOFTWARE_CONVENTION
+
+static void
+jbig2_arith_bytein (Jbig2ArithState *as)
+{
+  byte B;
+
+  if (as->next_word_bytes == 0)
+    {
+      Jbig2WordStream *ws = as->ws;
+      as->next_word = ws->get_next_word (ws, as->offset);
+      as->offset += 4;
+      as->next_word_bytes = 4;
+    }
+  /* Figure F.3 */
+  B = (as->next_word >> 24) & 0xFF;
+  if (B == 0xFF)
+    {
+      byte B1;
+      if (as->next_word_bytes == 1)
+	{
+	  Jbig2WordStream *ws = as->ws;
+	  as->next_word = ws->get_next_word (ws, as->offset);
+	  as->offset += 4;
+	  B1 = (as->next_word >> 24) & 0xFF;
+	  if (B1 > 0x8F)
+	    {
+	      printf ("read %02x (aa)\n", B);
+#ifndef SOFTWARE_CONVENTION
+	      as->C += 0xFF00;
+#endif
+	      as->CT = 8;
+	      as->next_word = (0xFF00 | B1) << 16;
+	      as->next_word_bytes = 2;
+	    }
+	  else
+	    {
+	      printf ("read %02x (a)\n", B);
+#ifdef SOFTWARE_CONVENTION
+	      as->C += 0xFE00 - (B << 8);
+#else
+	      as->C += 0xFF00;
+#endif
+	      as->CT = 7;
+	      as->next_word_bytes = 4;
+	    }
+	}
+      else
+	{
+	  B1 = (as->next_word >> 16) & 0xFF;
+	  if (B1 > 0x8F)
+	    {
+	      printf ("read %02x (ba)\n", B);
+#ifndef SOFTWARE_CONVENTION
+	      as->C += 0xFF00;
+#endif
+	      as->CT = 8;
+	    }
+	  else
+	    {
+	      as->next_word_bytes--;
+	      as->next_word <<= 8;
+	      printf ("read %02x (b)\n", B);
+#ifdef SOFTWARE_CONVENTION
+	      as->C += 0xFE00 - (B << 8);
+#else
+	      as->C += 0xFF00;
+#endif
+	      as->CT = 7;
+	    }
+	}
+    }
+  else
+    {
+      printf ("read %02x\n", B);
+#ifdef SOFTWARE_CONVENTION
+      as->C += 0xFF00 - (B << 8);
+#else
+      as->C += (B << 8);
+#endif
+      as->CT = 8;
+      as->next_word <<= 8;
+      as->next_word_bytes--;
+    }
+}
+
+#ifdef DEBUG
+#include <stdio.h>
+
+static void
+jbig2_arith_trace (Jbig2ArithState *as)
+{
+  printf ("I = %2d, A = %04x, CT = %2d, C = %08x\n",
+	  as->I_CX, as->A, as->CT, as->C);
+}
+#endif
+
+Jbig2ArithState *
+jbig2_arith_new (Jbig2WordStream *ws)
+{
+  Jbig2ArithState *result = (Jbig2ArithState *)malloc (sizeof(Jbig2ArithState));
+
+  result->ws = ws;
+
+  result->next_word = ws->get_next_word (ws, 0);
+  result->next_word_bytes = 4;
+  result->offset = 4;
+
+  /* Figure F.1 */
+#ifdef SOFTWARE_CONVENTION
+  result->C = (~(result->next_word >> 8)) & 0xFF0000;
+#else
+  result->C = (result->next_word >> 8) & 0xFF0000;
+#endif
+  result->next_word <<= 8;
+  result->next_word_bytes--;
+
+  jbig2_arith_bytein (result);
+  result->C <<= 7;
+  result->CT -= 7;
+  result->A = 0x8000;
+
+  /* note: these are merely defaults; it may be necessary to provide
+     additional arguments to initialize these to non-default values. */
+  result->I_CX = 0;
+  result->MPS_CX = 0;
+
+  return result;
+};
+
+/* could put bit fields in to minimize memory usage */
+typedef struct {
+  int Qe;
+  int NMPS;
+  int NLPS;
+  bool SWITCH;
+} Jbig2ArithQe;
+
+const Jbig2ArithQe jbig2_arith_Qe[] = {
+  { 0x5601, 1, 1, 1 },
+  { 0x3401, 2, 6, 0 },
+  { 0x1801, 3, 9, 0 },
+  { 0x0AC1, 4, 12, 0 },
+  { 0x0521, 5, 29, 0 },
+  { 0x0221, 38, 33, 0 },
+  { 0x5601, 7, 6, 1 },
+  { 0x5401, 8, 14, 0 },
+  { 0x4801, 9, 14, 0 },
+  { 0x3801, 10, 14, 0 },
+  { 0x3001, 11, 17, 0 },
+  { 0x2401, 12, 18, 0 },
+  { 0x1C01, 13, 20, 0 },
+  { 0x1601, 29, 21, 0 },
+  { 0x5601, 15, 14, 1 },
+  { 0x5401, 16, 14, 0 },
+  { 0x5101, 17, 15, 0 },
+  { 0x4801, 18, 16, 0 },
+  { 0x3801, 19, 17, 0 },
+  { 0x3401, 20, 18, 0 },
+  { 0x3001, 21, 19, 0 },
+  { 0x2801, 22, 19, 0 },
+  { 0x2401, 23, 20, 0 },
+  { 0x2201, 24, 21, 0 },
+  { 0x1C01, 25, 22, 0 },
+  { 0x1801, 26, 23, 0 },
+  { 0x1601, 27, 24, 0 },
+  { 0x1401, 28, 25, 0 },
+  { 0x1201, 29, 26, 0 },
+  { 0x1101, 30, 27, 0 },
+  { 0x0AC1, 31, 28, 0 },
+  { 0x09C1, 32, 29, 0 },
+  { 0x08A1, 33, 30, 0 },
+  { 0x0521, 34, 31, 0 },
+  { 0x0441, 35, 32, 0 },
+  { 0x02A1, 36, 33, 0 },
+  { 0x0221, 37, 34, 0 },
+  { 0x0141, 38, 35, 0 },
+  { 0x0111, 39, 36, 0 },
+  { 0x0085, 40, 37, 0 },
+  { 0x0049, 41, 38, 0 },
+  { 0x0025, 42, 39, 0 },
+  { 0x0015, 43, 40, 0 },
+  { 0x0009, 44, 41, 0 },
+  { 0x0005, 45, 42, 0 },
+  { 0x0001, 45, 43, 0 },
+  { 0x5601, 46, 46, 0 }
+};
+
+static void
+jbig2_arith_renormd (Jbig2ArithState *as)
+{
+  /* Figure E.18 */
+  do
+    {
+      if (as->CT == 0)
+	jbig2_arith_bytein (as);
+      as->A <<= 1;
+      as->C <<= 1;
+      as->CT--;
+    }
+  while ((as->A & 0x8000) == 0);
+}
+
+bool
+jbig2_arith_decode (Jbig2ArithState *as)
+{
+  const Jbig2ArithQe *pqe = &jbig2_arith_Qe[as->I_CX];
+  bool D;
+
+  /* Figure F.2 */
+  as->A -= pqe->Qe;
+  if (
+#ifdef SOFTWARE_CONVENTION
+      (as->C) >> 16 < as->A
+#else
+      !((as->C >> 16) < pqe->Qe)
+#endif
+      )
+    {
+#ifndef SOFTWARE_CONVENTION
+      as->C -= pqe->Qe << 16;
+#endif
+      if ((as->A & 0x8000) == 0)
+	{
+	  /* MPS_EXCHANGE, Figure E.16 */
+	  if (as->A < pqe->Qe)
+	    {
+	      D = 1 - as->MPS_CX;
+	      as->MPS_CX ^= pqe->SWITCH;
+	      as->I_CX = pqe->NLPS;
+	    }
+	  else
+	    {
+	      D = as->MPS_CX;
+	      as->I_CX = pqe->NMPS;
+	    }
+	  jbig2_arith_renormd (as);
+	  return D;
+	}
+      else
+	return as->MPS_CX;
+    }
+  else
+    {
+#ifdef SOFTWARE_CONVENTION
+      as->C -= (as->A) << 16;
+#endif
+      /* LPS_EXCHANGE, Figure E.17 */
+      if (as->A < pqe->Qe)
+	{
+	  as->A = pqe->Qe;
+	  D = as->MPS_CX;
+	  as->I_CX = pqe->NMPS;
+	}
+      else
+	{
+	  as->A = pqe->Qe;
+	  D = 1 - as->MPS_CX;
+	  as->MPS_CX ^= pqe->SWITCH;
+	  as->I_CX = pqe->NLPS;
+	}
+      jbig2_arith_renormd (as);
+      return D;
+    }
+}
+
+#ifdef TEST
+
+static int32
+test_get_word (Jbig2WordStream *self, int offset)
+{
+  byte stream[] = {
+    0x84, 0xC7, 0x3B, 0xFC, 0xE1, 0xA1, 0x43, 0x04, 0x02, 0x20, 0x00, 0x00,
+    0x41, 0x0D, 0xBB, 0x86, 0xF4, 0x31, 0x7F, 0xFF, 0x88, 0xFF, 0x37, 0x47,
+    0x1A, 0xDB, 0x6A, 0xDF, 0xFF, 0xAC,
+    0x00, 0x00
+  };
+  if (offset >= sizeof(stream))
+    return 0;
+  else
+    return (stream[offset] << 24) | (stream[offset + 1] << 16) |
+      (stream[offset + 2] << 8) | stream[offset + 3];
+}
+
+int
+main (int argc, char **argv)
+{
+  Jbig2WordStream ws;
+  Jbig2ArithState *as;
+  int i;
+
+  ws.get_next_word = test_get_word;
+  as = jbig2_arith_new (&ws);
+  jbig2_arith_trace (as);
+
+  for (i = 0; i < 256; i++)
+    {
+      bool D;
+
+      D = jbig2_arith_decode (as);
+      printf ("%3d: D = %d, ", i, D);
+      jbig2_arith_trace (as);
+      
+    }
+  return 0;
+}
+#endif
--- /dev/null
+++ b/jbig2_arith.h
@@ -1,0 +1,4 @@
+typedef struct _Jbig2ArithState Jbig2ArithState;
+
+Jbig2ArithState *
+jbig2_arith_new (Jbig2WordStream *ws);
--- /dev/null
+++ b/jbig2_huffman.c
@@ -1,0 +1,371 @@
+/* Huffman table decoding procedures */
+
+#include <stdlib.h>
+
+#include "jbig2.h"
+#include "jbig2_huffman.h"
+
+#define JBIG2_HUFFMAN_FLAGS_ISOOB 1
+#define JBIG2_HUFFMAN_FLAGS_ISLOW 2
+#define JBIG2_HUFFMAN_FLAGS_ISEXT 4
+
+typedef struct _Jbig2HuffmanEntry Jbig2HuffmanEntry;
+
+struct _Jbig2HuffmanEntry {
+  union {
+    int32 RANGELOW;
+    Jbig2HuffmanTable *ext_table;
+  } u;
+  byte PREFLEN;
+  byte RANGELEN;
+  byte flags;
+};
+
+struct _Jbig2HuffmanTable {
+  int log_table_size;
+  Jbig2HuffmanEntry *entries;
+};
+
+struct _Jbig2HuffmanState {
+  /* The current bit offset is equal to (offset * 8) + offset_bits.
+     The MSB of this_word is the current bit offset. The MSB of next_word
+     is (offset + 4) * 8. */
+  uint32 this_word;
+  uint32 next_word;
+  int offset_bits;
+  int offset;
+
+  Jbig2WordStream *ws;
+};
+
+
+Jbig2HuffmanState *
+jbig2_huffman_new (Jbig2WordStream *ws)
+{
+  Jbig2HuffmanState *result = (Jbig2HuffmanState *)malloc (sizeof(Jbig2HuffmanState));
+
+  result->offset = 0;
+  result->offset_bits = 0;
+  result->this_word = ws->get_next_word (ws, 0);
+  result->next_word = ws->get_next_word (ws, 4);
+
+  result->ws = ws;
+
+  return result;
+}
+
+int32
+jbig2_huffman_get (Jbig2HuffmanState *hs,
+		   const Jbig2HuffmanTable *table, bool *oob)
+{
+  Jbig2HuffmanEntry *entry;
+  byte flags;
+  int offset_bits = hs->offset_bits;
+  uint32 this_word = hs->this_word;
+  uint32 next_word;
+  int RANGELEN;
+  int32 result;
+
+  for (;;)
+    { 
+      int log_table_size = table->log_table_size;
+      int PREFLEN;
+
+      entry = &table->entries[this_word >> (32 - log_table_size)];
+      flags = entry->flags;
+      PREFLEN = entry->PREFLEN;
+
+      next_word = hs->next_word;
+      offset_bits += PREFLEN;
+      if (offset_bits >= 32)
+	{
+	  Jbig2WordStream *ws = hs->ws;
+	  this_word = next_word;
+	  hs->offset += 4;
+	  next_word = ws->get_next_word (ws, hs->offset + 4);
+	  offset_bits -= 32;
+	  hs->next_word = next_word;
+	  PREFLEN = offset_bits;
+	}
+      this_word = (this_word << PREFLEN) |
+	(next_word >> (32 - offset_bits));
+      if (flags & JBIG2_HUFFMAN_FLAGS_ISEXT)
+	{
+	  table = entry->u.ext_table;
+	}
+      else
+	break;
+    }
+  result = entry->u.RANGELOW;
+  RANGELEN = entry->RANGELEN;
+  if (RANGELEN > 0)
+    {
+      int32 HTOFFSET;
+
+      HTOFFSET = this_word >> (32 - RANGELEN);
+      if (flags & JBIG2_HUFFMAN_FLAGS_ISLOW)
+	result -= HTOFFSET;
+      else
+	result += HTOFFSET;
+
+      offset_bits += RANGELEN;
+      if (offset_bits >= 32)
+	{
+	  Jbig2WordStream *ws = hs->ws;
+	  this_word = next_word;
+	  hs->offset += 4;
+	  next_word = ws->get_next_word (ws, hs->offset + 4);
+	  offset_bits -= 32;
+	  hs->next_word = next_word;
+	  RANGELEN = offset_bits;
+	}
+      this_word = (this_word << RANGELEN) |
+	(next_word >> (32 - offset_bits));
+    }
+
+  hs->this_word = this_word;
+  hs->offset_bits = offset_bits;
+
+  if (oob != NULL)
+    *oob = (flags & JBIG2_HUFFMAN_FLAGS_ISOOB);
+
+  return result;
+}
+
+typedef struct _Jbig2HuffmanLine Jbig2HuffmanLine;
+
+struct _Jbig2HuffmanLine {
+  int PREFLEN;
+  int RANGELEN;
+  int RANGELOW;
+};
+
+struct _Jbig2HuffmanParams {
+  bool HTOOB;
+  int n_lines;
+  const Jbig2HuffmanLine *lines;
+};
+
+/* Table B.1 */
+const Jbig2HuffmanLine
+jbig_huffman_lines_A[] = {
+  { 1, 4, 0 },
+  { 2, 8, 16 },
+  { 3, 16, 272 },
+  { 0, 32, -1 },   /* low */
+  { 3, 32, 65808 } /* high */
+};
+
+const Jbig2HuffmanParams
+jbig_huffman_params_A = { FALSE, 5, jbig_huffman_lines_A };
+
+/* Table B.2 */
+const Jbig2HuffmanLine
+jbig_huffman_lines_B[] = {
+  { 1, 0, 0 },
+  { 2, 0, 1 },
+  { 3, 0, 2 },
+  { 4, 3, 3 },
+  { 5, 6, 11 },
+  { 0, 32, -1 }, /* low */
+  { 6, 32, 75 }, /* high */
+  { 6, 0, 0 }
+};
+
+const Jbig2HuffmanParams
+jbig_huffman_params_B = { TRUE, 8, jbig_huffman_lines_B };
+
+/* Table B.3 */
+const Jbig2HuffmanLine
+jbig_huffman_lines_C[] = {
+  { 8, 8, -256 },
+  { 1, 0, 0 },
+  { 2, 0, 1 },
+  { 3, 0, 2 },
+  { 4, 3, 3 },
+  { 5, 6, 11 },
+  { 8, 32, -257 }, /* low */
+  { 7, 32, 75 },   /* high */
+  { 6, 0, 0 } /* OOB */
+};
+
+const Jbig2HuffmanParams
+jbig_huffman_params_C = { TRUE, 9, jbig_huffman_lines_C };
+
+/* Table B.4 */
+const Jbig2HuffmanLine
+jbig_huffman_lines_D[] = {
+  { 1, 0, 1 },
+  { 2, 0, 2 },
+  { 3, 0, 3 },
+  { 4, 3, 4 },
+  { 5, 6, 12 },
+  { 0, 32, -1 }, /* low */
+  { 5, 32, 76 }, /* high */
+};
+
+const Jbig2HuffmanParams
+jbig_huffman_params_D = { FALSE, 7, jbig_huffman_lines_D };
+
+/* Table B.14 */
+const Jbig2HuffmanLine
+jbig_huffman_lines_N[] = {
+  { 3, 0, -2 },
+  { 3, 0, -1 },
+  { 1, 0, 0 },
+  { 3, 3, 1 },
+  { 3, 6, 2 },
+  { 0, 32, -1 }, /* low */
+  { 0, 32, 3 }, /* high */
+};
+
+const Jbig2HuffmanParams
+jbig_huffman_params_N = { FALSE, 7, jbig_huffman_lines_N };
+
+#define LOG_TABLE_SIZE_MAX 8
+
+Jbig2HuffmanTable *
+jbig2_build_huffman_table (const Jbig2HuffmanParams *params)
+{
+  int LENCOUNT[256];
+  int LENMAX = -1;
+  const Jbig2HuffmanLine *lines = params->lines;
+  int n_lines = params->n_lines;
+  int i, j;
+  int log_table_size = 0;
+  Jbig2HuffmanTable *result;
+  Jbig2HuffmanEntry *entries;
+  int CURLEN;
+  int firstcode = 0;
+  int CURCODE;
+  int CURTEMP;
+
+  /* B.3, 1. */
+  for (i = 0; i < params->n_lines; i++)
+    {
+      int PREFLEN = lines[i].PREFLEN;
+      int lts;
+
+      if (PREFLEN > LENMAX)
+	{
+	  for (j = LENMAX + 1; j < PREFLEN + 1; j++)
+	    LENCOUNT[j] = 0;
+	  LENMAX = PREFLEN;
+	}
+      LENCOUNT[PREFLEN]++;
+
+      lts = PREFLEN + lines[i].RANGELEN;
+      if (lts > LOG_TABLE_SIZE_MAX)
+	lts = PREFLEN;
+      if (lts <= LOG_TABLE_SIZE_MAX && log_table_size < lts)
+	log_table_size = lts;
+    }
+  result = (Jbig2HuffmanTable *)malloc (sizeof(Jbig2HuffmanTable));
+  result->log_table_size = log_table_size;
+  entries = (Jbig2HuffmanEntry *)malloc (sizeof(Jbig2HuffmanEntry) << log_table_size);
+  result->entries = entries;
+
+  LENCOUNT[0] = 0;
+
+  for (CURLEN = 1; CURLEN <= LENMAX; CURLEN++)
+    {
+      int shift = log_table_size - CURLEN;
+
+      /* B.3 3.(a) */
+      firstcode = (firstcode + LENCOUNT[CURLEN - 1]) << 1;
+      CURCODE = firstcode;
+      /* B.3 3.(b) */
+      for (CURTEMP = 0; CURTEMP < n_lines; CURTEMP++)
+	{
+	  int PREFLEN = lines[CURTEMP].PREFLEN;
+	  if (PREFLEN == CURLEN)
+	    {
+	      int RANGELEN = lines[CURTEMP].RANGELEN;
+	      int start_j = CURCODE << shift;
+	      int end_j = (CURCODE + 1) << shift;
+	      byte eflags = 0;
+
+	      /* todo: build extension tables */
+	      if (params->HTOOB && CURTEMP == n_lines - 1)
+		eflags |= JBIG2_HUFFMAN_FLAGS_ISOOB;
+	      if (CURTEMP == n_lines - (params->HTOOB ? 3 : 2))
+		eflags |= JBIG2_HUFFMAN_FLAGS_ISLOW;
+	      if (PREFLEN + RANGELEN > LOG_TABLE_SIZE_MAX)
+		{
+		  for (j = start_j; j < end_j; j++)
+		    {
+		      entries[j].u.RANGELOW = lines[CURTEMP].RANGELOW;
+		      entries[j].PREFLEN = PREFLEN;
+		      entries[j].RANGELEN = RANGELEN;
+		      entries[j].flags = eflags;
+		    }
+		}
+	      else
+		{
+		  for (j = start_j; j < end_j; j++)
+		    {
+		      int32 HTOFFSET = (j >> (shift - RANGELEN)) &
+			((1 << RANGELEN) - 1);
+		      if (eflags & JBIG2_HUFFMAN_FLAGS_ISLOW)
+			entries[j].u.RANGELOW = lines[CURTEMP].RANGELOW -
+			  HTOFFSET;
+		      else
+			entries[j].u.RANGELOW = lines[CURTEMP].RANGELOW +
+			  HTOFFSET;
+		      entries[j].PREFLEN = PREFLEN + RANGELEN;
+		      entries[j].RANGELEN = 0;
+		      entries[j].flags = eflags;
+		    }
+		}
+	      CURCODE++;
+	    }
+	}
+    }
+
+  return result;
+}
+
+#ifdef TEST
+#include <stdio.h>
+
+static int32
+test_get_word (Jbig2WordStream *self, int offset)
+{
+  if (offset == 0)
+    return 0xe9cbf400;
+  else
+    return 0;
+}
+
+int
+main (int argc, char **argv)
+{
+  Jbig2HuffmanTable *b1;
+  Jbig2HuffmanTable *b2;
+  Jbig2HuffmanTable *b4;
+  Jbig2HuffmanState *hs;
+  Jbig2WordStream ws;
+  bool oob;
+  int32 code;
+
+  b1 = jbig2_build_huffman_table (&jbig_huffman_params_A);
+  b2 = jbig2_build_huffman_table (&jbig_huffman_params_B);
+  b4 = jbig2_build_huffman_table (&jbig_huffman_params_D);
+  ws.get_next_word = test_get_word;
+  hs = jbig2_huffman_new (&ws);
+
+  code = jbig2_huffman_get (hs, b4, &oob);
+  printf ("%d %s\n", code, oob ? "OOB" : "");
+
+  code = jbig2_huffman_get (hs, b2, &oob);
+  printf ("%d %s\n", code, oob ? "OOB" : "");
+
+  code = jbig2_huffman_get (hs, b2, &oob);
+  printf ("%d %s\n", code, oob ? "OOB" : "");
+
+  code = jbig2_huffman_get (hs, b1, &oob);
+  printf ("%d %s\n", code, oob ? "OOB" : "");
+
+  return 0;
+}
+#endif
--- /dev/null
+++ b/jbig2_huffman.h
@@ -1,0 +1,13 @@
+typedef struct _Jbig2HuffmanState Jbig2HuffmanState;
+typedef struct _Jbig2HuffmanTable Jbig2HuffmanTable;
+typedef struct _Jbig2HuffmanParams Jbig2HuffmanParams;
+
+Jbig2HuffmanState *
+jbig2_huffman_new (Jbig2WordStream *ws);
+
+int32
+jbig2_huffman_get (Jbig2HuffmanState *hs,
+		   const Jbig2HuffmanTable *table, bool *oob);
+
+Jbig2HuffmanTable *
+jbig2_build_huffman_table (const Jbig2HuffmanParams *params);
--- /dev/null
+++ b/makefile
@@ -1,0 +1,12 @@
+CFLAGS=-Wall -g
+
+all:	jbig2
+
+jbig2:	jbig2.o jbig2_huffman.o jbig2_arith.o
+
+test_huffman:	jbig2_huffman.c
+	gcc $(CFLAGS) -DTEST jbig2_huffman.c -o test_huffman
+
+test_arith:	jbig2_arith.c
+	gcc $(CFLAGS) -DTEST -DDEBUG jbig2_arith.c -o test_arith
+