001/*
002 * Copyright 2002-2008 by Paulo Soares.
003 * 
004 * This code was originally released in 2001 by SUN (see class
005 * com.sun.media.imageioimpl.plugins.tiff.TIFFLZWDecompressor.java)
006 * using the BSD license in a specific wording. In a mail dating from
007 * January 23, 2008, Brian Burkhalter (@sun.com) gave us permission
008 * to use the code under the following version of the BSD license:
009 *
010 * Copyright (c) 2005 Sun Microsystems, Inc. All  Rights Reserved.
011 * 
012 * Redistribution and use in source and binary forms, with or without
013 * modification, are permitted provided that the following conditions
014 * are met: 
015 * 
016 * - Redistribution of source code must retain the above copyright 
017 *   notice, this  list of conditions and the following disclaimer.
018 * 
019 * - Redistribution in binary form must reproduce the above copyright
020 *   notice, this list of conditions and the following disclaimer in 
021 *   the documentation and/or other materials provided with the
022 *   distribution.
023 * 
024 * Neither the name of Sun Microsystems, Inc. or the names of 
025 * contributors may be used to endorse or promote products derived 
026 * from this software without specific prior written permission.
027 * 
028 * This software is provided "AS IS," without a warranty of any 
029 * kind. ALL EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND 
030 * WARRANTIES, INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY, 
031 * FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT, ARE HEREBY
032 * EXCLUDED. SUN MIDROSYSTEMS, INC. ("SUN") AND ITS LICENSORS SHALL 
033 * NOT BE LIABLE FOR ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF 
034 * USING, MODIFYING OR DISTRIBUTING THIS SOFTWARE OR ITS
035 * DERIVATIVES. IN NO EVENT WILL SUN OR ITS LICENSORS BE LIABLE FOR 
036 * ANY LOST REVENUE, PROFIT OR DATA, OR FOR DIRECT, INDIRECT, SPECIAL,
037 * CONSEQUENTIAL, INCIDENTAL OR PUNITIVE DAMAGES, HOWEVER CAUSED AND
038 * REGARDLESS OF THE THEORY OF LIABILITY, ARISING OUT OF THE USE OF OR
039 * INABILITY TO USE THIS SOFTWARE, EVEN IF SUN HAS BEEN ADVISED OF THE
040 * POSSIBILITY OF SUCH DAMAGES. 
041 * 
042 * You acknowledge that this software is not designed or intended for 
043 * use in the design, construction, operation or maintenance of any 
044 * nuclear facility.
045 */
046package com.itextpdf.text.pdf;
047import java.io.IOException;
048import java.io.OutputStream;
049import com.itextpdf.text.error_messages.MessageLocalization;
050
051import com.itextpdf.text.ExceptionConverter;
052/**
053 * A class for performing LZW decoding.
054 *
055 *
056 */
057public class LZWDecoder {
058    
059    byte stringTable[][];
060    byte data[] = null;
061    OutputStream uncompData;
062    int tableIndex, bitsToGet = 9;
063    int bytePointer, bitPointer;
064    int nextData = 0;
065    int nextBits = 0;
066    
067    int andTable[] = {
068        511,
069        1023,
070        2047,
071        4095
072    };
073    
074    public LZWDecoder() {
075    }
076    
077    /**
078     * Method to decode LZW compressed data.
079     *
080     * @param data            The compressed data.
081     * @param uncompData      Array to return the uncompressed data in.
082     */
083    public void decode(byte data[], OutputStream uncompData) {
084        
085        if(data[0] == (byte)0x00 && data[1] == (byte)0x01) {
086            throw new RuntimeException(MessageLocalization.getComposedMessage("lzw.flavour.not.supported"));
087        }
088        
089        initializeStringTable();
090        
091        this.data = data;
092        this.uncompData = uncompData;
093        
094        // Initialize pointers
095        bytePointer = 0;
096        bitPointer = 0;
097        
098        nextData = 0;
099        nextBits = 0;
100        
101        int code, oldCode = 0;
102        byte string[];
103        
104        while ((code = getNextCode()) != 257) {
105            
106            if (code == 256) {
107                
108                initializeStringTable();
109                code = getNextCode();
110                
111                if (code == 257) {
112                    break;
113                }
114                
115                writeString(stringTable[code]);
116                oldCode = code;
117                
118            } else {
119                
120                if (code < tableIndex) {
121                    
122                    string = stringTable[code];
123                    
124                    writeString(string);
125                    addStringToTable(stringTable[oldCode], string[0]);
126                    oldCode = code;
127                    
128                } else {
129                    
130                    string = stringTable[oldCode];
131                    string = composeString(string, string[0]);
132                    writeString(string);
133                    addStringToTable(string);
134                    oldCode = code;
135                }
136            }
137        }
138    }
139    
140    
141    /**
142     * Initialize the string table.
143     */
144    public void initializeStringTable() {
145        
146        stringTable = new byte[8192][];
147        
148        for (int i=0; i<256; i++) {
149            stringTable[i] = new byte[1];
150            stringTable[i][0] = (byte)i;
151        }
152        
153        tableIndex = 258;
154        bitsToGet = 9;
155    }
156    
157    /**
158     * Write out the string just uncompressed.
159     */
160    public void writeString(byte string[]) {
161        try {
162            uncompData.write(string);
163        }
164        catch (IOException e) {
165            throw new ExceptionConverter(e);
166        }
167    }
168    
169    /**
170     * Add a new string to the string table.
171     */
172    public void addStringToTable(byte oldString[], byte newString) {
173        int length = oldString.length;
174        byte string[] = new byte[length + 1];
175        System.arraycopy(oldString, 0, string, 0, length);
176        string[length] = newString;
177        
178        // Add this new String to the table
179        stringTable[tableIndex++] = string;
180        
181        if (tableIndex == 511) {
182            bitsToGet = 10;
183        } else if (tableIndex == 1023) {
184            bitsToGet = 11;
185        } else if (tableIndex == 2047) {
186            bitsToGet = 12;
187        }
188    }
189    
190    /**
191     * Add a new string to the string table.
192     */
193    public void addStringToTable(byte string[]) {
194        
195        // Add this new String to the table
196        stringTable[tableIndex++] = string;
197        
198        if (tableIndex == 511) {
199            bitsToGet = 10;
200        } else if (tableIndex == 1023) {
201            bitsToGet = 11;
202        } else if (tableIndex == 2047) {
203            bitsToGet = 12;
204        }
205    }
206    
207    /**
208     * Append <code>newString</code> to the end of <code>oldString</code>.
209     */
210    public byte[] composeString(byte oldString[], byte newString) {
211        int length = oldString.length;
212        byte string[] = new byte[length + 1];
213        System.arraycopy(oldString, 0, string, 0, length);
214        string[length] = newString;
215        
216        return string;
217    }
218    
219    // Returns the next 9, 10, 11 or 12 bits
220    public int getNextCode() {
221        // Attempt to get the next code. The exception is caught to make
222        // this robust to cases wherein the EndOfInformation code has been
223        // omitted from a strip. Examples of such cases have been observed
224        // in practice.
225        try {
226            nextData = (nextData << 8) | (data[bytePointer++] & 0xff);
227            nextBits += 8;
228            
229            if (nextBits < bitsToGet) {
230                nextData = (nextData << 8) | (data[bytePointer++] & 0xff);
231                nextBits += 8;
232            }
233            
234            int code =
235            (nextData >> (nextBits - bitsToGet)) & andTable[bitsToGet-9];
236            nextBits -= bitsToGet;
237            
238            return code;
239        } catch(ArrayIndexOutOfBoundsException e) {
240            // Strip not terminated as expected: return EndOfInformation code.
241            return 257;
242        }
243    }
244}