shithub: jbig2

Download patch

ref: c29b63557cb191f2b9371a06a73a11bc4b8f5544
parent: d26c7e4f39f76fe70cc61d6dc14d05a042a9be48
author: masaki <masaki@a1074d23-0009-0410-80fe-cf8c14f379e6>
date: Tue Jul 20 05:46:34 EDT 2010

Add support for user defined huffman table.

This code adds support for segment type 53 segments, which supplies
inline huffman code table.  Bug 689853.

Many thanks to Justin Greer.

git-svn-id: svn+ssh://svn.ghostscript.com/svn/ghostscript/trunk/gs/jbig2dec@11526 a1074d23-0009-0410-80fe-cf8c14f379e6
(cherry picked from commit e51f8e400e4502d7a6d60ee31890a195f55d1b64)

Signed-off-by: Masaki Ushizaka <masaki@ghostscript.com>

--- a/jbig2_huffman.c
+++ b/jbig2_huffman.c
@@ -451,6 +451,196 @@
   return;
 }
 
+/* Routines to handle "code table segment (53)" */
+
+/* return 'bitlen' bits from 'bitoffset' of 'data' */
+static uint32_t
+jbig2_table_read_bits(const byte *data, size_t *bitoffset, const int bitlen)
+{
+    uint32_t result = 0;
+    uint32_t byte_offset = *bitoffset / 8;
+    const int endbit = (*bitoffset & 7) + bitlen;
+    const int n_proc_bytes = (endbit + 7) / 8;
+    const int rshift = n_proc_bytes * 8 - endbit;
+    int i;
+    for (i = n_proc_bytes - 1; i >= 0; i--) {
+        uint32_t d = data[byte_offset++];
+        const int nshift = i * 8 - rshift;
+        if (nshift > 0)
+            d <<= nshift;
+        else if (nshift < 0)
+            d >>= -nshift;
+        result |= d;
+    }
+    result &= ~(-1 << bitlen);
+    *bitoffset += bitlen;
+    return result;
+}
+
+/* Parse a code table segment, store Jbig2HuffmanParams in segment->result */
+int
+jbig2_table(Jbig2Ctx *ctx, Jbig2Segment *segment, const byte *segment_data)
+{
+    Jbig2HuffmanParams *params = NULL;
+    Jbig2HuffmanLine *line = NULL;
+
+    segment->result = NULL;
+    if (segment->data_length < 10)
+        goto too_short;
+
+    {
+        /* B.2 1) (B.2.1) Code table flags */
+        const int code_table_flags = segment_data[0];
+        const int HTOOB = code_table_flags & 0x01; /* Bit 0: HTOOB */
+        /* Bits 1-3: Number of bits used in code table line prefix size fields */
+        const int HTPS  = (code_table_flags >> 1 & 0x07) + 1;
+        /* Bits 4-6: Number of bits used in code table line range size fields */
+        const int HTRS  = (code_table_flags >> 4 & 0x07) + 1;
+        /* B.2 2) (B.2.2) The lower bound of the first table line in the encoded table */
+        const int32_t HTLOW  = jbig2_get_int32(segment_data + 1);
+        /* B.2 3) (B.2.3) One larger than the upeer bound of
+           the last normal table line in the encoded table */
+        const int32_t HTHIGH = jbig2_get_int32(segment_data + 5);
+        /* estimated number of lines int this table, used for alloacting memory for lines */
+        const size_t lines_max = (segment->data_length * 8 - HTPS * (HTOOB ? 3 : 2)) /
+                                                        (HTPS + HTRS) + (HTOOB ? 3 : 2);
+        /* points to a first table line data */
+        const byte *lines_data = segment_data + 9;
+        const size_t lines_data_bitlen = (segment->data_length - 9) * 8;    /* length in bit */
+        /* bit offset: controls bit reading */
+        size_t boffset = 0;
+        /* B.2 4) */
+        int32_t CURRANGELOW = HTLOW;
+        int NTEMP = 0;
+
+#ifdef JBIG2_DEBUG
+        jbig2_error(ctx, JBIG2_SEVERITY_DEBUG, segment->number, 
+            "DECODING USER TABLE... Flags: %d, HTOOB: %d, HTPS: %d, HTRS: %d, HTLOW: %d, HTHIGH: %d", 
+            code_table_flags, HTOOB, HTPS, HTRS, HTLOW, HTHIGH);
+#endif
+
+        /* allocate HuffmanParams & HuffmanLine */
+        params = jbig2_new(ctx, Jbig2HuffmanParams, 1);
+        if (params == NULL) {
+            jbig2_error(ctx, JBIG2_SEVERITY_FATAL, segment->number,
+                            "Could not allocate Huffman Table Parameter");
+            goto error_exit;
+        }
+        line = jbig2_new(ctx, Jbig2HuffmanLine, lines_max);
+        if (line == NULL) {
+            jbig2_error(ctx, JBIG2_SEVERITY_FATAL, segment->number,
+                            "Could not allocate Huffman Table Lines");
+            goto error_exit;
+        }
+        /* B.2 5) */
+        while (CURRANGELOW < HTHIGH) {
+            /* B.2 5) a) */
+            if (boffset + HTPS >= lines_data_bitlen)
+                goto too_short;
+            line[NTEMP].PREFLEN  = jbig2_table_read_bits(lines_data, &boffset, HTPS);
+            /* B.2 5) b) */
+            if (boffset + HTRS >= lines_data_bitlen)
+                goto too_short;
+            line[NTEMP].RANGELEN = jbig2_table_read_bits(lines_data, &boffset, HTRS);
+            /* B.2 5) c) */
+            line[NTEMP].RANGELOW = CURRANGELOW;
+            CURRANGELOW += (1 << line[NTEMP].RANGELEN);
+            NTEMP++;
+        }
+        /* B.2 6), B.2 7) lower range table line */
+        if (boffset + HTPS >= lines_data_bitlen)
+            goto too_short;
+        line[NTEMP].PREFLEN  = jbig2_table_read_bits(lines_data, &boffset, HTPS);
+        line[NTEMP].RANGELEN = 32;
+        line[NTEMP].RANGELOW = HTLOW - 1;
+        NTEMP++;
+        /* B.2 8), B.2 9) upper range table line */
+        if (boffset + HTPS >= lines_data_bitlen)
+            goto too_short;
+        line[NTEMP].PREFLEN  = jbig2_table_read_bits(lines_data, &boffset, HTPS);
+        line[NTEMP].RANGELEN = 32;
+        line[NTEMP].RANGELOW = HTHIGH;
+        NTEMP++;
+        /* B.2 10) */
+        if (HTOOB) {
+            /* B.2 10) a), B.2 10) b) out-of-bound table line */
+            if (boffset + HTPS >= lines_data_bitlen)
+                goto too_short;
+            line[NTEMP].PREFLEN  = jbig2_table_read_bits(lines_data, &boffset, HTPS);
+            line[NTEMP].RANGELEN = 0;
+            line[NTEMP].RANGELOW = 0;
+            NTEMP++;
+        }
+        if (NTEMP != lines_max) {
+            Jbig2HuffmanLine *new_line = (Jbig2HuffmanLine *)jbig2_realloc(ctx->allocator,
+                                                    line, sizeof(Jbig2HuffmanLine) * NTEMP);
+            if ( new_line == NULL ) {
+                jbig2_error(ctx, JBIG2_SEVERITY_FATAL, segment->number,
+                                "Could not reallocate Huffman Table Lines");
+                goto error_exit;
+            }
+            line = new_line;
+        }
+        params->HTOOB   = HTOOB;
+        params->n_lines = NTEMP;
+        params->lines   = line;
+        segment->result = params;
+
+#ifdef JBIG2_DEBUG
+        {
+            int i;
+            for (i = 0; i < NTEMP; i++) {
+                jbig2_error(ctx, JBIG2_SEVERITY_DEBUG, segment->number, 
+                    "Line: %d, PREFLEN: %d, RANGELEN: %d, RANGELOW: %d", 
+                    i, params->lines[i].PREFLEN, params->lines[i].RANGELEN, params->lines[i].RANGELOW);
+            }
+        }
+#endif
+    }
+    return 0;
+
+too_short:
+    jbig2_error(ctx, JBIG2_SEVERITY_FATAL, segment->number, "Segment too short");
+error_exit:
+    if (line != NULL) {
+        jbig2_free(ctx->allocator, line);
+    }
+    if (params != NULL) {
+        jbig2_free(ctx->allocator, params);
+    }
+    return -1;
+}
+
+/* free Jbig2HuffmanParams allocated by jbig2_huffman_table() */
+void
+jbig2_table_free(Jbig2Ctx *ctx, Jbig2HuffmanParams *params)
+{
+    if (params != NULL) {
+        if (params->lines != NULL)
+            jbig2_free(ctx->allocator, (void *)params->lines);
+        jbig2_free(ctx->allocator, params);
+    }
+}
+
+/* find a user supplied table used by 'segment' and by 'index' */
+const Jbig2HuffmanParams *
+jbig2_find_table(Jbig2Ctx *ctx, Jbig2Segment *segment, int index)
+{
+    int i, table_index = 0;
+
+    for (i = 0; i < segment->referred_to_segment_count; i++) {
+        const Jbig2Segment * const rsegment =
+                jbig2_find_segment(ctx, segment->referred_to_segments[i]);
+        if (rsegment && (rsegment->flags & 63) == 53) {
+            if (table_index == index)
+                return (const Jbig2HuffmanParams *)rsegment->result;
+            ++table_index;
+        }
+    }
+    return NULL;
+}
+
+
 #ifdef TEST
 #include <stdio.h>
 
--- a/jbig2_huffman.h
+++ b/jbig2_huffman.h
@@ -101,5 +101,18 @@
 extern const Jbig2HuffmanParams jbig2_huffman_params_N; /* Table B.14 */
 extern const Jbig2HuffmanParams jbig2_huffman_params_O; /* Table B.15 */
 
+/* Routines to handle "code table segment (53)" */
+
+/* Parse a code table segment, store Jbig2HuffmanParams in segment->result */
+int
+jbig2_table(Jbig2Ctx *ctx, Jbig2Segment *segment, const byte *segment_data);
+
+/* free Jbig2HuffmanParams allocated by jbig2_huffman_table() */
+void
+jbig2_table_free(Jbig2Ctx *ctx, Jbig2HuffmanParams *params);
+
+/* find a user supplied table used by 'segment' and by 'index' */
+const Jbig2HuffmanParams *
+jbig2_find_table(Jbig2Ctx *ctx, Jbig2Segment *segment, int index);
 
 #endif /* JBIG2_HUFFMAN_H */
--- a/jbig2_segment.c
+++ b/jbig2_segment.c
@@ -22,6 +22,7 @@
 
 #include "jbig2.h"
 #include "jbig2_priv.h"
+#include "jbig2_huffman.h"
 #include "jbig2_symbol_dict.h"
 #include "jbig2_metadata.h"
 
@@ -142,6 +143,10 @@
 	  if (segment->result != NULL)
 	    jbig2_image_release(ctx, (Jbig2Image*)segment->result);
 	  break;
+	case 53: /* user-supplied huffman table */
+	  if (segment->result != NULL)
+		jbig2_table_free(ctx, (Jbig2HuffmanParams*)segment->result);
+	  break;
 	case 62:
 	  if (segment->result != NULL)
 	    jbig2_metadata_free(ctx, (Jbig2Metadata*)segment->result);
@@ -282,9 +287,8 @@
     case 52:
       return jbig2_error(ctx, JBIG2_SEVERITY_WARNING, segment->number,
         "unhandled segment type 'profile'");
-    case 53:
-      return jbig2_error(ctx, JBIG2_SEVERITY_WARNING, segment->number,
-        "unhandled table segment");
+    case 53: /* user-supplied huffman table */
+      return jbig2_table(ctx, segment, segment_data);
     case 62:
       return jbig2_parse_extension_segment(ctx, segment, segment_data);
     default:
--- a/jbig2_symbol_dict.c
+++ b/jbig2_symbol_dict.c
@@ -751,6 +751,8 @@
   int offset;
   Jbig2ArithCx *GB_stats = NULL;
   Jbig2ArithCx *GR_stats = NULL;
+  int table_index = 0;
+  const Jbig2HuffmanParams *huffman_params;
 
   if (segment->data_length < 10)
     goto too_short;
@@ -778,9 +780,16 @@
 		                       &jbig2_huffman_params_E);
 	break;
       case 3: /* Custom table from referred segment */
-	/* We handle this case later by leaving the table as NULL */
-	return jbig2_error(ctx, JBIG2_SEVERITY_FATAL, segment->number,
-	    "symbol dictionary uses custom DH huffman table (NYI)");
+        huffman_params = jbig2_find_table(ctx, segment, table_index);
+        if (huffman_params == NULL) {
+            return jbig2_error(ctx, JBIG2_SEVERITY_FATAL, segment->number,
+                "Custom DH huffman table not found (%d)", table_index);
+        }
+        params.SDHUFFDH = jbig2_build_huffman_table(ctx, huffman_params);
+        ++table_index;
+        break;
+        /* FIXME: this function leaks memory when error happens.
+           i.e. not calling jbig2_release_huffman_table() */
       case 2:
       default:
 	return jbig2_error(ctx, JBIG2_SEVERITY_FATAL, segment->number,
@@ -797,9 +806,14 @@
 		                       &jbig2_huffman_params_C);
 	break;
       case 3: /* Custom table from referred segment */
-	/* We handle this case later by leaving the table as NULL */
-	return jbig2_error(ctx, JBIG2_SEVERITY_FATAL, segment->number,
-	    "symbol dictionary uses custom DW huffman table (NYI)");
+        huffman_params = jbig2_find_table(ctx, segment, table_index);
+        if (huffman_params == NULL) {
+            return jbig2_error(ctx, JBIG2_SEVERITY_FATAL, segment->number,
+                "Custom DW huffman table not found (%d)", table_index);
+        }
+        params.SDHUFFDW = jbig2_build_huffman_table(ctx, huffman_params);
+        ++table_index;
+        break;
       case 2:
       default:
 	return jbig2_error(ctx, JBIG2_SEVERITY_FATAL, segment->number,
@@ -808,8 +822,13 @@
     }
     if (flags & 0x0040) {
         /* Custom table from referred segment */
-	return jbig2_error(ctx, JBIG2_SEVERITY_FATAL, segment->number,
-	    "symbol dictionary uses custom BMSIZE huffman table (NYI)");
+        huffman_params = jbig2_find_table(ctx, segment, table_index);
+        if (huffman_params == NULL) {
+            return jbig2_error(ctx, JBIG2_SEVERITY_FATAL, segment->number,
+                "Custom BMSIZE huffman table not found (%d)", table_index);
+        }
+        params.SDHUFFBMSIZE = jbig2_build_huffman_table(ctx, huffman_params);
+        ++table_index;
     } else {
 	/* Table B.1 */
 	params.SDHUFFBMSIZE = jbig2_build_huffman_table(ctx,
@@ -817,8 +836,13 @@
     }
     if (flags & 0x0080) {
         /* Custom table from referred segment */
-	return jbig2_error(ctx, JBIG2_SEVERITY_FATAL, segment->number,
-	    "symbol dictionary uses custom REFAGG huffman table (NYI)");
+        huffman_params = jbig2_find_table(ctx, segment, table_index);
+        if (huffman_params == NULL) {
+            return jbig2_error(ctx, JBIG2_SEVERITY_FATAL, segment->number,
+                "Custom REFAGG huffman table not found (%d)", table_index);
+        }
+        params.SDHUFFAGGINST = jbig2_build_huffman_table(ctx, huffman_params);
+        ++table_index;
     } else {
 	/* Table B.1 */
 	params.SDHUFFAGGINST = jbig2_build_huffman_table(ctx,
--- a/jbig2_text.c
+++ b/jbig2_text.c
@@ -419,6 +419,8 @@
     int code = 0;
     Jbig2WordStream *ws = NULL;
     Jbig2ArithState *as = NULL;
+    int table_index = 0;
+    const Jbig2HuffmanParams *huffman_params;
 
     /* 7.4.1 */
     if (segment->data_length < 17)
@@ -496,10 +498,16 @@
 			&jbig2_huffman_params_G);
 	    break;
 	  case 3: /* Custom table from referred segment */
-	    /* We handle this case later by leaving the table as NULL */
-	    return jbig2_error(ctx, JBIG2_SEVERITY_FATAL, segment->number,
-		"text region uses custom FS huffman table (NYI)");
-	    break;
+            huffman_params = jbig2_find_table(ctx, segment, table_index);
+            if (huffman_params == NULL) {
+                return jbig2_error(ctx, JBIG2_SEVERITY_FATAL, segment->number,
+                    "Custom FS huffman table not found (%d)", table_index);
+            }
+            params.SBHUFFFS = jbig2_build_huffman_table(ctx, huffman_params);
+            ++table_index;
+            /* FIXME: this function leaks memory when error happens.
+               i.e. not calling jbig2_release_huffman_table() */
+            break;
 	  case 2: /* invalid */
 	  default:
 	    return jbig2_error(ctx, JBIG2_SEVERITY_FATAL, segment->number,
@@ -520,10 +528,14 @@
 			&jbig2_huffman_params_J);
 	    break;
 	  case 3: /* Custom table from referred segment */
-	    /* We handle this case later by leaving the table as NULL */
-	    return jbig2_error(ctx, JBIG2_SEVERITY_FATAL, segment->number,
-		"text region uses custom DS huffman table (NYI)");
-	    break;
+            huffman_params = jbig2_find_table(ctx, segment, table_index);
+            if (huffman_params == NULL) {
+                return jbig2_error(ctx, JBIG2_SEVERITY_FATAL, segment->number,
+                    "Custom DS huffman table not found (%d)", table_index);
+            }
+            params.SBHUFFDS = jbig2_build_huffman_table(ctx, huffman_params);
+            ++table_index;
+            break;
 	}
 	switch ((huffman_flags & 0x0030) >> 4) {
 	  case 0: /* Table B.11 */
@@ -539,10 +551,14 @@
 			&jbig2_huffman_params_M);
 	    break;
 	  case 3: /* Custom table from referred segment */
-	    /* We handle this case later by leaving the table as NULL */
-	    return jbig2_error(ctx, JBIG2_SEVERITY_FATAL, segment->number,
-		"text region uses custom DT huffman table (NYI)");
-	    break;
+            huffman_params = jbig2_find_table(ctx, segment, table_index);
+            if (huffman_params == NULL) {
+                return jbig2_error(ctx, JBIG2_SEVERITY_FATAL, segment->number,
+                    "Custom DT huffman table not found (%d)", table_index);
+            }
+            params.SBHUFFDT = jbig2_build_huffman_table(ctx, huffman_params);
+            ++table_index;
+            break;
 	}
 	switch ((huffman_flags & 0x00c0) >> 6) {
 	  case 0: /* Table B.14 */
@@ -554,9 +570,13 @@
 			&jbig2_huffman_params_O);
 	    break;
 	  case 3: /* Custom table from referred segment */
-	    /* We handle this case later by leaving the table as NULL */
-	    return jbig2_error(ctx, JBIG2_SEVERITY_FATAL, segment->number,
-		"text region uses custom RDW huffman table (NYI)");
+            huffman_params = jbig2_find_table(ctx, segment, table_index);
+            if (huffman_params == NULL) {
+                return jbig2_error(ctx, JBIG2_SEVERITY_FATAL, segment->number,
+                    "Custom RDW huffman table not found (%d)", table_index);
+            }
+            params.SBHUFFRDW = jbig2_build_huffman_table(ctx, huffman_params);
+            ++table_index;
 	    break;
 	  case 2: /* invalid */
 	  default:
@@ -574,9 +594,13 @@
 			&jbig2_huffman_params_O);
 	    break;
 	  case 3: /* Custom table from referred segment */
-	    /* We handle this case later by leaving the table as NULL */
-	    return jbig2_error(ctx, JBIG2_SEVERITY_FATAL, segment->number,
-		"text region uses custom RDH huffman table (NYI)");
+            huffman_params = jbig2_find_table(ctx, segment, table_index);
+            if (huffman_params == NULL) {
+                return jbig2_error(ctx, JBIG2_SEVERITY_FATAL, segment->number,
+                    "Custom RDH huffman table not found (%d)", table_index);
+            }
+            params.SBHUFFRDH = jbig2_build_huffman_table(ctx, huffman_params);
+            ++table_index;
 	    break;
 	  case 2: /* invalid */
 	  default:
@@ -594,9 +618,13 @@
 			&jbig2_huffman_params_O);
 	    break;
 	  case 3: /* Custom table from referred segment */
-	    /* We handle this case later by leaving the table as NULL */
-	    return jbig2_error(ctx, JBIG2_SEVERITY_FATAL, segment->number,
-		"text region uses custom RDX huffman table (NYI)");
+            huffman_params = jbig2_find_table(ctx, segment, table_index);
+            if (huffman_params == NULL) {
+                return jbig2_error(ctx, JBIG2_SEVERITY_FATAL, segment->number,
+                    "Custom RDX huffman table not found (%d)", table_index);
+            }
+            params.SBHUFFRDX = jbig2_build_huffman_table(ctx, huffman_params);
+            ++table_index;
 	    break;
 	  case 2: /* invalid */
 	  default:
@@ -614,9 +642,13 @@
 			&jbig2_huffman_params_O);
 	    break;
 	  case 3: /* Custom table from referred segment */
-	    /* We handle this case later by leaving the table as NULL */
-	    return jbig2_error(ctx, JBIG2_SEVERITY_FATAL, segment->number,
-		"text region uses custom RDY huffman table (NYI)");
+            huffman_params = jbig2_find_table(ctx, segment, table_index);
+            if (huffman_params == NULL) {
+                return jbig2_error(ctx, JBIG2_SEVERITY_FATAL, segment->number,
+                    "Custom RDY huffman table not found (%d)", table_index);
+            }
+            params.SBHUFFRDY = jbig2_build_huffman_table(ctx, huffman_params);
+            ++table_index;
 	    break;
 	  case 2: /* invalid */
 	  default:
@@ -630,9 +662,13 @@
 			&jbig2_huffman_params_A);
 	    break;
 	  case 1: /* Custom table from referred segment */
-	    /* We handle this case later by leaving the table as NULL */
-	    return jbig2_error(ctx, JBIG2_SEVERITY_FATAL, segment->number,
-		"text region uses custom RSIZE huffman table (NYI)");
+            huffman_params = jbig2_find_table(ctx, segment, table_index);
+            if (huffman_params == NULL) {
+                return jbig2_error(ctx, JBIG2_SEVERITY_FATAL, segment->number,
+                    "Custom RSIZE huffman table not found (%d)", table_index);
+            }
+            params.SBHUFFRSIZE = jbig2_build_huffman_table(ctx, huffman_params);
+            ++table_index;
 	    break;
 	}