From c7f924e3773c18640262ba073335f5cf1df2548b Mon Sep 17 00:00:00 2001 From: Nick Burch Date: Fri, 18 Jul 2008 18:22:25 +0000 Subject: [PATCH] Partial support for .xlsm files (bug #45431), but still not quite there as they seem to have some hidden reference we don't know about to update git-svn-id: https://svn.apache.org/repos/asf/poi/branches/ooxml@677990 13f79535-47bb-0310-9956-ffa450edef68 --- .../org/apache/poi/xssf/model/BinaryPart.java | 67 ++++++++++++++++++ .../org/apache/poi/xssf/model/XSSFModel.java | 9 +++ .../poi/xssf/usermodel/XSSFWorkbook.java | 43 ++++++++++- .../poi/xssf/usermodel/TestXSSFBugs.java | 51 +++++++++++++ .../org/apache/poi/hssf/data/45431.xlsm | Bin 0 -> 13817 bytes 5 files changed, 167 insertions(+), 3 deletions(-) create mode 100644 src/ooxml/java/org/apache/poi/xssf/model/BinaryPart.java create mode 100644 src/testcases/org/apache/poi/hssf/data/45431.xlsm diff --git a/src/ooxml/java/org/apache/poi/xssf/model/BinaryPart.java b/src/ooxml/java/org/apache/poi/xssf/model/BinaryPart.java new file mode 100644 index 0000000000..9582b8a61d --- /dev/null +++ b/src/ooxml/java/org/apache/poi/xssf/model/BinaryPart.java @@ -0,0 +1,67 @@ +/* ==================================================================== + 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.xssf.model; + +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; + +/** + * An implementation of XSSFModel for binary parts of + * the file, eg images or vba macros + */ +public class BinaryPart implements XSSFModel { + private byte[] data; + + public BinaryPart(InputStream in) throws IOException { + readFrom(in); + } + + /** + * Fetch the contents of the binary part + */ + public byte[] getContents() { + return data; + } + /** + * Changes the contents of the binary part + */ + public void setContents(byte[] data) { + this.data = data; + } + + /** + * Reads the contents of the binary part in. + */ + public void readFrom(InputStream is) throws IOException { + int read = 0; + byte[] buffer = new byte[4096]; + ByteArrayOutputStream baos = new ByteArrayOutputStream(); + + while( (read = is.read(buffer)) != -1 ) { + if(read > 0) { + baos.write(buffer, 0, read); + } + } + data = baos.toByteArray(); + } + + public void writeTo(OutputStream out) throws IOException { + out.write(data); + } +} diff --git a/src/ooxml/java/org/apache/poi/xssf/model/XSSFModel.java b/src/ooxml/java/org/apache/poi/xssf/model/XSSFModel.java index eedf391179..03d08c030f 100644 --- a/src/ooxml/java/org/apache/poi/xssf/model/XSSFModel.java +++ b/src/ooxml/java/org/apache/poi/xssf/model/XSSFModel.java @@ -20,6 +20,15 @@ import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; +import org.apache.poi.xssf.usermodel.XSSFWorkbook.XSSFRelation; + +/** + * Common interface for XSSF models, which deal with + * parts of the xssf file. + * These should also implement a constructor of + * (InputStream is), so they can be used with + * {@link XSSFRelation} + */ public interface XSSFModel { /** Read from the given InputStream */ public void readFrom(InputStream is) throws IOException; diff --git a/src/ooxml/java/org/apache/poi/xssf/usermodel/XSSFWorkbook.java b/src/ooxml/java/org/apache/poi/xssf/usermodel/XSSFWorkbook.java index 719fc63a1d..c98135d585 100644 --- a/src/ooxml/java/org/apache/poi/xssf/usermodel/XSSFWorkbook.java +++ b/src/ooxml/java/org/apache/poi/xssf/usermodel/XSSFWorkbook.java @@ -44,6 +44,7 @@ import org.apache.poi.ss.usermodel.Row.MissingCellPolicy; import org.apache.poi.ss.util.SheetReferences; import org.apache.poi.util.POILogFactory; import org.apache.poi.util.POILogger; +import org.apache.poi.xssf.model.BinaryPart; import org.apache.poi.xssf.model.CommentsTable; import org.apache.poi.xssf.model.SharedStringsTable; import org.apache.poi.xssf.model.StylesTable; @@ -125,14 +126,19 @@ public class XSSFWorkbook extends POIXMLDocument implements Workbook { null, OLE_OBJECT_REL_TYPE, null, - null + BinaryPart.class ); - public static final XSSFRelation PACKEMBEDDINGS = new XSSFRelation( null, PACK_OBJECT_REL_TYPE, null, - null + BinaryPart.class + ); + public static final XSSFRelation VBA_MACROS = new XSSFRelation( + "application/vnd.ms-office.vbaProject", + "http://schemas.microsoft.com/office/2006/relationships/vbaProject", + "/xl/vbaProject.bin", + BinaryPart.class ); @@ -151,6 +157,26 @@ public class XSSFWorkbook extends POIXMLDocument implements Workbook { public String getRelation() { return REL; } public String getDefaultFileName() { return DEFAULT_NAME; } + /** + * Does one of these exist for the given core + * package part? + */ + public boolean exists(PackagePart corePart) throws IOException, InvalidFormatException { + if(corePart == null) { + // new file, can't exist + return false; + } + + PackageRelationshipCollection prc = + corePart.getRelationshipsByType(REL); + Iterator it = prc.iterator(); + if(it.hasNext()) { + return true; + } else { + return false; + } + } + /** * Returns the filename for the nth one of these, * eg /xl/comments4.xml @@ -813,6 +839,17 @@ public class XSSFWorkbook extends POIXMLDocument implements Workbook { workbook.setDefinedNames(null); } } + + // VBA Macros + if(VBA_MACROS.exists( getCorePart() )) { + // Copy over + try { + XSSFModel vba = VBA_MACROS.load(getCorePart()); + VBA_MACROS.save(vba, corePart); + } catch(Exception e) { + throw new RuntimeException("Unable to copy vba macros over", e); + } + } // Now we can write out the main Workbook, with // the correct references to the other parts diff --git a/src/ooxml/testcases/org/apache/poi/xssf/usermodel/TestXSSFBugs.java b/src/ooxml/testcases/org/apache/poi/xssf/usermodel/TestXSSFBugs.java index e3d6745734..642b723a5a 100644 --- a/src/ooxml/testcases/org/apache/poi/xssf/usermodel/TestXSSFBugs.java +++ b/src/ooxml/testcases/org/apache/poi/xssf/usermodel/TestXSSFBugs.java @@ -17,7 +17,14 @@ package org.apache.poi.xssf.usermodel; +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; import java.io.File; +import java.io.FileOutputStream; + +import org.openxml4j.opc.Package; +import org.openxml4j.opc.PackagePart; +import org.openxml4j.opc.PackagingURIHelper; import junit.framework.TestCase; @@ -32,6 +39,16 @@ public class TestXSSFBugs extends TestCase { return xml.toString(); } + private Package saveAndOpen(XSSFWorkbook wb) throws Exception { + ByteArrayOutputStream baos = new ByteArrayOutputStream(); + wb.write(baos); + ByteArrayInputStream inp = new ByteArrayInputStream( + baos.toByteArray() + ); + Package pkg = Package.open(inp); + return pkg; + } + /** * Named ranges had the right reference, but * the wrong sheet name @@ -55,4 +72,38 @@ public class TestXSSFBugs extends TestCase { assertEquals("SheetC!$A$1", wb.getNameAt(2).getReference()); assertEquals("SheetC", wb.getNameAt(2).getSheetName()); } + + /** + * We should carry vba macros over after save + */ + public void test45431() throws Exception { + Package pkg = Package.open(getFilePath("45431.xlsm")); + XSSFWorkbook wb = new XSSFWorkbook(pkg); + + PackagePart vba = pkg.getPart( + PackagingURIHelper.createPartName("/xl/vbaProject.bin") + ); + assertNotNull(vba); + + // Save and re-open, is still there + Package nPkg = saveAndOpen(wb); + XSSFWorkbook nwb = new XSSFWorkbook(nPkg); + vba = nPkg.getPart( + PackagingURIHelper.createPartName("/xl/vbaProject.bin") + ); + assertNotNull(vba); + + // And again, just to be sure + nPkg = saveAndOpen(nwb); + nwb = new XSSFWorkbook(nPkg); + vba = nPkg.getPart( + PackagingURIHelper.createPartName("/xl/vbaProject.bin") + ); + assertNotNull(vba); + + // For testing with excel +// FileOutputStream fout = new FileOutputStream("/tmp/foo.xlsm"); +// nwb.write(fout); +// fout.close(); + } } diff --git a/src/testcases/org/apache/poi/hssf/data/45431.xlsm b/src/testcases/org/apache/poi/hssf/data/45431.xlsm new file mode 100644 index 0000000000000000000000000000000000000000..fabbd0df3955d1599d4778767e293dc61f85e5d3 GIT binary patch literal 13817 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