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);
+ }
+}