| /* ==================================================================== |
| Licensed to the Apache Software Foundation (ASF) under one or more |
| contributor license agreements. See the NOTICE file distributed with |
| this work for additional information regarding copyright ownership. |
| The ASF licenses this file to You 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.apache.poi.hdgf.streams; |
| |
| import java.io.ByteArrayInputStream; |
| import java.io.IOException; |
| |
| import org.apache.poi.hdgf.HDGFLZW; |
| import org.apache.poi.util.IOUtils; |
| |
| /** |
| * A StreamStore where the data on-disk is compressed, |
| * using the crazy Visio LZW |
| */ |
| public final class CompressedStreamStore extends StreamStore { |
| |
| //arbitrarily selected; may need to increase |
| private static final int MAX_RECORD_LENGTH = 64_000_000; |
| |
| /** The raw, compressed contents */ |
| private byte[] compressedContents; |
| /** |
| * We're not sure what this is, but it comes before the |
| * real contents in the de-compressed data |
| */ |
| private final byte[] blockHeader; |
| private boolean blockHeaderInContents; |
| |
| byte[] _getCompressedContents() { return compressedContents; } |
| byte[] _getBlockHeader() { return blockHeader; } |
| |
| /** |
| * Creates a new compressed StreamStore, which will handle |
| * the decompression. |
| */ |
| CompressedStreamStore(byte[] data, int offset, int length) throws IOException { |
| this(decompress(data,offset,length)); |
| compressedContents = IOUtils.safelyClone(data, offset, length, MAX_RECORD_LENGTH); |
| } |
| /** |
| * Handles passing the de-compressed data onto our superclass. |
| */ |
| private CompressedStreamStore(byte[][] decompressedData) { |
| super(decompressedData[1], 0, decompressedData[1].length); |
| blockHeader = decompressedData[0]; |
| } |
| |
| /** |
| * Some kinds of streams expect their 4 byte header to be |
| * on the front of the contents. |
| * They can call this to have it sorted. |
| */ |
| protected void copyBlockHeaderToContents() { |
| if(blockHeaderInContents) return; |
| |
| prependContentsWith(blockHeader); |
| blockHeaderInContents = true; |
| } |
| |
| |
| /** |
| * Decompresses the given data, returning it as header + contents |
| */ |
| public static byte[][] decompress(byte[] data, int offset, int length) throws IOException { |
| ByteArrayInputStream bais = new ByteArrayInputStream(data, offset, length); |
| |
| // Decompress |
| HDGFLZW lzw = new HDGFLZW(); |
| byte[] decompressed = lzw.decompress(bais); |
| |
| // Split into header and contents |
| byte[][] ret = new byte[2][]; |
| ret[0] = new byte[4]; |
| ret[1] = new byte[decompressed.length - 4]; |
| |
| System.arraycopy(decompressed, 0, ret[0], 0, 4); |
| System.arraycopy(decompressed, 4, ret[1], 0, ret[1].length); |
| |
| // All done |
| return ret; |
| } |
| } |