/* * Copyright 2006-2017 ICEsoft Technologies Canada Corp. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the * License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an "AS * IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either * express or implied. See the License for the specific language * governing permissions and limitations under the License. */ package org.icepdf.core.pobjects.filters; import org.icepdf.core.io.BitStream; import org.icepdf.core.pobjects.Name; import org.icepdf.core.util.Library; import java.io.IOException; import java.util.HashMap; import java.util.Stack; /** * @author Mark Collette * @since 2.0 */ public class LZWDecode extends ChunkingInputStream { public static final Name DECODEPARMS_KEY = new Name("DecodeParms"); public static final Name EARLYCHANGE_KEY = new Name("EarlyChange"); private BitStream inb; private int earlyChange; private int code; private int old_code; private boolean firstTime; private int code_len; private int last_code; private Code[] codes; public LZWDecode(BitStream inb, Library library, HashMap entries) { this.inb = inb; this.earlyChange = 1; // Default value HashMap decodeParmsDictionary = library.getDictionary(entries, DECODEPARMS_KEY); if (decodeParmsDictionary != null) { Number earlyChangeNumber = library.getNumber(decodeParmsDictionary, EARLYCHANGE_KEY); if (earlyChangeNumber != null) { this.earlyChange = earlyChangeNumber.intValue(); } } code = 0; old_code = 0; firstTime = true; initCodeTable(); setBufferSize(4096); } protected int fillInternalBuffer() throws IOException { int numRead = 0; // start decompression, haven't tried to optimized this one yet for // speed or for memory. if (firstTime) { firstTime = false; old_code = code = inb.getBits(code_len); } else if (inb.atEndOfFile()) return -1; do { if (code == 256) { initCodeTable(); } else if (code == 257) { break; } else { if (codes[code] != null) { Stack stack = new Stack(); codes[code].getString(stack); Code c = (Code) stack.pop(); addToBuffer(c.c, numRead); numRead++; //System.err.println((char)c.c); byte first = c.c; while (!stack.empty()) { c = (Code) stack.pop(); addToBuffer(c.c, numRead); numRead++; //System.err.println((char)c.c); } // while (codes[last_code]!=null) last_code++; codes[last_code++] = new Code(codes[old_code], first); } else { //System.err.println("MISS: "+last_code+" "+code); if (code != last_code) throw new RuntimeException("LZWDecode failure"); Stack stack = new Stack(); codes[old_code].getString(stack); Code c = (Code) stack.pop(); addToBuffer(c.c, numRead); numRead++; //System.err.println((char)c.c); byte first = c.c; while (!stack.empty()) { c = (Code) stack.pop(); addToBuffer(c.c, numRead); numRead++; //System.err.println((char)c.c); } addToBuffer(first, numRead); numRead++; codes[code] = new Code(codes[old_code], first); last_code++; } } if (code_len < 12 && last_code == (1 << code_len) - earlyChange) { //System.err.println(last_code+" "+code_len); code_len++; } old_code = code; code = inb.getBits(code_len); if (inb.atEndOfFile()) break; } while (numRead < buffer.length); return numRead; } private void initCodeTable() { code_len = 9; last_code = 257; codes = new Code[4096]; for (int i = 0; i < 256; i++) codes[i] = new Code(null, (byte) i); } private void addToBuffer(byte b, int offset) { if (offset >= buffer.length) { // Should never happen byte[] bufferNew = new byte[buffer.length * 2]; System.arraycopy(buffer, 0, bufferNew, 0, buffer.length); buffer = bufferNew; } buffer[offset] = b; } public void close() throws IOException { super.close(); if (inb != null) { inb.close(); inb = null; } } /** * Utility class for decode methods. */ private static class Code { Code prefix; byte c; Code(Code p, byte cc) { prefix = p; c = cc; } @SuppressWarnings("unchecked") void getString(Stack s) { s.push(this); if (prefix != null) prefix.getString(s); } } }