You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@pdfbox.apache.org by le...@apache.org on 2017/10/16 18:09:17 UTC

[4/8] pdfbox-jbig2 git commit: initial commit of the JBig2 ImageIO plugin

http://git-wip-us.apache.org/repos/asf/pdfbox-jbig2/blob/4619d28b/src/main/java/org/apache/pdfbox/jbig2/segments/GenericRegion.java
----------------------------------------------------------------------
diff --git a/src/main/java/org/apache/pdfbox/jbig2/segments/GenericRegion.java b/src/main/java/org/apache/pdfbox/jbig2/segments/GenericRegion.java
new file mode 100644
index 0000000..9c42f04
--- /dev/null
+++ b/src/main/java/org/apache/pdfbox/jbig2/segments/GenericRegion.java
@@ -0,0 +1,906 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.pdfbox.jbig2.segments;
+
+import java.io.IOException;
+
+import org.apache.pdfbox.jbig2.Bitmap;
+import org.apache.pdfbox.jbig2.Region;
+import org.apache.pdfbox.jbig2.SegmentHeader;
+import org.apache.pdfbox.jbig2.decoder.arithmetic.ArithmeticDecoder;
+import org.apache.pdfbox.jbig2.decoder.arithmetic.CX;
+import org.apache.pdfbox.jbig2.decoder.mmr.MMRDecompressor;
+import org.apache.pdfbox.jbig2.err.InvalidHeaderValueException;
+import org.apache.pdfbox.jbig2.io.SubInputStream;
+import org.apache.pdfbox.jbig2.util.log.Logger;
+import org.apache.pdfbox.jbig2.util.log.LoggerFactory;
+
+/**
+ * This class represents a generic region segment.<br>
+ * Parsing is done as described in 7.4.5.<br>
+ * Decoding procedure is done as described in 6.2.5.7 and 7.4.6.4.
+ */
+public class GenericRegion implements Region {
+
+  private final Logger log = LoggerFactory.getLogger(GenericRegion.class);
+
+  private SubInputStream subInputStream;
+  private long dataHeaderOffset;
+  private long dataHeaderLength;
+  private long dataOffset;
+  private long dataLength;
+
+  /** Region segment information field, 7.4.1 */
+  private RegionSegmentInformation regionInfo;
+
+  /** Generic region segment flags, 7.4.6.2 */
+  private boolean useExtTemplates;
+  private boolean isTPGDon;
+  private byte gbTemplate;
+  private boolean isMMREncoded;
+
+  /** Generic region segment AT flags, 7.4.6.3 */
+  private short[] gbAtX;
+  private short[] gbAtY;
+  private boolean[] gbAtOverride;
+
+  /**
+   * If true, AT pixels are not on their nominal location and have to be overridden
+   */
+  private boolean override;
+
+  /** Decoded data as pixel values (use row stride/width to wrap line) */
+  private Bitmap regionBitmap;
+
+  private ArithmeticDecoder arithDecoder;
+  private CX cx;
+
+  private MMRDecompressor mmrDecompressor;
+
+  public GenericRegion() {
+  }
+
+  public GenericRegion(final SubInputStream subInputStream) {
+    this.subInputStream = subInputStream;
+    this.regionInfo = new RegionSegmentInformation(subInputStream);
+  }
+
+  private void parseHeader() throws IOException, InvalidHeaderValueException {
+    regionInfo.parseHeader();
+
+    /* Bit 5-7 */
+    subInputStream.readBits(3); // Dirty read...
+
+    /* Bit 4 */
+    if (subInputStream.readBit() == 1) {
+      useExtTemplates = true;
+    }
+
+    /* Bit 3 */
+    if (subInputStream.readBit() == 1) {
+      isTPGDon = true;
+    }
+
+    /* Bit 1-2 */
+    gbTemplate = (byte) (subInputStream.readBits(2) & 0xf);
+
+    /* Bit 0 */
+    if (subInputStream.readBit() == 1) {
+      isMMREncoded = true;
+    }
+
+    if (!isMMREncoded) {
+      final int amountOfGbAt;
+      if (gbTemplate == 0) {
+        if (useExtTemplates) {
+          amountOfGbAt = 12;
+        } else {
+          amountOfGbAt = 4;
+        }
+      } else {
+        amountOfGbAt = 1;
+      }
+
+      readGbAtPixels(amountOfGbAt);
+    }
+
+    /* Segment data structure */
+    computeSegmentDataStructure();
+
+    this.checkInput();
+  }
+
+  private void readGbAtPixels(final int amountOfGbAt) throws IOException {
+    gbAtX = new short[amountOfGbAt];
+    gbAtY = new short[amountOfGbAt];
+
+    for (int i = 0; i < amountOfGbAt; i++) {
+      gbAtX[i] = subInputStream.readByte();
+      gbAtY[i] = subInputStream.readByte();
+    }
+  }
+
+  private void computeSegmentDataStructure() throws IOException {
+    dataOffset = subInputStream.getStreamPosition();
+    dataHeaderLength = dataOffset - dataHeaderOffset;
+    dataLength = subInputStream.length() - dataHeaderLength;
+  }
+
+  private void checkInput() throws InvalidHeaderValueException {
+    if (isMMREncoded) {
+      if (gbTemplate != 0) {
+        log.info("gbTemplate should contain the value 0");
+      }
+    }
+  }
+
+  /**
+   * The procedure is described in 6.2.5.7, page 17.
+   * 
+   * @returns The decoded {@link Bitmap} of this region.
+   */
+  public Bitmap getRegionBitmap() throws IOException {
+    if (null == regionBitmap) {
+
+      if (isMMREncoded) {
+
+        /*
+         * MMR DECODER CALL
+         */
+        if (null == mmrDecompressor) {
+          mmrDecompressor = new MMRDecompressor(regionInfo.getBitmapWidth(), regionInfo.getBitmapHeight(),
+              new SubInputStream(subInputStream, dataOffset, dataLength));
+        }
+
+        /* 6.2.6 */
+        regionBitmap = mmrDecompressor.uncompress();
+
+      } else {
+
+        /*
+         * ARITHMETIC DECODER PROCEDURE for generic region segments
+         */
+
+        updateOverrideFlags();
+
+        /* 6.2.5.7 - 1) */
+        int ltp = 0;
+
+        if (arithDecoder == null) {
+          arithDecoder = new ArithmeticDecoder(subInputStream);
+        }
+        if (cx == null) {
+          cx = new CX(65536, 1);
+        }
+
+        /* 6.2.5.7 - 2) */
+        regionBitmap = new Bitmap(regionInfo.getBitmapWidth(), regionInfo.getBitmapHeight());
+
+        final int paddedWidth = (regionBitmap.getWidth() + 7) & -8;
+
+        /* 6.2.5.7 - 3 */
+        for (int line = 0; line < regionBitmap.getHeight(); line++) {
+
+          /* 6.2.5.7 - 3 b) */
+          if (isTPGDon) {
+            ltp ^= decodeSLTP();
+          }
+
+          /* 6.2.5.7 - 3 c) */
+          if (ltp == 1) {
+            if (line > 0) {
+              copyLineAbove(line);
+            }
+          } else {
+            /* 3 d) */
+            // NOT USED ATM - If corresponding pixel of SKIP bitmap is 0, set
+            // current pixel to 0. Something like that:
+            // if (useSkip) {
+            // for (int i = 1; i < rowstride; i++) {
+            // if (skip[pixel] == 1) {
+            // gbReg[pixel] = 0;
+            // }
+            // pixel++;
+            // }
+            // } else {
+            decodeLine(line, regionBitmap.getWidth(), regionBitmap.getRowStride(), paddedWidth);
+            // }
+          }
+        }
+      }
+    }
+
+    // if (JBIG2ImageReader.DEBUG)
+    // if (header != null && header.getSegmentNr() == 3)
+    // new Testbild(gbReg.getByteArray(), (int) gbReg.getWidth(), (int) gbReg.getHeight(),
+    // gbReg.getRowStride());
+
+    /* 4 */
+    return regionBitmap;
+  }
+
+  private int decodeSLTP() throws IOException {
+    switch (gbTemplate){
+      case 0 :
+        cx.setIndex(0x9b25);
+        break;
+      case 1 :
+        cx.setIndex(0x795);
+        break;
+      case 2 :
+        cx.setIndex(0xe5);
+        break;
+      case 3 :
+        cx.setIndex(0x195);
+        break;
+    }
+    return arithDecoder.decode(cx);
+  }
+
+  private void decodeLine(final int lineNumber, final int width, final int rowStride, final int paddedWidth)
+      throws IOException {
+    final int byteIndex = regionBitmap.getByteIndex(0, lineNumber);
+    final int idx = byteIndex - rowStride;
+
+    switch (gbTemplate){
+      case 0 :
+        if (!useExtTemplates) {
+          decodeTemplate0a(lineNumber, width, rowStride, paddedWidth, byteIndex, idx);
+        } else {
+          decodeTemplate0b(lineNumber, width, rowStride, paddedWidth, byteIndex, idx);
+        }
+        break;
+      case 1 :
+        decodeTemplate1(lineNumber, width, rowStride, paddedWidth, byteIndex, idx);
+        break;
+      case 2 :
+        decodeTemplate2(lineNumber, width, rowStride, paddedWidth, byteIndex, idx);
+        break;
+      case 3 :
+        decodeTemplate3(lineNumber, width, rowStride, paddedWidth, byteIndex, idx);
+        break;
+    }
+  }
+
+  /**
+   * Each pixel gets the value from the corresponding pixel of the row above. Line 0 cannot get
+   * copied values (source will be -1, doesn't exist).
+   * 
+   * @param lineNumber - Coordinate of the row that should be set.
+   */
+  private void copyLineAbove(final int lineNumber) {
+    int targetByteIndex = lineNumber * regionBitmap.getRowStride();
+    int sourceByteIndex = targetByteIndex - regionBitmap.getRowStride();
+
+    for (int i = 0; i < regionBitmap.getRowStride(); i++) {
+      // Get the byte that should be copied and put it into Bitmap
+      regionBitmap.setByte(targetByteIndex++, regionBitmap.getByte(sourceByteIndex++));
+    }
+  }
+
+  private void decodeTemplate0a(final int lineNumber, final int width, final int rowStride, final int paddedWidth,
+      int byteIndex, int idx) throws IOException {
+    int context;
+    int overriddenContext = 0;
+
+    int line1 = 0;
+    int line2 = 0;
+
+    if (lineNumber >= 1) {
+      line1 = regionBitmap.getByteAsInteger(idx);
+    }
+
+    if (lineNumber >= 2) {
+      line2 = regionBitmap.getByteAsInteger(idx - rowStride) << 6;
+    }
+
+    context = (line1 & 0xf0) | (line2 & 0x3800);
+
+    int nextByte;
+    for (int x = 0; x < paddedWidth; x = nextByte) {
+      /* 6.2.5.7 3d */
+      byte result = 0;
+      nextByte = x + 8;
+      final int minorWidth = width - x > 8 ? 8 : width - x;
+
+      if (lineNumber > 0) {
+        line1 = (line1 << 8) | (nextByte < width ? regionBitmap.getByteAsInteger(idx + 1) : 0);
+      }
+
+      if (lineNumber > 1) {
+        line2 = (line2 << 8) | (nextByte < width ? regionBitmap.getByteAsInteger(idx - rowStride + 1) << 6 : 0);
+      }
+
+      for (int minorX = 0; minorX < minorWidth; minorX++) {
+        final int toShift = 7 - minorX;
+        if (override) {
+          overriddenContext = overrideAtTemplate0a(context, (x + minorX), lineNumber, result, minorX, toShift);
+          cx.setIndex(overriddenContext);
+        } else {
+          cx.setIndex(context);
+        }
+
+        int bit = arithDecoder.decode(cx);
+
+        result |= bit << toShift;
+
+        context = ((context & 0x7bf7) << 1) | bit | ((line1 >> toShift) & 0x10) | ((line2 >> toShift) & 0x800);
+      }
+
+      regionBitmap.setByte(byteIndex++, result);
+      idx++;
+    }
+  }
+
+  private void decodeTemplate0b(final int lineNumber, final int width, final int rowStride, final int paddedWidth,
+      int byteIndex, int idx) throws IOException {
+    int context;
+    int overriddenContext = 0;
+
+    int line1 = 0;
+    int line2 = 0;
+
+    if (lineNumber >= 1) {
+      line1 = regionBitmap.getByteAsInteger(idx);
+    }
+
+    if (lineNumber >= 2) {
+      line2 = regionBitmap.getByteAsInteger(idx - rowStride) << 6;
+    }
+
+    context = (line1 & 0xf0) | (line2 & 0x3800);
+
+    int nextByte;
+    for (int x = 0; x < paddedWidth; x = nextByte) {
+      /* 6.2.5.7 3d */
+      byte result = 0;
+      nextByte = x + 8;
+      final int minorWidth = width - x > 8 ? 8 : width - x;
+
+      if (lineNumber > 0) {
+        line1 = (line1 << 8) | (nextByte < width ? regionBitmap.getByteAsInteger(idx + 1) : 0);
+      }
+
+      if (lineNumber > 1) {
+        line2 = (line2 << 8) | (nextByte < width ? regionBitmap.getByteAsInteger(idx - rowStride + 1) << 6 : 0);
+      }
+
+      for (int minorX = 0; minorX < minorWidth; minorX++) {
+        final int toShift = 7 - minorX;
+        if (override) {
+          overriddenContext = overrideAtTemplate0b(context, (x + minorX), lineNumber, result, minorX, toShift);
+          cx.setIndex(overriddenContext);
+        } else {
+          cx.setIndex(context);
+        }
+
+        final int bit = arithDecoder.decode(cx);
+
+        result |= bit << toShift;
+
+        context = ((context & 0x7bf7) << 1) | bit | ((line1 >> toShift) & 0x10) | ((line2 >> toShift) & 0x800);
+      }
+
+      regionBitmap.setByte(byteIndex++, result);
+      idx++;
+    }
+  }
+
+  private void decodeTemplate1(final int lineNumber, int width, final int rowStride, final int paddedWidth,
+      int byteIndex, int idx) throws IOException {
+    int context;
+    int overriddenContext;
+
+    int line1 = 0;
+    int line2 = 0;
+
+    if (lineNumber >= 1) {
+      line1 = regionBitmap.getByteAsInteger(idx);
+    }
+
+    if (lineNumber >= 2) {
+      line2 = regionBitmap.getByteAsInteger(idx - rowStride) << 5;
+    }
+
+    context = ((line1 >> 1) & 0x1f8) | ((line2 >> 1) & 0x1e00);
+
+    int nextByte;
+    for (int x = 0; x < paddedWidth; x = nextByte) {
+      /* 6.2.5.7 3d */
+      byte result = 0;
+      nextByte = x + 8;
+      final int minorWidth = width - x > 8 ? 8 : width - x;
+
+      if (lineNumber >= 1) {
+        line1 = (line1 << 8) | (nextByte < width ? regionBitmap.getByteAsInteger(idx + 1) : 0);
+      }
+
+      if (lineNumber >= 2) {
+        line2 = (line2 << 8) | (nextByte < width ? regionBitmap.getByteAsInteger(idx - rowStride + 1) << 5 : 0);
+      }
+
+      for (int minorX = 0; minorX < minorWidth; minorX++) {
+        if (override) {
+          overriddenContext = overrideAtTemplate1(context, x + minorX, lineNumber, result, minorX);
+          cx.setIndex(overriddenContext);
+        } else {
+          cx.setIndex(context);
+        }
+
+        final int bit = arithDecoder.decode(cx);
+
+        result |= bit << 7 - minorX;
+
+        final int toShift = 8 - minorX;
+        context = ((context & 0xefb) << 1) | bit | ((line1 >> toShift) & 0x8) | ((line2 >> toShift) & 0x200);
+      }
+
+      regionBitmap.setByte(byteIndex++, result);
+      idx++;
+    }
+  }
+
+  private void decodeTemplate2(final int lineNumber, final int width, final int rowStride, final int paddedWidth,
+      int byteIndex, int idx) throws IOException {
+    int context;
+    int overriddenContext;
+
+    int line1 = 0;
+    int line2 = 0;
+
+    if (lineNumber >= 1) {
+      line1 = regionBitmap.getByteAsInteger(idx);
+    }
+
+    if (lineNumber >= 2) {
+      line2 = regionBitmap.getByteAsInteger(idx - rowStride) << 4;
+    }
+
+    context = ((line1 >> 3) & 0x7c) | ((line2 >> 3) & 0x380);
+
+    int nextByte;
+    for (int x = 0; x < paddedWidth; x = nextByte) {
+      /* 6.2.5.7 3d */
+      byte result = 0;
+      nextByte = x + 8;
+      final int minorWidth = width - x > 8 ? 8 : width - x;
+
+      if (lineNumber >= 1) {
+        line1 = (line1 << 8) | (nextByte < width ? regionBitmap.getByteAsInteger(idx + 1) : 0);
+      }
+
+      if (lineNumber >= 2) {
+        line2 = (line2 << 8) | (nextByte < width ? regionBitmap.getByteAsInteger(idx - rowStride + 1) << 4 : 0);
+      }
+
+      for (int minorX = 0; minorX < minorWidth; minorX++) {
+
+        if (override) {
+          overriddenContext = overrideAtTemplate2(context, x + minorX, lineNumber, result, minorX);
+          cx.setIndex(overriddenContext);
+        } else {
+          cx.setIndex(context);
+        }
+
+        final int bit = arithDecoder.decode(cx);
+
+        result |= bit << (7 - minorX);
+
+        final int toShift = 10 - minorX;
+        context = ((context & 0x1bd) << 1) | bit | ((line1 >> toShift) & 0x4) | ((line2 >> toShift) & 0x80);
+      }
+
+      regionBitmap.setByte(byteIndex++, result);
+      idx++;
+    }
+  }
+
+  private void decodeTemplate3(final int lineNumber, final int width, final int rowStride, final int paddedWidth,
+      int byteIndex, int idx) throws IOException {
+    int context;
+    int overriddenContext;
+
+    int line1 = 0;
+
+    if (lineNumber >= 1) {
+      line1 = regionBitmap.getByteAsInteger(idx);
+    }
+
+    context = (line1 >> 1) & 0x70;
+
+    int nextByte;
+    for (int x = 0; x < paddedWidth; x = nextByte) {
+      /* 6.2.5.7 3d */
+      byte result = 0;
+      nextByte = x + 8;
+      final int minorWidth = width - x > 8 ? 8 : width - x;
+
+      if (lineNumber >= 1) {
+        line1 = (line1 << 8) | (nextByte < width ? regionBitmap.getByteAsInteger(idx + 1) : 0);
+      }
+
+      for (int minorX = 0; minorX < minorWidth; minorX++) {
+
+        if (override) {
+          overriddenContext = overrideAtTemplate3(context, x + minorX, lineNumber, result, minorX);
+          cx.setIndex(overriddenContext);
+        } else {
+          cx.setIndex(context);
+        }
+
+        final int bit = arithDecoder.decode(cx);
+
+        result |= bit << (7 - minorX);
+        context = ((context & 0x1f7) << 1) | bit | ((line1 >> (8 - minorX)) & 0x010);
+      }
+
+      regionBitmap.setByte(byteIndex++, result);
+      idx++;
+    }
+  }
+
+  private void updateOverrideFlags() {
+    if (gbAtX == null || gbAtY == null) {
+      log.info("AT pixels not set");
+      return;
+    }
+
+    if (gbAtX.length != gbAtY.length) {
+      log.info("AT pixel inconsistent, amount of x pixels: " + gbAtX.length + ", amount of y pixels:" + gbAtY.length);
+      return;
+    }
+
+    gbAtOverride = new boolean[gbAtX.length];
+
+    switch (gbTemplate){
+      case 0 :
+        if (!useExtTemplates) {
+          if (gbAtX[0] != 3 || gbAtY[0] != -1)
+            setOverrideFlag(0);
+
+          if (gbAtX[1] != -3 || gbAtY[1] != -1)
+            setOverrideFlag(1);
+
+          if (gbAtX[2] != 2 || gbAtY[2] != -2)
+            setOverrideFlag(2);
+
+          if (gbAtX[3] != -2 || gbAtY[3] != -2)
+            setOverrideFlag(3);
+
+        } else {
+          if (gbAtX[0] != -2 || gbAtY[0] != 0)
+            setOverrideFlag(0);
+
+          if (gbAtX[1] != 0 || gbAtY[1] != -2)
+            setOverrideFlag(1);
+
+          if (gbAtX[2] != -2 || gbAtY[2] != -1)
+            setOverrideFlag(2);
+
+          if (gbAtX[3] != -1 || gbAtY[3] != -2)
+            setOverrideFlag(3);
+
+          if (gbAtX[4] != 1 || gbAtY[4] != -2)
+            setOverrideFlag(4);
+
+          if (gbAtX[5] != 2 || gbAtY[5] != -1)
+            setOverrideFlag(5);
+
+          if (gbAtX[6] != -3 || gbAtY[6] != 0)
+            setOverrideFlag(6);
+
+          if (gbAtX[7] != -4 || gbAtY[7] != 0)
+            setOverrideFlag(7);
+
+          if (gbAtX[8] != 2 || gbAtY[8] != -2)
+            setOverrideFlag(8);
+
+          if (gbAtX[9] != 3 || gbAtY[9] != -1)
+            setOverrideFlag(9);
+
+          if (gbAtX[10] != -2 || gbAtY[10] != -2)
+            setOverrideFlag(10);
+
+          if (gbAtX[11] != -3 || gbAtY[11] != -1)
+            setOverrideFlag(11);
+        }
+        break;
+      case 1 :
+        if (gbAtX[0] != 3 || gbAtY[0] != -1)
+          setOverrideFlag(0);
+        break;
+      case 2 :
+        if (gbAtX[0] != 2 || gbAtY[0] != -1)
+          setOverrideFlag(0);
+        break;
+      case 3 :
+        if (gbAtX[0] != 2 || gbAtY[0] != -1)
+          setOverrideFlag(0);
+        break;
+    }
+
+  }
+
+  private void setOverrideFlag(final int index) {
+    gbAtOverride[index] = true;
+    override = true;
+  }
+
+  private int overrideAtTemplate0a(int context, final int x, final int y, final int result, final int minorX,
+      final int toShift) throws IOException {
+    if (gbAtOverride[0]) {
+      context &= 0xffef;
+      if (gbAtY[0] == 0 && gbAtX[0] >= -minorX)
+        context |= (result >> (toShift - gbAtX[0]) & 0x1) << 4;
+      else
+        context |= getPixel(x + gbAtX[0], y + gbAtY[0]) << 4;
+    }
+
+    if (gbAtOverride[1]) {
+      context &= 0xfbff;
+      if (gbAtY[1] == 0 && gbAtX[1] >= -minorX)
+        context |= (result >> (toShift - gbAtX[1]) & 0x1) << 10;
+      else
+        context |= getPixel(x + gbAtX[1], y + gbAtY[1]) << 10;
+    }
+
+    if (gbAtOverride[2]) {
+      context &= 0xf7ff;
+      if (gbAtY[2] == 0 && gbAtX[2] >= -minorX)
+        context |= (result >> (toShift - gbAtX[2]) & 0x1) << 11;
+      else
+        context |= getPixel(x + gbAtX[2], y + gbAtY[2]) << 11;
+    }
+
+    if (gbAtOverride[3]) {
+      context &= 0x7fff;
+      if (gbAtY[3] == 0 && gbAtX[3] >= -minorX)
+        context |= (result >> (toShift - gbAtX[3]) & 0x1) << 15;
+      else
+        context |= getPixel(x + gbAtX[3], y + gbAtY[3]) << 15;
+    }
+    return context;
+  }
+
+  private int overrideAtTemplate0b(int context, final int x, final int y, final int result, final int minorX,
+      final int toShift) throws IOException {
+    if (gbAtOverride[0]) {
+      context &= 0xfffd;
+      if (gbAtY[0] == 0 && gbAtX[0] >= -minorX)
+        context |= (result >> (toShift - gbAtX[0]) & 0x1) << 1;
+      else
+        context |= getPixel(x + gbAtX[0], y + gbAtY[0]) << 1;
+    }
+
+    if (gbAtOverride[1]) {
+      context &= 0xdfff;
+      if (gbAtY[1] == 0 && gbAtX[1] >= -minorX)
+        context |= (result >> (toShift - gbAtX[1]) & 0x1) << 13;
+      else
+        context |= getPixel(x + gbAtX[1], y + gbAtY[1]) << 13;
+    }
+    if (gbAtOverride[2]) {
+      context &= 0xfdff;
+      if (gbAtY[2] == 0 && gbAtX[2] >= -minorX)
+        context |= (result >> (toShift - gbAtX[2]) & 0x1) << 9;
+      else
+        context |= getPixel(x + gbAtX[2], y + gbAtY[2]) << 9;
+    }
+    if (gbAtOverride[3]) {
+      context &= 0xbfff;
+      if (gbAtY[3] == 0 && gbAtX[3] >= -minorX)
+        context |= (result >> (toShift - gbAtX[3]) & 0x1) << 14;
+      else
+        context |= getPixel(x + gbAtX[3], y + gbAtY[3]) << 14;
+    }
+    if (gbAtOverride[4]) {
+      context &= 0xefff;
+      if (gbAtY[4] == 0 && gbAtX[4] >= -minorX)
+        context |= (result >> (toShift - gbAtX[4]) & 0x1) << 12;
+      else
+        context |= getPixel(x + gbAtX[4], y + gbAtY[4]) << 12;
+    }
+    if (gbAtOverride[5]) {
+      context &= 0xffdf;
+      if (gbAtY[5] == 0 && gbAtX[5] >= -minorX)
+        context |= (result >> (toShift - gbAtX[5]) & 0x1) << 5;
+      else
+        context |= getPixel(x + gbAtX[5], y + gbAtY[5]) << 5;
+    }
+    if (gbAtOverride[6]) {
+      context &= 0xfffb;
+      if (gbAtY[6] == 0 && gbAtX[6] >= -minorX)
+        context |= (result >> (toShift - gbAtX[6]) & 0x1) << 2;
+      else
+        context |= getPixel(x + gbAtX[6], y + gbAtY[6]) << 2;
+    }
+    if (gbAtOverride[7]) {
+      context &= 0xfff7;
+      if (gbAtY[7] == 0 && gbAtX[7] >= -minorX)
+        context |= (result >> (toShift - gbAtX[7]) & 0x1) << 3;
+      else
+        context |= getPixel(x + gbAtX[7], y + gbAtY[7]) << 3;
+    }
+    if (gbAtOverride[8]) {
+      context &= 0xf7ff;
+      if (gbAtY[8] == 0 && gbAtX[8] >= -minorX)
+        context |= (result >> (toShift - gbAtX[8]) & 0x1) << 11;
+      else
+        context |= getPixel(x + gbAtX[8], y + gbAtY[8]) << 11;
+    }
+    if (gbAtOverride[9]) {
+      context &= 0xffef;
+      if (gbAtY[9] == 0 && gbAtX[9] >= -minorX)
+        context |= (result >> (toShift - gbAtX[9]) & 0x1) << 4;
+      else
+        context |= getPixel(x + gbAtX[9], y + gbAtY[9]) << 4;
+    }
+    if (gbAtOverride[10]) {
+      context &= 0x7fff;
+      if (gbAtY[10] == 0 && gbAtX[10] >= -minorX)
+        context |= (result >> (toShift - gbAtX[10]) & 0x1) << 15;
+      else
+        context |= getPixel(x + gbAtX[10], y + gbAtY[10]) << 15;
+    }
+    if (gbAtOverride[11]) {
+      context &= 0xfdff;
+      if (gbAtY[11] == 0 && gbAtX[11] >= -minorX)
+        context |= (result >> (toShift - gbAtX[11]) & 0x1) << 10;
+      else
+        context |= getPixel(x + gbAtX[11], y + gbAtY[11]) << 10;
+    }
+
+    return context;
+  }
+
+  private int overrideAtTemplate1(int context, final int x, final int y, final int result, final int minorX)
+      throws IOException {
+    context &= 0x1ff7;
+    if (gbAtY[0] == 0 && gbAtX[0] >= -minorX)
+      return (context | (result >> (7 - (minorX + gbAtX[0])) & 0x1) << 3);
+    else
+      return (context | getPixel(x + gbAtX[0], y + gbAtY[0]) << 3);
+  }
+
+  private int overrideAtTemplate2(int context, final int x, final int y, final int result, final int minorX)
+      throws IOException {
+    context &= 0x3fb;
+    if (gbAtY[0] == 0 && gbAtX[0] >= -minorX)
+      return (context | (result >> (7 - (minorX + gbAtX[0])) & 0x1) << 2);
+    else
+      return (context | getPixel(x + gbAtX[0], y + gbAtY[0]) << 2);
+  }
+
+  private int overrideAtTemplate3(int context, final int x, final int y, final int result, final int minorX)
+      throws IOException {
+    context &= 0x3ef;
+    if (gbAtY[0] == 0 && gbAtX[0] >= -minorX)
+      return (context | (result >> (7 - (minorX + gbAtX[0])) & 0x1) << 4);
+    else
+      return (context | getPixel(x + gbAtX[0], y + gbAtY[0]) << 4);
+  }
+
+  private byte getPixel(final int x, final int y) throws IOException {
+    if (x < 0 || x >= regionBitmap.getWidth())
+      return 0;
+
+    if (y < 0 || y >= regionBitmap.getHeight())
+      return 0;
+
+    return regionBitmap.getPixel(x, y);
+  }
+
+  /**
+   * Used by {@link SymbolDictionary}.
+   */
+  protected void setParameters(final boolean isMMREncoded, final long dataOffset, final long dataLength, final int gbh,
+      final int gbw) {
+    this.isMMREncoded = isMMREncoded;
+    this.dataOffset = dataOffset;
+    this.dataLength = dataLength;
+    this.regionInfo.setBitmapHeight(gbh);
+    this.regionInfo.setBitmapWidth(gbw);
+
+    this.mmrDecompressor = null;
+    resetBitmap();
+  }
+
+  /**
+   * Used by {@link SymbolDictionary}.
+   */
+  protected void setParameters(final boolean isMMREncoded, final byte sdTemplate, final boolean isTPGDon,
+      final boolean useSkip, final short[] sdATX, final short[] sdATY, final int symWidth, final int hcHeight,
+      final CX cx, final ArithmeticDecoder arithmeticDecoder) {
+    this.isMMREncoded = isMMREncoded;
+    this.gbTemplate = sdTemplate;
+    this.isTPGDon = isTPGDon;
+    this.gbAtX = sdATX;
+    this.gbAtY = sdATY;
+    this.regionInfo.setBitmapWidth(symWidth);
+    this.regionInfo.setBitmapHeight(hcHeight);
+    if (null != cx)
+      this.cx = cx;
+    if (null != arithmeticDecoder)
+      this.arithDecoder = arithmeticDecoder;
+
+    this.mmrDecompressor = null;
+    resetBitmap();
+  }
+
+  /**
+   * Used by {@link PatternDictionary} and {@link HalftoneRegion}.
+   */
+  protected void setParameters(final boolean isMMREncoded, final long dataOffset, final long dataLength, final int gbh,
+      final int gbw, final byte gbTemplate, final boolean isTPGDon, final boolean useSkip, final short[] gbAtX,
+      final short[] gbAtY) {
+    this.dataOffset = dataOffset;
+    this.dataLength = dataLength;
+
+    this.regionInfo = new RegionSegmentInformation();
+    this.regionInfo.setBitmapHeight(gbh);
+    this.regionInfo.setBitmapWidth(gbw);
+    this.gbTemplate = gbTemplate;
+
+    this.isMMREncoded = isMMREncoded;
+    this.isTPGDon = isTPGDon;
+    this.gbAtX = gbAtX;
+    this.gbAtY = gbAtY;
+  }
+
+  /**
+   * Simply sets the memory-critical bitmap of this region to {@code null}.
+   */
+  protected void resetBitmap() {
+    this.regionBitmap = null;
+  }
+
+  public void init(final SegmentHeader header, final SubInputStream sis) throws InvalidHeaderValueException,
+      IOException {
+    this.subInputStream = sis;
+    this.regionInfo = new RegionSegmentInformation(subInputStream);
+    parseHeader();
+  }
+
+  public RegionSegmentInformation getRegionInfo() {
+    return regionInfo;
+  }
+
+  protected boolean useExtTemplates() {
+    return useExtTemplates;
+  }
+
+  protected boolean isTPGDon() {
+    return isTPGDon;
+  }
+
+  protected byte getGbTemplate() {
+    return gbTemplate;
+  }
+
+  protected boolean isMMREncoded() {
+    return isMMREncoded;
+  }
+
+  protected short[] getGbAtX() {
+    return gbAtX;
+  }
+
+  protected short[] getGbAtY() {
+    return gbAtY;
+  }
+}

http://git-wip-us.apache.org/repos/asf/pdfbox-jbig2/blob/4619d28b/src/main/java/org/apache/pdfbox/jbig2/segments/HalftoneRegion.java
----------------------------------------------------------------------
diff --git a/src/main/java/org/apache/pdfbox/jbig2/segments/HalftoneRegion.java b/src/main/java/org/apache/pdfbox/jbig2/segments/HalftoneRegion.java
new file mode 100644
index 0000000..384f09f
--- /dev/null
+++ b/src/main/java/org/apache/pdfbox/jbig2/segments/HalftoneRegion.java
@@ -0,0 +1,406 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.pdfbox.jbig2.segments;
+
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.Arrays;
+
+import org.apache.pdfbox.jbig2.Bitmap;
+import org.apache.pdfbox.jbig2.Region;
+import org.apache.pdfbox.jbig2.SegmentHeader;
+import org.apache.pdfbox.jbig2.err.InvalidHeaderValueException;
+import org.apache.pdfbox.jbig2.image.Bitmaps;
+import org.apache.pdfbox.jbig2.io.SubInputStream;
+import org.apache.pdfbox.jbig2.util.CombinationOperator;
+import org.apache.pdfbox.jbig2.util.log.Logger;
+import org.apache.pdfbox.jbig2.util.log.LoggerFactory;
+
+/**
+ * This class represents the data of segment type "Halftone region". Parsing is described in 7.4.5,
+ * page 67. Decoding procedure in 6.6.5 and 7.4.5.2.
+ */
+public class HalftoneRegion implements Region {
+
+  private final Logger log = LoggerFactory.getLogger(HalftoneRegion.class);
+
+  private SubInputStream subInputStream;
+  private SegmentHeader segmentHeader;
+  private long dataHeaderOffset;
+  private long dataHeaderLength;
+  private long dataOffset;
+  private long dataLength;
+
+  /** Region segment information field, 7.4.1 */
+  private RegionSegmentInformation regionInfo;
+
+  /** Halftone segment information field, 7.4.5.1.1 */
+  private byte hDefaultPixel;
+  private CombinationOperator hCombinationOperator;
+  private boolean hSkipEnabled;
+  private byte hTemplate;
+  private boolean isMMREncoded;
+
+  /** Halftone grid position and size, 7.4.5.1.2 */
+  /** Width of the gray-scale image, 7.4.5.1.2.1 */
+  private int hGridWidth;
+  /** Height of the gray-scale image, 7.4.5.1.2.2 */
+  private int hGridHeight;
+  /** Horizontal offset of the grid, 7.4.5.1.2.3 */
+  private int hGridX;
+  /** Vertical offset of the grid, 7.4.5.1.2.4 */
+  private int hGridY;
+
+  /** Halftone grid vector, 7.4.5.1.3 */
+  /** Horizontal coordinate of the halftone grid vector, 7.4.5.1.3.1 */
+  private int hRegionX;
+  /** Vertical coordinate of the halftone grod vector, 7.4.5.1.3.2 */
+  private int hRegionY;
+
+  /** Decoded data */
+  private Bitmap halftoneRegionBitmap;
+
+  /**
+   * Previously decoded data from other regions or dictionaries, stored to use as patterns in this
+   * region.
+   */
+  private ArrayList<Bitmap> patterns;
+
+  public HalftoneRegion() {
+  }
+
+  public HalftoneRegion(final SubInputStream subInputStream) {
+    this.subInputStream = subInputStream;
+    this.regionInfo = new RegionSegmentInformation(subInputStream);
+  }
+
+  public HalftoneRegion(final SubInputStream subInputStream, final SegmentHeader segmentHeader) {
+    this.subInputStream = subInputStream;
+    this.segmentHeader = segmentHeader;
+    this.regionInfo = new RegionSegmentInformation(subInputStream);
+  }
+
+  private void parseHeader() throws IOException, InvalidHeaderValueException {
+    regionInfo.parseHeader();
+
+    /* Bit 7 */
+    hDefaultPixel = (byte) subInputStream.readBit();
+
+    /* Bit 4-6 */
+    hCombinationOperator = CombinationOperator.translateOperatorCodeToEnum((short) (subInputStream.readBits(3) & 0xf));
+
+    /* Bit 3 */
+    if (subInputStream.readBit() == 1) {
+      hSkipEnabled = true;
+    }
+
+    /* Bit 1-2 */
+    hTemplate = (byte) (subInputStream.readBits(2) & 0xf);
+
+    /* Bit 0 */
+    if (subInputStream.readBit() == 1) {
+      isMMREncoded = true;
+    }
+
+    hGridWidth = (int) (subInputStream.readBits(32) & 0xffffffff);
+    hGridHeight = (int) (subInputStream.readBits(32) & 0xffffffff);
+
+    hGridX = (int) subInputStream.readBits(32);
+    hGridY = (int) subInputStream.readBits(32);
+
+    hRegionX = (int) subInputStream.readBits(16) & 0xffff;
+    hRegionY = (int) subInputStream.readBits(16) & 0xffff;
+
+    /* Segment data structure */
+    computeSegmentDataStructure();
+
+    this.checkInput();
+  }
+
+  private void computeSegmentDataStructure() throws IOException {
+    dataOffset = subInputStream.getStreamPosition();
+    dataHeaderLength = dataOffset - dataHeaderOffset;
+    dataLength = subInputStream.length() - dataHeaderLength;
+  }
+
+  private void checkInput() throws InvalidHeaderValueException {
+    if (isMMREncoded) {
+      if (hTemplate != 0) {
+        log.info("hTemplate = " + hTemplate + " (should contain the value 0)");
+      }
+
+      if (hSkipEnabled) {
+        log.info("hSkipEnabled 0 " + hSkipEnabled + " (should contain the value false)");
+      }
+    }
+  }
+
+  /**
+   * The procedure is described in JBIG2 ISO standard, 6.6.5.
+   * 
+   * @returns The decoded {@link Bitmap} of this region.
+   */
+  public Bitmap getRegionBitmap() throws IOException, InvalidHeaderValueException {
+    if (null == halftoneRegionBitmap) {
+
+      /* 6.6.5, page 40 */
+      /* 1) */
+      halftoneRegionBitmap = new Bitmap(regionInfo.getBitmapWidth(), regionInfo.getBitmapHeight());
+
+      if (patterns == null) {
+        patterns = getPatterns();
+      }
+
+      if (hDefaultPixel == 1) {
+        Arrays.fill(halftoneRegionBitmap.getByteArray(), (byte) 0xff);
+      }
+
+      /* 2) */
+      /*
+       * 6.6.5.1 Computing hSkip - At the moment SKIP is not used... we are not able to test it.
+       */
+      // Bitmap hSkip;
+      // if (hSkipEnabled) {
+      // int hPatternHeight = (int) hPats.get(0).getHeight();
+      // int hPatternWidth = (int) hPats.get(0).getWidth();
+      // TODO: Set or get pattern width and height from referred
+      // pattern segments. The method is called like this:
+      // hSkip = computeHSkip(hPatternHeight, hPatternWidth);
+      // }
+
+      /* 3) */
+      final int bitsPerValue = (int) Math.ceil(Math.log(patterns.size()) / Math.log(2));
+
+      /* 4) */
+      final int[][] grayScaleValues = grayScaleDecoding(bitsPerValue);
+
+      /* 5), rendering the pattern, described in 6.6.5.2 */
+      renderPattern(grayScaleValues);
+    }
+    /* 6) */
+    return halftoneRegionBitmap;
+  }
+
+  /**
+   * This method draws the pattern into the region bitmap ({@code htReg}), as described in 6.6.5.2,
+   * page 42
+   */
+  private void renderPattern(final int[][] grayScaleValues) {
+    int x = 0, y = 0;
+
+    // 1)
+    for (int m = 0; m < hGridHeight; m++) {
+      // a)
+      for (int n = 0; n < hGridWidth; n++) {
+        // i)
+        x = computeX(m, n);
+        y = computeY(m, n);
+
+        // ii)
+        final Bitmap patternBitmap = patterns.get(grayScaleValues[m][n]);
+        Bitmaps.blit(patternBitmap, halftoneRegionBitmap, (x + hGridX), (y + hGridY), hCombinationOperator);
+      }
+    }
+  }
+
+  /**
+   * @throws IOException
+   * @throws InvalidHeaderValueException
+   * 
+   */
+  private ArrayList<Bitmap> getPatterns() throws InvalidHeaderValueException, IOException {
+    final ArrayList<Bitmap> patterns = new ArrayList<Bitmap>();
+
+    for (SegmentHeader s : segmentHeader.getRtSegments()) {
+      final PatternDictionary patternDictionary = (PatternDictionary) s.getSegmentData();
+      patterns.addAll(patternDictionary.getDictionary());
+    }
+
+    return patterns;
+  }
+
+  /**
+   * Gray-scale image decoding procedure is special for halftone region decoding and is described in
+   * Annex C.5 on page 98.
+   */
+  private int[][] grayScaleDecoding(final int bitsPerValue) throws IOException {
+
+    short[] gbAtX = null;
+    short[] gbAtY = null;
+
+    if (!isMMREncoded) {
+      gbAtX = new short[4];
+      gbAtY = new short[4];
+      // Set AT pixel values
+      if (hTemplate <= 1)
+        gbAtX[0] = 3;
+      else if (hTemplate >= 2)
+        gbAtX[0] = 2;
+
+      gbAtY[0] = -1;
+      gbAtX[1] = -3;
+      gbAtY[1] = -1;
+      gbAtX[2] = 2;
+      gbAtY[2] = -2;
+      gbAtX[3] = -2;
+      gbAtY[3] = -2;
+    }
+
+    Bitmap[] grayScalePlanes = new Bitmap[bitsPerValue];
+
+    // 1)
+    GenericRegion genericRegion = new GenericRegion(subInputStream);
+    genericRegion.setParameters(isMMREncoded, dataOffset, dataLength, hGridHeight, hGridWidth, hTemplate, false,
+        hSkipEnabled, gbAtX, gbAtY);
+
+    // 2)
+    int j = bitsPerValue - 1;
+
+    grayScalePlanes[j] = genericRegion.getRegionBitmap();
+
+    while (j > 0) {
+      j--;
+      genericRegion.resetBitmap();
+      // 3) a)
+      grayScalePlanes[j] = genericRegion.getRegionBitmap();
+      // 3) b)
+      grayScalePlanes = combineGrayScalePlanes(grayScalePlanes, j);
+    }
+
+    // 4)
+    return computeGrayScaleValues(grayScalePlanes, bitsPerValue);
+  }
+
+  private Bitmap[] combineGrayScalePlanes(Bitmap[] grayScalePlanes, int j) {
+    int byteIndex = 0;
+    for (int y = 0; y < grayScalePlanes[j].getHeight(); y++) {
+
+      for (int x = 0; x < grayScalePlanes[j].getWidth(); x += 8) {
+        final byte newValue = grayScalePlanes[j + 1].getByte(byteIndex);
+        final byte oldValue = grayScalePlanes[j].getByte(byteIndex);
+
+        grayScalePlanes[j].setByte(byteIndex++, Bitmaps.combineBytes(oldValue, newValue, CombinationOperator.XOR));
+      }
+    }
+    return grayScalePlanes;
+  }
+
+  private int[][] computeGrayScaleValues(final Bitmap[] grayScalePlanes, final int bitsPerValue) {
+    // Gray-scale decoding procedure, page 98
+    final int[][] grayScaleValues = new int[hGridHeight][hGridWidth];
+
+    // 4)
+    for (int y = 0; y < hGridHeight; y++) {
+      for (int x = 0; x < hGridWidth; x += 8) {
+        final int minorWidth = hGridWidth - x > 8 ? 8 : hGridWidth - x;
+        int byteIndex = grayScalePlanes[0].getByteIndex(x, y);
+
+        for (int minorX = 0; minorX < minorWidth; minorX++) {
+          final int i = minorX + x;
+          grayScaleValues[y][i] = 0;
+
+          for (int j = 0; j < bitsPerValue; j++) {
+            grayScaleValues[y][i] += ((grayScalePlanes[j].getByte(byteIndex) >> (7 - i & 7)) & 1) * (1 << j);
+          }
+        }
+      }
+    }
+    return grayScaleValues;
+  }
+
+  private int computeX(final int m, final int n) {
+    return shiftAndFill((hGridX + m * hRegionY + n * hRegionX));
+  }
+
+  private int computeY(final int m, final int n) {
+    return shiftAndFill((hGridY + m * hRegionX - n * hRegionY));
+  }
+
+  private int shiftAndFill(int value) {
+    // shift value by 8 and let the leftmost 8 bits be 0
+    value >>= 8;
+
+    if (value < 0) {
+      // fill the leftmost 8 bits with 1
+      final int bitPosition = (int) (Math.log(Integer.highestOneBit(value)) / Math.log(2));
+
+      for (int i = 1; i < 31 - bitPosition; i++) {
+        // bit flip
+        value |= 1 << (31 - i);
+      }
+    }
+
+    return value;
+  }
+
+  public void init(final SegmentHeader header, final SubInputStream sis) throws InvalidHeaderValueException,
+      IOException {
+    this.segmentHeader = header;
+    this.subInputStream = sis;
+    this.regionInfo = new RegionSegmentInformation(subInputStream);
+    parseHeader();
+  }
+
+  public CombinationOperator getCombinationOperator() {
+    return hCombinationOperator;
+  }
+
+  public RegionSegmentInformation getRegionInfo() {
+    return regionInfo;
+  }
+
+  protected byte getHTemplate() {
+    return hTemplate;
+  }
+
+  protected boolean isHSkipEnabled() {
+    return hSkipEnabled;
+  }
+
+  protected boolean isMMREncoded() {
+    return isMMREncoded;
+  }
+
+  protected int getHGridWidth() {
+    return hGridWidth;
+  }
+
+  protected int getHGridHeight() {
+    return hGridHeight;
+  }
+
+  protected int getHGridX() {
+    return hGridX;
+  }
+
+  protected int getHGridY() {
+    return hGridY;
+  }
+
+  protected int getHRegionX() {
+    return hRegionX;
+  }
+
+  protected int getHRegionY() {
+    return hRegionY;
+  }
+
+  protected byte getHDefaultPixel() {
+    return hDefaultPixel;
+  }
+}

http://git-wip-us.apache.org/repos/asf/pdfbox-jbig2/blob/4619d28b/src/main/java/org/apache/pdfbox/jbig2/segments/PageInformation.java
----------------------------------------------------------------------
diff --git a/src/main/java/org/apache/pdfbox/jbig2/segments/PageInformation.java b/src/main/java/org/apache/pdfbox/jbig2/segments/PageInformation.java
new file mode 100644
index 0000000..513dcca
--- /dev/null
+++ b/src/main/java/org/apache/pdfbox/jbig2/segments/PageInformation.java
@@ -0,0 +1,226 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.pdfbox.jbig2.segments;
+
+import java.io.IOException;
+
+import org.apache.pdfbox.jbig2.SegmentData;
+import org.apache.pdfbox.jbig2.SegmentHeader;
+import org.apache.pdfbox.jbig2.err.InvalidHeaderValueException;
+import org.apache.pdfbox.jbig2.io.SubInputStream;
+import org.apache.pdfbox.jbig2.util.CombinationOperator;
+import org.apache.pdfbox.jbig2.util.log.Logger;
+import org.apache.pdfbox.jbig2.util.log.LoggerFactory;
+
+/**
+ * This class represents the segment type "Page information", 7.4.8 (page 73).
+ */
+public class PageInformation implements SegmentData {
+
+  private final Logger log = LoggerFactory.getLogger(PageInformation.class);
+
+  private SubInputStream subInputStream;
+
+  /** Page bitmap width, four byte, 7.4.8.1 */
+  private int bitmapWidth;
+
+  /** Page bitmap height, four byte, 7.4.8.2 */
+  private int bitmapHeight;
+
+  /** Page X resolution, four byte, 7.4.8.3 */
+  private int resolutionX;
+
+  /** Page Y resolution, four byte, 7.4.8.4 */
+  private int resolutionY;
+
+  /** Page segment flags, one byte, 7.4.8.5 */
+  private boolean combinationOperatorOverrideAllowed;
+  private CombinationOperator combinationOperator;
+  private boolean requiresAuxiliaryBuffer;
+  private short defaultPixelValue;
+  private boolean mightContainRefinements;
+  private boolean isLossless;
+
+  /** Page striping information, two byte, 7.4.8.6 */
+  private boolean isStriped;
+  private short maxStripeSize;
+
+  private void parseHeader() throws IOException, InvalidHeaderValueException {
+
+    readWidthAndHeight();
+    readResolution();
+
+    /* Bit 7 */
+    subInputStream.readBit(); // dirty read
+
+    /* Bit 6 */
+    readCombinationOperatorOverrideAllowed();
+
+    /* Bit 5 */
+    readRequiresAuxiliaryBuffer();
+
+    /* Bit 3-4 */
+    readCombinationOperator();
+
+    /* Bit 2 */
+    readDefaultPixelvalue();
+
+    /* Bit 1 */
+    readContainsRefinement();
+
+    /* Bit 0 */
+    readIsLossless();
+
+    /* Bit 15 */
+    readIsStriped();
+
+    /* Bit 0-14 */
+    readMaxStripeSize();
+
+    this.checkInput();
+
+  }
+
+  private void readResolution() throws IOException {
+    resolutionX = (int) subInputStream.readBits(32) & 0xffffffff;
+    resolutionY = (int) subInputStream.readBits(32) & 0xffffffff;
+  }
+
+  private void checkInput() throws InvalidHeaderValueException {
+    if (bitmapHeight == 0xffffffffL)
+      if (!isStriped)
+        log.info("isStriped should contaion the value true");
+  }
+
+  private void readCombinationOperatorOverrideAllowed() throws IOException {
+    /* Bit 6 */
+    if (subInputStream.readBit() == 1) {
+      combinationOperatorOverrideAllowed = true;
+    }
+  }
+
+  private void readRequiresAuxiliaryBuffer() throws IOException {
+    /* Bit 5 */
+    if (subInputStream.readBit() == 1) {
+      requiresAuxiliaryBuffer = true;
+    }
+  }
+
+  private void readCombinationOperator() throws IOException {
+    /* Bit 3-4 */
+    combinationOperator = CombinationOperator.translateOperatorCodeToEnum((short) (subInputStream.readBits(2) & 0xf));
+  }
+
+  private void readDefaultPixelvalue() throws IOException {
+    /* Bit 2 */
+    defaultPixelValue = (short) subInputStream.readBit();
+  }
+
+  private void readContainsRefinement() throws IOException {
+    /* Bit 1 */
+    if (subInputStream.readBit() == 1) {
+      mightContainRefinements = true;
+    }
+  }
+
+  private void readIsLossless() throws IOException {
+    /* Bit 0 */
+    if (subInputStream.readBit() == 1) {
+      isLossless = true;
+    }
+  }
+
+  private void readIsStriped() throws IOException {
+    /* Bit 15 */
+    if (subInputStream.readBit() == 1) {
+      isStriped = true;
+    }
+  }
+
+  private void readMaxStripeSize() throws IOException {
+    /* Bit 0-14 */
+    maxStripeSize = (short) (subInputStream.readBits(15) & 0xffff);
+  }
+
+  private void readWidthAndHeight() throws IOException {
+    bitmapWidth = (int) subInputStream.readBits(32); // & 0xffffffff;
+    bitmapHeight = (int) subInputStream.readBits(32); // & 0xffffffff;
+  }
+
+  public void init(final SegmentHeader header, final SubInputStream sis) throws InvalidHeaderValueException, IOException {
+    subInputStream = sis;
+
+    parseHeader();
+  }
+
+  public int getWidth() {
+    return bitmapWidth;
+  }
+
+  public int getHeight() {
+    return bitmapHeight;
+  }
+
+  public int getResolutionX() {
+    return resolutionX;
+  }
+
+  public int getResolutionY() {
+    return resolutionY;
+  }
+
+  public short getDefaultPixelValue() {
+    return defaultPixelValue;
+  }
+
+  public boolean isCombinationOperatorOverrideAllowed() {
+    return combinationOperatorOverrideAllowed;
+  }
+
+  public CombinationOperator getCombinationOperator() {
+    return combinationOperator;
+  }
+
+  public boolean isStriped() {
+    return isStriped;
+  }
+
+  public short getMaxStripeSize() {
+    return maxStripeSize;
+  }
+
+  public boolean isAuxiliaryBufferRequired() {
+    return requiresAuxiliaryBuffer;
+  }
+
+  public boolean mightContainRefinements() {
+    return mightContainRefinements;
+  }
+
+  public boolean isLossless() {
+    return isLossless;
+  }
+
+  protected int getBitmapWidth() {
+    return bitmapWidth;
+  }
+
+  protected int getBitmapHeight() {
+    return bitmapHeight;
+  }
+}

http://git-wip-us.apache.org/repos/asf/pdfbox-jbig2/blob/4619d28b/src/main/java/org/apache/pdfbox/jbig2/segments/PatternDictionary.java
----------------------------------------------------------------------
diff --git a/src/main/java/org/apache/pdfbox/jbig2/segments/PatternDictionary.java b/src/main/java/org/apache/pdfbox/jbig2/segments/PatternDictionary.java
new file mode 100644
index 0000000..160d8fa
--- /dev/null
+++ b/src/main/java/org/apache/pdfbox/jbig2/segments/PatternDictionary.java
@@ -0,0 +1,220 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.pdfbox.jbig2.segments;
+
+import java.awt.Rectangle;
+import java.io.IOException;
+import java.util.ArrayList;
+
+import org.apache.pdfbox.jbig2.Bitmap;
+import org.apache.pdfbox.jbig2.Dictionary;
+import org.apache.pdfbox.jbig2.SegmentHeader;
+import org.apache.pdfbox.jbig2.err.InvalidHeaderValueException;
+import org.apache.pdfbox.jbig2.image.Bitmaps;
+import org.apache.pdfbox.jbig2.io.SubInputStream;
+import org.apache.pdfbox.jbig2.util.log.Logger;
+import org.apache.pdfbox.jbig2.util.log.LoggerFactory;
+
+/**
+ * This class represents the segment type "Pattern dictionary", 7.4.4.
+ */
+public class PatternDictionary implements Dictionary {
+
+  private final Logger log = LoggerFactory.getLogger(PatternDictionary.class);
+
+  private SubInputStream subInputStream;
+
+  /** Segment data structure (only necessary if MMR is used) */
+  private long dataHeaderOffset;
+  private long dataHeaderLength;
+  private long dataOffset;
+  private long dataLength;
+
+  private short[] gbAtX = null;
+  private short[] gbAtY = null;
+
+  /** Pattern dictionary flags, 7.4.4.1.1 */
+  private boolean isMMREncoded;
+  private byte hdTemplate;
+
+  /** Width of the patterns in the pattern dictionary, 7.4.4.1.2 */
+  private short hdpWidth;
+
+  /** Height of the patterns in the pattern dictionary, 7.4.4.1.3 */
+  private short hdpHeight;
+
+  /** Decoded bitmaps, stored to be used by segments, that refer to it */
+  private ArrayList<Bitmap> patterns;
+
+  /**
+   * Largest gray-scale value, 7.4.4.1.4
+   * 
+   * Value: one less than the number of patterns defined in this pattern dictionary
+   */
+  private int grayMax;
+
+  private void parseHeader() throws IOException, InvalidHeaderValueException {
+    /* Bit 3-7 */
+    subInputStream.readBits(5); // Dirty read ...
+
+    /* Bit 1-2 */
+    readTemplate();
+
+    /* Bit 0 */
+    readIsMMREncoded();
+
+    readPatternWidthAndHeight();
+
+    readGrayMax();
+
+    /* Segment data structure */
+    computeSegmentDataStructure();
+
+    this.checkInput();
+  }
+
+  private void readTemplate() throws IOException {
+    /* Bit 1-2 */
+    hdTemplate = (byte) subInputStream.readBits(2);
+  }
+
+  private void readIsMMREncoded() throws IOException {
+    /* Bit 0 */
+    if (subInputStream.readBit() == 1) {
+      isMMREncoded = true;
+    }
+  }
+
+  private void readPatternWidthAndHeight() throws IOException {
+    hdpWidth = subInputStream.readByte();
+    hdpHeight = subInputStream.readByte();
+  }
+
+  private void readGrayMax() throws IOException {
+    grayMax = (int) (subInputStream.readBits(32) & 0xffffffff);
+  }
+
+  private void computeSegmentDataStructure() throws IOException {
+    dataOffset = subInputStream.getStreamPosition();
+    dataHeaderLength = dataOffset - dataHeaderOffset;
+    dataLength = subInputStream.length() - dataHeaderLength;
+  }
+
+  private void checkInput() throws InvalidHeaderValueException {
+    if (hdpHeight < 1 || hdpWidth < 1) {
+      throw new InvalidHeaderValueException("Width/Heigth must be greater than zero.");
+    }
+
+    if (isMMREncoded) {
+      if (hdTemplate != 0) {
+        log.info("hdTemplate should contain the value 0");
+      }
+    }
+  }
+
+  /**
+   * This method decodes a pattern dictionary segment and returns an array of {@link Bitmap} s. Each
+   * of this {@link Bitmap}s is a pattern.<br>
+   * The procedure is described in 6.7.5 (page 43).
+   * 
+   * @return An array of {@link Bitmap}s as result of the decoding procedure.
+   */
+  public ArrayList<Bitmap> getDictionary() throws IOException, InvalidHeaderValueException {
+    if (null == patterns) {
+
+      if (!isMMREncoded) {
+        setGbAtPixels();
+      }
+
+      // 2)
+      final GenericRegion genericRegion = new GenericRegion(subInputStream);
+      genericRegion.setParameters(isMMREncoded, dataOffset, dataLength, hdpHeight, (grayMax + 1) * hdpWidth,
+          hdTemplate, false, false, gbAtX, gbAtY);
+
+      final Bitmap collectiveBitmap = genericRegion.getRegionBitmap();
+
+      // 4)
+      extractPatterns(collectiveBitmap);
+    }
+
+    return patterns;
+  }
+
+  private void extractPatterns(Bitmap collectiveBitmap) {
+    // 3)
+    int gray = 0;
+    patterns = new ArrayList<Bitmap>(grayMax + 1);
+
+    // 4)
+    while (gray <= grayMax) {
+      // 4) a) Retrieve a pattern bitmap by extracting it out of the collective bitmap
+      final Rectangle roi = new Rectangle(hdpWidth * gray, 0, hdpWidth, hdpHeight);
+      final Bitmap patternBitmap = Bitmaps.extract(roi, collectiveBitmap);
+      patterns.add(patternBitmap);
+
+      // 4) b)
+      gray++;
+    }
+  }
+
+  private void setGbAtPixels() {
+    if (hdTemplate == 0) {
+      gbAtX = new short[4];
+      gbAtY = new short[4];
+      gbAtX[0] = (short) -hdpWidth;
+      gbAtY[0] = 0;
+      gbAtX[1] = -3;
+      gbAtY[1] = -1;
+      gbAtX[2] = 2;
+      gbAtY[2] = -2;
+      gbAtX[3] = -2;
+      gbAtY[3] = -2;
+
+    } else {
+      gbAtX = new short[1];
+      gbAtY = new short[1];
+      gbAtX[0] = (short) -hdpWidth;
+      gbAtY[0] = 0;
+    }
+  }
+
+  public void init(SegmentHeader header, SubInputStream sis) throws InvalidHeaderValueException, IOException {
+    this.subInputStream = sis;
+    parseHeader();
+  }
+
+  protected boolean isMMREncoded() {
+    return isMMREncoded;
+  }
+
+  protected byte getHdTemplate() {
+    return hdTemplate;
+  }
+
+  protected short getHdpWidth() {
+    return hdpWidth;
+  }
+
+  protected short getHdpHeight() {
+    return hdpHeight;
+  }
+
+  protected int getGrayMax() {
+    return grayMax;
+  }
+}

http://git-wip-us.apache.org/repos/asf/pdfbox-jbig2/blob/4619d28b/src/main/java/org/apache/pdfbox/jbig2/segments/Profiles.java
----------------------------------------------------------------------
diff --git a/src/main/java/org/apache/pdfbox/jbig2/segments/Profiles.java b/src/main/java/org/apache/pdfbox/jbig2/segments/Profiles.java
new file mode 100644
index 0000000..e5eee40
--- /dev/null
+++ b/src/main/java/org/apache/pdfbox/jbig2/segments/Profiles.java
@@ -0,0 +1,31 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.pdfbox.jbig2.segments;
+
+import org.apache.pdfbox.jbig2.SegmentData;
+import org.apache.pdfbox.jbig2.SegmentHeader;
+import org.apache.pdfbox.jbig2.io.SubInputStream;
+
+/**
+ * <b>TODO:</b> This class is not implemented yet and empty. Wait for use cases.
+ */
+public class Profiles implements SegmentData {
+
+  public void init(SegmentHeader header, SubInputStream sis) {
+  }
+}

http://git-wip-us.apache.org/repos/asf/pdfbox-jbig2/blob/4619d28b/src/main/java/org/apache/pdfbox/jbig2/segments/RegionSegmentInformation.java
----------------------------------------------------------------------
diff --git a/src/main/java/org/apache/pdfbox/jbig2/segments/RegionSegmentInformation.java b/src/main/java/org/apache/pdfbox/jbig2/segments/RegionSegmentInformation.java
new file mode 100644
index 0000000..6cbd9a2
--- /dev/null
+++ b/src/main/java/org/apache/pdfbox/jbig2/segments/RegionSegmentInformation.java
@@ -0,0 +1,107 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.pdfbox.jbig2.segments;
+
+import java.io.IOException;
+
+import org.apache.pdfbox.jbig2.SegmentData;
+import org.apache.pdfbox.jbig2.SegmentHeader;
+import org.apache.pdfbox.jbig2.err.IntegerMaxValueException;
+import org.apache.pdfbox.jbig2.err.InvalidHeaderValueException;
+import org.apache.pdfbox.jbig2.io.SubInputStream;
+import org.apache.pdfbox.jbig2.util.CombinationOperator;
+
+/**
+ * This class represents the "Region segment information" field, 7.4.1 (page 50). <br>
+ * Every region segment data starts with this part.
+ */
+public class RegionSegmentInformation implements SegmentData {
+
+  private SubInputStream subInputStream;
+
+  /** Region segment bitmap width, 7.4.1.1 */
+  private int bitmapWidth;
+
+  /** Region segment bitmap height, 7.4.1.2 */
+  private int bitmapHeight;
+
+  /** Region segment bitmap X location, 7.4.1.3 */
+  private int xLocation;
+
+  /** Region segment bitmap Y location, 7.4.1.4 */
+  private int yLocation;
+
+  /** Region segment flags, 7.4.1.5 */
+  private CombinationOperator combinationOperator;
+
+  public RegionSegmentInformation(SubInputStream subInputStream) {
+    this.subInputStream = subInputStream;
+  }
+
+  public RegionSegmentInformation() {
+  }
+
+  public void parseHeader() throws IOException {
+    this.bitmapWidth = ((int) (subInputStream.readBits(32) & 0xffffffff));
+    this.bitmapHeight = ((int) (subInputStream.readBits(32) & 0xffffffff));
+    this.xLocation = ((int) (subInputStream.readBits(32) & 0xffffffff));
+    this.yLocation = ((int) (subInputStream.readBits(32) & 0xffffffff));
+
+    /* Bit 3-7 */
+    subInputStream.readBits(5); // Dirty read... reserved bits are 0
+
+    /* Bit 0-2 */
+    readCombinationOperator();
+  }
+
+  private void readCombinationOperator() throws IOException {
+    this.combinationOperator = (CombinationOperator.translateOperatorCodeToEnum((short) (subInputStream.readBits(3) & 0xf)));
+  }
+
+  public void init(SegmentHeader header, SubInputStream sis) throws InvalidHeaderValueException,
+      IntegerMaxValueException, IOException {
+  }
+
+  public void setBitmapWidth(final int bitmapWidth) {
+    this.bitmapWidth = bitmapWidth;
+  }
+
+  public int getBitmapWidth() {
+    return bitmapWidth;
+  }
+
+  public void setBitmapHeight(final int bitmapHeight) {
+    this.bitmapHeight = bitmapHeight;
+  }
+
+  public int getBitmapHeight() {
+    return bitmapHeight;
+  }
+
+  public int getXLocation() {
+    return xLocation;
+  }
+
+  public int getYLocation() {
+    return yLocation;
+  }
+
+  public CombinationOperator getCombinationOperator() {
+    return combinationOperator;
+  }
+}

http://git-wip-us.apache.org/repos/asf/pdfbox-jbig2/blob/4619d28b/src/main/java/org/apache/pdfbox/jbig2/segments/SymbolDictionary.java
----------------------------------------------------------------------
diff --git a/src/main/java/org/apache/pdfbox/jbig2/segments/SymbolDictionary.java b/src/main/java/org/apache/pdfbox/jbig2/segments/SymbolDictionary.java
new file mode 100644
index 0000000..a3a6394
--- /dev/null
+++ b/src/main/java/org/apache/pdfbox/jbig2/segments/SymbolDictionary.java
@@ -0,0 +1,843 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.pdfbox.jbig2.segments;
+
+import java.awt.Rectangle;
+import java.io.IOException;
+import java.util.ArrayList;
+
+import org.apache.pdfbox.jbig2.Bitmap;
+import org.apache.pdfbox.jbig2.Dictionary;
+import org.apache.pdfbox.jbig2.JBIG2ImageReader;
+import org.apache.pdfbox.jbig2.Region;
+import org.apache.pdfbox.jbig2.SegmentHeader;
+import org.apache.pdfbox.jbig2.decoder.arithmetic.ArithmeticDecoder;
+import org.apache.pdfbox.jbig2.decoder.arithmetic.ArithmeticIntegerDecoder;
+import org.apache.pdfbox.jbig2.decoder.arithmetic.CX;
+import org.apache.pdfbox.jbig2.decoder.huffman.EncodedTable;
+import org.apache.pdfbox.jbig2.decoder.huffman.HuffmanTable;
+import org.apache.pdfbox.jbig2.decoder.huffman.StandardTables;
+import org.apache.pdfbox.jbig2.err.IntegerMaxValueException;
+import org.apache.pdfbox.jbig2.err.InvalidHeaderValueException;
+import org.apache.pdfbox.jbig2.image.Bitmaps;
+import org.apache.pdfbox.jbig2.io.SubInputStream;
+import org.apache.pdfbox.jbig2.util.log.Logger;
+import org.apache.pdfbox.jbig2.util.log.LoggerFactory;
+
+/**
+ * This class represents the data of segment type "Symbol dictionary". Parsing is described in
+ * 7.4.2.1.1 - 7.4.1.1.5 and decoding procedure is described in 6.5.
+ */
+public class SymbolDictionary implements Dictionary {
+
+  private final Logger log = LoggerFactory.getLogger(SymbolDictionary.class);
+
+  private SubInputStream subInputStream;
+
+  /** Symbol dictionary flags, 7.4.2.1.1 */
+  private short sdrTemplate;
+  private byte sdTemplate;
+  private boolean isCodingContextRetained;
+  private boolean isCodingContextUsed;
+  private short sdHuffAggInstanceSelection;
+  private short sdHuffBMSizeSelection;
+  private short sdHuffDecodeWidthSelection;
+  private short sdHuffDecodeHeightSelection;
+  private boolean useRefinementAggregation;
+  private boolean isHuffmanEncoded;
+
+  /** Symbol dictionary AT flags, 7.4.2.1.2 */
+  private short[] sdATX;
+  private short[] sdATY;
+
+  /** Symbol dictionary refinement AT flags, 7.4.2.1.3 */
+  private short[] sdrATX;
+  private short[] sdrATY;
+
+  /** Number of exported symbols, 7.4.2.1.4 */
+  private int amountOfExportSymbolss;
+
+  /** Number of new symbols, 7.4.2.1.5 */
+  private int amountOfNewSymbolss;
+
+  /** Further parameters */
+  private SegmentHeader segmentHeader;
+  private int amountOfImportedSymbolss;
+  private ArrayList<Bitmap> importSymbols;
+  private int amountOfDecodedSymbols;
+  private Bitmap[] newSymbols;
+
+  /** User-supplied tables * */
+  private HuffmanTable dhTable;
+  private HuffmanTable dwTable;
+  private HuffmanTable bmSizeTable;
+  private HuffmanTable aggInstTable;
+
+  /** Return value of that segment */
+  private ArrayList<Bitmap> exportSymbols;
+  private ArrayList<Bitmap> sbSymbols;
+
+  private ArithmeticDecoder arithmeticDecoder;
+  private ArithmeticIntegerDecoder iDecoder;
+
+  private TextRegion textRegion;
+  private GenericRegion genericRegion;
+  private GenericRefinementRegion genericRefinementRegion;
+  private CX cx;
+
+  private CX cxIADH;
+  private CX cxIADW;
+  private CX cxIAAI;
+  private CX cxIAEX;
+  private CX cxIARDX;
+  private CX cxIARDY;
+  private CX cxIADT;
+
+  protected CX cxIAID;
+  private int sbSymCodeLen;
+
+  public SymbolDictionary() {
+  }
+
+  public SymbolDictionary(SubInputStream subInputStream, SegmentHeader segmentHeader) throws IOException {
+    this.subInputStream = subInputStream;
+    this.segmentHeader = segmentHeader;
+  }
+
+  private void parseHeader() throws IOException, InvalidHeaderValueException, IntegerMaxValueException {
+    readRegionFlags();
+    setAtPixels();
+    setRefinementAtPixels();
+    readAmountOfExportedSymbols();
+    readAmountOfNewSymbols();
+    setInSyms();
+
+    if (isCodingContextUsed) {
+      SegmentHeader[] rtSegments = segmentHeader.getRtSegments();
+
+      for (int i = rtSegments.length - 1; i >= 0; i--) {
+
+        if (rtSegments[i].getSegmentType() == 0) {
+          SymbolDictionary symbolDictionary = (SymbolDictionary) rtSegments[i].getSegmentData();
+
+          if (symbolDictionary.isCodingContextRetained) {
+            /* 7.4.2.2 3) */
+            setRetainedCodingContexts(symbolDictionary);
+          }
+          break;
+        }
+      }
+    }
+
+    this.checkInput();
+  }
+
+  private void readRegionFlags() throws IOException {
+    /* Bit 13-15 */
+    subInputStream.readBits(3); // Dirty read... reserved bits must be 0
+
+    /* Bit 12 */
+    sdrTemplate = (short) subInputStream.readBit();
+
+    /* Bit 10-11 */
+    sdTemplate = (byte) (subInputStream.readBits(2) & 0xf);
+
+    /* Bit 9 */
+    if (subInputStream.readBit() == 1) {
+      isCodingContextRetained = true;
+    }
+
+    /* Bit 8 */
+    if (subInputStream.readBit() == 1) {
+      isCodingContextUsed = true;
+    }
+
+    /* Bit 7 */
+    sdHuffAggInstanceSelection = (short) subInputStream.readBit();
+
+    /* Bit 6 */
+    sdHuffBMSizeSelection = (short) subInputStream.readBit();
+
+    /* Bit 4-5 */
+    sdHuffDecodeWidthSelection = (short) (subInputStream.readBits(2) & 0xf);
+
+    /* Bit 2-3 */
+    sdHuffDecodeHeightSelection = (short) (subInputStream.readBits(2) & 0xf);
+
+    /* Bit 1 */
+    if (subInputStream.readBit() == 1) {
+      useRefinementAggregation = true;
+    }
+
+    /* Bit 0 */
+    if (subInputStream.readBit() == 1) {
+      isHuffmanEncoded = true;
+    }
+  }
+
+  private void setAtPixels() throws IOException {
+    if (!isHuffmanEncoded) {
+      if (sdTemplate == 0) {
+        readAtPixels(4);
+      } else {
+        readAtPixels(1);
+      }
+    }
+  }
+
+  private void setRefinementAtPixels() throws IOException {
+    if (useRefinementAggregation && sdrTemplate == 0) {
+      readRefinementAtPixels(2);
+    }
+  }
+
+  private void readAtPixels(final int amountOfPixels) throws IOException {
+    sdATX = new short[amountOfPixels];
+    sdATY = new short[amountOfPixels];
+
+    for (int i = 0; i < amountOfPixels; i++) {
+      sdATX[i] = subInputStream.readByte();
+      sdATY[i] = subInputStream.readByte();
+    }
+  }
+
+  private void readRefinementAtPixels(final int amountOfAtPixels) throws IOException {
+    sdrATX = new short[amountOfAtPixels];
+    sdrATY = new short[amountOfAtPixels];
+
+    for (int i = 0; i < amountOfAtPixels; i++) {
+      sdrATX[i] = subInputStream.readByte();
+      sdrATY[i] = subInputStream.readByte();
+    }
+  }
+
+  private void readAmountOfExportedSymbols() throws IOException {
+    amountOfExportSymbolss = (int) subInputStream.readBits(32); // & 0xffffffff;
+  }
+
+  private void readAmountOfNewSymbols() throws IOException {
+    amountOfNewSymbolss = (int) subInputStream.readBits(32); // & 0xffffffff;
+  }
+
+  private void setInSyms() throws IOException, InvalidHeaderValueException, IntegerMaxValueException {
+    if (segmentHeader.getRtSegments() != null) {
+      retrieveImportSymbols();
+    } else {
+      importSymbols = new ArrayList<Bitmap>();
+    }
+  }
+
+  private void setRetainedCodingContexts(final SymbolDictionary sd) {
+    this.arithmeticDecoder = sd.arithmeticDecoder;
+    this.isHuffmanEncoded = sd.isHuffmanEncoded;
+    this.useRefinementAggregation = sd.useRefinementAggregation;
+    this.sdTemplate = sd.sdTemplate;
+    this.sdrTemplate = sd.sdrTemplate;
+    this.sdATX = sd.sdATX;
+    this.sdATY = sd.sdATY;
+    this.sdrATX = sd.sdrATX;
+    this.sdrATY = sd.sdrATY;
+    this.cx = sd.cx;
+  }
+
+  private void checkInput() throws InvalidHeaderValueException {
+    if (sdHuffDecodeHeightSelection == 2) {
+      log.info("sdHuffDecodeHeightSelection = " + sdHuffDecodeHeightSelection + " (value not permitted)");
+    }
+
+    if (sdHuffDecodeWidthSelection == 2) {
+      log.info("sdHuffDecodeWidthSelection = " + sdHuffDecodeWidthSelection + " (value not permitted)");
+    }
+
+    if (isHuffmanEncoded) {
+      if (sdTemplate != 0) {
+        log.info("sdTemplate = " + sdTemplate + " (should be 0)");
+        sdTemplate = 0;
+      }
+      if (!useRefinementAggregation) {
+        if (isCodingContextRetained) {
+          log.info("isCodingContextRetained = " + isCodingContextRetained + " (should be 0)");
+          isCodingContextRetained = false;
+        }
+
+        if (isCodingContextUsed) {
+          log.info("isCodingContextUsed = " + isCodingContextUsed + " (should be 0)");
+          isCodingContextUsed = false;
+        }
+      }
+
+    } else {
+      if (sdHuffBMSizeSelection != 0) {
+        log.info("sdHuffBMSizeSelection should be 0");
+        sdHuffBMSizeSelection = 0;
+      }
+      if (sdHuffDecodeWidthSelection != 0) {
+        log.info("sdHuffDecodeWidthSelection should be 0");
+        sdHuffDecodeWidthSelection = 0;
+      }
+      if (sdHuffDecodeHeightSelection != 0) {
+        log.info("sdHuffDecodeHeightSelection should be 0");
+        sdHuffDecodeHeightSelection = 0;
+      }
+    }
+
+    if (!useRefinementAggregation) {
+      if (sdrTemplate != 0) {
+        log.info("sdrTemplate = " + sdrTemplate + " (should be 0)");
+        sdrTemplate = 0;
+      }
+    }
+
+    if (!isHuffmanEncoded || !useRefinementAggregation) {
+      if (sdHuffAggInstanceSelection != 0) {
+        log.info("sdHuffAggInstanceSelection = " + sdHuffAggInstanceSelection + " (should be 0)");
+        sdHuffAggInstanceSelection = 0;
+      }
+    }
+  }
+
+  /**
+   * 6.5.5 Decoding the symbol dictionary
+   * 
+   * @return List of decoded symbol bitmaps as an <code>ArrayList</code>
+   */
+  public ArrayList<Bitmap> getDictionary() throws IOException, IntegerMaxValueException, InvalidHeaderValueException {
+    long timestamp = System.currentTimeMillis();
+    if (null == exportSymbols) {
+
+      if (useRefinementAggregation)
+        sbSymCodeLen = getSbSymCodeLen();
+
+      if (!isHuffmanEncoded) {
+        setCodingStatistics();
+      }
+
+      /* 6.5.5 1) */
+      newSymbols = new Bitmap[amountOfNewSymbolss];
+
+      /* 6.5.5 2) */
+      int[] newSymbolsWidths = null;
+      if (isHuffmanEncoded && !useRefinementAggregation) {
+        newSymbolsWidths = new int[amountOfNewSymbolss];
+      }
+
+      setSymbolsArray();
+
+      /* 6.5.5 3) */
+      int heightClassHeight = 0;
+      amountOfDecodedSymbols = 0;
+
+      /* 6.5.5 4 a) */
+      while (amountOfDecodedSymbols != amountOfNewSymbolss) {
+
+        /* 6.5.5 4 b) */
+        heightClassHeight += decodeHeightClassDeltaHeight();
+        int symbolWidth = 0;
+        int totalWidth = 0;
+        final int heightClassFirstSymbolIndex = amountOfDecodedSymbols;
+
+        /* 6.5.5 4 c) */
+
+        // Repeat until OOB - OOB sends a break;
+        while (true) {
+          /* 4 c) i) */
+          final long differenceWidth = decodeDifferenceWidth();
+
+          // If result is OOB, then all the symbols in this height
+          // class has been decoded; proceed to step 4 d)
+          if (differenceWidth == Long.MAX_VALUE) {
+            break;
+          }
+
+          symbolWidth += differenceWidth;
+          totalWidth += symbolWidth;
+
+          /* 4 c) ii) */
+          if (!isHuffmanEncoded || useRefinementAggregation) {
+            if (!useRefinementAggregation) {
+              // 6.5.8.1 - Direct coded
+              decodeDirectlyThroughGenericRegion(symbolWidth, heightClassHeight);
+            } else {
+              // 6.5.8.2 - Refinement/Aggregate-coded
+              decodeAggregate(symbolWidth, heightClassHeight);
+            }
+          } else if (isHuffmanEncoded && !useRefinementAggregation) {
+            /* 4 c) iii) */
+            newSymbolsWidths[amountOfDecodedSymbols] = symbolWidth;
+          }
+          amountOfDecodedSymbols++;
+        }
+
+        /* 6.5.5 4 d) */
+        if (isHuffmanEncoded && !useRefinementAggregation) {
+          /* 6.5.9 */
+          final long bmSize;
+          if (sdHuffBMSizeSelection == 0) {
+            bmSize = StandardTables.getTable(1).decode(subInputStream);
+          } else {
+            bmSize = huffDecodeBmSize();
+          }
+
+          subInputStream.skipBits();
+
+          final Bitmap heightClassCollectiveBitmap = decodeHeightClassCollectiveBitmap(bmSize, heightClassHeight,
+              totalWidth);
+
+          subInputStream.skipBits();
+          decodeHeightClassBitmap(heightClassCollectiveBitmap, heightClassFirstSymbolIndex, heightClassHeight,
+              newSymbolsWidths);
+        }
+      }
+
+      /* 5) */
+      /* 6.5.10 1) - 5) */
+
+      final int[] exFlags = getToExportFlags();
+
+      /* 6.5.10 6) - 8) */
+      setExportedSymbols(exFlags);
+    }
+
+    if (JBIG2ImageReader.PERFORMANCE_TEST)
+      log.info("SYMBOL DECODING: " + (System.currentTimeMillis() - timestamp) + " ms");
+
+    // DictionaryViewer.viewSymbols(sdExSyms);
+
+    return exportSymbols;
+  }
+
+  private void setCodingStatistics() throws IOException {
+    if (cxIADT == null) {
+      cxIADT = new CX(512, 1);
+    }
+
+    if (cxIADH == null) {
+      cxIADH = new CX(512, 1);
+    }
+
+    if (cxIADW == null) {
+      cxIADW = new CX(512, 1);
+    }
+
+    if (cxIAAI == null) {
+      cxIAAI = new CX(512, 1);
+    }
+
+    if (cxIAEX == null) {
+      cxIAEX = new CX(512, 1);
+    }
+
+    if (useRefinementAggregation && cxIAID == null) {
+      cxIAID = new CX(1 << sbSymCodeLen, 1);
+      cxIARDX = new CX(512, 1);
+      cxIARDY = new CX(512, 1);
+    }
+
+    if (cx == null) {
+      cx = new CX(65536, 1);
+    }
+
+    if (arithmeticDecoder == null) {
+      arithmeticDecoder = new ArithmeticDecoder(subInputStream);
+    }
+
+    if (iDecoder == null) {
+      iDecoder = new ArithmeticIntegerDecoder(arithmeticDecoder);
+    }
+
+  }
+
+  private final void decodeHeightClassBitmap(final Bitmap heightClassCollectiveBitmap,
+      final int heightClassFirstSymbol, final int heightClassHeight, final int[] newSymbolsWidths)
+      throws IntegerMaxValueException, InvalidHeaderValueException, IOException {
+
+    for (int i = heightClassFirstSymbol; i < amountOfDecodedSymbols; i++) {
+      int startColumn = 0;
+
+      for (int j = heightClassFirstSymbol; j <= i - 1; j++) {
+        startColumn += newSymbolsWidths[j];
+      }
+
+      final Rectangle roi = new Rectangle(startColumn, 0, newSymbolsWidths[i], heightClassHeight);
+      final Bitmap symbolBitmap = Bitmaps.extract(roi, heightClassCollectiveBitmap);
+      newSymbols[i] = symbolBitmap;
+      sbSymbols.add(symbolBitmap);
+    }
+  }
+
+  private final void decodeAggregate(final int symbolWidth, final int heightClassHeight) throws IOException,
+      InvalidHeaderValueException, IntegerMaxValueException {
+    // 6.5.8.2 1)
+    // 6.5.8.2.1 - Number of symbol instances in aggregation
+    final long amountOfRefinementAggregationInstances;
+    if (isHuffmanEncoded) {
+      log.info("Refinement or aggregate-coded symbols may couse problems with huffman decoding!");
+      amountOfRefinementAggregationInstances = huffDecodeRefAggNInst();
+    } else {
+      amountOfRefinementAggregationInstances = iDecoder.decode(cxIAAI);
+    }
+
+    if (amountOfRefinementAggregationInstances > 1) {
+      // 6.5.8.2 2)
+      decodeThroughTextRegion(symbolWidth, heightClassHeight, amountOfRefinementAggregationInstances);
+    } else if (amountOfRefinementAggregationInstances == 1) {
+      // 6.5.8.2 3) refers to 6.5.8.2.2
+      decodeRefinedSymbol(symbolWidth, heightClassHeight);
+    }
+  }
+
+  private final long huffDecodeRefAggNInst() throws IOException, InvalidHeaderValueException {
+    if (sdHuffAggInstanceSelection == 0) {
+      return StandardTables.getTable(1).decode(subInputStream);
+    } else if (sdHuffAggInstanceSelection == 1) {
+      if (aggInstTable == null) {
+        int aggregationInstanceNumber = 0;
+
+        if (sdHuffDecodeHeightSelection == 3) {
+          aggregationInstanceNumber++;
+        }
+        if (sdHuffDecodeWidthSelection == 3) {
+          aggregationInstanceNumber++;
+        }
+        if (sdHuffBMSizeSelection == 3) {
+          aggregationInstanceNumber++;
+        }
+
+        aggInstTable = getUserTable(aggregationInstanceNumber);
+      }
+      return aggInstTable.decode(subInputStream);
+    }
+    return 0;
+  }
+
+  private final void decodeThroughTextRegion(final int symbolWidth, final int heightClassHeight,
+      final long amountOfRefinementAggregationInstances) throws IOException, IntegerMaxValueException,
+      InvalidHeaderValueException {
+    if (textRegion == null) {
+      textRegion = new TextRegion(subInputStream, null);
+
+      textRegion.setContexts(cx, // default context
+          new CX(512, 1), // IADT
+          new CX(512, 1), // IAFS
+          new CX(512, 1), // IADS
+          new CX(512, 1), // IAIT
+          cxIAID, // IAID
+          new CX(512, 1), // IARDW
+          new CX(512, 1), // IARDH
+          new CX(512, 1), // IARDX
+          new CX(512, 1) // IARDY
+      );
+    }
+
+    // 6.5.8.2.4 Concatenating the array used as parameter later.
+    setSymbolsArray();
+
+    // 6.5.8.2 2) Parameters set according to Table 17, page 36
+    textRegion.setParameters(arithmeticDecoder, iDecoder, isHuffmanEncoded, true, symbolWidth, heightClassHeight,
+        amountOfRefinementAggregationInstances, 1, (amountOfImportedSymbolss + amountOfDecodedSymbols), (short) 0,
+        (short) 0, (short) 0, (short) 1, (short) 0, (short) 0, (short) 0, (short) 0, (short) 0, (short) 0, (short) 0,
+        (short) 0, (short) 0, sdrTemplate, sdrATX, sdrATY, sbSymbols, sbSymCodeLen);
+
+    addSymbol(textRegion);
+  }
+
+  private final void decodeRefinedSymbol(final int symbolWidth, final int heightClassHeight) throws IOException,
+      InvalidHeaderValueException, IntegerMaxValueException {
+
+    final int id;
+    final int rdx;
+    final int rdy;
+    // long symInRefSize = 0;
+    if (isHuffmanEncoded) {
+      /* 2) - 4) */
+      id = (int) subInputStream.readBits(sbSymCodeLen);
+      rdx = (int) StandardTables.getTable(15).decode(subInputStream);
+      rdy = (int) StandardTables.getTable(15).decode(subInputStream);
+
+      /* 5) a) */
+      /* symInRefSize = */StandardTables.getTable(1).decode(subInputStream);
+
+      /* 5) b) - Skip over remaining bits */
+      subInputStream.skipBits();
+    } else {
+      /* 2) - 4) */
+      id = iDecoder.decodeIAID(cxIAID, sbSymCodeLen);
+      rdx = (int) iDecoder.decode(cxIARDX);
+      rdy = (int) iDecoder.decode(cxIARDY);
+    }
+
+    /* 6) */
+    setSymbolsArray();
+    final Bitmap ibo = sbSymbols.get(id);
+    decodeNewSymbols(symbolWidth, heightClassHeight, ibo, rdx, rdy);
+
+    /* 7) */
+    if (isHuffmanEncoded) {
+      subInputStream.skipBits();
+      // Make sure that the processed bytes are equal to the value read in step 5 a)
+    }
+  }
+
+  private final void decodeNewSymbols(final int symWidth, final int hcHeight, final Bitmap ibo, final int rdx,
+      final int rdy) throws IOException, InvalidHeaderValueException, IntegerMaxValueException {
+    if (genericRefinementRegion == null) {
+      genericRefinementRegion = new GenericRefinementRegion(subInputStream);
+
+      if (arithmeticDecoder == null) {
+        arithmeticDecoder = new ArithmeticDecoder(subInputStream);
+      }
+
+      if (cx == null) {
+        cx = new CX(65536, 1);
+      }
+    }
+
+    // Parameters as shown in Table 18, page 36
+    genericRefinementRegion.setParameters(cx, arithmeticDecoder, sdrTemplate, symWidth, hcHeight, ibo, rdx, rdy, false,
+        sdrATX, sdrATY);
+
+    addSymbol(genericRefinementRegion);
+  }
+
+  private final void decodeDirectlyThroughGenericRegion(final int symWidth, final int hcHeight) throws IOException,
+      IntegerMaxValueException, InvalidHeaderValueException {
+    if (genericRegion == null) {
+      genericRegion = new GenericRegion(subInputStream);
+    }
+
+    // Parameters set according to Table 16, page 35
+    genericRegion.setParameters(false, sdTemplate, false, false, sdATX, sdATY, symWidth, hcHeight, cx,
+        arithmeticDecoder);
+
+    addSymbol(genericRegion);
+  }
+
+  private final void addSymbol(final Region region) throws IntegerMaxValueException, InvalidHeaderValueException,
+      IOException {
+    final Bitmap symbol = region.getRegionBitmap();
+    newSymbols[amountOfDecodedSymbols] = symbol;
+    sbSymbols.add(symbol);
+  }
+
+  private final long decodeDifferenceWidth() throws IOException, InvalidHeaderValueException {
+    if (isHuffmanEncoded) {
+      switch (sdHuffDecodeWidthSelection){
+        case 0 :
+          return StandardTables.getTable(2).decode(subInputStream);
+        case 1 :
+          return StandardTables.getTable(3).decode(subInputStream);
+        case 3 :
+          if (dwTable == null) {
+            int dwNr = 0;
+
+            if (sdHuffDecodeHeightSelection == 3) {
+              dwNr++;
+            }
+            dwTable = getUserTable(dwNr);
+          }
+
+          return dwTable.decode(subInputStream);
+      }
+    } else {
+      return iDecoder.decode(cxIADW);
+    }
+    return 0;
+  }
+
+  private final long decodeHeightClassDeltaHeight() throws IOException, InvalidHeaderValueException {
+    if (isHuffmanEncoded) {
+      return decodeHeightClassDeltaHeightWithHuffman();
+    } else {
+      return iDecoder.decode(cxIADH);
+    }
+  }
+
+  /**
+   * 6.5.6 if isHuffmanEncoded
+   * 
+   * @return long - Result of decoding HCDH
+   * @throws IOException
+   * @throws InvalidHeaderValueException
+   */
+  private final long decodeHeightClassDeltaHeightWithHuffman() throws IOException, InvalidHeaderValueException {
+    switch (sdHuffDecodeHeightSelection){
+      case 0 :
+        return StandardTables.getTable(4).decode(subInputStream);
+      case 1 :
+        return StandardTables.getTable(5).decode(subInputStream);
+      case 3 :
+        if (dhTable == null) {
+          dhTable = getUserTable(0);
+        }
+        return dhTable.decode(subInputStream);
+    }
+
+    return 0;
+  }
+
+  private final Bitmap decodeHeightClassCollectiveBitmap(final long bmSize, final int heightClassHeight,
+      final int totalWidth) throws IOException {
+    if (bmSize == 0) {
+      final Bitmap heightClassCollectiveBitmap = new Bitmap(totalWidth, heightClassHeight);
+
+      for (int i = 0; i < heightClassCollectiveBitmap.getByteArray().length; i++) {
+        heightClassCollectiveBitmap.setByte(i, subInputStream.readByte());
+      }
+
+      return heightClassCollectiveBitmap;
+    } else {
+      if (genericRegion == null) {
+        genericRegion = new GenericRegion(subInputStream);
+      }
+
+      genericRegion.setParameters(true, subInputStream.getStreamPosition(), bmSize, heightClassHeight, totalWidth);
+
+      return genericRegion.getRegionBitmap();
+    }
+  }
+
+  private void setExportedSymbols(int[] toExportFlags) {
+    exportSymbols = new ArrayList<Bitmap>(amountOfExportSymbolss);
+
+    for (int i = 0; i < amountOfImportedSymbolss + amountOfNewSymbolss; i++) {
+
+      if (toExportFlags[i] == 1) {
+        if (i < amountOfImportedSymbolss) {
+          exportSymbols.add(importSymbols.get(i));
+        } else {
+          exportSymbols.add(newSymbols[i - amountOfImportedSymbolss]);
+        }
+      }
+    }
+  }
+
+  private int[] getToExportFlags() throws IOException, InvalidHeaderValueException {
+    int currentExportFlag = 0;
+    long exRunLength = 0;
+    final int[] exportFlags = new int[amountOfImportedSymbolss + amountOfNewSymbolss];
+
+    for (int exportIndex = 0; exportIndex < amountOfImportedSymbolss + amountOfNewSymbolss; exportIndex += exRunLength) {
+
+      if (isHuffmanEncoded) {
+        exRunLength = StandardTables.getTable(1).decode(subInputStream);
+      } else {
+        exRunLength = iDecoder.decode(cxIAEX);
+      }
+
+      if (exRunLength != 0) {
+        for (int index = exportIndex; index < exportIndex + exRunLength; index++) {
+          exportFlags[index] = currentExportFlag;
+        }
+      }
+
+      currentExportFlag = (currentExportFlag == 0) ? 1 : 0;
+    }
+
+    return exportFlags;
+  }
+
+  private final long huffDecodeBmSize() throws IOException, InvalidHeaderValueException {
+    if (bmSizeTable == null) {
+      int bmNr = 0;
+
+      if (sdHuffDecodeHeightSelection == 3) {
+        bmNr++;
+      }
+
+      if (sdHuffDecodeWidthSelection == 3) {
+        bmNr++;
+      }
+
+      bmSizeTable = getUserTable(bmNr);
+    }
+    return bmSizeTable.decode(subInputStream);
+  }
+
+  /**
+   * 6.5.8.2.3 - Setting SBSYMCODES and SBSYMCODELEN
+   * 
+   * @return Result of computing SBSYMCODELEN
+   * @throws IOException
+   */
+  private int getSbSymCodeLen() throws IOException {
+    if (isHuffmanEncoded) {
+      return Math.max((int) (Math.ceil(Math.log(amountOfImportedSymbolss + amountOfNewSymbolss) / Math.log(2))), 1);
+    } else {
+      return (int) (Math.ceil(Math.log(amountOfImportedSymbolss + amountOfNewSymbolss) / Math.log(2)));
+    }
+  }
+
+  /**
+   * 6.5.8.2.4 - Setting SBSYMS
+   * 
+   * @throws IOException
+   * @throws InvalidHeaderValueException
+   * @throws IntegerMaxValueException
+   */
+  private final void setSymbolsArray() throws IOException, InvalidHeaderValueException, IntegerMaxValueException {
+    if (importSymbols == null) {
+      retrieveImportSymbols();
+    }
+
+    if (sbSymbols == null) {
+      sbSymbols = new ArrayList<Bitmap>();
+      sbSymbols.addAll(importSymbols);
+    }
+  }
+
+  /**
+   * Concatenates symbols from all referred-to segments.
+   * 
+   * @throws IOException
+   * @throws InvalidHeaderValueException
+   * @throws IntegerMaxValueException
+   */
+  private void retrieveImportSymbols() throws IOException, InvalidHeaderValueException, IntegerMaxValueException {
+    importSymbols = new ArrayList<Bitmap>();
+    for (final SegmentHeader referredToSegmentHeader : segmentHeader.getRtSegments()) {
+      if (referredToSegmentHeader.getSegmentType() == 0) {
+        final SymbolDictionary sd = (SymbolDictionary) referredToSegmentHeader.getSegmentData();
+        importSymbols.addAll(sd.getDictionary());
+        amountOfImportedSymbolss += sd.amountOfExportSymbolss;
+      }
+    }
+  }
+
+  private HuffmanTable getUserTable(final int tablePosition) throws InvalidHeaderValueException, IOException {
+    int tableCounter = 0;
+
+    for (final SegmentHeader referredToSegmentHeader : segmentHeader.getRtSegments()) {
+      if (referredToSegmentHeader.getSegmentType() == 53) {
+        if (tableCounter == tablePosition) {
+          final Table t = (Table) referredToSegmentHeader.getSegmentData();
+          return new EncodedTable(t);
+        } else {
+          tableCounter++;
+        }
+      }
+    }
+    return null;
+  }
+
+  public void init(final SegmentHeader header, final SubInputStream sis) throws InvalidHeaderValueException,
+      IntegerMaxValueException, IOException {
+    this.subInputStream = sis;
+    this.segmentHeader = header;
+    parseHeader();
+  }
+}