001: /*
002: * $Id: LZWDecode.java,v 1.2 2007/12/20 18:33:32 rbair Exp $
003: *
004: * Copyright 2004 Sun Microsystems, Inc., 4150 Network Circle,
005: * Santa Clara, California 95054, U.S.A. All rights reserved.
006: *
007: * This library is free software; you can redistribute it and/or
008: * modify it under the terms of the GNU Lesser General Public
009: * License as published by the Free Software Foundation; either
010: * version 2.1 of the License, or (at your option) any later version.
011: *
012: * This library is distributed in the hope that it will be useful,
013: * but WITHOUT ANY WARRANTY; without even the implied warranty of
014: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
015: * Lesser General Public License for more details.
016: *
017: * You should have received a copy of the GNU Lesser General Public
018: * License along with this library; if not, write to the Free Software
019: * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
020: */
021:
022: package com.sun.pdfview.decode;
023:
024: import java.io.ByteArrayOutputStream;
025: import java.io.IOException;
026: import java.nio.ByteBuffer;
027:
028: import com.sun.pdfview.PDFObject;
029: import com.sun.pdfview.PDFParseException;
030:
031: /**
032: * decode an LZW-encoded array of bytes. LZW is a patented algorithm.
033: * Consult with Sun Legal before using this code in a commercial product.
034: *
035: * @author Mike Wessler
036: */
037: public class LZWDecode {
038: ByteBuffer buf;
039: int bytepos;
040: int bitpos;
041: byte[] dict[] = new byte[4096][];
042: int dictlen = 0;
043: int bitspercode = 9;
044: static int STOP = 257;
045: static int CLEARDICT = 256;
046:
047: /**
048: * initialize this decoder with an array of encoded bytes
049: * @param buf the buffer of bytes
050: */
051: private LZWDecode(ByteBuffer buf) throws PDFParseException {
052: for (int i = 0; i < 256; i++) {
053: dict[i] = new byte[1];
054: dict[i][0] = (byte) i;
055: }
056: dictlen = 258;
057: bitspercode = 9;
058: this .buf = buf;
059: bytepos = 0;
060: bitpos = 0;
061: }
062:
063: /**
064: * reset the dictionary to the initial 258 entries
065: */
066: private void resetDict() {
067: dictlen = 258;
068: bitspercode = 9;
069: }
070:
071: /**
072: * get the next code from the input stream
073: */
074: private int nextCode() {
075: int fillbits = bitspercode;
076: int value = 0;
077: if (bytepos >= buf.limit() - 1) {
078: return -1;
079: }
080: while (fillbits > 0) {
081: int nextbits = buf.get(bytepos); // bitsource
082: int bitsfromhere = 8 - bitpos; // how many bits can we take?
083: if (bitsfromhere > fillbits) { // don't take more than we need
084: bitsfromhere = fillbits;
085: }
086: value |= ((nextbits >> (8 - bitpos - bitsfromhere)) & (0xff >> (8 - bitsfromhere))) << (fillbits - bitsfromhere);
087: fillbits -= bitsfromhere;
088: bitpos += bitsfromhere;
089: if (bitpos >= 8) {
090: bitpos = 0;
091: bytepos++;
092: }
093: }
094: return value;
095: }
096:
097: /**
098: * decode the array.
099: * @return the uncompressed byte array
100: */
101: private ByteBuffer decode() throws PDFParseException {
102: // algorithm derived from:
103: // http://www.rasip.fer.hr/research/compress/algorithms/fund/lz/lzw.html
104: // and the PDFReference
105: int cW = CLEARDICT;
106: ByteArrayOutputStream baos = new ByteArrayOutputStream();
107: while (true) {
108: int pW = cW;
109: cW = nextCode();
110: if (cW == -1) {
111: throw new PDFParseException(
112: "Missed the stop code in LZWDecode!");
113: }
114: if (cW == STOP) {
115: break;
116: } else if (cW == CLEARDICT) {
117: resetDict();
118: // pW= -1;
119: } else if (pW == CLEARDICT) {
120: baos.write(dict[cW], 0, dict[cW].length);
121: } else {
122: if (cW < dictlen) { // it's a code in the dictionary
123: baos.write(dict[cW], 0, dict[cW].length);
124: byte[] p = new byte[dict[pW].length + 1];
125: System
126: .arraycopy(dict[pW], 0, p, 0,
127: dict[pW].length);
128: p[dict[pW].length] = dict[cW][0];
129: dict[dictlen++] = p;
130: } else { // not in the dictionary (should==dictlen)
131: // if (cW!=dictlen) {
132: // System.out.println("Got a bouncy code: "+cW+" (dictlen="+dictlen+")");
133: // }
134: byte[] p = new byte[dict[pW].length + 1];
135: System
136: .arraycopy(dict[pW], 0, p, 0,
137: dict[pW].length);
138: p[dict[pW].length] = p[0];
139: baos.write(p, 0, p.length);
140: dict[dictlen++] = p;
141: }
142: if (dictlen >= (1 << bitspercode) - 1
143: && bitspercode < 12) {
144: bitspercode++;
145: }
146: }
147: }
148: return ByteBuffer.wrap(baos.toByteArray());
149: }
150:
151: /**
152: * decode an array of LZW-encoded bytes to a byte array.
153: *
154: * @param buf the buffer of encoded bytes
155: * @param params parameters for the decoder (unused)
156: * @return the decoded uncompressed bytes
157: */
158: public static ByteBuffer decode(ByteBuffer buf, PDFObject params)
159: throws IOException {
160: // decode the array
161: LZWDecode me = new LZWDecode(buf);
162: ByteBuffer outBytes = me.decode();
163:
164: // undo a predictor algorithm, if any was used
165: if (params != null
166: && params.getDictionary().containsKey("Predictor")) {
167: Predictor predictor = Predictor.getPredictor(params);
168: if (predictor != null) {
169: outBytes = predictor.unpredict(outBytes);
170: }
171: }
172:
173: return outBytes;
174: }
175:
176: }
|