PDF格式分析(三)语法之Filter

在PDF中,为了让文件变的更小,通常的做法是,将stream对象进行压缩,因为stream对象的数据块比较大,所以,是重点关注的地方。
从PDF1.5开始往后的版本,支持了Object stream,可以把多个对象(非stream对象)放到同一个stream对象中,并进行压缩,达到减少文件大小的效果。
Filter大部分的类型是压缩,当然有一个是特殊的类型”Crypt“,这个类型是对该stream进行单独加密。
一个stream对象,可以单次压缩,也可以多次压缩。/Filter [/ASCII85Decode /LZWDecode],表示被描述的这个stream对象进行了ASCII85Decode和LZWDecode两次压缩,因此,对该stream进行解压缩的时候,也要按照反顺序解,分别压缩。
PDF支持的filter可以分为三大类:
一、ASCII filters(ASCIIHexDecode、ASCII85Decode),这种编码类型可以将8位二进制数据编码成ASCII文本。注意:这种类型的filter不能用在被加密的PDF文档中。
二、加密filters(LZWDecode、FlateDecode、RunLengthDecode、CCITTFaxDecode、JBIG2Decode、DCTDecode,JPXDecode),这些压缩类型包括无损压缩和有损压缩。
三、加密filter(Crypt)
下面咱们来详细介绍每一种filter的用法和作用:
ASCIIHexDecode
将数据编码成ASCII十六进制形式,通常就是将二进制数据(如图片数据),编码成7位ASCII字符串。
算法如下(itext):

    /** Decodes a stream that has the ASCIIHexDecode filter.
     * @param in the input data
     * @return the decoded data
     */
    public static byte[] ASCIIHexDecode(final byte in[]) {
        ByteArrayOutputStream out = new ByteArrayOutputStream();
        boolean first = true;
        int n1 = 0;
        for (int k = 0; k < in.length; ++k) {
            int ch = in[k] & 0xff;
            if (ch == '>')
                break;
            if (PRTokeniser.isWhitespace(ch))
                continue;
            int n = PRTokeniser.getHex(ch);
            if (n == -1)
                throw new RuntimeException(MessageLocalization.getComposedMessage("illegal.character.in.asciihexdecode"));
            if (first)
                n1 = n;
            else
                out.write((byte)((n1 << 4) + n));
            first = !first;
        }
        if (!first)
            out.write((byte)(n1 << 4));
        return out.toByteArray();
    }

ASCII85Decode
作用与ASCIIHexDecode差不多,通常,ASCII85Decode建议优先使用,因为ASCII85Decode拥有更高的压缩比。ASCIIHexDecode的压缩比为4:5,ASCII85Decode的压缩比为1:2。
算法如下(itext):

    /** Decodes a stream that has the ASCII85Decode filter.
     * @param in the input data
     * @return the decoded data
     */
    public static byte[] ASCII85Decode(final byte in[]) {
        ByteArrayOutputStream out = new ByteArrayOutputStream();
        int state = 0;
        int chn[] = new int[5];
        for (int k = 0; k < in.length; ++k) {
            int ch = in[k] & 0xff;
            if (ch == '~')
                break;
            if (PRTokeniser.isWhitespace(ch))
                continue;
            if (ch == 'z' && state == 0) {
                out.write(0);
                out.write(0);
                out.write(0);
                out.write(0);
                continue;
            }
            if (ch < '!' || ch > 'u')
                throw new RuntimeException(MessageLocalization.getComposedMessage("illegal.character.in.ascii85decode"));
            chn[state] = ch - '!';
            ++state;
            if (state == 5) {
                state = 0;
                int r = 0;
                for (int j = 0; j < 5; ++j)
                    r = r * 85 + chn[j];
                out.write((byte)(r >> 24));
                out.write((byte)(r >> 16));
                out.write((byte)(r >> 8));
                out.write((byte)r);
            }
        }
        int r = 0;
        // We'll ignore the next two lines for the sake of perpetuating broken PDFs
//        if (state == 1)
//            throw new RuntimeException(MessageLocalization.getComposedMessage("illegal.length.in.ascii85decode"));
        if (state == 2) {
            r = chn[0] * 85 * 85 * 85 * 85 + chn[1] * 85 * 85 * 85 + 85 * 85 * 85  + 85 * 85 + 85;
            out.write((byte)(r >> 24));
        }
        else if (state == 3) {
            r = chn[0] * 85 * 85 * 85 * 85 + chn[1] * 85 * 85 * 85  + chn[2] * 85 * 85 + 85 * 85 + 85;
            out.write((byte)(r >> 24));
            out.write((byte)(r >> 16));
        }
        else if (state == 4) {
            r = chn[0] * 85 * 85 * 85 * 85 + chn[1] * 85 * 85 * 85  + chn[2] * 85 * 85  + chn[3] * 85 + 85;
            out.write((byte)(r >> 24));
            out.write((byte)(r >> 16));
            out.write((byte)(r >> 8));
        }
        return out.toByteArray();
    }

LZWDecode
LZW(Lempel-Ziv-Welch)是一种可变长度的自适应压缩方法,作为TIFF(Tag Image File Format)标准的压缩方式之一。该压缩方式可以压缩二进制数据和ASCII文本,最终生成的数据是二进制的。LZW具体压缩原理就不在这里赘述了,百度很容易找到。
算法如下(itext):

    /**
     * Handles LZWDECODE filter
     */
    private static class Filter_LZWDECODE implements FilterHandler{
        public byte[] decode(byte[] b, PdfName filterName, PdfObject decodeParams, PdfDictionary streamDictionary) throws IOException {
            b = PdfReader.LZWDecode(b);
            b = PdfReader.decodePredictor(b, decodeParams);
            return b;
        }
    }
/**
 * A class for performing LZW decoding.
 *
 *
 */
public class LZWDecoder {

    byte stringTable[][];
    byte data[] = null;
    OutputStream uncompData;
    int tableIndex, bitsToGet = 9;
    int bytePointer, bitPointer;
    int nextData = 0;
    int nextBits = 0;

    int andTable[] = {
        511,
        1023,
        2047,
        4095
    };

    public LZWDecoder() {
    }

    /**
     * Method to decode LZW compressed data.
     *
     * @param data            The compressed data.
     * @param uncompData      Array to return the uncompressed data in.
     */
    public void decode(byte data[], OutputStream uncompData) {

        if(data[0] == (byte)0x00 && data[1] == (byte)0x01) {
            throw new RuntimeException(MessageLocalization.getComposedMessage("lzw.flavour.not.supported"));
        }

        initializeStringTable();

        this.data = data;
        this.uncompData = uncompData;

        // Initialize pointers
        bytePointer = 0;
        bitPointer = 0;

        nextData = 0;
        nextBits = 0;

        int code, oldCode = 0;
        byte string[];

        while ((code = getNextCode()) != 257) {

            if (code == 256) {

                initializeStringTable();
                code = getNextCode();

                if (code == 257) {
                    break;
                }

                writeString(stringTable[code]);
                oldCode = code;

            } else {

                if (code < tableIndex) {

                    string = stringTable[code];

                    writeString(string);
                    addStringToTable(stringTable[oldCode], string[0]);
                    oldCode = code;

                } else {

                    string = stringTable[oldCode];
                    string = composeString(string, string[0]);
                    writeString(string);
                    addStringToTable(string);
                    oldCode = code;
                }
            }
        }
    }


    /**
     * Initialize the string table.
     */
    public void initializeStringTable() {

        stringTable = new byte[8192][];

        for (int i=0; i<256; i++) {
            stringTable[i] = new byte[1];
            stringTable[i][0] = (byte)i;
        }

        tableIndex = 258;
        bitsToGet = 9;
    }

    /**
     * Write out the string just uncompressed.
     */
    public void writeString(byte string[]) {
        try {
            uncompData.write(string);
        }
        catch (IOException e) {
            throw new ExceptionConverter(e);
        }
    }

    /**
     * Add a new string to the string table.
     */
    public void addStringToTable(byte oldString[], byte newString) {
        int length = oldString.length;
        byte string[] = new byte[length + 1];
        System.arraycopy(oldString, 0, string, 0, length);
        string[length] = newString;

        // Add this new String to the table
        stringTable[tableIndex++] = string;

        if (tableIndex == 511) {
            bitsToGet = 10;
        } else if (tableIndex == 1023) {
            bitsToGet = 11;
        } else if (tableIndex == 2047) {
            bitsToGet = 12;
        }
    }

    /**
     * Add a new string to the string table.
     */
    public void addStringToTable(byte string[]) {

        // Add this new String to the table
        stringTable[tableIndex++] = string;

        if (tableIndex == 511) {
            bitsToGet = 10;
        } else if (tableIndex == 1023) {
            bitsToGet = 11;
        } else if (tableIndex == 2047) {
            bitsToGet = 12;
        }
    }

    /**
     * Append <code>newString</code> to the end of <code>oldString</code>.
     */
    public byte[] composeString(byte oldString[], byte newString) {
        int length = oldString.length;
        byte string[] = new byte[length + 1];
        System.arraycopy(oldString, 0, string, 0, length);
        string[length] = newString;

        return string;
    }

    // Returns the next 9, 10, 11 or 12 bits
    public int getNextCode() {
        // Attempt to get the next code. The exception is caught to make
        // this robust to cases wherein the EndOfInformation code has been
        // omitted from a strip. Examples of such cases have been observed
        // in practice.
        try {
            nextData = (nextData << 8) | (data[bytePointer++] & 0xff);
            nextBits += 8;

            if (nextBits < bitsToGet) {
                nextData = (nextData << 8) | (data[bytePointer++] & 0xff);
                nextBits += 8;
            }

            int code =
            (nextData >> (nextBits - bitsToGet)) & andTable[bitsToGet-9];
            nextBits -= bitsToGet;

            return code;
        } catch(ArrayIndexOutOfBoundsException e) {
            // Strip not terminated as expected: return EndOfInformation code.
            return 257;
        }
    }
}

FlateDecode
该方法是基于 zlib/deflate压缩方法,是一种可变长度Lopel-ZIV自适应压缩方法,与自适应霍夫曼编码级联,被定义在Internet RFCS 1950、ZLIB压缩数据格式规范和1951,DEFLATE压缩数据格式规范中。
该压缩方式可以压缩二进制数据和ASCII文本,最终生成的数据是二进制的。
算法如下(itext):

private static class Filter_FLATEDECODE implements FilterHandler{
    public byte[] decode(byte[] b, PdfName filterName, PdfObject decodeParams, PdfDictionary streamDictionary) throws IOException {
        b = PdfReader.FlateDecode(b);
        b = PdfReader.decodePredictor(b, decodeParams);
        return b;
    }
}
/** Decodes a stream that has the FlateDecode filter.
 * @param in the input data
 * @return the decoded data
 */
public static byte[] FlateDecode(final byte in[]) {
    byte b[] = FlateDecode(in, true);
    if (b == null)
        return FlateDecode(in, false);
    return b;
}

/** A helper to FlateDecode.
 * @param in the input data
 * @param strict <CODE>true</CODE> to read a correct stream. <CODE>false</CODE>
 * to try to read a corrupted stream
 * @return the decoded data
 */
public static byte[] FlateDecode(final byte in[], final boolean strict) {
    ByteArrayInputStream stream = new ByteArrayInputStream(in);
    InflaterInputStream zip = new InflaterInputStream(stream);
    ByteArrayOutputStream out = new ByteArrayOutputStream();
    byte b[] = new byte[strict ? 4092 : 1];
    try {
        int n;
        while ((n = zip.read(b)) >= 0) {
            out.write(b, 0, n);
        }
        zip.close();
        out.close();
        return out.toByteArray();
    }
    catch (Exception e) {
        if (strict)
            return null;
        return out.toByteArray();
    }
    finally {
        try {
            zip.close();
        } catch (IOException ex) {
        }
        try {
            out.close();
        } catch (IOException ex) {
        }
    }
}

RunLengthDecode
行程长度压缩法即根据字符串的连续重复字符进行编码的一种方法.
算法如下(itext):

    /*
     * Handles RUNLENGTHDECODE filter
     */
    private static class Filter_RUNLENGTHDECODE implements FilterHandler{

        public byte[] decode(byte[] b, PdfName filterName, PdfObject decodeParams, PdfDictionary streamDictionary) throws IOException {
         // allocate the output buffer
            ByteArrayOutputStream baos = new ByteArrayOutputStream();
            byte dupCount = -1;
            for(int i = 0; i < b.length; i++){
                dupCount = b[i];
                if (dupCount == -128) break; // this is implicit end of data

                if (dupCount >= 0 && dupCount <= 127){
                    int bytesToCopy = dupCount+1;
                    baos.write(b, i, bytesToCopy);
                    i+=bytesToCopy;
                } else {
                    // make dupcount copies of the next byte
                    i++;
                    for(int j = 0; j < 1-(int)(dupCount);j++){ 
                        baos.write(b[i]);
                    }
                }
            }

            return baos.toByteArray();
        }
    }

CCITTFaxDecode
该压缩方式主要用于对图片数据进行压缩,采用group3或group4。CCITT压缩是为了对单色图(每个像素点用1bit表示)进行有效压缩而设计的。
算法如下(itext):

/**
 * Class that can decompress TIFF files.
 * @since 5.0.3
 */
public class TIFFFaxDecompressor {

    /**
     * The logical order of bits within a byte.
     * <pre>
     * 1 = MSB-to-LSB
     * 2 = LSB-to-MSB (flipped)
     * </pre>
     */
    protected int fillOrder;
    protected int compression;
    private int t4Options;
    private int t6Options;
    public int fails;
    // Variables set by T4Options
    /**
     * Uncompressed mode flag: 1 if uncompressed, 0 if not.
     */
    protected int uncompressedMode = 0;
    /**
     * EOL padding flag: 1 if fill bits have been added before an EOL such
     * that the EOL ends on a byte boundary, 0 otherwise.
     */
    protected int fillBits = 0;
    /**
     * Coding dimensionality: 1 for 2-dimensional, 0 for 1-dimensional.
     */
    protected int oneD;
    private byte[] data;
    private int bitPointer, bytePointer;
    // Output image buffer
    private byte[] buffer;
    private int w, h, bitsPerScanline;
    private int lineBitNum;
    // Data structures needed to store changing elements for the previous
    // and the current scanline
    private int changingElemSize = 0;
    private int prevChangingElems[];
    private int currChangingElems[];
    // Element at which to start search in getNextChangingElement
    private int lastChangingElement = 0;
    static int table1[] = {
        0x00, // 0 bits are left in first byte - SHOULD NOT HAPPEN
        0x01, // 1 bits are left in first byte
        0x03, // 2 bits are left in first byte
        0x07, // 3 bits are left in first byte
        0x0f, // 4 bits are left in first byte
        0x1f, // 5 bits are left in first byte
        0x3f, // 6 bits are left in first byte
        0x7f, // 7 bits are left in first byte
        0xff // 8 bits are left in first byte
    };
    static int table2[] = {
        0x00, // 0
        0x80, // 1
        0xc0, // 2
        0xe0, // 3
        0xf0, // 4
        0xf8, // 5
        0xfc, // 6
        0xfe, // 7
        0xff // 8
    };
    // Table to be used when fillOrder = 2, for flipping bytes.
    static byte flipTable[] = {
        0, -128, 64, -64, 32, -96, 96, -32,
        16, -112, 80, -48, 48, -80, 112, -16,
        8, -120, 72, -56, 40, -88, 104, -24,
        24, -104, 88, -40, 56, -72, 120, -8,
        4, -124, 68, -60, 36, -92, 100, -28,
        20, -108, 84, -44, 52, -76, 116, -12,
        12, -116, 76, -52, 44, -84, 108, -20,
        28, -100, 92, -36, 60, -68, 124, -4,
        2, -126, 66, -62, 34, -94, 98, -30,
        18, -110, 82, -46, 50, -78, 114, -14,
        10, -118, 74, -54, 42, -86, 106, -22,
        26, -102, 90, -38, 58, -70, 122, -6,
        6, -122, 70, -58, 38, -90, 102, -26,
        22, -106, 86, -42, 54, -74, 118, -10,
        14, -114, 78, -50, 46, -82, 110, -18,
        30, -98, 94, -34, 62, -66, 126, -2,
        1, -127, 65, -63, 33, -95, 97, -31,
        17, -111, 81, -47, 49, -79, 113, -15,
        9, -119, 73, -55, 41, -87, 105, -23,
        25, -103, 89, -39, 57, -71, 121, -7,
        5, -123, 69, -59, 37, -91, 101, -27,
        21, -107, 85, -43, 53, -75, 117, -11,
        13, -115, 77, -51, 45, -83, 109, -19,
        29, -99, 93, -35, 61, -67, 125, -3,
        3, -125, 67, -61, 35, -93, 99, -29,
        19, -109, 83, -45, 51, -77, 115, -13,
        11, -117, 75, -53, 43, -85, 107, -21,
        27, -101, 91, -37, 59, -69, 123, -5,
        7, -121, 71, -57, 39, -89, 103, -25,
        23, -105, 87, -41, 55, -73, 119, -9,
        15, -113, 79, -49, 47, -81, 111, -17,
        31, -97, 95, -33, 63, -65, 127, -1,};
    // The main 10 bit white runs lookup table
    static short white[] = {
        // 0 - 7
        6430, 6400, 6400, 6400, 3225, 3225, 3225, 3225,
        // 8 - 15
        944, 944, 944, 944, 976, 976, 976, 976,
        // 16 - 23
        1456, 1456, 1456, 1456, 1488, 1488, 1488, 1488,
        // 24 - 31
        718, 718, 718, 718, 718, 718, 718, 718,
        // 32 - 39
        750, 750, 750, 750, 750, 750, 750, 750,
        // 40 - 47
        1520, 1520, 1520, 1520, 1552, 1552, 1552, 1552,
        // 48 - 55
        428, 428, 428, 428, 428, 428, 428, 428,
        // 56 - 63
        428, 428, 428, 428, 428, 428, 428, 428,
        // 64 - 71
        654, 654, 654, 654, 654, 654, 654, 654,
        // 72 - 79
        1072, 1072, 1072, 1072, 1104, 1104, 1104, 1104,
        // 80 - 87
        1136, 1136, 1136, 1136, 1168, 1168, 1168, 1168,
        // 88 - 95
        1200, 1200, 1200, 1200, 1232, 1232, 1232, 1232,
        // 96 - 103
        622, 622, 622, 622, 622, 622, 622, 622,
        // 104 - 111
        1008, 1008, 1008, 1008, 1040, 1040, 1040, 1040,
        // 112 - 119
        44, 44, 44, 44, 44, 44, 44, 44,
        // 120 - 127
        44, 44, 44, 44, 44, 44, 44, 44,
        // 128 - 135
        396, 396, 396, 396, 396, 396, 396, 396,
        // 136 - 143
        396, 396, 396, 396, 396, 396, 396, 396,
        // 144 - 151
        1712, 1712, 1712, 1712, 1744, 1744, 1744, 1744,
        // 152 - 159
        846, 846, 846, 846, 846, 846, 846, 846,
        // 160 - 167
        1264, 1264, 1264, 1264, 1296, 1296, 1296, 1296,
        // 168 - 175
        1328, 1328, 1328, 1328, 1360, 1360, 1360, 1360,
        // 176 - 183
        1392, 1392, 1392, 1392, 1424, 1424, 1424, 1424,
        // 184 - 191
        686, 686, 686, 686, 686, 686, 686, 686,
        // 192 - 199
        910, 910, 910, 910, 910, 910, 910, 910,
        // 200 - 207
        1968, 1968, 1968, 1968, 2000, 2000, 2000, 2000,
        // 208 - 215
        2032, 2032, 2032, 2032, 16, 16, 16, 16,
        // 216 - 223
        10257, 10257, 10257, 10257, 12305, 12305, 12305, 12305,
        // 224 - 231
        330, 330, 330, 330, 330, 330, 330, 330,
        // 232 - 239
        330, 330, 330, 330, 330, 330, 330, 330,
        // 240 - 247
        330, 330, 330, 330, 330, 330, 330, 330,
        // 248 - 255
        330, 330, 330, 330, 330, 330, 330, 330,
        // 256 - 263
        362, 362, 362, 362, 362, 362, 362, 362,
        // 264 - 271
        362, 362, 362, 362, 362, 362, 362, 362,
        // 272 - 279
        362, 362, 362, 362, 362, 362, 362, 362,
        // 280 - 287
        362, 362, 362, 362, 362, 362, 362, 362,
        // 288 - 295
        878, 878, 878, 878, 878, 878, 878, 878,
        // 296 - 303
        1904, 1904, 1904, 1904, 1936, 1936, 1936, 1936,
        // 304 - 311
        -18413, -18413, -16365, -16365, -14317, -14317, -10221, -10221,
        // 312 - 319
        590, 590, 590, 590, 590, 590, 590, 590,
        // 320 - 327
        782, 782, 782, 782, 782, 782, 782, 782,
        // 328 - 335
        1584, 1584, 1584, 1584, 1616, 1616, 1616, 1616,
        // 336 - 343
        1648, 1648, 1648, 1648, 1680, 1680, 1680, 1680,
        // 344 - 351
        814, 814, 814, 814, 814, 814, 814, 814,
        // 352 - 359
        1776, 1776, 1776, 1776, 1808, 1808, 1808, 1808,
        // 360 - 367
        1840, 1840, 1840, 1840, 1872, 1872, 1872, 1872,
        // 368 - 375
        6157, 6157, 6157, 6157, 6157, 6157, 6157, 6157,
        // 376 - 383
        6157, 6157, 6157, 6157, 6157, 6157, 6157, 6157,
        // 384 - 391
        -12275, -12275, -12275, -12275, -12275, -12275, -12275, -12275,
        // 392 - 399
        -12275, -12275, -12275, -12275, -12275, -12275, -12275, -12275,
        // 400 - 407
        14353, 14353, 14353, 14353, 16401, 16401, 16401, 16401,
        // 408 - 415
        22547, 22547, 24595, 24595, 20497, 20497, 20497, 20497,
        // 416 - 423
        18449, 18449, 18449, 18449, 26643, 26643, 28691, 28691,
        // 424 - 431
        30739, 30739, -32749, -32749, -30701, -30701, -28653, -28653,
        // 432 - 439
        -26605, -26605, -24557, -24557, -22509, -22509, -20461, -20461,
        // 440 - 447
        8207, 8207, 8207, 8207, 8207, 8207, 8207, 8207,
        // 448 - 455
        72, 72, 72, 72, 72, 72, 72, 72,
        // 456 - 463
        72, 72, 72, 72, 72, 72, 72, 72,
        // 464 - 471
        72, 72, 72, 72, 72, 72, 72, 72,
        // 472 - 479
        72, 72, 72, 72, 72, 72, 72, 72,
        // 480 - 487
        72, 72, 72, 72, 72, 72, 72, 72,
        // 488 - 495
        72, 72, 72, 72, 72, 72, 72, 72,
        // 496 - 503
        72, 72, 72, 72, 72, 72, 72, 72,
        // 504 - 511
        72, 72, 72, 72, 72, 72, 72, 72,
        // 512 - 519
        104, 104, 104, 104, 104, 104, 104, 104,
        // 520 - 527
        104, 104, 104, 104, 104, 104, 104, 104,
        // 528 - 535
        104, 104, 104, 104, 104, 104, 104, 104,
        // 536 - 543
        104, 104, 104, 104, 104, 104, 104, 104,
        // 544 - 551
        104, 104, 104, 104, 104, 104, 104, 104,
        // 552 - 559
        104, 104, 104, 104, 104, 104, 104, 104,
        // 560 - 567
        104, 104, 104, 104, 104, 104, 104, 104,
        // 568 - 575
        104, 104, 104, 104, 104, 104, 104, 104,
        // 576 - 583
        4107, 4107, 4107, 4107, 4107, 4107, 4107, 4107,
        // 584 - 591
        4107, 4107, 4107, 4107, 4107, 4107, 4107, 4107,
        // 592 - 599
        4107, 4107, 4107, 4107, 4107, 4107, 4107, 4107,
        // 600 - 607
        4107, 4107, 4107, 4107, 4107, 4107, 4107, 4107,
        // 608 - 615
        266, 266, 266, 266, 266, 266, 266, 266,
        // 616 - 623
        266, 266, 266, 266, 266, 266, 266, 266,
        // 624 - 631
        266, 266, 266, 266, 266, 266, 266, 266,
        // 632 - 639
        266, 266, 266, 266, 266, 266, 266, 266,
        // 640 - 647
        298, 298, 298, 298, 298, 298, 298, 298,
        // 648 - 655
        298, 298, 298, 298, 298, 298, 298, 298,
        // 656 - 663
        298, 298, 298, 298, 298, 298, 298, 298,
        // 664 - 671
        298, 298, 298, 298, 298, 298, 298, 298,
        // 672 - 679
        524, 524, 524, 524, 524, 524, 524, 524,
        // 680 - 687
        524, 524, 524, 524, 524, 524, 524, 524,
        // 688 - 695
        556, 556, 556, 556, 556, 556, 556, 556,
        // 696 - 703
        556, 556, 556, 556, 556, 556, 556, 556,
        // 704 - 711
        136, 136, 136, 136, 136, 136, 136, 136,
        // 712 - 719
        136, 136, 136, 136, 136, 136, 136, 136,
        // 720 - 727
        136, 136, 136, 136, 136, 136, 136, 136,
        // 728 - 735
        136, 136, 136, 136, 136, 136, 136, 136,
        // 736 - 743
        136, 136, 136, 136, 136, 136, 136, 136,
        // 744 - 751
        136, 136, 136, 136, 136, 136, 136, 136,
        // 752 - 759
        136, 136, 136, 136, 136, 136, 136, 136,
        // 760 - 767
        136, 136, 136, 136, 136, 136, 136, 136,
        // 768 - 775
        168, 168, 168, 168, 168, 168, 168, 168,
        // 776 - 783
        168, 168, 168, 168, 168, 168, 168, 168,
        // 784 - 791
        168, 168, 168, 168, 168, 168, 168, 168,
        // 792 - 799
        168, 168, 168, 168, 168, 168, 168, 168,
        // 800 - 807
        168, 168, 168, 168, 168, 168, 168, 168,
        // 808 - 815
        168, 168, 168, 168, 168, 168, 168, 168,
        // 816 - 823
        168, 168, 168, 168, 168, 168, 168, 168,
        // 824 - 831
        168, 168, 168, 168, 168, 168, 168, 168,
        // 832 - 839
        460, 460, 460, 460, 460, 460, 460, 460,
        // 840 - 847
        460, 460, 460, 460, 460, 460, 460, 460,
        // 848 - 855
        492, 492, 492, 492, 492, 492, 492, 492,
        // 856 - 863
        492, 492, 492, 492, 492, 492, 492, 492,
        // 864 - 871
        2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059,
        // 872 - 879
        2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059,
        // 880 - 887
        2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059,
        // 888 - 895
        2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059,
        // 896 - 903
        200, 200, 200, 200, 200, 200, 200, 200,
        // 904 - 911
        200, 200, 200, 200, 200, 200, 200, 200,
        // 912 - 919
        200, 200, 200, 200, 200, 200, 200, 200,
        // 920 - 927
        200, 200, 200, 200, 200, 200, 200, 200,
        // 928 - 935
        200, 200, 200, 200, 200, 200, 200, 200,
        // 936 - 943
        200, 200, 200, 200, 200, 200, 200, 200,
        // 944 - 951
        200, 200, 200, 200, 200, 200, 200, 200,
        // 952 - 959
        200, 200, 200, 200, 200, 200, 200, 200,
        // 960 - 967
        232, 232, 232, 232, 232, 232, 232, 232,
        // 968 - 975
        232, 232, 232, 232, 232, 232, 232, 232,
        // 976 - 983
        232, 232, 232, 232, 232, 232, 232, 232,
        // 984 - 991
        232, 232, 232, 232, 232, 232, 232, 232,
        // 992 - 999
        232, 232, 232, 232, 232, 232, 232, 232,
        // 1000 - 1007
        232, 232, 232, 232, 232, 232, 232, 232,
        // 1008 - 1015
        232, 232, 232, 232, 232, 232, 232, 232,
        // 1016 - 1023
        232, 232, 232, 232, 232, 232, 232, 232,};
    // Additional make up codes for both White and Black runs
    static short additionalMakeup[] = {
        28679, 28679, 31752, (short) 32777,
        (short) 33801, (short) 34825, (short) 35849, (short) 36873,
        (short) 29703, (short) 29703, (short) 30727, (short) 30727,
        (short) 37897, (short) 38921, (short) 39945, (short) 40969
    };
    // Initial black run look up table, uses the first 4 bits of a code
    static short initBlack[] = {
        // 0 - 7
        3226, 6412, 200, 168, 38, 38, 134, 134,
        // 8 - 15
        100, 100, 100, 100, 68, 68, 68, 68
    };
    // 
    static short twoBitBlack[] = {292, 260, 226, 226};   // 0 - 3
    // Main black run table, using the last 9 bits of possible 13 bit code
    static short black[] = {
        // 0 - 7
        62, 62, 30, 30, 0, 0, 0, 0,
        // 8 - 15
        0, 0, 0, 0, 0, 0, 0, 0,
        // 16 - 23
        0, 0, 0, 0, 0, 0, 0, 0,
        // 24 - 31
        0, 0, 0, 0, 0, 0, 0, 0,
        // 32 - 39
        3225, 3225, 3225, 3225, 3225, 3225, 3225, 3225,
        // 40 - 47
        3225, 3225, 3225, 3225, 3225, 3225, 3225, 3225,
        // 48 - 55
        3225, 3225, 3225, 3225, 3225, 3225, 3225, 3225,
        // 56 - 63
        3225, 3225, 3225, 3225, 3225, 3225, 3225, 3225,
        // 64 - 71
        588, 588, 588, 588, 588, 588, 588, 588,
        // 72 - 79
        1680, 1680, 20499, 22547, 24595, 26643, 1776, 1776,
        // 80 - 87
        1808, 1808, -24557, -22509, -20461, -18413, 1904, 1904,
        // 88 - 95
        1936, 1936, -16365, -14317, 782, 782, 782, 782,
        // 96 - 103
        814, 814, 814, 814, -12269, -10221, 10257, 10257,
        // 104 - 111
        12305, 12305, 14353, 14353, 16403, 18451, 1712, 1712,
        // 112 - 119
        1744, 1744, 28691, 30739, -32749, -30701, -28653, -26605,
        // 120 - 127
        2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061,
        // 128 - 135
        424, 424, 424, 424, 424, 424, 424, 424,
        // 136 - 143
        424, 424, 424, 424, 424, 424, 424, 424,
        // 144 - 151
        424, 424, 424, 424, 424, 424, 424, 424,
        // 152 - 159
        424, 424, 424, 424, 424, 424, 424, 424,
        // 160 - 167
        750, 750, 750, 750, 1616, 1616, 1648, 1648,
        // 168 - 175
        1424, 1424, 1456, 1456, 1488, 1488, 1520, 1520,
        // 176 - 183
        1840, 1840, 1872, 1872, 1968, 1968, 8209, 8209,
        // 184 - 191
        524, 524, 524, 524, 524, 524, 524, 524,
        // 192 - 199
        556, 556, 556, 556, 556, 556, 556, 556,
        // 200 - 207
        1552, 1552, 1584, 1584, 2000, 2000, 2032, 2032,
        // 208 - 215
        976, 976, 1008, 1008, 1040, 1040, 1072, 1072,
        // 216 - 223
        1296, 1296, 1328, 1328, 718, 718, 718, 718,
        // 224 - 231
        456, 456, 456, 456, 456, 456, 456, 456,
        // 232 - 239
        456, 456, 456, 456, 456, 456, 456, 456,
        // 240 - 247
        456, 456, 456, 456, 456, 456, 456, 456,
        // 248 - 255
        456, 456, 456, 456, 456, 456, 456, 456,
        // 256 - 263
        326, 326, 326, 326, 326, 326, 326, 326,
        // 264 - 271
        326, 326, 326, 326, 326, 326, 326, 326,
        // 272 - 279
        326, 326, 326, 326, 326, 326, 326, 326,
        // 280 - 287
        326, 326, 326, 326, 326, 326, 326, 326,
        // 288 - 295
        326, 326, 326, 326, 326, 326, 326, 326,
        // 296 - 303
        326, 326, 326, 326, 326, 326, 326, 326,
        // 304 - 311
        326, 326, 326, 326, 326, 326, 326, 326,
        // 312 - 319
        326, 326, 326, 326, 326, 326, 326, 326,
        // 320 - 327
        358, 358, 358, 358, 358, 358, 358, 358,
        // 328 - 335
        358, 358, 358, 358, 358, 358, 358, 358,
        // 336 - 343
        358, 358, 358, 358, 358, 358, 358, 358,
        // 344 - 351
        358, 358, 358, 358, 358, 358, 358, 358,
        // 352 - 359
        358, 358, 358, 358, 358, 358, 358, 358,
        // 360 - 367
        358, 358, 358, 358, 358, 358, 358, 358,
        // 368 - 375
        358, 358, 358, 358, 358, 358, 358, 358,
        // 376 - 383
        358, 358, 358, 358, 358, 358, 358, 358,
        // 384 - 391
        490, 490, 490, 490, 490, 490, 490, 490,
        // 392 - 399
        490, 490, 490, 490, 490, 490, 490, 490,
        // 400 - 407
        4113, 4113, 6161, 6161, 848, 848, 880, 880,
        // 408 - 415
        912, 912, 944, 944, 622, 622, 622, 622,
        // 416 - 423
        654, 654, 654, 654, 1104, 1104, 1136, 1136,
        // 424 - 431
        1168, 1168, 1200, 1200, 1232, 1232, 1264, 1264,
        // 432 - 439
        686, 686, 686, 686, 1360, 1360, 1392, 1392,
        // 440 - 447
        12, 12, 12, 12, 12, 12, 12, 12,
        // 448 - 455
        390, 390, 390, 390, 390, 390, 390, 390,
        // 456 - 463
        390, 390, 390, 390, 390, 390, 390, 390,
        // 464 - 471
        390, 390, 390, 390, 390, 390, 390, 390,
        // 472 - 479
        390, 390, 390, 390, 390, 390, 390, 390,
        // 480 - 487
        390, 390, 390, 390, 390, 390, 390, 390,
        // 488 - 495
        390, 390, 390, 390, 390, 390, 390, 390,
        // 496 - 503
        390, 390, 390, 390, 390, 390, 390, 390,
        // 504 - 511
        390, 390, 390, 390, 390, 390, 390, 390,};
    static byte twoDCodes[] = {
        // 0 - 7
        80, 88, 23, 71, 30, 30, 62, 62,
        // 8 - 15
        4, 4, 4, 4, 4, 4, 4, 4,
        // 16 - 23
        11, 11, 11, 11, 11, 11, 11, 11,
        // 24 - 31
        11, 11, 11, 11, 11, 11, 11, 11,
        // 32 - 39
        35, 35, 35, 35, 35, 35, 35, 35,
        // 40 - 47
        35, 35, 35, 35, 35, 35, 35, 35,
        // 48 - 55
        51, 51, 51, 51, 51, 51, 51, 51,
        // 56 - 63
        51, 51, 51, 51, 51, 51, 51, 51,
        // 64 - 71
        41, 41, 41, 41, 41, 41, 41, 41,
        // 72 - 79
        41, 41, 41, 41, 41, 41, 41, 41,
        // 80 - 87
        41, 41, 41, 41, 41, 41, 41, 41,
        // 88 - 95
        41, 41, 41, 41, 41, 41, 41, 41,
        // 96 - 103
        41, 41, 41, 41, 41, 41, 41, 41,
        // 104 - 111
        41, 41, 41, 41, 41, 41, 41, 41,
        // 112 - 119
        41, 41, 41, 41, 41, 41, 41, 41,
        // 120 - 127
        41, 41, 41, 41, 41, 41, 41, 41,};

    public TIFFFaxDecompressor() {
    }

    /**
     * Invokes the superclass method and then sets instance variables on
     * the basis of the metadata set on this decompressor.
     */
    public void SetOptions(int fillOrder, int compression, int t4Options, int t6Options) {
        this.fillOrder = fillOrder;
        this.compression = compression;
        this.t4Options = t4Options;
        this.t6Options = t6Options;
        this.oneD = (int) (t4Options & 0x01);
        this.uncompressedMode = (int) ((t4Options & 0x02) >> 1);
        this.fillBits = (int) ((t4Options & 0x04) >> 2);
    }

    public void decodeRaw(byte[] buffer, byte[] compData, int w, int h) {

        this.buffer = buffer;
        this.data = compData;
        this.w = w;
        this.h = h;
        this.bitsPerScanline = w;
        this.lineBitNum = 0;

        this.bitPointer = 0;
        this.bytePointer = 0;
        this.prevChangingElems = new int[w + 1];
        this.currChangingElems = new int[w + 1];

        fails = 0;

        try {
            if (compression == TIFFConstants.COMPRESSION_CCITTRLE) {
                decodeRLE();
            } else if (compression == TIFFConstants.COMPRESSION_CCITTFAX3) {
                decodeT4();
            } else if (compression == TIFFConstants.COMPRESSION_CCITTFAX4) {
                this.uncompressedMode = (int) ((t6Options & 0x02) >> 1);
                decodeT6();
            } else {
                throw new RuntimeException("Unknown compression type " + compression);
            }
        } catch (ArrayIndexOutOfBoundsException e) {
            //ignore
        }
    }

    public void decodeRLE() {
        for (int i = 0; i < h; i++) {
            // Decode the line.
            decodeNextScanline();

            // Advance to the next byte boundary if not already there.
            if (bitPointer != 0) {
                bytePointer++;
                bitPointer = 0;
            }

            // Update the total number of bits.
            lineBitNum += bitsPerScanline;
        }
    }

    public void decodeNextScanline() {
        int bits = 0, code = 0, isT = 0;
        int current, entry, twoBits;
        boolean isWhite = true;

        int bitOffset = 0;

        // Initialize starting of the changing elements array
        changingElemSize = 0;

        // While scanline not complete
        while (bitOffset < w) {

            // Mark start of white run.
            int runOffset = bitOffset;

            while (isWhite && bitOffset < w) {
                // White run
                current = nextNBits(10);
                entry = white[current];

                // Get the 3 fields from the entry
                isT = entry & 0x0001;
                bits = (entry >>> 1) & 0x0f;

                if (bits == 12) {          // Additional Make up code
                    // Get the next 2 bits
                    twoBits = nextLesserThan8Bits(2);
                    // Consolidate the 2 new bits and last 2 bits into 4 bits
                    current = ((current << 2) & 0x000c) | twoBits;
                    entry = additionalMakeup[current];
                    bits = (entry >>> 1) & 0x07;     // 3 bits 0000 0111
                    code = (entry >>> 4) & 0x0fff;  // 12 bits
                    bitOffset += code; // Skip white run

                    updatePointer(4 - bits);
                } else if (bits == 0) {     // ERROR
                    ++fails;
                    // XXX return?
                } else if (bits == 15) {    // EOL
                    //
                    // Instead of throwing an exception, assume that the
                    // EOL was premature; emit a warning and return.
                    //
                    ++fails;
                    return;
                } else {
                    // 11 bits - 0000 0111 1111 1111 = 0x07ff
                    code = (entry >>> 5) & 0x07ff;
                    bitOffset += code;

                    updatePointer(10 - bits);
                    if (isT == 0) {
                        isWhite = false;
                        currChangingElems[changingElemSize++] = bitOffset;
                    }
                }
            }

            // Check whether this run completed one width
            if (bitOffset == w) {
                // If the white run has not been terminated then ensure that
                // the next code word is a terminating code for a white run
                // of length zero.
                int runLength = bitOffset - runOffset;
                if (isWhite
                        && runLength != 0 && runLength % 64 == 0
                        && nextNBits(8) != 0x35) {
                    ++fails;
                    updatePointer(8);
                }
                break;
            }

            // Mark start of black run.
            runOffset = bitOffset;

            while (isWhite == false && bitOffset < w) {
                // Black run
                current = nextLesserThan8Bits(4);
                entry = initBlack[current];

                // Get the 3 fields from the entry
                isT = entry & 0x0001;
                bits = (entry >>> 1) & 0x000f;
                code = (entry >>> 5) & 0x07ff;

                if (code == 100) {
                    current = nextNBits(9);
                    entry = black[current];

                    // Get the 3 fields from the entry
                    isT = entry & 0x0001;
                    bits = (entry >>> 1) & 0x000f;
                    code = (entry >>> 5) & 0x07ff;

                    if (bits == 12) {
                        // Additional makeup codes
                        updatePointer(5);
                        current = nextLesserThan8Bits(4);
                        entry = additionalMakeup[current];
                        bits = (entry >>> 1) & 0x07;     // 3 bits 0000 0111
                        code = (entry >>> 4) & 0x0fff;  // 12 bits

                        setToBlack(bitOffset, code);
                        bitOffset += code;

                        updatePointer(4 - bits);
                    } else if (bits == 15) {
                        //
                        // Instead of throwing an exception, assume that the
                        // EOL was premature; emit a warning and return.
                        //
                        ++fails;
                        return;
                    } else {
                        setToBlack(bitOffset, code);
                        bitOffset += code;

                        updatePointer(9 - bits);
                        if (isT == 0) {
                            isWhite = true;
                            currChangingElems[changingElemSize++] = bitOffset;
                        }
                    }
                } else if (code == 200) {
                    // Is a Terminating code
                    current = nextLesserThan8Bits(2);
                    entry = twoBitBlack[current];
                    code = (entry >>> 5) & 0x07ff;
                    bits = (entry >>> 1) & 0x0f;

                    setToBlack(bitOffset, code);
                    bitOffset += code;

                    updatePointer(2 - bits);
                    isWhite = true;
                    currChangingElems[changingElemSize++] = bitOffset;
                } else {
                    // Is a Terminating code
                    setToBlack(bitOffset, code);
                    bitOffset += code;

                    updatePointer(4 - bits);
                    isWhite = true;
                    currChangingElems[changingElemSize++] = bitOffset;
                }
            }

            // Check whether this run completed one width
            if (bitOffset == w) {
                // If the black run has not been terminated then ensure that
                // the next code word is a terminating code for a black run
                // of length zero.
                int runLength = bitOffset - runOffset;
                if (!isWhite
                        && runLength != 0 && runLength % 64 == 0
                        && nextNBits(10) != 0x37) {
                    ++fails;
                    updatePointer(10);
                }
                break;
            }
        }

        currChangingElems[changingElemSize++] = bitOffset;
    }

    public void decodeT4() {
        int height = h;

        int a0, a1, b1, b2;
        int[] b = new int[2];
        int entry, code, bits, color;
        boolean isWhite;
        int currIndex = 0;
        int temp[];

        if (data.length < 2) {
            throw new RuntimeException("Insufficient data to read initial EOL.");
        }

        // The data should start with an EOL code
        int next12 = nextNBits(12);
        if (next12 != 1) {
            ++fails;
        }
        updatePointer(12);

        // Find the first one-dimensionally encoded line.
        int modeFlag = 0;
        int lines = -1; // indicates imaginary line before first actual line.
        while (modeFlag != 1) {
            try {
                modeFlag = findNextLine();
                lines++; // Normally 'lines' will be 0 on exiting loop.
            } catch (Exception eofe) {
                throw new RuntimeException("No reference line present.");
            }
        }

        int bitOffset;

        // Then the 1D encoded scanline data will occur, changing elements
        // array gets set.
        decodeNextScanline();
        lines++;
        lineBitNum += bitsPerScanline;

        while (lines < height) {

            // Every line must begin with an EOL followed by a bit which
            // indicates whether the following scanline is 1D or 2D encoded.
            try {
                modeFlag = findNextLine();
            } catch (Exception eofe) {
                ++fails;
                break;
            }
            if (modeFlag == 0) {
                // 2D encoded scanline follows

                // Initialize previous scanlines changing elements, and
                // initialize current scanline's changing elements array
                temp = prevChangingElems;
                prevChangingElems = currChangingElems;
                currChangingElems = temp;
                currIndex = 0;

                // a0 has to be set just before the start of this scanline.
                a0 = -1;
                isWhite = true;
                bitOffset = 0;

                lastChangingElement = 0;

                while (bitOffset < w) {
                    // Get the next changing element
                    getNextChangingElement(a0, isWhite, b);

                    b1 = b[0];
                    b2 = b[1];

                    // Get the next seven bits
                    entry = nextLesserThan8Bits(7);

                    // Run these through the 2DCodes table
                    entry = (int) (twoDCodes[entry] & 0xff);

                    // Get the code and the number of bits used up
                    code = (entry & 0x78) >>> 3;
                    bits = entry & 0x07;

                    if (code == 0) {
                        if (!isWhite) {
                            setToBlack(bitOffset, b2 - bitOffset);
                        }
                        bitOffset = a0 = b2;

                        // Set pointer to consume the correct number of bits.
                        updatePointer(7 - bits);
                    } else if (code == 1) {
                        // Horizontal
                        updatePointer(7 - bits);

                        // identify the next 2 codes.
                        int number;
                        if (isWhite) {
                            number = decodeWhiteCodeWord();
                            bitOffset += number;
                            currChangingElems[currIndex++] = bitOffset;

                            number = decodeBlackCodeWord();
                            setToBlack(bitOffset, number);
                            bitOffset += number;
                            currChangingElems[currIndex++] = bitOffset;
                        } else {
                            number = decodeBlackCodeWord();
                            setToBlack(bitOffset, number);
                            bitOffset += number;
                            currChangingElems[currIndex++] = bitOffset;

                            number = decodeWhiteCodeWord();
                            bitOffset += number;
                            currChangingElems[currIndex++] = bitOffset;
                        }

                        a0 = bitOffset;
                    } else if (code <= 8) {
                        // Vertical
                        a1 = b1 + (code - 5);

                        currChangingElems[currIndex++] = a1;

                        // We write the current color till a1 - 1 pos,
                        // since a1 is where the next color starts
                        if (!isWhite) {
                            setToBlack(bitOffset, a1 - bitOffset);
                        }
                        bitOffset = a0 = a1;
                        isWhite = !isWhite;

                        updatePointer(7 - bits);
                    } else {
                        ++fails;
                        // Find the next one-dimensionally encoded line.
                        int numLinesTested = 0;
                        while (modeFlag != 1) {
                            try {
                                modeFlag = findNextLine();
                                numLinesTested++;
                            } catch (Exception eofe) {
                                return;
                            }
                        }
                        lines += numLinesTested - 1;
                        updatePointer(13);
                        break;
                    }
                }

                // Add the changing element beyond the current scanline for the
                // other color too
                currChangingElems[currIndex++] = bitOffset;
                changingElemSize = currIndex;
            } else { // modeFlag == 1
                // 1D encoded scanline follows
                decodeNextScanline();
            }

            lineBitNum += bitsPerScanline;
            lines++;
        } // while(lines < height)
    }

    public synchronized void decodeT6() {
        int height = h;


        int a0, a1, b1, b2;
        int entry, code, bits;
        boolean isWhite;
        int currIndex;
        int temp[];

        // Return values from getNextChangingElement
        int[] b = new int[2];

        // uncompressedMode - have written some code for this, but this
        // has not been tested due to lack of test images using this optional
        // extension. This code is when code == 11. aastha 03/03/1999

        // Local cached reference
        int[] cce = currChangingElems;

        // Assume invisible preceding row of all white pixels and insert
        // both black and white changing elements beyond the end of this
        // imaginary scanline.
        changingElemSize = 0;
        cce[changingElemSize++] = w;
        cce[changingElemSize++] = w;

        int bitOffset;

        for (int lines = 0; lines < height; lines++) {
            // a0 has to be set just before the start of the scanline.
            a0 = -1;
            isWhite = true;

            // Assign the changing elements of the previous scanline to
            // prevChangingElems and start putting this new scanline's
            // changing elements into the currChangingElems.
            temp = prevChangingElems;
            prevChangingElems = currChangingElems;
            cce = currChangingElems = temp;
            currIndex = 0;

            // Start decoding the scanline
            bitOffset = 0;

            // Reset search start position for getNextChangingElement
            lastChangingElement = 0;

            // Till one whole scanline is decoded
            while (bitOffset < w) {
                // Get the next changing element
                getNextChangingElement(a0, isWhite, b);
                b1 = b[0];
                b2 = b[1];

                // Get the next seven bits
                entry = nextLesserThan8Bits(7);
                // Run these through the 2DCodes table
                entry = (int) (twoDCodes[entry] & 0xff);

                // Get the code and the number of bits used up
                code = (entry & 0x78) >>> 3;
                bits = entry & 0x07;

                if (code == 0) { // Pass
                    // We always assume WhiteIsZero format for fax.
                    if (!isWhite) {
                        if (b2 > w) {
                            b2 = w;
                        }
                        setToBlack(bitOffset, b2 - bitOffset);
                    }
                    bitOffset = a0 = b2;

                    // Set pointer to only consume the correct number of bits.
                    updatePointer(7 - bits);
                } else if (code == 1) { // Horizontal
                    // Set pointer to only consume the correct number of bits.
                    updatePointer(7 - bits);

                    // identify the next 2 alternating color codes.
                    int number;
                    if (isWhite) {
                        // Following are white and black runs
                        number = decodeWhiteCodeWord();
                        bitOffset += number;
                        cce[currIndex++] = bitOffset;

                        number = decodeBlackCodeWord();
                        if (number > w - bitOffset) {
                            number = w - bitOffset;
                        }
                        setToBlack(bitOffset, number);
                        bitOffset += number;
                        cce[currIndex++] = bitOffset;
                    } else {
                        // First a black run and then a white run follows
                        number = decodeBlackCodeWord();
                        if (number > w - bitOffset) {
                            number = w - bitOffset;
                        }
                        setToBlack(bitOffset, number);
                        bitOffset += number;
                        cce[currIndex++] = bitOffset;

                        number = decodeWhiteCodeWord();
                        bitOffset += number;
                        cce[currIndex++] = bitOffset;
                    }

                    a0 = bitOffset;
                } else if (code <= 8) { // Vertical
                    a1 = b1 + (code - 5);
                    cce[currIndex++] = a1;

                    // We write the current color till a1 - 1 pos,
                    // since a1 is where the next color starts
                    if (!isWhite) {
                        if (a1 > w) {
                            a1 = w;
                        }
                        setToBlack(bitOffset, a1 - bitOffset);
                    }
                    bitOffset = a0 = a1;
                    isWhite = !isWhite;

                    updatePointer(7 - bits);
                } else if (code == 11) {
                    int entranceCode = nextLesserThan8Bits(3);

                    int zeros = 0;
                    boolean exit = false;

                    while (!exit) {
                        while (nextLesserThan8Bits(1) != 1) {
                            zeros++;
                        }

                        if (zeros > 5) {
                            // Exit code

                            // Zeros before exit code
                            zeros = zeros - 6;

                            if (!isWhite && (zeros > 0)) {
                                cce[currIndex++] = bitOffset;
                            }

                            // Zeros before the exit code
                            bitOffset += zeros;
                            if (zeros > 0) {
                                // Some zeros have been written
                                isWhite = true;
                            }

                            // Read in the bit which specifies the color of
                            // the following run
                            if (nextLesserThan8Bits(1) == 0) {
                                if (!isWhite) {
                                    cce[currIndex++] = bitOffset;
                                }
                                isWhite = true;
                            } else {
                                if (isWhite) {
                                    cce[currIndex++] = bitOffset;
                                }
                                isWhite = false;
                            }

                            exit = true;
                        }

                        if (zeros == 5) {
                            if (!isWhite) {
                                cce[currIndex++] = bitOffset;
                            }
                            bitOffset += zeros;

                            // Last thing written was white
                            isWhite = true;
                        } else {
                            bitOffset += zeros;

                            cce[currIndex++] = bitOffset;
                            setToBlack(bitOffset, 1);
                            ++bitOffset;

                            // Last thing written was black
                            isWhite = false;
                        }

                    }
                }
            } // while bitOffset < w

            // Add the changing element beyond the current scanline for the
            // other color too, if not already added previously
            if (currIndex <= w)
                cce[currIndex++] = bitOffset;

            // Number of changing elements in this scanline.
            changingElemSize = currIndex;

            lineBitNum += bitsPerScanline;
        } // for lines < height
    }

    private void setToBlack(int bitNum, int numBits) {
        // bitNum is relative to current scanline so bump it by lineBitNum
        bitNum += lineBitNum;

        int lastBit = bitNum + numBits;
        int byteNum = bitNum >> 3;

        // Handle bits in first byte
        int shift = bitNum & 0x7;
        if (shift > 0) {
            int maskVal = 1 << (7 - shift);
            byte val = buffer[byteNum];
            while (maskVal > 0 && bitNum < lastBit) {
                val |= maskVal;
                maskVal >>= 1;
                ++bitNum;
            }
            buffer[byteNum] = val;
        }

        // Fill in 8 bits at a time
        byteNum = bitNum >> 3;
        while (bitNum < lastBit - 7) {
            buffer[byteNum++] = (byte) 255;
            bitNum += 8;
        }

        // Fill in remaining bits
        while (bitNum < lastBit) {
            byteNum = bitNum >> 3;
            buffer[byteNum] |= 1 << (7 - (bitNum & 0x7));
            ++bitNum;
        }
    }

    // Returns run length
    private int decodeWhiteCodeWord() {
        int current, entry, bits, isT, twoBits, code = -1;
        int runLength = 0;
        boolean isWhite = true;

        while (isWhite) {
            current = nextNBits(10);
            entry = white[current];

            // Get the 3 fields from the entry
            isT = entry & 0x0001;
            bits = (entry >>> 1) & 0x0f;

            if (bits == 12) {           // Additional Make up code
                // Get the next 2 bits
                twoBits = nextLesserThan8Bits(2);
                // Consolidate the 2 new bits and last 2 bits into 4 bits
                current = ((current << 2) & 0x000c) | twoBits;
                entry = additionalMakeup[current];
                bits = (entry >>> 1) & 0x07;     // 3 bits 0000 0111
                code = (entry >>> 4) & 0x0fff;   // 12 bits
                runLength += code;
                updatePointer(4 - bits);
            } else if (bits == 0) {     // ERROR
                throw new RuntimeException("Error 0");
            } else if (bits == 15) {    // EOL
                throw new RuntimeException("Error 1");
            } else {
                // 11 bits - 0000 0111 1111 1111 = 0x07ff
                code = (entry >>> 5) & 0x07ff;
                runLength += code;
                updatePointer(10 - bits);
                if (isT == 0) {
                    isWhite = false;
                }
            }
        }

        return runLength;
    }

    // Returns run length
    private int decodeBlackCodeWord() {
        int current, entry, bits, isT, twoBits, code = -1;
        int runLength = 0;
        boolean isWhite = false;

        while (!isWhite) {
            current = nextLesserThan8Bits(4);
            entry = initBlack[current];

            // Get the 3 fields from the entry
            isT = entry & 0x0001;
            bits = (entry >>> 1) & 0x000f;
            code = (entry >>> 5) & 0x07ff;

            if (code == 100) {
                current = nextNBits(9);
                entry = black[current];

                // Get the 3 fields from the entry
                isT = entry & 0x0001;
                bits = (entry >>> 1) & 0x000f;
                code = (entry >>> 5) & 0x07ff;

                if (bits == 12) {
                    // Additional makeup codes
                    updatePointer(5);
                    current = nextLesserThan8Bits(4);
                    entry = additionalMakeup[current];
                    bits = (entry >>> 1) & 0x07;     // 3 bits 0000 0111
                    code = (entry >>> 4) & 0x0fff;  // 12 bits
                    runLength += code;

                    updatePointer(4 - bits);
                } else if (bits == 15) {
                    // EOL code
                    throw new RuntimeException("Error 2");
                } else {
                    runLength += code;
                    updatePointer(9 - bits);
                    if (isT == 0) {
                        isWhite = true;
                    }
                }
            } else if (code == 200) {
                // Is a Terminating code
                current = nextLesserThan8Bits(2);
                entry = twoBitBlack[current];
                code = (entry >>> 5) & 0x07ff;
                runLength += code;
                bits = (entry >>> 1) & 0x0f;
                updatePointer(2 - bits);
                isWhite = true;
            } else {
                // Is a Terminating code
                runLength += code;
                updatePointer(4 - bits);
                isWhite = true;
            }
        }

        return runLength;
    }

    private int findNextLine() {
        // Set maximum and current bit index into the compressed data.
        int bitIndexMax = data.length * 8 - 1;
        int bitIndexMax12 = bitIndexMax - 12;
        int bitIndex = bytePointer * 8 + bitPointer;

        // Loop while at least 12 bits are available.
        while (bitIndex <= bitIndexMax12) {
            // Get the next 12 bits.
            int next12Bits = nextNBits(12);
            bitIndex += 12;

            // Loop while the 12 bits are not unity, i.e., while the EOL
            // has not been reached, and there is at least one bit left.
            while (next12Bits != 1 && bitIndex < bitIndexMax) {
                next12Bits =
                        ((next12Bits & 0x000007ff) << 1)
                        | (nextLesserThan8Bits(1) & 0x00000001);
                bitIndex++;
            }

            if (next12Bits == 1) { // now positioned just after EOL
                if (oneD == 1) { // two-dimensional coding
                    if (bitIndex < bitIndexMax) {
                        // check next bit against type of line being sought
                        return nextLesserThan8Bits(1);
                    }
                } else {
                    return 1;
                }
            }
        }

        // EOL not found.
        throw new RuntimeException();
    }

    private void getNextChangingElement(int a0, boolean isWhite, int[] ret) {
        // Local copies of instance variables
        int[] pce = this.prevChangingElems;
        int ces = this.changingElemSize;

        // If the previous match was at an odd element, we still
        // have to search the preceeding element.
        // int start = lastChangingElement & ~0x1;
        int start = lastChangingElement > 0 ? lastChangingElement - 1 : 0;
        if (isWhite) {
            start &= ~0x1; // Search even numbered elements
        } else {
            start |= 0x1; // Search odd numbered elements
        }

        int i = start;
        for (; i < ces; i += 2) {
            int temp = pce[i];
            if (temp > a0) {
                lastChangingElement = i;
                ret[0] = temp;
                break;
            }
        }

        if (i + 1 < ces) {
            ret[1] = pce[i + 1];
        }
    }

    private int nextNBits(int bitsToGet) {
        byte b, next, next2next;
        int l = data.length - 1;
        int bp = this.bytePointer;

        if (fillOrder == 1) {
            b = data[bp];

            if (bp == l) {
                next = 0x00;
                next2next = 0x00;
            } else if ((bp + 1) == l) {
                next = data[bp + 1];
                next2next = 0x00;
            } else {
                next = data[bp + 1];
                next2next = data[bp + 2];
            }
        } else if (fillOrder == 2) {
            b = flipTable[data[bp] & 0xff];

            if (bp == l) {
                next = 0x00;
                next2next = 0x00;
            } else if ((bp + 1) == l) {
                next = flipTable[data[bp + 1] & 0xff];
                next2next = 0x00;
            } else {
                next = flipTable[data[bp + 1] & 0xff];
                next2next = flipTable[data[bp + 2] & 0xff];
            }
        } else {
            throw new RuntimeException("Invalid FillOrder");
        }

        int bitsLeft = 8 - bitPointer;
        int bitsFromNextByte = bitsToGet - bitsLeft;
        int bitsFromNext2NextByte = 0;
        if (bitsFromNextByte > 8) {
            bitsFromNext2NextByte = bitsFromNextByte - 8;
            bitsFromNextByte = 8;
        }

        bytePointer++;

        int i1 = (b & table1[bitsLeft]) << (bitsToGet - bitsLeft);
        int i2 = (next & table2[bitsFromNextByte]) >>> (8 - bitsFromNextByte);

        int i3 = 0;
        if (bitsFromNext2NextByte != 0) {
            i2 <<= bitsFromNext2NextByte;
            i3 = (next2next & table2[bitsFromNext2NextByte])
                    >>> (8 - bitsFromNext2NextByte);
            i2 |= i3;
            bytePointer++;
            bitPointer = bitsFromNext2NextByte;
        } else {
            if (bitsFromNextByte == 8) {
                bitPointer = 0;
                bytePointer++;
            } else {
                bitPointer = bitsFromNextByte;
            }
        }

        int i = i1 | i2;
        return i;
    }

    private int nextLesserThan8Bits(int bitsToGet) {
        byte b, next;
        int l = data.length - 1;
        int bp = this.bytePointer;

        if (fillOrder == 1) {
            b = data[bp];
            if (bp == l) {
                next = 0x00;
            } else {
                next = data[bp + 1];
            }
        } else if (fillOrder == 2) {
            b = flipTable[data[bp] & 0xff];
            if (bp == l) {
                next = 0x00;
            } else {
                next = flipTable[data[bp + 1] & 0xff];
            }
        } else {
            throw new RuntimeException("Invalid FillOrder");
        }

        int bitsLeft = 8 - bitPointer;
        int bitsFromNextByte = bitsToGet - bitsLeft;

        int shift = bitsLeft - bitsToGet;
        int i1, i2;
        if (shift >= 0) {
            i1 = (b & table1[bitsLeft]) >>> shift;
            bitPointer += bitsToGet;
            if (bitPointer == 8) {
                bitPointer = 0;
                bytePointer++;
            }
        } else {
            i1 = (b & table1[bitsLeft]) << (-shift);
            i2 = (next & table2[bitsFromNextByte]) >>> (8 - bitsFromNextByte);

            i1 |= i2;
            bytePointer++;
            bitPointer = bitsFromNextByte;
        }

        return i1;
    }

    // Move pointer backwards by given amount of bits
    private void updatePointer(int bitsToMoveBack) {
        if (bitsToMoveBack > 8) {
            bytePointer -= bitsToMoveBack / 8;
            bitsToMoveBack %= 8;
        }

        int i = bitPointer - bitsToMoveBack;
        if (i < 0) {
            bytePointer--;
            bitPointer = 8 + i;
        } else {
            bitPointer = i;
        }
    }
}
/**
 * Class that can decode TIFF files.
 */
public class TIFFFaxDecoder {

    private int bitPointer, bytePointer;
    private byte[] data;
    private int w, h;
    private long fillOrder;

    // Data structures needed to store changing elements for the previous
    // and the current scanline
    private int changingElemSize = 0;
    private int prevChangingElems[];
    private int currChangingElems[];

    // Element at which to start search in getNextChangingElement
    private int lastChangingElement = 0;

    private int compression = 2;

    // Variables set by T4Options
    private int uncompressedMode = 0;
    private int fillBits = 0;
    private int oneD;

    // should iText try to recover from images it can't read?
    private boolean recoverFromImageError;

    static int table1[] = {
        0x00, // 0 bits are left in first byte - SHOULD NOT HAPPEN
        0x01, // 1 bits are left in first byte
        0x03, // 2 bits are left in first byte
        0x07, // 3 bits are left in first byte
        0x0f, // 4 bits are left in first byte
        0x1f, // 5 bits are left in first byte
        0x3f, // 6 bits are left in first byte
        0x7f, // 7 bits are left in first byte
        0xff  // 8 bits are left in first byte
    };

    static int table2[] = {
        0x00, // 0
        0x80, // 1
        0xc0, // 2
        0xe0, // 3
        0xf0, // 4
        0xf8, // 5
        0xfc, // 6
        0xfe, // 7
        0xff  // 8
    };

    // Table to be used when fillOrder = 2, for flipping bytes.
    static byte flipTable[] = {
        0,  -128,    64,   -64,    32,   -96,    96,   -32,
        16,  -112,    80,   -48,    48,   -80,   112,   -16,
        8,  -120,    72,   -56,    40,   -88,   104,   -24,
        24,  -104,    88,   -40,    56,   -72,   120,    -8,
        4,  -124,    68,   -60,    36,   -92,   100,   -28,
        20,  -108,    84,   -44,    52,   -76,   116,   -12,
        12,  -116,    76,   -52,    44,   -84,   108,   -20,
        28,  -100,    92,   -36,    60,   -68,   124,    -4,
        2,  -126,    66,   -62,    34,   -94,    98,   -30,
        18,  -110,    82,   -46,    50,   -78,   114,   -14,
        10,  -118,    74,   -54,    42,   -86,   106,   -22,
        26,  -102,    90,   -38,    58,   -70,   122,    -6,
        6,  -122,    70,   -58,    38,   -90,   102,   -26,
        22,  -106,    86,   -42,    54,   -74,   118,   -10,
        14,  -114,    78,   -50,    46,   -82,   110,   -18,
        30,   -98,    94,   -34,    62,   -66,   126,    -2,
        1,  -127,    65,   -63,    33,   -95,    97,   -31,
        17,  -111,    81,   -47,    49,   -79,   113,   -15,
        9,  -119,    73,   -55,    41,   -87,   105,   -23,
        25,  -103,    89,   -39,    57,   -71,   121,    -7,
        5,  -123,    69,   -59,    37,   -91,   101,   -27,
        21,  -107,    85,   -43,    53,   -75,   117,   -11,
        13,  -115,    77,   -51,    45,   -83,   109,   -19,
        29,   -99,    93,   -35,    61,   -67,   125,    -3,
        3,  -125,    67,   -61,    35,   -93,    99,   -29,
        19,  -109,    83,   -45,    51,   -77,   115,   -13,
        11,  -117,    75,   -53,    43,   -85,   107,   -21,
        27,  -101,    91,   -37,    59,   -69,   123,    -5,
        7,  -121,    71,   -57,    39,   -89,   103,   -25,
        23,  -105,    87,   -41,    55,   -73,   119,    -9,
        15,  -113,    79,   -49,    47,   -81,   111,   -17,
        31,   -97,    95,   -33,    63,   -65,   127,    -1,
    };

    // The main 10 bit white runs lookup table
    static short white[] = {
        // 0 - 7
        6430,   6400,   6400,   6400,   3225,   3225,   3225,   3225,
        // 8 - 15
        944,    944,    944,    944,    976,    976,    976,    976,
        // 16 - 23
        1456,   1456,   1456,   1456,   1488,   1488,   1488,   1488,
        // 24 - 31
        718,    718,    718,    718,    718,    718,    718,    718,
        // 32 - 39
        750,    750,    750,    750,    750,    750,    750,    750,
        // 40 - 47
        1520,   1520,   1520,   1520,   1552,   1552,   1552,   1552,
        // 48 - 55
        428,    428,    428,    428,    428,    428,    428,    428,
        // 56 - 63
        428,    428,    428,    428,    428,    428,    428,    428,
        // 64 - 71
        654,    654,    654,    654,    654,    654,    654,    654,
        // 72 - 79
        1072,   1072,   1072,   1072,   1104,   1104,   1104,   1104,
        // 80 - 87
        1136,   1136,   1136,   1136,   1168,   1168,   1168,   1168,
        // 88 - 95
        1200,   1200,   1200,   1200,   1232,   1232,   1232,   1232,
        // 96 - 103
        622,    622,    622,    622,    622,    622,    622,    622,
        // 104 - 111
        1008,   1008,   1008,   1008,   1040,   1040,   1040,   1040,
        // 112 - 119
        44,     44,     44,     44,     44,     44,     44,     44,
        // 120 - 127
        44,     44,     44,     44,     44,     44,     44,     44,
        // 128 - 135
        396,    396,    396,    396,    396,    396,    396,    396,
        // 136 - 143
        396,    396,    396,    396,    396,    396,    396,    396,
        // 144 - 151
        1712,   1712,   1712,   1712,   1744,   1744,   1744,   1744,
        // 152 - 159
        846,    846,    846,    846,    846,    846,    846,    846,
        // 160 - 167
        1264,   1264,   1264,   1264,   1296,   1296,   1296,   1296,
        // 168 - 175
        1328,   1328,   1328,   1328,   1360,   1360,   1360,   1360,
        // 176 - 183
        1392,   1392,   1392,   1392,   1424,   1424,   1424,   1424,
        // 184 - 191
        686,    686,    686,    686,    686,    686,    686,    686,
        // 192 - 199
        910,    910,    910,    910,    910,    910,    910,    910,
        // 200 - 207
        1968,   1968,   1968,   1968,   2000,   2000,   2000,   2000,
        // 208 - 215
        2032,   2032,   2032,   2032,     16,     16,     16,     16,
        // 216 - 223
        10257,  10257,  10257,  10257,  12305,  12305,  12305,  12305,
        // 224 - 231
        330,    330,    330,    330,    330,    330,    330,    330,
        // 232 - 239
        330,    330,    330,    330,    330,    330,    330,    330,
        // 240 - 247
        330,    330,    330,    330,    330,    330,    330,    330,
        // 248 - 255
        330,    330,    330,    330,    330,    330,    330,    330,
        // 256 - 263
        362,    362,    362,    362,    362,    362,    362,    362,
        // 264 - 271
        362,    362,    362,    362,    362,    362,    362,    362,
        // 272 - 279
        362,    362,    362,    362,    362,    362,    362,    362,
        // 280 - 287
        362,    362,    362,    362,    362,    362,    362,    362,
        // 288 - 295
        878,    878,    878,    878,    878,    878,    878,    878,
        // 296 - 303
        1904,   1904,   1904,   1904,   1936,   1936,   1936,   1936,
        // 304 - 311
        -18413, -18413, -16365, -16365, -14317, -14317, -10221, -10221,
        // 312 - 319
        590,    590,    590,    590,    590,    590,    590,    590,
        // 320 - 327
        782,    782,    782,    782,    782,    782,    782,    782,
        // 328 - 335
        1584,   1584,   1584,   1584,   1616,   1616,   1616,   1616,
        // 336 - 343
        1648,   1648,   1648,   1648,   1680,   1680,   1680,   1680,
        // 344 - 351
        814,    814,    814,    814,    814,    814,    814,    814,
        // 352 - 359
        1776,   1776,   1776,   1776,   1808,   1808,   1808,   1808,
        // 360 - 367
        1840,   1840,   1840,   1840,   1872,   1872,   1872,   1872,
        // 368 - 375
        6157,   6157,   6157,   6157,   6157,   6157,   6157,   6157,
        // 376 - 383
        6157,   6157,   6157,   6157,   6157,   6157,   6157,   6157,
        // 384 - 391
        -12275, -12275, -12275, -12275, -12275, -12275, -12275, -12275,
        // 392 - 399
        -12275, -12275, -12275, -12275, -12275, -12275, -12275, -12275,
        // 400 - 407
        14353,  14353,  14353,  14353,  16401,  16401,  16401,  16401,
        // 408 - 415
        22547,  22547,  24595,  24595,  20497,  20497,  20497,  20497,
        // 416 - 423
        18449,  18449,  18449,  18449,  26643,  26643,  28691,  28691,
        // 424 - 431
        30739,  30739, -32749, -32749, -30701, -30701, -28653, -28653,
        // 432 - 439
        -26605, -26605, -24557, -24557, -22509, -22509, -20461, -20461,
        // 440 - 447
        8207,   8207,   8207,   8207,   8207,   8207,   8207,   8207,
        // 448 - 455
        72,     72,     72,     72,     72,     72,     72,     72,
        // 456 - 463
        72,     72,     72,     72,     72,     72,     72,     72,
        // 464 - 471
        72,     72,     72,     72,     72,     72,     72,     72,
        // 472 - 479
        72,     72,     72,     72,     72,     72,     72,     72,
        // 480 - 487
        72,     72,     72,     72,     72,     72,     72,     72,
        // 488 - 495
        72,     72,     72,     72,     72,     72,     72,     72,
        // 496 - 503
        72,     72,     72,     72,     72,     72,     72,     72,
        // 504 - 511
        72,     72,     72,     72,     72,     72,     72,     72,
        // 512 - 519
        104,    104,    104,    104,    104,    104,    104,    104,
        // 520 - 527
        104,    104,    104,    104,    104,    104,    104,    104,
        // 528 - 535
        104,    104,    104,    104,    104,    104,    104,    104,
        // 536 - 543
        104,    104,    104,    104,    104,    104,    104,    104,
        // 544 - 551
        104,    104,    104,    104,    104,    104,    104,    104,
        // 552 - 559
        104,    104,    104,    104,    104,    104,    104,    104,
        // 560 - 567
        104,    104,    104,    104,    104,    104,    104,    104,
        // 568 - 575
        104,    104,    104,    104,    104,    104,    104,    104,
        // 576 - 583
        4107,   4107,   4107,   4107,   4107,   4107,   4107,   4107,
        // 584 - 591
        4107,   4107,   4107,   4107,   4107,   4107,   4107,   4107,
        // 592 - 599
        4107,   4107,   4107,   4107,   4107,   4107,   4107,   4107,
        // 600 - 607
        4107,   4107,   4107,   4107,   4107,   4107,   4107,   4107,
        // 608 - 615
        266,    266,    266,    266,    266,    266,    266,    266,
        // 616 - 623
        266,    266,    266,    266,    266,    266,    266,    266,
        // 624 - 631
        266,    266,    266,    266,    266,    266,    266,    266,
        // 632 - 639
        266,    266,    266,    266,    266,    266,    266,    266,
        // 640 - 647
        298,    298,    298,    298,    298,    298,    298,    298,
        // 648 - 655
        298,    298,    298,    298,    298,    298,    298,    298,
        // 656 - 663
        298,    298,    298,    298,    298,    298,    298,    298,
        // 664 - 671
        298,    298,    298,    298,    298,    298,    298,    298,
        // 672 - 679
        524,    524,    524,    524,    524,    524,    524,    524,
        // 680 - 687
        524,    524,    524,    524,    524,    524,    524,    524,
        // 688 - 695
        556,    556,    556,    556,    556,    556,    556,    556,
        // 696 - 703
        556,    556,    556,    556,    556,    556,    556,    556,
        // 704 - 711
        136,    136,    136,    136,    136,    136,    136,    136,
        // 712 - 719
        136,    136,    136,    136,    136,    136,    136,    136,
        // 720 - 727
        136,    136,    136,    136,    136,    136,    136,    136,
        // 728 - 735
        136,    136,    136,    136,    136,    136,    136,    136,
        // 736 - 743
        136,    136,    136,    136,    136,    136,    136,    136,
        // 744 - 751
        136,    136,    136,    136,    136,    136,    136,    136,
        // 752 - 759
        136,    136,    136,    136,    136,    136,    136,    136,
        // 760 - 767
        136,    136,    136,    136,    136,    136,    136,    136,
        // 768 - 775
        168,    168,    168,    168,    168,    168,    168,    168,
        // 776 - 783
        168,    168,    168,    168,    168,    168,    168,    168,
        // 784 - 791
        168,    168,    168,    168,    168,    168,    168,    168,
        // 792 - 799
        168,    168,    168,    168,    168,    168,    168,    168,
        // 800 - 807
        168,    168,    168,    168,    168,    168,    168,    168,
        // 808 - 815
        168,    168,    168,    168,    168,    168,    168,    168,
        // 816 - 823
        168,    168,    168,    168,    168,    168,    168,    168,
        // 824 - 831
        168,    168,    168,    168,    168,    168,    168,    168,
        // 832 - 839
        460,    460,    460,    460,    460,    460,    460,    460,
        // 840 - 847
        460,    460,    460,    460,    460,    460,    460,    460,
        // 848 - 855
        492,    492,    492,    492,    492,    492,    492,    492,
        // 856 - 863
        492,    492,    492,    492,    492,    492,    492,    492,
        // 864 - 871
        2059,   2059,   2059,   2059,   2059,   2059,   2059,   2059,
        // 872 - 879
        2059,   2059,   2059,   2059,   2059,   2059,   2059,   2059,
        // 880 - 887
        2059,   2059,   2059,   2059,   2059,   2059,   2059,   2059,
        // 888 - 895
        2059,   2059,   2059,   2059,   2059,   2059,   2059,   2059,
        // 896 - 903
        200,    200,    200,    200,    200,    200,    200,    200,
        // 904 - 911
        200,    200,    200,    200,    200,    200,    200,    200,
        // 912 - 919
        200,    200,    200,    200,    200,    200,    200,    200,
        // 920 - 927
        200,    200,    200,    200,    200,    200,    200,    200,
        // 928 - 935
        200,    200,    200,    200,    200,    200,    200,    200,
        // 936 - 943
        200,    200,    200,    200,    200,    200,    200,    200,
        // 944 - 951
        200,    200,    200,    200,    200,    200,    200,    200,
        // 952 - 959
        200,    200,    200,    200,    200,    200,    200,    200,
        // 960 - 967
        232,    232,    232,    232,    232,    232,    232,    232,
        // 968 - 975
        232,    232,    232,    232,    232,    232,    232,    232,
        // 976 - 983
        232,    232,    232,    232,    232,    232,    232,    232,
        // 984 - 991
        232,    232,    232,    232,    232,    232,    232,    232,
        // 992 - 999
        232,    232,    232,    232,    232,    232,    232,    232,
        // 1000 - 1007
        232,    232,    232,    232,    232,    232,    232,    232,
        // 1008 - 1015
        232,    232,    232,    232,    232,    232,    232,    232,
        // 1016 - 1023
        232,    232,    232,    232,    232,    232,    232,    232,
    };

    // Additional make up codes for both White and Black runs
    static short additionalMakeup[] = {
        28679,  28679,  31752,  (short)32777,
        (short)33801,  (short)34825,  (short)35849,  (short)36873,
        (short)29703,  (short)29703,  (short)30727,  (short)30727,
        (short)37897,  (short)38921,  (short)39945,  (short)40969
    };

    // Initial black run look up table, uses the first 4 bits of a code
    static short initBlack[] = {
        // 0 - 7
        3226,  6412,    200,    168,    38,     38,    134,    134,
        // 8 - 15
        100,    100,    100,    100,    68,     68,     68,     68
    };

    //
    static short twoBitBlack[] = {292, 260, 226, 226};   // 0 - 3

    // Main black run table, using the last 9 bits of possible 13 bit code
    static short black[] = {
        // 0 - 7
        62,     62,     30,     30,     0,      0,      0,      0,
        // 8 - 15
        0,      0,      0,      0,      0,      0,      0,      0,
        // 16 - 23
        0,      0,      0,      0,      0,      0,      0,      0,
        // 24 - 31
        0,      0,      0,      0,      0,      0,      0,      0,
        // 32 - 39
        3225,   3225,   3225,   3225,   3225,   3225,   3225,   3225,
        // 40 - 47
        3225,   3225,   3225,   3225,   3225,   3225,   3225,   3225,
        // 48 - 55
        3225,   3225,   3225,   3225,   3225,   3225,   3225,   3225,
        // 56 - 63
        3225,   3225,   3225,   3225,   3225,   3225,   3225,   3225,
        // 64 - 71
        588,    588,    588,    588,    588,    588,    588,    588,
        // 72 - 79
        1680,   1680,  20499,  22547,  24595,  26643,   1776,   1776,
        // 80 - 87
        1808,   1808, -24557, -22509, -20461, -18413,   1904,   1904,
        // 88 - 95
        1936,   1936, -16365, -14317,    782,    782,    782,    782,
        // 96 - 103
        814,    814,    814,    814, -12269, -10221,  10257,  10257,
        // 104 - 111
        12305,  12305,  14353,  14353,  16403,  18451,   1712,   1712,
        // 112 - 119
        1744,   1744,  28691,  30739, -32749, -30701, -28653, -26605,
        // 120 - 127
        2061,   2061,   2061,   2061,   2061,   2061,   2061,   2061,
        // 128 - 135
        424,    424,    424,    424,    424,    424,    424,    424,
        // 136 - 143
        424,    424,    424,    424,    424,    424,    424,    424,
        // 144 - 151
        424,    424,    424,    424,    424,    424,    424,    424,
        // 152 - 159
        424,    424,    424,    424,    424,    424,    424,    424,
        // 160 - 167
        750,    750,    750,    750,   1616,   1616,   1648,   1648,
        // 168 - 175
        1424,   1424,   1456,   1456,   1488,   1488,   1520,   1520,
        // 176 - 183
        1840,   1840,   1872,   1872,   1968,   1968,   8209,   8209,
        // 184 - 191
        524,    524,    524,    524,    524,    524,    524,    524,
        // 192 - 199
        556,    556,    556,    556,    556,    556,    556,    556,
        // 200 - 207
        1552,   1552,   1584,   1584,   2000,   2000,   2032,   2032,
        // 208 - 215
        976,    976,   1008,   1008,   1040,   1040,   1072,   1072,
        // 216 - 223
        1296,   1296,   1328,   1328,    718,    718,    718,    718,
        // 224 - 231
        456,    456,    456,    456,    456,    456,    456,    456,
        // 232 - 239
        456,    456,    456,    456,    456,    456,    456,    456,
        // 240 - 247
        456,    456,    456,    456,    456,    456,    456,    456,
        // 248 - 255
        456,    456,    456,    456,    456,    456,    456,    456,
        // 256 - 263
        326,    326,    326,    326,    326,    326,    326,    326,
        // 264 - 271
        326,    326,    326,    326,    326,    326,    326,    326,
        // 272 - 279
        326,    326,    326,    326,    326,    326,    326,    326,
        // 280 - 287
        326,    326,    326,    326,    326,    326,    326,    326,
        // 288 - 295
        326,    326,    326,    326,    326,    326,    326,    326,
        // 296 - 303
        326,    326,    326,    326,    326,    326,    326,    326,
        // 304 - 311
        326,    326,    326,    326,    326,    326,    326,    326,
        // 312 - 319
        326,    326,    326,    326,    326,    326,    326,    326,
        // 320 - 327
        358,    358,    358,    358,    358,    358,    358,    358,
        // 328 - 335
        358,    358,    358,    358,    358,    358,    358,    358,
        // 336 - 343
        358,    358,    358,    358,    358,    358,    358,    358,
        // 344 - 351
        358,    358,    358,    358,    358,    358,    358,    358,
        // 352 - 359
        358,    358,    358,    358,    358,    358,    358,    358,
        // 360 - 367
        358,    358,    358,    358,    358,    358,    358,    358,
        // 368 - 375
        358,    358,    358,    358,    358,    358,    358,    358,
        // 376 - 383
        358,    358,    358,    358,    358,    358,    358,    358,
        // 384 - 391
        490,    490,    490,    490,    490,    490,    490,    490,
        // 392 - 399
        490,    490,    490,    490,    490,    490,    490,    490,
        // 400 - 407
        4113,   4113,   6161,   6161,    848,    848,    880,    880,
        // 408 - 415
        912,    912,    944,    944,    622,    622,    622,    622,
        // 416 - 423
        654,    654,    654,    654,   1104,   1104,   1136,   1136,
        // 424 - 431
        1168,   1168,   1200,   1200,   1232,   1232,   1264,   1264,
        // 432 - 439
        686,    686,    686,    686,   1360,   1360,   1392,   1392,
        // 440 - 447
        12,     12,     12,     12,     12,     12,     12,     12,
        // 448 - 455
        390,    390,    390,    390,    390,    390,    390,    390,
        // 456 - 463
        390,    390,    390,    390,    390,    390,    390,    390,
        // 464 - 471
        390,    390,    390,    390,    390,    390,    390,    390,
        // 472 - 479
        390,    390,    390,    390,    390,    390,    390,    390,
        // 480 - 487
        390,    390,    390,    390,    390,    390,    390,    390,
        // 488 - 495
        390,    390,    390,    390,    390,    390,    390,    390,
        // 496 - 503
        390,    390,    390,    390,    390,    390,    390,    390,
        // 504 - 511
        390,    390,    390,    390,    390,    390,    390,    390,
    };

    static byte twoDCodes[] = {
        // 0 - 7
        80,     88,     23,     71,     30,     30,     62,     62,
        // 8 - 15
        4,      4,      4,      4,      4,      4,      4,      4,
        // 16 - 23
        11,     11,     11,     11,     11,     11,     11,     11,
        // 24 - 31
        11,     11,     11,     11,     11,     11,     11,     11,
        // 32 - 39
        35,     35,     35,     35,     35,     35,     35,     35,
        // 40 - 47
        35,     35,     35,     35,     35,     35,     35,     35,
        // 48 - 55
        51,     51,     51,     51,     51,     51,     51,     51,
        // 56 - 63
        51,     51,     51,     51,     51,     51,     51,     51,
        // 64 - 71
        41,     41,     41,     41,     41,     41,     41,     41,
        // 72 - 79
        41,     41,     41,     41,     41,     41,     41,     41,
        // 80 - 87
        41,     41,     41,     41,     41,     41,     41,     41,
        // 88 - 95
        41,     41,     41,     41,     41,     41,     41,     41,
        // 96 - 103
        41,     41,     41,     41,     41,     41,     41,     41,
        // 104 - 111
        41,     41,     41,     41,     41,     41,     41,     41,
        // 112 - 119
        41,     41,     41,     41,     41,     41,     41,     41,
        // 120 - 127
        41,     41,     41,     41,     41,     41,     41,     41,
    };

    /**
     * @param fillOrder   The fill order of the compressed data bytes.
     * @param w
     * @param h
     */
    public TIFFFaxDecoder(long fillOrder, int w, int h) {
        this.fillOrder = fillOrder;
        this.w = w;
        this.h = h;

        this.bitPointer = 0;
        this.bytePointer = 0;
        this.prevChangingElems = new int[2*w];
        this.currChangingElems = new int[2*w];
    }

    /**
      * Reverses the bits in the array
      * @param b the bits to reverse
      *
      * @since 2.0.7
     */
    public static void reverseBits(byte[] b) {
        for (int k = 0; k < b.length; ++k)
            b[k] = flipTable[b[k] & 0xff];
    }

    // One-dimensional decoding methods

    public void decode1D(byte[] buffer, byte[] compData, int startX, int height) {
        this.data = compData;

        int lineOffset = 0;
        int scanlineStride = (w + 7)/8;

        bitPointer = 0;
        bytePointer = 0;

        for (int i = 0; i < height; i++) {
            decodeNextScanline(buffer, lineOffset, startX);
            lineOffset += scanlineStride;
        }
    }

    public void decodeNextScanline(byte[] buffer, int lineOffset, int bitOffset) {
        int bits = 0, code = 0, isT = 0;
        int current, entry, twoBits;
        boolean isWhite = true;

        // Initialize starting of the changing elements array
        changingElemSize = 0;

        // While scanline not complete
        while (bitOffset < w) {
            while (isWhite) {
                // White run
                current = nextNBits(10);
                entry = white[current];

                // Get the 3 fields from the entry
                isT = entry & 0x0001;
                bits = (entry >>> 1) & 0x0f;

                if (bits == 12) {          // Additional Make up code
                    // Get the next 2 bits
                    twoBits = nextLesserThan8Bits(2);
                    // Consolidate the 2 new bits and last 2 bits into 4 bits
                    current = ((current << 2) & 0x000c) | twoBits;
                    entry = additionalMakeup[current];
                    bits = (entry >>> 1) & 0x07;     // 3 bits 0000 0111
                    code  = (entry >>> 4) & 0x0fff;  // 12 bits
                    bitOffset += code; // Skip white run

                    updatePointer(4 - bits);
                } else if (bits == 0) {     // ERROR
                    throw new RuntimeException(MessageLocalization.getComposedMessage("invalid.code.encountered"));
                } else if (bits == 15) {    // EOL
                    throw new RuntimeException(MessageLocalization.getComposedMessage("eol.code.word.encountered.in.white.run"));
                } else {
                    // 11 bits - 0000 0111 1111 1111 = 0x07ff
                    code = (entry >>> 5) & 0x07ff;
                    bitOffset += code;

                    updatePointer(10 - bits);
                    if (isT == 0) {
                        isWhite = false;
                        currChangingElems[changingElemSize++] = bitOffset;
                    }
                }
            }

            // Check whether this run completed one width, if so
            // advance to next byte boundary for compression = 2.
            if (bitOffset == w) {
                if (compression == 2) {
                    advancePointer();
                }
                break;
            }

            while (!isWhite) {
                // Black run
                current = nextLesserThan8Bits(4);
                entry = initBlack[current];

                // Get the 3 fields from the entry
                isT = entry & 0x0001;
                bits = (entry >>> 1) & 0x000f;
                code = (entry >>> 5) & 0x07ff;

                if (code == 100) {
                    current = nextNBits(9);
                    entry = black[current];

                    // Get the 3 fields from the entry
                    isT = entry & 0x0001;
                    bits = (entry >>> 1) & 0x000f;
                    code = (entry >>> 5) & 0x07ff;

                    if (bits == 12) {
                        // Additional makeup codes
                        updatePointer(5);
                        current = nextLesserThan8Bits(4);
                        entry = additionalMakeup[current];
                        bits = (entry >>> 1) & 0x07;     // 3 bits 0000 0111
                        code  = (entry >>> 4) & 0x0fff;  // 12 bits

                        setToBlack(buffer, lineOffset, bitOffset, code);
                        bitOffset += code;

                        updatePointer(4 - bits);
                    } else if (bits == 15) {
                        // EOL code
                        throw new RuntimeException(MessageLocalization.getComposedMessage("eol.code.word.encountered.in.black.run"));
                    } else {
                        setToBlack(buffer, lineOffset, bitOffset, code);
                        bitOffset += code;

                        updatePointer(9 - bits);
                        if (isT == 0) {
                            isWhite = true;
                            currChangingElems[changingElemSize++] = bitOffset;
                        }
                    }
                } else if (code == 200) {
                    // Is a Terminating code
                    current = nextLesserThan8Bits(2);
                    entry = twoBitBlack[current];
                    code = (entry >>> 5) & 0x07ff;
                    bits = (entry >>> 1) & 0x0f;

                    setToBlack(buffer, lineOffset, bitOffset, code);
                    bitOffset += code;

                    updatePointer(2 - bits);
                    isWhite = true;
                    currChangingElems[changingElemSize++] = bitOffset;
                } else {
                    // Is a Terminating code
                    setToBlack(buffer, lineOffset, bitOffset, code);
                    bitOffset += code;

                    updatePointer(4 - bits);
                    isWhite = true;
                    currChangingElems[changingElemSize++] = bitOffset;
                }
            }

            // Check whether this run completed one width
            if (bitOffset == w) {
                if (compression == 2) {
                    advancePointer();
                }
                break;
            }
        }

        currChangingElems[changingElemSize++] = bitOffset;
    }

    // Two-dimensional decoding methods

    public void decode2D(byte[] buffer, byte compData[], int startX, int height, long tiffT4Options) {
        this.data = compData;
        compression = 3;

        bitPointer = 0;
        bytePointer = 0;

        int scanlineStride = (w + 7)/8;

        int a0, a1, b1, b2;
        int[] b = new int[2];
        int entry, code, bits;
        boolean isWhite;
        int currIndex = 0;
        int temp[];

        // fillBits - dealt with this in readEOL
        // 1D/2D encoding - dealt with this in readEOL

        // uncompressedMode - haven't dealt with this yet.


        oneD = (int)(tiffT4Options & 0x01);
        uncompressedMode = (int)((tiffT4Options & 0x02) >> 1);
        fillBits = (int)((tiffT4Options & 0x04) >> 2);

        // The data must start with an EOL code
        if (readEOL(true) != 1) {
            throw new RuntimeException(MessageLocalization.getComposedMessage("first.scanline.must.be.1d.encoded"));
        }

        int lineOffset = 0;
        int bitOffset;

        // Then the 1D encoded scanline data will occur, changing elements
        // array gets set.
        decodeNextScanline(buffer, lineOffset, startX);
        lineOffset += scanlineStride;

        for (int lines = 1; lines < height; lines++) {

            // Every line must begin with an EOL followed by a bit which
            // indicates whether the following scanline is 1D or 2D encoded.
            if (readEOL(false) == 0) {
                // 2D encoded scanline follows

                // Initialize previous scanlines changing elements, and
                // initialize current scanline's changing elements array
                temp = prevChangingElems;
                prevChangingElems = currChangingElems;
                currChangingElems = temp;
                currIndex = 0;

                // a0 has to be set just before the start of this scanline.
                a0 = -1;
                isWhite = true;
                bitOffset = startX;

                lastChangingElement = 0;

                while (bitOffset < w) {
                    // Get the next changing element
                    getNextChangingElement(a0, isWhite, b);

                    b1 = b[0];
                    b2 = b[1];

                    // Get the next seven bits
                    entry = nextLesserThan8Bits(7);

                    // Run these through the 2DCodes table
                    entry = twoDCodes[entry] & 0xff;

                    // Get the code and the number of bits used up
                    code = (entry & 0x78) >>> 3;
                    bits = entry & 0x07;

                    if (code == 0) {
                        if (!isWhite) {
                            setToBlack(buffer, lineOffset, bitOffset,
                            b2 - bitOffset);
                        }
                        bitOffset = a0 = b2;

                        // Set pointer to consume the correct number of bits.
                        updatePointer(7 - bits);
                    } else if (code == 1) {
                        // Horizontal
                        updatePointer(7 - bits);

                        // identify the next 2 codes.
                        int number;
                        if (isWhite) {
                            number = decodeWhiteCodeWord();
                            bitOffset += number;
                            currChangingElems[currIndex++] = bitOffset;

                            number = decodeBlackCodeWord();
                            setToBlack(buffer, lineOffset, bitOffset, number);
                            bitOffset += number;
                            currChangingElems[currIndex++] = bitOffset;
                        } else {
                            number = decodeBlackCodeWord();
                            setToBlack(buffer, lineOffset, bitOffset, number);
                            bitOffset += number;
                            currChangingElems[currIndex++] = bitOffset;

                            number = decodeWhiteCodeWord();
                            bitOffset += number;
                            currChangingElems[currIndex++] = bitOffset;
                        }

                        a0 = bitOffset;
                    } else if (code <= 8) {
                        // Vertical
                        a1 = b1 + (code - 5);

                        currChangingElems[currIndex++] = a1;

                        // We write the current color till a1 - 1 pos,
                        // since a1 is where the next color starts
                        if (!isWhite) {
                            setToBlack(buffer, lineOffset, bitOffset,
                            a1 - bitOffset);
                        }
                        bitOffset = a0 = a1;
                        isWhite = !isWhite;

                        updatePointer(7 - bits);
                    } else {
                        throw new RuntimeException(MessageLocalization.getComposedMessage("invalid.code.encountered.while.decoding.2d.group.3.compressed.data"));
                    }
                }

                // Add the changing element beyond the current scanline for the
                // other color too
                currChangingElems[currIndex++] = bitOffset;
                changingElemSize = currIndex;
            } else {
                // 1D encoded scanline follows
                decodeNextScanline(buffer, lineOffset, startX);
            }

            lineOffset += scanlineStride;
        }
    }

    public void decodeT6(byte[] buffer,
    byte[] compData,
    int startX,
    int height,
    long tiffT6Options) {
        this.data = compData;
        compression = 4;

        bitPointer = 0;
        bytePointer = 0;

        int scanlineStride = (w + 7)/8;

        int a0, a1, b1, b2;
        int entry, code, bits;
        boolean isWhite;
        int currIndex;
        int temp[];

        // Return values from getNextChangingElement
        int[] b = new int[2];

        // uncompressedMode - have written some code for this, but this
        // has not been tested due to lack of test images using this optional

        uncompressedMode = (int)((tiffT6Options & 0x02) >> 1);

        // Local cached reference
        int[] cce = currChangingElems;

        // Assume invisible preceding row of all white pixels and insert
        // both black and white changing elements beyond the end of this
        // imaginary scanline.
        changingElemSize = 0;
        cce[changingElemSize++] = w;
        cce[changingElemSize++] = w;

        int lineOffset = 0;
        int bitOffset;

        for (int lines = 0; lines < height; lines++) {
            // a0 has to be set just before the start of the scanline.
            a0 = -1;
            isWhite = true;

            // Assign the changing elements of the previous scanline to
            // prevChangingElems and start putting this new scanline's
            // changing elements into the currChangingElems.
            temp = prevChangingElems;
            prevChangingElems = currChangingElems;
            cce = currChangingElems = temp;
            currIndex = 0;

            // Start decoding the scanline at startX in the raster
            bitOffset = startX;

            // Reset search start position for getNextChangingElement
            lastChangingElement = 0;

            // Till one whole scanline is decoded
            escape:
            while (bitOffset < w && bytePointer < data.length - 1) {
                // Get the next changing element
                getNextChangingElement(a0, isWhite, b);
                b1 = b[0];
                b2 = b[1];

                // Get the next seven bits
                entry = nextLesserThan8Bits(7);
                // Run these through the 2DCodes table
                entry = twoDCodes[entry] & 0xff;

                // Get the code and the number of bits used up
                code = (entry & 0x78) >>> 3;
                bits = entry & 0x07;

                if (code == 0) { // Pass
                    // We always assume WhiteIsZero format for fax.
                    if (!isWhite) {
                        setToBlack(buffer, lineOffset, bitOffset,
                        b2 - bitOffset);
                    }
                    bitOffset = a0 = b2;

                    // Set pointer to only consume the correct number of bits.
                    updatePointer(7 - bits);
                } else if (code == 1) { // Horizontal
                    // Set pointer to only consume the correct number of bits.
                    updatePointer(7 - bits);

                    // identify the next 2 alternating color codes.
                    int number;
                    if (isWhite) {
                        // Following are white and black runs
                        number = decodeWhiteCodeWord();
                        bitOffset += number;
                        cce[currIndex++] = bitOffset;

                        number = decodeBlackCodeWord();
                        setToBlack(buffer, lineOffset, bitOffset, number);
                        bitOffset += number;
                        cce[currIndex++] = bitOffset;
                    } else {
                        // First a black run and then a white run follows
                        number = decodeBlackCodeWord();
                        setToBlack(buffer, lineOffset, bitOffset, number);
                        bitOffset += number;
                        cce[currIndex++] = bitOffset;

                        number = decodeWhiteCodeWord();
                        bitOffset += number;
                        cce[currIndex++] = bitOffset;
                    }

                    a0 = bitOffset;
                } else if (code <= 8) { // Vertical
                    a1 = b1 + (code - 5);
                    cce[currIndex++] = a1;

                    // We write the current color till a1 - 1 pos,
                    // since a1 is where the next color starts
                    if (!isWhite) {
                        setToBlack(buffer, lineOffset, bitOffset,
                        a1 - bitOffset);
                    }
                    bitOffset = a0 = a1;
                    isWhite = !isWhite;

                    updatePointer(7 - bits);
                } else if (code == 11) {
                    if (nextLesserThan8Bits(3) != 7) {
                        throw new InvalidImageException(MessageLocalization.getComposedMessage("invalid.code.encountered.while.decoding.2d.group.4.compressed.data"));
                    }

                    int zeros = 0;
                    boolean exit = false;

                    while (!exit) {
                        while (nextLesserThan8Bits(1) != 1) {
                            zeros++;
                        }

                        if (zeros > 5) {
                            // Exit code

                            // Zeros before exit code
                            zeros = zeros - 6;

                            if (!isWhite && (zeros > 0)) {
                                cce[currIndex++] = bitOffset;
                            }

                            // Zeros before the exit code
                            bitOffset += zeros;
                            if (zeros > 0) {
                                // Some zeros have been written
                                isWhite = true;
                            }

                            // Read in the bit which specifies the color of
                            // the following run
                            if (nextLesserThan8Bits(1) == 0) {
                                if (!isWhite) {
                                    cce[currIndex++] = bitOffset;
                                }
                                isWhite = true;
                            } else {
                                if (isWhite) {
                                    cce[currIndex++] = bitOffset;
                                }
                                isWhite = false;
                            }

                            exit = true;
                        }

                        if (zeros == 5) {
                            if (!isWhite) {
                                cce[currIndex++] = bitOffset;
                            }
                            bitOffset += zeros;

                            // Last thing written was white
                            isWhite = true;
                        } else {
                            bitOffset += zeros;

                            cce[currIndex++] = bitOffset;
                            setToBlack(buffer, lineOffset, bitOffset, 1);
                            ++bitOffset;

                            // Last thing written was black
                            isWhite = false;
                        }

                    }
                } else {
                    //[email protected]
                    //Microsoft TIFF renderers seem to treat unknown codes as line-breaks
                    //That is, they give up on the current line and move on to the next one
                    //set bitOffset to w to move on to the next scan line.
                    bitOffset = w;
                    updatePointer(7 - bits);
                }
            } // end loop

            // Add the changing element beyond the current scanline for the
            // other color too
            //make sure that the index does not exceed the bounds of the array
            if(currIndex < cce.length) 
            cce[currIndex++] = bitOffset;

            // Number of changing elements in this scanline.
            changingElemSize = currIndex;

            lineOffset += scanlineStride;
        }
    }

    private void setToBlack(byte[] buffer,
    int lineOffset, int bitOffset,
    int numBits) {
        int bitNum = 8*lineOffset + bitOffset;
        int lastBit = bitNum + numBits;

        int byteNum = bitNum >> 3;

        // Handle bits in first byte
        int shift = bitNum & 0x7;
        if (shift > 0) {
            int maskVal = 1 << (7 - shift);
            byte val = buffer[byteNum];
            while (maskVal > 0 && bitNum < lastBit) {
                val |= maskVal;
                maskVal >>= 1;
                ++bitNum;
            }
            buffer[byteNum] = val;
        }

        // Fill in 8 bits at a time
        byteNum = bitNum >> 3;
        while (bitNum < lastBit - 7) {
            buffer[byteNum++] = (byte)255;
            bitNum += 8;
        }

        // Fill in remaining bits
        while (bitNum < lastBit) {
            byteNum = bitNum >> 3;
            if ( recoverFromImageError && !(byteNum < buffer.length) ) {
                // do nothing
            } else {
                buffer[byteNum] |= 1 << (7 - (bitNum & 0x7));
            }
            ++bitNum;
        }
    }

    // Returns run length
    private int decodeWhiteCodeWord() {
        int current, entry, bits, isT, twoBits, code = -1;
        int runLength = 0;
        boolean isWhite = true;

        while (isWhite) {
            current = nextNBits(10);
            entry = white[current];

            // Get the 3 fields from the entry
            isT = entry & 0x0001;
            bits = (entry >>> 1) & 0x0f;

            if (bits == 12) {           // Additional Make up code
                // Get the next 2 bits
                twoBits = nextLesserThan8Bits(2);
                // Consolidate the 2 new bits and last 2 bits into 4 bits
                current = ((current << 2) & 0x000c) | twoBits;
                entry = additionalMakeup[current];
                bits = (entry >>> 1) & 0x07;     // 3 bits 0000 0111
                code = (entry >>> 4) & 0x0fff;   // 12 bits
                runLength += code;
                updatePointer(4 - bits);
            } else if (bits == 0) {     // ERROR
                throw new InvalidImageException(MessageLocalization.getComposedMessage("invalid.code.encountered"));
            } else if (bits == 15) {    // EOL
                if ( runLength == 0 ) {
                    isWhite = false;
                } else {
                    throw new RuntimeException(MessageLocalization.getComposedMessage("eol.code.word.encountered.in.white.run"));
                }
            } else {
                // 11 bits - 0000 0111 1111 1111 = 0x07ff
                code = (entry >>> 5) & 0x07ff;
                runLength += code;
                updatePointer(10 - bits);
                if (isT == 0) {
                    isWhite = false;
                }
            }
        }

        return runLength;
    }

    // Returns run length
    private int decodeBlackCodeWord() {
        int current, entry, bits, isT, code = -1;
        int runLength = 0;
        boolean isWhite = false;

        while (!isWhite) {
            current = nextLesserThan8Bits(4);
            entry = initBlack[current];

            // Get the 3 fields from the entry
            isT = entry & 0x0001;
            bits = (entry >>> 1) & 0x000f;
            code = (entry >>> 5) & 0x07ff;

            if (code == 100) {
                current = nextNBits(9);
                entry = black[current];

                // Get the 3 fields from the entry
                isT = entry & 0x0001;
                bits = (entry >>> 1) & 0x000f;
                code = (entry >>> 5) & 0x07ff;

                if (bits == 12) {
                    // Additional makeup codes
                    updatePointer(5);
                    current = nextLesserThan8Bits(4);
                    entry = additionalMakeup[current];
                    bits = (entry >>> 1) & 0x07;     // 3 bits 0000 0111
                    code  = (entry >>> 4) & 0x0fff;  // 12 bits
                    runLength += code;

                    updatePointer(4 - bits);
                } else if (bits == 15) {
                    // EOL code
                    throw new RuntimeException(MessageLocalization.getComposedMessage("eol.code.word.encountered.in.black.run"));
                } else {
                    runLength += code;
                    updatePointer(9 - bits);
                    if (isT == 0) {
                        isWhite = true;
                    }
                }
            } else if (code == 200) {
                // Is a Terminating code
                current = nextLesserThan8Bits(2);
                entry = twoBitBlack[current];
                code = (entry >>> 5) & 0x07ff;
                runLength += code;
                bits = (entry >>> 1) & 0x0f;
                updatePointer(2 - bits);
                isWhite = true;
            } else {
                // Is a Terminating code
                runLength += code;
                updatePointer(4 - bits);
                isWhite = true;
            }
        }

        return runLength;
    }

    private int readEOL(boolean isFirstEOL) {
        if (fillBits == 0) {
            int next12Bits = nextNBits(12);
            if (isFirstEOL && next12Bits == 0) {

                // Might have the case of EOL padding being used even
                // though it was not flagged in the T4Options field.
                // This was observed to be the case in TIFFs produced
                // by a well known vendor who shall remain nameless.

                if(nextNBits(4) == 1) {

                    // EOL must be padded: reset the fillBits flag.

                    fillBits = 1;
                    return 1;
                }
            }
            if(next12Bits != 1) {
                throw new RuntimeException(MessageLocalization.getComposedMessage("scanline.must.begin.with.eol.code.word"));
            }
        } else if (fillBits == 1) {

            // First EOL code word xxxx 0000 0000 0001 will occur
            // As many fill bits will be present as required to make
            // the EOL code of 12 bits end on a byte boundary.

            int bitsLeft = 8 - bitPointer;

            if (nextNBits(bitsLeft) != 0) {
                throw new RuntimeException(MessageLocalization.getComposedMessage("all.fill.bits.preceding.eol.code.must.be.0"));
            }

            // If the number of bitsLeft is less than 8, then to have a 12
            // bit EOL sequence, two more bytes are certainly going to be
            // required. The first of them has to be all zeros, so ensure
            // that.
            if (bitsLeft < 4) {
                if (nextNBits(8) != 0) {
                    throw new RuntimeException(MessageLocalization.getComposedMessage("all.fill.bits.preceding.eol.code.must.be.0"));
                }
            }

            // There might be a random number of fill bytes with 0s, so
            // loop till the EOL of 0000 0001 is found, as long as all
            // the bytes preceding it are 0's.
            int n;
            while ((n = nextNBits(8)) != 1) {
                // If not all zeros
                if (n != 0) {
                    throw new RuntimeException(MessageLocalization.getComposedMessage("all.fill.bits.preceding.eol.code.must.be.0"));
                }
            }
        }

        // If one dimensional encoding mode, then always return 1
        if (oneD == 0) {
            return 1;
        } else {
            // Otherwise for 2D encoding mode,
            // The next one bit signifies 1D/2D encoding of next line.
            return nextLesserThan8Bits(1);
        }
    }

    private void getNextChangingElement(int a0, boolean isWhite, int[] ret) {
        // Local copies of instance variables
        int[] pce = this.prevChangingElems;
        int ces = this.changingElemSize;

        // If the previous match was at an odd element, we still
        // have to search the preceeding element.
        // int start = lastChangingElement & ~0x1;
        int start = lastChangingElement > 0 ? lastChangingElement - 1 : 0;
        if (isWhite) {
            start &= ~0x1; // Search even numbered elements
        } else {
            start |= 0x1; // Search odd numbered elements
        }

        int i = start;
        for (; i < ces; i += 2) {
            int temp = pce[i];
            if (temp > a0) {
                lastChangingElement = i;
                ret[0] = temp;
                break;
            }
        }

        if (i + 1 < ces) {
            ret[1] = pce[i + 1];
        }
    }

    private int nextNBits(int bitsToGet) {
        byte b, next, next2next;
        int l = data.length - 1;
        int bp = this.bytePointer;

        if (fillOrder == 1) {
            b = data[bp];

            if (bp == l) {
                next = 0x00;
                next2next = 0x00;
            } else if ((bp + 1) == l) {
                next = data[bp + 1];
                next2next = 0x00;
            } else {
                next = data[bp + 1];
                next2next = data[bp + 2];
            }
        } else if (fillOrder == 2) {
            b = flipTable[data[bp] & 0xff];

            if (bp == l) {
                next = 0x00;
                next2next = 0x00;
            } else if ((bp + 1) == l) {
                next = flipTable[data[bp + 1] & 0xff];
                next2next = 0x00;
            } else {
                next = flipTable[data[bp + 1] & 0xff];
                next2next = flipTable[data[bp + 2] & 0xff];
            }
        } else {
            throw new RuntimeException(MessageLocalization.getComposedMessage("tiff.fill.order.tag.must.be.either.1.or.2"));
        }

        int bitsLeft = 8 - bitPointer;
        int bitsFromNextByte = bitsToGet - bitsLeft;
        int bitsFromNext2NextByte = 0;
        if (bitsFromNextByte > 8) {
            bitsFromNext2NextByte = bitsFromNextByte - 8;
            bitsFromNextByte = 8;
        }

        bytePointer++;

        int i1 = (b & table1[bitsLeft]) << (bitsToGet - bitsLeft);
        int i2 = (next & table2[bitsFromNextByte]) >>> (8 - bitsFromNextByte);

        int i3 = 0;
        if (bitsFromNext2NextByte != 0) {
            i2 <<= bitsFromNext2NextByte;
            i3 = (next2next & table2[bitsFromNext2NextByte]) >>>
            (8 - bitsFromNext2NextByte);
            i2 |= i3;
            bytePointer++;
            bitPointer = bitsFromNext2NextByte;
        } else {
            if (bitsFromNextByte == 8) {
                bitPointer = 0;
                bytePointer++;
            } else {
                bitPointer = bitsFromNextByte;
            }
        }

        int i = i1 | i2;
        return i;
    }

    private int nextLesserThan8Bits(int bitsToGet) {
        byte b = 0, next = 0;
        int l = data.length - 1;
        int bp = this.bytePointer;

        if (fillOrder == 1) {
            b = data[bp];
            if (bp == l) {
                next = 0x00;
            } else {
                next = data[bp + 1];
            }
        } else if (fillOrder == 2) {
            if ( recoverFromImageError && !(bp < data.length)  ) {
                // do nothing
            } else {
                b = flipTable[data[bp] & 0xff];
                if (bp == l) {
                    next = 0x00;
                } else {
                    next = flipTable[data[bp + 1] & 0xff];
                }
            }
        } else {
            throw new RuntimeException(MessageLocalization.getComposedMessage("tiff.fill.order.tag.must.be.either.1.or.2"));
        }

        int bitsLeft = 8 - bitPointer;
        int bitsFromNextByte = bitsToGet - bitsLeft;

        int shift = bitsLeft - bitsToGet;
        int i1, i2;
        if (shift >= 0) {
            i1 = (b & table1[bitsLeft]) >>> shift;
            bitPointer += bitsToGet;
            if (bitPointer == 8) {
                bitPointer = 0;
                bytePointer++;
            }
        } else {
            i1 = (b & table1[bitsLeft]) << (-shift);
            i2 = (next & table2[bitsFromNextByte]) >>> (8 - bitsFromNextByte);

            i1 |= i2;
            bytePointer++;
            bitPointer = bitsFromNextByte;
        }

        return i1;
    }

    // Move pointer backwards by given amount of bits
    private void updatePointer(int bitsToMoveBack) {
        int i = bitPointer - bitsToMoveBack;

        if (i < 0) {
            bytePointer--;
            bitPointer = 8 + i;
        } else {
            bitPointer = i;
        }
    }

    // Move to the next byte boundary
    private boolean advancePointer() {
        if (bitPointer != 0) {
            bytePointer++;
            bitPointer = 0;
        }

        return true;
    }

    public void setRecoverFromImageError(boolean recoverFromImageError) {
        this.recoverFromImageError = recoverFromImageError;
    }
}
    /**
     * Handles CCITTFAXDECODE filter
     */
    private static class Filter_CCITTFAXDECODE implements FilterHandler{
        public byte[] decode(byte[] b, PdfName filterName, PdfObject decodeParams, PdfDictionary streamDictionary) throws IOException {
            PdfNumber wn = (PdfNumber)PdfReader.getPdfObjectRelease(streamDictionary.get(PdfName.WIDTH));
            PdfNumber hn = (PdfNumber)PdfReader.getPdfObjectRelease(streamDictionary.get(PdfName.HEIGHT));
            if (wn == null || hn == null)
                throw new UnsupportedPdfException(MessageLocalization.getComposedMessage("filter.ccittfaxdecode.is.only.supported.for.images"));
            int width = wn.intValue();
            int height = hn.intValue();

            PdfDictionary param = decodeParams instanceof PdfDictionary ? (PdfDictionary)decodeParams : null;
            int k = 0;
            boolean blackIs1 = false;
            boolean byteAlign = false;
            if (param != null) {
                PdfNumber kn = param.getAsNumber(PdfName.K);
                if (kn != null)
                    k = kn.intValue();
                PdfBoolean bo = param.getAsBoolean(PdfName.BLACKIS1);
                if (bo != null)
                    blackIs1 = bo.booleanValue();
                bo = param.getAsBoolean(PdfName.ENCODEDBYTEALIGN);
                if (bo != null)
                    byteAlign = bo.booleanValue();
            }
            byte[] outBuf = new byte[(width + 7) / 8 * height];
            TIFFFaxDecompressor decoder = new TIFFFaxDecompressor();
            if (k == 0 || k > 0) {
                int tiffT4Options = k > 0 ? TIFFConstants.GROUP3OPT_2DENCODING : 0;
                tiffT4Options |= byteAlign ? TIFFConstants.GROUP3OPT_FILLBITS : 0;
                decoder.SetOptions(1, TIFFConstants.COMPRESSION_CCITTFAX3, tiffT4Options, 0);
                decoder.decodeRaw(outBuf, b, width, height);
                if (decoder.fails > 0) {
                    byte[] outBuf2 = new byte[(width + 7) / 8 * height];
                    int oldFails = decoder.fails;
                    decoder.SetOptions(1, TIFFConstants.COMPRESSION_CCITTRLE, tiffT4Options, 0);
                    decoder.decodeRaw(outBuf2, b, width, height);
                    if (decoder.fails < oldFails) {
                        outBuf = outBuf2;
                    }
                }
            }
            else {
                TIFFFaxDecoder deca = new TIFFFaxDecoder(1, width, height);
                deca.decodeT6(outBuf, b, 0, height, 0);
            }
            if (!blackIs1) {
                int len = outBuf.length;
                for (int t = 0; t < len; ++t) {
                    outBuf[t] ^= 0xff;
                }
            }
            b = outBuf;       
            return b;
        }
    }

JBIG2Decode
JBIG2是JBIG的改进版本。
JBIG(Joint Bi-level Image Experts Group,联合二值图像专家组)是发布二值图像编码标准的专家组。在官方来说,JBIG是ISO/IEC JTC1 SC29工作组1,这个工作组也负责JPEG标准,它是一套压缩算法,用来产生Web浏览器支持的以及典型地用于复杂图像(例如照片)的图像文件。  
JBIG已经发布了一个二值图像压缩的标准,二值图像是一种只用1bit来表达每个像素的颜色值的图像。这个标准也可用于对灰度图像以及每个像素使用有限个比特的彩色图像进行编码。JBIG专门用于使用传真编码发送的图像,它提供了比组3和组4的传真编码好的多的压缩。
算法如下(pdfbox):

/**
 * Decompresses data encoded using the JBIG2 standard, reproducing the original
 * monochrome (1 bit per pixel) image data (or an approximation of that data).
 *
 * Requires a JBIG2 plugin for Java Image I/O to be installed. A known working
 * plug-in is the Apache PDFBox JBIG2 plugin.
 *
 * @author Timo Boehme
 */
final class JBIG2Filter extends Filter
{
    private static final Log LOG = LogFactory.getLog(JBIG2Filter.class);

    private static boolean levigoLogged = false;

    private static synchronized void logLevigoDonated()
    {
        if (!levigoLogged)
        {
            LOG.info("The Levigo JBIG2 plugin has been donated to the Apache Foundation");
            LOG.info("and an improved version is available for download at "
                    + "https://pdfbox.apache.org/download.cgi");
            levigoLogged = true;
        }
    }

    @Override
    public DecodeResult decode(InputStream encoded, OutputStream decoded, COSDictionary
            parameters, int index, DecodeOptions options) throws IOException
    {
        ImageReader reader = findImageReader("JBIG2", "jbig2-imageio is not installed");
        if (reader.getClass().getName().contains("levigo"))
        {
            logLevigoDonated();
        }

        int bits = parameters.getInt(COSName.BITS_PER_COMPONENT, 1);
        COSDictionary params = getDecodeParams(parameters, index);

        ImageReadParam irp = reader.getDefaultReadParam();
        irp.setSourceSubsampling(options.getSubsamplingX(), options.getSubsamplingY(),
                options.getSubsamplingOffsetX(), options.getSubsamplingOffsetY());
        irp.setSourceRegion(options.getSourceRegion());
        options.setFilterSubsampled(true);

        COSStream globals = null;
        if (params != null)
        {
            globals = (COSStream) params.getDictionaryObject(COSName.JBIG2_GLOBALS);
        }

        ImageInputStream iis = null;
        try
        {
            if (globals != null)
            {
                iis = ImageIO.createImageInputStream(
                        new SequenceInputStream(globals.createInputStream(), encoded));
                reader.setInput(iis);
            }
            else
            {
                iis = ImageIO.createImageInputStream(encoded);
                reader.setInput(iis);
            }

            BufferedImage image;
            try
            {
                image = reader.read(0, irp);
            }
            catch (Exception e)
            {
                // wrap and rethrow any exceptions
                throw new IOException("Could not read JBIG2 image", e);
            }

            // I am assuming since JBIG2 is always black and white
            // depending on your renderer this might or might be needed
            if (image.getColorModel().getPixelSize() != bits)
            {
                if (bits != 1)
                {
                    LOG.warn("Attempting to handle a JBIG2 with more than 1-bit depth");
                }
                BufferedImage packedImage = new BufferedImage(image.getWidth(), image.getHeight(),
                        BufferedImage.TYPE_BYTE_BINARY);
                Graphics graphics = packedImage.getGraphics();
                graphics.drawImage(image, 0, 0, null);
                graphics.dispose();
                image = packedImage;
            }

            DataBuffer dBuf = image.getData().getDataBuffer();
            if (dBuf.getDataType() == DataBuffer.TYPE_BYTE)
            {
                decoded.write(((DataBufferByte) dBuf).getData());
            }
            else
            {
                throw new IOException("Unexpected image buffer type");
            }
        }
        finally
        {
            if (iis != null)
            {
                iis.close();
            }
            reader.dispose();
        }
        return new DecodeResult(parameters);
    }

    @Override
    public DecodeResult decode(InputStream encoded, OutputStream decoded,
                               COSDictionary parameters, int index) throws IOException
    {
        return decode(encoded, decoded, parameters, index, DecodeOptions.DEFAULT);
    }

    @Override
    protected void encode(InputStream input, OutputStream encoded, COSDictionary parameters)
            throws IOException
    {
        throw new UnsupportedOperationException("JBIG2 encoding not implemented");
    }
}

DCTDecode
该压缩方式(JPEG)应用于灰度图和彩色图的压缩。该压缩方式有损压缩。
算法如下(pdfbox):

/**
 * Decompresses data encoded using a DCT (discrete cosine transform)
 * technique based on the JPEG standard.
 *
 * @author John Hewson
 */
final class DCTFilter extends Filter
{
    private static final Log LOG = LogFactory.getLog(DCTFilter.class);

    private static final int POS_TRANSFORM = 11;
    private static final String ADOBE = "Adobe";

    @Override
    public DecodeResult decode(InputStream encoded, OutputStream decoded, COSDictionary
            parameters, int index, DecodeOptions options) throws IOException
    {
        ImageReader reader = findImageReader("JPEG", "a suitable JAI I/O image filter is not installed");
        ImageInputStream iis = null;
        try
        {
            iis = ImageIO.createImageInputStream(encoded);

            // skip one LF if there
            if (iis.read() != 0x0A)
            {
                iis.seek(0);
            }

            reader.setInput(iis);
            ImageReadParam irp = reader.getDefaultReadParam();
            irp.setSourceSubsampling(options.getSubsamplingX(), options.getSubsamplingY(),
                    options.getSubsamplingOffsetX(), options.getSubsamplingOffsetY());
            irp.setSourceRegion(options.getSourceRegion());
            options.setFilterSubsampled(true);

            String numChannels = getNumChannels(reader);

            // get the raster using horrible JAI workarounds
            ImageIO.setUseCache(false);
            Raster raster;

            // Strategy: use read() for RGB or "can't get metadata"
            // use readRaster() for CMYK and gray and as fallback if read() fails 
            // after "can't get metadata" because "no meta" file was CMYK
            if ("3".equals(numChannels) || numChannels.isEmpty())
            {
                try
                {
                    // I'd like to use ImageReader#readRaster but it is buggy and can't read RGB correctly
                    BufferedImage image = reader.read(0, irp);
                    raster = image.getRaster();
                }
                catch (IIOException e)
                {
                    // JAI can't read CMYK JPEGs using ImageReader#read or ImageIO.read but
                    // fortunately ImageReader#readRaster isn't buggy when reading 4-channel files
                    raster = reader.readRaster(0, irp);
                }
            }
            else
            {
                // JAI can't read CMYK JPEGs using ImageReader#read or ImageIO.read but
                // fortunately ImageReader#readRaster isn't buggy when reading 4-channel files
                raster = reader.readRaster(0, irp);
            }

            // special handling for 4-component images
            if (raster.getNumBands() == 4)
            {
                // get APP14 marker
                Integer transform;
                try
                {
                    transform = getAdobeTransform(reader.getImageMetadata(0));
                }
                catch (IIOException e)
                {
                    // we really tried asking nicely, now we're using brute force.
                    transform = getAdobeTransformByBruteForce(iis);
                }
                catch (NegativeArraySizeException e)
                {
                    // we really tried asking nicely, now we're using brute force.
                    transform = getAdobeTransformByBruteForce(iis);
                }
                int colorTransform = transform != null ? transform : 0;

                // 0 = Unknown (RGB or CMYK), 1 = YCbCr, 2 = YCCK
                switch (colorTransform)
                {
                    case 0:
                        // already CMYK
                        break;
                    case 1:
                        raster = fromYCbCrtoCMYK(raster);
                        break;
                    case 2:
                        raster = fromYCCKtoCMYK(raster);
                        break;
                    default:
                        throw new IllegalArgumentException("Unknown colorTransform");
                }
            }
            else if (raster.getNumBands() == 3)
            {
                // BGR to RGB
                raster = fromBGRtoRGB(raster);
            }

            DataBufferByte dataBuffer = (DataBufferByte)raster.getDataBuffer();
            decoded.write(dataBuffer.getData());
        }
        finally
        {
            if (iis != null)
            {
                iis.close();
            }
            reader.dispose();
        }
        return new DecodeResult(parameters);
    }

    @Override
    public DecodeResult decode(InputStream encoded, OutputStream decoded,
                               COSDictionary parameters, int index) throws IOException
    {
        return decode(encoded, decoded, parameters, index, DecodeOptions.DEFAULT);
    }

    // reads the APP14 Adobe transform tag and returns its value, or 0 if unknown
    private Integer getAdobeTransform(IIOMetadata metadata)
    {
        Element tree = (Element)metadata.getAsTree("javax_imageio_jpeg_image_1.0");
        Element markerSequence = (Element)tree.getElementsByTagName("markerSequence").item(0);
        NodeList app14AdobeNodeList = markerSequence.getElementsByTagName("app14Adobe");
        if (app14AdobeNodeList != null && app14AdobeNodeList.getLength() > 0)
        {
            Element adobe = (Element) app14AdobeNodeList.item(0);
            return Integer.parseInt(adobe.getAttribute("transform"));
        }
        return 0;
    }

    // See in https://github.com/haraldk/TwelveMonkeys
    // com.twelvemonkeys.imageio.plugins.jpeg.AdobeDCT class for structure of APP14 segment
    private int getAdobeTransformByBruteForce(ImageInputStream iis) throws IOException
    {
        int a = 0;
        iis.seek(0);
        int by;
        while ((by = iis.read()) != -1)
        {
            if (ADOBE.charAt(a) == by)
            {
                ++a;
                if (a != ADOBE.length())
                {
                    continue;
                }
                // match
                a = 0;
                long afterAdobePos = iis.getStreamPosition();
                iis.seek(iis.getStreamPosition() - 9);
                int tag = iis.readUnsignedShort();
                if (tag != 0xFFEE)
                {
                    iis.seek(afterAdobePos);
                    continue;
                }
                int len = iis.readUnsignedShort();
                if (len >= POS_TRANSFORM + 1)
                {
                    byte[] app14 = new byte[Math.max(len, POS_TRANSFORM + 1)];
                    if (iis.read(app14) >= POS_TRANSFORM + 1)
                    {
                        return app14[POS_TRANSFORM];
                    }
                }
            }
            else
            {
                a = 0;
            }
        }
        return 0;
    }

    // converts YCCK image to CMYK. YCCK is an equivalent encoding for
    // CMYK data, so no color management code is needed here, nor does the
    // PDF color space have to be consulted
    private WritableRaster fromYCCKtoCMYK(Raster raster)
    {
        WritableRaster writableRaster = raster.createCompatibleWritableRaster();

        int[] value = new int[4];
        for (int y = 0, height = raster.getHeight(); y < height; y++)
        {
            for (int x = 0, width = raster.getWidth(); x < width; x++)
            {
                raster.getPixel(x, y, value);

                // 4-channels 0..255
                float Y = value[0];
                float Cb = value[1];
                float Cr = value[2];
                float K = value[3];

                // YCCK to RGB, see http://software.intel.com/en-us/node/442744
                int r = clamp(Y + 1.402f * Cr - 179.456f);
                int g = clamp(Y - 0.34414f * Cb - 0.71414f * Cr + 135.45984f);
                int b = clamp(Y + 1.772f * Cb - 226.816f);

                // naive RGB to CMYK
                int cyan = 255 - r;
                int magenta = 255 - g;
                int yellow = 255 - b;

                // update new raster
                value[0] = cyan;
                value[1] = magenta;
                value[2] = yellow;
                value[3] = (int)K;
                writableRaster.setPixel(x, y, value);
            }
        }
        return writableRaster;
    }

    private WritableRaster fromYCbCrtoCMYK(Raster raster)
    {
        WritableRaster writableRaster = raster.createCompatibleWritableRaster();

        int[] value = new int[4];
        for (int y = 0, height = raster.getHeight(); y < height; y++)
        {
            for (int x = 0, width = raster.getWidth(); x < width; x++)
            {
                raster.getPixel(x, y, value);

                // 4-channels 0..255
                float Y = value[0];
                float Cb = value[1];
                float Cr = value[2];
                float K = value[3];

                // YCbCr to RGB, see http://www.equasys.de/colorconversion.html
                int r = clamp( (1.164f * (Y-16)) + (1.596f * (Cr - 128)) );
                int g = clamp( (1.164f * (Y-16)) + (-0.392f * (Cb-128)) + (-0.813f * (Cr-128)));
                int b = clamp( (1.164f * (Y-16)) + (2.017f * (Cb-128)));

                // naive RGB to CMYK
                int cyan = 255 - r;
                int magenta = 255 - g;
                int yellow = 255 - b;

                // update new raster
                value[0] = cyan;
                value[1] = magenta;
                value[2] = yellow;
                value[3] = (int)K;
                writableRaster.setPixel(x, y, value);
            }
        }
        return writableRaster;
    }

    // converts from BGR to RGB
    private WritableRaster fromBGRtoRGB(Raster raster)
    {
        WritableRaster writableRaster = raster.createCompatibleWritableRaster();

        int width = raster.getWidth();
        int height = raster.getHeight();
        int w3 = width * 3;
        int[] tab = new int[w3];
        //BEWARE: handling the full image at a time is slower than one line at a time        
        for (int y = 0; y < height; y++)
        {
            raster.getPixels(0, y, width, 1, tab);
            for (int off = 0; off < w3; off += 3)
            {
                int tmp = tab[off];
                tab[off] = tab[off + 2];
                tab[off + 2] = tmp;
            }
            writableRaster.setPixels(0, y, width, 1, tab);
        }
        return writableRaster;
    }

    // returns the number of channels as a string, or an empty string if there is an error getting the meta data
    private String getNumChannels(ImageReader reader)
    {
        try
        {
            IIOMetadata imageMetadata = reader.getImageMetadata(0);
            if (imageMetadata == null)
            {
                return "";
            }
            IIOMetadataNode metaTree = (IIOMetadataNode) imageMetadata.getAsTree("javax_imageio_1.0");
            Element numChannelsItem = (Element) metaTree.getElementsByTagName("NumChannels").item(0);
            if (numChannelsItem == null)
            {
                return "";
            }
            return numChannelsItem.getAttribute("value");
        }
        catch (IOException e)
        {
            return "";
        }        
        catch (NegativeArraySizeException e)
        {
            return "";
        }
    }    

    // clamps value to 0-255 range
    private int clamp(float value)
    {
        return (int)((value < 0) ? 0 : ((value > 255) ? 255 : value));
    }

    @Override
    protected void encode(InputStream input, OutputStream encoded, COSDictionary parameters)
            throws IOException
    {
        throw new UnsupportedOperationException("DCTFilter encoding not implemented, use the JPEGFactory methods instead");
    }
}

JPXDecode
该压缩方式是JPEG2000,JPEG 2000是基于小波变换的图像压缩标准,由Joint Photographic Experts Group组织创建和维护。JPEG 2000通常被认为是未来取代JPEG(基于离散余弦变换)的下一代图像压缩标准。JPEG 2000文件的副档名通常为.jp2,MIME类型是image/jp2。
JPEG2000的压缩比更高,而且不会产生原先的基于离散余弦变换的JPEG标准产生的块状模糊瑕疵。JPEG2000同时支持有损压缩和无损压缩。另外,JPEG2000也支持更复杂的渐进式显示和下载。
JPEG2000是国际标准化组织(ISO)发布的标准,文档代码为ISO/IEC 15444-1:2000。虽然JPEG2000在技术上有一定的优势,但是到目前为止(2006年),网络上采用JPEG2000技术制作的图像文件数量仍然很少,并且大多数的浏览器仍然没有内置支持JPEG2000图像文件的显示。但是,由于JPEG2000在无损压缩下仍然能有比较好的压缩率,所以JPEG2000在图像品质要求比较高的医学图像的分析和处理中已经有了一定程度的广泛应用。
算法如下(pdfbox):

/**
 * Decompress data encoded using the wavelet-based JPEG 2000 standard,
 * reproducing the original data.
 *
 * Requires the Java Advanced Imaging (JAI) Image I/O Tools to be installed from java.net, see
 * <a href="http://download.java.net/media/jai-imageio/builds/release/1.1/">jai-imageio</a>.
 * Alternatively you can build from the source available in the
 * <a href="https://java.net/projects/jai-imageio-core/">jai-imageio-core svn repo</a>.
 *
 * Mac OS X users should download the tar.gz file for linux and unpack it to obtain the
 * required jar files. The .so file can be safely ignored.
 *
 * @author John Hewson
 * @author Timo Boehme
 */
public final class JPXFilter extends Filter
{
    @Override
    public DecodeResult decode(InputStream encoded, OutputStream decoded, COSDictionary
            parameters, int index, DecodeOptions options) throws IOException
    {
        DecodeResult result = new DecodeResult(new COSDictionary());
        result.getParameters().addAll(parameters);
        BufferedImage image = readJPX(encoded, options, result);

        WritableRaster raster = image.getRaster();
        switch (raster.getDataBuffer().getDataType())
        {
            case DataBuffer.TYPE_BYTE:
                DataBufferByte byteBuffer = (DataBufferByte) raster.getDataBuffer();
                decoded.write(byteBuffer.getData());
                return result;

            case DataBuffer.TYPE_USHORT:
                DataBufferUShort wordBuffer = (DataBufferUShort) raster.getDataBuffer();
                for (short w : wordBuffer.getData())
                {
                    decoded.write(w >> 8);
                    decoded.write(w);
                }
                return result;

            default:
                throw new IOException("Data type " + raster.getDataBuffer().getDataType() + " not implemented");
        }
    }

    @Override
    public DecodeResult decode(InputStream encoded, OutputStream decoded,
                               COSDictionary parameters, int index) throws IOException
    {
        return decode(encoded, decoded, parameters, index, DecodeOptions.DEFAULT);
    }

    // try to read using JAI Image I/O
    private BufferedImage readJPX(InputStream input, DecodeOptions options, DecodeResult result) throws IOException
    {
        ImageReader reader = findImageReader("JPEG2000", "Java Advanced Imaging (JAI) Image I/O Tools are not installed");
        ImageInputStream iis = null;
        try
        {
            // PDFBOX-4121: ImageIO.createImageInputStream() is much slower
            iis = new MemoryCacheImageInputStream(input);

            reader.setInput(iis, true, true);
            ImageReadParam irp = reader.getDefaultReadParam();
            irp.setSourceRegion(options.getSourceRegion());
            irp.setSourceSubsampling(options.getSubsamplingX(), options.getSubsamplingY(),
                    options.getSubsamplingOffsetX(), options.getSubsamplingOffsetY());
            options.setFilterSubsampled(true);

            BufferedImage image;
            try
            {
                image = reader.read(0, irp);
            }
            catch (Exception e)
            {
                // wrap and rethrow any exceptions
                throw new IOException("Could not read JPEG 2000 (JPX) image", e);
            }

            COSDictionary parameters = result.getParameters();

            // "If the image stream uses the JPXDecode filter, this entry is optional
            // and shall be ignored if present"
            //
            // note that indexed color spaces make the BPC logic tricky, see PDFBOX-2204
            int bpc = image.getColorModel().getPixelSize() / image.getRaster().getNumBands();
            parameters.setInt(COSName.BITS_PER_COMPONENT, bpc);

            // "Decode shall be ignored, except in the case where the image is treated as a mask"
            if (!parameters.getBoolean(COSName.IMAGE_MASK, false))
            {
                parameters.setItem(COSName.DECODE, null);
            }

            // override dimensions, see PDFBOX-1735
            parameters.setInt(COSName.WIDTH, reader.getWidth(0));
            parameters.setInt(COSName.HEIGHT, reader.getHeight(0));

            // extract embedded color space
            if (!parameters.containsKey(COSName.COLORSPACE))
            {
                result.setColorSpace(new PDJPXColorSpace(image.getColorModel().getColorSpace()));
            }

            return image;
        }
        finally
        {
            if (iis != null)
            {
                iis.close();
            }
            reader.dispose();
        }
    }

    @Override
    protected void encode(InputStream input, OutputStream encoded, COSDictionary parameters)
            throws IOException
    {
        throw new UnsupportedOperationException("JPX encoding not implemented");
    }
}

Crypt
该Filter是文档级加密,单独对某个stream进行加密。加密方法将在后面的章节中描述。

总结:
本章主要描述了PDF中各类压缩方式,并贴出了对应的加解压缩算法,参考的来自itext,pdfbox,这两个开源的source,能支持绝大部分PDF,但是对于极个别特殊文件,它们加解密算法并不能达到100%正确,请各位注意。

猜你喜欢

转载自blog.csdn.net/steve_cui/article/details/81947208