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
This commit is contained in:
parent
5e1c3ce90f
commit
2f8aa558b3
@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -601,11 +601,14 @@ public abstract class PackagePart implements RelationshipSource, Comparable<Pack
|
||||
*/
|
||||
public void setContentType(String contentType)
|
||||
throws InvalidFormatException {
|
||||
if (_container == null)
|
||||
this._contentType = new ContentType(contentType);
|
||||
else
|
||||
throw new InvalidOperationException(
|
||||
"You can't change the content type of a part.");
|
||||
if (_container == null) {
|
||||
_contentType = new ContentType(contentType);
|
||||
}
|
||||
else {
|
||||
_container.unregisterPartAndContentType(_partName);
|
||||
_contentType = new ContentType(contentType);
|
||||
_container.registerPartAndContentType(this);
|
||||
}
|
||||
}
|
||||
|
||||
public OPCPackage getPackage() {
|
||||
|
@ -281,7 +281,7 @@ public final class XSSFRelation extends POIXMLRelation {
|
||||
"application/vnd.ms-office.vbaProject",
|
||||
"http://schemas.microsoft.com/office/2006/relationships/vbaProject",
|
||||
"/xl/vbaProject.bin",
|
||||
null
|
||||
XSSFVBAPart.class
|
||||
);
|
||||
|
||||
public static final XSSFRelation ACTIVEX_CONTROLS = new XSSFRelation(
|
||||
|
@ -0,0 +1,52 @@
|
||||
/* ====================================================================
|
||||
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;
|
||||
|
||||
import org.apache.poi.POIXMLDocumentPart;
|
||||
import org.apache.poi.openxml4j.opc.PackagePart;
|
||||
import org.apache.poi.openxml4j.opc.PackageRelationship;
|
||||
|
||||
public class XSSFVBAPart extends POIXMLDocumentPart {
|
||||
|
||||
/**
|
||||
* Create a new XSSFVBAPart node
|
||||
*/
|
||||
protected XSSFVBAPart() {
|
||||
super();
|
||||
}
|
||||
|
||||
/**
|
||||
* Construct XSSFVBAPart from a package part
|
||||
*
|
||||
* @param part the package part holding the VBA data,
|
||||
* @param rel the package relationship holding this part
|
||||
*/
|
||||
protected XSSFVBAPart(PackagePart part, PackageRelationship rel) {
|
||||
super(part, rel);
|
||||
}
|
||||
|
||||
/**
|
||||
* Like *PictureData, VBA objects store the actual content in the part
|
||||
* directly without keeping a copy like all others therefore we need to
|
||||
* handle them differently.
|
||||
*/
|
||||
protected void prepareForCommit() {
|
||||
// do not clear the part here
|
||||
}
|
||||
|
||||
}
|
@ -215,7 +215,15 @@ public class XSSFWorkbook extends POIXMLDocument implements Workbook, Iterable<X
|
||||
* Create a new SpreadsheetML workbook.
|
||||
*/
|
||||
public XSSFWorkbook() {
|
||||
super(newPackage());
|
||||
this(XSSFWorkbookType.XLSX);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new SpreadsheetML workbook.
|
||||
* @param workbookType The type of workbook to make (.xlsx or .xlsm).
|
||||
*/
|
||||
public XSSFWorkbook(XSSFWorkbookType workbookType) {
|
||||
super(newPackage(workbookType));
|
||||
onWorkbookCreate();
|
||||
}
|
||||
|
||||
@ -429,7 +437,7 @@ public class XSSFWorkbook extends POIXMLDocument implements Workbook, Iterable<X
|
||||
/**
|
||||
* Create a new SpreadsheetML package and setup the default minimal content
|
||||
*/
|
||||
protected static OPCPackage newPackage() {
|
||||
protected static OPCPackage newPackage(XSSFWorkbookType workbookType) {
|
||||
try {
|
||||
OPCPackage pkg = OPCPackage.create(new ByteArrayOutputStream());
|
||||
// Main part
|
||||
@ -437,7 +445,7 @@ public class XSSFWorkbook extends POIXMLDocument implements Workbook, Iterable<X
|
||||
// Create main part relationship
|
||||
pkg.addRelationship(corePartName, TargetMode.INTERNAL, PackageRelationshipTypes.CORE_DOCUMENT);
|
||||
// Create main document part
|
||||
pkg.createPart(corePartName, XSSFRelation.WORKBOOK.getContentType());
|
||||
pkg.createPart(corePartName, workbookType.getContentType());
|
||||
|
||||
pkg.getPackageProperties().setCreatorProperty(DOCUMENT_CREATOR);
|
||||
|
||||
@ -2044,4 +2052,67 @@ public class XSSFWorkbook extends POIXMLDocument implements Workbook, Iterable<X
|
||||
protected void setPivotTables(List<XSSFPivotTable> 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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
}
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user