Please wait. This can take some minutes ...
Many resources are needed to download a project. Please understand that we have to compensate our server costs. Thank you in advance.
Project price only 1 $
You can buy this project and download/modify it how often you want.
org.jpedal.io.filter.ccitt.CCITT1D Maven / Gradle / Ivy
/*
* ===========================================
* Java Pdf Extraction Decoding Access Library
* ===========================================
*
* Project Info: http://www.idrsolutions.com
* Help section for developers at http://www.idrsolutions.com/support/
*
* (C) Copyright 1997-2017 IDRsolutions and Contributors.
*
* This file is part of JPedal/JPDF2HTML5
*
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;
import org.jpedal.utils.LogWriter;
/**
* implement 1D CCITT decoding
*/
public class CCITT1D implements CCITTDecoder {
/**
* values set in PdfObject
*/
boolean BlackIs1, isByteAligned;
int columns = 1728;
byte[] data;
int bitReached;
private static final int EOL = -1;
private static final boolean debug = false;
boolean isWhite = true;
private boolean isTerminating;
private boolean isEndOfLine;
private boolean EOS;
private int cRTC;
int width;
int height;
private int line;
BitSet out;
private BitSet inputBits;
int outPtr;
private int bytesNeeded;
int scanlineStride;
private int inputBitCount;
// ---------- BLACK --------------
private static final int[][][] b = {
{{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 --------------
private static final int[][][] w = {{
{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(final byte[] rawData, final int width, int height, final PdfObject DecodeParms) {
this.data = rawData;
this.bitReached = 0;
columns = width; //default if not set (in theory should be the same if set)
//and any values from PDFobject
if (DecodeParms != null) {
BlackIs1 = DecodeParms.getBoolean(PdfDictionary.BlackIs1);
final int columnsSet = DecodeParms.getInt(PdfDictionary.Columns);
if (columnsSet != -1) {
columns = columnsSet;
}
final int rowsSet = DecodeParms.getInt(PdfDictionary.Rows);
if (rowsSet > 0) //allow for value set to 0 which is impossible! (see abacus/aba_Dossier)
{
height = rowsSet;
}
isByteAligned = DecodeParms.getBoolean(PdfDictionary.EncodedByteAlign);
if (debug) {
System.out.println("BlackIs1=" + BlackIs1 + "\ncolumnsSet=" + columnsSet + "\nisByteAligned=" + isByteAligned + "\nrowsSet=" + rowsSet);
}
}
//and other values which might use defaults set from PdfObject
this.width = columns;
this.height = height;
scanlineStride = (columns + 7) >> 3;
bytesNeeded = (height * scanlineStride);
out = new BitSet(bytesNeeded << 3);
//number of bits in raw compressed data
inputBitCount = data.length << 3;
//raw data bits to read for codewords
inputBits = fromByteArray(data, inputBitCount);
}
CCITT1D() {
}
@Override
public byte[] decode() {
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
final 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 (BlackIs1) {
for (int i = 0; i < buffer.length; i++) {
buffer[i] = (byte) (255 - buffer[i]); //invert all bytes
}
}
return buffer;
}
byte[] createOutputFromBitset() {
final byte[] output = new byte[bytesNeeded];
//assemble all tokens into a decompressed output data block
int bytePtr = 0, bitPtr = 7, mask;
byte entry = 0;
for (int j = 0; j < outPtr; j++) {
if (out.get(j)) {
mask = 1 << bitPtr;
entry |= mask;
bitPtr--;
} else {
bitPtr--;
}
if (((j + 1) % (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 (!EOS) { //repeat until we hit the end of the data
//set flags for codeword
if (isTerminating) {
isTerminating = false;
isWhite = !isWhite;
if (isEndOfLine) {
isEndOfLine = false;
isWhite = true;
}
}
//remember state as getPixelCount will alter
final boolean pixelIsWhite = isWhite;
//decode the next codeword and get how many pixels written out
final int pixelCount = getCodeWord();
//set bits
if (pixelCount > 0) {
if (pixelIsWhite) {
out.set(outPtr, (outPtr + pixelCount), true);
}
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 (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, isWhite); //see if it is a key
//if it is we exit
if (itemFound != -1) {
bits = bitLength;
bitLength = endBit;
} else if (bitLength == 8) {
maskAdj++;
bitReached++;
}
}
//we have a match so process the codeword and move on pointer
if (itemFound != -1) {
//update count of bits
bitReached = bitReached + bits - maskAdj;
pixelCount = processCodeWord(itemFound, code, bits);
} else if (bitReached > inputBitCount) {
EOS = true;
}
return pixelCount;
}
private int processCodeWord(final int itemFound, final int code, final int bits) {
final int pixelCount;
final boolean isT;
//values in the table
if (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) {
isTerminating = true;
}
if (pixelCount == -1) {
if (line != 0) {
LogWriter.writeLog("EOF marker encountered but not EOL yet!");
// pixelCount = width - line;
}
line = 0;
isWhite = true;
isTerminating = false;
}
if (pixelCount != -1) {
line += pixelCount;
if (line == width) {
if (isT) {
line = 0;
isEndOfLine = true;
}
} else if (line > width) {
line = 0;
isEndOfLine = true;
}
}
if (bits == 12 && code == 1) {
cRTC++;
if (cRTC == 6) {
EOS = true;
}
} else {
cRTC = 0;
}
if (cRTC != 6 && isEndOfLine && isByteAligned) {
//get bits over 8 and align to byte boundary
final int iPart = (bitReached) % 8;
final int iDrop = 8 - (iPart);
if (iPart > 0) {
bitReached += iDrop;
}
}
return pixelCount;
}
//2D version
int get1DBits(final int bitsToGet) {
return get1DBits(bitsToGet, false);
}
private int get1DBits(final int bitsToGet, final boolean is1D) {
int tmp = 0;
int maskAdj = 0;
if (is1D && bitsToGet > 8) {
maskAdj++;
}
int mask;
for (int y = 0; y < bitsToGet; y++) {
if (inputBits.get(y + bitReached)) {
mask = 1 << (bitsToGet - y - 1 - maskAdj);
tmp |= mask;
}
}
return tmp;
}
private static int checkTables(final int possCode, final int bitLength, final boolean isWhite) {
int itemFound = -1;
final int[][] table;
if (isWhite) {
table = w[bitLength - 4];
} else {
table = b[bitLength - 2];
}
final 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(final byte[] bytes, final int bitsNeeded) {
int bitSetPtr = 0, value;
byte tmp;
final BitSet bits = new BitSet(bitsNeeded);
for (final 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;
}
private void moveToEOLMarker() {
boolean isEOL = false, bit;
int i = 0;
while (!isEOL) {
isEOL = true;
for (i = 0; i < 12; i++) {
bit = inputBits.get(i + bitReached);
if (i == 11) {
if (!bit) {
isEOL = false;
}
} else {
if (bit) {
isEOL = false;
}
}
}
bitReached++;
// if EOL not found in the first 10 bits assume that
// there is no EOL at the start od the line and start
// at 0
if (bitReached > 26) {
bitReached = 0;
return;
}
}
bitReached = bitReached + i - 1;
}
}