You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@pdfbox.apache.org by le...@apache.org on 2017/10/16 18:09:17 UTC
[4/8] pdfbox-jbig2 git commit: initial commit of the JBig2 ImageIO
plugin
http://git-wip-us.apache.org/repos/asf/pdfbox-jbig2/blob/4619d28b/src/main/java/org/apache/pdfbox/jbig2/segments/GenericRegion.java
----------------------------------------------------------------------
diff --git a/src/main/java/org/apache/pdfbox/jbig2/segments/GenericRegion.java b/src/main/java/org/apache/pdfbox/jbig2/segments/GenericRegion.java
new file mode 100644
index 0000000..9c42f04
--- /dev/null
+++ b/src/main/java/org/apache/pdfbox/jbig2/segments/GenericRegion.java
@@ -0,0 +1,906 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.pdfbox.jbig2.segments;
+
+import java.io.IOException;
+
+import org.apache.pdfbox.jbig2.Bitmap;
+import org.apache.pdfbox.jbig2.Region;
+import org.apache.pdfbox.jbig2.SegmentHeader;
+import org.apache.pdfbox.jbig2.decoder.arithmetic.ArithmeticDecoder;
+import org.apache.pdfbox.jbig2.decoder.arithmetic.CX;
+import org.apache.pdfbox.jbig2.decoder.mmr.MMRDecompressor;
+import org.apache.pdfbox.jbig2.err.InvalidHeaderValueException;
+import org.apache.pdfbox.jbig2.io.SubInputStream;
+import org.apache.pdfbox.jbig2.util.log.Logger;
+import org.apache.pdfbox.jbig2.util.log.LoggerFactory;
+
+/**
+ * This class represents a generic region segment.<br>
+ * Parsing is done as described in 7.4.5.<br>
+ * Decoding procedure is done as described in 6.2.5.7 and 7.4.6.4.
+ */
+public class GenericRegion implements Region {
+
+ private final Logger log = LoggerFactory.getLogger(GenericRegion.class);
+
+ private SubInputStream subInputStream;
+ private long dataHeaderOffset;
+ private long dataHeaderLength;
+ private long dataOffset;
+ private long dataLength;
+
+ /** Region segment information field, 7.4.1 */
+ private RegionSegmentInformation regionInfo;
+
+ /** Generic region segment flags, 7.4.6.2 */
+ private boolean useExtTemplates;
+ private boolean isTPGDon;
+ private byte gbTemplate;
+ private boolean isMMREncoded;
+
+ /** Generic region segment AT flags, 7.4.6.3 */
+ private short[] gbAtX;
+ private short[] gbAtY;
+ private boolean[] gbAtOverride;
+
+ /**
+ * If true, AT pixels are not on their nominal location and have to be overridden
+ */
+ private boolean override;
+
+ /** Decoded data as pixel values (use row stride/width to wrap line) */
+ private Bitmap regionBitmap;
+
+ private ArithmeticDecoder arithDecoder;
+ private CX cx;
+
+ private MMRDecompressor mmrDecompressor;
+
+ public GenericRegion() {
+ }
+
+ public GenericRegion(final SubInputStream subInputStream) {
+ this.subInputStream = subInputStream;
+ this.regionInfo = new RegionSegmentInformation(subInputStream);
+ }
+
+ private void parseHeader() throws IOException, InvalidHeaderValueException {
+ regionInfo.parseHeader();
+
+ /* Bit 5-7 */
+ subInputStream.readBits(3); // Dirty read...
+
+ /* Bit 4 */
+ if (subInputStream.readBit() == 1) {
+ useExtTemplates = true;
+ }
+
+ /* Bit 3 */
+ if (subInputStream.readBit() == 1) {
+ isTPGDon = true;
+ }
+
+ /* Bit 1-2 */
+ gbTemplate = (byte) (subInputStream.readBits(2) & 0xf);
+
+ /* Bit 0 */
+ if (subInputStream.readBit() == 1) {
+ isMMREncoded = true;
+ }
+
+ if (!isMMREncoded) {
+ final int amountOfGbAt;
+ if (gbTemplate == 0) {
+ if (useExtTemplates) {
+ amountOfGbAt = 12;
+ } else {
+ amountOfGbAt = 4;
+ }
+ } else {
+ amountOfGbAt = 1;
+ }
+
+ readGbAtPixels(amountOfGbAt);
+ }
+
+ /* Segment data structure */
+ computeSegmentDataStructure();
+
+ this.checkInput();
+ }
+
+ private void readGbAtPixels(final int amountOfGbAt) throws IOException {
+ gbAtX = new short[amountOfGbAt];
+ gbAtY = new short[amountOfGbAt];
+
+ for (int i = 0; i < amountOfGbAt; i++) {
+ gbAtX[i] = subInputStream.readByte();
+ gbAtY[i] = subInputStream.readByte();
+ }
+ }
+
+ private void computeSegmentDataStructure() throws IOException {
+ dataOffset = subInputStream.getStreamPosition();
+ dataHeaderLength = dataOffset - dataHeaderOffset;
+ dataLength = subInputStream.length() - dataHeaderLength;
+ }
+
+ private void checkInput() throws InvalidHeaderValueException {
+ if (isMMREncoded) {
+ if (gbTemplate != 0) {
+ log.info("gbTemplate should contain the value 0");
+ }
+ }
+ }
+
+ /**
+ * The procedure is described in 6.2.5.7, page 17.
+ *
+ * @returns The decoded {@link Bitmap} of this region.
+ */
+ public Bitmap getRegionBitmap() throws IOException {
+ if (null == regionBitmap) {
+
+ if (isMMREncoded) {
+
+ /*
+ * MMR DECODER CALL
+ */
+ if (null == mmrDecompressor) {
+ mmrDecompressor = new MMRDecompressor(regionInfo.getBitmapWidth(), regionInfo.getBitmapHeight(),
+ new SubInputStream(subInputStream, dataOffset, dataLength));
+ }
+
+ /* 6.2.6 */
+ regionBitmap = mmrDecompressor.uncompress();
+
+ } else {
+
+ /*
+ * ARITHMETIC DECODER PROCEDURE for generic region segments
+ */
+
+ updateOverrideFlags();
+
+ /* 6.2.5.7 - 1) */
+ int ltp = 0;
+
+ if (arithDecoder == null) {
+ arithDecoder = new ArithmeticDecoder(subInputStream);
+ }
+ if (cx == null) {
+ cx = new CX(65536, 1);
+ }
+
+ /* 6.2.5.7 - 2) */
+ regionBitmap = new Bitmap(regionInfo.getBitmapWidth(), regionInfo.getBitmapHeight());
+
+ final int paddedWidth = (regionBitmap.getWidth() + 7) & -8;
+
+ /* 6.2.5.7 - 3 */
+ for (int line = 0; line < regionBitmap.getHeight(); line++) {
+
+ /* 6.2.5.7 - 3 b) */
+ if (isTPGDon) {
+ ltp ^= decodeSLTP();
+ }
+
+ /* 6.2.5.7 - 3 c) */
+ if (ltp == 1) {
+ if (line > 0) {
+ copyLineAbove(line);
+ }
+ } else {
+ /* 3 d) */
+ // NOT USED ATM - If corresponding pixel of SKIP bitmap is 0, set
+ // current pixel to 0. Something like that:
+ // if (useSkip) {
+ // for (int i = 1; i < rowstride; i++) {
+ // if (skip[pixel] == 1) {
+ // gbReg[pixel] = 0;
+ // }
+ // pixel++;
+ // }
+ // } else {
+ decodeLine(line, regionBitmap.getWidth(), regionBitmap.getRowStride(), paddedWidth);
+ // }
+ }
+ }
+ }
+ }
+
+ // if (JBIG2ImageReader.DEBUG)
+ // if (header != null && header.getSegmentNr() == 3)
+ // new Testbild(gbReg.getByteArray(), (int) gbReg.getWidth(), (int) gbReg.getHeight(),
+ // gbReg.getRowStride());
+
+ /* 4 */
+ return regionBitmap;
+ }
+
+ private int decodeSLTP() throws IOException {
+ switch (gbTemplate){
+ case 0 :
+ cx.setIndex(0x9b25);
+ break;
+ case 1 :
+ cx.setIndex(0x795);
+ break;
+ case 2 :
+ cx.setIndex(0xe5);
+ break;
+ case 3 :
+ cx.setIndex(0x195);
+ break;
+ }
+ return arithDecoder.decode(cx);
+ }
+
+ private void decodeLine(final int lineNumber, final int width, final int rowStride, final int paddedWidth)
+ throws IOException {
+ final int byteIndex = regionBitmap.getByteIndex(0, lineNumber);
+ final int idx = byteIndex - rowStride;
+
+ switch (gbTemplate){
+ case 0 :
+ if (!useExtTemplates) {
+ decodeTemplate0a(lineNumber, width, rowStride, paddedWidth, byteIndex, idx);
+ } else {
+ decodeTemplate0b(lineNumber, width, rowStride, paddedWidth, byteIndex, idx);
+ }
+ break;
+ case 1 :
+ decodeTemplate1(lineNumber, width, rowStride, paddedWidth, byteIndex, idx);
+ break;
+ case 2 :
+ decodeTemplate2(lineNumber, width, rowStride, paddedWidth, byteIndex, idx);
+ break;
+ case 3 :
+ decodeTemplate3(lineNumber, width, rowStride, paddedWidth, byteIndex, idx);
+ break;
+ }
+ }
+
+ /**
+ * Each pixel gets the value from the corresponding pixel of the row above. Line 0 cannot get
+ * copied values (source will be -1, doesn't exist).
+ *
+ * @param lineNumber - Coordinate of the row that should be set.
+ */
+ private void copyLineAbove(final int lineNumber) {
+ int targetByteIndex = lineNumber * regionBitmap.getRowStride();
+ int sourceByteIndex = targetByteIndex - regionBitmap.getRowStride();
+
+ for (int i = 0; i < regionBitmap.getRowStride(); i++) {
+ // Get the byte that should be copied and put it into Bitmap
+ regionBitmap.setByte(targetByteIndex++, regionBitmap.getByte(sourceByteIndex++));
+ }
+ }
+
+ private void decodeTemplate0a(final int lineNumber, final int width, final int rowStride, final int paddedWidth,
+ int byteIndex, int idx) throws IOException {
+ int context;
+ int overriddenContext = 0;
+
+ int line1 = 0;
+ int line2 = 0;
+
+ if (lineNumber >= 1) {
+ line1 = regionBitmap.getByteAsInteger(idx);
+ }
+
+ if (lineNumber >= 2) {
+ line2 = regionBitmap.getByteAsInteger(idx - rowStride) << 6;
+ }
+
+ context = (line1 & 0xf0) | (line2 & 0x3800);
+
+ int nextByte;
+ for (int x = 0; x < paddedWidth; x = nextByte) {
+ /* 6.2.5.7 3d */
+ byte result = 0;
+ nextByte = x + 8;
+ final int minorWidth = width - x > 8 ? 8 : width - x;
+
+ if (lineNumber > 0) {
+ line1 = (line1 << 8) | (nextByte < width ? regionBitmap.getByteAsInteger(idx + 1) : 0);
+ }
+
+ if (lineNumber > 1) {
+ line2 = (line2 << 8) | (nextByte < width ? regionBitmap.getByteAsInteger(idx - rowStride + 1) << 6 : 0);
+ }
+
+ for (int minorX = 0; minorX < minorWidth; minorX++) {
+ final int toShift = 7 - minorX;
+ if (override) {
+ overriddenContext = overrideAtTemplate0a(context, (x + minorX), lineNumber, result, minorX, toShift);
+ cx.setIndex(overriddenContext);
+ } else {
+ cx.setIndex(context);
+ }
+
+ int bit = arithDecoder.decode(cx);
+
+ result |= bit << toShift;
+
+ context = ((context & 0x7bf7) << 1) | bit | ((line1 >> toShift) & 0x10) | ((line2 >> toShift) & 0x800);
+ }
+
+ regionBitmap.setByte(byteIndex++, result);
+ idx++;
+ }
+ }
+
+ private void decodeTemplate0b(final int lineNumber, final int width, final int rowStride, final int paddedWidth,
+ int byteIndex, int idx) throws IOException {
+ int context;
+ int overriddenContext = 0;
+
+ int line1 = 0;
+ int line2 = 0;
+
+ if (lineNumber >= 1) {
+ line1 = regionBitmap.getByteAsInteger(idx);
+ }
+
+ if (lineNumber >= 2) {
+ line2 = regionBitmap.getByteAsInteger(idx - rowStride) << 6;
+ }
+
+ context = (line1 & 0xf0) | (line2 & 0x3800);
+
+ int nextByte;
+ for (int x = 0; x < paddedWidth; x = nextByte) {
+ /* 6.2.5.7 3d */
+ byte result = 0;
+ nextByte = x + 8;
+ final int minorWidth = width - x > 8 ? 8 : width - x;
+
+ if (lineNumber > 0) {
+ line1 = (line1 << 8) | (nextByte < width ? regionBitmap.getByteAsInteger(idx + 1) : 0);
+ }
+
+ if (lineNumber > 1) {
+ line2 = (line2 << 8) | (nextByte < width ? regionBitmap.getByteAsInteger(idx - rowStride + 1) << 6 : 0);
+ }
+
+ for (int minorX = 0; minorX < minorWidth; minorX++) {
+ final int toShift = 7 - minorX;
+ if (override) {
+ overriddenContext = overrideAtTemplate0b(context, (x + minorX), lineNumber, result, minorX, toShift);
+ cx.setIndex(overriddenContext);
+ } else {
+ cx.setIndex(context);
+ }
+
+ final int bit = arithDecoder.decode(cx);
+
+ result |= bit << toShift;
+
+ context = ((context & 0x7bf7) << 1) | bit | ((line1 >> toShift) & 0x10) | ((line2 >> toShift) & 0x800);
+ }
+
+ regionBitmap.setByte(byteIndex++, result);
+ idx++;
+ }
+ }
+
+ private void decodeTemplate1(final int lineNumber, int width, final int rowStride, final int paddedWidth,
+ int byteIndex, int idx) throws IOException {
+ int context;
+ int overriddenContext;
+
+ int line1 = 0;
+ int line2 = 0;
+
+ if (lineNumber >= 1) {
+ line1 = regionBitmap.getByteAsInteger(idx);
+ }
+
+ if (lineNumber >= 2) {
+ line2 = regionBitmap.getByteAsInteger(idx - rowStride) << 5;
+ }
+
+ context = ((line1 >> 1) & 0x1f8) | ((line2 >> 1) & 0x1e00);
+
+ int nextByte;
+ for (int x = 0; x < paddedWidth; x = nextByte) {
+ /* 6.2.5.7 3d */
+ byte result = 0;
+ nextByte = x + 8;
+ final int minorWidth = width - x > 8 ? 8 : width - x;
+
+ if (lineNumber >= 1) {
+ line1 = (line1 << 8) | (nextByte < width ? regionBitmap.getByteAsInteger(idx + 1) : 0);
+ }
+
+ if (lineNumber >= 2) {
+ line2 = (line2 << 8) | (nextByte < width ? regionBitmap.getByteAsInteger(idx - rowStride + 1) << 5 : 0);
+ }
+
+ for (int minorX = 0; minorX < minorWidth; minorX++) {
+ if (override) {
+ overriddenContext = overrideAtTemplate1(context, x + minorX, lineNumber, result, minorX);
+ cx.setIndex(overriddenContext);
+ } else {
+ cx.setIndex(context);
+ }
+
+ final int bit = arithDecoder.decode(cx);
+
+ result |= bit << 7 - minorX;
+
+ final int toShift = 8 - minorX;
+ context = ((context & 0xefb) << 1) | bit | ((line1 >> toShift) & 0x8) | ((line2 >> toShift) & 0x200);
+ }
+
+ regionBitmap.setByte(byteIndex++, result);
+ idx++;
+ }
+ }
+
+ private void decodeTemplate2(final int lineNumber, final int width, final int rowStride, final int paddedWidth,
+ int byteIndex, int idx) throws IOException {
+ int context;
+ int overriddenContext;
+
+ int line1 = 0;
+ int line2 = 0;
+
+ if (lineNumber >= 1) {
+ line1 = regionBitmap.getByteAsInteger(idx);
+ }
+
+ if (lineNumber >= 2) {
+ line2 = regionBitmap.getByteAsInteger(idx - rowStride) << 4;
+ }
+
+ context = ((line1 >> 3) & 0x7c) | ((line2 >> 3) & 0x380);
+
+ int nextByte;
+ for (int x = 0; x < paddedWidth; x = nextByte) {
+ /* 6.2.5.7 3d */
+ byte result = 0;
+ nextByte = x + 8;
+ final int minorWidth = width - x > 8 ? 8 : width - x;
+
+ if (lineNumber >= 1) {
+ line1 = (line1 << 8) | (nextByte < width ? regionBitmap.getByteAsInteger(idx + 1) : 0);
+ }
+
+ if (lineNumber >= 2) {
+ line2 = (line2 << 8) | (nextByte < width ? regionBitmap.getByteAsInteger(idx - rowStride + 1) << 4 : 0);
+ }
+
+ for (int minorX = 0; minorX < minorWidth; minorX++) {
+
+ if (override) {
+ overriddenContext = overrideAtTemplate2(context, x + minorX, lineNumber, result, minorX);
+ cx.setIndex(overriddenContext);
+ } else {
+ cx.setIndex(context);
+ }
+
+ final int bit = arithDecoder.decode(cx);
+
+ result |= bit << (7 - minorX);
+
+ final int toShift = 10 - minorX;
+ context = ((context & 0x1bd) << 1) | bit | ((line1 >> toShift) & 0x4) | ((line2 >> toShift) & 0x80);
+ }
+
+ regionBitmap.setByte(byteIndex++, result);
+ idx++;
+ }
+ }
+
+ private void decodeTemplate3(final int lineNumber, final int width, final int rowStride, final int paddedWidth,
+ int byteIndex, int idx) throws IOException {
+ int context;
+ int overriddenContext;
+
+ int line1 = 0;
+
+ if (lineNumber >= 1) {
+ line1 = regionBitmap.getByteAsInteger(idx);
+ }
+
+ context = (line1 >> 1) & 0x70;
+
+ int nextByte;
+ for (int x = 0; x < paddedWidth; x = nextByte) {
+ /* 6.2.5.7 3d */
+ byte result = 0;
+ nextByte = x + 8;
+ final int minorWidth = width - x > 8 ? 8 : width - x;
+
+ if (lineNumber >= 1) {
+ line1 = (line1 << 8) | (nextByte < width ? regionBitmap.getByteAsInteger(idx + 1) : 0);
+ }
+
+ for (int minorX = 0; minorX < minorWidth; minorX++) {
+
+ if (override) {
+ overriddenContext = overrideAtTemplate3(context, x + minorX, lineNumber, result, minorX);
+ cx.setIndex(overriddenContext);
+ } else {
+ cx.setIndex(context);
+ }
+
+ final int bit = arithDecoder.decode(cx);
+
+ result |= bit << (7 - minorX);
+ context = ((context & 0x1f7) << 1) | bit | ((line1 >> (8 - minorX)) & 0x010);
+ }
+
+ regionBitmap.setByte(byteIndex++, result);
+ idx++;
+ }
+ }
+
+ private void updateOverrideFlags() {
+ if (gbAtX == null || gbAtY == null) {
+ log.info("AT pixels not set");
+ return;
+ }
+
+ if (gbAtX.length != gbAtY.length) {
+ log.info("AT pixel inconsistent, amount of x pixels: " + gbAtX.length + ", amount of y pixels:" + gbAtY.length);
+ return;
+ }
+
+ gbAtOverride = new boolean[gbAtX.length];
+
+ switch (gbTemplate){
+ case 0 :
+ if (!useExtTemplates) {
+ if (gbAtX[0] != 3 || gbAtY[0] != -1)
+ setOverrideFlag(0);
+
+ if (gbAtX[1] != -3 || gbAtY[1] != -1)
+ setOverrideFlag(1);
+
+ if (gbAtX[2] != 2 || gbAtY[2] != -2)
+ setOverrideFlag(2);
+
+ if (gbAtX[3] != -2 || gbAtY[3] != -2)
+ setOverrideFlag(3);
+
+ } else {
+ if (gbAtX[0] != -2 || gbAtY[0] != 0)
+ setOverrideFlag(0);
+
+ if (gbAtX[1] != 0 || gbAtY[1] != -2)
+ setOverrideFlag(1);
+
+ if (gbAtX[2] != -2 || gbAtY[2] != -1)
+ setOverrideFlag(2);
+
+ if (gbAtX[3] != -1 || gbAtY[3] != -2)
+ setOverrideFlag(3);
+
+ if (gbAtX[4] != 1 || gbAtY[4] != -2)
+ setOverrideFlag(4);
+
+ if (gbAtX[5] != 2 || gbAtY[5] != -1)
+ setOverrideFlag(5);
+
+ if (gbAtX[6] != -3 || gbAtY[6] != 0)
+ setOverrideFlag(6);
+
+ if (gbAtX[7] != -4 || gbAtY[7] != 0)
+ setOverrideFlag(7);
+
+ if (gbAtX[8] != 2 || gbAtY[8] != -2)
+ setOverrideFlag(8);
+
+ if (gbAtX[9] != 3 || gbAtY[9] != -1)
+ setOverrideFlag(9);
+
+ if (gbAtX[10] != -2 || gbAtY[10] != -2)
+ setOverrideFlag(10);
+
+ if (gbAtX[11] != -3 || gbAtY[11] != -1)
+ setOverrideFlag(11);
+ }
+ break;
+ case 1 :
+ if (gbAtX[0] != 3 || gbAtY[0] != -1)
+ setOverrideFlag(0);
+ break;
+ case 2 :
+ if (gbAtX[0] != 2 || gbAtY[0] != -1)
+ setOverrideFlag(0);
+ break;
+ case 3 :
+ if (gbAtX[0] != 2 || gbAtY[0] != -1)
+ setOverrideFlag(0);
+ break;
+ }
+
+ }
+
+ private void setOverrideFlag(final int index) {
+ gbAtOverride[index] = true;
+ override = true;
+ }
+
+ private int overrideAtTemplate0a(int context, final int x, final int y, final int result, final int minorX,
+ final int toShift) throws IOException {
+ if (gbAtOverride[0]) {
+ context &= 0xffef;
+ if (gbAtY[0] == 0 && gbAtX[0] >= -minorX)
+ context |= (result >> (toShift - gbAtX[0]) & 0x1) << 4;
+ else
+ context |= getPixel(x + gbAtX[0], y + gbAtY[0]) << 4;
+ }
+
+ if (gbAtOverride[1]) {
+ context &= 0xfbff;
+ if (gbAtY[1] == 0 && gbAtX[1] >= -minorX)
+ context |= (result >> (toShift - gbAtX[1]) & 0x1) << 10;
+ else
+ context |= getPixel(x + gbAtX[1], y + gbAtY[1]) << 10;
+ }
+
+ if (gbAtOverride[2]) {
+ context &= 0xf7ff;
+ if (gbAtY[2] == 0 && gbAtX[2] >= -minorX)
+ context |= (result >> (toShift - gbAtX[2]) & 0x1) << 11;
+ else
+ context |= getPixel(x + gbAtX[2], y + gbAtY[2]) << 11;
+ }
+
+ if (gbAtOverride[3]) {
+ context &= 0x7fff;
+ if (gbAtY[3] == 0 && gbAtX[3] >= -minorX)
+ context |= (result >> (toShift - gbAtX[3]) & 0x1) << 15;
+ else
+ context |= getPixel(x + gbAtX[3], y + gbAtY[3]) << 15;
+ }
+ return context;
+ }
+
+ private int overrideAtTemplate0b(int context, final int x, final int y, final int result, final int minorX,
+ final int toShift) throws IOException {
+ if (gbAtOverride[0]) {
+ context &= 0xfffd;
+ if (gbAtY[0] == 0 && gbAtX[0] >= -minorX)
+ context |= (result >> (toShift - gbAtX[0]) & 0x1) << 1;
+ else
+ context |= getPixel(x + gbAtX[0], y + gbAtY[0]) << 1;
+ }
+
+ if (gbAtOverride[1]) {
+ context &= 0xdfff;
+ if (gbAtY[1] == 0 && gbAtX[1] >= -minorX)
+ context |= (result >> (toShift - gbAtX[1]) & 0x1) << 13;
+ else
+ context |= getPixel(x + gbAtX[1], y + gbAtY[1]) << 13;
+ }
+ if (gbAtOverride[2]) {
+ context &= 0xfdff;
+ if (gbAtY[2] == 0 && gbAtX[2] >= -minorX)
+ context |= (result >> (toShift - gbAtX[2]) & 0x1) << 9;
+ else
+ context |= getPixel(x + gbAtX[2], y + gbAtY[2]) << 9;
+ }
+ if (gbAtOverride[3]) {
+ context &= 0xbfff;
+ if (gbAtY[3] == 0 && gbAtX[3] >= -minorX)
+ context |= (result >> (toShift - gbAtX[3]) & 0x1) << 14;
+ else
+ context |= getPixel(x + gbAtX[3], y + gbAtY[3]) << 14;
+ }
+ if (gbAtOverride[4]) {
+ context &= 0xefff;
+ if (gbAtY[4] == 0 && gbAtX[4] >= -minorX)
+ context |= (result >> (toShift - gbAtX[4]) & 0x1) << 12;
+ else
+ context |= getPixel(x + gbAtX[4], y + gbAtY[4]) << 12;
+ }
+ if (gbAtOverride[5]) {
+ context &= 0xffdf;
+ if (gbAtY[5] == 0 && gbAtX[5] >= -minorX)
+ context |= (result >> (toShift - gbAtX[5]) & 0x1) << 5;
+ else
+ context |= getPixel(x + gbAtX[5], y + gbAtY[5]) << 5;
+ }
+ if (gbAtOverride[6]) {
+ context &= 0xfffb;
+ if (gbAtY[6] == 0 && gbAtX[6] >= -minorX)
+ context |= (result >> (toShift - gbAtX[6]) & 0x1) << 2;
+ else
+ context |= getPixel(x + gbAtX[6], y + gbAtY[6]) << 2;
+ }
+ if (gbAtOverride[7]) {
+ context &= 0xfff7;
+ if (gbAtY[7] == 0 && gbAtX[7] >= -minorX)
+ context |= (result >> (toShift - gbAtX[7]) & 0x1) << 3;
+ else
+ context |= getPixel(x + gbAtX[7], y + gbAtY[7]) << 3;
+ }
+ if (gbAtOverride[8]) {
+ context &= 0xf7ff;
+ if (gbAtY[8] == 0 && gbAtX[8] >= -minorX)
+ context |= (result >> (toShift - gbAtX[8]) & 0x1) << 11;
+ else
+ context |= getPixel(x + gbAtX[8], y + gbAtY[8]) << 11;
+ }
+ if (gbAtOverride[9]) {
+ context &= 0xffef;
+ if (gbAtY[9] == 0 && gbAtX[9] >= -minorX)
+ context |= (result >> (toShift - gbAtX[9]) & 0x1) << 4;
+ else
+ context |= getPixel(x + gbAtX[9], y + gbAtY[9]) << 4;
+ }
+ if (gbAtOverride[10]) {
+ context &= 0x7fff;
+ if (gbAtY[10] == 0 && gbAtX[10] >= -minorX)
+ context |= (result >> (toShift - gbAtX[10]) & 0x1) << 15;
+ else
+ context |= getPixel(x + gbAtX[10], y + gbAtY[10]) << 15;
+ }
+ if (gbAtOverride[11]) {
+ context &= 0xfdff;
+ if (gbAtY[11] == 0 && gbAtX[11] >= -minorX)
+ context |= (result >> (toShift - gbAtX[11]) & 0x1) << 10;
+ else
+ context |= getPixel(x + gbAtX[11], y + gbAtY[11]) << 10;
+ }
+
+ return context;
+ }
+
+ private int overrideAtTemplate1(int context, final int x, final int y, final int result, final int minorX)
+ throws IOException {
+ context &= 0x1ff7;
+ if (gbAtY[0] == 0 && gbAtX[0] >= -minorX)
+ return (context | (result >> (7 - (minorX + gbAtX[0])) & 0x1) << 3);
+ else
+ return (context | getPixel(x + gbAtX[0], y + gbAtY[0]) << 3);
+ }
+
+ private int overrideAtTemplate2(int context, final int x, final int y, final int result, final int minorX)
+ throws IOException {
+ context &= 0x3fb;
+ if (gbAtY[0] == 0 && gbAtX[0] >= -minorX)
+ return (context | (result >> (7 - (minorX + gbAtX[0])) & 0x1) << 2);
+ else
+ return (context | getPixel(x + gbAtX[0], y + gbAtY[0]) << 2);
+ }
+
+ private int overrideAtTemplate3(int context, final int x, final int y, final int result, final int minorX)
+ throws IOException {
+ context &= 0x3ef;
+ if (gbAtY[0] == 0 && gbAtX[0] >= -minorX)
+ return (context | (result >> (7 - (minorX + gbAtX[0])) & 0x1) << 4);
+ else
+ return (context | getPixel(x + gbAtX[0], y + gbAtY[0]) << 4);
+ }
+
+ private byte getPixel(final int x, final int y) throws IOException {
+ if (x < 0 || x >= regionBitmap.getWidth())
+ return 0;
+
+ if (y < 0 || y >= regionBitmap.getHeight())
+ return 0;
+
+ return regionBitmap.getPixel(x, y);
+ }
+
+ /**
+ * Used by {@link SymbolDictionary}.
+ */
+ protected void setParameters(final boolean isMMREncoded, final long dataOffset, final long dataLength, final int gbh,
+ final int gbw) {
+ this.isMMREncoded = isMMREncoded;
+ this.dataOffset = dataOffset;
+ this.dataLength = dataLength;
+ this.regionInfo.setBitmapHeight(gbh);
+ this.regionInfo.setBitmapWidth(gbw);
+
+ this.mmrDecompressor = null;
+ resetBitmap();
+ }
+
+ /**
+ * Used by {@link SymbolDictionary}.
+ */
+ protected void setParameters(final boolean isMMREncoded, final byte sdTemplate, final boolean isTPGDon,
+ final boolean useSkip, final short[] sdATX, final short[] sdATY, final int symWidth, final int hcHeight,
+ final CX cx, final ArithmeticDecoder arithmeticDecoder) {
+ this.isMMREncoded = isMMREncoded;
+ this.gbTemplate = sdTemplate;
+ this.isTPGDon = isTPGDon;
+ this.gbAtX = sdATX;
+ this.gbAtY = sdATY;
+ this.regionInfo.setBitmapWidth(symWidth);
+ this.regionInfo.setBitmapHeight(hcHeight);
+ if (null != cx)
+ this.cx = cx;
+ if (null != arithmeticDecoder)
+ this.arithDecoder = arithmeticDecoder;
+
+ this.mmrDecompressor = null;
+ resetBitmap();
+ }
+
+ /**
+ * Used by {@link PatternDictionary} and {@link HalftoneRegion}.
+ */
+ protected void setParameters(final boolean isMMREncoded, final long dataOffset, final long dataLength, final int gbh,
+ final int gbw, final byte gbTemplate, final boolean isTPGDon, final boolean useSkip, final short[] gbAtX,
+ final short[] gbAtY) {
+ this.dataOffset = dataOffset;
+ this.dataLength = dataLength;
+
+ this.regionInfo = new RegionSegmentInformation();
+ this.regionInfo.setBitmapHeight(gbh);
+ this.regionInfo.setBitmapWidth(gbw);
+ this.gbTemplate = gbTemplate;
+
+ this.isMMREncoded = isMMREncoded;
+ this.isTPGDon = isTPGDon;
+ this.gbAtX = gbAtX;
+ this.gbAtY = gbAtY;
+ }
+
+ /**
+ * Simply sets the memory-critical bitmap of this region to {@code null}.
+ */
+ protected void resetBitmap() {
+ this.regionBitmap = null;
+ }
+
+ public void init(final SegmentHeader header, final SubInputStream sis) throws InvalidHeaderValueException,
+ IOException {
+ this.subInputStream = sis;
+ this.regionInfo = new RegionSegmentInformation(subInputStream);
+ parseHeader();
+ }
+
+ public RegionSegmentInformation getRegionInfo() {
+ return regionInfo;
+ }
+
+ protected boolean useExtTemplates() {
+ return useExtTemplates;
+ }
+
+ protected boolean isTPGDon() {
+ return isTPGDon;
+ }
+
+ protected byte getGbTemplate() {
+ return gbTemplate;
+ }
+
+ protected boolean isMMREncoded() {
+ return isMMREncoded;
+ }
+
+ protected short[] getGbAtX() {
+ return gbAtX;
+ }
+
+ protected short[] getGbAtY() {
+ return gbAtY;
+ }
+}
http://git-wip-us.apache.org/repos/asf/pdfbox-jbig2/blob/4619d28b/src/main/java/org/apache/pdfbox/jbig2/segments/HalftoneRegion.java
----------------------------------------------------------------------
diff --git a/src/main/java/org/apache/pdfbox/jbig2/segments/HalftoneRegion.java b/src/main/java/org/apache/pdfbox/jbig2/segments/HalftoneRegion.java
new file mode 100644
index 0000000..384f09f
--- /dev/null
+++ b/src/main/java/org/apache/pdfbox/jbig2/segments/HalftoneRegion.java
@@ -0,0 +1,406 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.pdfbox.jbig2.segments;
+
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.Arrays;
+
+import org.apache.pdfbox.jbig2.Bitmap;
+import org.apache.pdfbox.jbig2.Region;
+import org.apache.pdfbox.jbig2.SegmentHeader;
+import org.apache.pdfbox.jbig2.err.InvalidHeaderValueException;
+import org.apache.pdfbox.jbig2.image.Bitmaps;
+import org.apache.pdfbox.jbig2.io.SubInputStream;
+import org.apache.pdfbox.jbig2.util.CombinationOperator;
+import org.apache.pdfbox.jbig2.util.log.Logger;
+import org.apache.pdfbox.jbig2.util.log.LoggerFactory;
+
+/**
+ * This class represents the data of segment type "Halftone region". Parsing is described in 7.4.5,
+ * page 67. Decoding procedure in 6.6.5 and 7.4.5.2.
+ */
+public class HalftoneRegion implements Region {
+
+ private final Logger log = LoggerFactory.getLogger(HalftoneRegion.class);
+
+ private SubInputStream subInputStream;
+ private SegmentHeader segmentHeader;
+ private long dataHeaderOffset;
+ private long dataHeaderLength;
+ private long dataOffset;
+ private long dataLength;
+
+ /** Region segment information field, 7.4.1 */
+ private RegionSegmentInformation regionInfo;
+
+ /** Halftone segment information field, 7.4.5.1.1 */
+ private byte hDefaultPixel;
+ private CombinationOperator hCombinationOperator;
+ private boolean hSkipEnabled;
+ private byte hTemplate;
+ private boolean isMMREncoded;
+
+ /** Halftone grid position and size, 7.4.5.1.2 */
+ /** Width of the gray-scale image, 7.4.5.1.2.1 */
+ private int hGridWidth;
+ /** Height of the gray-scale image, 7.4.5.1.2.2 */
+ private int hGridHeight;
+ /** Horizontal offset of the grid, 7.4.5.1.2.3 */
+ private int hGridX;
+ /** Vertical offset of the grid, 7.4.5.1.2.4 */
+ private int hGridY;
+
+ /** Halftone grid vector, 7.4.5.1.3 */
+ /** Horizontal coordinate of the halftone grid vector, 7.4.5.1.3.1 */
+ private int hRegionX;
+ /** Vertical coordinate of the halftone grod vector, 7.4.5.1.3.2 */
+ private int hRegionY;
+
+ /** Decoded data */
+ private Bitmap halftoneRegionBitmap;
+
+ /**
+ * Previously decoded data from other regions or dictionaries, stored to use as patterns in this
+ * region.
+ */
+ private ArrayList<Bitmap> patterns;
+
+ public HalftoneRegion() {
+ }
+
+ public HalftoneRegion(final SubInputStream subInputStream) {
+ this.subInputStream = subInputStream;
+ this.regionInfo = new RegionSegmentInformation(subInputStream);
+ }
+
+ public HalftoneRegion(final SubInputStream subInputStream, final SegmentHeader segmentHeader) {
+ this.subInputStream = subInputStream;
+ this.segmentHeader = segmentHeader;
+ this.regionInfo = new RegionSegmentInformation(subInputStream);
+ }
+
+ private void parseHeader() throws IOException, InvalidHeaderValueException {
+ regionInfo.parseHeader();
+
+ /* Bit 7 */
+ hDefaultPixel = (byte) subInputStream.readBit();
+
+ /* Bit 4-6 */
+ hCombinationOperator = CombinationOperator.translateOperatorCodeToEnum((short) (subInputStream.readBits(3) & 0xf));
+
+ /* Bit 3 */
+ if (subInputStream.readBit() == 1) {
+ hSkipEnabled = true;
+ }
+
+ /* Bit 1-2 */
+ hTemplate = (byte) (subInputStream.readBits(2) & 0xf);
+
+ /* Bit 0 */
+ if (subInputStream.readBit() == 1) {
+ isMMREncoded = true;
+ }
+
+ hGridWidth = (int) (subInputStream.readBits(32) & 0xffffffff);
+ hGridHeight = (int) (subInputStream.readBits(32) & 0xffffffff);
+
+ hGridX = (int) subInputStream.readBits(32);
+ hGridY = (int) subInputStream.readBits(32);
+
+ hRegionX = (int) subInputStream.readBits(16) & 0xffff;
+ hRegionY = (int) subInputStream.readBits(16) & 0xffff;
+
+ /* Segment data structure */
+ computeSegmentDataStructure();
+
+ this.checkInput();
+ }
+
+ private void computeSegmentDataStructure() throws IOException {
+ dataOffset = subInputStream.getStreamPosition();
+ dataHeaderLength = dataOffset - dataHeaderOffset;
+ dataLength = subInputStream.length() - dataHeaderLength;
+ }
+
+ private void checkInput() throws InvalidHeaderValueException {
+ if (isMMREncoded) {
+ if (hTemplate != 0) {
+ log.info("hTemplate = " + hTemplate + " (should contain the value 0)");
+ }
+
+ if (hSkipEnabled) {
+ log.info("hSkipEnabled 0 " + hSkipEnabled + " (should contain the value false)");
+ }
+ }
+ }
+
+ /**
+ * The procedure is described in JBIG2 ISO standard, 6.6.5.
+ *
+ * @returns The decoded {@link Bitmap} of this region.
+ */
+ public Bitmap getRegionBitmap() throws IOException, InvalidHeaderValueException {
+ if (null == halftoneRegionBitmap) {
+
+ /* 6.6.5, page 40 */
+ /* 1) */
+ halftoneRegionBitmap = new Bitmap(regionInfo.getBitmapWidth(), regionInfo.getBitmapHeight());
+
+ if (patterns == null) {
+ patterns = getPatterns();
+ }
+
+ if (hDefaultPixel == 1) {
+ Arrays.fill(halftoneRegionBitmap.getByteArray(), (byte) 0xff);
+ }
+
+ /* 2) */
+ /*
+ * 6.6.5.1 Computing hSkip - At the moment SKIP is not used... we are not able to test it.
+ */
+ // Bitmap hSkip;
+ // if (hSkipEnabled) {
+ // int hPatternHeight = (int) hPats.get(0).getHeight();
+ // int hPatternWidth = (int) hPats.get(0).getWidth();
+ // TODO: Set or get pattern width and height from referred
+ // pattern segments. The method is called like this:
+ // hSkip = computeHSkip(hPatternHeight, hPatternWidth);
+ // }
+
+ /* 3) */
+ final int bitsPerValue = (int) Math.ceil(Math.log(patterns.size()) / Math.log(2));
+
+ /* 4) */
+ final int[][] grayScaleValues = grayScaleDecoding(bitsPerValue);
+
+ /* 5), rendering the pattern, described in 6.6.5.2 */
+ renderPattern(grayScaleValues);
+ }
+ /* 6) */
+ return halftoneRegionBitmap;
+ }
+
+ /**
+ * This method draws the pattern into the region bitmap ({@code htReg}), as described in 6.6.5.2,
+ * page 42
+ */
+ private void renderPattern(final int[][] grayScaleValues) {
+ int x = 0, y = 0;
+
+ // 1)
+ for (int m = 0; m < hGridHeight; m++) {
+ // a)
+ for (int n = 0; n < hGridWidth; n++) {
+ // i)
+ x = computeX(m, n);
+ y = computeY(m, n);
+
+ // ii)
+ final Bitmap patternBitmap = patterns.get(grayScaleValues[m][n]);
+ Bitmaps.blit(patternBitmap, halftoneRegionBitmap, (x + hGridX), (y + hGridY), hCombinationOperator);
+ }
+ }
+ }
+
+ /**
+ * @throws IOException
+ * @throws InvalidHeaderValueException
+ *
+ */
+ private ArrayList<Bitmap> getPatterns() throws InvalidHeaderValueException, IOException {
+ final ArrayList<Bitmap> patterns = new ArrayList<Bitmap>();
+
+ for (SegmentHeader s : segmentHeader.getRtSegments()) {
+ final PatternDictionary patternDictionary = (PatternDictionary) s.getSegmentData();
+ patterns.addAll(patternDictionary.getDictionary());
+ }
+
+ return patterns;
+ }
+
+ /**
+ * Gray-scale image decoding procedure is special for halftone region decoding and is described in
+ * Annex C.5 on page 98.
+ */
+ private int[][] grayScaleDecoding(final int bitsPerValue) throws IOException {
+
+ short[] gbAtX = null;
+ short[] gbAtY = null;
+
+ if (!isMMREncoded) {
+ gbAtX = new short[4];
+ gbAtY = new short[4];
+ // Set AT pixel values
+ if (hTemplate <= 1)
+ gbAtX[0] = 3;
+ else if (hTemplate >= 2)
+ gbAtX[0] = 2;
+
+ gbAtY[0] = -1;
+ gbAtX[1] = -3;
+ gbAtY[1] = -1;
+ gbAtX[2] = 2;
+ gbAtY[2] = -2;
+ gbAtX[3] = -2;
+ gbAtY[3] = -2;
+ }
+
+ Bitmap[] grayScalePlanes = new Bitmap[bitsPerValue];
+
+ // 1)
+ GenericRegion genericRegion = new GenericRegion(subInputStream);
+ genericRegion.setParameters(isMMREncoded, dataOffset, dataLength, hGridHeight, hGridWidth, hTemplate, false,
+ hSkipEnabled, gbAtX, gbAtY);
+
+ // 2)
+ int j = bitsPerValue - 1;
+
+ grayScalePlanes[j] = genericRegion.getRegionBitmap();
+
+ while (j > 0) {
+ j--;
+ genericRegion.resetBitmap();
+ // 3) a)
+ grayScalePlanes[j] = genericRegion.getRegionBitmap();
+ // 3) b)
+ grayScalePlanes = combineGrayScalePlanes(grayScalePlanes, j);
+ }
+
+ // 4)
+ return computeGrayScaleValues(grayScalePlanes, bitsPerValue);
+ }
+
+ private Bitmap[] combineGrayScalePlanes(Bitmap[] grayScalePlanes, int j) {
+ int byteIndex = 0;
+ for (int y = 0; y < grayScalePlanes[j].getHeight(); y++) {
+
+ for (int x = 0; x < grayScalePlanes[j].getWidth(); x += 8) {
+ final byte newValue = grayScalePlanes[j + 1].getByte(byteIndex);
+ final byte oldValue = grayScalePlanes[j].getByte(byteIndex);
+
+ grayScalePlanes[j].setByte(byteIndex++, Bitmaps.combineBytes(oldValue, newValue, CombinationOperator.XOR));
+ }
+ }
+ return grayScalePlanes;
+ }
+
+ private int[][] computeGrayScaleValues(final Bitmap[] grayScalePlanes, final int bitsPerValue) {
+ // Gray-scale decoding procedure, page 98
+ final int[][] grayScaleValues = new int[hGridHeight][hGridWidth];
+
+ // 4)
+ for (int y = 0; y < hGridHeight; y++) {
+ for (int x = 0; x < hGridWidth; x += 8) {
+ final int minorWidth = hGridWidth - x > 8 ? 8 : hGridWidth - x;
+ int byteIndex = grayScalePlanes[0].getByteIndex(x, y);
+
+ for (int minorX = 0; minorX < minorWidth; minorX++) {
+ final int i = minorX + x;
+ grayScaleValues[y][i] = 0;
+
+ for (int j = 0; j < bitsPerValue; j++) {
+ grayScaleValues[y][i] += ((grayScalePlanes[j].getByte(byteIndex) >> (7 - i & 7)) & 1) * (1 << j);
+ }
+ }
+ }
+ }
+ return grayScaleValues;
+ }
+
+ private int computeX(final int m, final int n) {
+ return shiftAndFill((hGridX + m * hRegionY + n * hRegionX));
+ }
+
+ private int computeY(final int m, final int n) {
+ return shiftAndFill((hGridY + m * hRegionX - n * hRegionY));
+ }
+
+ private int shiftAndFill(int value) {
+ // shift value by 8 and let the leftmost 8 bits be 0
+ value >>= 8;
+
+ if (value < 0) {
+ // fill the leftmost 8 bits with 1
+ final int bitPosition = (int) (Math.log(Integer.highestOneBit(value)) / Math.log(2));
+
+ for (int i = 1; i < 31 - bitPosition; i++) {
+ // bit flip
+ value |= 1 << (31 - i);
+ }
+ }
+
+ return value;
+ }
+
+ public void init(final SegmentHeader header, final SubInputStream sis) throws InvalidHeaderValueException,
+ IOException {
+ this.segmentHeader = header;
+ this.subInputStream = sis;
+ this.regionInfo = new RegionSegmentInformation(subInputStream);
+ parseHeader();
+ }
+
+ public CombinationOperator getCombinationOperator() {
+ return hCombinationOperator;
+ }
+
+ public RegionSegmentInformation getRegionInfo() {
+ return regionInfo;
+ }
+
+ protected byte getHTemplate() {
+ return hTemplate;
+ }
+
+ protected boolean isHSkipEnabled() {
+ return hSkipEnabled;
+ }
+
+ protected boolean isMMREncoded() {
+ return isMMREncoded;
+ }
+
+ protected int getHGridWidth() {
+ return hGridWidth;
+ }
+
+ protected int getHGridHeight() {
+ return hGridHeight;
+ }
+
+ protected int getHGridX() {
+ return hGridX;
+ }
+
+ protected int getHGridY() {
+ return hGridY;
+ }
+
+ protected int getHRegionX() {
+ return hRegionX;
+ }
+
+ protected int getHRegionY() {
+ return hRegionY;
+ }
+
+ protected byte getHDefaultPixel() {
+ return hDefaultPixel;
+ }
+}
http://git-wip-us.apache.org/repos/asf/pdfbox-jbig2/blob/4619d28b/src/main/java/org/apache/pdfbox/jbig2/segments/PageInformation.java
----------------------------------------------------------------------
diff --git a/src/main/java/org/apache/pdfbox/jbig2/segments/PageInformation.java b/src/main/java/org/apache/pdfbox/jbig2/segments/PageInformation.java
new file mode 100644
index 0000000..513dcca
--- /dev/null
+++ b/src/main/java/org/apache/pdfbox/jbig2/segments/PageInformation.java
@@ -0,0 +1,226 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.pdfbox.jbig2.segments;
+
+import java.io.IOException;
+
+import org.apache.pdfbox.jbig2.SegmentData;
+import org.apache.pdfbox.jbig2.SegmentHeader;
+import org.apache.pdfbox.jbig2.err.InvalidHeaderValueException;
+import org.apache.pdfbox.jbig2.io.SubInputStream;
+import org.apache.pdfbox.jbig2.util.CombinationOperator;
+import org.apache.pdfbox.jbig2.util.log.Logger;
+import org.apache.pdfbox.jbig2.util.log.LoggerFactory;
+
+/**
+ * This class represents the segment type "Page information", 7.4.8 (page 73).
+ */
+public class PageInformation implements SegmentData {
+
+ private final Logger log = LoggerFactory.getLogger(PageInformation.class);
+
+ private SubInputStream subInputStream;
+
+ /** Page bitmap width, four byte, 7.4.8.1 */
+ private int bitmapWidth;
+
+ /** Page bitmap height, four byte, 7.4.8.2 */
+ private int bitmapHeight;
+
+ /** Page X resolution, four byte, 7.4.8.3 */
+ private int resolutionX;
+
+ /** Page Y resolution, four byte, 7.4.8.4 */
+ private int resolutionY;
+
+ /** Page segment flags, one byte, 7.4.8.5 */
+ private boolean combinationOperatorOverrideAllowed;
+ private CombinationOperator combinationOperator;
+ private boolean requiresAuxiliaryBuffer;
+ private short defaultPixelValue;
+ private boolean mightContainRefinements;
+ private boolean isLossless;
+
+ /** Page striping information, two byte, 7.4.8.6 */
+ private boolean isStriped;
+ private short maxStripeSize;
+
+ private void parseHeader() throws IOException, InvalidHeaderValueException {
+
+ readWidthAndHeight();
+ readResolution();
+
+ /* Bit 7 */
+ subInputStream.readBit(); // dirty read
+
+ /* Bit 6 */
+ readCombinationOperatorOverrideAllowed();
+
+ /* Bit 5 */
+ readRequiresAuxiliaryBuffer();
+
+ /* Bit 3-4 */
+ readCombinationOperator();
+
+ /* Bit 2 */
+ readDefaultPixelvalue();
+
+ /* Bit 1 */
+ readContainsRefinement();
+
+ /* Bit 0 */
+ readIsLossless();
+
+ /* Bit 15 */
+ readIsStriped();
+
+ /* Bit 0-14 */
+ readMaxStripeSize();
+
+ this.checkInput();
+
+ }
+
+ private void readResolution() throws IOException {
+ resolutionX = (int) subInputStream.readBits(32) & 0xffffffff;
+ resolutionY = (int) subInputStream.readBits(32) & 0xffffffff;
+ }
+
+ private void checkInput() throws InvalidHeaderValueException {
+ if (bitmapHeight == 0xffffffffL)
+ if (!isStriped)
+ log.info("isStriped should contaion the value true");
+ }
+
+ private void readCombinationOperatorOverrideAllowed() throws IOException {
+ /* Bit 6 */
+ if (subInputStream.readBit() == 1) {
+ combinationOperatorOverrideAllowed = true;
+ }
+ }
+
+ private void readRequiresAuxiliaryBuffer() throws IOException {
+ /* Bit 5 */
+ if (subInputStream.readBit() == 1) {
+ requiresAuxiliaryBuffer = true;
+ }
+ }
+
+ private void readCombinationOperator() throws IOException {
+ /* Bit 3-4 */
+ combinationOperator = CombinationOperator.translateOperatorCodeToEnum((short) (subInputStream.readBits(2) & 0xf));
+ }
+
+ private void readDefaultPixelvalue() throws IOException {
+ /* Bit 2 */
+ defaultPixelValue = (short) subInputStream.readBit();
+ }
+
+ private void readContainsRefinement() throws IOException {
+ /* Bit 1 */
+ if (subInputStream.readBit() == 1) {
+ mightContainRefinements = true;
+ }
+ }
+
+ private void readIsLossless() throws IOException {
+ /* Bit 0 */
+ if (subInputStream.readBit() == 1) {
+ isLossless = true;
+ }
+ }
+
+ private void readIsStriped() throws IOException {
+ /* Bit 15 */
+ if (subInputStream.readBit() == 1) {
+ isStriped = true;
+ }
+ }
+
+ private void readMaxStripeSize() throws IOException {
+ /* Bit 0-14 */
+ maxStripeSize = (short) (subInputStream.readBits(15) & 0xffff);
+ }
+
+ private void readWidthAndHeight() throws IOException {
+ bitmapWidth = (int) subInputStream.readBits(32); // & 0xffffffff;
+ bitmapHeight = (int) subInputStream.readBits(32); // & 0xffffffff;
+ }
+
+ public void init(final SegmentHeader header, final SubInputStream sis) throws InvalidHeaderValueException, IOException {
+ subInputStream = sis;
+
+ parseHeader();
+ }
+
+ public int getWidth() {
+ return bitmapWidth;
+ }
+
+ public int getHeight() {
+ return bitmapHeight;
+ }
+
+ public int getResolutionX() {
+ return resolutionX;
+ }
+
+ public int getResolutionY() {
+ return resolutionY;
+ }
+
+ public short getDefaultPixelValue() {
+ return defaultPixelValue;
+ }
+
+ public boolean isCombinationOperatorOverrideAllowed() {
+ return combinationOperatorOverrideAllowed;
+ }
+
+ public CombinationOperator getCombinationOperator() {
+ return combinationOperator;
+ }
+
+ public boolean isStriped() {
+ return isStriped;
+ }
+
+ public short getMaxStripeSize() {
+ return maxStripeSize;
+ }
+
+ public boolean isAuxiliaryBufferRequired() {
+ return requiresAuxiliaryBuffer;
+ }
+
+ public boolean mightContainRefinements() {
+ return mightContainRefinements;
+ }
+
+ public boolean isLossless() {
+ return isLossless;
+ }
+
+ protected int getBitmapWidth() {
+ return bitmapWidth;
+ }
+
+ protected int getBitmapHeight() {
+ return bitmapHeight;
+ }
+}
http://git-wip-us.apache.org/repos/asf/pdfbox-jbig2/blob/4619d28b/src/main/java/org/apache/pdfbox/jbig2/segments/PatternDictionary.java
----------------------------------------------------------------------
diff --git a/src/main/java/org/apache/pdfbox/jbig2/segments/PatternDictionary.java b/src/main/java/org/apache/pdfbox/jbig2/segments/PatternDictionary.java
new file mode 100644
index 0000000..160d8fa
--- /dev/null
+++ b/src/main/java/org/apache/pdfbox/jbig2/segments/PatternDictionary.java
@@ -0,0 +1,220 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.pdfbox.jbig2.segments;
+
+import java.awt.Rectangle;
+import java.io.IOException;
+import java.util.ArrayList;
+
+import org.apache.pdfbox.jbig2.Bitmap;
+import org.apache.pdfbox.jbig2.Dictionary;
+import org.apache.pdfbox.jbig2.SegmentHeader;
+import org.apache.pdfbox.jbig2.err.InvalidHeaderValueException;
+import org.apache.pdfbox.jbig2.image.Bitmaps;
+import org.apache.pdfbox.jbig2.io.SubInputStream;
+import org.apache.pdfbox.jbig2.util.log.Logger;
+import org.apache.pdfbox.jbig2.util.log.LoggerFactory;
+
+/**
+ * This class represents the segment type "Pattern dictionary", 7.4.4.
+ */
+public class PatternDictionary implements Dictionary {
+
+ private final Logger log = LoggerFactory.getLogger(PatternDictionary.class);
+
+ private SubInputStream subInputStream;
+
+ /** Segment data structure (only necessary if MMR is used) */
+ private long dataHeaderOffset;
+ private long dataHeaderLength;
+ private long dataOffset;
+ private long dataLength;
+
+ private short[] gbAtX = null;
+ private short[] gbAtY = null;
+
+ /** Pattern dictionary flags, 7.4.4.1.1 */
+ private boolean isMMREncoded;
+ private byte hdTemplate;
+
+ /** Width of the patterns in the pattern dictionary, 7.4.4.1.2 */
+ private short hdpWidth;
+
+ /** Height of the patterns in the pattern dictionary, 7.4.4.1.3 */
+ private short hdpHeight;
+
+ /** Decoded bitmaps, stored to be used by segments, that refer to it */
+ private ArrayList<Bitmap> patterns;
+
+ /**
+ * Largest gray-scale value, 7.4.4.1.4
+ *
+ * Value: one less than the number of patterns defined in this pattern dictionary
+ */
+ private int grayMax;
+
+ private void parseHeader() throws IOException, InvalidHeaderValueException {
+ /* Bit 3-7 */
+ subInputStream.readBits(5); // Dirty read ...
+
+ /* Bit 1-2 */
+ readTemplate();
+
+ /* Bit 0 */
+ readIsMMREncoded();
+
+ readPatternWidthAndHeight();
+
+ readGrayMax();
+
+ /* Segment data structure */
+ computeSegmentDataStructure();
+
+ this.checkInput();
+ }
+
+ private void readTemplate() throws IOException {
+ /* Bit 1-2 */
+ hdTemplate = (byte) subInputStream.readBits(2);
+ }
+
+ private void readIsMMREncoded() throws IOException {
+ /* Bit 0 */
+ if (subInputStream.readBit() == 1) {
+ isMMREncoded = true;
+ }
+ }
+
+ private void readPatternWidthAndHeight() throws IOException {
+ hdpWidth = subInputStream.readByte();
+ hdpHeight = subInputStream.readByte();
+ }
+
+ private void readGrayMax() throws IOException {
+ grayMax = (int) (subInputStream.readBits(32) & 0xffffffff);
+ }
+
+ private void computeSegmentDataStructure() throws IOException {
+ dataOffset = subInputStream.getStreamPosition();
+ dataHeaderLength = dataOffset - dataHeaderOffset;
+ dataLength = subInputStream.length() - dataHeaderLength;
+ }
+
+ private void checkInput() throws InvalidHeaderValueException {
+ if (hdpHeight < 1 || hdpWidth < 1) {
+ throw new InvalidHeaderValueException("Width/Heigth must be greater than zero.");
+ }
+
+ if (isMMREncoded) {
+ if (hdTemplate != 0) {
+ log.info("hdTemplate should contain the value 0");
+ }
+ }
+ }
+
+ /**
+ * This method decodes a pattern dictionary segment and returns an array of {@link Bitmap} s. Each
+ * of this {@link Bitmap}s is a pattern.<br>
+ * The procedure is described in 6.7.5 (page 43).
+ *
+ * @return An array of {@link Bitmap}s as result of the decoding procedure.
+ */
+ public ArrayList<Bitmap> getDictionary() throws IOException, InvalidHeaderValueException {
+ if (null == patterns) {
+
+ if (!isMMREncoded) {
+ setGbAtPixels();
+ }
+
+ // 2)
+ final GenericRegion genericRegion = new GenericRegion(subInputStream);
+ genericRegion.setParameters(isMMREncoded, dataOffset, dataLength, hdpHeight, (grayMax + 1) * hdpWidth,
+ hdTemplate, false, false, gbAtX, gbAtY);
+
+ final Bitmap collectiveBitmap = genericRegion.getRegionBitmap();
+
+ // 4)
+ extractPatterns(collectiveBitmap);
+ }
+
+ return patterns;
+ }
+
+ private void extractPatterns(Bitmap collectiveBitmap) {
+ // 3)
+ int gray = 0;
+ patterns = new ArrayList<Bitmap>(grayMax + 1);
+
+ // 4)
+ while (gray <= grayMax) {
+ // 4) a) Retrieve a pattern bitmap by extracting it out of the collective bitmap
+ final Rectangle roi = new Rectangle(hdpWidth * gray, 0, hdpWidth, hdpHeight);
+ final Bitmap patternBitmap = Bitmaps.extract(roi, collectiveBitmap);
+ patterns.add(patternBitmap);
+
+ // 4) b)
+ gray++;
+ }
+ }
+
+ private void setGbAtPixels() {
+ if (hdTemplate == 0) {
+ gbAtX = new short[4];
+ gbAtY = new short[4];
+ gbAtX[0] = (short) -hdpWidth;
+ gbAtY[0] = 0;
+ gbAtX[1] = -3;
+ gbAtY[1] = -1;
+ gbAtX[2] = 2;
+ gbAtY[2] = -2;
+ gbAtX[3] = -2;
+ gbAtY[3] = -2;
+
+ } else {
+ gbAtX = new short[1];
+ gbAtY = new short[1];
+ gbAtX[0] = (short) -hdpWidth;
+ gbAtY[0] = 0;
+ }
+ }
+
+ public void init(SegmentHeader header, SubInputStream sis) throws InvalidHeaderValueException, IOException {
+ this.subInputStream = sis;
+ parseHeader();
+ }
+
+ protected boolean isMMREncoded() {
+ return isMMREncoded;
+ }
+
+ protected byte getHdTemplate() {
+ return hdTemplate;
+ }
+
+ protected short getHdpWidth() {
+ return hdpWidth;
+ }
+
+ protected short getHdpHeight() {
+ return hdpHeight;
+ }
+
+ protected int getGrayMax() {
+ return grayMax;
+ }
+}
http://git-wip-us.apache.org/repos/asf/pdfbox-jbig2/blob/4619d28b/src/main/java/org/apache/pdfbox/jbig2/segments/Profiles.java
----------------------------------------------------------------------
diff --git a/src/main/java/org/apache/pdfbox/jbig2/segments/Profiles.java b/src/main/java/org/apache/pdfbox/jbig2/segments/Profiles.java
new file mode 100644
index 0000000..e5eee40
--- /dev/null
+++ b/src/main/java/org/apache/pdfbox/jbig2/segments/Profiles.java
@@ -0,0 +1,31 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.pdfbox.jbig2.segments;
+
+import org.apache.pdfbox.jbig2.SegmentData;
+import org.apache.pdfbox.jbig2.SegmentHeader;
+import org.apache.pdfbox.jbig2.io.SubInputStream;
+
+/**
+ * <b>TODO:</b> This class is not implemented yet and empty. Wait for use cases.
+ */
+public class Profiles implements SegmentData {
+
+ public void init(SegmentHeader header, SubInputStream sis) {
+ }
+}
http://git-wip-us.apache.org/repos/asf/pdfbox-jbig2/blob/4619d28b/src/main/java/org/apache/pdfbox/jbig2/segments/RegionSegmentInformation.java
----------------------------------------------------------------------
diff --git a/src/main/java/org/apache/pdfbox/jbig2/segments/RegionSegmentInformation.java b/src/main/java/org/apache/pdfbox/jbig2/segments/RegionSegmentInformation.java
new file mode 100644
index 0000000..6cbd9a2
--- /dev/null
+++ b/src/main/java/org/apache/pdfbox/jbig2/segments/RegionSegmentInformation.java
@@ -0,0 +1,107 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.pdfbox.jbig2.segments;
+
+import java.io.IOException;
+
+import org.apache.pdfbox.jbig2.SegmentData;
+import org.apache.pdfbox.jbig2.SegmentHeader;
+import org.apache.pdfbox.jbig2.err.IntegerMaxValueException;
+import org.apache.pdfbox.jbig2.err.InvalidHeaderValueException;
+import org.apache.pdfbox.jbig2.io.SubInputStream;
+import org.apache.pdfbox.jbig2.util.CombinationOperator;
+
+/**
+ * This class represents the "Region segment information" field, 7.4.1 (page 50). <br>
+ * Every region segment data starts with this part.
+ */
+public class RegionSegmentInformation implements SegmentData {
+
+ private SubInputStream subInputStream;
+
+ /** Region segment bitmap width, 7.4.1.1 */
+ private int bitmapWidth;
+
+ /** Region segment bitmap height, 7.4.1.2 */
+ private int bitmapHeight;
+
+ /** Region segment bitmap X location, 7.4.1.3 */
+ private int xLocation;
+
+ /** Region segment bitmap Y location, 7.4.1.4 */
+ private int yLocation;
+
+ /** Region segment flags, 7.4.1.5 */
+ private CombinationOperator combinationOperator;
+
+ public RegionSegmentInformation(SubInputStream subInputStream) {
+ this.subInputStream = subInputStream;
+ }
+
+ public RegionSegmentInformation() {
+ }
+
+ public void parseHeader() throws IOException {
+ this.bitmapWidth = ((int) (subInputStream.readBits(32) & 0xffffffff));
+ this.bitmapHeight = ((int) (subInputStream.readBits(32) & 0xffffffff));
+ this.xLocation = ((int) (subInputStream.readBits(32) & 0xffffffff));
+ this.yLocation = ((int) (subInputStream.readBits(32) & 0xffffffff));
+
+ /* Bit 3-7 */
+ subInputStream.readBits(5); // Dirty read... reserved bits are 0
+
+ /* Bit 0-2 */
+ readCombinationOperator();
+ }
+
+ private void readCombinationOperator() throws IOException {
+ this.combinationOperator = (CombinationOperator.translateOperatorCodeToEnum((short) (subInputStream.readBits(3) & 0xf)));
+ }
+
+ public void init(SegmentHeader header, SubInputStream sis) throws InvalidHeaderValueException,
+ IntegerMaxValueException, IOException {
+ }
+
+ public void setBitmapWidth(final int bitmapWidth) {
+ this.bitmapWidth = bitmapWidth;
+ }
+
+ public int getBitmapWidth() {
+ return bitmapWidth;
+ }
+
+ public void setBitmapHeight(final int bitmapHeight) {
+ this.bitmapHeight = bitmapHeight;
+ }
+
+ public int getBitmapHeight() {
+ return bitmapHeight;
+ }
+
+ public int getXLocation() {
+ return xLocation;
+ }
+
+ public int getYLocation() {
+ return yLocation;
+ }
+
+ public CombinationOperator getCombinationOperator() {
+ return combinationOperator;
+ }
+}
http://git-wip-us.apache.org/repos/asf/pdfbox-jbig2/blob/4619d28b/src/main/java/org/apache/pdfbox/jbig2/segments/SymbolDictionary.java
----------------------------------------------------------------------
diff --git a/src/main/java/org/apache/pdfbox/jbig2/segments/SymbolDictionary.java b/src/main/java/org/apache/pdfbox/jbig2/segments/SymbolDictionary.java
new file mode 100644
index 0000000..a3a6394
--- /dev/null
+++ b/src/main/java/org/apache/pdfbox/jbig2/segments/SymbolDictionary.java
@@ -0,0 +1,843 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.pdfbox.jbig2.segments;
+
+import java.awt.Rectangle;
+import java.io.IOException;
+import java.util.ArrayList;
+
+import org.apache.pdfbox.jbig2.Bitmap;
+import org.apache.pdfbox.jbig2.Dictionary;
+import org.apache.pdfbox.jbig2.JBIG2ImageReader;
+import org.apache.pdfbox.jbig2.Region;
+import org.apache.pdfbox.jbig2.SegmentHeader;
+import org.apache.pdfbox.jbig2.decoder.arithmetic.ArithmeticDecoder;
+import org.apache.pdfbox.jbig2.decoder.arithmetic.ArithmeticIntegerDecoder;
+import org.apache.pdfbox.jbig2.decoder.arithmetic.CX;
+import org.apache.pdfbox.jbig2.decoder.huffman.EncodedTable;
+import org.apache.pdfbox.jbig2.decoder.huffman.HuffmanTable;
+import org.apache.pdfbox.jbig2.decoder.huffman.StandardTables;
+import org.apache.pdfbox.jbig2.err.IntegerMaxValueException;
+import org.apache.pdfbox.jbig2.err.InvalidHeaderValueException;
+import org.apache.pdfbox.jbig2.image.Bitmaps;
+import org.apache.pdfbox.jbig2.io.SubInputStream;
+import org.apache.pdfbox.jbig2.util.log.Logger;
+import org.apache.pdfbox.jbig2.util.log.LoggerFactory;
+
+/**
+ * This class represents the data of segment type "Symbol dictionary". Parsing is described in
+ * 7.4.2.1.1 - 7.4.1.1.5 and decoding procedure is described in 6.5.
+ */
+public class SymbolDictionary implements Dictionary {
+
+ private final Logger log = LoggerFactory.getLogger(SymbolDictionary.class);
+
+ private SubInputStream subInputStream;
+
+ /** Symbol dictionary flags, 7.4.2.1.1 */
+ private short sdrTemplate;
+ private byte sdTemplate;
+ private boolean isCodingContextRetained;
+ private boolean isCodingContextUsed;
+ private short sdHuffAggInstanceSelection;
+ private short sdHuffBMSizeSelection;
+ private short sdHuffDecodeWidthSelection;
+ private short sdHuffDecodeHeightSelection;
+ private boolean useRefinementAggregation;
+ private boolean isHuffmanEncoded;
+
+ /** Symbol dictionary AT flags, 7.4.2.1.2 */
+ private short[] sdATX;
+ private short[] sdATY;
+
+ /** Symbol dictionary refinement AT flags, 7.4.2.1.3 */
+ private short[] sdrATX;
+ private short[] sdrATY;
+
+ /** Number of exported symbols, 7.4.2.1.4 */
+ private int amountOfExportSymbolss;
+
+ /** Number of new symbols, 7.4.2.1.5 */
+ private int amountOfNewSymbolss;
+
+ /** Further parameters */
+ private SegmentHeader segmentHeader;
+ private int amountOfImportedSymbolss;
+ private ArrayList<Bitmap> importSymbols;
+ private int amountOfDecodedSymbols;
+ private Bitmap[] newSymbols;
+
+ /** User-supplied tables * */
+ private HuffmanTable dhTable;
+ private HuffmanTable dwTable;
+ private HuffmanTable bmSizeTable;
+ private HuffmanTable aggInstTable;
+
+ /** Return value of that segment */
+ private ArrayList<Bitmap> exportSymbols;
+ private ArrayList<Bitmap> sbSymbols;
+
+ private ArithmeticDecoder arithmeticDecoder;
+ private ArithmeticIntegerDecoder iDecoder;
+
+ private TextRegion textRegion;
+ private GenericRegion genericRegion;
+ private GenericRefinementRegion genericRefinementRegion;
+ private CX cx;
+
+ private CX cxIADH;
+ private CX cxIADW;
+ private CX cxIAAI;
+ private CX cxIAEX;
+ private CX cxIARDX;
+ private CX cxIARDY;
+ private CX cxIADT;
+
+ protected CX cxIAID;
+ private int sbSymCodeLen;
+
+ public SymbolDictionary() {
+ }
+
+ public SymbolDictionary(SubInputStream subInputStream, SegmentHeader segmentHeader) throws IOException {
+ this.subInputStream = subInputStream;
+ this.segmentHeader = segmentHeader;
+ }
+
+ private void parseHeader() throws IOException, InvalidHeaderValueException, IntegerMaxValueException {
+ readRegionFlags();
+ setAtPixels();
+ setRefinementAtPixels();
+ readAmountOfExportedSymbols();
+ readAmountOfNewSymbols();
+ setInSyms();
+
+ if (isCodingContextUsed) {
+ SegmentHeader[] rtSegments = segmentHeader.getRtSegments();
+
+ for (int i = rtSegments.length - 1; i >= 0; i--) {
+
+ if (rtSegments[i].getSegmentType() == 0) {
+ SymbolDictionary symbolDictionary = (SymbolDictionary) rtSegments[i].getSegmentData();
+
+ if (symbolDictionary.isCodingContextRetained) {
+ /* 7.4.2.2 3) */
+ setRetainedCodingContexts(symbolDictionary);
+ }
+ break;
+ }
+ }
+ }
+
+ this.checkInput();
+ }
+
+ private void readRegionFlags() throws IOException {
+ /* Bit 13-15 */
+ subInputStream.readBits(3); // Dirty read... reserved bits must be 0
+
+ /* Bit 12 */
+ sdrTemplate = (short) subInputStream.readBit();
+
+ /* Bit 10-11 */
+ sdTemplate = (byte) (subInputStream.readBits(2) & 0xf);
+
+ /* Bit 9 */
+ if (subInputStream.readBit() == 1) {
+ isCodingContextRetained = true;
+ }
+
+ /* Bit 8 */
+ if (subInputStream.readBit() == 1) {
+ isCodingContextUsed = true;
+ }
+
+ /* Bit 7 */
+ sdHuffAggInstanceSelection = (short) subInputStream.readBit();
+
+ /* Bit 6 */
+ sdHuffBMSizeSelection = (short) subInputStream.readBit();
+
+ /* Bit 4-5 */
+ sdHuffDecodeWidthSelection = (short) (subInputStream.readBits(2) & 0xf);
+
+ /* Bit 2-3 */
+ sdHuffDecodeHeightSelection = (short) (subInputStream.readBits(2) & 0xf);
+
+ /* Bit 1 */
+ if (subInputStream.readBit() == 1) {
+ useRefinementAggregation = true;
+ }
+
+ /* Bit 0 */
+ if (subInputStream.readBit() == 1) {
+ isHuffmanEncoded = true;
+ }
+ }
+
+ private void setAtPixels() throws IOException {
+ if (!isHuffmanEncoded) {
+ if (sdTemplate == 0) {
+ readAtPixels(4);
+ } else {
+ readAtPixels(1);
+ }
+ }
+ }
+
+ private void setRefinementAtPixels() throws IOException {
+ if (useRefinementAggregation && sdrTemplate == 0) {
+ readRefinementAtPixels(2);
+ }
+ }
+
+ private void readAtPixels(final int amountOfPixels) throws IOException {
+ sdATX = new short[amountOfPixels];
+ sdATY = new short[amountOfPixels];
+
+ for (int i = 0; i < amountOfPixels; i++) {
+ sdATX[i] = subInputStream.readByte();
+ sdATY[i] = subInputStream.readByte();
+ }
+ }
+
+ private void readRefinementAtPixels(final int amountOfAtPixels) throws IOException {
+ sdrATX = new short[amountOfAtPixels];
+ sdrATY = new short[amountOfAtPixels];
+
+ for (int i = 0; i < amountOfAtPixels; i++) {
+ sdrATX[i] = subInputStream.readByte();
+ sdrATY[i] = subInputStream.readByte();
+ }
+ }
+
+ private void readAmountOfExportedSymbols() throws IOException {
+ amountOfExportSymbolss = (int) subInputStream.readBits(32); // & 0xffffffff;
+ }
+
+ private void readAmountOfNewSymbols() throws IOException {
+ amountOfNewSymbolss = (int) subInputStream.readBits(32); // & 0xffffffff;
+ }
+
+ private void setInSyms() throws IOException, InvalidHeaderValueException, IntegerMaxValueException {
+ if (segmentHeader.getRtSegments() != null) {
+ retrieveImportSymbols();
+ } else {
+ importSymbols = new ArrayList<Bitmap>();
+ }
+ }
+
+ private void setRetainedCodingContexts(final SymbolDictionary sd) {
+ this.arithmeticDecoder = sd.arithmeticDecoder;
+ this.isHuffmanEncoded = sd.isHuffmanEncoded;
+ this.useRefinementAggregation = sd.useRefinementAggregation;
+ this.sdTemplate = sd.sdTemplate;
+ this.sdrTemplate = sd.sdrTemplate;
+ this.sdATX = sd.sdATX;
+ this.sdATY = sd.sdATY;
+ this.sdrATX = sd.sdrATX;
+ this.sdrATY = sd.sdrATY;
+ this.cx = sd.cx;
+ }
+
+ private void checkInput() throws InvalidHeaderValueException {
+ if (sdHuffDecodeHeightSelection == 2) {
+ log.info("sdHuffDecodeHeightSelection = " + sdHuffDecodeHeightSelection + " (value not permitted)");
+ }
+
+ if (sdHuffDecodeWidthSelection == 2) {
+ log.info("sdHuffDecodeWidthSelection = " + sdHuffDecodeWidthSelection + " (value not permitted)");
+ }
+
+ if (isHuffmanEncoded) {
+ if (sdTemplate != 0) {
+ log.info("sdTemplate = " + sdTemplate + " (should be 0)");
+ sdTemplate = 0;
+ }
+ if (!useRefinementAggregation) {
+ if (isCodingContextRetained) {
+ log.info("isCodingContextRetained = " + isCodingContextRetained + " (should be 0)");
+ isCodingContextRetained = false;
+ }
+
+ if (isCodingContextUsed) {
+ log.info("isCodingContextUsed = " + isCodingContextUsed + " (should be 0)");
+ isCodingContextUsed = false;
+ }
+ }
+
+ } else {
+ if (sdHuffBMSizeSelection != 0) {
+ log.info("sdHuffBMSizeSelection should be 0");
+ sdHuffBMSizeSelection = 0;
+ }
+ if (sdHuffDecodeWidthSelection != 0) {
+ log.info("sdHuffDecodeWidthSelection should be 0");
+ sdHuffDecodeWidthSelection = 0;
+ }
+ if (sdHuffDecodeHeightSelection != 0) {
+ log.info("sdHuffDecodeHeightSelection should be 0");
+ sdHuffDecodeHeightSelection = 0;
+ }
+ }
+
+ if (!useRefinementAggregation) {
+ if (sdrTemplate != 0) {
+ log.info("sdrTemplate = " + sdrTemplate + " (should be 0)");
+ sdrTemplate = 0;
+ }
+ }
+
+ if (!isHuffmanEncoded || !useRefinementAggregation) {
+ if (sdHuffAggInstanceSelection != 0) {
+ log.info("sdHuffAggInstanceSelection = " + sdHuffAggInstanceSelection + " (should be 0)");
+ sdHuffAggInstanceSelection = 0;
+ }
+ }
+ }
+
+ /**
+ * 6.5.5 Decoding the symbol dictionary
+ *
+ * @return List of decoded symbol bitmaps as an <code>ArrayList</code>
+ */
+ public ArrayList<Bitmap> getDictionary() throws IOException, IntegerMaxValueException, InvalidHeaderValueException {
+ long timestamp = System.currentTimeMillis();
+ if (null == exportSymbols) {
+
+ if (useRefinementAggregation)
+ sbSymCodeLen = getSbSymCodeLen();
+
+ if (!isHuffmanEncoded) {
+ setCodingStatistics();
+ }
+
+ /* 6.5.5 1) */
+ newSymbols = new Bitmap[amountOfNewSymbolss];
+
+ /* 6.5.5 2) */
+ int[] newSymbolsWidths = null;
+ if (isHuffmanEncoded && !useRefinementAggregation) {
+ newSymbolsWidths = new int[amountOfNewSymbolss];
+ }
+
+ setSymbolsArray();
+
+ /* 6.5.5 3) */
+ int heightClassHeight = 0;
+ amountOfDecodedSymbols = 0;
+
+ /* 6.5.5 4 a) */
+ while (amountOfDecodedSymbols != amountOfNewSymbolss) {
+
+ /* 6.5.5 4 b) */
+ heightClassHeight += decodeHeightClassDeltaHeight();
+ int symbolWidth = 0;
+ int totalWidth = 0;
+ final int heightClassFirstSymbolIndex = amountOfDecodedSymbols;
+
+ /* 6.5.5 4 c) */
+
+ // Repeat until OOB - OOB sends a break;
+ while (true) {
+ /* 4 c) i) */
+ final long differenceWidth = decodeDifferenceWidth();
+
+ // If result is OOB, then all the symbols in this height
+ // class has been decoded; proceed to step 4 d)
+ if (differenceWidth == Long.MAX_VALUE) {
+ break;
+ }
+
+ symbolWidth += differenceWidth;
+ totalWidth += symbolWidth;
+
+ /* 4 c) ii) */
+ if (!isHuffmanEncoded || useRefinementAggregation) {
+ if (!useRefinementAggregation) {
+ // 6.5.8.1 - Direct coded
+ decodeDirectlyThroughGenericRegion(symbolWidth, heightClassHeight);
+ } else {
+ // 6.5.8.2 - Refinement/Aggregate-coded
+ decodeAggregate(symbolWidth, heightClassHeight);
+ }
+ } else if (isHuffmanEncoded && !useRefinementAggregation) {
+ /* 4 c) iii) */
+ newSymbolsWidths[amountOfDecodedSymbols] = symbolWidth;
+ }
+ amountOfDecodedSymbols++;
+ }
+
+ /* 6.5.5 4 d) */
+ if (isHuffmanEncoded && !useRefinementAggregation) {
+ /* 6.5.9 */
+ final long bmSize;
+ if (sdHuffBMSizeSelection == 0) {
+ bmSize = StandardTables.getTable(1).decode(subInputStream);
+ } else {
+ bmSize = huffDecodeBmSize();
+ }
+
+ subInputStream.skipBits();
+
+ final Bitmap heightClassCollectiveBitmap = decodeHeightClassCollectiveBitmap(bmSize, heightClassHeight,
+ totalWidth);
+
+ subInputStream.skipBits();
+ decodeHeightClassBitmap(heightClassCollectiveBitmap, heightClassFirstSymbolIndex, heightClassHeight,
+ newSymbolsWidths);
+ }
+ }
+
+ /* 5) */
+ /* 6.5.10 1) - 5) */
+
+ final int[] exFlags = getToExportFlags();
+
+ /* 6.5.10 6) - 8) */
+ setExportedSymbols(exFlags);
+ }
+
+ if (JBIG2ImageReader.PERFORMANCE_TEST)
+ log.info("SYMBOL DECODING: " + (System.currentTimeMillis() - timestamp) + " ms");
+
+ // DictionaryViewer.viewSymbols(sdExSyms);
+
+ return exportSymbols;
+ }
+
+ private void setCodingStatistics() throws IOException {
+ if (cxIADT == null) {
+ cxIADT = new CX(512, 1);
+ }
+
+ if (cxIADH == null) {
+ cxIADH = new CX(512, 1);
+ }
+
+ if (cxIADW == null) {
+ cxIADW = new CX(512, 1);
+ }
+
+ if (cxIAAI == null) {
+ cxIAAI = new CX(512, 1);
+ }
+
+ if (cxIAEX == null) {
+ cxIAEX = new CX(512, 1);
+ }
+
+ if (useRefinementAggregation && cxIAID == null) {
+ cxIAID = new CX(1 << sbSymCodeLen, 1);
+ cxIARDX = new CX(512, 1);
+ cxIARDY = new CX(512, 1);
+ }
+
+ if (cx == null) {
+ cx = new CX(65536, 1);
+ }
+
+ if (arithmeticDecoder == null) {
+ arithmeticDecoder = new ArithmeticDecoder(subInputStream);
+ }
+
+ if (iDecoder == null) {
+ iDecoder = new ArithmeticIntegerDecoder(arithmeticDecoder);
+ }
+
+ }
+
+ private final void decodeHeightClassBitmap(final Bitmap heightClassCollectiveBitmap,
+ final int heightClassFirstSymbol, final int heightClassHeight, final int[] newSymbolsWidths)
+ throws IntegerMaxValueException, InvalidHeaderValueException, IOException {
+
+ for (int i = heightClassFirstSymbol; i < amountOfDecodedSymbols; i++) {
+ int startColumn = 0;
+
+ for (int j = heightClassFirstSymbol; j <= i - 1; j++) {
+ startColumn += newSymbolsWidths[j];
+ }
+
+ final Rectangle roi = new Rectangle(startColumn, 0, newSymbolsWidths[i], heightClassHeight);
+ final Bitmap symbolBitmap = Bitmaps.extract(roi, heightClassCollectiveBitmap);
+ newSymbols[i] = symbolBitmap;
+ sbSymbols.add(symbolBitmap);
+ }
+ }
+
+ private final void decodeAggregate(final int symbolWidth, final int heightClassHeight) throws IOException,
+ InvalidHeaderValueException, IntegerMaxValueException {
+ // 6.5.8.2 1)
+ // 6.5.8.2.1 - Number of symbol instances in aggregation
+ final long amountOfRefinementAggregationInstances;
+ if (isHuffmanEncoded) {
+ log.info("Refinement or aggregate-coded symbols may couse problems with huffman decoding!");
+ amountOfRefinementAggregationInstances = huffDecodeRefAggNInst();
+ } else {
+ amountOfRefinementAggregationInstances = iDecoder.decode(cxIAAI);
+ }
+
+ if (amountOfRefinementAggregationInstances > 1) {
+ // 6.5.8.2 2)
+ decodeThroughTextRegion(symbolWidth, heightClassHeight, amountOfRefinementAggregationInstances);
+ } else if (amountOfRefinementAggregationInstances == 1) {
+ // 6.5.8.2 3) refers to 6.5.8.2.2
+ decodeRefinedSymbol(symbolWidth, heightClassHeight);
+ }
+ }
+
+ private final long huffDecodeRefAggNInst() throws IOException, InvalidHeaderValueException {
+ if (sdHuffAggInstanceSelection == 0) {
+ return StandardTables.getTable(1).decode(subInputStream);
+ } else if (sdHuffAggInstanceSelection == 1) {
+ if (aggInstTable == null) {
+ int aggregationInstanceNumber = 0;
+
+ if (sdHuffDecodeHeightSelection == 3) {
+ aggregationInstanceNumber++;
+ }
+ if (sdHuffDecodeWidthSelection == 3) {
+ aggregationInstanceNumber++;
+ }
+ if (sdHuffBMSizeSelection == 3) {
+ aggregationInstanceNumber++;
+ }
+
+ aggInstTable = getUserTable(aggregationInstanceNumber);
+ }
+ return aggInstTable.decode(subInputStream);
+ }
+ return 0;
+ }
+
+ private final void decodeThroughTextRegion(final int symbolWidth, final int heightClassHeight,
+ final long amountOfRefinementAggregationInstances) throws IOException, IntegerMaxValueException,
+ InvalidHeaderValueException {
+ if (textRegion == null) {
+ textRegion = new TextRegion(subInputStream, null);
+
+ textRegion.setContexts(cx, // default context
+ new CX(512, 1), // IADT
+ new CX(512, 1), // IAFS
+ new CX(512, 1), // IADS
+ new CX(512, 1), // IAIT
+ cxIAID, // IAID
+ new CX(512, 1), // IARDW
+ new CX(512, 1), // IARDH
+ new CX(512, 1), // IARDX
+ new CX(512, 1) // IARDY
+ );
+ }
+
+ // 6.5.8.2.4 Concatenating the array used as parameter later.
+ setSymbolsArray();
+
+ // 6.5.8.2 2) Parameters set according to Table 17, page 36
+ textRegion.setParameters(arithmeticDecoder, iDecoder, isHuffmanEncoded, true, symbolWidth, heightClassHeight,
+ amountOfRefinementAggregationInstances, 1, (amountOfImportedSymbolss + amountOfDecodedSymbols), (short) 0,
+ (short) 0, (short) 0, (short) 1, (short) 0, (short) 0, (short) 0, (short) 0, (short) 0, (short) 0, (short) 0,
+ (short) 0, (short) 0, sdrTemplate, sdrATX, sdrATY, sbSymbols, sbSymCodeLen);
+
+ addSymbol(textRegion);
+ }
+
+ private final void decodeRefinedSymbol(final int symbolWidth, final int heightClassHeight) throws IOException,
+ InvalidHeaderValueException, IntegerMaxValueException {
+
+ final int id;
+ final int rdx;
+ final int rdy;
+ // long symInRefSize = 0;
+ if (isHuffmanEncoded) {
+ /* 2) - 4) */
+ id = (int) subInputStream.readBits(sbSymCodeLen);
+ rdx = (int) StandardTables.getTable(15).decode(subInputStream);
+ rdy = (int) StandardTables.getTable(15).decode(subInputStream);
+
+ /* 5) a) */
+ /* symInRefSize = */StandardTables.getTable(1).decode(subInputStream);
+
+ /* 5) b) - Skip over remaining bits */
+ subInputStream.skipBits();
+ } else {
+ /* 2) - 4) */
+ id = iDecoder.decodeIAID(cxIAID, sbSymCodeLen);
+ rdx = (int) iDecoder.decode(cxIARDX);
+ rdy = (int) iDecoder.decode(cxIARDY);
+ }
+
+ /* 6) */
+ setSymbolsArray();
+ final Bitmap ibo = sbSymbols.get(id);
+ decodeNewSymbols(symbolWidth, heightClassHeight, ibo, rdx, rdy);
+
+ /* 7) */
+ if (isHuffmanEncoded) {
+ subInputStream.skipBits();
+ // Make sure that the processed bytes are equal to the value read in step 5 a)
+ }
+ }
+
+ private final void decodeNewSymbols(final int symWidth, final int hcHeight, final Bitmap ibo, final int rdx,
+ final int rdy) throws IOException, InvalidHeaderValueException, IntegerMaxValueException {
+ if (genericRefinementRegion == null) {
+ genericRefinementRegion = new GenericRefinementRegion(subInputStream);
+
+ if (arithmeticDecoder == null) {
+ arithmeticDecoder = new ArithmeticDecoder(subInputStream);
+ }
+
+ if (cx == null) {
+ cx = new CX(65536, 1);
+ }
+ }
+
+ // Parameters as shown in Table 18, page 36
+ genericRefinementRegion.setParameters(cx, arithmeticDecoder, sdrTemplate, symWidth, hcHeight, ibo, rdx, rdy, false,
+ sdrATX, sdrATY);
+
+ addSymbol(genericRefinementRegion);
+ }
+
+ private final void decodeDirectlyThroughGenericRegion(final int symWidth, final int hcHeight) throws IOException,
+ IntegerMaxValueException, InvalidHeaderValueException {
+ if (genericRegion == null) {
+ genericRegion = new GenericRegion(subInputStream);
+ }
+
+ // Parameters set according to Table 16, page 35
+ genericRegion.setParameters(false, sdTemplate, false, false, sdATX, sdATY, symWidth, hcHeight, cx,
+ arithmeticDecoder);
+
+ addSymbol(genericRegion);
+ }
+
+ private final void addSymbol(final Region region) throws IntegerMaxValueException, InvalidHeaderValueException,
+ IOException {
+ final Bitmap symbol = region.getRegionBitmap();
+ newSymbols[amountOfDecodedSymbols] = symbol;
+ sbSymbols.add(symbol);
+ }
+
+ private final long decodeDifferenceWidth() throws IOException, InvalidHeaderValueException {
+ if (isHuffmanEncoded) {
+ switch (sdHuffDecodeWidthSelection){
+ case 0 :
+ return StandardTables.getTable(2).decode(subInputStream);
+ case 1 :
+ return StandardTables.getTable(3).decode(subInputStream);
+ case 3 :
+ if (dwTable == null) {
+ int dwNr = 0;
+
+ if (sdHuffDecodeHeightSelection == 3) {
+ dwNr++;
+ }
+ dwTable = getUserTable(dwNr);
+ }
+
+ return dwTable.decode(subInputStream);
+ }
+ } else {
+ return iDecoder.decode(cxIADW);
+ }
+ return 0;
+ }
+
+ private final long decodeHeightClassDeltaHeight() throws IOException, InvalidHeaderValueException {
+ if (isHuffmanEncoded) {
+ return decodeHeightClassDeltaHeightWithHuffman();
+ } else {
+ return iDecoder.decode(cxIADH);
+ }
+ }
+
+ /**
+ * 6.5.6 if isHuffmanEncoded
+ *
+ * @return long - Result of decoding HCDH
+ * @throws IOException
+ * @throws InvalidHeaderValueException
+ */
+ private final long decodeHeightClassDeltaHeightWithHuffman() throws IOException, InvalidHeaderValueException {
+ switch (sdHuffDecodeHeightSelection){
+ case 0 :
+ return StandardTables.getTable(4).decode(subInputStream);
+ case 1 :
+ return StandardTables.getTable(5).decode(subInputStream);
+ case 3 :
+ if (dhTable == null) {
+ dhTable = getUserTable(0);
+ }
+ return dhTable.decode(subInputStream);
+ }
+
+ return 0;
+ }
+
+ private final Bitmap decodeHeightClassCollectiveBitmap(final long bmSize, final int heightClassHeight,
+ final int totalWidth) throws IOException {
+ if (bmSize == 0) {
+ final Bitmap heightClassCollectiveBitmap = new Bitmap(totalWidth, heightClassHeight);
+
+ for (int i = 0; i < heightClassCollectiveBitmap.getByteArray().length; i++) {
+ heightClassCollectiveBitmap.setByte(i, subInputStream.readByte());
+ }
+
+ return heightClassCollectiveBitmap;
+ } else {
+ if (genericRegion == null) {
+ genericRegion = new GenericRegion(subInputStream);
+ }
+
+ genericRegion.setParameters(true, subInputStream.getStreamPosition(), bmSize, heightClassHeight, totalWidth);
+
+ return genericRegion.getRegionBitmap();
+ }
+ }
+
+ private void setExportedSymbols(int[] toExportFlags) {
+ exportSymbols = new ArrayList<Bitmap>(amountOfExportSymbolss);
+
+ for (int i = 0; i < amountOfImportedSymbolss + amountOfNewSymbolss; i++) {
+
+ if (toExportFlags[i] == 1) {
+ if (i < amountOfImportedSymbolss) {
+ exportSymbols.add(importSymbols.get(i));
+ } else {
+ exportSymbols.add(newSymbols[i - amountOfImportedSymbolss]);
+ }
+ }
+ }
+ }
+
+ private int[] getToExportFlags() throws IOException, InvalidHeaderValueException {
+ int currentExportFlag = 0;
+ long exRunLength = 0;
+ final int[] exportFlags = new int[amountOfImportedSymbolss + amountOfNewSymbolss];
+
+ for (int exportIndex = 0; exportIndex < amountOfImportedSymbolss + amountOfNewSymbolss; exportIndex += exRunLength) {
+
+ if (isHuffmanEncoded) {
+ exRunLength = StandardTables.getTable(1).decode(subInputStream);
+ } else {
+ exRunLength = iDecoder.decode(cxIAEX);
+ }
+
+ if (exRunLength != 0) {
+ for (int index = exportIndex; index < exportIndex + exRunLength; index++) {
+ exportFlags[index] = currentExportFlag;
+ }
+ }
+
+ currentExportFlag = (currentExportFlag == 0) ? 1 : 0;
+ }
+
+ return exportFlags;
+ }
+
+ private final long huffDecodeBmSize() throws IOException, InvalidHeaderValueException {
+ if (bmSizeTable == null) {
+ int bmNr = 0;
+
+ if (sdHuffDecodeHeightSelection == 3) {
+ bmNr++;
+ }
+
+ if (sdHuffDecodeWidthSelection == 3) {
+ bmNr++;
+ }
+
+ bmSizeTable = getUserTable(bmNr);
+ }
+ return bmSizeTable.decode(subInputStream);
+ }
+
+ /**
+ * 6.5.8.2.3 - Setting SBSYMCODES and SBSYMCODELEN
+ *
+ * @return Result of computing SBSYMCODELEN
+ * @throws IOException
+ */
+ private int getSbSymCodeLen() throws IOException {
+ if (isHuffmanEncoded) {
+ return Math.max((int) (Math.ceil(Math.log(amountOfImportedSymbolss + amountOfNewSymbolss) / Math.log(2))), 1);
+ } else {
+ return (int) (Math.ceil(Math.log(amountOfImportedSymbolss + amountOfNewSymbolss) / Math.log(2)));
+ }
+ }
+
+ /**
+ * 6.5.8.2.4 - Setting SBSYMS
+ *
+ * @throws IOException
+ * @throws InvalidHeaderValueException
+ * @throws IntegerMaxValueException
+ */
+ private final void setSymbolsArray() throws IOException, InvalidHeaderValueException, IntegerMaxValueException {
+ if (importSymbols == null) {
+ retrieveImportSymbols();
+ }
+
+ if (sbSymbols == null) {
+ sbSymbols = new ArrayList<Bitmap>();
+ sbSymbols.addAll(importSymbols);
+ }
+ }
+
+ /**
+ * Concatenates symbols from all referred-to segments.
+ *
+ * @throws IOException
+ * @throws InvalidHeaderValueException
+ * @throws IntegerMaxValueException
+ */
+ private void retrieveImportSymbols() throws IOException, InvalidHeaderValueException, IntegerMaxValueException {
+ importSymbols = new ArrayList<Bitmap>();
+ for (final SegmentHeader referredToSegmentHeader : segmentHeader.getRtSegments()) {
+ if (referredToSegmentHeader.getSegmentType() == 0) {
+ final SymbolDictionary sd = (SymbolDictionary) referredToSegmentHeader.getSegmentData();
+ importSymbols.addAll(sd.getDictionary());
+ amountOfImportedSymbolss += sd.amountOfExportSymbolss;
+ }
+ }
+ }
+
+ private HuffmanTable getUserTable(final int tablePosition) throws InvalidHeaderValueException, IOException {
+ int tableCounter = 0;
+
+ for (final SegmentHeader referredToSegmentHeader : segmentHeader.getRtSegments()) {
+ if (referredToSegmentHeader.getSegmentType() == 53) {
+ if (tableCounter == tablePosition) {
+ final Table t = (Table) referredToSegmentHeader.getSegmentData();
+ return new EncodedTable(t);
+ } else {
+ tableCounter++;
+ }
+ }
+ }
+ return null;
+ }
+
+ public void init(final SegmentHeader header, final SubInputStream sis) throws InvalidHeaderValueException,
+ IntegerMaxValueException, IOException {
+ this.subInputStream = sis;
+ this.segmentHeader = header;
+ parseHeader();
+ }
+}