diff --git a/src/documentation/xdocs/poifs/fileformat.xml b/src/documentation/xdocs/poifs/fileformat.xml index 7042b5959..5e17a0e4d 100644 --- a/src/documentation/xdocs/poifs/fileformat.xml +++ b/src/documentation/xdocs/poifs/fileformat.xml @@ -465,8 +465,8 @@ public int getShort (byte[] rec) -2 - UK11 - Unknown Constant + SBAT_Block_Count + Number of big blocks holding the SBAT 0x0040 Integer 1 diff --git a/src/java/org/apache/poi/hssf/usermodel/HSSFWorkbook.java b/src/java/org/apache/poi/hssf/usermodel/HSSFWorkbook.java index a345c4433..99ee6fb38 100644 --- a/src/java/org/apache/poi/hssf/usermodel/HSSFWorkbook.java +++ b/src/java/org/apache/poi/hssf/usermodel/HSSFWorkbook.java @@ -64,6 +64,10 @@ import org.apache.poi.hssf.model.Sheet; import org.apache.poi.hssf.model.Workbook; import org.apache.poi.hssf.record.*; import org.apache.poi.poifs.filesystem.POIFSFileSystem; +import org.apache.poi.poifs.filesystem.Entry; +import org.apache.poi.poifs.filesystem.DirectoryEntry; +import org.apache.poi.poifs.filesystem.DocumentEntry; +import org.apache.poi.poifs.filesystem.DocumentInputStream; import org.apache.poi.util.POILogger; import java.io.ByteArrayInputStream; @@ -72,6 +76,7 @@ import java.io.InputStream; import java.io.OutputStream; import java.util.ArrayList; import java.util.List; +import java.util.Iterator; /** * High level representation of a workbook. This is the first object most users @@ -117,6 +122,18 @@ public class HSSFWorkbook */ private ArrayList names; + + /** + * holds whether or not to preserve other nodes in the POIFS. Used + * for macros and embedded objects. + */ + private boolean preserveNodes; + + /** + * if you do preserve the nodes, you'll need to hold the whole POIFS in + * memory. + */ + private POIFSFileSystem poifs; private static POILogger log = POILogFactory.getLogger(HSSFWorkbook.class); @@ -132,18 +149,31 @@ public class HSSFWorkbook names = new ArrayList(INITIAL_CAPACITY); } + public HSSFWorkbook(POIFSFileSystem fs) throws IOException { + this(fs,true); + } + /** * given a POI POIFSFileSystem object, read in its Workbook and populate the high and * low level models. If you're reading in a workbook...start here. * * @param fs the POI filesystem that contains the Workbook stream. + * @param preserveNodes whether to preseve other nodes, such as + * macros. This takes more memory, so only say yes if you + * need to. * @see org.apache.poi.poifs.filesystem.POIFSFileSystem * @exception IOException if the stream cannot be read */ - public HSSFWorkbook(POIFSFileSystem fs) + public HSSFWorkbook(POIFSFileSystem fs, boolean preserveNodes) throws IOException { + this.preserveNodes = preserveNodes; + + if (preserveNodes) { + this.poifs = fs; + } + sheets = new ArrayList(INITIAL_CAPACITY); names = new ArrayList(INITIAL_CAPACITY); @@ -175,20 +205,27 @@ public class HSSFWorkbook } } + public HSSFWorkbook(InputStream s) throws IOException { + this(s,true); + } + /** * Companion to HSSFWorkbook(POIFSFileSystem), this constructs the POI filesystem around your * inputstream. * * @param s the POI filesystem that contains the Workbook stream. + * @param preserveNodes whether to preseve other nodes, such as + * macros. This takes more memory, so only say yes if you + * need to. * @see org.apache.poi.poifs.filesystem.POIFSFileSystem * @see #HSSFWorkbook(POIFSFileSystem) * @exception IOException if the stream cannot be read */ - public HSSFWorkbook(InputStream s) + public HSSFWorkbook(InputStream s, boolean preserveNodes) throws IOException { - this((new POIFSFileSystem(s))); + this(new POIFSFileSystem(s), preserveNodes); } /** @@ -515,9 +552,16 @@ public class HSSFWorkbook { byte[] bytes = getBytes(); POIFSFileSystem fs = new POIFSFileSystem(); - + fs.createDocument(new ByteArrayInputStream(bytes), "Workbook"); + + if (preserveNodes) { + List excepts = new ArrayList(1); + excepts.add("Workbook"); + copyNodes(this.poifs,fs,excepts); + } fs.writeFilesystem(stream); + //poifs.writeFilesystem(stream); } /** @@ -548,12 +592,12 @@ public class HSSFWorkbook // sheetbytes.add((( HSSFSheet ) sheets.get(k)).getSheet().getSize()); totalsize += ((HSSFSheet) sheets.get(k)).getSheet().getSize(); } - if (totalsize < 4096) +/* if (totalsize < 4096) { totalsize = 4096; - } - byte[] data = new byte[totalsize]; - int pos = workbook.serialize(0, data); + }*/ + byte[] retval = new byte[totalsize]; + int pos = workbook.serialize(0, retval); // System.arraycopy(wb, 0, retval, 0, wb.length); for (int k = 0; k < sheets.size(); k++) @@ -562,13 +606,13 @@ public class HSSFWorkbook // byte[] sb = (byte[])sheetbytes.get(k); // System.arraycopy(sb, 0, retval, pos, sb.length); pos += ((HSSFSheet) sheets.get(k)).getSheet().serialize(pos, - data); // sb.length; + retval); // sb.length; } - for (int k = pos; k < totalsize; k++) +/* for (int k = pos; k < totalsize; k++) { - data[k] = 0; - } - return data; + retval[k] = 0; + }*/ + return retval; } public int addSSTString(String string) @@ -677,5 +721,57 @@ public class HSSFWorkbook removeName(index); } - + + /** + * Copies nodes from one POIFS to the other minus the excepts + * @param source is the source POIFS to copy from + * @param target is the target POIFS to copy to + * @param excepts is a list of Strings specifying what nodes NOT to copy + */ + private void copyNodes(POIFSFileSystem source, POIFSFileSystem target, + List excepts) throws IOException { + //System.err.println("CopyNodes called"); + + DirectoryEntry root = source.getRoot(); + DirectoryEntry newRoot = target.getRoot(); + + Iterator entries = root.getEntries(); + + while (entries.hasNext()) { + Entry entry = (Entry)entries.next(); + if (!isInList(entry.getName(), excepts)) { + copyNodeRecursively(entry,newRoot); + } + } + } + + private boolean isInList(String entry, List list) { + for (int k = 0; k < list.size(); k++) { + if (((String)list.get(k)).equals(entry)) { + return true; + } + } + return false; + } + + private void copyNodeRecursively(Entry entry, DirectoryEntry target) + throws IOException { + //System.err.println("copyNodeRecursively called with "+entry.getName()+ + // ","+target.getName()); + DirectoryEntry newTarget = null; + if (entry.isDirectoryEntry()) { + newTarget = target.createDirectory(entry.getName()); + Iterator entries = ((DirectoryEntry)entry).getEntries(); + + while (entries.hasNext()) { + copyNodeRecursively((Entry)entries.next(),newTarget); + } + } else { + DocumentEntry dentry = (DocumentEntry)entry; + DocumentInputStream dstream = new DocumentInputStream(dentry); + target.createDocument(dentry.getName(),dstream); + dstream.close(); + } + } + } diff --git a/src/java/org/apache/poi/poifs/filesystem/POIFSFileSystem.java b/src/java/org/apache/poi/poifs/filesystem/POIFSFileSystem.java index dbd473f23..7652753f6 100644 --- a/src/java/org/apache/poi/poifs/filesystem/POIFSFileSystem.java +++ b/src/java/org/apache/poi/poifs/filesystem/POIFSFileSystem.java @@ -269,6 +269,9 @@ public class POIFSFileSystem // set the small block allocation table start block header_block_writer.setSBATStart(sbtw.getSBAT().getStartBlock()); + // set the small block allocation table block count + header_block_writer.setSBATBlockCount(sbtw.getSBATBlockCount()); + // the header is now properly initialized. Make a list of // writers (the header block, followed by the documents, the // property table, the small block store, the small block diff --git a/src/java/org/apache/poi/poifs/storage/HeaderBlockConstants.java b/src/java/org/apache/poi/poifs/storage/HeaderBlockConstants.java index c2c440160..cfe4729c4 100644 --- a/src/java/org/apache/poi/poifs/storage/HeaderBlockConstants.java +++ b/src/java/org/apache/poi/poifs/storage/HeaderBlockConstants.java @@ -70,18 +70,19 @@ import org.apache.poi.util.ShortField; public interface HeaderBlockConstants { - public static final long _signature = 0xE11AB1A1E011CFD0L; - public static final int _bat_array_offset = 0x4c; - public static final int _max_bats_in_header = + public static final long _signature = 0xE11AB1A1E011CFD0L; + public static final int _bat_array_offset = 0x4c; + public static final int _max_bats_in_header = (POIFSConstants.BIG_BLOCK_SIZE - _bat_array_offset) / LittleEndianConsts.INT_SIZE; // useful offsets - public static final int _signature_offset = 0; - public static final int _bat_count_offset = 0x2C; - public static final int _property_start_offset = 0x30; - public static final int _sbat_start_offset = 0x3C; - public static final int _xbat_start_offset = 0x44; - public static final int _xbat_count_offset = 0x48; + public static final int _signature_offset = 0; + public static final int _bat_count_offset = 0x2C; + public static final int _property_start_offset = 0x30; + public static final int _sbat_start_offset = 0x3C; + public static final int _sbat_block_count_offset = 0x40; + public static final int _xbat_start_offset = 0x44; + public static final int _xbat_count_offset = 0x48; } // end public interface HeaderBlockConstants diff --git a/src/java/org/apache/poi/poifs/storage/HeaderBlockWriter.java b/src/java/org/apache/poi/poifs/storage/HeaderBlockWriter.java index 9253d1c61..c0c7a512f 100644 --- a/src/java/org/apache/poi/poifs/storage/HeaderBlockWriter.java +++ b/src/java/org/apache/poi/poifs/storage/HeaderBlockWriter.java @@ -88,6 +88,9 @@ public class HeaderBlockWriter // block allocation table's first big block) private IntegerField _sbat_start; + // number of big blocks holding the small block allocation table + private IntegerField _sbat_block_count; + // big block index for extension to the big block allocation table private IntegerField _xbat_start; private IntegerField _xbat_count; @@ -121,7 +124,8 @@ public class HeaderBlockWriter new IntegerField(0x38, 0x1000, _data); _sbat_start = new IntegerField(_sbat_start_offset, POIFSConstants.END_OF_CHAIN, _data); - new IntegerField(0x40, 1, _data); + _sbat_block_count = new IntegerField(_sbat_block_count_offset, 0, + _data); _xbat_start = new IntegerField(_xbat_start_offset, POIFSConstants.END_OF_CHAIN, _data); _xbat_count = new IntegerField(_xbat_count_offset, 0, _data); @@ -200,6 +204,17 @@ public class HeaderBlockWriter _sbat_start.set(startBlock, _data); } + /** + * Set count of SBAT blocks + * + * @param count the number of SBAT blocks + */ + + public void setSBATBlockCount(final int count) + { + _sbat_block_count.set(count, _data); + } + /** * For a given number of BAT blocks, calculate how many XBAT * blocks will be needed diff --git a/src/java/org/apache/poi/poifs/storage/SmallBlockTableWriter.java b/src/java/org/apache/poi/poifs/storage/SmallBlockTableWriter.java index 76b77b54b..80987d069 100644 --- a/src/java/org/apache/poi/poifs/storage/SmallBlockTableWriter.java +++ b/src/java/org/apache/poi/poifs/storage/SmallBlockTableWriter.java @@ -112,6 +112,17 @@ public class SmallBlockTableWriter _big_block_count = SmallDocumentBlock.fill(_small_blocks); } + /** + * Get the number of SBAT blocks + * + * @return number of SBAT big blocks + */ + + public int getSBATBlockCount() + { + return (_big_block_count + 15) / 16; + } + /** * Get the SBAT * diff --git a/src/testcases/org/apache/poi/poifs/storage/TestHeaderBlockWriter.java b/src/testcases/org/apache/poi/poifs/storage/TestHeaderBlockWriter.java index 30a9f35c5..f52107394 100644 --- a/src/testcases/org/apache/poi/poifs/storage/TestHeaderBlockWriter.java +++ b/src/testcases/org/apache/poi/poifs/storage/TestHeaderBlockWriter.java @@ -117,7 +117,7 @@ public class TestHeaderBlockWriter ( byte ) 0x00, ( byte ) 0x00, ( byte ) 0x00, ( byte ) 0x00, ( byte ) 0x00, ( byte ) 0x10, ( byte ) 0x00, ( byte ) 0x00, ( byte ) 0xFE, ( byte ) 0xFF, ( byte ) 0xFF, ( byte ) 0xFF, - ( byte ) 0x01, ( byte ) 0x00, ( byte ) 0x00, ( byte ) 0x00, + ( byte ) 0x00, ( byte ) 0x00, ( byte ) 0x00, ( byte ) 0x00, ( byte ) 0xFE, ( byte ) 0xFF, ( byte ) 0xFF, ( byte ) 0xFF, ( byte ) 0x00, ( byte ) 0x00, ( byte ) 0x00, ( byte ) 0x00, ( byte ) 0xFF, ( byte ) 0xFF, ( byte ) 0xFF, ( byte ) 0xFF, @@ -281,7 +281,7 @@ public class TestHeaderBlockWriter ( byte ) 0x00, ( byte ) 0x00, ( byte ) 0x00, ( byte ) 0x00, ( byte ) 0x00, ( byte ) 0x10, ( byte ) 0x00, ( byte ) 0x00, ( byte ) 0x67, ( byte ) 0x45, ( byte ) 0x23, ( byte ) 0x01, - ( byte ) 0x01, ( byte ) 0x00, ( byte ) 0x00, ( byte ) 0x00, + ( byte ) 0x00, ( byte ) 0x00, ( byte ) 0x00, ( byte ) 0x00, ( byte ) 0xFE, ( byte ) 0xFF, ( byte ) 0xFF, ( byte ) 0xFF, ( byte ) 0x00, ( byte ) 0x00, ( byte ) 0x00, ( byte ) 0x00, ( byte ) 0xFF, ( byte ) 0xFF, ( byte ) 0xFF, ( byte ) 0xFF, @@ -436,7 +436,7 @@ public class TestHeaderBlockWriter ( byte ) 0x00, ( byte ) 0x00, ( byte ) 0x00, ( byte ) 0x00, ( byte ) 0x00, ( byte ) 0x10, ( byte ) 0x00, ( byte ) 0x00, ( byte ) 0xFE, ( byte ) 0xFF, ( byte ) 0xFF, ( byte ) 0xFF, - ( byte ) 0x01, ( byte ) 0x00, ( byte ) 0x00, ( byte ) 0x00, + ( byte ) 0x00, ( byte ) 0x00, ( byte ) 0x00, ( byte ) 0x00, ( byte ) 0xFE, ( byte ) 0xFF, ( byte ) 0xFF, ( byte ) 0xFF, ( byte ) 0x00, ( byte ) 0x00, ( byte ) 0x00, ( byte ) 0x00, ( byte ) 0xFF, ( byte ) 0xFF, ( byte ) 0xFF, ( byte ) 0xFF, @@ -597,7 +597,7 @@ public class TestHeaderBlockWriter ( byte ) 0x00, ( byte ) 0x00, ( byte ) 0x00, ( byte ) 0x00, ( byte ) 0x00, ( byte ) 0x10, ( byte ) 0x00, ( byte ) 0x00, ( byte ) 0xFE, ( byte ) 0xFF, ( byte ) 0xFF, ( byte ) 0xFF, - ( byte ) 0x01, ( byte ) 0x00, ( byte ) 0x00, ( byte ) 0x00, + ( byte ) 0x00, ( byte ) 0x00, ( byte ) 0x00, ( byte ) 0x00, ( byte ) 0xFE, ( byte ) 0xFF, ( byte ) 0xFF, ( byte ) 0xFF, ( byte ) 0x00, ( byte ) 0x00, ( byte ) 0x00, ( byte ) 0x00, ( byte ) 0x67, ( byte ) 0x45, ( byte ) 0x23, ( byte ) 0x01, @@ -744,7 +744,7 @@ public class TestHeaderBlockWriter ( byte ) 0x00, ( byte ) 0x00, ( byte ) 0x00, ( byte ) 0x00, ( byte ) 0x00, ( byte ) 0x10, ( byte ) 0x00, ( byte ) 0x00, ( byte ) 0xFE, ( byte ) 0xFF, ( byte ) 0xFF, ( byte ) 0xFF, - ( byte ) 0x01, ( byte ) 0x00, ( byte ) 0x00, ( byte ) 0x00, + ( byte ) 0x00, ( byte ) 0x00, ( byte ) 0x00, ( byte ) 0x00, ( byte ) 0xFE, ( byte ) 0xFF, ( byte ) 0xFF, ( byte ) 0xFF, ( byte ) 0x00, ( byte ) 0x00, ( byte ) 0x00, ( byte ) 0x00, ( byte ) 0x67, ( byte ) 0x45, ( byte ) 0x23, ( byte ) 0x01, @@ -891,7 +891,7 @@ public class TestHeaderBlockWriter ( byte ) 0x00, ( byte ) 0x00, ( byte ) 0x00, ( byte ) 0x00, ( byte ) 0x00, ( byte ) 0x10, ( byte ) 0x00, ( byte ) 0x00, ( byte ) 0xFE, ( byte ) 0xFF, ( byte ) 0xFF, ( byte ) 0xFF, - ( byte ) 0x01, ( byte ) 0x00, ( byte ) 0x00, ( byte ) 0x00, + ( byte ) 0x00, ( byte ) 0x00, ( byte ) 0x00, ( byte ) 0x00, ( byte ) 0x67, ( byte ) 0x46, ( byte ) 0x23, ( byte ) 0x01, ( byte ) 0x02, ( byte ) 0x00, ( byte ) 0x00, ( byte ) 0x00, ( byte ) 0x67, ( byte ) 0x45, ( byte ) 0x23, ( byte ) 0x01,