From: David North Date: Mon, 13 Jul 2015 09:05:17 +0000 (+0000) Subject: Patch from Chris Boyle to add basic support for .xlsm (macro-enabled) workbooks.... X-Git-Tag: REL_3_13_BETA1~45 X-Git-Url: https://source.dussan.org/?a=commitdiff_plain;h=1c69d7596cb247f4a786c0960369fd3c3631c918;p=poi.git Patch from Chris Boyle to add basic support for .xlsm (macro-enabled) workbooks. The binary blob containing the VBA macros may be copied from one such workbook into another. Fixes #58036 git-svn-id: https://svn.apache.org/repos/asf/poi/trunk@1690593 13f79535-47bb-0310-9956-ffa450edef68 --- diff --git a/src/ooxml/java/org/apache/poi/openxml4j/opc/OPCPackage.java b/src/ooxml/java/org/apache/poi/openxml4j/opc/OPCPackage.java index 8b65418e08..ccf57a6553 100644 --- a/src/ooxml/java/org/apache/poi/openxml4j/opc/OPCPackage.java +++ b/src/ooxml/java/org/apache/poi/openxml4j/opc/OPCPackage.java @@ -1551,4 +1551,31 @@ public abstract class OPCPackage implements RelationshipSource, Closeable { } return success; } + + /** + * Add the specified part, and register its content type with the content + * type manager. + * + * @param part + * The part to add. + */ + public void registerPartAndContentType(PackagePart part) { + addPackagePart(part); + this.contentTypeManager.addContentType(part.getPartName(), part.getContentType()); + this.isDirty = true; + } + + /** + * Remove the specified part, and clear its content type from the content + * type manager. + * + * @param partName + * The part name of the part to remove. + */ + public void unregisterPartAndContentType(PackagePartName partName) { + removePart(partName); + this.contentTypeManager.removeContentType(partName); + this.isDirty = true; + } + } diff --git a/src/ooxml/java/org/apache/poi/openxml4j/opc/PackagePart.java b/src/ooxml/java/org/apache/poi/openxml4j/opc/PackagePart.java index 1943cf6a54..8f961c9335 100644 --- a/src/ooxml/java/org/apache/poi/openxml4j/opc/PackagePart.java +++ b/src/ooxml/java/org/apache/poi/openxml4j/opc/PackagePart.java @@ -601,11 +601,14 @@ public abstract class PackagePart implements RelationshipSource, Comparable pivotTables) { this.pivotTables = pivotTables; } + + public XSSFWorkbookType getWorkbookType() { + return isMacroEnabled() ? XSSFWorkbookType.XLSM : XSSFWorkbookType.XLSX; + } + + /** + * Sets whether the workbook will be an .xlsx or .xlsm (macro-enabled) file. + */ + public void setWorkbookType(XSSFWorkbookType type) { + try { + getPackagePart().setContentType(type.getContentType()); + } catch (InvalidFormatException e) { + throw new POIXMLException(e); + } + } + + /** + * Adds a vbaProject.bin file to the workbook. This will change the workbook + * type if necessary. + * + * @throws IOException + */ + public void setVBAProject(InputStream vbaProjectStream) throws IOException { + if (!isMacroEnabled()) { + setWorkbookType(XSSFWorkbookType.XLSM); + } + + PackagePartName ppName; + try { + ppName = PackagingURIHelper.createPartName(XSSFRelation.VBA_MACROS.getDefaultFileName()); + } catch (InvalidFormatException e) { + throw new POIXMLException(e); + } + OPCPackage opc = getPackage(); + OutputStream outputStream; + if (!opc.containPart(ppName)) { + POIXMLDocumentPart relationship = createRelationship(XSSFRelation.VBA_MACROS, XSSFFactory.getInstance()); + outputStream = relationship.getPackagePart().getOutputStream(); + } else { + PackagePart part = opc.getPart(ppName); + outputStream = part.getOutputStream(); + } + try { + IOUtils.copy(vbaProjectStream, outputStream); + } finally { + IOUtils.closeQuietly(outputStream); + } + } + + /** + * Adds a vbaProject.bin file taken from another, given workbook to this one. + * @throws IOException + * @throws InvalidFormatException + */ + public void setVBAProject(XSSFWorkbook macroWorkbook) throws IOException, InvalidFormatException { + if (!macroWorkbook.isMacroEnabled()) { + return; + } + InputStream vbaProjectStream = XSSFRelation.VBA_MACROS.getContents(macroWorkbook.getCorePart()); + if (vbaProjectStream != null) { + setVBAProject(vbaProjectStream); + } + } } diff --git a/src/ooxml/java/org/apache/poi/xssf/usermodel/XSSFWorkbookType.java b/src/ooxml/java/org/apache/poi/xssf/usermodel/XSSFWorkbookType.java new file mode 100644 index 0000000000..917d3d6daa --- /dev/null +++ b/src/ooxml/java/org/apache/poi/xssf/usermodel/XSSFWorkbookType.java @@ -0,0 +1,45 @@ +/* ==================================================================== + 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.usermodel; + + +/** + * Represents the two different kinds of XML-based OOXML document. + */ +public enum XSSFWorkbookType { + + XLSX(XSSFRelation.WORKBOOK.getContentType(), "xlsx"), + XLSM(XSSFRelation.MACROS_WORKBOOK.getContentType(), "xlsm"); + + private final String _contentType; + private final String _extension; + + private XSSFWorkbookType(String contentType, String extension) { + _contentType = contentType; + _extension = extension; + } + + public String getContentType() { + return _contentType; + } + + public String getExtension() { + return _extension; + } + +} diff --git a/src/ooxml/testcases/org/apache/poi/xssf/usermodel/TestXSSFWorkbook.java b/src/ooxml/testcases/org/apache/poi/xssf/usermodel/TestXSSFWorkbook.java index 209bded8c4..392ab906ef 100644 --- a/src/ooxml/testcases/org/apache/poi/xssf/usermodel/TestXSSFWorkbook.java +++ b/src/ooxml/testcases/org/apache/poi/xssf/usermodel/TestXSSFWorkbook.java @@ -17,6 +17,7 @@ package org.apache.poi.xssf.usermodel; +import static org.junit.Assert.assertArrayEquals; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertNotNull; @@ -25,6 +26,7 @@ import static org.junit.Assert.assertSame; import static org.junit.Assert.assertTrue; import static org.junit.Assert.fail; +import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; import java.io.File; import java.io.FileOutputStream; @@ -39,10 +41,11 @@ import org.apache.poi.openxml4j.opc.ContentTypes; import org.apache.poi.openxml4j.opc.OPCPackage; import org.apache.poi.openxml4j.opc.PackagePart; import org.apache.poi.openxml4j.opc.PackagePartName; +import org.apache.poi.openxml4j.opc.PackageRelationshipCollection; import org.apache.poi.openxml4j.opc.PackagingURIHelper; import org.apache.poi.openxml4j.opc.internal.MemoryPackagePart; import org.apache.poi.openxml4j.opc.internal.PackagePropertiesPart; - +import org.apache.poi.ss.SpreadsheetVersion; import org.apache.poi.ss.usermodel.BaseTestWorkbook; import org.apache.poi.ss.usermodel.Cell; import org.apache.poi.ss.usermodel.CellStyle; @@ -78,7 +81,8 @@ public final class TestXSSFWorkbook extends BaseTestWorkbook { */ @Test public void saveLoadNew() throws Exception { - XSSFWorkbook workbook = new XSSFWorkbook(); + @SuppressWarnings("resource") + XSSFWorkbook workbook = new XSSFWorkbook(); //check that the default date system is set to 1900 CTWorkbookPr pr = workbook.getCTWorkbook().getWorkbookPr(); @@ -121,7 +125,8 @@ public final class TestXSSFWorkbook extends BaseTestWorkbook { // Links to the three sheets, shared strings and styles assertTrue(wbPart.hasRelationships()); assertEquals(5, wbPart.getRelationships().size()); - + workbook.close(); + // Load back the XSSFWorkbook workbook = new XSSFWorkbook(pkg); assertEquals(3, workbook.getNumberOfSheets()); @@ -777,7 +782,7 @@ public final class TestXSSFWorkbook extends BaseTestWorkbook { Cell cell9 = row3.createCell(2); cell9.setCellValue("Bepa"); - AreaReference source = new AreaReference("A1:B2"); + AreaReference source = new AreaReference("A1:B2", SpreadsheetVersion.EXCEL2007); sheet.createPivotTable(source, new CellReference("H5")); } @@ -869,4 +874,54 @@ public final class TestXSSFWorkbook extends BaseTestWorkbook { wb.close(); } } + + /** + * Tests that we can save a workbook with macros and reload it. + */ + @Test + public void testSetVBAProject() throws Exception { + XSSFWorkbook workbook = null; + OutputStream out = null; + File file; + final byte[] allBytes = new byte[256]; + for (int i = 0; i < 256; i++) { + allBytes[i] = (byte) (i - 128); + } + try { + workbook = new XSSFWorkbook(); + workbook.createSheet(); + workbook.setVBAProject(new ByteArrayInputStream(allBytes)); + file = TempFile.createTempFile("poi-", ".xlsm"); + out = new FileOutputStream(file); + workbook.write(out); + } + finally { + IOUtils.closeQuietly(out); + IOUtils.closeQuietly(workbook); + } + + try { + // Check the package contains what we'd expect it to + OPCPackage pkg = OPCPackage.open(file.toString()); + PackagePart wbPart = pkg.getPart(PackagingURIHelper.createPartName("/xl/workbook.xml")); + assertTrue(wbPart.hasRelationships()); + final PackageRelationshipCollection relationships = wbPart.getRelationships().getRelationships(XSSFRelation.VBA_MACROS.getRelation()); + assertEquals(1, relationships.size()); + assertEquals(XSSFRelation.VBA_MACROS.getDefaultFileName(), relationships.getRelationship(0).getTargetURI().toString()); + PackagePart vbaPart = pkg.getPart(PackagingURIHelper.createPartName(XSSFRelation.VBA_MACROS.getDefaultFileName())); + assertNotNull(vbaPart); + assertFalse(vbaPart.isRelationshipPart()); + assertEquals(XSSFRelation.VBA_MACROS.getContentType(), vbaPart.getContentType()); + final byte[] fromFile = IOUtils.toByteArray(vbaPart.getInputStream()); + assertArrayEquals(allBytes, fromFile); + + // Load back the XSSFWorkbook just to check nothing explodes + workbook = new XSSFWorkbook(pkg); + assertEquals(1, workbook.getNumberOfSheets()); + assertEquals(XSSFWorkbookType.XLSM, workbook.getWorkbookType()); + } + finally { + IOUtils.closeQuietly(workbook); + } + } }