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:20 UTC

[7/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/JBIG2Page.java
----------------------------------------------------------------------
diff --git a/src/main/java/org/apache/pdfbox/jbig2/JBIG2Page.java b/src/main/java/org/apache/pdfbox/jbig2/JBIG2Page.java
new file mode 100644
index 0000000..566cdf7
--- /dev/null
+++ b/src/main/java/org/apache/pdfbox/jbig2/JBIG2Page.java
@@ -0,0 +1,376 @@
+/**
+ * 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;
+
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Map;
+import java.util.Set;
+import java.util.TreeMap;
+
+import org.apache.pdfbox.jbig2.err.IntegerMaxValueException;
+import org.apache.pdfbox.jbig2.err.InvalidHeaderValueException;
+import org.apache.pdfbox.jbig2.err.JBIG2Exception;
+import org.apache.pdfbox.jbig2.image.Bitmaps;
+import org.apache.pdfbox.jbig2.segments.EndOfStripe;
+import org.apache.pdfbox.jbig2.segments.PageInformation;
+import org.apache.pdfbox.jbig2.segments.RegionSegmentInformation;
+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 a JBIG2 page.
+ */
+class JBIG2Page {
+
+  private static final Logger log = LoggerFactory.getLogger(JBIG2Page.class);
+
+  /**
+   * This list contains all segments of this page, sorted by segment number in ascending order.
+   */
+  private final Map<Integer, SegmentHeader> segments = new TreeMap<Integer, SegmentHeader>();
+
+  /** NOTE: page number != segmentList index */
+  private final int pageNumber;
+
+  /** The page bitmap that represents the page buffer */
+  private Bitmap pageBitmap;
+
+  private int finalHeight;
+  private int finalWidth;
+  private int resolutionX;
+  private int resolutionY;
+
+  private final JBIG2Document document;
+
+  protected JBIG2Page(JBIG2Document document, int pageNumber) {
+    this.document = document;
+    this.pageNumber = pageNumber;
+  }
+
+  /**
+   * This method searches for a segment specified by its number.
+   * 
+   * @param number - Segment number of the segment to search.
+   * 
+   * @return The retrieved {@link SegmentHeader} or {@code null} if not available.
+   */
+  public SegmentHeader getSegment(int number) {
+    SegmentHeader s = segments.get(number);
+
+    if (null != s) {
+      return s;
+    }
+
+    if (null != document) {
+      return document.getGlobalSegment(number);
+    }
+
+    log.info("Segment not found, returning null.");
+    return null;
+  }
+
+  /**
+   * Returns the associated page information segment.
+   * 
+   * @return The associated {@link PageInformation} segment or {@code null} if not available.
+   */
+  protected SegmentHeader getPageInformationSegment() {
+    for (SegmentHeader s : segments.values()) {
+      if (s.getSegmentType() == 48) {
+        return s;
+      }
+    }
+
+    log.info("Page information segment not found.");
+    return null;
+  }
+
+  /**
+   * This method returns the decoded bitmap if present. Otherwise the page bitmap will be composed
+   * before returning the result.
+   * 
+   * @return pageBitmap - The result of decoding a page
+   * @throws JBIG2Exception
+   * @throws IOException
+   */
+  protected Bitmap getBitmap() throws JBIG2Exception, IOException {
+    long timestamp;
+
+    if (JBIG2ImageReader.PERFORMANCE_TEST) {
+      timestamp = System.currentTimeMillis();
+    }
+
+    if (null == pageBitmap) {
+      composePageBitmap();
+    }
+
+    if (JBIG2ImageReader.PERFORMANCE_TEST) {
+      log.info("PAGE DECODING: " + (System.currentTimeMillis() - timestamp) + " ms");
+    }
+
+    return pageBitmap;
+  }
+
+  /**
+   * This method composes the segments' bitmaps to a page and stores the page as a {@link Bitmap}
+   * 
+   * @throws IOException
+   * @throws JBIG2Exception
+   */
+  private void composePageBitmap() throws IOException, JBIG2Exception {
+    if (pageNumber > 0) {
+      // Page 79, 1) Decoding the page information segment
+      PageInformation pageInformation = (PageInformation) getPageInformationSegment().getSegmentData();
+      createPage(pageInformation);
+      clearSegmentData();
+    }
+  }
+
+  private void createPage(PageInformation pageInformation) throws IOException, IntegerMaxValueException,
+      InvalidHeaderValueException {
+    if (!pageInformation.isStriped() || pageInformation.getHeight() != -1) {
+      // Page 79, 4)
+      createNormalPage(pageInformation);
+    } else {
+      createStripedPage(pageInformation);
+    }
+  }
+
+  private void createNormalPage(PageInformation pageInformation) throws IOException, IntegerMaxValueException,
+      InvalidHeaderValueException {
+
+    pageBitmap = new Bitmap(pageInformation.getWidth(), pageInformation.getHeight());
+
+    // Page 79, 3)
+    // If default pixel value is not 0, byte will be filled with 0xff
+    if (pageInformation.getDefaultPixelValue() != 0) {
+      Arrays.fill(pageBitmap.getByteArray(), (byte) 0xff);
+    }
+
+    for (SegmentHeader s : segments.values()) {
+      // Page 79, 5)
+      switch (s.getSegmentType()){
+        case 6 : // Immediate text region
+        case 7 : // Immediate lossless text region
+        case 22 : // Immediate halftone region
+        case 23 : // Immediate lossless halftone region
+        case 38 : // Immediate generic region
+        case 39 : // Immediate lossless generic region
+        case 42 : // Immediate generic refinement region
+        case 43 : // Immediate lossless generic refinement region
+          final Region r = (Region) s.getSegmentData();
+
+          final Bitmap regionBitmap = r.getRegionBitmap();
+
+          if (fitsPage(pageInformation, regionBitmap)) {
+            pageBitmap = regionBitmap;
+          } else {
+            final RegionSegmentInformation regionInfo = r.getRegionInfo();
+            final CombinationOperator op = getCombinationOperator(pageInformation,
+                regionInfo.getCombinationOperator());
+            Bitmaps.blit(regionBitmap, pageBitmap, regionInfo.getXLocation(), regionInfo.getYLocation(), op);
+          }
+
+          break;
+      }
+    }
+  }
+
+  /**
+   * Check if we have only one region that forms the complete page. If the dimension equals the
+   * page's dimension set the region's bitmap as the page's bitmap. Otherwise we have to blit the
+   * smaller region's bitmap into the page's bitmap (see Issue 6).
+   * 
+   * @param pageInformation
+   * @param regionBitmap
+   * @return
+   */
+  private boolean fitsPage(PageInformation pageInformation, final Bitmap regionBitmap) {
+    return countRegions() == 1 && pageInformation.getDefaultPixelValue() == 0
+        && pageInformation.getWidth() == regionBitmap.getWidth()
+        && pageInformation.getHeight() == regionBitmap.getHeight();
+  }
+
+  private void createStripedPage(PageInformation pageInformation) throws IOException, IntegerMaxValueException,
+      InvalidHeaderValueException {
+    final ArrayList<SegmentData> pageStripes = collectPageStripes();
+
+    pageBitmap = new Bitmap(pageInformation.getWidth(), finalHeight);
+
+    int startLine = 0;
+    for (SegmentData sd : pageStripes) {
+      if (sd instanceof EndOfStripe) {
+        startLine = ((EndOfStripe) sd).getLineNumber() + 1;
+      } else {
+        final Region r = (Region) sd;
+        final RegionSegmentInformation regionInfo = r.getRegionInfo();
+        final CombinationOperator op = getCombinationOperator(pageInformation, regionInfo.getCombinationOperator());
+        Bitmaps.blit(r.getRegionBitmap(), pageBitmap, regionInfo.getXLocation(), startLine, op);
+      }
+    }
+  }
+
+  private ArrayList<SegmentData> collectPageStripes() {
+    final ArrayList<SegmentData> pageStripes = new ArrayList<SegmentData>();
+    for (SegmentHeader s : segments.values()) {
+      // Page 79, 5)
+      switch (s.getSegmentType()){
+        case 6 : // Immediate text region
+        case 7 : // Immediate lossless text region
+        case 22 : // Immediate halftone region
+        case 23 : // Immediate lossless halftone region
+        case 38 : // Immediate generic region
+        case 39 : // Immediate lossless generic region
+        case 42 : // Immediate generic refinement region
+        case 43 : // Immediate lossless generic refinement region
+          Region r = (Region) s.getSegmentData();
+          pageStripes.add(r);
+          break;
+
+        case 50 : // End of stripe
+          EndOfStripe eos = (EndOfStripe) s.getSegmentData();
+          pageStripes.add(eos);
+          finalHeight = eos.getLineNumber() + 1;
+          break;
+      }
+    }
+
+    return pageStripes;
+  }
+
+  /**
+   * This method counts the regions segments. If there is only one region, the bitmap of this
+   * segment is equal to the page bitmap and blitting is not necessary.
+   * 
+   * @return Amount of regions.
+   */
+  private int countRegions() {
+    int regionCount = 0;
+
+    for (SegmentHeader s : segments.values()) {
+      switch (s.getSegmentType()){
+        case 6 : // Immediate text region
+        case 7 : // Immediate lossless text region
+        case 22 : // Immediate halftone region
+        case 23 : // Immediate lossless halftone region
+        case 38 : // Immediate generic region
+        case 39 : // Immediate lossless generic region
+        case 42 : // Immediate generic refinement region
+        case 43 : // Immediate lossless generic refinement region
+          regionCount++;
+      }
+    }
+
+    return regionCount;
+  }
+
+  /**
+   * This method checks and sets, which combination operator shall be used.
+   * 
+   * @param pi - <code>PageInformation</code> object
+   * @param newOperator - The combination operator, specified by actual segment
+   * @return the new combination operator
+   */
+  private CombinationOperator getCombinationOperator(PageInformation pi, CombinationOperator newOperator) {
+    if (pi.isCombinationOperatorOverrideAllowed()) {
+      return newOperator;
+    } else {
+      return pi.getCombinationOperator();
+    }
+  }
+
+  /**
+   * Adds a {@link SegmentHeader} into the page's segments map.
+   * 
+   * @param segment - The segment to be added.
+   */
+  protected void add(SegmentHeader segment) {
+
+    segments.put(segment.getSegmentNr(), segment);
+  }
+
+  /**
+   * Resets the memory-critical segments to force on-demand-decoding and to avoid holding the
+   * segments' bitmap too long.
+   */
+  private void clearSegmentData() {
+    Set<Integer> keySet = segments.keySet();
+
+    for (Integer key : keySet) {
+      segments.get(key).cleanSegmentData();
+    }
+  }
+
+  /**
+   * Reset memory-critical parts of page.
+   */
+  protected void clearPageData() {
+    pageBitmap = null;
+  }
+
+  /**
+   * Returns the final height of the page.
+   * 
+   * @return The final height of the page.
+   * @throws IOException
+   * @throws JBIG2Exception
+   */
+  protected int getHeight() throws IOException, JBIG2Exception {
+    if (finalHeight == 0) {
+      PageInformation pi = (PageInformation) getPageInformationSegment().getSegmentData();
+      if (pi.getHeight() == 0xffffffff) {
+        getBitmap();
+      } else {
+        finalHeight = pi.getHeight();
+      }
+    }
+    return finalHeight;
+  }
+
+  protected int getWidth() {
+    if (finalWidth == 0) {
+      PageInformation pi = (PageInformation) getPageInformationSegment().getSegmentData();
+      finalWidth = pi.getWidth();
+    }
+    return finalWidth;
+  }
+
+  protected int getResolutionX() {
+    if (resolutionX == 0) {
+      PageInformation pi = (PageInformation) getPageInformationSegment().getSegmentData();
+      resolutionX = pi.getResolutionX();
+    }
+    return resolutionX;
+  }
+
+  protected int getResolutionY() {
+    if (resolutionY == 0) {
+      PageInformation pi = (PageInformation) getPageInformationSegment().getSegmentData();
+      resolutionY = pi.getResolutionY();
+    }
+    return resolutionY;
+  }
+
+  @Override
+  public String toString() {
+    return getClass().getSimpleName() + " (Page number: " + pageNumber + ")";
+  }
+}

http://git-wip-us.apache.org/repos/asf/pdfbox-jbig2/blob/4619d28b/src/main/java/org/apache/pdfbox/jbig2/JBIG2ReadParam.java
----------------------------------------------------------------------
diff --git a/src/main/java/org/apache/pdfbox/jbig2/JBIG2ReadParam.java b/src/main/java/org/apache/pdfbox/jbig2/JBIG2ReadParam.java
new file mode 100644
index 0000000..7554df2
--- /dev/null
+++ b/src/main/java/org/apache/pdfbox/jbig2/JBIG2ReadParam.java
@@ -0,0 +1,48 @@
+/**
+ * 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;
+
+import java.awt.Dimension;
+import java.awt.Rectangle;
+
+import javax.imageio.ImageReadParam;
+
+/**
+ * This class extends {@code ImageReadParam} and contains region of interest and scale / subsampling
+ * functionality
+ */
+public class JBIG2ReadParam extends ImageReadParam {
+
+  public JBIG2ReadParam() {
+    this(1, 1, 0, 0, null, null);
+  }
+
+  public JBIG2ReadParam(final int sourceXSubsampling, final int sourceYSubsampling, final int subsamplingXOffset,
+      final int subsamplingYOffset, final Rectangle sourceRegion, final Dimension sourceRenderSize) {
+    this.canSetSourceRenderSize = true;
+    this.sourceRegion = sourceRegion;
+    this.sourceRenderSize = sourceRenderSize;
+
+    if (sourceXSubsampling < 1 || sourceYSubsampling < 1) {
+      throw new IllegalArgumentException("Illegal subsampling factor: shall be 1 or greater; but was "
+          + " sourceXSubsampling=" + sourceXSubsampling + ", sourceYSubsampling=" + sourceYSubsampling);
+    }
+
+    setSourceSubsampling(sourceXSubsampling, sourceYSubsampling, subsamplingXOffset, subsamplingYOffset);
+  }
+}

http://git-wip-us.apache.org/repos/asf/pdfbox-jbig2/blob/4619d28b/src/main/java/org/apache/pdfbox/jbig2/Region.java
----------------------------------------------------------------------
diff --git a/src/main/java/org/apache/pdfbox/jbig2/Region.java b/src/main/java/org/apache/pdfbox/jbig2/Region.java
new file mode 100644
index 0000000..dd30dcd
--- /dev/null
+++ b/src/main/java/org/apache/pdfbox/jbig2/Region.java
@@ -0,0 +1,48 @@
+/**
+ * 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;
+
+import java.io.IOException;
+
+import org.apache.pdfbox.jbig2.err.IntegerMaxValueException;
+import org.apache.pdfbox.jbig2.err.InvalidHeaderValueException;
+import org.apache.pdfbox.jbig2.segments.RegionSegmentInformation;
+
+/**
+ * Interface for all JBIG2 region segments.
+ */
+public interface Region extends SegmentData {
+
+  /**
+   * Decodes and returns a regions content.
+   * 
+   * @return The decoded region as {@link Bitmap}.
+   * 
+   * @throws IOException
+   * @throws IntegerMaxValueException
+   * @throws InvalidHeaderValueException
+   */
+  public Bitmap getRegionBitmap() throws IOException, IntegerMaxValueException, InvalidHeaderValueException;
+
+  /**
+   * Simply returns the {@link RegionSegmentInformation}.
+   * 
+   * @return The {@link RegionSegmentInformation}.
+   */
+  public RegionSegmentInformation getRegionInfo();
+}

http://git-wip-us.apache.org/repos/asf/pdfbox-jbig2/blob/4619d28b/src/main/java/org/apache/pdfbox/jbig2/SegmentData.java
----------------------------------------------------------------------
diff --git a/src/main/java/org/apache/pdfbox/jbig2/SegmentData.java b/src/main/java/org/apache/pdfbox/jbig2/SegmentData.java
new file mode 100644
index 0000000..8efca74
--- /dev/null
+++ b/src/main/java/org/apache/pdfbox/jbig2/SegmentData.java
@@ -0,0 +1,43 @@
+/**
+ * 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;
+
+import java.io.IOException;
+
+import org.apache.pdfbox.jbig2.err.IntegerMaxValueException;
+import org.apache.pdfbox.jbig2.err.InvalidHeaderValueException;
+import org.apache.pdfbox.jbig2.io.SubInputStream;
+
+/**
+ * Interface for all data parts of segments.
+ */
+public interface SegmentData {
+
+  /**
+   * Parse the stream and read information of header.
+   * 
+   * @param header - The segments' header (to make referred-to segments available in data part).
+   * @param sis - Wrapped {@code ImageInputStream} into {@code SubInputStream}.
+   * 
+   * @throws InvalidHeaderValueException
+   * @throws IntegerMaxValueException
+   * @throws IOException
+   */
+  public void init(SegmentHeader header, SubInputStream sis) throws InvalidHeaderValueException, IntegerMaxValueException,
+      IOException;
+}

http://git-wip-us.apache.org/repos/asf/pdfbox-jbig2/blob/4619d28b/src/main/java/org/apache/pdfbox/jbig2/SegmentHeader.java
----------------------------------------------------------------------
diff --git a/src/main/java/org/apache/pdfbox/jbig2/SegmentHeader.java b/src/main/java/org/apache/pdfbox/jbig2/SegmentHeader.java
new file mode 100644
index 0000000..c4e9cea
--- /dev/null
+++ b/src/main/java/org/apache/pdfbox/jbig2/SegmentHeader.java
@@ -0,0 +1,432 @@
+/**
+ * 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;
+
+import java.io.IOException;
+import java.lang.ref.Reference;
+import java.lang.ref.SoftReference;
+import java.util.HashMap;
+import java.util.Map;
+
+import javax.imageio.stream.ImageInputStream;
+
+import org.apache.pdfbox.jbig2.io.SubInputStream;
+import org.apache.pdfbox.jbig2.segments.EndOfStripe;
+import org.apache.pdfbox.jbig2.segments.GenericRefinementRegion;
+import org.apache.pdfbox.jbig2.segments.GenericRegion;
+import org.apache.pdfbox.jbig2.segments.HalftoneRegion;
+import org.apache.pdfbox.jbig2.segments.PageInformation;
+import org.apache.pdfbox.jbig2.segments.PatternDictionary;
+import org.apache.pdfbox.jbig2.segments.Profiles;
+import org.apache.pdfbox.jbig2.segments.SymbolDictionary;
+import org.apache.pdfbox.jbig2.segments.Table;
+import org.apache.pdfbox.jbig2.segments.TextRegion;
+import org.apache.pdfbox.jbig2.util.log.Logger;
+import org.apache.pdfbox.jbig2.util.log.LoggerFactory;
+
+/**
+ * The basic class for all JBIG2 segments.
+ */
+@SuppressWarnings("unchecked")
+public class SegmentHeader {
+  private static final Logger log = LoggerFactory.getLogger(SegmentHeader.class);
+
+  private static final Map<Integer, Class<? extends SegmentData>> SEGMENT_TYPE_MAP = new HashMap<Integer, Class<? extends SegmentData>>();
+
+  static {
+    Object SEGMENT_TYPES[][] = {
+        {
+            0, SymbolDictionary.class
+        }, {
+            4, TextRegion.class
+        }, {
+            6, TextRegion.class
+        }, {
+            7, TextRegion.class
+        }, {
+            16, PatternDictionary.class
+        }, {
+            20, HalftoneRegion.class
+        }, {
+            22, HalftoneRegion.class
+        }, {
+            23, HalftoneRegion.class
+        }, {
+            36, GenericRegion.class
+        }, {
+            38, GenericRegion.class
+        }, {
+            39, GenericRegion.class
+        }, {
+            40, GenericRefinementRegion.class
+        }, {
+            42, GenericRefinementRegion.class
+        }, {
+            43, GenericRefinementRegion.class
+        }, {
+            48, PageInformation.class
+        }, {
+            50, EndOfStripe.class
+        }, {
+            52, Profiles.class
+        }, {
+            53, Table.class
+        },
+    };
+
+    for (int i = 0; i < SEGMENT_TYPES.length; i++) {
+      Object[] objects = SEGMENT_TYPES[i];
+      SEGMENT_TYPE_MAP.put((Integer) objects[0], (Class<? extends SegmentData>) objects[1]);
+    }
+  }
+
+  private int segmentNr;
+  private int segmentType;
+  private byte retainFlag;
+  private int pageAssociation;
+  private byte pageAssociationFieldSize;
+  private SegmentHeader[] rtSegments;
+  private long segmentHeaderLength;
+  private long segmentDataLength;
+  private long segmentDataStartOffset;
+  private final SubInputStream subInputStream;
+
+  private Reference<SegmentData> segmentData;
+
+
+  public SegmentHeader(JBIG2Document document, SubInputStream sis, long offset, int organisationType)
+      throws IOException {
+    this.subInputStream = sis;
+    parse(document, sis, offset, organisationType);
+  }
+
+  /**
+   * 
+   * 
+   * @param document
+   * @param subInputStream
+   * @param organisationType
+   * @param offset - The offset where the segment header starts
+   * @throws IOException
+   */
+  private void parse(JBIG2Document document, ImageInputStream subInputStream, long offset, int organisationType)
+      throws IOException {
+
+    printDebugMessage("\n########################");
+    printDebugMessage("Segment parsing started.");
+
+    subInputStream.seek(offset);
+    printDebugMessage("|-Seeked to offset: " + offset);
+
+    /* 7.2.2 Segment number */
+    readSegmentNumber(subInputStream);
+
+    /* 7.2.3 Segment header flags */
+    readSegmentHeaderFlag(subInputStream);
+
+    /* 7.2.4 Amount of referred-to segments */
+    int countOfRTS = readAmountOfReferredToSegments(subInputStream);
+
+    /* 7.2.5 Referred-to segments numbers */
+    int[] rtsNumbers = readReferredToSegmentsNumbers(subInputStream, countOfRTS);
+
+    /* 7.2.6 Segment page association (Checks how big the page association field is.) */
+    readSegmentPageAssociation(document, subInputStream, countOfRTS, rtsNumbers);
+
+    /* 7.2.7 Segment data length (Contains the length of the data part (in bytes).) */
+    readSegmentDataLength(subInputStream);
+
+    readDataStartOffset(subInputStream, organisationType);
+    readSegmentHeaderLength(subInputStream, offset);
+    printDebugMessage("########################\n");
+  }
+
+  /**
+   * 7.2.2 Segment number
+   * 
+   * @param subInputStream
+   * @throws IOException
+   */
+  private void readSegmentNumber(ImageInputStream subInputStream) throws IOException {
+    segmentNr = (int) (subInputStream.readBits(32) & 0xffffffff);
+    printDebugMessage("|-Segment Nr: " + segmentNr);
+  }
+
+  /**
+   * 7.2.3 Segment header flags
+   * 
+   * @param subInputStream
+   * @throws IOException
+   */
+  private void readSegmentHeaderFlag(ImageInputStream subInputStream) throws IOException {
+    // Bit 7: Retain Flag, if 1, this segment is flagged as retained;
+    retainFlag = (byte) subInputStream.readBit();
+    printDebugMessage("|-Retain flag: " + retainFlag);
+
+    // Bit 6: Size of the page association field. One byte if 0, four bytes if 1;
+    pageAssociationFieldSize = (byte) subInputStream.readBit();
+    printDebugMessage("|-Page association field size=" + pageAssociationFieldSize);
+
+    // Bit 5-0: Contains the values (between 0 and 62 with gaps) for segment types, specified in 7.3
+    segmentType = (int) (subInputStream.readBits(6) & 0xff);
+    printDebugMessage("|-Segment type=" + segmentType);
+  }
+
+  /**
+   * 7.2.4 Amount of referred-to segments
+   * 
+   * @param subInputStream
+   * @return The amount of referred-to segments.
+   * @throws IOException
+   */
+  private int readAmountOfReferredToSegments(ImageInputStream subInputStream) throws IOException {
+    int countOfRTS = (int) (subInputStream.readBits(3) & 0xf);
+    printDebugMessage("|-RTS count: " + countOfRTS);
+
+    byte[] retainBit;
+
+    printDebugMessage("  |-Stream position before RTS: " + subInputStream.getStreamPosition());
+
+    if (countOfRTS <= 4) {
+      /* short format */
+      retainBit = new byte[5];
+      for (int i = 0; i <= 4; i++) {
+        retainBit[i] = (byte) subInputStream.readBit();
+      }
+    } else {
+      /* long format */
+      countOfRTS = (int) (subInputStream.readBits(29) & 0xffffffff);
+
+      int arrayLength = (countOfRTS + 8) >> 3;
+      retainBit = new byte[arrayLength <<= 3];
+
+      for (int i = 0; i < arrayLength; i++) {
+        retainBit[i] = (byte) subInputStream.readBit();
+      }
+    }
+
+    printDebugMessage("  |-Stream position after RTS: " + subInputStream.getStreamPosition());
+
+    return countOfRTS;
+  }
+
+  /**
+   * 7.2.5 Referred-to segments numbers
+   * <p>
+   * Gathers all segment numbers of referred-to segments. The segments itself are stored in the
+   * {@link #rtSegments} array.
+   * 
+   * @param subInputStream - Wrapped source data input stream.
+   * @param countOfRTS - The amount of referred-to segments.
+   * 
+   * @return An array with the segment number of all referred-to segments.
+   * 
+   * @throws IOException
+   */
+  private int[] readReferredToSegmentsNumbers(ImageInputStream subInputStream, int countOfRTS) throws IOException {
+    int[] rtsNumbers = new int[countOfRTS];
+
+    if (countOfRTS > 0) {
+      short rtsSize = 1;
+      if (segmentNr > 256) {
+        rtsSize = 2;
+        if (segmentNr > 65536) {
+          rtsSize = 4;
+        }
+      }
+
+      rtSegments = new SegmentHeader[countOfRTS];
+
+      printDebugMessage("|-Length of RT segments list: " + rtSegments.length);
+
+      for (int i = 0; i < countOfRTS; i++) {
+        rtsNumbers[i] = (int) (subInputStream.readBits(rtsSize << 3) & 0xffffffff);
+      }
+    }
+
+    return rtsNumbers;
+  }
+
+  /**
+   * 7.2.6 Segment page association
+   * 
+   * @param document
+   * @param subInputStream
+   * @param countOfRTS
+   * @param rtsNumbers
+   * @throws IOException
+   */
+  private void readSegmentPageAssociation(JBIG2Document document, ImageInputStream subInputStream, int countOfRTS,
+      int[] rtsNumbers) throws IOException {
+    if (pageAssociationFieldSize == 0) {
+      // Short format
+      pageAssociation = (short) (subInputStream.readBits(8) & 0xff);
+    } else {
+      // Long format
+      pageAssociation = (int) (subInputStream.readBits(32) & 0xffffffff);
+    }
+
+    if (countOfRTS > 0) {
+      final JBIG2Page page = document.getPage(pageAssociation);
+      for (int i = 0; i < countOfRTS; i++) {
+        rtSegments[i] = (null != page ? page.getSegment(rtsNumbers[i]) : document.getGlobalSegment(rtsNumbers[i]));
+      }
+    }
+  }
+
+  /**
+   * 7.2.7 Segment data length
+   * <p>
+   * Contains the length of the data part in bytes.
+   * 
+   * @param subInputStream
+   * @throws IOException
+   */
+  private void readSegmentDataLength(ImageInputStream subInputStream) throws IOException {
+    segmentDataLength = (subInputStream.readBits(32) & 0xffffffff);
+    printDebugMessage("|-Data length: " + segmentDataLength);
+  }
+
+  /**
+   * Sets the offset only if organization type is SEQUENTIAL. If random, data starts after segment
+   * headers and can be determined when all segment headers are parsed and allocated.
+   * 
+   * @param subInputStream
+   * @param organisationType
+   * @throws IOException
+   */
+  private void readDataStartOffset(ImageInputStream subInputStream, int organisationType) throws IOException {
+    if (organisationType == JBIG2Document.SEQUENTIAL) {
+      printDebugMessage("|-Organization is sequential.");
+      segmentDataStartOffset = subInputStream.getStreamPosition();
+    }
+  }
+
+  private void readSegmentHeaderLength(ImageInputStream subInputStream, long offset) throws IOException {
+    segmentHeaderLength = subInputStream.getStreamPosition() - offset;
+    printDebugMessage("|-Segment header length: " + segmentHeaderLength);
+  }
+
+  private void printDebugMessage(String message) {
+    log.debug(message);
+  }
+
+  public int getSegmentNr() {
+    return segmentNr;
+  }
+
+  public int getSegmentType() {
+    return segmentType;
+  }
+
+  public long getSegmentHeaderLength() {
+    return segmentHeaderLength;
+  }
+
+  public long getSegmentDataLength() {
+    return segmentDataLength;
+  }
+
+  public long getSegmentDataStartOffset() {
+    return segmentDataStartOffset;
+  }
+
+  public void setSegmentDataStartOffset(long segmentDataStartOffset) {
+    this.segmentDataStartOffset = segmentDataStartOffset;
+  }
+
+  public SegmentHeader[] getRtSegments() {
+    return rtSegments;
+  }
+
+  public int getPageAssociation() {
+    return pageAssociation;
+  }
+
+  public short getRetainFlag() {
+    return retainFlag;
+  }
+
+  /**
+   * Creates and returns a new {@link SubInputStream} that provides the data part of this segment.
+   * It is a clipped view of the source input stream.
+   * 
+   * @return The {@link SubInputStream} that represents the data part of the segment.
+   */
+  public SubInputStream getDataInputStream() {
+    return new SubInputStream(subInputStream, segmentDataStartOffset, segmentDataLength);
+  }
+
+  /**
+   * Retrieves the segments' data part.
+   * 
+   * @return Retrieved {@link SegmentData} instance.
+   */
+  public SegmentData getSegmentData() {
+    SegmentData segmentDataPart = null;
+
+    if (null != segmentData) {
+      segmentDataPart = segmentData.get();
+    }
+
+    if (null == segmentDataPart) {
+      try {
+
+        Class<? extends SegmentData> segmentClass = SEGMENT_TYPE_MAP.get(segmentType);
+
+        if (null == segmentClass) {
+          throw new IllegalArgumentException("No segment class for type " + segmentType);
+        }
+
+        segmentDataPart = segmentClass.newInstance();
+        segmentDataPart.init(this, getDataInputStream());
+
+        segmentData = new SoftReference<SegmentData>(segmentDataPart);
+
+      } catch (Exception e) {
+        throw new RuntimeException("Can't instantiate segment class", e);
+      }
+    }
+
+    return segmentDataPart;
+  }
+
+  public void cleanSegmentData() {
+    if (segmentData != null) {
+      segmentData = null;
+    }
+  }
+
+  public String toString() {
+    StringBuilder stringBuilder = new StringBuilder();
+
+    if (rtSegments != null) {
+      for (SegmentHeader s : rtSegments) {
+        stringBuilder.append(s.segmentNr + " ");
+      }
+    } else {
+      stringBuilder.append("none");
+    }
+
+    return "\n#SegmentNr: " + segmentNr //
+        + "\n SegmentType: " + segmentType //
+        + "\n PageAssociation: " + pageAssociation //
+        + "\n Referred-to segments: " + stringBuilder.toString() //
+        + "\n"; //
+  }
+}

http://git-wip-us.apache.org/repos/asf/pdfbox-jbig2/blob/4619d28b/src/main/java/org/apache/pdfbox/jbig2/TestImage.java
----------------------------------------------------------------------
diff --git a/src/main/java/org/apache/pdfbox/jbig2/TestImage.java b/src/main/java/org/apache/pdfbox/jbig2/TestImage.java
new file mode 100644
index 0000000..10bb9c7
--- /dev/null
+++ b/src/main/java/org/apache/pdfbox/jbig2/TestImage.java
@@ -0,0 +1,242 @@
+/**
+ * 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;
+
+import java.awt.Dimension;
+import java.awt.Graphics;
+import java.awt.Graphics2D;
+import java.awt.Image;
+import java.awt.Insets;
+import java.awt.MediaTracker;
+import java.awt.Point;
+import java.awt.image.BufferedImage;
+import java.awt.image.ColorModel;
+import java.awt.image.DataBuffer;
+import java.awt.image.DataBufferByte;
+import java.awt.image.IndexColorModel;
+import java.awt.image.MultiPixelPackedSampleModel;
+import java.awt.image.Raster;
+import java.awt.image.SampleModel;
+import java.awt.image.WritableRaster;
+import java.io.IOException;
+
+import javax.swing.JComponent;
+import javax.swing.JFrame;
+import javax.swing.JScrollPane;
+
+/**
+ * This is a utility class. It can be used to show intermediary results.
+ */
+public class TestImage extends JFrame {
+  private static final long serialVersionUID = 7353175320371957550L;
+
+  public static void main(String[] args) {
+    int w = 250;
+    int h = 250;
+
+    // (w+7) / 8 entspricht Aufrundung!
+    int scanlineStride = (w + 7) / 8;
+
+    // hier sind die Daten
+    byte data[] = new byte[h * scanlineStride];
+
+    // dummy-Daten erzeugen
+    for (int i = 0; i < data.length; i++)
+      data[i] = (byte) i;
+
+    new TestImage(data, w, h, scanlineStride);
+  }
+
+  static class ImageComponent extends JComponent {
+    private static final long serialVersionUID = -5921296548288376287L;
+    Image myImage;
+    int imgWidth = -1;
+    int imgHeight = -1;
+    Dimension prefSize = null;
+    private int scale = 1;
+
+    /**
+     * Constructor for ImageComponent.
+     */
+    protected ImageComponent() {
+      super();
+    }
+
+    /**
+     * Constructor for ImageComponent.
+     */
+    public ImageComponent(Image image) {
+      super();
+      setImage(image);
+    }
+
+    /**
+     * Gets the preffered Size of the Component
+     * 
+     * @param image java.awt.Image
+     */
+    public Dimension getPreferredSize() {
+      if (prefSize != null)
+        return this.prefSize;
+      else
+        return super.getPreferredSize();
+    }
+
+    /**
+     * Gets the minimum Size of the Component
+     * 
+     * @param image java.awt.Image
+     */
+    public Dimension getMinimumSize() {
+      if (prefSize != null)
+        return prefSize;
+      else
+        return super.getMinimumSize();
+    }
+
+    /**
+     * Sets an image to be shown
+     * 
+     * @param image java.awt.Image
+     */
+    public void setImage(Image image) {
+      if (myImage != null) {
+        myImage.flush();
+      }
+
+      myImage = image;
+
+      if (myImage != null) {
+        MediaTracker mt = new MediaTracker(this);
+
+        mt.addImage(myImage, 0);
+
+        try {
+          mt.waitForAll();
+        } catch (Exception ex) {
+        }
+
+        imgWidth = myImage.getWidth(this);
+        imgHeight = myImage.getHeight(this);
+
+        setSize(imgWidth * scale, imgHeight * scale);
+        prefSize = getSize();
+        invalidate();
+        validate();
+        repaint();
+      }
+    }
+
+    /**
+     * Get the Insets fo the Component
+     * 
+     * @return Insets the Insets of the Component
+     */
+    public Insets getInsets() {
+      return new Insets(1, 1, 1, 1);
+    }
+
+    /**
+     * Paints the component
+     * 
+     * @param g java.awt.Graphics
+     */
+    protected void paintComponent(Graphics g) {
+      Graphics2D g2 = (Graphics2D) g;
+      if (myImage != null) {
+        g2.scale(scale, scale);
+        g2.drawImage(myImage, 1, 1, imgWidth, imgHeight, this);
+      }
+    }
+
+    public void setScale(int scale) {
+      this.scale = scale;
+
+      setSize(imgWidth * scale, imgHeight * scale);
+      prefSize = getSize();
+
+      revalidate();
+      repaint();
+    }
+
+    public int getScale() {
+      return scale;
+    }
+  }
+
+  public TestImage(byte data[], int w, int h, int scanlineStride) {
+    super("Demobild");
+
+    // Color-Model sagt: bit = 0 -> schwarz, bit = 1 -> weiss. Ggf. umdrehen.
+    ColorModel colorModel = new IndexColorModel(1, 2, new byte[]{
+        (byte) 0xff, 0x00
+    }, new byte[]{
+        (byte) 0xff, 0x00
+    }, new byte[]{
+        (byte) 0xff, 0x00
+    });
+
+    DataBuffer dataBuffer = new DataBufferByte(data, data.length);
+    SampleModel sampleModel = new MultiPixelPackedSampleModel(DataBuffer.TYPE_BYTE, w, h, 1, scanlineStride, 0);
+    WritableRaster writableRaster = Raster.createWritableRaster(sampleModel, dataBuffer, new Point(0, 0));
+
+    BufferedImage image = new BufferedImage(colorModel, writableRaster, false, null);
+
+    ImageComponent imageComponent = new ImageComponent(image);
+    // imageComponent.setScale(4);
+
+    JScrollPane sp = new JScrollPane(imageComponent);
+
+    setContentPane(sp);
+
+    pack();
+    setSize(new Dimension(1600, 900));
+    setVisible(true);
+
+    try {
+      System.in.read();
+    } catch (IOException e) {
+      // TODO Auto-generated catch block
+      e.printStackTrace();
+    }
+  }
+
+  public TestImage(BufferedImage bufferedImage) {
+    super("Demobild");
+
+    setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
+
+    ImageComponent imageComponent = new ImageComponent(bufferedImage);
+    imageComponent.setScale(1);
+
+    JScrollPane sp = new JScrollPane(imageComponent);
+
+    setContentPane(sp);
+
+    pack();
+    setSize(new Dimension(1600, 900));
+    setVisible(true);
+
+    try {
+      System.in.read();
+    } catch (IOException e) {
+      // TODO Auto-generated catch block
+      e.printStackTrace();
+    }
+  }
+}

http://git-wip-us.apache.org/repos/asf/pdfbox-jbig2/blob/4619d28b/src/main/java/org/apache/pdfbox/jbig2/decoder/arithmetic/ArithmeticDecoder.java
----------------------------------------------------------------------
diff --git a/src/main/java/org/apache/pdfbox/jbig2/decoder/arithmetic/ArithmeticDecoder.java b/src/main/java/org/apache/pdfbox/jbig2/decoder/arithmetic/ArithmeticDecoder.java
new file mode 100644
index 0000000..b380afd
--- /dev/null
+++ b/src/main/java/org/apache/pdfbox/jbig2/decoder/arithmetic/ArithmeticDecoder.java
@@ -0,0 +1,262 @@
+/**
+ * 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.decoder.arithmetic;
+
+import java.io.IOException;
+
+import javax.imageio.stream.ImageInputStream;
+
+/**
+ * This class represents the arithmetic decoder, described in ISO/IEC 14492:2001 in E.3
+ */
+public class ArithmeticDecoder {
+
+  private static final int QE[][] = {
+      {
+          0x5601, 1, 1, 1
+      }, {
+          0x3401, 2, 6, 0
+      }, {
+          0x1801, 3, 9, 0
+      }, {
+          0x0AC1, 4, 12, 0
+      }, {
+          0x0521, 5, 29, 0
+      }, {
+          0x0221, 38, 33, 0
+      }, {
+          0x5601, 7, 6, 1
+      }, {
+          0x5401, 8, 14, 0
+      }, {
+          0x4801, 9, 14, 0
+      }, {
+          0x3801, 10, 14, 0
+      }, {
+          0x3001, 11, 17, 0
+      }, {
+          0x2401, 12, 18, 0
+      }, {
+          0x1C01, 13, 20, 0
+      }, {
+          0x1601, 29, 21, 0
+      }, {
+          0x5601, 15, 14, 1
+      }, {
+          0x5401, 16, 14, 0
+      }, {
+          0x5101, 17, 15, 0
+      }, {
+          0x4801, 18, 16, 0
+      }, {
+          0x3801, 19, 17, 0
+      }, {
+          0x3401, 20, 18, 0
+      }, {
+          0x3001, 21, 19, 0
+      }, {
+          0x2801, 22, 19, 0
+      }, {
+          0x2401, 23, 20, 0
+      }, {
+          0x2201, 24, 21, 0
+      }, {
+          0x1C01, 25, 22, 0
+      }, {
+          0x1801, 26, 23, 0
+      }, {
+          0x1601, 27, 24, 0
+      }, {
+          0x1401, 28, 25, 0
+      }, {
+          0x1201, 29, 26, 0
+      }, {
+          0x1101, 30, 27, 0
+      }, {
+          0x0AC1, 31, 28, 0
+      }, {
+          0x09C1, 32, 29, 0
+      }, {
+          0x08A1, 33, 30, 0
+      }, {
+          0x0521, 34, 31, 0
+      }, {
+          0x0441, 35, 32, 0
+      }, {
+          0x02A1, 36, 33, 0
+      }, {
+          0x0221, 37, 34, 0
+      }, {
+          0x0141, 38, 35, 0
+      }, {
+          0x0111, 39, 36, 0
+      }, {
+          0x0085, 40, 37, 0
+      }, {
+          0x0049, 41, 38, 0
+      }, {
+          0x0025, 42, 39, 0
+      }, {
+          0x0015, 43, 40, 0
+      }, {
+          0x0009, 44, 41, 0
+      }, {
+          0x0005, 45, 42, 0
+      }, {
+          0x0001, 45, 43, 0
+      }, {
+          0x5601, 46, 46, 0
+      }
+  };
+
+  private int a;
+  private long c;
+  private int ct;
+
+  private int b;
+
+  private long streamPos0;
+
+  private final ImageInputStream iis;
+
+  public ArithmeticDecoder(ImageInputStream iis) throws IOException {
+    this.iis = iis;
+    init();
+  }
+
+  private void init() throws IOException {
+    this.streamPos0 = iis.getStreamPosition();
+    b = this.iis.read();
+
+    c = b << 16;
+
+    byteIn();
+
+    c <<= 7;
+    ct -= 7;
+    a = 0x8000;
+  }
+
+  public int decode(CX cx) throws IOException {
+    int d;
+    final int qeValue = QE[cx.cx()][0];
+    final int icx = cx.cx();
+
+    a -= qeValue;
+
+    if ((c >> 16) < qeValue) {
+      d = lpsExchange(cx, icx, qeValue);
+      renormalize();
+    } else {
+      c -= (qeValue << 16);
+      if ((a & 0x8000) == 0) {
+        d = mpsExchange(cx, icx);
+        renormalize();
+      } else {
+        return cx.mps();
+      }
+    }
+
+    return d;
+  }
+
+  private void byteIn() throws IOException {
+    if (iis.getStreamPosition() > streamPos0) {
+      iis.seek(iis.getStreamPosition() - 1);
+    }
+
+    b = iis.read();
+
+    if (b == 0xFF) {
+      final int b1 = iis.read();
+      if (b1 > 0x8f) {
+        c += 0xff00;
+        ct = 8;
+        iis.seek(iis.getStreamPosition() - 2);
+      } else {
+        c += b1 << 9;
+        ct = 7;
+      }
+    } else {
+      b = iis.read();
+      c += b << 8;
+      ct = 8;
+    }
+
+    c &= 0xffffffffL;
+  }
+
+  private void renormalize() throws IOException {
+    do {
+      if (ct == 0) {
+        byteIn();
+      }
+
+      a <<= 1;
+      c <<= 1;
+      ct--;
+
+    } while ((a & 0x8000) == 0);
+
+    c &= 0xffffffffL;
+  }
+
+  private int mpsExchange(CX cx, int icx) {
+    final int mps = cx.mps();
+
+    if (a < QE[icx][0]) {
+
+      if (QE[icx][3] == 1) {
+        cx.toggleMps();
+      }
+
+      cx.setCx(QE[icx][2]);
+      return 1 - mps;
+    } else {
+      cx.setCx(QE[icx][1]);
+      return mps;
+    }
+  }
+
+  private int lpsExchange(CX cx, int icx, int qeValue) {
+    final int mps = cx.mps();
+
+    if (a < qeValue) {
+      cx.setCx(QE[icx][1]);
+      a = qeValue;
+
+      return mps;
+    } else {
+      if (QE[icx][3] == 1) {
+        cx.toggleMps();
+      }
+
+      cx.setCx(QE[icx][2]);
+      a = qeValue;
+      return 1 - mps;
+    }
+  }
+
+  int getA() {
+    return a;
+  }
+
+  long getC() {
+    return c;
+  }
+}

http://git-wip-us.apache.org/repos/asf/pdfbox-jbig2/blob/4619d28b/src/main/java/org/apache/pdfbox/jbig2/decoder/arithmetic/ArithmeticIntegerDecoder.java
----------------------------------------------------------------------
diff --git a/src/main/java/org/apache/pdfbox/jbig2/decoder/arithmetic/ArithmeticIntegerDecoder.java b/src/main/java/org/apache/pdfbox/jbig2/decoder/arithmetic/ArithmeticIntegerDecoder.java
new file mode 100644
index 0000000..0f9fa7c
--- /dev/null
+++ b/src/main/java/org/apache/pdfbox/jbig2/decoder/arithmetic/ArithmeticIntegerDecoder.java
@@ -0,0 +1,155 @@
+/**
+ * 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.decoder.arithmetic;
+
+import java.io.IOException;
+
+/**
+ * This class represents the arithmetic integer decoder, described in ISO/IEC 14492:2001 (Annex A).
+ */
+public class ArithmeticIntegerDecoder {
+
+  private final ArithmeticDecoder decoder;
+
+  private int prev;
+
+  public ArithmeticIntegerDecoder(ArithmeticDecoder decoder) {
+    this.decoder = decoder;
+  }
+
+  /**
+   * Arithmetic Integer Decoding Procedure, Annex A.2.
+   * 
+   * @return Decoded value.
+   * @throws IOException
+   */
+  public long decode(CX cxIAx) throws IOException {
+    int v = 0;
+    int d, s;
+
+    int bitsToRead;
+    int offset;
+
+    if (cxIAx == null) {
+      cxIAx = new CX(512, 1);
+    }
+
+    prev = 1;
+
+    cxIAx.setIndex(prev);
+    s = decoder.decode(cxIAx);
+    setPrev(s);
+
+    cxIAx.setIndex(prev);
+    d = decoder.decode(cxIAx);
+    setPrev(d);
+
+    if (d == 1) {
+      cxIAx.setIndex(prev);
+      d = decoder.decode(cxIAx);
+      setPrev(d);
+
+      if (d == 1) {
+        cxIAx.setIndex(prev);
+        d = decoder.decode(cxIAx);
+        setPrev(d);
+
+        if (d == 1) {
+          cxIAx.setIndex(prev);
+          d = decoder.decode(cxIAx);
+          setPrev(d);
+
+          if (d == 1) {
+            cxIAx.setIndex(prev);
+            d = decoder.decode(cxIAx);
+            setPrev(d);
+
+            if (d == 1) {
+              bitsToRead = 32;
+              offset = 4436;
+            } else {
+              bitsToRead = 12;
+              offset = 340;
+            }
+          } else {
+            bitsToRead = 8;
+            offset = 84;
+          }
+        } else {
+          bitsToRead = 6;
+          offset = 20;
+        }
+      } else {
+        bitsToRead = 4;
+        offset = 4;
+      }
+    } else {
+      bitsToRead = 2;
+      offset = 0;
+    }
+
+    for (int i = 0; i < bitsToRead; i++) {
+      cxIAx.setIndex(prev);
+      d = decoder.decode(cxIAx);
+      setPrev(d);
+      v = (v << 1) | d;
+    }
+
+    v += offset;
+
+    if (s == 0) {
+      return v;
+    } else if (s == 1 && v > 0) {
+      return -v;
+    }
+
+    return Long.MAX_VALUE;
+  }
+
+  private void setPrev(int bit) {
+    if (prev < 256) {
+      prev = ((prev << 1) | bit) & 0x1ff;
+    } else {
+      prev = ((((prev << 1) | bit) & 511) | 256) & 0x1ff;
+    }
+  }
+
+  /**
+   * The IAID decoding procedure, Annex A.3.
+   * 
+   * @param cxIAID - The contexts and statistics for decoding procedure.
+   * @param symCodeLen - Symbol code length.
+   * 
+   * @return The decoded value.
+   * 
+   * @throws IOException
+   */
+  public int decodeIAID(CX cxIAID, long symCodeLen) throws IOException {
+    // A.3 1)
+    prev = 1;
+
+    // A.3 2)
+    for (int i = 0; i < symCodeLen; i++) {
+      cxIAID.setIndex(prev);
+      prev = (prev << 1) | decoder.decode(cxIAID);
+    }
+
+    // A.3 3) & 4)
+    return (prev - (1 << symCodeLen));
+  }
+}

http://git-wip-us.apache.org/repos/asf/pdfbox-jbig2/blob/4619d28b/src/main/java/org/apache/pdfbox/jbig2/decoder/arithmetic/CX.java
----------------------------------------------------------------------
diff --git a/src/main/java/org/apache/pdfbox/jbig2/decoder/arithmetic/CX.java b/src/main/java/org/apache/pdfbox/jbig2/decoder/arithmetic/CX.java
new file mode 100644
index 0000000..6d03f58
--- /dev/null
+++ b/src/main/java/org/apache/pdfbox/jbig2/decoder/arithmetic/CX.java
@@ -0,0 +1,69 @@
+/**
+ * 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.decoder.arithmetic;
+
+/**
+ * CX represents the context used by arithmetic decoding and arithmetic integer decoding. It selects
+ * the probability estimate and statistics used during decoding procedure.
+ */
+public final class CX {
+  private int index;
+
+  private final byte cx[];
+  private final byte mps[];
+
+  /**
+   * @param size - Amount of context values.
+   * @param index - Start index.
+   */
+  public CX(int size, int index) {
+    this.index = index;
+    cx = new byte[size];
+    mps = new byte[size];
+  }
+
+  protected int cx() {
+    return cx[index] & 0x7f;
+  }
+
+  protected void setCx(int value) {
+    cx[index] = (byte) (value & 0x7f);
+  }
+
+  /**
+   * @return The decision. Possible values are {@code 0} or {@code 1}.
+   */
+  protected byte mps() {
+    return mps[index];
+  }
+
+  /**
+   * Flips the bit in actual "more predictable symbol" array element.
+   */
+  protected void toggleMps() {
+    mps[index] ^= 1;
+  }
+
+  protected int getIndex() {
+    return index;
+  }
+
+  public void setIndex(int index) {
+    this.index = index;
+  }
+}

http://git-wip-us.apache.org/repos/asf/pdfbox-jbig2/blob/4619d28b/src/main/java/org/apache/pdfbox/jbig2/decoder/huffman/EncodedTable.java
----------------------------------------------------------------------
diff --git a/src/main/java/org/apache/pdfbox/jbig2/decoder/huffman/EncodedTable.java b/src/main/java/org/apache/pdfbox/jbig2/decoder/huffman/EncodedTable.java
new file mode 100644
index 0000000..77e71c6
--- /dev/null
+++ b/src/main/java/org/apache/pdfbox/jbig2/decoder/huffman/EncodedTable.java
@@ -0,0 +1,91 @@
+/**
+ * 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.decoder.huffman;
+
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.List;
+
+import org.apache.pdfbox.jbig2.io.SubInputStream;
+import org.apache.pdfbox.jbig2.segments.Table;
+
+/**
+ * This class represents a encoded huffman table.
+ */
+public class EncodedTable extends HuffmanTable {
+
+  private Table table;
+
+  public EncodedTable(Table table) throws IOException {
+    this.table = table;
+    parseTable();
+  }
+
+  public void parseTable() throws IOException {
+
+    SubInputStream sis = table.getSubInputStream();
+
+    List<Code> codeTable = new ArrayList<Code>();
+
+    int prefLen, rangeLen, rangeLow;
+    int curRangeLow = table.getHtLow();
+
+    /* Annex B.2 5) - decode table lines */
+    while (curRangeLow < table.getHtHigh()) {
+      prefLen = (int) sis.readBits(table.getHtPS());
+      rangeLen = (int) sis.readBits(table.getHtRS());
+      rangeLow = curRangeLow;
+
+      codeTable.add(new Code(prefLen, rangeLen, rangeLow, false));
+
+      curRangeLow += 1 << rangeLen;
+    }
+
+    /* Annex B.2 6) */
+    prefLen = (int) sis.readBits(table.getHtPS());
+
+    /*
+     * Annex B.2 7) - lower range table line
+     * 
+     * Made some correction. Spec specifies an incorrect variable -> Replaced highPrefLen with
+     * lowPrefLen
+     */
+    rangeLen = 32;
+    rangeLow = table.getHtHigh() - 1;
+    codeTable.add(new Code(prefLen, rangeLen, rangeLow, true));
+    // }
+
+    /* Annex B.2 8) */
+    prefLen = (int) sis.readBits(table.getHtPS());
+
+    /* Annex B.2 9) - upper range table line */
+    rangeLen = 32;
+    rangeLow = table.getHtHigh();
+    codeTable.add(new Code(prefLen, rangeLen, rangeLow, false));
+
+    /* Annex B.2 10) - out-of-band table line */
+    if (table.getHtOOB() == 1) {
+      prefLen = (int) sis.readBits(table.getHtPS());
+      codeTable.add(new Code(prefLen, -1, -1, false));
+    }
+
+    System.out.println(codeTableToString(codeTable));
+
+    initTree(codeTable);
+  }
+}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/pdfbox-jbig2/blob/4619d28b/src/main/java/org/apache/pdfbox/jbig2/decoder/huffman/FixedSizeTable.java
----------------------------------------------------------------------
diff --git a/src/main/java/org/apache/pdfbox/jbig2/decoder/huffman/FixedSizeTable.java b/src/main/java/org/apache/pdfbox/jbig2/decoder/huffman/FixedSizeTable.java
new file mode 100644
index 0000000..b6f8822
--- /dev/null
+++ b/src/main/java/org/apache/pdfbox/jbig2/decoder/huffman/FixedSizeTable.java
@@ -0,0 +1,29 @@
+/**
+ * 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.decoder.huffman;
+
+import java.util.List;
+
+/**
+ * This class represents a fixed size huffman table.
+ */
+public class FixedSizeTable extends HuffmanTable {
+  public FixedSizeTable(List<Code> runCodeTable) {
+    initTree(runCodeTable);
+  }
+}

http://git-wip-us.apache.org/repos/asf/pdfbox-jbig2/blob/4619d28b/src/main/java/org/apache/pdfbox/jbig2/decoder/huffman/HuffmanTable.java
----------------------------------------------------------------------
diff --git a/src/main/java/org/apache/pdfbox/jbig2/decoder/huffman/HuffmanTable.java b/src/main/java/org/apache/pdfbox/jbig2/decoder/huffman/HuffmanTable.java
new file mode 100644
index 0000000..1e0f661
--- /dev/null
+++ b/src/main/java/org/apache/pdfbox/jbig2/decoder/huffman/HuffmanTable.java
@@ -0,0 +1,117 @@
+/**
+ * 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.decoder.huffman;
+
+import java.io.IOException;
+import java.util.List;
+
+import javax.imageio.stream.ImageInputStream;
+
+import org.apache.pdfbox.jbig2.JBIG2ImageReader;
+
+/**
+ * This abstract class is the base class for all types of huffman tables.
+ */
+public abstract class HuffmanTable {
+
+  /**
+   * This static class represents a code for use in huffman tables.
+   */
+  public static class Code {
+    final int prefixLength;
+    final int rangeLength;
+    final int rangeLow;
+    final boolean isLowerRange;
+    int code = -1;
+
+    public Code(int prefixLength, int rangeLength, int rangeLow, boolean isLowerRange) {
+      this.prefixLength = prefixLength;
+      this.rangeLength = rangeLength;
+      this.rangeLow = rangeLow;
+      this.isLowerRange = isLowerRange;
+    }
+
+    @Override
+    public String toString() {
+      return (code != -1 ? ValueNode.bitPattern(code, prefixLength) : "?") + "/" + prefixLength + "/" + rangeLength
+          + "/" + rangeLow;
+    }
+  }
+
+  private InternalNode rootNode = new InternalNode();
+
+  public void initTree(List<Code> codeTable) {
+    preprocessCodes(codeTable);
+
+    for (Code c : codeTable) {
+      rootNode.append(c);
+    }
+  }
+
+  public long decode(ImageInputStream iis) throws IOException {
+    return rootNode.decode(iis);
+  }
+
+  @Override
+  public String toString() {
+    return rootNode + "\n";
+  }
+
+  public static String codeTableToString(List<Code> codeTable) {
+    StringBuilder sb = new StringBuilder();
+
+    for (Code c : codeTable) {
+      sb.append(c.toString()).append("\n");
+    }
+
+    return sb.toString();
+  }
+
+  private void preprocessCodes(List<Code> codeTable) {
+    /* Annex B.3 1) - build the histogram */
+    int maxPrefixLength = 0;
+
+    for (Code c : codeTable) {
+      maxPrefixLength = Math.max(maxPrefixLength, c.prefixLength);
+    }
+
+    int lenCount[] = new int[maxPrefixLength + 1];
+    for (Code c : codeTable) {
+      lenCount[c.prefixLength]++;
+    }
+
+    int curCode;
+    int firstCode[] = new int[lenCount.length + 1];
+    lenCount[0] = 0;
+
+    /* Annex B.3 3) */
+    for (int curLen = 1; curLen <= lenCount.length; curLen++) {
+      firstCode[curLen] = (firstCode[curLen - 1] + (lenCount[curLen - 1]) << 1);
+      curCode = firstCode[curLen];
+      for (Code code : codeTable) {
+        if (code.prefixLength == curLen) {
+          code.code = curCode;
+          curCode++;
+        }
+      }
+    }
+
+    if (JBIG2ImageReader.DEBUG)
+      System.out.println(codeTableToString(codeTable));
+  }
+}

http://git-wip-us.apache.org/repos/asf/pdfbox-jbig2/blob/4619d28b/src/main/java/org/apache/pdfbox/jbig2/decoder/huffman/InternalNode.java
----------------------------------------------------------------------
diff --git a/src/main/java/org/apache/pdfbox/jbig2/decoder/huffman/InternalNode.java b/src/main/java/org/apache/pdfbox/jbig2/decoder/huffman/InternalNode.java
new file mode 100644
index 0000000..ea4a4dd
--- /dev/null
+++ b/src/main/java/org/apache/pdfbox/jbig2/decoder/huffman/InternalNode.java
@@ -0,0 +1,119 @@
+/**
+ * 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.decoder.huffman;
+
+import java.io.IOException;
+
+import javax.imageio.stream.ImageInputStream;
+
+import org.apache.pdfbox.jbig2.JBIG2ImageReader;
+import org.apache.pdfbox.jbig2.decoder.huffman.HuffmanTable.Code;
+
+/**
+ * This class represents an internal node of a huffman tree. It contains two child nodes.
+ */
+class InternalNode extends Node {
+  private final int depth;
+
+  private Node zero;
+  private Node one;
+
+  protected InternalNode() {
+    depth = 0;
+  }
+
+  protected InternalNode(int depth) {
+    this.depth = depth;
+  }
+
+  protected void append(Code c) {
+    if (JBIG2ImageReader.DEBUG)
+      System.out.println("I'm working on " + c.toString());
+
+    // ignore unused codes
+    if (c.prefixLength == 0)
+      return;
+
+    int shift = c.prefixLength - 1 - depth;
+
+    if (shift < 0)
+      throw new IllegalArgumentException("Negative shifting is not possible.");
+
+    int bit = (c.code >> shift) & 1;
+    if (shift == 0) {
+      if (c.rangeLength == -1) {
+        // the child will be a OutOfBand
+        if (bit == 1) {
+          if (one != null)
+            throw new IllegalStateException("already have a OOB for " + c);
+          one = new OutOfBandNode(c);
+        } else {
+          if (zero != null)
+            throw new IllegalStateException("already have a OOB for " + c);
+          zero = new OutOfBandNode(c);
+        }
+      } else {
+        // the child will be a ValueNode
+        if (bit == 1) {
+          if (one != null)
+            throw new IllegalStateException("already have a ValueNode for " + c);
+          one = new ValueNode(c);
+        } else {
+          if (zero != null)
+            throw new IllegalStateException("already have a ValueNode for " + c);
+          zero = new ValueNode(c);
+        }
+      }
+    } else {
+      // the child will be an InternalNode
+      if (bit == 1) {
+        if (one == null)
+          one = new InternalNode(depth + 1);
+        ((InternalNode) one).append(c);
+      } else {
+        if (zero == null)
+          zero = new InternalNode(depth + 1);
+        ((InternalNode) zero).append(c);
+      }
+    }
+  }
+
+  @Override
+  protected long decode(ImageInputStream iis) throws IOException {
+    int b = iis.readBit();
+    Node n = b == 0 ? zero : one;
+    return n.decode(iis);
+  }
+
+  @Override
+  public String toString() {
+    StringBuilder sb = new StringBuilder("\n");
+
+    pad(sb);
+    sb.append("0: ").append(zero).append("\n");
+    pad(sb);
+    sb.append("1: ").append(one).append("\n");
+
+    return sb.toString();
+  }
+
+  private void pad(StringBuilder sb) {
+    for (int i = 0; i < depth; i++)
+      sb.append("   ");
+  }
+}

http://git-wip-us.apache.org/repos/asf/pdfbox-jbig2/blob/4619d28b/src/main/java/org/apache/pdfbox/jbig2/decoder/huffman/Node.java
----------------------------------------------------------------------
diff --git a/src/main/java/org/apache/pdfbox/jbig2/decoder/huffman/Node.java b/src/main/java/org/apache/pdfbox/jbig2/decoder/huffman/Node.java
new file mode 100644
index 0000000..1adbf8c
--- /dev/null
+++ b/src/main/java/org/apache/pdfbox/jbig2/decoder/huffman/Node.java
@@ -0,0 +1,29 @@
+/**
+ * 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.decoder.huffman;
+
+import java.io.IOException;
+
+import javax.imageio.stream.ImageInputStream;
+
+/**
+ * Base class for all nodes in a huffman tree.
+ */
+abstract class Node {
+  protected abstract long decode(ImageInputStream iis) throws IOException;
+}

http://git-wip-us.apache.org/repos/asf/pdfbox-jbig2/blob/4619d28b/src/main/java/org/apache/pdfbox/jbig2/decoder/huffman/OutOfBandNode.java
----------------------------------------------------------------------
diff --git a/src/main/java/org/apache/pdfbox/jbig2/decoder/huffman/OutOfBandNode.java b/src/main/java/org/apache/pdfbox/jbig2/decoder/huffman/OutOfBandNode.java
new file mode 100644
index 0000000..c379c89
--- /dev/null
+++ b/src/main/java/org/apache/pdfbox/jbig2/decoder/huffman/OutOfBandNode.java
@@ -0,0 +1,40 @@
+/**
+ * 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.decoder.huffman;
+
+import java.io.IOException;
+
+import javax.imageio.stream.ImageInputStream;
+
+import org.apache.pdfbox.jbig2.decoder.huffman.HuffmanTable.Code;
+
+
+/**
+ * Represents a out of band node in a huffman tree.
+ */
+class OutOfBandNode extends Node {
+
+  protected OutOfBandNode(Code c) {
+  }
+
+  @Override
+  protected long decode(ImageInputStream iis) throws IOException {
+    return Long.MAX_VALUE;
+  }
+
+}

http://git-wip-us.apache.org/repos/asf/pdfbox-jbig2/blob/4619d28b/src/main/java/org/apache/pdfbox/jbig2/decoder/huffman/StandardTables.java
----------------------------------------------------------------------
diff --git a/src/main/java/org/apache/pdfbox/jbig2/decoder/huffman/StandardTables.java b/src/main/java/org/apache/pdfbox/jbig2/decoder/huffman/StandardTables.java
new file mode 100644
index 0000000..70452b8
--- /dev/null
+++ b/src/main/java/org/apache/pdfbox/jbig2/decoder/huffman/StandardTables.java
@@ -0,0 +1,277 @@
+/**
+ * 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.decoder.huffman;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import org.apache.pdfbox.jbig2.*;
+
+public class StandardTables {
+	static class StandardTable extends HuffmanTable {
+		private StandardTable(int table[][]) {
+
+			List<Code> codeTable = new ArrayList<Code>();
+
+			for (int i = 0; i < table.length; i++) {
+				int prefixLength = table[i][0];
+				int rangeLength = table[i][1];
+				int rangeLow = table[i][2];
+				boolean isLowerRange = false;
+				if (table[i].length > 3)
+					isLowerRange = true;
+				codeTable.add(new Code(prefixLength, rangeLength, rangeLow,
+						isLowerRange));
+			}
+
+			if (JBIG2ImageReader.DEBUG)
+				System.out.println(HuffmanTable.codeTableToString(codeTable));
+
+			initTree(codeTable);
+		}
+	}
+
+	// Fourth Value (999) is used for the LowerRange-line
+	private static final int TABLES[][][] = {
+	// B1
+			{   { 1, 4, 0 }, //
+					{ 2, 8, 16 }, //
+					{ 3, 16, 272 }, //
+					{ 3, 32, 65808 } /* high */
+			},
+			// B2
+			{   { 1, 0, 0 }, //
+					{ 2, 0, 1 }, //
+					{ 3, 0, 2 }, //
+					{ 4, 3, 3 }, //
+					{ 5, 6, 11 }, //
+					{ 6, 32, 75 }, /* high */
+					{ 6, -1, 0 } /* OOB */
+			},
+			// B3
+			{   { 8, 8, -256 }, //
+					{ 1, 0, 0 }, //
+					{ 2, 0, 1 }, //
+					{ 3, 0, 2 }, //
+					{ 4, 3, 3 }, //
+					{ 5, 6, 11 }, //
+					{ 8, 32, -257, 999 }, /* low */
+					{ 7, 32, 75 }, /* high */
+					{ 6, -1, 0 } /* OOB */
+			},
+			// B4
+			{   { 1, 0, 1 }, //
+					{ 2, 0, 2 }, //
+					{ 3, 0, 3 }, //
+					{ 4, 3, 4 }, //
+					{ 5, 6, 12 }, //
+					{ 5, 32, 76 } /* high */
+			},
+			// B5
+			{   { 7, 8, -255 }, //
+					{ 1, 0, 1 }, //
+					{ 2, 0, 2 }, //
+					{ 3, 0, 3 }, //
+					{ 4, 3, 4 }, //
+					{ 5, 6, 12 }, //
+					{ 7, 32, -256, 999 }, /* low */
+					{ 6, 32, 76 } /* high */
+			},
+			// B6
+			{   { 5, 10, -2048 }, //
+					{ 4, 9, -1024 }, //
+					{ 4, 8, -512 }, //
+					{ 4, 7, -256 }, //
+					{ 5, 6, -128 }, //
+					{ 5, 5, -64 }, //
+					{ 4, 5, -32 }, //
+					{ 2, 7, 0 }, //
+					{ 3, 7, 128 }, //
+					{ 3, 8, 256 }, //
+					{ 4, 9, 512 }, //
+					{ 4, 10, 1024 }, //
+					{ 6, 32, -2049, 999 }, /* low */
+					{ 6, 32, 2048 } /* high */
+			},
+			// B7
+			{   { 4, 9, -1024 }, //
+					{ 3, 8, -512 }, //
+					{ 4, 7, -256 }, //
+					{ 5, 6, -128 }, //
+					{ 5, 5, -64 }, //
+					{ 4, 5, -32 }, //
+					{ 4, 5, 0 }, //
+					{ 5, 5, 32 }, //
+					{ 5, 6, 64 }, //
+					{ 4, 7, 128 }, //
+					{ 3, 8, 256 }, //
+					{ 3, 9, 512 }, //
+					{ 3, 10, 1024 }, //
+					{ 5, 32, -1025, 999 }, /* low */
+					{ 5, 32, 2048 } /* high */
+			},
+			// B8
+			{   { 8, 3, -15 }, //
+					{ 9, 1, -7 }, //
+					{ 8, 1, -5 }, //
+					{ 9, 0, -3 }, //
+					{ 7, 0, -2 }, //
+					{ 4, 0, -1 }, //
+					{ 2, 1, 0 }, //
+					{ 5, 0, 2 }, //
+					{ 6, 0, 3 }, //
+					{ 3, 4, 4 }, //
+					{ 6, 1, 20 }, //
+					{ 4, 4, 22 }, //
+					{ 4, 5, 38 }, //
+					{ 5, 6, 70 }, //
+					{ 5, 7, 134 }, //
+					{ 6, 7, 262 }, //
+					{ 7, 8, 390 }, //
+					{ 6, 10, 646 }, //
+					{ 9, 32, -16, 999 }, /* low */
+					{ 9, 32, 1670 }, /* high */
+					{ 2, -1, 0 } /* OOB */
+			},
+			// B9
+			{   { 8, 4, -31 }, //
+					{ 9, 2, -15 }, //
+					{ 8, 2, -11 }, //
+					{ 9, 1, -7 }, //
+					{ 7, 1, -5 }, //
+					{ 4, 1, -3 }, //
+					{ 3, 1, -1 }, //
+					{ 3, 1, 1 }, //
+					{ 5, 1, 3 }, //
+					{ 6, 1, 5 }, //
+					{ 3, 5, 7 }, //
+					{ 6, 2, 39 }, //
+					{ 4, 5, 43 }, //
+					{ 4, 6, 75 }, //
+					{ 5, 7, 139 }, //
+					{ 5, 8, 267 }, //
+					{ 6, 8, 523 }, //
+					{ 7, 9, 779 }, //
+					{ 6, 11, 1291 }, //
+					{ 9, 32, -32, 999 }, /* low */
+					{ 9, 32, 3339 }, /* high */
+					{ 2, -1, 0 } /* OOB */
+			},
+			// B10
+			{   { 7, 4, -21 }, //
+					{ 8, 0, -5 }, //
+					{ 7, 0, -4 }, //
+					{ 5, 0, -3 }, //
+					{ 2, 2, -2 }, //
+					{ 5, 0, 2 }, //
+					{ 6, 0, 3 }, //
+					{ 7, 0, 4 }, //
+					{ 8, 0, 5 }, //
+					{ 2, 6, 6 }, //
+					{ 5, 5, 70 }, //
+					{ 6, 5, 102 }, //
+					{ 6, 6, 134 }, //
+					{ 6, 7, 198 }, //
+					{ 6, 8, 326 }, //
+					{ 6, 9, 582 }, //
+					{ 6, 10, 1094 }, //
+					{ 7, 11, 2118 }, //
+					{ 8, 32, -22, 999 }, /* low */
+					{ 8, 32, 4166 }, /* high */
+					{ 2, -1, 0 } /* OOB */
+			},
+			// B11
+			{   { 1, 0, 1 }, //
+					{ 2, 1, 2 }, //
+					{ 4, 0, 4 }, //
+					{ 4, 1, 5 }, //
+					{ 5, 1, 7 }, //
+					{ 5, 2, 9 }, //
+					{ 6, 2, 13 }, //
+					{ 7, 2, 17 }, //
+					{ 7, 3, 21 }, //
+					{ 7, 4, 29 }, //
+					{ 7, 5, 45 }, //
+					{ 7, 6, 77 }, //
+					{ 7, 32, 141 } /* high */
+			},
+			// B12
+			{   { 1, 0, 1 }, //
+					{ 2, 0, 2 }, //
+					{ 3, 1, 3 }, //
+					{ 5, 0, 5 }, //
+					{ 5, 1, 6 }, //
+					{ 6, 1, 8 }, //
+					{ 7, 0, 10 }, //
+					{ 7, 1, 11 }, //
+					{ 7, 2, 13 }, //
+					{ 7, 3, 17 }, //
+					{ 7, 4, 25 }, //
+					{ 8, 5, 41 }, //
+					{ 8, 32, 73 } //
+			},
+			// B13
+			{   { 1, 0, 1 }, //
+					{ 3, 0, 2 }, //
+					{ 4, 0, 3 }, //
+					{ 5, 0, 4 }, //
+					{ 4, 1, 5 }, //
+					{ 3, 3, 7 }, //
+					{ 6, 1, 15 }, //
+					{ 6, 2, 17 }, //
+					{ 6, 3, 21 }, //
+					{ 6, 4, 29 }, //
+					{ 6, 5, 45 }, //
+					{ 7, 6, 77 }, //
+					{ 7, 32, 141 } /* high */
+			},
+			// B14
+			{   { 3, 0, -2 }, //
+					{ 3, 0, -1 }, //
+					{ 1, 0, 0 }, //
+					{ 3, 0, 1 }, //
+					{ 3, 0, 2 } //
+			},
+			// B15
+			{   { 7, 4, -24 }, //
+					{ 6, 2, -8 }, //
+					{ 5, 1, -4 }, //
+					{ 4, 0, -2 }, //
+					{ 3, 0, -1 }, //
+					{ 1, 0, 0 }, //
+					{ 3, 0, 1 }, //
+					{ 4, 0, 2 }, //
+					{ 5, 1, 3 }, //
+					{ 6, 2, 5 }, //
+					{ 7, 4, 9 }, //
+					{ 7, 32, -25, 999 }, /* low */
+					{ 7, 32, 25 } /* high */
+			} };
+
+	private static HuffmanTable STANDARD_TABLES[] = new HuffmanTable[TABLES.length];
+
+	public static HuffmanTable getTable(int number) {
+		HuffmanTable table = STANDARD_TABLES[number - 1];
+		if (table == null) {
+			table = new StandardTable(TABLES[number - 1]);
+			STANDARD_TABLES[number - 1] = table;
+		}
+
+		return table;
+	}
+}

http://git-wip-us.apache.org/repos/asf/pdfbox-jbig2/blob/4619d28b/src/main/java/org/apache/pdfbox/jbig2/decoder/huffman/ValueNode.java
----------------------------------------------------------------------
diff --git a/src/main/java/org/apache/pdfbox/jbig2/decoder/huffman/ValueNode.java b/src/main/java/org/apache/pdfbox/jbig2/decoder/huffman/ValueNode.java
new file mode 100644
index 0000000..7543f7e
--- /dev/null
+++ b/src/main/java/org/apache/pdfbox/jbig2/decoder/huffman/ValueNode.java
@@ -0,0 +1,59 @@
+/**
+ * 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.decoder.huffman;
+
+import java.io.IOException;
+
+import javax.imageio.stream.ImageInputStream;
+
+import org.apache.pdfbox.jbig2.decoder.huffman.HuffmanTable.Code;
+
+/**
+ * Represents a value node in a huffman tree. It is a leaf of a tree.
+ */
+class ValueNode extends Node {
+  private int rangeLen;
+  private int rangeLow;
+  private boolean isLowerRange;
+
+  protected ValueNode(Code c) {
+    rangeLen = c.rangeLength;
+    rangeLow = c.rangeLow;
+    isLowerRange = c.isLowerRange;
+  }
+
+  @Override
+  protected long decode(ImageInputStream iis) throws IOException {
+
+    if (isLowerRange) {
+      /* B.4 4) */
+      return (rangeLow - iis.readBits(rangeLen));
+    } else {
+      /* B.4 5) */
+      return rangeLow + iis.readBits(rangeLen);
+    }
+  }
+
+  static String bitPattern(int v, int len) {
+    char result[] = new char[len];
+    for (int i = 1; i <= len; i++)
+      result[i - 1] = (v >> (len - i) & 1) != 0 ? '1' : '0';
+
+    return new String(result);
+  }
+}