From cfa2b94845cde964037c11047bb07ab53a002d6e Mon Sep 17 00:00:00 2001 From: Yegor Kozlov Date: Sat, 25 Jun 2011 13:46:00 +0000 Subject: [PATCH] Bug 50474 - Example demonstrating how to update workbook embedded in a WordprocessingML document git-svn-id: https://svn.apache.org/repos/asf/poi/trunk@1139541 13f79535-47bb-0310-9956-ffa450edef68 --- src/documentation/content/xdocs/status.xml | 1 + .../poi/xwpf/usermodel/UpdateEmbeddedDoc.java | 219 ++++++++++++++++++ test-data/document/EmbeddedDocument.docx | Bin 0 -> 13268 bytes 3 files changed, 220 insertions(+) create mode 100644 src/examples/src/org/apache/poi/xwpf/usermodel/UpdateEmbeddedDoc.java create mode 100644 test-data/document/EmbeddedDocument.docx diff --git a/src/documentation/content/xdocs/status.xml b/src/documentation/content/xdocs/status.xml index 61e2425f33..55edae324e 100644 --- a/src/documentation/content/xdocs/status.xml +++ b/src/documentation/content/xdocs/status.xml @@ -34,6 +34,7 @@ + 50474 - Example demonstrating how to update Excel workbook embedded in a WordprocessingML document 51431 - Avoid IndexOutOfBoundException when removing freeze panes in XSSF 48877 - Fixed XSSFRichTextString to respect leading and trailing line breaks 49564 - Fixed default behaviour of XSSFCellStyle.getLocked() diff --git a/src/examples/src/org/apache/poi/xwpf/usermodel/UpdateEmbeddedDoc.java b/src/examples/src/org/apache/poi/xwpf/usermodel/UpdateEmbeddedDoc.java new file mode 100644 index 0000000000..ffdb7e2049 --- /dev/null +++ b/src/examples/src/org/apache/poi/xwpf/usermodel/UpdateEmbeddedDoc.java @@ -0,0 +1,219 @@ +/* + * ==================================================================== + * 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.xwpf.usermodel; + +import java.io.File; +import java.io.FileInputStream; +import java.io.FileOutputStream; +import java.io.FileNotFoundException; +import java.io.IOException; +import java.util.List; +import java.util.Iterator; + +import junit.framework.Assert; +import org.apache.poi.openxml4j.opc.PackagePart; +import org.apache.poi.openxml4j.exceptions.OpenXML4JException; +import org.apache.poi.ss.usermodel.WorkbookFactory; +import org.apache.poi.ss.usermodel.Workbook; +import org.apache.poi.ss.usermodel.Sheet; +import org.apache.poi.ss.usermodel.Row; +import org.apache.poi.ss.usermodel.Cell; + +/** + * Tests whether it is possible to successfully update an Excel workbook that is + * embedded into a WordprocessingML document. Note that the test has currently + * only been conducted with a binary Excel workbook and NOT yet with a + * SpreadsheetML workbook embedded into the document. + * + *

+ * This code was successfully tested with the following file from the POI test collection: + * http://svn.apache.org/repos/asf/poi/trunk/test-data/document/EmbeddedDocument.docx + *

+ * + * @author Mark B + */ +public class UpdateEmbeddedDoc { + + private XWPFDocument doc = null; + private File docFile = null; + + private static final int SHEET_NUM = 0; + private static final int ROW_NUM = 0; + private static final int CELL_NUM = 0; + private static final double NEW_VALUE = 100.98D; + private static final String BINARY_EXTENSION = "xls"; + private static final String OPENXML_EXTENSION = "xlsx"; + + /** + * Create a new instance of the UpdateEmbeddedDoc class using the following + * parameters; + * + * @param filename An instance of the String class that encapsulates the name + * of and path to a WordprocessingML Word document that contains an + * embedded binary Excel workbook. + * @throws java.io.FileNotFoundException Thrown if the file cannot be found + * on the underlying file system. + * @throws java.io.IOException Thrown if a problem occurs in the underlying + * file system. + */ + public UpdateEmbeddedDoc(String filename) throws FileNotFoundException, IOException { + this.docFile = new File(filename); + FileInputStream fis = null; + if (!this.docFile.exists()) { + throw new FileNotFoundException("The Word dcoument " + + filename + + " does not exist."); + } + try { + + // Open the Word document file and instantiate the XWPFDocument + // class. + fis = new FileInputStream(this.docFile); + this.doc = new XWPFDocument(fis); + } finally { + if (fis != null) { + try { + fis.close(); + fis = null; + } catch (IOException ioEx) { + System.out.println("IOException caught trying to close " + + "FileInputStream in the constructor of " + + "UpdateEmbeddedDoc."); + } + } + } + } + + /** + * Called to update the embedded Excel workbook. As the format and structire + * of the workbook are known in advance, all this code attempts to do is + * write a new value into the first cell on the first row of the first + * worksheet. Prior to executing this method, that cell will contain the + * value 1. + * + * @throws org.apache.poi.openxml4j.exceptions.OpenXML4JException + * Rather + * than use the specific classes (HSSF/XSSF) to handle the embedded + * workbook this method uses those defeined in the SS stream. As + * a result, it might be the case that a SpreadsheetML file is + * opened for processing, throwing this exception if that file is + * invalid. + * @throws java.io.IOException Thrown if a problem occurs in the underlying + * file system. + */ + public void updateEmbeddedDoc() throws OpenXML4JException, IOException { + Workbook workbook = null; + Sheet sheet = null; + Row row = null; + Cell cell = null; + PackagePart pPart = null; + Iterator pIter = null; + List embeddedDocs = this.doc.getAllEmbedds(); + if (embeddedDocs != null && !embeddedDocs.isEmpty()) { + pIter = embeddedDocs.iterator(); + while (pIter.hasNext()) { + pPart = pIter.next(); + if (pPart.getPartName().getExtension().equals(BINARY_EXTENSION) || + pPart.getPartName().getExtension().equals(OPENXML_EXTENSION)) { + + // Get an InputStream from the pacage part and pass that + // to the create method of the WorkbookFactory class. Update + // the resulting Workbook and then stream that out again + // using an OutputStream obtained from the same PackagePart. + workbook = WorkbookFactory.create(pPart.getInputStream()); + sheet = workbook.getSheetAt(SHEET_NUM); + row = sheet.getRow(ROW_NUM); + cell = row.getCell(CELL_NUM); + cell.setCellValue(NEW_VALUE); + workbook.write(pPart.getOutputStream()); + } + } + + // Finally, write the newly modified Word document out to file. + this.doc.write(new FileOutputStream(this.docFile)); + } + } + + /** + * Called to test whether or not the embedded workbook was correctly + * updated. This method simply recovers the first cell from the first row + * of the first workbook and tests the value it contains. + *

+ * Note that execution will not continue up to the assertion as the + * embedded workbook is now corrupted and causes an IllegalArgumentException + * with the following message + *

+ * java.lang.IllegalArgumentException: Your InputStream was neither an + * OLE2 stream, nor an OOXML stream + *

+ * to be thrown when the WorkbookFactory.createWorkbook(InputStream) method + * is executed. + * + * @throws org.apache.poi.openxml4j.exceptions.OpenXML4JException + * Rather + * than use the specific classes (HSSF/XSSF) to handle the embedded + * workbook this method uses those defeined in the SS stream. As + * a result, it might be the case that a SpreadsheetML file is + * opened for processing, throwing this exception if that file is + * invalid. + * @throws java.io.IOException Thrown if a problem occurs in the underlying + * file system. + */ + public void checkUpdatedDoc() throws OpenXML4JException, IOException { + Workbook workbook = null; + Sheet sheet = null; + Row row = null; + Cell cell = null; + PackagePart pPart = null; + Iterator pIter = null; + List embeddedDocs = this.doc.getAllEmbedds(); + if (embeddedDocs != null && !embeddedDocs.isEmpty()) { + pIter = embeddedDocs.iterator(); + while (pIter.hasNext()) { + pPart = pIter.next(); + if (pPart.getPartName().getExtension().equals(BINARY_EXTENSION) || + pPart.getPartName().getExtension().equals(OPENXML_EXTENSION)) { + workbook = WorkbookFactory.create(pPart.getInputStream()); + sheet = workbook.getSheetAt(SHEET_NUM); + row = sheet.getRow(ROW_NUM); + cell = row.getCell(CELL_NUM); + Assert.assertEquals(cell.getNumericCellValue(), NEW_VALUE); + } + } + } + } + + /** + * Code to test updating of the embedded Excel workbook. + * + * @param args + */ + public static void main(String[] args) { + try { + UpdateEmbeddedDoc ued = new UpdateEmbeddedDoc(args[0]); + ued.updateEmbeddedDoc(); + ued.checkUpdatedDoc(); + } catch (Exception ex) { + System.out.println(ex.getClass().getName()); + System.out.println(ex.getMessage()); + ex.printStackTrace(System.out); + } + } +} diff --git a/test-data/document/EmbeddedDocument.docx b/test-data/document/EmbeddedDocument.docx new file mode 100644 index 0000000000000000000000000000000000000000..1cb35a5cf7b48bd4b8fc4a13571a46591b60798f GIT binary patch literal 13268 zcmbVz1yo$i(k|}q9^BpC-CY9&cXxLPt^tBO!QI`R;I6^lA^3ybbN`c*_5SnLdNs3V zX7{e@?%g%jyZY;I%S!=+pa4L;y%@v8cmRGA=(pdx_C}TtbhPiEWpUpnff*1&&K^

d5%*f@~C4? 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 B$&dg5 literal 0 HcmV?d00001