Change how we do the control and drawing write out, so that XSSFRelation can do much more of the work for us
git-svn-id: https://svn.apache.org/repos/asf/poi/branches/ooxml@680889 13f79535-47bb-0310-9956-ffa450edef68
This commit is contained in:
parent
613f361d61
commit
647537c626
@ -9,11 +9,7 @@ import org.apache.poi.xssf.usermodel.XSSFActiveXData;
|
||||
import org.apache.poi.xssf.usermodel.XSSFWorkbook;
|
||||
import org.apache.xmlbeans.XmlException;
|
||||
import org.apache.xmlbeans.XmlOptions;
|
||||
import org.openxml4j.exceptions.InvalidFormatException;
|
||||
import org.openxml4j.opc.PackagePart;
|
||||
import org.openxml4j.opc.PackagePartName;
|
||||
import org.openxml4j.opc.PackagingURIHelper;
|
||||
import org.openxml4j.opc.TargetMode;
|
||||
import org.openxmlformats.schemas.spreadsheetml.x2006.main.CTControl;
|
||||
|
||||
/**
|
||||
@ -71,6 +67,10 @@ public class Control implements XSSFChildContainingModel {
|
||||
};
|
||||
}
|
||||
|
||||
public int getNumberOfChildren() {
|
||||
return activexBins.size();
|
||||
}
|
||||
|
||||
/**
|
||||
* Generates and adds XSSFActiveXData children
|
||||
*/
|
||||
@ -79,22 +79,14 @@ public class Control implements XSSFChildContainingModel {
|
||||
activexBins.add(actX);
|
||||
}
|
||||
|
||||
/**
|
||||
* Writes back out our XSSFPictureData children
|
||||
*/
|
||||
public void writeChildren(PackagePart modelPart) throws IOException, InvalidFormatException {
|
||||
int binIndex = 1;
|
||||
OutputStream out;
|
||||
|
||||
for(XSSFActiveXData actX : activexBins) {
|
||||
PackagePartName binPartName = PackagingURIHelper.createPartName(XSSFWorkbook.ACTIVEX_BINS.getFileName(binIndex));
|
||||
modelPart.addRelationship(binPartName, TargetMode.INTERNAL, XSSFWorkbook.ACTIVEX_BINS.getRelation(), getOriginalId());
|
||||
PackagePart imagePart = modelPart.getPackage().createPart(binPartName, XSSFWorkbook.ACTIVEX_BINS.getContentType());
|
||||
out = imagePart.getOutputStream();
|
||||
actX.writeTo(out);
|
||||
out.close();
|
||||
binIndex++;
|
||||
public WritableChild getChildForWriting(int index) {
|
||||
if(index >= activexBins.size()) {
|
||||
throw new IllegalArgumentException("Can't get child at " + index + " when size is " + getNumberOfChildren());
|
||||
}
|
||||
return new WritableChild(
|
||||
activexBins.get(index),
|
||||
XSSFWorkbook.ACTIVEX_BINS
|
||||
);
|
||||
}
|
||||
|
||||
public ArrayList<XSSFActiveXData> getData() {
|
||||
|
@ -9,11 +9,7 @@ import org.apache.poi.xssf.usermodel.XSSFPictureData;
|
||||
import org.apache.poi.xssf.usermodel.XSSFWorkbook;
|
||||
import org.apache.xmlbeans.XmlException;
|
||||
import org.apache.xmlbeans.XmlOptions;
|
||||
import org.openxml4j.exceptions.InvalidFormatException;
|
||||
import org.openxml4j.opc.PackagePart;
|
||||
import org.openxml4j.opc.PackagePartName;
|
||||
import org.openxml4j.opc.PackagingURIHelper;
|
||||
import org.openxml4j.opc.TargetMode;
|
||||
import org.openxmlformats.schemas.spreadsheetml.x2006.main.CTDrawing;
|
||||
|
||||
/**
|
||||
@ -73,6 +69,10 @@ public class Drawing implements XSSFChildContainingModel {
|
||||
};
|
||||
}
|
||||
|
||||
public int getNumberOfChildren() {
|
||||
return pictures.size();
|
||||
}
|
||||
|
||||
/**
|
||||
* Generates and adds XSSFActiveXData children
|
||||
*/
|
||||
@ -81,22 +81,14 @@ public class Drawing implements XSSFChildContainingModel {
|
||||
pictures.add(pd);
|
||||
}
|
||||
|
||||
/**
|
||||
* Writes back out our XSSFPictureData children
|
||||
*/
|
||||
public void writeChildren(PackagePart modelPart) throws IOException, InvalidFormatException {
|
||||
int pictureIndex = 1;
|
||||
OutputStream out;
|
||||
|
||||
for(XSSFPictureData picture : pictures) {
|
||||
PackagePartName imagePartName = PackagingURIHelper.createPartName(XSSFWorkbook.IMAGES.getFileName(pictureIndex));
|
||||
modelPart.addRelationship(imagePartName, TargetMode.INTERNAL, XSSFWorkbook.IMAGES.getRelation(), getOriginalId());
|
||||
PackagePart imagePart = modelPart.getPackage().createPart(imagePartName, XSSFWorkbook.IMAGES.getContentType());
|
||||
out = imagePart.getOutputStream();
|
||||
picture.writeTo(out);
|
||||
out.close();
|
||||
pictureIndex++;
|
||||
public WritableChild getChildForWriting(int index) {
|
||||
if(index >= pictures.size()) {
|
||||
throw new IllegalArgumentException("Can't get child at " + index + " when size is " + getNumberOfChildren());
|
||||
}
|
||||
return new WritableChild(
|
||||
pictures.get(index),
|
||||
XSSFWorkbook.IMAGES
|
||||
);
|
||||
}
|
||||
|
||||
public ArrayList<XSSFPictureData> getPictures()
|
||||
|
@ -18,6 +18,7 @@ package org.apache.poi.xssf.model;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
import org.apache.poi.xssf.usermodel.XSSFWorkbook.XSSFRelation;
|
||||
import org.openxml4j.exceptions.InvalidFormatException;
|
||||
import org.openxml4j.opc.PackagePart;
|
||||
|
||||
@ -43,9 +44,27 @@ public interface XSSFChildContainingModel extends XSSFModel {
|
||||
public void generateChild(PackagePart childPart, String childRelId);
|
||||
|
||||
/**
|
||||
* Writes out any children associated with the {@link XSSFModel},
|
||||
* along with the required relationship stuff.
|
||||
* @param modelPart The new PackagePart of this model
|
||||
* Returns the number of children contained, which
|
||||
* will need to be written out.
|
||||
*/
|
||||
public void writeChildren(PackagePart modelPart) throws IOException, InvalidFormatException;
|
||||
public int getNumberOfChildren();
|
||||
/**
|
||||
* Called for each child in turn when writing out,
|
||||
* which returns a WritableChild for the child in
|
||||
* that position. The WritableChild has enough
|
||||
* information to be able to write it out.
|
||||
* @param index A zero based index of this child
|
||||
*/
|
||||
public WritableChild getChildForWriting(int index);
|
||||
|
||||
static class WritableChild {
|
||||
private XSSFWritableModel model;
|
||||
private XSSFRelation relation;
|
||||
public WritableChild(XSSFWritableModel model, XSSFRelation rel) {
|
||||
this.model = model;
|
||||
this.relation = rel;
|
||||
}
|
||||
public XSSFWritableModel getModel() { return model; }
|
||||
public XSSFRelation getRelation() { return relation; }
|
||||
}
|
||||
}
|
||||
|
@ -18,7 +18,6 @@ package org.apache.poi.xssf.model;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.OutputStream;
|
||||
|
||||
import org.apache.poi.xssf.usermodel.XSSFWorkbook.XSSFRelation;
|
||||
|
||||
@ -29,9 +28,7 @@ import org.apache.poi.xssf.usermodel.XSSFWorkbook.XSSFRelation;
|
||||
* (InputStream is), so they can be used with
|
||||
* {@link XSSFRelation}
|
||||
*/
|
||||
public interface XSSFModel {
|
||||
public interface XSSFModel extends XSSFWritableModel {
|
||||
/** Read from the given InputStream */
|
||||
public void readFrom(InputStream is) throws IOException;
|
||||
/** Write to the supplied OutputStream, with default options */
|
||||
public void writeTo(OutputStream out) throws IOException;
|
||||
}
|
||||
|
@ -0,0 +1,37 @@
|
||||
/* ====================================================================
|
||||
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.IOException;
|
||||
import java.io.OutputStream;
|
||||
|
||||
import org.apache.poi.xssf.usermodel.XSSFWorkbook.XSSFRelation;
|
||||
|
||||
/**
|
||||
* A very stripped down XSSF models interface,
|
||||
* which only allows writing out. Normally only
|
||||
* used with the children of a
|
||||
* {@link XSSFChildContainingModel}.
|
||||
* Most proper models will go for {@link XSSFModel}
|
||||
* instead of this one.
|
||||
* {@link XSSFRelation} needs classes to implement
|
||||
* this, so that it can write them out.
|
||||
*/
|
||||
public interface XSSFWritableModel {
|
||||
/** Write to the supplied OutputStream, with default options */
|
||||
public void writeTo(OutputStream out) throws IOException;
|
||||
}
|
@ -5,9 +5,10 @@ import java.io.OutputStream;
|
||||
|
||||
import org.apache.poi.ss.usermodel.PictureData;
|
||||
import org.apache.poi.util.IOUtils;
|
||||
import org.apache.poi.xssf.model.XSSFWritableModel;
|
||||
import org.openxml4j.opc.PackagePart;
|
||||
|
||||
public class XSSFActiveXData implements PictureData {
|
||||
public class XSSFActiveXData implements PictureData, XSSFWritableModel {
|
||||
|
||||
private PackagePart packagePart;
|
||||
private String originalId;
|
||||
|
@ -22,13 +22,14 @@ import java.io.OutputStream;
|
||||
|
||||
import org.apache.poi.ss.usermodel.PictureData;
|
||||
import org.apache.poi.util.IOUtils;
|
||||
import org.apache.poi.xssf.model.XSSFWritableModel;
|
||||
import org.openxml4j.opc.PackagePart;
|
||||
|
||||
/**
|
||||
* Raw picture data, normally attached to a
|
||||
* vmlDrawing
|
||||
*/
|
||||
public class XSSFPictureData implements PictureData {
|
||||
public class XSSFPictureData implements PictureData, XSSFWritableModel {
|
||||
private PackagePart packagePart;
|
||||
private String originalId;
|
||||
|
||||
|
@ -53,6 +53,7 @@ import org.apache.poi.xssf.model.SharedStringsTable;
|
||||
import org.apache.poi.xssf.model.StylesTable;
|
||||
import org.apache.poi.xssf.model.XSSFChildContainingModel;
|
||||
import org.apache.poi.xssf.model.XSSFModel;
|
||||
import org.apache.poi.xssf.model.XSSFWritableModel;
|
||||
import org.apache.xmlbeans.XmlException;
|
||||
import org.apache.xmlbeans.XmlObject;
|
||||
import org.apache.xmlbeans.XmlOptions;
|
||||
@ -118,7 +119,7 @@ public class XSSFWorkbook extends POIXMLDocument implements Workbook {
|
||||
"application/vnd.openxmlformats-officedocument.vmlDrawing",
|
||||
"http://schemas.openxmlformats.org/officeDocument/2006/relationships/vmlDrawing",
|
||||
"/xl/drawings/vmlDrawing#.vml",
|
||||
null
|
||||
Drawing.class
|
||||
);
|
||||
public static final XSSFRelation IMAGES = new XSSFRelation(
|
||||
"image/x-emf", // TODO
|
||||
@ -161,7 +162,7 @@ public class XSSFWorkbook extends POIXMLDocument implements Workbook {
|
||||
"application/vnd.ms-office.activeX+xml",
|
||||
"http://schemas.openxmlformats.org/officeDocument/2006/relationships/control",
|
||||
"/xl/activeX/activeX#.xml",
|
||||
null
|
||||
Control.class
|
||||
);
|
||||
public static final XSSFRelation ACTIVEX_BINS = new XSSFRelation(
|
||||
"application/vnd.ms-office.activeX",
|
||||
@ -220,7 +221,8 @@ public class XSSFWorkbook extends POIXMLDocument implements Workbook {
|
||||
|
||||
/**
|
||||
* Fetches the InputStream to read the contents, based
|
||||
* of the specified core part
|
||||
* of the specified core part, for which we are defined
|
||||
* as a suitable relationship
|
||||
*/
|
||||
public InputStream getContents(PackagePart corePart) throws IOException, InvalidFormatException {
|
||||
PackageRelationshipCollection prc =
|
||||
@ -238,45 +240,81 @@ public class XSSFWorkbook extends POIXMLDocument implements Workbook {
|
||||
}
|
||||
|
||||
/**
|
||||
* Finds all the XSSFModels of this type which are
|
||||
* Loads all the XSSFModels of this type which are
|
||||
* defined as relationships of the given parent part
|
||||
*/
|
||||
public ArrayList<? extends XSSFModel> findAll(PackagePart parentPart) throws Exception {
|
||||
public ArrayList<? extends XSSFModel> loadAll(PackagePart parentPart) throws Exception {
|
||||
ArrayList<XSSFModel> found = new ArrayList<XSSFModel>();
|
||||
for(PackageRelationship rel : parentPart.getRelationshipsByType(REL)) {
|
||||
PackagePart part = getTargetPart(parentPart.getPackage(), rel);
|
||||
found.add(load(part));
|
||||
found.add(create(part, rel));
|
||||
}
|
||||
return found;
|
||||
}
|
||||
|
||||
/**
|
||||
* Load, off the specified core part
|
||||
* Load a single Model, which is defined as a suitable
|
||||
* relationship from the specified core (parent)
|
||||
* package part.
|
||||
*/
|
||||
public XSSFModel load(PackagePart corePart) throws Exception {
|
||||
Constructor<? extends XSSFModel> c = CLASS.getConstructor(InputStream.class);
|
||||
PackageRelationshipCollection prc =
|
||||
corePart.getRelationshipsByType(REL);
|
||||
Iterator<PackageRelationship> it = prc.iterator();
|
||||
if(it.hasNext()) {
|
||||
PackageRelationship rel = it.next();
|
||||
PackagePartName relName = PackagingURIHelper.createPartName(rel.getTargetURI());
|
||||
PackagePart part = corePart.getPackage().getPart(relName);
|
||||
return create(part, rel);
|
||||
} else {
|
||||
log.log(POILogger.WARN, "No part " + DEFAULT_NAME + " found");
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Does the actual Model creation
|
||||
*/
|
||||
private XSSFModel create(PackagePart thisPart, PackageRelationship rel) throws Exception {
|
||||
XSSFModel model = null;
|
||||
|
||||
InputStream inp = getContents(corePart);
|
||||
Constructor<? extends XSSFModel> c;
|
||||
boolean withString = false;
|
||||
|
||||
// Find the right constructor
|
||||
try {
|
||||
c = CLASS.getConstructor(InputStream.class, String.class);
|
||||
withString = true;
|
||||
} catch(NoSuchMethodException e) {
|
||||
c = CLASS.getConstructor(InputStream.class);
|
||||
}
|
||||
|
||||
// Instantiate, if we can
|
||||
InputStream inp = thisPart.getInputStream();
|
||||
if(inp != null) {
|
||||
try {
|
||||
model = c.newInstance(inp);
|
||||
if(withString) {
|
||||
model = c.newInstance(inp, rel.getId());
|
||||
} else {
|
||||
model = c.newInstance(inp);
|
||||
}
|
||||
} finally {
|
||||
inp.close();
|
||||
}
|
||||
|
||||
// Do children, if required
|
||||
if(model instanceof XSSFChildContainingModel) {
|
||||
XSSFChildContainingModel ccm =
|
||||
(XSSFChildContainingModel)model;
|
||||
for(String relType : ccm.getChildrenRelationshipTypes()) {
|
||||
for(PackageRelationship cRel : thisPart.getRelationshipsByType(relType)) {
|
||||
PackagePart childPart = getTargetPart(thisPart.getPackage(), cRel);
|
||||
ccm.generateChild(childPart, cRel.getId());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Do children, if required
|
||||
if(model instanceof XSSFChildContainingModel) {
|
||||
XSSFChildContainingModel ccm =
|
||||
(XSSFChildContainingModel)model;
|
||||
for(String relType : ccm.getChildrenRelationshipTypes()) {
|
||||
for(PackageRelationship rel : corePart.getRelationshipsByType(relType)) {
|
||||
PackagePart childPart = getTargetPart(corePart.getPackage(), rel);
|
||||
ccm.generateChild(childPart, rel.getId());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return model;
|
||||
}
|
||||
@ -285,19 +323,26 @@ public class XSSFWorkbook extends POIXMLDocument implements Workbook {
|
||||
* Save, with the default name
|
||||
* @return The internal reference ID it was saved at, normally then used as an r:id
|
||||
*/
|
||||
private String save(XSSFModel model, PackagePart corePart) throws IOException {
|
||||
private String save(XSSFWritableModel model, PackagePart corePart) throws IOException {
|
||||
return save(model, corePart, DEFAULT_NAME);
|
||||
}
|
||||
/**
|
||||
* Save, with the name generated by the given index
|
||||
* @return The internal reference ID it was saved at, normally then used as an r:id
|
||||
*/
|
||||
private String save(XSSFWritableModel model, PackagePart corePart, int index) throws IOException {
|
||||
return save(model, corePart, getFileName(index));
|
||||
}
|
||||
/**
|
||||
* Save, with the specified name
|
||||
* @return The internal reference ID it was saved at, normally then used as an r:id
|
||||
*/
|
||||
private String save(XSSFModel model, PackagePart corePart, String name) throws IOException {
|
||||
private String save(XSSFWritableModel model, PackagePart corePart, String name) throws IOException {
|
||||
PackagePartName ppName = null;
|
||||
try {
|
||||
ppName = PackagingURIHelper.createPartName(name);
|
||||
} catch(InvalidFormatException e) {
|
||||
throw new IllegalStateException(e);
|
||||
throw new IllegalStateException("Can't create part with name " + name + " for " + model, e);
|
||||
}
|
||||
PackageRelationship rel =
|
||||
corePart.addRelationship(ppName, TargetMode.INTERNAL, REL);
|
||||
@ -307,6 +352,23 @@ public class XSSFWorkbook extends POIXMLDocument implements Workbook {
|
||||
model.writeTo(out);
|
||||
out.close();
|
||||
|
||||
// Do children, if required
|
||||
if(model instanceof XSSFChildContainingModel) {
|
||||
XSSFChildContainingModel ccm =
|
||||
(XSSFChildContainingModel)model;
|
||||
// Loop over each child, writing it out
|
||||
int numChildren = ccm.getNumberOfChildren();
|
||||
for(int i=0; i<numChildren; i++) {
|
||||
XSSFChildContainingModel.WritableChild child =
|
||||
ccm.getChildForWriting(i);
|
||||
child.getRelation().save(
|
||||
child.getModel(),
|
||||
part,
|
||||
(i+1)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
return rel.getId();
|
||||
}
|
||||
}
|
||||
@ -382,15 +444,15 @@ public class XSSFWorkbook extends POIXMLDocument implements Workbook {
|
||||
ArrayList<Control> controls;
|
||||
try {
|
||||
// Get the comments for the sheet, if there are any
|
||||
childModels = SHEET_COMMENTS.findAll(part);
|
||||
childModels = SHEET_COMMENTS.loadAll(part);
|
||||
if(childModels.size() > 0) {
|
||||
comments = (CommentsSource)childModels.get(0);
|
||||
}
|
||||
|
||||
// Get the drawings for the sheet, if there are any
|
||||
drawings = (ArrayList<Drawing>)VML_DRAWINGS.findAll(part);
|
||||
drawings = (ArrayList<Drawing>)VML_DRAWINGS.loadAll(part);
|
||||
// Get the activeX controls for the sheet, if there are any
|
||||
controls = (ArrayList<Control>)ACTIVEX_CONTROLS.findAll(part);
|
||||
controls = (ArrayList<Control>)ACTIVEX_CONTROLS.loadAll(part);
|
||||
} catch(Exception e) {
|
||||
throw new RuntimeException("Unable to construct child part",e);
|
||||
}
|
||||
@ -890,29 +952,18 @@ public class XSSFWorkbook extends POIXMLDocument implements Workbook {
|
||||
// If our sheet has comments, then write out those
|
||||
if(sheet.hasComments()) {
|
||||
CommentsTable ct = (CommentsTable)sheet.getCommentsSourceIfExists();
|
||||
PackagePartName ctName = PackagingURIHelper.createPartName(
|
||||
SHEET_COMMENTS.getFileName(sheetNumber));
|
||||
part.addRelationship(ctName, TargetMode.INTERNAL, SHEET_COMMENTS.getRelation(), "rComments");
|
||||
PackagePart ctPart = pkg.createPart(ctName, SHEET_COMMENTS.getContentType());
|
||||
|
||||
out = ctPart.getOutputStream();
|
||||
ct.writeTo(out);
|
||||
out.close();
|
||||
SHEET_COMMENTS.save(ct, part, sheetNumber);
|
||||
}
|
||||
|
||||
// If our sheet has drawings, then write out those
|
||||
if(sheet.getDrawings() != null) {
|
||||
int drawingIndex = 1;
|
||||
for(Drawing drawing : sheet.getDrawings()) {
|
||||
PackagePartName drName = PackagingURIHelper.createPartName(
|
||||
VML_DRAWINGS.getFileName(drawingIndex));
|
||||
part.addRelationship(drName, TargetMode.INTERNAL, VML_DRAWINGS.getRelation(), drawing.getOriginalId());
|
||||
PackagePart drPart = pkg.createPart(drName, VML_DRAWINGS.getContentType());
|
||||
|
||||
drawing.writeChildren(drPart);
|
||||
out = drPart.getOutputStream();
|
||||
drawing.writeTo(out);
|
||||
out.close();
|
||||
VML_DRAWINGS.save(
|
||||
drawing,
|
||||
part,
|
||||
drawingIndex
|
||||
);
|
||||
drawingIndex++;
|
||||
}
|
||||
}
|
||||
@ -921,15 +972,11 @@ public class XSSFWorkbook extends POIXMLDocument implements Workbook {
|
||||
if(sheet.getControls() != null) {
|
||||
int controlIndex = 1;
|
||||
for(Control control : sheet.getControls()) {
|
||||
PackagePartName crName = PackagingURIHelper.createPartName(
|
||||
ACTIVEX_CONTROLS.getFileName(controlIndex));
|
||||
part.addRelationship(crName, TargetMode.INTERNAL, ACTIVEX_CONTROLS.getRelation(), control.getOriginalId());
|
||||
PackagePart crPart = pkg.createPart(crName, ACTIVEX_CONTROLS.getContentType());
|
||||
|
||||
control.writeChildren(crPart);
|
||||
out = crPart.getOutputStream();
|
||||
control.writeTo(out);
|
||||
out.close();
|
||||
ACTIVEX_CONTROLS.save(
|
||||
control,
|
||||
part,
|
||||
controlIndex
|
||||
);
|
||||
controlIndex++;
|
||||
}
|
||||
}
|
||||
|
@ -128,10 +128,6 @@ public class TestXSSFBugs extends TestCase {
|
||||
);
|
||||
assertNotNull(drw);
|
||||
|
||||
FileOutputStream fout = new FileOutputStream("/tmp/foo.xlsm");
|
||||
nwb.write(fout);
|
||||
fout.close();
|
||||
|
||||
// For testing with excel
|
||||
// FileOutputStream fout = new FileOutputStream("/tmp/foo.xlsm");
|
||||
// nwb.write(fout);
|
||||
|
Loading…
Reference in New Issue
Block a user