All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.jpedal.io.filter.ccitt.CCITT1D Maven / Gradle / Ivy

The newest version!
/*
 * ===========================================
 * Java Pdf Extraction Decoding Access Library
 * ===========================================
 *
 * Project Info:  http://www.idrsolutions.com
 * Help section for developers at http://www.idrsolutions.com/java-pdf-library-support/
 *
 * (C) Copyright 1997-2013, IDRsolutions and Contributors.
 *
 * 	This file is part of JPedal
 *
     This library is free software; you can redistribute it and/or
    modify it under the terms of the GNU Lesser General Public
    License as published by the Free Software Foundation; either
    version 2.1 of the License, or (at your option) any later version.

    This library is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
    Lesser General Public License for more details.

    You should have received a copy of the GNU Lesser General Public
    License along with this library; if not, write to the Free Software
    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA


 *
 * ---------------
 * CCITT1D.java
 * ---------------
 */
package org.jpedal.io.filter.ccitt;

import java.util.BitSet;

import org.jpedal.objects.raw.PdfDictionary;
import org.jpedal.objects.raw.PdfObject;

/**
 * implement 1D CCITT decoding
 */
public class CCITT1D implements CCITTDecoder {

	/**
	 * values set in PdfObject
	 */
	boolean BlackIs1 = false, isByteAligned = false;
	int columns = 1728;

	byte[] data;

	int bitReached;

	private final static int EOL = -1;

	private static final boolean debug = false;

	boolean isWhite = true;
	private boolean isTerminating = false;

	private boolean isEndOfLine = false;
	private boolean EOS = false;

	private int cRTC = 0;
	int width = 0;
	int height = 0;
	private int line = 0;

	BitSet out;
	private BitSet inputBits;
	int outPtr = 0;

	private int bytesNeeded = 0;
	int scanlineStride;
	private int inputBitCount = 0;

	// ---------- BLACK --------------

	final static private int[][][] b = new int[][][] { { { 3, 2, 0 }, { 2, 3, 0 } },
			{ { 2, 1, 0 }, { 3, 4, 0 } },

			{ // b4
			{ 3, 5, 0 }, { 2, 6, 0 } },

			// b5
			{ { 3, 7, 0 } },
			// b6
			{ { 5, 8, 0 }, { 4, 9, 0 } },
			// b7
			{ { 4, 10, 0 }, { 5, 11, 0 }, { 7, 12, 0 } },
			// b8
			{ { 4, 13, 0 }, { 7, 14, 0 } },

			// b9
			{ { 24, 15, 0 } },

			// b10
			{ { 55, 0, 0 }, { 23, 16, 0 }, { 24, 17, 0 }, { 8, 18, 0 }, { 15, 64, 1 } },

			// b11
			{ { 103, 19, 0 }, { 104, 20, 0 }, { 108, 21, 0 }, { 55, 22, 0 }, { 40, 23, 0 }, { 23, 24, 0 }, { 24, 25, 0 }, { 8, 1792, 1 },
					{ 12, 1856, 1 }, { 13, 1920, 1 } },

			// b12
			{ { 202, 26, 0 }, { 203, 27, 0 }, { 204, 28, 0 }, { 205, 29, 0 }, { 104, 30, 0 }, { 105, 31, 0 }, { 106, 32, 0 }, { 107, 33, 0 },
					{ 210, 34, 0 }, { 211, 35, 0 }, { 212, 36, 0 }, { 213, 37, 0 }, { 214, 38, 0 }, { 215, 39, 0 }, { 108, 40, 0 }, { 109, 41, 0 },
					{ 218, 42, 0 }, { 219, 43, 0 }, { 84, 44, 0 }, { 85, 45, 0 }, { 86, 46, 0 }, { 87, 47, 0 }, { 100, 48, 0 }, { 101, 49, 0 },
					{ 82, 50, 0 }, { 83, 51, 0 }, { 36, 52, 0 }, { 55, 53, 0 }, { 56, 54, 0 }, { 39, 55, 0 }, { 40, 56, 0 }, { 88, 57, 0 },
					{ 89, 58, 0 }, { 43, 59, 0 }, { 44, 60, 0 }, { 90, 61, 0 }, { 102, 62, 0 }, { 103, 63, 0 }, { 200, 128, 1 }, { 201, 192, 1 },
					{ 91, 256, 1 }, { 51, 320, 1 }, { 52, 384, 1 }, { 53, 448, 1 }, { 1, EOL, 1 }, { 18, 1984, 1 }, { 19, 2048, 1 }, { 20, 2112, 1 },
					{ 21, 2176, 1 }, { 22, 2240, 1 }, { 23, 2304, 1 }, { 28, 2368, 1 }, { 29, 2432, 1 }, { 30, 2496, 1 }, { 31, 2560, 1 } },

			// b13
			{ { 108, 512, 1 }, { 109, 576, 1 }, { 74, 640, 1 }, { 75, 704, 1 }, { 76, 768, 1 }, { 77, 832, 1 }, { 114, 896, 1 }, { 115, 960, 1 },
					{ 116, 1024, 1 }, { 117, 1088, 1 }, { 118, 1152, 1 }, { 119, 1216, 1 }, { 82, 1280, 1 }, { 83, 1344, 1 }, { 84, 1408, 1 },
					{ 85, 1472, 1 }, { 90, 1536, 1 }, { 91, 1600, 1 }, { 100, 1664, 1 }, { 101, 1728, 1 } } };

	// ---------- WHITE --------------

	final static private int[][][] w = new int[][][] {
			{ { 7, 2, 0 }, { 8, 3, 0 }, { 11, 4, 0 }, { 12, 5, 0 }, { 14, 6, 0 }, { 15, 7, 0 } },
			// b5
			{ { 19, 8, 0 }, { 20, 9, 0 }, { 7, 10, 0 }, { 8, 11, 0 }, { 27, 64, 1 }, { 18, 128, 1 } },
			// b6
			{ { 7, 1, 0 }, { 8, 12, 0 }, { 3, 13, 0 }, { 52, 14, 0 }, { 53, 15, 0 }, { 42, 16, 0 }, { 43, 17, 0 }, { 23, 192, 1 }, { 24, 1664, 1 } },
			// w7
			{ { 39, 18, 0 }, { 12, 19, 0 }, { 8, 20, 0 }, { 23, 21, 0 }, { 3, 22, 0 }, { 4, 23, 0 }, { 40, 24, 0 }, { 43, 25, 0 }, { 19, 26, 0 },
					{ 36, 27, 0 }, { 24, 28, 0 }, { 55, 256, 1 } },

			// w8
			{ { 53, 0, 0 }, { 2, 29, 0 }, { 3, 30, 0 }, { 26, 31, 0 }, { 27, 32, 0 }, { 18, 33, 0 }, { 19, 34, 0 }, { 20, 35, 0 }, { 21, 36, 0 },
					{ 22, 37, 0 }, { 23, 38, 0 }, { 40, 39, 0 }, { 41, 40, 0 }, { 42, 41, 0 }, { 43, 42, 0 }, { 44, 43, 0 }, { 45, 44, 0 },
					{ 4, 45, 0 }, { 5, 46, 0 }, { 10, 47, 0 }, { 11, 48, 0 }, { 82, 49, 0 }, { 83, 50, 0 }, { 84, 51, 0 }, { 85, 52, 0 },
					{ 36, 53, 0 }, { 37, 54, 0 }, { 88, 55, 0 }, { 89, 56, 0 }, { 90, 57, 0 }, { 91, 58, 0 }, { 74, 59, 0 }, { 75, 60, 0 },
					{ 50, 61, 0 }, { 51, 62, 0 }, { 52, 63, 0 }, { 54, 320, 1 }, { 55, 384, 1 }, { 100, 448, 1 }, { 101, 512, 1 }, { 104, 576, 1 },
					{ 103, 640, 1 } },

			// w9
			{ { 204, 704, 1 }, { 205, 768, 1 }, { 210, 832, 1 }, { 211, 896, 1 }, { 212, 960, 1 }, { 213, 1024, 1 }, { 214, 1088, 1 },
					{ 215, 1152, 1 }, { 216, 1216, 1 }, { 217, 1280, 1 }, { 218, 1344, 1 }, { 219, 1408, 1 }, { 152, 1472, 1 }, { 153, 1536, 1 },
					{ 154, 1600, 1 }, { 155, 1728, 1 } },

			// w10
			{},

			// w11
			{ { 8, 1792, 1 }, { 12, 1856, 1 }, { 13, 1920, 1 } },

			// w12
			{ { 1, EOL, 1 }, { 18, 1984, 1 }, { 19, 2048, 1 }, { 20, 2112, 1 }, { 21, 2176, 1 }, { 22, 2240, 1 }, { 23, 2304, 1 }, { 28, 2368, 1 },
					{ 29, 2432, 1 }, { 30, 2496, 1 }, { 31, 2560, 1 } } };

	public CCITT1D(byte[] rawData, int width, int height, PdfObject DecodeParms) {

		this.data = rawData;
		this.bitReached = 0;

		this.columns = width; // default if not set (in theory should be the same if set)

		// and any values from PDFobject
		if (DecodeParms != null) {

			this.BlackIs1 = DecodeParms.getBoolean(PdfDictionary.BlackIs1);

			int columnsSet = DecodeParms.getInt(PdfDictionary.Columns);
			if (columnsSet != -1) this.columns = columnsSet;

			int rowsSet = DecodeParms.getInt(PdfDictionary.Rows);
			if (rowsSet > 0) // allow for value set to 0 which is impossible! (see abacus/aba_Dossier)
			height = rowsSet;

			this.isByteAligned = DecodeParms.getBoolean(PdfDictionary.EncodedByteAlign);

			if (debug) System.out.println("BlackIs1=" + this.BlackIs1 + "\ncolumnsSet=" + columnsSet + "\nisByteAligned=" + this.isByteAligned
					+ "\nrowsSet=" + rowsSet);
		}

		// and other values which might use defaults set from PdfObject
		this.width = this.columns;
		this.height = height;

		this.scanlineStride = (this.columns + 7) >> 3;

		this.bytesNeeded = (height * this.scanlineStride);

		this.out = new BitSet(this.bytesNeeded << 3);

		// number of bits in raw compressed data
		this.inputBitCount = this.data.length << 3;

		// raw data bits to read for codewords
		this.inputBits = fromByteArray(this.data, this.inputBitCount);
	}

	CCITT1D() {}

	@Override
	public byte[] decode() {

		// Commented out as breaks dotloop file
		// moveToEOLMarker();

		// read all the tokens and find sub values until End Of Stream
		decode1DRun();

		// put output bits together into byte[] we return
		// put it all together
		byte[] buffer = createOutputFromBitset();

		// by default blackIs1 is false so black pixels do not need to be set
		// invert image if needed -
		// ultimately will be quicker to add into decode
		// k==0 seems to be reverse of others
		if (this.BlackIs1) {
			for (int i = 0; i < buffer.length; i++)
				buffer[i] = (byte) (255 - buffer[i]); // invert all bytes
		}

		return buffer;
	}

	byte[] createOutputFromBitset() {

		byte[] output = new byte[this.bytesNeeded];

		// assemble all tokens into a decompressed output data block
		int bytePtr = 0, bitPtr = 7, mask;
		byte entry = 0;

		for (int j = 0; j < this.outPtr; j++) {

			if (this.out.get(j)) {
				mask = 1 << bitPtr;

				entry |= mask;
				bitPtr--;
			}
			else {
				bitPtr--;
			}

			if (((j + 1) % (this.width)) == 0 && j != 0) {
				bitPtr = -1;
			}

			if (bitPtr < 0 && bytePtr < output.length) {
				output[bytePtr] = entry;
				bytePtr++;
				bitPtr = 7;
				entry = 0;
			}
		}

		return output;
	}

	/**
	 * work through a 1D block reading code words and creating uncompressed data
	 */
	private void decode1DRun() {

		while (!this.EOS) { // repeat until we hit the end of the data

			// set flags for codeword
			if (this.isTerminating) {

				this.isTerminating = false;
				this.isWhite = !this.isWhite;

				if (this.isEndOfLine) {
					this.isEndOfLine = false;
					this.isWhite = true;
				}
			}

			// remember state as getPixelCount will alter
			boolean pixelIsWhite = this.isWhite;

			// decode the next codeword and get how many pixels written out
			int pixelCount = getCodeWord();

			// set bits
			if (pixelCount > 0) {
				if (pixelIsWhite) {
					this.out.set(this.outPtr, (this.outPtr + pixelCount), true);
				}
				this.outPtr = this.outPtr + pixelCount;
			}
		}
	}

	private int getCodeWord() {

		int pixelCount = 0, itemFound = -1, maskAdj = 0;

		/**
		 * look for valid value starting at 2 bits
		 * 
		 * starting with 2 bits (4 for white) look at next pixels until we find a valid value for next codeword
		 */
		int startBitLength = 2, endBit = 14, code = 0, bits = 0;
		if (this.isWhite) { // no values for 2,3 in this case
			startBitLength = 4;
			endBit = 13;
		}

		// loop to find the valid keys by checking next bit value against tables
		for (int bitLength = startBitLength; bitLength < endBit; bitLength++) {
			code = get1DBits(bitLength, true) & 255; // next n bits as possible key
			itemFound = checkTables(code, bitLength, this.isWhite); // see if it is a key

			// if it is we exit
			if (itemFound != -1) {
				bits = bitLength;
				bitLength = endBit;
			}
			else
				if (bitLength == 8) {
					maskAdj++;
					this.bitReached++;
				}
		}

		// we have a match so process the codeword and move on pointer
		if (itemFound != -1) {
			// update count of bits
			this.bitReached = this.bitReached + bits - maskAdj;

			pixelCount = processCodeWord(itemFound, code, bits);
		}
		else
			if (this.bitReached > this.inputBitCount) {
				this.EOS = true;
			}

		return pixelCount;
	}

	private int processCodeWord(int itemFound, int code, int bits) {

		int pixelCount;
		boolean isT;

		// values in the table
		if (this.isWhite) {
			pixelCount = w[bits - 4][itemFound][1];
			isT = w[bits - 4][itemFound][2] == 0;
		}
		else {
			pixelCount = b[bits - 2][itemFound][1];
			isT = b[bits - 2][itemFound][2] == 0;
		}

		if (isT) this.isTerminating = true;

		if (pixelCount == -1) {
			if (this.line != 0) {
				pixelCount = this.width - this.line;
			}

			this.line = 0;
			this.isWhite = true;
			this.isTerminating = false;
		}

		if (pixelCount != -1) {
			this.line = this.line + pixelCount;

			if (this.line == this.width) {
				if (isT) {
					this.line = 0;
					this.isEndOfLine = true;
				}

			}
			else
				if (this.line > this.width) {
					this.line = 0;
					this.isEndOfLine = true;
				}
		}

		if (bits == 12 && code == 1) {
			this.cRTC++;
			if (this.cRTC == 6) {
				this.EOS = true;
			}
		}
		else {
			this.cRTC = 0;
		}

		if (this.cRTC != 6 && this.isEndOfLine && this.isByteAligned) {

			// get bits over 8 and align to byte boundary
			int iPart = (this.bitReached) % 8;
			int iDrop = 8 - (iPart);
			if (iPart > 0) {
				this.bitReached = this.bitReached + iDrop;
			}
		}

		return pixelCount;
	}

	// 2D version
	int get1DBits(int bitsToGet) {
		return get1DBits(bitsToGet, false);
	}

	private int get1DBits(int bitsToGet, boolean is1D) {

		int tmp = 0;

		int maskAdj = 0;
		if (is1D && bitsToGet > 8) maskAdj++;

		int mask;
		for (int y = 0; y < bitsToGet; y++) {
			if (this.inputBits.get(y + this.bitReached)) {
				mask = 1 << (bitsToGet - y - 1 - maskAdj);

				tmp |= mask;
			}
		}

		return tmp;
	}

	private static int checkTables(int possCode, int bitLength, boolean isWhite) {

		int itemFound = -1;
		int[][] table;

		if (isWhite) {
			table = w[bitLength - 4];
		}
		else {
			table = b[bitLength - 2];
		}

		int size = table.length;

		for (int z = 0; z < size; z++) {
			if (possCode == table[z][0]) {
				itemFound = z;
				z = size;

			}
		}
		return itemFound;
	}

	private static BitSet fromByteArray(byte[] bytes, int bitsNeeded) {

		int bitSetPtr = 0, value;
		byte tmp;

		BitSet bits = new BitSet(bitsNeeded);
		for (byte aByte : bytes) {
			tmp = aByte;
			for (int z = 7; z >= 0; z--) {

				value = (tmp & (1 << z));

				if (value >= 1) bits.set(bitSetPtr, true);

				bitSetPtr++;
			}
		}

		return bits;
	}

}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy