diff options
author | Andreas Beeker <kiwiwings@apache.org> | 2017-12-31 01:14:08 +0000 |
---|---|---|
committer | Andreas Beeker <kiwiwings@apache.org> | 2017-12-31 01:14:08 +0000 |
commit | 704b41ab9a11574bcce25a63c4bf43670389f841 (patch) | |
tree | f66d8dd2fd373c1506cf644d9fd648d2225c298f /src/java/org/apache/poi/sl/usermodel | |
parent | cdab1a45110536d6e4abed82152d288b70568d82 (diff) | |
download | poi-704b41ab9a11574bcce25a63c4bf43670389f841.tar.gz poi-704b41ab9a11574bcce25a63c4bf43670389f841.zip |
#61797 - Embed Excel / Ole objects into powerpoint
git-svn-id: https://svn.apache.org/repos/asf/poi/trunk@1819710 13f79535-47bb-0310-9956-ffa450edef68
Diffstat (limited to 'src/java/org/apache/poi/sl/usermodel')
4 files changed, 357 insertions, 0 deletions
diff --git a/src/java/org/apache/poi/sl/usermodel/ObjectData.java b/src/java/org/apache/poi/sl/usermodel/ObjectData.java new file mode 100644 index 0000000000..db30a9b245 --- /dev/null +++ b/src/java/org/apache/poi/sl/usermodel/ObjectData.java @@ -0,0 +1,102 @@ +/* ==================================================================== + 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.sl.usermodel; + +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; + +import org.apache.poi.poifs.filesystem.DirectoryEntry; +import org.apache.poi.poifs.filesystem.FileMagic; +import org.apache.poi.poifs.filesystem.POIFSFileSystem; +import org.apache.poi.util.IOUtils; +import org.apache.poi.util.POILogFactory; +import org.apache.poi.util.POILogger; + +/** + * Common interface for OLE shapes, i.e. shapes linked to embedded documents + * + * @since POI 4.0.0 + */ +public interface ObjectData { + + /** + * Gets an input stream which returns the binary of the embedded data. + * + * @return the input stream which will contain the binary of the embedded data. + */ + InputStream getInputStream() throws IOException; + + + /** + * @return the object data as stream (for writing) + */ + OutputStream getOutputStream() throws IOException; + + /** + * Convenience method to get the embedded data as byte array. + * + * @return the embedded data. + */ + default byte[] getBytes() throws IOException { + try (InputStream is = getInputStream()) { + return IOUtils.toByteArray(is); + } + } + + /** + * @return does this ObjectData have an associated POIFS Directory Entry? + * (Not all do, those that don't have a data portion) + */ + default boolean hasDirectoryEntry() { + try (final InputStream is = FileMagic.prepareToCheckMagic(getInputStream())) { + FileMagic fm = FileMagic.valueOf(is); + return fm == FileMagic.OLE2; + } catch (IOException e) { + POILogger LOG = POILogFactory.getLogger(ObjectData.class); + LOG.log(POILogger.WARN, "Can't determine filemagic of ole stream", e); + return false; + } + } + + /** + * Gets the object data. Only call for ones that have + * data though. See {@link #hasDirectoryEntry()}. + * The caller has to close the corresponding POIFSFileSystem + * + * @return the object data as an OLE2 directory. + * @throws IOException if there was an error reading the data. + */ + @SuppressWarnings("resource") + default DirectoryEntry getDirectory() throws IOException { + try (final InputStream is = getInputStream()) { + return new POIFSFileSystem(is).getRoot(); + } + } + + /** + * @return the OLE2 Class Name of the object + */ + String getOLE2ClassName(); + + /** + * @return a filename suggestion - inspecting/interpreting the Directory object probably gives a better result + */ + String getFileName(); + +} diff --git a/src/java/org/apache/poi/sl/usermodel/ObjectMetaData.java b/src/java/org/apache/poi/sl/usermodel/ObjectMetaData.java new file mode 100644 index 0000000000..1851b0c8b0 --- /dev/null +++ b/src/java/org/apache/poi/sl/usermodel/ObjectMetaData.java @@ -0,0 +1,96 @@ +/* ==================================================================== + 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.sl.usermodel; + +import org.apache.poi.hpsf.ClassID; +import org.apache.poi.hpsf.ClassIDPredefined; +import org.apache.poi.util.Beta; + +@Beta +public interface ObjectMetaData { + enum Application { + EXCEL_V8("Worksheet", "Excel.Sheet.8", "Package", ClassIDPredefined.EXCEL_V8), + EXCEL_V12("Worksheet", "Excel.Sheet.12", "Package", ClassIDPredefined.EXCEL_V12), + WORD_V8("Document", "Word.Document.8", "Package", ClassIDPredefined.WORD_V8), + WORD_V12("Document", "Word.Document.12", "Package", ClassIDPredefined.WORD_V12), + PDF("PDF", "AcroExch.Document", "Contents", ClassIDPredefined.PDF), + CUSTOM(null, null, null, null); + + String objectName; + String progId; + String oleEntry; + ClassID classId; + + Application(String objectName, String progId, String oleEntry, ClassIDPredefined classId) { + this.objectName = objectName; + this.progId = progId; + this.classId = (classId == null) ? null : classId.getClassID(); + this.oleEntry = oleEntry; + } + + public static Application lookup(String progId) { + for (Application a : values()) { + if (a.progId != null && a.progId.equals(progId)) { + return a; + } + } + return null; + } + + + public ObjectMetaData getMetaData() { + return new ObjectMetaData() { + public String getObjectName() { + return objectName; + } + + public String getProgId() { + return progId; + } + + public String getOleEntry() { + return oleEntry; + } + + public ClassID getClassID() { + return classId; + } + }; + } + } + + /** + * @return the name of the OLE shape + */ + String getObjectName(); + + /** + * @return the program id assigned to the OLE container application + */ + String getProgId(); + + /** + * @return the storage classid of the OLE entry + */ + ClassID getClassID(); + + /** + * @return the name of the OLE entry inside the oleObject#.bin + */ + String getOleEntry(); +} diff --git a/src/java/org/apache/poi/sl/usermodel/ObjectShape.java b/src/java/org/apache/poi/sl/usermodel/ObjectShape.java new file mode 100644 index 0000000000..c7d421950e --- /dev/null +++ b/src/java/org/apache/poi/sl/usermodel/ObjectShape.java @@ -0,0 +1,152 @@ +/* ==================================================================== + 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.sl.usermodel; + +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; + +import org.apache.poi.poifs.filesystem.DirectoryNode; +import org.apache.poi.poifs.filesystem.FileMagic; +import org.apache.poi.poifs.filesystem.POIFSFileSystem; +import org.apache.poi.sl.usermodel.ObjectMetaData.Application; +import org.apache.poi.util.IOUtils; + +/** + * An shape which references an embedded OLE object + * + * @since POI 4.0.0 + */ +public interface ObjectShape< + S extends Shape<S,P>, + P extends TextParagraph<S,P,? extends TextRun> +> extends Shape<S,P>, PlaceableShape<S,P> { + + /** + * Returns the picture data for this picture. + * + * @return the picture data for this picture. + */ + PictureData getPictureData(); + + /** + * Returns the ProgID that stores the OLE Programmatic Identifier. + * A ProgID is a string that uniquely identifies a given object, for example, + * "Word.Document.8" or "Excel.Sheet.8". + * + * @return the ProgID + */ + String getProgId(); + + /** + * Returns the full name of the embedded object, + * e.g. "Microsoft Word Document" or "Microsoft Office Excel Worksheet". + * + * @return the full name of the embedded object + */ + String getFullName(); + + /** + * Updates the ole data. If there wasn't an object registered before, a new + * ole embedding is registered in the parent slideshow.<p> + * + * For HSLF this needs to be a {@link POIFSFileSystem} stream. + * + * @param application a preset application enum + * @param metaData or a custom metaData object, can be {@code null} if the application has been set + * + * @return an {@link OutputStream} which receives the new data, the data will be persisted on {@code close()} + * + * @throws IOException if the linked object data couldn't be found or a new object data couldn't be initialized + */ + OutputStream updateObjectData(ObjectMetaData.Application application, ObjectMetaData metaData) throws IOException; + + /** + * Reads the ole data as stream - the application specific stream is served + * The {@link #readObjectDataRaw() raw data} serves the outer/wrapped object, which is usually a + * {@link POIFSFileSystem} stream, whereas this method return the unwrapped entry + * + * @return an {@link InputStream} which serves the object data + * + * @throws IOException if the linked object data couldn't be found + */ + default InputStream readObjectData() throws IOException { + final String progId = getProgId(); + if (progId == null) { + throw new IllegalStateException( + "Ole object hasn't been initialized or provided in the source xml. " + + "use updateObjectData() first or check the corresponding slideXXX.xml"); + } + + final Application app = Application.lookup(progId); + + final ByteArrayOutputStream bos = new ByteArrayOutputStream(50000); + try (final InputStream is = FileMagic.prepareToCheckMagic(readObjectDataRaw())) { + final FileMagic fm = FileMagic.valueOf(is); + if (fm == FileMagic.OLE2) { + try (final POIFSFileSystem poifs = new POIFSFileSystem(is)) { + String[] names = { + (app == null) ? null : app.getMetaData().getOleEntry(), + // fallback to the usual suspects + "Package", + "Contents", + "CONTENTS", + "CONTENTSV30", + }; + final DirectoryNode root = poifs.getRoot(); + String entryName = null; + for (String n : names) { + if (root.hasEntry(n)) { + entryName = n; + break; + } + } + if (entryName == null) { + poifs.writeFilesystem(bos); + } else { + try (final InputStream is2 = poifs.createDocumentInputStream(entryName)) { + IOUtils.copy(is2, bos); + } + } + } + } else { + IOUtils.copy(is, bos); + } + } + + return new ByteArrayInputStream(bos.toByteArray()); + } + + /** + * Convenience method to return the raw data as {@code InputStream} + * + * @return the raw data stream + * + * @throws IOException if the data couldn't be retrieved + */ + default InputStream readObjectDataRaw() throws IOException { + return getObjectData().getInputStream(); + } + + /** + * @return the data object + */ + ObjectData getObjectData(); +} diff --git a/src/java/org/apache/poi/sl/usermodel/ShapeContainer.java b/src/java/org/apache/poi/sl/usermodel/ShapeContainer.java index ba02e4b05e..7706828c52 100644 --- a/src/java/org/apache/poi/sl/usermodel/ShapeContainer.java +++ b/src/java/org/apache/poi/sl/usermodel/ShapeContainer.java @@ -84,4 +84,11 @@ public interface ShapeContainer< * @param numCols the number of columns */ TableShape<S,P> createTable(int numRows, int numCols); + + /** + * Create a new OLE object shape with the given pictureData as preview image + * + * @param pictureData the preview image + */ + ObjectShape<?,?> createOleShape(PictureData pictureData); } |