--- src/scratchpad/src/org/apache/poi/hslf/usermodel/ObjectData.java (revision 0) +++ src/scratchpad/src/org/apache/poi/hslf/usermodel/ObjectData.java (revision 0) @@ -0,0 +1,51 @@ +/* ==================================================================== + 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.hslf.usermodel; + +import java.io.InputStream; + +import org.apache.poi.hslf.record.ExOleObjStg; + +/** + * A class that represents object data embedded in a slide show. + * + * @author Daniel Noll + */ +public class ObjectData { + /** + * The record that contains the object data. + */ + private ExOleObjStg storage; + + /** + * Creates the object data wrapping the record that contains the object data. + * + * @param storage the record that contains the object data. + */ + public ObjectData(ExOleObjStg storage) { + this.storage = storage; + } + + /** + * 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. + */ + public InputStream getData() { + return storage.getData(); + } +} --- src/scratchpad/src/org/apache/poi/hslf/record/RecordTypes.java (revision 2744) +++ src/scratchpad/src/org/apache/poi/hslf/record/RecordTypes.java (working copy) @@ -98,11 +98,11 @@ public static final Type FontEmbeddedData = new Type(4024,null); public static final Type CString = new Type(4026,CString.class); public static final Type MetaFile = new Type(4033,null); - public static final Type ExOleObjAtom = new Type(4035,null); + public static final Type ExOleObjAtom = new Type(4035,ExOleObjAtom.class); public static final Type SrKinsoku = new Type(4040,null); public static final Type HandOut = new Type(4041,null); - public static final Type ExEmbed = new Type(4044,null); - public static final Type ExEmbedAtom = new Type(4045,null); + public static final Type ExEmbed = new Type(4044,ExEmbed.class); + public static final Type ExEmbedAtom = new Type(4045,ExEmbedAtom.class); public static final Type ExLink = new Type(4046,null); public static final Type BookmarkEntityAtom = new Type(4048,null); public static final Type ExLinkAtom = new Type(4049,null); @@ -136,7 +136,7 @@ public static final Type ExCDAudio = new Type(4110,null); public static final Type ExWAVAudioEmbedded = new Type(4111,null); public static final Type ExWAVAudioLink = new Type(4112,null); - public static final Type ExOleObjStg = new Type(4113,null); + public static final Type ExOleObjStg = new Type(4113,ExOleObjStg.class); public static final Type ExCDAudioAtom = new Type(4114,null); public static final Type ExWAVAudioEmbeddedAtom = new Type(4115,null); public static final Type AnimationInfoAtom = new Type(4116,null); --- src/scratchpad/src/org/apache/poi/hslf/record/ExOleObjStg.java (revision 0) +++ src/scratchpad/src/org/apache/poi/hslf/record/ExOleObjStg.java (revision 0) @@ -0,0 +1,112 @@ +/* ==================================================================== + 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.hslf.record; + +import java.io.ByteArrayInputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.util.zip.InflaterInputStream; + +import org.apache.poi.util.LittleEndian; + +/** + * Storage for embedded OLE objects. + * + * @author Daniel Noll + */ +public class ExOleObjStg extends RecordAtom { + /** + * Record header. + */ + private byte[] _header; + + /** + * Record data. + */ + private byte[] _data; + + /** + * Constructs a new empty storage container. + */ + protected ExOleObjStg() { + _header = new byte[8]; + _data = new byte[0]; + + LittleEndian.putShort(_header, 2, (short)getRecordType()); + LittleEndian.putInt(_header, 4, _data.length); + } + + /** + * Constructs the link related atom record from its + * source data. + * + * @param source the source data as a byte array. + * @param start the start offset into the byte array. + * @param len the length of the slice in the byte array. + */ + protected ExOleObjStg(byte[] source, int start, int len) { + // Get the header. + _header = new byte[8]; + System.arraycopy(source,start,_header,0,8); + + // Get the record data. + _data = new byte[len-8]; + System.arraycopy(source,start+8,_data,0,len-8); + } + + /** + * Gets the uncompressed length of the data. + * + * @return the uncompressed length of the data. + */ + public int getDataLength() { + return LittleEndian.getInt(_data, 0); + } + + /** + * Opens an input stream which will decompress the data on the fly. + * + * @return the data input stream. + */ + public InputStream getData() { + InputStream compressedStream = new ByteArrayInputStream(_data, 4, _data.length); + return new InflaterInputStream(compressedStream); + } + + /** + * Gets the record type. + * + * @return the record type. + */ + public long getRecordType() { + return RecordTypes.ExOleObjStg.typeID; + } + + /** + * Write the contents of the record back, so it can be written + * to disk. + * + * @param out the output stream to write to. + * @throws IOException if an error occurs. + */ + public void writeOut(OutputStream out) throws IOException { + out.write(_header); + out.write(_data); + } +} --- src/scratchpad/src/org/apache/poi/hslf/record/ExOleObjAtom.java (revision 0) +++ src/scratchpad/src/org/apache/poi/hslf/record/ExOleObjAtom.java (revision 0) @@ -0,0 +1,213 @@ +/* ==================================================================== + 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.hslf.record; + +import java.io.IOException; +import java.io.OutputStream; + +import org.apache.poi.util.LittleEndian; + +/** + * Atom storing information for an OLE object. + * + * + * + * @author Daniel Noll + */ +public class ExOleObjAtom extends RecordAtom { + + public static final int DRAW_ASPECT_VISIBLE = 1; + public static final int DRAW_ASPECT_ICON = 4; + + public static final int TYPE_EMBEDDED = 0; + public static final int TYPE_LINKED = 1; + + public static final int SUBTYPE_DEFAULT = 0; + public static final int SUBTYPE_CLIPART_GALLERY = 1; + public static final int SUBTYPE_WORD_TABLE = 2; + public static final int SUBTYPE_EXCEL = 3; + public static final int SUBTYPE_GRAPH = 4; + public static final int SUBTYPE_ORGANIZATION_CHART = 5; + public static final int SUBTYPE_EQUATION = 6; + public static final int SUBTYPE_WORDART = 7; + public static final int SUBTYPE_SOUND = 8; + public static final int SUBTYPE_IMAGE = 9; + public static final int SUBTYPE_POWERPOINT_PRESENTATION = 10; + public static final int SUBTYPE_POWERPOINT_SLIDE = 11; + public static final int SUBTYPE_PROJECT = 12; + public static final int SUBTYPE_NOTEIT = 13; + public static final int SUBTYPE_EXCEL_CHART = 14; + public static final int SUBTYPE_MEDIA_PLAYER = 15; + + /** + * Record header. + */ + private byte[] _header; + + /** + * Record data. + */ + private byte[] _data; + + /** + * Constructs a brand new link related atom record. + */ + protected ExOleObjAtom() { + _header = new byte[8]; + _data = new byte[18]; + + LittleEndian.putShort(_header, 2, (short)getRecordType()); + LittleEndian.putInt(_header, 4, _data.length); + + // I hope it is fine for the other values to be zero. + } + + /** + * Constructs the link related atom record from its + * source data. + * + * @param source the source data as a byte array. + * @param start the start offset into the byte array. + * @param len the length of the slice in the byte array. + */ + protected ExOleObjAtom(byte[] source, int start, int len) { + // Get the header. + _header = new byte[8]; + System.arraycopy(source,start,_header,0,8); + + // Get the record data. + _data = new byte[len-8]; + System.arraycopy(source,start+8,_data,0,len-8); + + // Must be at least 24 bytes long + if(_data.length < 24) { + throw new IllegalArgumentException("The length of the data for a ExOleObjAtom must be at least 24 bytes, but was only " + _data.length); + } + } + + /** + * Gets whether the object can be completely seen, or if only the + * icon is visible. + * + * @return the draw aspect, one of the {@code DRAW_ASPECT_*} constants. + */ + public int getDrawAspect() { + return LittleEndian.getInt(_data, 0); + } + + /** + * Gets whether the object is embedded or linked. + * + * @return the type, one of the {@code TYPE_EMBEDDED_*} constants. + */ + public int getType() { + return LittleEndian.getInt(_data, 4); + } + + /** + * Gets the unique identifier for the OLE object. + * + * @return the object ID. + */ + public int getObjID() { + return LittleEndian.getInt(_data, 8); + } + + /** + * Gets the type of OLE object. + * + * @return the sub-type, one of the {@code SUBTYPE_*} constants. + */ + public int getSubType() { + return LittleEndian.getInt(_data, 12); + } + + /** + * Gets the reference to the persistent object + * + * @return the reference to the persistent object, corresponds with an + * {@code ExOleObjStg} storage container. + */ + public int getObjStgDataRef() { + return LittleEndian.getInt(_data, 16); + } + + /** + * Gets whether the object's image is blank. + * + * @return {@code true} if the object's image is blank. + */ + public boolean getIsBlank() { + // Even though this is a mere boolean, KOffice's code says it's an int. + return LittleEndian.getInt(_data, 20) != 0; + } + + /** + * Returns the type (held as a little endian in bytes 3 and 4) + * that this class handles. + */ + public long getRecordType() { + return RecordTypes.ExOleObjAtom.typeID; + } + + /** + * Have the contents printer out into an OutputStream, used when + * writing a file back out to disk + * (Normally, atom classes will keep their bytes around, but + * non atom classes will just request the bytes from their + * children, then chuck on their header and return) + */ + public void writeOut(OutputStream out) throws IOException { + out.write(_header); + out.write(_data); + } +} --- src/scratchpad/src/org/apache/poi/hslf/record/ExEmbedAtom.java (revision 0) +++ src/scratchpad/src/org/apache/poi/hslf/record/ExEmbedAtom.java (revision 0) @@ -0,0 +1,161 @@ +/* ==================================================================== + 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.hslf.record; + +import java.io.IOException; +import java.io.OutputStream; + +import org.apache.poi.util.LittleEndian; + +/** + * The atom that holds metadata on a specific embedded object in the document. + * + * + * + * @author Daniel Noll + */ +public class ExEmbedAtom extends RecordAtom { + + /** + * Embedded document does not follow the color scheme. + */ + public static final int DOES_NOT_FOLLOW_COLOR_SCHEME = 0; + + /** + * Embedded document follows the entire color scheme. + */ + public static final int FOLLOWS_ENTIRE_COLOR_SCHEME = 1; + + /** + * Embedded document follows the text and background scheme. + */ + public static final int FOLLOWS_TEXT_AND_BACKGROUND_SCHEME = 2; + + /** + * Record header. + */ + private byte[] _header; + + /** + * Record data. + */ + private byte[] _data; + + /** + * Constructs a brand new embedded object atom record. + */ + protected ExEmbedAtom() { + _header = new byte[8]; + _data = new byte[7]; + + LittleEndian.putShort(_header, 2, (short)getRecordType()); + LittleEndian.putInt(_header, 4, _data.length); + + // It is fine for the other values to be zero + } + + /** + * Constructs the embedded object atom record from its source data. + * + * @param source the source data as a byte array. + * @param start the start offset into the byte array. + * @param len the length of the slice in the byte array. + */ + protected ExEmbedAtom(byte[] source, int start, int len) { + // Get the header. + _header = new byte[8]; + System.arraycopy(source,start,_header,0,8); + + // Get the record data. + _data = new byte[len-8]; + System.arraycopy(source,start+8,_data,0,len-8); + + // Must be at least 4 bytes long + if(_data.length < 7) { + throw new IllegalArgumentException("The length of the data for a ExEmbedAtom must be at least 4 bytes, but was only " + _data.length); + } + } + + /** + * Gets whether the object follows the color scheme. + * + * @return one of {@link #DOES_NOT_FOLLOW_COLOR_SCHEME}, + * {@link #FOLLOWS_ENTIRE_COLOR_SCHEME}, or + * {@link #FOLLOWS_TEXT_AND_BACKGROUND_SCHEME}. + */ + public int getFollowColorScheme() { + return LittleEndian.getInt(_data, 0); + } + + /** + * Gets whether the embedded server cannot be locked. + * + * @return {@code true} if the embedded server cannot be locked. + */ + public boolean getCantLockServerB() { + return _data[4] != 0; + } + + /** + * Gets whether it is not required to send the dimensions to the embedded object. + * + * @return {@code true} if the embedded server does not require the object dimensions. + */ + public boolean getNoSizeToServerB() { + return _data[5] != 0; + } + + /** + * Getswhether the object is a Word table. + * + * @return {@code true} if the object is a Word table. + */ + public boolean getIsTable() { + return _data[6] != 0; + } + + /** + * Gets the record type. + * @return the record type. + */ + public long getRecordType() { + return RecordTypes.ExEmbedAtom.typeID; + } + + /** + * Write the contents of the record back, so it can be written + * to disk + * + * @param out the output stream to write to. + * @throws IOException if an error occurs. + */ + public void writeOut(OutputStream out) throws IOException { + out.write(_header); + out.write(_data); + } + +} --- src/scratchpad/src/org/apache/poi/hslf/record/CString.java (revision 2744) +++ src/scratchpad/src/org/apache/poi/hslf/record/CString.java (working copy) @@ -111,4 +111,12 @@ // Write out our text out.write(_text); } + + /** + * Gets a string representation of this object, primarily for debugging. + * @return a string representation of this object. + */ + public String toString() { + return getText(); + } } --- src/scratchpad/src/org/apache/poi/hslf/record/ExEmbed.java (revision 0) +++ src/scratchpad/src/org/apache/poi/hslf/record/ExEmbed.java (revision 0) @@ -0,0 +1,188 @@ +/* ==================================================================== + 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.hslf.record; + +import java.io.OutputStream; +import java.io.IOException; + +import org.apache.poi.util.LittleEndian; +import org.apache.poi.util.POILogger; + +/** + * This data represents an embedded object in the document. + * + * @author Daniel Noll + */ +public class ExEmbed extends RecordContainer { + + /** + * Record header data. + */ + private byte[] _header; + + // Links to our more interesting children + private ExEmbedAtom embedAtom; + private ExOleObjAtom oleObjAtom; + private CString menuName; + private CString progId; + private CString clipboardName; + + /** + * Set things up, and find our more interesting children + * + * @param source the source data as a byte array. + * @param start the start offset into the byte array. + * @param len the length of the slice in the byte array. + */ + protected ExEmbed(byte[] source, int start, int len) { + // Grab the header + _header = new byte[8]; + System.arraycopy(source,start,_header,0,8); + + // Find our children + _children = Record.findChildRecords(source,start+8,len-8); + findInterestingChildren(); + } + + /** + * Create a new ExEmbed, with blank fields + */ + public ExEmbed() { + _header = new byte[8]; + _children = new Record[5]; + + // Setup our header block + _header[0] = 0x0f; // We are a container record + LittleEndian.putShort(_header, 2, (short)getRecordType()); + + // Setup our child records + CString cs1 = new CString(); + CString cs2 = new CString(); + CString cs3 = new CString(); +// cs1.setCount(0x00); +// cs2.setCount(0x10); + _children[0] = new ExEmbedAtom(); + _children[1] = new ExOleObjAtom(); + _children[2] = cs1; + _children[3] = cs2; + _children[4] = cs3; + findInterestingChildren(); + } + + /** + * Go through our child records, picking out the ones that are + * interesting, and saving those for use by the easy helper methods. + */ + private void findInterestingChildren() { + + // First child should be the ExHyperlinkAtom + if(_children[0] instanceof ExEmbedAtom) { + embedAtom = (ExEmbedAtom)_children[0]; + } else { + logger.log(POILogger.ERROR, "First child record wasn't a ExEmbedAtom, was of type " + _children[0].getRecordType()); + } + + // Second child should be the ExOleObjAtom + if (_children[1] instanceof ExOleObjAtom) { + oleObjAtom = (ExOleObjAtom)_children[1]; + } else { + logger.log(POILogger.ERROR, "Second child record wasn't a ExOleObjAtom, was of type " + _children[1].getRecordType()); + } + + for (int i = 2; i < _children.length; i++) { + if (_children[i] instanceof CString){ + if (menuName == null) menuName = (CString)_children[i]; + else if (progId == null) progId = (CString)_children[i]; + else if (clipboardName == null) clipboardName = (CString)_children[i]; + } else { + logger.log(POILogger.ERROR, "Record after atoms wasn't a CString, was of type " + _children[i].getRecordType()); + } + } + } + + /** + * Gets the {@code ExEmbedAtom}. + * + * @return the {@code ExEmbedAtom}. + */ + public ExEmbedAtom getExEmbedAtom() + { + return embedAtom; + } + + /** + * Gets the {@code ExOleObjAtom}. + * + * @return the {@code ExOleObjAtom}. + */ + public ExOleObjAtom getExOleObjAtom() + { + return oleObjAtom; + } + + /** + * Gets the name used for menus and the Links dialog box. + * + * @return the name used for menus and the Links dialog box. + */ + public String getMenuName() + { + return menuName == null ? null : menuName.getText(); + } + + /** + * Gets the OLE Programmatic Identifier. + * + * @return the OLE Programmatic Identifier. + */ + public String getProgId() + { + return progId == null ? null : progId.getText(); + } + + /** + * Gets the name that appears in the paste special dialog. + * + * @return the name that appears in the paste special dialog. + */ + public String getClipboardName() + { + return clipboardName == null ? null : clipboardName.getText(); + } + + /** + * Returns the type (held as a little endian in bytes 3 and 4) + * that this class handles. + * + * @return the record type. + */ + public long getRecordType() { + return RecordTypes.ExEmbed.typeID; + } + + /** + * Have the contents printer out into an OutputStream, used when + * writing a file back out to disk. + * + * @param out the output stream. + * @throws IOException if there was an error writing to the stream. + */ + public void writeOut(OutputStream out) throws IOException { + writeOut(_header[0],_header[1],getRecordType(),_children,out); + } +} --- src/scratchpad/src/org/apache/poi/hslf/HSLFSlideShow.java (revision 2744) +++ src/scratchpad/src/org/apache/poi/hslf/HSLFSlideShow.java (working copy) @@ -20,28 +20,37 @@ package org.apache.poi.hslf; -import java.util.*; -import java.io.*; +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.FileInputStream; +import java.io.FileNotFoundException; +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Hashtable; +import java.util.Iterator; +import java.util.List; import org.apache.poi.POIDocument; -import org.apache.poi.util.LittleEndian; -import org.apache.poi.util.POILogger; -import org.apache.poi.util.POILogFactory; -import org.apache.poi.poifs.filesystem.POIFSFileSystem; -import org.apache.poi.poifs.filesystem.DocumentEntry; -import org.apache.poi.poifs.filesystem.DocumentInputStream; - -import org.apache.poi.hpsf.PropertySet; -import org.apache.poi.hpsf.PropertySetFactory; -import org.apache.poi.hpsf.MutablePropertySet; -import org.apache.poi.hpsf.SummaryInformation; -import org.apache.poi.hpsf.DocumentSummaryInformation; - import org.apache.poi.hslf.exceptions.CorruptPowerPointFileException; import org.apache.poi.hslf.exceptions.EncryptedPowerPointFileException; import org.apache.poi.hslf.exceptions.HSLFException; -import org.apache.poi.hslf.record.*; +import org.apache.poi.hslf.record.CurrentUserAtom; +import org.apache.poi.hslf.record.ExOleObjStg; +import org.apache.poi.hslf.record.PersistPtrHolder; +import org.apache.poi.hslf.record.PositionDependentRecord; +import org.apache.poi.hslf.record.Record; +import org.apache.poi.hslf.record.UserEditAtom; +import org.apache.poi.hslf.usermodel.ObjectData; import org.apache.poi.hslf.usermodel.PictureData; +import org.apache.poi.poifs.filesystem.DocumentEntry; +import org.apache.poi.poifs.filesystem.DocumentInputStream; +import org.apache.poi.poifs.filesystem.POIFSFileSystem; +import org.apache.poi.util.LittleEndian; +import org.apache.poi.util.POILogFactory; +import org.apache.poi.util.POILogger; /** * This class contains the main functionality for the Powerpoint file @@ -68,8 +77,11 @@ // Raw Pictures contained in the pictures stream private PictureData[] _pictures; - - /** + + // Embedded objects stored in storage records in the document stream, lazily populated. + private ObjectData[] _objects; + + /** * Returns the underlying POIFSFileSystem for the document * that is open. */ @@ -507,4 +519,22 @@ public PictureData[] getPictures() { return _pictures; } + + /** + * Gets embedded object data from the slide show. + * + * @return the embedded objects. + */ + public ObjectData[] getEmbeddedObjects() { + if (_objects == null) { + List objects = new ArrayList(); + for (int i = 0; i < _records.length; i++) { + if (_records[i] instanceof ExOleObjStg) { + objects.add(new ObjectData((ExOleObjStg) _records[i])); + } + } + _objects = (ObjectData[]) objects.toArray(new ObjectData[objects.size()]); + } + return _objects; + } }