]> source.dussan.org Git - poi.git/commitdiff
First stab at hdgf, an implementation of the visio format. Basic support for processi...
authorNick Burch <nick@apache.org>
Mon, 18 Jun 2007 18:10:57 +0000 (18:10 +0000)
committerNick Burch <nick@apache.org>
Mon, 18 Jun 2007 18:10:57 +0000 (18:10 +0000)
git-svn-id: https://svn.apache.org/repos/asf/jakarta/poi/trunk@548428 13f79535-47bb-0310-9956-ffa450edef68

src/scratchpad/src/org/apache/poi/hdgf/HDGFDiagram.java [new file with mode: 0644]
src/scratchpad/src/org/apache/poi/hdgf/LZW4HDGF.java [new file with mode: 0644]
src/scratchpad/testcases/org/apache/poi/hdgf/TestLZW4HDGF.java [new file with mode: 0644]

diff --git a/src/scratchpad/src/org/apache/poi/hdgf/HDGFDiagram.java b/src/scratchpad/src/org/apache/poi/hdgf/HDGFDiagram.java
new file mode 100644 (file)
index 0000000..dfe46ac
--- /dev/null
@@ -0,0 +1,287 @@
+/* ====================================================================
+   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;
+
+import java.io.ByteArrayInputStream;
+import java.io.FileInputStream;
+import java.io.IOException;
+
+import org.apache.poi.poifs.filesystem.DocumentEntry;
+import org.apache.poi.poifs.filesystem.POIFSFileSystem;
+import org.apache.poi.util.LittleEndian;
+
+/**
+ * See
+ *  http://www.redferni.uklinux.net/visio/
+ *  http://www.gnome.ru/projects/docs/vsdocs.html
+ *  http://www.gnome.ru/projects/docs/slide1.png
+ *  http://www.gnome.ru/projects/docs/slide2.png
+ */
+public class HDGFDiagram {
+       private static final String VISIO_HEADER = "Visio (TM) Drawing\r\n";
+       
+       private POIFSFileSystem filesystem;
+       private byte[] _docstream;
+       
+       private short version;
+       private long docSize;
+       
+       private VisioPointer trailerPointer;
+       private PointerBlock trailer;
+       
+       public HDGFDiagram(POIFSFileSystem fs) throws IOException {
+               filesystem = fs;
+               
+               DocumentEntry docProps =
+                       (DocumentEntry)filesystem.getRoot().getEntry("VisioDocument");
+
+               // Grab the document stream
+               _docstream = new byte[docProps.getSize()];
+               filesystem.createDocumentInputStream("VisioDocument").read(_docstream);
+               
+               // Check it's really visio
+               String typeString = new String(_docstream, 0, 20);
+               if(! typeString.equals(VISIO_HEADER)) {
+                       throw new IllegalArgumentException("Wasn't a valid visio document, started with " + typeString);
+               }
+               
+               // Grab the version number, 0x1a -> 0x1b
+               version = LittleEndian.getShort(_docstream, 0x1a);
+               // Grab the document size, 0x1c -> 0x1f
+               docSize = LittleEndian.getUInt(_docstream, 0x1c);
+               // ??? 0x20 -> 0x23
+               
+               // Grab the pointer to the trailer
+               trailerPointer = VisioPointer.getPointerAt(_docstream, 0x24);
+               
+               // And now grab the trailer
+               trailer = new CompressedPointerBlock(trailerPointer, _docstream);
+       }
+       
+       public void debug() throws IOException {
+               System.err.println("Trailer is at " + trailerPointer.offset);
+               System.err.println("Trailer has type " + trailerPointer.type);
+               System.err.println("Trailer has length " + trailerPointer.length);
+               System.err.println("Trailer has format " + trailerPointer.format);
+               
+               for(int i=0; i<trailer.getPointers().length; i++) {
+                       VisioPointer ptr = trailer.getPointers()[i];
+                       
+                       System.err.println("Looking at pointer " + i);
+                       System.err.println("\tType is " + ptr.type + "\t\t" + Integer.toHexString(ptr.type));
+                       System.err.println("\tOffset is " + ptr.offset + "\t\t" + Long.toHexString(ptr.offset));
+                       System.err.println("\tAddress is " + ptr.address + "\t" + Long.toHexString(ptr.address));
+                       System.err.println("\tLength is " + ptr.length + "\t\t" + Long.toHexString(ptr.length));
+                       System.err.println("\tFormat is " + ptr.format + "\t\t" + Long.toHexString(ptr.format));
+                       System.err.println("\tCompressed is " + ptr.destinationCompressed());
+                       
+                       if(ptr.destinationHasPointers()) {
+                               PointerBlock pb = PointerBlock.createAppropriateBlock(ptr, _docstream);
+                               
+                               if(pb.getPointers() != null && pb.getPointers().length > 0) {
+                                       System.err.println("\tContains " + pb.getPointers().length + " other pointers");
+                                       for(int j=0; j<pb.getPointers().length; j++) {
+                                               VisioPointer sptr = pb.getPointers()[j];
+                                               System.err.println("\t\t" + j + " - Type is " + sptr.type + "\t\t" + Integer.toHexString(sptr.type));
+                                               System.err.println("\t\t" + j + " - Length is " + sptr.length + "\t\t" + Long.toHexString(sptr.length));
+                                       }
+                               }
+                       }
+                       
+                       if(ptr.destinationHasStrings()) {
+                               System.err.println("**strings**");
+                               PointerBlock pb = PointerBlock.createAppropriateBlock(ptr, _docstream);
+                               System.err.println(pb.contents.length);
+                       }
+               }
+       }
+       /**
+        * Will only work on Test_Visio-Some_Random_Text.vsd !
+        */
+       public void debugTestFile() throws Exception {
+               System.err.println();
+               
+               VisioPointer p61ee = trailer.pointers[8];
+               System.err.println(p61ee.type + " " + Integer.toHexString(p61ee.type));
+               PointerBlock pb61ee = PointerBlock.createAppropriateBlock(p61ee, _docstream);
+               
+               VisioPointer p4524 = pb61ee.pointers[4];
+               System.err.println(p4524.type + " " + Integer.toHexString(p4524.type));
+               PointerBlock pb4524 = PointerBlock.createAppropriateBlock(p4524, _docstream);
+               
+               VisioPointer p44d3 = pb4524.pointers[5];
+               System.err.println(p44d3.type + " " + Integer.toHexString(p44d3.type));
+               PointerBlock pb44d3 = PointerBlock.createAppropriateBlock(p44d3, _docstream);
+               
+               VisioPointer p4312 = pb44d3.pointers[1];
+               System.err.println(p4312.type + " " + Integer.toHexString(p4312.type));
+               PointerBlock pb4312 = PointerBlock.createAppropriateBlock(p4312, _docstream);
+               
+               VisioPointer p347f = pb4312.pointers[0];
+               System.err.println();
+               System.err.println(p347f.type + " " + Integer.toHexString(p347f.type));
+               System.err.println(p347f.offset + " " + Long.toHexString(p347f.offset));
+               System.err.println(p347f.length + " " + Long.toHexString(p347f.length));
+               System.err.println("Has Strings - " + p347f.destinationHasStrings());
+               System.err.println("Compressed - " + p347f.destinationCompressed());
+               
+               PointerBlock pb347f = PointerBlock.createAppropriateBlock(p347f, _docstream);
+       }
+       
+       /**
+        * For testing only
+        */
+       public static void main(String args[]) throws Exception {
+               HDGFDiagram hdgf = new HDGFDiagram(new POIFSFileSystem(new FileInputStream(args[0])));
+               hdgf.debug();
+               hdgf.debugTestFile();
+       }
+
+       /**
+        * A block containing lots of pointers to other blocks.
+        */
+       public static class PointerBlock {
+               protected VisioPointer pointer;
+               private byte[] contents;
+               
+               protected VisioPointer[] pointers;
+               
+               protected PointerBlock(VisioPointer pointer) {
+                       this.pointer = pointer;
+               }
+               protected PointerBlock(VisioPointer pointer, byte[] data) {
+                       this(pointer);
+                       
+                       processData(data, (int)pointer.offset, (int)pointer.length);
+               }
+               
+               protected static PointerBlock createAppropriateBlock(VisioPointer pointer, byte[] data) throws IOException {
+                       if(pointer.destinationCompressed()) {
+                               return new CompressedPointerBlock(pointer,data);
+                       } else {
+                               return new PointerBlock(pointer,data);
+                       }
+               }
+               
+               public VisioPointer[] getPointers() { return pointers; }
+               
+               /**
+                * Splits the data up into header + contents, and processes 
+                */
+               protected void processData(byte[] data, int offset, int len) {
+                       if(len > data.length - offset) {
+                               len = data.length - offset;
+                       }
+                       if(offset < 0) { len = 0; }
+                       
+                       contents = new byte[len];
+                       if(len > 0)
+                               System.arraycopy(data, offset, contents, 0, contents.length);
+                       
+                       // If we're of type 20, we have child pointers
+                       if(len > 0 && (pointer.type == 20 || pointer.destinationHasPointers())) {
+                               // Grab the offset to the number of pointers
+                               int nPointersAt = (int)LittleEndian.getUInt(contents, 0);
+                               int numPointers = (int)LittleEndian.getUInt(contents, nPointersAt);
+                               int unknownA = (int)LittleEndian.getUInt(contents, nPointersAt+4);
+                               
+                               pointers = new VisioPointer[numPointers];
+                               int pos = nPointersAt + 8;
+                               for(int i=0; i<numPointers; i++) {
+                                       pointers[i] = VisioPointer.getPointerAt(contents, pos);
+                                       pos += 18;
+                               }
+                       }
+                       
+                       // If we have strings, try to make sense of them
+                       if(len > 0 && (pointer.destinationHasStrings())) {
+                               for(int i=0; i<64; i+=1) {
+                                       short s = LittleEndian.getShort(contents, i);
+                                       long l = LittleEndian.getUInt(contents, i);
+                                       System.err.println(i + "\t" + s + "\t" + Integer.toHexString(s));
+                                       System.err.println(i + "\t" + l + "\t" + Long.toHexString(l));
+                               }
+                       }
+               }
+       }
+       
+       /**
+        * A block containing lots of pointers to other blocks, that
+        *  is itself compressed
+        */
+       public static class CompressedPointerBlock extends PointerBlock {
+               protected byte[] compressedContents;
+               private byte[] blockHeader = new byte[4];
+               
+               protected CompressedPointerBlock(VisioPointer pointer, byte[] data) throws IOException {
+                       super(pointer);
+                       
+                       compressedContents = new byte[(int)pointer.length];
+                       System.arraycopy(data, (int)pointer.offset, compressedContents, 0, compressedContents.length);
+                       
+                       // Decompress
+                       ByteArrayInputStream bais = new ByteArrayInputStream(compressedContents);
+                       
+//                     TIFFLZWDecoder lzw = new TIFFLZWDecoder();
+//                     byte[] out = new byte[4096]; 
+//                     contents = lzw.decode(compressedContents, out);
+                       
+                       LZW4HDGF lzw = new LZW4HDGF();
+                       byte[] decomp = lzw.decode(bais);
+                       System.arraycopy(decomp, 0, blockHeader, 0, 4);
+                       processData(decomp, 4, decomp.length-4);
+               }
+       }
+       
+       /**
+        * A visio pointer, for visio versions 6+
+        */
+       public static class VisioPointer {
+               private int type;
+               private long address;
+               private long offset;
+               private long length;
+               private short format;
+               
+               public boolean destinationHasStrings() {
+                       return (0x40 <= format && format < 0x50);
+               }
+               public boolean destinationHasPointers() {
+                       if(format == 0x1d || format == 0x1e) return true;
+                       return (0x50 <= format && format < 0x60);
+               }
+               public boolean destinationHasChunks() {
+                       return (0xd0 <= format && format < 0xd0);
+               }
+               
+               public boolean destinationCompressed() {
+                       // Apparently, it's the second least significant bit
+                       return (format & 2) > 0;
+               }
+
+               public static VisioPointer getPointerAt(byte[] data, int offset) {
+                       VisioPointer p = new VisioPointer();
+                       p.type = LittleEndian.getInt(data, offset+0);
+                       p.address = LittleEndian.getUInt(data, offset+4);
+                       p.offset = LittleEndian.getUInt(data, offset+8);
+                       p.length = LittleEndian.getUInt(data, offset+12);
+                       p.format = LittleEndian.getShort(data, offset+16);
+                       
+                       return p;
+               }
+       }
+}
diff --git a/src/scratchpad/src/org/apache/poi/hdgf/LZW4HDGF.java b/src/scratchpad/src/org/apache/poi/hdgf/LZW4HDGF.java
new file mode 100644 (file)
index 0000000..ad4e6de
--- /dev/null
@@ -0,0 +1,80 @@
+/**
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 3 of the License.
+ * 
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+package org.apache.poi.hdgf;
+
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+
+/**
+ * A decoder for the crazy LZW implementation used
+ *  in Visio.
+ * This is a port of vsd_inflate.c from vsdump
+ *  (http://www.gnome.ru/projects/vsdump_en.html)
+ */
+public class LZW4HDGF {
+
+public byte fromInt(int b) {
+       if(b < 128) return (byte)b;
+       return (byte)(b - 256);
+}
+
+public byte[] decode(InputStream src) throws IOException {
+       ByteArrayOutputStream res = new ByteArrayOutputStream();
+       int pos = 0;
+       int flag;
+       byte[] buffer = new byte[4096];
+       buffer[0] = 0;
+       
+       byte data;
+       int tmp;
+       int addr1, addr2; 
+       int len, pntr;
+
+       while ( (flag = src.read()) != -1 ) {
+               for (int mask = 1; mask < 0x100 ; mask <<= 1) {
+                       if ( (flag & mask) > 0) {
+                               if( (tmp = src.read()) != -1) {
+                                       buffer[(pos&4095)] = fromInt(tmp);
+                                       pos++;
+                                       res.write( new byte[] {fromInt(tmp)} );
+                               }
+                       } else {
+                               tmp = src.read();
+                               if(tmp == -1) break;
+                               addr1 = tmp;
+                                                            
+                               tmp = src.read();
+                               if(tmp == -1) break;
+                               addr2 = tmp;
+                               
+                               len = (addr2 & 15) + 3;
+                               pntr = (addr2 & 240)*16 + addr1;
+                
+                               if(pntr > 4078) {
+                                       pntr = pntr - 4078;
+                               } else {
+                                       pntr = pntr + 18;
+                               }
+                               
+                               for(int i=0; i<len; i++) {
+                                       buffer [(pos + i) & 4095] = buffer [(pntr + i) & 4095];
+                                       data = buffer[(pntr + i ) & 4095];
+                                       res.write(new byte[] {data});
+                               }
+                                    
+                               pos = pos + len;
+                       }
+               }
+    }
+    return res.toByteArray();
+}
+}
diff --git a/src/scratchpad/testcases/org/apache/poi/hdgf/TestLZW4HDGF.java b/src/scratchpad/testcases/org/apache/poi/hdgf/TestLZW4HDGF.java
new file mode 100644 (file)
index 0000000..c2576b2
--- /dev/null
@@ -0,0 +1,101 @@
+/* ====================================================================
+   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;
+
+import java.io.ByteArrayInputStream;
+
+import junit.framework.TestCase;
+
+public class TestLZW4HDGF extends TestCase {
+       public static final byte[] testTrailerComp = new byte[] {
+               123, -60, 2, -21, -16, 1, 0, 0, -72, -13, -16, 78, -32, -5, 1, 
+               0, 3, -21, -16, 10, 5, 4, -21, -16, 21, 9, -21, -16, 103, -21, 
+               -16, 34, -36, -1, 52, 15, 70, 15, 120, 88, 15, -7, -2, -28, -9, 
+               -123, 21, 0, 44, -122, 1, -4, 104, 15, -24, -13, 40, -98, 32, 
+               78, 102, -67, -1, -2, -30, 64, 40, -67, -113, -73, 116, -98, 
+               -85, 2, 66, 123, 9, 109, -85, 2, -89, 14, -56, -69, -83, -79, 
+               -34, -3, 120, 110, 75, -9, -10, 20, -6, -25, -12, 22, -21, -16, 
+               -12, -81, 67, 1, -128, -70, -21, -16, 84, -21, -16, 70, 0, 23, 
+               -21, -16, 76, 47, -40, 79, 1, -44, -21, -16, 32, 3, 18, 12, 17, 
+               -43, -68, 17, 16, -8, 21, 22, -1, -21, -16, -84, -1, -35, 79, 
+               -9, -10, 96, 0, 46, -21, -16, 44, -39, -41, 79, 1, 119, -13, 
+               -16, -106, -13, -16, 84, 0, 125, 26, -21, -16, 68, -38, 79, 1, 
+               17, 10, 0, -97, 50, 10, 0, 0, -42, -108, 15, 118, 31, 0, -3, 29, 
+               -21, -16, -100, -25, 79, 1, -18, 97, -36, 76, 16, -21, -16, 86, 
+               0, 36, -5, 1, -5, 79, 63, 1, -124, 98, 0, 0, 28, 3, 20, -34, -3, 
+               125, 33, -21, -16, 100, -4, 79, 1, -92, -91, 16, -22, 24, 19, 41, 
+               -21, -16, -44, -59, 16, 108, 100, 0, -21, 0, 71, -105, 18, 39, 85, 
+               17, -3, 79, 1, 95, -108, 113, 0, 0, 104, 3, 18, 49, 49, 17, -1, 64, 
+               85, 1, 0, 114, 0, 0, -93, -36, -21, -16, 100, 31, 0, 0, -40, -21, 
+               -16, -92, 66, 127, 85, 1, 98, 119, 0, 0, -48, 79, 18, -3, 50, -17, 
+               1, 67, 85, 1, 81, -127, 0, -41, 0, 14, 6, 4, 17, 63, -63, 17, 68, 
+               85, -65, 1, 30, -120, 0, 0, 42, 79, 18, 68, 126, -21, -16, -76, 69, 
+               85, 1, 102, -119, 72, 37, 0, 97, 33 };
+       public static final byte[] testTrailerDecomp = new byte[] {
+               -60, 2, 0, 0, 0, 1, 0, 0, -72, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 
+               0, 0, 0, 0, 0, 1, 0, 3, 0, 0, 0, 3, 0, 0, 0, 3, 0, 0, 0, 4, 0, 0, 
+               0, 9, 0, 0, 0, 103, 0, 0, 0, 34, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 
+               0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 
+               0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 
+               0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 
+               0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 
+               -123, 21, 0, 44, -123, 21, 0, 44, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 
+               0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 40, -98, 32, 78, 102, -67, 
+               -2, -30, 64, 40, -67, -113, -73, 116, -67, -2, -30, 64, 40, 66, 
+               123, 9, 109, -67, -2, -30, 64, 40, -98, 32, 78, 102, -67, -2, -30, 
+               64, 40, -67, -113, -73, 116, -67, -2, -30, 64, -56, -83, -79, 0, 0, 
+               0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 120, 110, 75, 1, 0, 0, 0, 
+               0, 0, 0, 0, 0, 20, 0, 0, 0, 0, 0, 0, 0, 22, 0, 0, 0, -12, -81, 67, 
+               1, -128, 0, 0, 0, 84, 0, 0, 0, 70, 0, 23, 0, 0, 0, 76, -40, 79, 1, 
+               -44, 0, 0, 0, 32, 0, 0, 0, 84, 0, 23, 0, 0, 0, -68, -40, 79, 1, -8, 
+               0, 0, 0, 32, 0, 0, 0, 84, 0, -1, 0, 0, 0, -84, -1, 79, 1, 0, 0, 0, 
+               0, 0, 0, 0, 0, 96, 0, 46, 0, 0, 0, 44, -39, 79, 1, 119, 1, 0, 0, 
+               -106, 1, 0, 0, 84, 0, 26, 0, 0, 0, 68, -38, 79, 1, 17, 3, 0, 0, 
+               50, 10, 0, 0, -42, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 
+               0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 
+               29, 0, 0, 0, -100, -25, 79, 1, -18, 97, 0, 0, -106, 0, 0, 0, 86, 0, 
+               36, 0, 0, 0, -12, -5, 79, 1, -124, 98, 0, 0, 28, 0, 0, 0, 84, 0, 0, 
+               0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 33, 0, 0, 0, 100, 
+               -4, 79, 1, -92, 98, 0, 0, 32, 0, 0, 0, 84, 0, 41, 0, 0, 0, -44, -4, 
+               79, 1, 108, 100, 0, 0, 71, 0, 0, 0, 86, 0, 39, 0, 0, 0, 68, -3, 79, 
+               1, -108, 113, 0, 0, 104, 0, 0, 0, 84, 0, 49, 0, 0, 0, -84, 64, 85, 
+               1, 0, 114, 0, 0, -93, 0, 0, 0, -42, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 
+               0, 0, 0, 0, 0, 0, 0, 0, 0, -40, 0, 0, 0, -92, 66, 85, 1, 98, 119, 
+               0, 0, -48, 1, 0, 0, 84, 0, 50, 0, 0, 0, 20, 67, 85, 1, 81, -127, 
+               0, 0, 14, 6, 0, 0, 84, 0, 63, 0, 0, 0, 100, 68, 85, 1, 30, -120, 
+               0, 0, 42, 1, 0, 0, 84, 0, 68, 0, 0, 0, -76, 69, 85, 1, 102, -119, 
+               0, 0, 42, 1, 0, 0, 84, 0, 0, 0, 0, 0
+       };
+       
+       public void testCounts() throws Exception {
+               assertEquals(339, testTrailerComp.length);
+               assertEquals(632, testTrailerDecomp.length);
+               
+               // Decode it using our engine
+               LZW4HDGF lzw2 = new LZW4HDGF();
+               byte[] dec = lzw2.decode(new ByteArrayInputStream(testTrailerComp));
+               
+               // Check it's of the right size
+               assertEquals(632, dec.length);
+               
+               // Now check it matches
+               for(int i=0; i<dec.length; i++) {
+                       if(dec[i] != testTrailerDecomp[i]) 
+                               System.err.println(i + "\t" + dec[i] + "\t" + testTrailerDecomp[i]);
+               }
+       }
+}