diff --git a/src/scratchpad/src/org/apache/poi/hslf/HSLFSlideShow.java b/src/scratchpad/src/org/apache/poi/hslf/HSLFSlideShow.java
index ed2de1af2..f6bd2cf98 100644
--- a/src/scratchpad/src/org/apache/poi/hslf/HSLFSlideShow.java
+++ b/src/scratchpad/src/org/apache/poi/hslf/HSLFSlideShow.java
@@ -23,7 +23,6 @@ import java.util.*;
import java.io.*;
import org.apache.poi.poifs.filesystem.POIFSFileSystem;
-import org.apache.poi.poifs.filesystem.POIFSDocument;
import org.apache.poi.poifs.filesystem.DocumentEntry;
import org.apache.poi.poifs.filesystem.DocumentInputStream;
@@ -33,10 +32,8 @@ import org.apache.poi.hpsf.MutablePropertySet;
import org.apache.poi.hpsf.SummaryInformation;
import org.apache.poi.hpsf.DocumentSummaryInformation;
-import org.apache.poi.util.LittleEndian;
-
import org.apache.poi.hslf.record.*;
-import org.apache.poi.hslf.usermodel.Picture;
+import org.apache.poi.hslf.usermodel.PictureData;
/**
* This class contains the main functionality for the Powerpoint file
@@ -47,78 +44,91 @@ import org.apache.poi.hslf.usermodel.Picture;
public class HSLFSlideShow
{
- private InputStream istream;
- private POIFSFileSystem filesystem;
+ private InputStream istream;
+ private POIFSFileSystem filesystem;
- // Holds metadata on our document
- private SummaryInformation sInf;
- private DocumentSummaryInformation dsInf;
- private CurrentUserAtom currentUser;
+ // Holds metadata on our document
+ private SummaryInformation sInf;
+ private DocumentSummaryInformation dsInf;
+ private CurrentUserAtom currentUser;
- // Low level contents of the file
- private byte[] _docstream;
+ // Low level contents of the file
+ private byte[] _docstream;
- // Low level contents
- private Record[] _records;
+ // Low level contents
+ private Record[] _records;
- /**
- * Constructs a Powerpoint document from fileName. Parses the document
- * and places all the important stuff into data structures.
- *
- * @param fileName The name of the file to read.
- * @throws IOException if there is a problem while parsing the document.
- */
- public HSLFSlideShow(String fileName) throws IOException
- {
- this(new FileInputStream(fileName));
- }
+ // Raw Pictures contained in the pictures stream
+ private PictureData[] _pictures;
+
+ /**
+ * Constructs a Powerpoint document from fileName. Parses the document
+ * and places all the important stuff into data structures.
+ *
+ * @param fileName The name of the file to read.
+ * @throws IOException if there is a problem while parsing the document.
+ */
+ public HSLFSlideShow(String fileName) throws IOException
+ {
+ this(new FileInputStream(fileName));
+ }
- /**
- * Constructs a Powerpoint document from an input stream. Parses the
- * document and places all the important stuff into data structures.
- *
- * @param inputStream the source of the data
- * @throws IOException if there is a problem while parsing the document.
- */
- public HSLFSlideShow(InputStream inputStream) throws IOException
- {
- //do Ole stuff
+ /**
+ * Constructs a Powerpoint document from an input stream. Parses the
+ * document and places all the important stuff into data structures.
+ *
+ * @param inputStream the source of the data
+ * @throws IOException if there is a problem while parsing the document.
+ */
+ public HSLFSlideShow(InputStream inputStream) throws IOException
+ {
+ //do Ole stuff
this(new POIFSFileSystem(inputStream));
- istream = inputStream;
- }
+ istream = inputStream;
+ }
- /**
- * Constructs a Powerpoint document from a POIFS Filesystem. Parses the
- * document and places all the important stuff into data structures.
- *
- * @param filesystem the POIFS FileSystem to read from
- * @throws IOException if there is a problem while parsing the document.
- */
- public HSLFSlideShow(POIFSFileSystem filesystem) throws IOException
- {
+ /**
+ * Constructs a Powerpoint document from a POIFS Filesystem. Parses the
+ * document and places all the important stuff into data structures.
+ *
+ * @param filesystem the POIFS FileSystem to read from
+ * @throws IOException if there is a problem while parsing the document.
+ */
+ public HSLFSlideShow(POIFSFileSystem filesystem) throws IOException
+ {
this.filesystem = filesystem;
- // Go find a PowerPoint document in the stream
- // Save anything useful we come across
- readFIB();
+ // Go find a PowerPoint document in the stream
+ // Save anything useful we come across
+ readFIB();
// Look for Property Streams:
readProperties();
- }
-
- /**
- * Shuts things down. Closes underlying streams etc
- *
- * @throws IOException
- */
- public void close() throws IOException
- {
- if(istream != null) {
- istream.close();
+ // Look for Picture Streams:
+ readPictures();
+ }
+
+ /**
+ * Constructs a new, empty, Powerpoint document.
+ */
+ public HSLFSlideShow() throws IOException
+ {
+ this(HSLFSlideShow.class.getResourceAsStream("/org/apache/poi/hslf/data/empty.ppt"));
+ }
+
+ /**
+ * Shuts things down. Closes underlying streams etc
+ *
+ * @throws IOException
+ */
+ public void close() throws IOException
+ {
+ if(istream != null) {
+ istream.close();
+ }
+ filesystem = null;
}
- filesystem = null;
- }
/**
@@ -175,24 +185,52 @@ public class HSLFSlideShow
}
- /**
- * Find the properties from the filesystem, and load them
- */
- public void readProperties() {
- // DocumentSummaryInformation
- dsInf = (DocumentSummaryInformation)getPropertySet("\005DocumentSummaryInformation");
+ /**
+ * Find the properties from the filesystem, and load them
+ */
+ public void readProperties() {
+ // DocumentSummaryInformation
+ dsInf = (DocumentSummaryInformation)getPropertySet("\005DocumentSummaryInformation");
- // SummaryInformation
- sInf = (SummaryInformation)getPropertySet("\005SummaryInformation");
+ // SummaryInformation
+ sInf = (SummaryInformation)getPropertySet("\005SummaryInformation");
- // Current User
- try {
- currentUser = new CurrentUserAtom(filesystem);
- } catch(IOException ie) {
- System.err.println("Error finding Current User Atom:\n" + ie);
- currentUser = new CurrentUserAtom();
+ // Current User
+ try {
+ currentUser = new CurrentUserAtom(filesystem);
+ } catch(IOException ie) {
+ System.err.println("Error finding Current User Atom:\n" + ie);
+ currentUser = new CurrentUserAtom();
+ }
+ }
+
+ /**
+ * Find and read in pictures contained in this presentation
+ */
+ private void readPictures() throws IOException {
+ byte[] pictstream;
+
+ try {
+ DocumentEntry entry = (DocumentEntry)filesystem.getRoot().getEntry("Pictures");
+ pictstream = new byte[entry.getSize()];
+ DocumentInputStream is = filesystem.createDocumentInputStream("Pictures");
+ is.read(pictstream);
+ } catch (FileNotFoundException e){
+ // Silently catch exceptions if the presentation doesn't
+ // contain pictures - will use a null set instead
+ return;
+ }
+
+ ArrayList p = new ArrayList();
+ int pos = 0;
+ while (pos < pictstream.length) {
+ PictureData pict = new PictureData(pictstream, pos);
+ p.add(pict);
+ pos += PictureData.HEADER_SIZE + pict.getSize();
+ }
+
+ _pictures = (PictureData[])p.toArray(new PictureData[p.size()]);
}
- }
/**
@@ -287,6 +325,17 @@ public class HSLFSlideShow
currentUser.setCurrentEditOffset(newLastUserEditAtomPos.intValue());
currentUser.writeToFS(outFS);
+
+ // Write any pictures, into another stream
+ if (_pictures != null) {
+ ByteArrayOutputStream pict = new ByteArrayOutputStream();
+ for (int i = 0; i < _pictures.length; i++ ) {
+ _pictures[i].write(pict);
+ }
+ outFS.createDocument(
+ new ByteArrayInputStream(pict.toByteArray()), "Pictures"
+ );
+ }
// Send the POIFSFileSystem object out to the underlying stream
outFS.writeFilesystem(out);
@@ -311,87 +360,86 @@ public class HSLFSlideShow
}
- /* ******************* fetching methods follow ********************* */
-
-
- /**
- * Returns an array of all the records found in the slideshow
- */
- public Record[] getRecords() { return _records; }
-
- /**
- * Adds a new root level record, at the end, but before the last
- * PersistPtrIncrementalBlock.
- */
- public synchronized int appendRootLevelRecord(Record newRecord) {
- int addedAt = -1;
- Record[] r = new Record[_records.length+1];
- boolean added = false;
- for(int i=(_records.length-1); i>=0; i--) {
- if(added) {
- // Just copy over
- r[i] = _records[i];
- } else {
- r[(i+1)] = _records[i];
- if(_records[i] instanceof PersistPtrHolder) {
- r[i] = newRecord;
- added = true;
- addedAt = i;
- }
- }
- }
- _records = r;
- return addedAt;
- }
-
- /**
- * Returns an array of the bytes of the file. Only correct after a
- * call to open or write - at all other times might be wrong!
- */
- public byte[] getUnderlyingBytes() { return _docstream; }
-
- /**
- * Fetch the Document Summary Information of the document
- */
- public DocumentSummaryInformation getDocumentSummaryInformation() { return dsInf; }
-
- /**
- * Fetch the Summary Information of the document
- */
- public SummaryInformation getSummaryInformation() { return sInf; }
-
- /**
- * Fetch the Current User Atom of the document
- */
- public CurrentUserAtom getCurrentUserAtom() { return currentUser; }
+ /* ******************* adding methods follow ********************* */
/**
- * Read pictures contained in this presentation
+ * Adds a new root level record, at the end, but before the last
+ * PersistPtrIncrementalBlock.
+ */
+ public synchronized int appendRootLevelRecord(Record newRecord) {
+ int addedAt = -1;
+ Record[] r = new Record[_records.length+1];
+ boolean added = false;
+ for(int i=(_records.length-1); i>=0; i--) {
+ if(added) {
+ // Just copy over
+ r[i] = _records[i];
+ } else {
+ r[(i+1)] = _records[i];
+ if(_records[i] instanceof PersistPtrHolder) {
+ r[i] = newRecord;
+ added = true;
+ addedAt = i;
+ }
+ }
+ }
+ _records = r;
+ return addedAt;
+ }
+
+ /**
+ * Add a new picture to this presentation.
+ */
+ public void addPicture(PictureData img) {
+ // Copy over the existing pictures, into an array one bigger
+ PictureData[] lst;
+ if(_pictures == null) {
+ lst = new PictureData[1];
+ } else {
+ lst = new PictureData[(_pictures.length+1)];
+ System.arraycopy(_pictures,0,lst,0,_pictures.length);
+ }
+ // Add in the new image
+ lst[lst.length - 1] = img;
+ _pictures = lst;
+ }
+
+ /* ******************* fetching methods follow ********************* */
+
+
+ /**
+ * Returns an array of all the records found in the slideshow
+ */
+ public Record[] getRecords() { return _records; }
+
+ /**
+ * Returns an array of the bytes of the file. Only correct after a
+ * call to open or write - at all other times might be wrong!
+ */
+ public byte[] getUnderlyingBytes() { return _docstream; }
+
+ /**
+ * Fetch the Document Summary Information of the document
+ */
+ public DocumentSummaryInformation getDocumentSummaryInformation() { return dsInf; }
+
+ /**
+ * Fetch the Summary Information of the document
+ */
+ public SummaryInformation getSummaryInformation() { return sInf; }
+
+ /**
+ * Fetch the Current User Atom of the document
+ */
+ public CurrentUserAtom getCurrentUserAtom() { return currentUser; }
+
+ /**
+ * Return array of pictures contained in this presentation
*
- * @return array with the read pictures ot null
if the
+ * @return array with the read pictures or null
if the
* presentation doesn't contain pictures.
*/
- public Picture[] getPictures() throws IOException {
- byte[] pictstream;
-
- try {
- DocumentEntry entry = (DocumentEntry)filesystem.getRoot().getEntry("Pictures");
- pictstream = new byte[entry.getSize()];
- DocumentInputStream is = filesystem.createDocumentInputStream("Pictures");
- is.read(pictstream);
- } catch (FileNotFoundException e){
- //silently catch exceptions if the presentation doesn't contain pictures
- return null;
- }
-
- ArrayList p = new ArrayList();
- int pos = 0;
- while (pos < pictstream.length) {
- Picture pict = new Picture(pictstream, pos);
- p.add(pict);
- pos += Picture.HEADER_SIZE + pict.getSize();
- }
-
- return (Picture[])p.toArray(new Picture[p.size()]);
+ public PictureData[] getPictures() {
+ return _pictures;
}
}
diff --git a/src/scratchpad/src/org/apache/poi/hslf/data/empty.ppt b/src/scratchpad/src/org/apache/poi/hslf/data/empty.ppt
new file mode 100644
index 000000000..23e1e94ca
Binary files /dev/null and b/src/scratchpad/src/org/apache/poi/hslf/data/empty.ppt differ
diff --git a/src/scratchpad/src/org/apache/poi/hslf/model/Ellipse.java b/src/scratchpad/src/org/apache/poi/hslf/model/Ellipse.java
index 9db95012c..0c58bdc12 100644
--- a/src/scratchpad/src/org/apache/poi/hslf/model/Ellipse.java
+++ b/src/scratchpad/src/org/apache/poi/hslf/model/Ellipse.java
@@ -33,16 +33,15 @@ public class Ellipse extends SimpleShape {
public Ellipse(Shape parent){
super(null, parent);
- _escherContainer = create(parent instanceof ShapeGroup);
+ _escherContainer = createSpContainer(parent instanceof ShapeGroup);
}
public Ellipse(){
this(null);
}
- protected EscherContainerRecord create(boolean isChild){
- EscherContainerRecord spcont = super.create(isChild);
- spcont.setOptions((short)15);
+ protected EscherContainerRecord createSpContainer(boolean isChild){
+ EscherContainerRecord spcont = super.createSpContainer(isChild);
EscherSpRecord spRecord = spcont.getChildById(EscherSpRecord.RECORD_ID);
short type = (ShapeTypes.Ellipse << 4) + 2;
diff --git a/src/scratchpad/src/org/apache/poi/hslf/model/Line.java b/src/scratchpad/src/org/apache/poi/hslf/model/Line.java
index ea8e32fa6..d6ea230cd 100644
--- a/src/scratchpad/src/org/apache/poi/hslf/model/Line.java
+++ b/src/scratchpad/src/org/apache/poi/hslf/model/Line.java
@@ -96,16 +96,15 @@ public class Line extends SimpleShape {
public Line(Shape parent){
super(null, parent);
- _escherContainer = create(parent instanceof ShapeGroup);
+ _escherContainer = createSpContainer(parent instanceof ShapeGroup);
}
public Line(){
this(null);
}
- protected EscherContainerRecord create(boolean isChild){
- EscherContainerRecord spcont = super.create(isChild);
- spcont.setOptions((short)15);
+ protected EscherContainerRecord createSpContainer(boolean isChild){
+ EscherContainerRecord spcont = super.createSpContainer(isChild);
EscherSpRecord spRecord = spcont.getChildById(EscherSpRecord.RECORD_ID);
short type = (ShapeTypes.Line << 4) + 2;
diff --git a/src/scratchpad/src/org/apache/poi/hslf/model/Picture.java b/src/scratchpad/src/org/apache/poi/hslf/model/Picture.java
new file mode 100644
index 000000000..5e74988ae
--- /dev/null
+++ b/src/scratchpad/src/org/apache/poi/hslf/model/Picture.java
@@ -0,0 +1,137 @@
+package org.apache.poi.hslf.model;
+
+import org.apache.poi.ddf.*;
+import org.apache.poi.hslf.usermodel.PictureData;
+import org.apache.poi.hslf.usermodel.SlideShow;
+
+import javax.imageio.ImageIO;
+import java.awt.image.BufferedImage;
+import java.io.ByteArrayInputStream;
+import java.io.IOException;
+
+
+/**
+ * Represents a picture in a PowerPoint document.
+ *
+ * The information about an image in PowerPoint document is stored in + * two places: + *
+ * Data in the "Pictures" OLE stream is organized as follows:
+ * For each image there is an entry: 25 byte header + image data.
+ * Image data is the exact content of the JPEG file, i.e. PowerPoint
+ * puts the whole jpeg file there without any modifications.
+ * Header format:
+ *
Picture
+ *
+ * @param idx the index of the picture
+ */
+ public Picture(int idx){
+ super(null, null);
+ _escherContainer = createSpContainer(idx);
+ }
+
+ /**
+ * Create a Picture
object
+ *
+ * @param escherRecord the EscherSpContainer
record which holds information about
+ * this picture in the Slide
+ * @param parent the parent shape of this picture
+ */
+ protected Picture(EscherContainerRecord escherRecord, Shape parent){
+ super(escherRecord, parent);
+ }
+
+ /**
+ * Returns index associated with this picture.
+ * Index starts with 1 and points to a EscherBSE record which
+ * holds information about this picture.
+ *
+ * @return the index to this picture (1 based).
+ */
+ public int getPictureIndex(){
+ EscherOptRecord opt = (EscherOptRecord)getEscherChild(_escherContainer, EscherOptRecord.RECORD_ID);
+ EscherSimpleProperty prop = (EscherSimpleProperty)getEscherProperty(opt, EscherProperties.BLIP__BLIPTODISPLAY + 0x4000);
+ return prop.getPropertyValue();
+ }
+
+ /**
+ * Create a new Picture and populate the inital structure of the EscherSp
record which holds information about this picture.
+
+ * @param idx the index of the picture which referes to EscherBSE
container.
+ * @return the create Picture object
+ */
+ protected EscherContainerRecord createSpContainer(int idx) {
+ EscherContainerRecord spContainer = super.createSpContainer(false);
+ spContainer.setOptions((short)15);
+
+ EscherSpRecord spRecord = spContainer.getChildById(EscherSpRecord.RECORD_ID);
+ spRecord.setOptions((short)((ShapeTypes.PictureFrame << 4) | 0x2));
+
+ //set default properties for a picture
+ EscherOptRecord opt = (EscherOptRecord)getEscherChild(spContainer, EscherOptRecord.RECORD_ID);
+ setEscherProperty(opt, EscherProperties.PROTECTION__LOCKAGAINSTGROUPING, 8388736);
+
+ //another weird feature of powerpoint: for picture id we must add 0x4000.
+ setEscherProperty(opt, (short)(EscherProperties.BLIP__BLIPTODISPLAY + 0x4000), idx);
+
+ return spContainer;
+ }
+
+ /**
+ * Set default size of the picture
+ *
+ * @param ppt presentation which holds the picture
+ */
+ public void setDefaultSize(SlideShow ppt) throws IOException {
+ int idx = getPictureIndex();
+
+ PictureData pict = ppt.getPictures()[idx-1];
+ BufferedImage img = ImageIO.read(new ByteArrayInputStream(pict.getData()));
+
+ setAnchor(new java.awt.Rectangle(0, 0, img.getWidth()*6, img.getHeight()*6));
+ }
+}
diff --git a/src/scratchpad/src/org/apache/poi/hslf/model/Rectangle.java b/src/scratchpad/src/org/apache/poi/hslf/model/Rectangle.java
index a4be8e271..2c5d69d22 100644
--- a/src/scratchpad/src/org/apache/poi/hslf/model/Rectangle.java
+++ b/src/scratchpad/src/org/apache/poi/hslf/model/Rectangle.java
@@ -33,15 +33,15 @@ public class Rectangle extends SimpleShape {
public Rectangle(Shape parent){
super(null, parent);
- _escherContainer = create(parent instanceof ShapeGroup);
+ _escherContainer = createSpContainer(parent instanceof ShapeGroup);
}
public Rectangle(){
this(null);
}
- protected EscherContainerRecord create(boolean isChild){
- EscherContainerRecord spcont = super.create(isChild);
+ protected EscherContainerRecord createSpContainer(boolean isChild){
+ EscherContainerRecord spcont = super.createSpContainer(isChild);
spcont.setOptions((short)15);
EscherSpRecord spRecord = spcont.getChildById(EscherSpRecord.RECORD_ID);
diff --git a/src/scratchpad/src/org/apache/poi/hslf/model/Shape.java b/src/scratchpad/src/org/apache/poi/hslf/model/Shape.java
index a01b11691..aa1b200e6 100644
--- a/src/scratchpad/src/org/apache/poi/hslf/model/Shape.java
+++ b/src/scratchpad/src/org/apache/poi/hslf/model/Shape.java
@@ -26,25 +26,37 @@ import java.util.Iterator;
*
* @author Yegor Kozlov
*/
-public class Shape {
+public abstract class Shape {
public static final int EMU_PER_POINT = 12700;
- /**
- * The parent of the shape
- */
- protected Shape _parent;
-
/**
* Either EscherSpContainer or EscheSpgrContainer record
* which holds information about this shape.
*/
protected EscherContainerRecord _escherContainer;
- protected Shape(EscherContainerRecord escherRecord, Shape parent){
+ /**
+ * Parent of this shape.
+ * null
for the topmost shapes.
+ */
+ protected Shape _parent;
+
+ /**
+ * Create a Shape object. This constructor is used when an existing Shape is read from from a PowerPoint document.
+ *
+ * @param escherRecord EscherSpContainer
container which holds information about this shape
+ * @param parent the parent of this Shape
+ */
+ protected Shape(EscherContainerRecord escherRecord, Shape parent){
_escherContainer = escherRecord;
_parent = parent;
- }
+ }
+
+ /**
+ * Creates the lowerlevel escher records for this shape.
+ */
+ protected abstract EscherContainerRecord createSpContainer(boolean isChild);
/**
* @return the parent of this shape
@@ -128,17 +140,27 @@ public class Shape {
setAnchor(anchor);
}
- protected static EscherRecord getEscherChild(EscherContainerRecord owner, int recordId){
+ /**
+ * Helper method to return escher child by record ID
+ *
+ * @return escher record or null
if not found.
+ */
+ public static EscherRecord getEscherChild(EscherContainerRecord owner, int recordId){
for ( Iterator iterator = owner.getChildRecords().iterator(); iterator.hasNext(); )
{
EscherRecord escherRecord = (EscherRecord) iterator.next();
if (escherRecord.getRecordId() == recordId)
- return (EscherRecord) escherRecord;
+ return escherRecord;
}
return null;
}
- protected static EscherProperty getEscherProperty(EscherOptRecord opt, int propId){
+ /**
+ * Returns escher property by id.
+ *
+ * @return escher property or null
if not found.
+ */
+ public static EscherProperty getEscherProperty(EscherOptRecord opt, int propId){
for ( Iterator iterator = opt.getEscherProperties().iterator(); iterator.hasNext(); )
{
EscherProperty prop = (EscherProperty) iterator.next();
@@ -148,7 +170,14 @@ public class Shape {
return null;
}
- protected static void setEscherProperty(EscherOptRecord opt, short propId, int value){
+ /**
+ * Set an escher property in the opt record.
+ *
+ * @param opt The opt record to set the properties to.
+ * @param propId The id of the property. One of the constants defined in EscherOptRecord.
+ * @param value value of the property
+ */
+ public static void setEscherProperty(EscherOptRecord opt, short propId, int value){
java.util.List props = opt.getEscherProperties();
for ( Iterator iterator = props.iterator(); iterator.hasNext(); ) {
EscherProperty prop = (EscherProperty) iterator.next();
@@ -163,10 +192,10 @@ public class Shape {
}
/**
- *
- * @return escher container which holds information about this shape
+ * @return The shape container and it's children that can represent this
+ * shape.
*/
- public EscherContainerRecord getShapeRecord(){
+ public EscherContainerRecord getSpContainer(){
return _escherContainer;
}
}
diff --git a/src/scratchpad/src/org/apache/poi/hslf/model/ShapeFactory.java b/src/scratchpad/src/org/apache/poi/hslf/model/ShapeFactory.java
index 67f2d708b..c886638c3 100644
--- a/src/scratchpad/src/org/apache/poi/hslf/model/ShapeFactory.java
+++ b/src/scratchpad/src/org/apache/poi/hslf/model/ShapeFactory.java
@@ -37,10 +37,10 @@ public class ShapeFactory {
switch (type){
case ShapeTypes.TextBox:
case ShapeTypes.Rectangle:
- shape = new Shape(spContainer, parent);
+ shape = new Rectangle(spContainer, parent);
break;
case ShapeTypes.PictureFrame:
- shape = new Shape(spContainer, parent);
+ shape = new Picture(spContainer, parent);
break;
case ShapeTypes.Line:
shape = new Line(spContainer, parent);
@@ -52,7 +52,7 @@ public class ShapeFactory {
shape = new ShapeGroup(spContainer, parent);
break;
default:
- shape = new Shape(spContainer, parent);
+ shape = new SimpleShape(spContainer, parent);
break;
}
return shape;
diff --git a/src/scratchpad/src/org/apache/poi/hslf/model/ShapeGroup.java b/src/scratchpad/src/org/apache/poi/hslf/model/ShapeGroup.java
index c11e29471..0d2d61e95 100644
--- a/src/scratchpad/src/org/apache/poi/hslf/model/ShapeGroup.java
+++ b/src/scratchpad/src/org/apache/poi/hslf/model/ShapeGroup.java
@@ -27,13 +27,9 @@ import java.util.List;
*/
public class ShapeGroup extends Shape{
- public ShapeGroup(Shape parent){
- super(null, parent);
- _escherContainer = create();
- }
-
public ShapeGroup(){
- this(null);
+ this(null, null);
+ _escherContainer = createSpContainer(false);
}
protected ShapeGroup(EscherContainerRecord escherRecord, Shape parent){
@@ -91,7 +87,7 @@ public class ShapeGroup extends Shape{
/**
* Create a new ShapeGroup and create an instance of EscherSpgrContainer
which represents a group of shapes
*/
- protected EscherContainerRecord create() {
+ protected EscherContainerRecord createSpContainer(boolean isChild) {
EscherContainerRecord spgr = new EscherContainerRecord();
spgr.setRecordId(EscherContainerRecord.SPGR_CONTAINER);
spgr.setOptions((short)15);
@@ -124,7 +120,7 @@ public class ShapeGroup extends Shape{
* @param shape - the Shape to add
*/
public void addShape(Shape shape){
- _escherContainer.addChildRecord(shape.getShapeRecord());
+ _escherContainer.addChildRecord(shape.getSpContainer());
}
/**
diff --git a/src/scratchpad/src/org/apache/poi/hslf/model/Sheet.java b/src/scratchpad/src/org/apache/poi/hslf/model/Sheet.java
index f0834578e..e4a688628 100644
--- a/src/scratchpad/src/org/apache/poi/hslf/model/Sheet.java
+++ b/src/scratchpad/src/org/apache/poi/hslf/model/Sheet.java
@@ -158,7 +158,7 @@ public abstract class Sheet
EscherContainerRecord dgContainer = (EscherContainerRecord)ppdrawing.getEscherRecords()[0];
EscherContainerRecord spgr = (EscherContainerRecord)Shape.getEscherChild(dgContainer, EscherContainerRecord.SPGR_CONTAINER);
- spgr.addChildRecord(shape.getShapeRecord());
+ spgr.addChildRecord(shape.getSpContainer());
EscherDgRecord dg = (EscherDgRecord)Shape.getEscherChild(dgContainer, EscherDgRecord.RECORD_ID);
dg.setNumShapes(dg.getNumShapes()+1);
diff --git a/src/scratchpad/src/org/apache/poi/hslf/model/SimpleShape.java b/src/scratchpad/src/org/apache/poi/hslf/model/SimpleShape.java
index 7f08b83a6..787780dcf 100644
--- a/src/scratchpad/src/org/apache/poi/hslf/model/SimpleShape.java
+++ b/src/scratchpad/src/org/apache/poi/hslf/model/SimpleShape.java
@@ -39,10 +39,10 @@ public class SimpleShape extends Shape {
* @param isChild true
if the Line is inside a group, false
otherwise
* @return the record container which holds this shape
*/
- protected EscherContainerRecord create(boolean isChild) {
+ protected EscherContainerRecord createSpContainer(boolean isChild) {
EscherContainerRecord spContainer = new EscherContainerRecord();
spContainer.setRecordId( EscherContainerRecord.SP_CONTAINER );
- //spContainer.setOptions((short)15);
+ spContainer.setOptions((short)15);
EscherSpRecord sp = new EscherSpRecord();
int flags = EscherSpRecord.FLAG_HAVEANCHOR | EscherSpRecord.FLAG_HASSHAPETYPE;
diff --git a/src/scratchpad/src/org/apache/poi/hslf/record/Document.java b/src/scratchpad/src/org/apache/poi/hslf/record/Document.java
index c3c0012dd..2593eecd3 100644
--- a/src/scratchpad/src/org/apache/poi/hslf/record/Document.java
+++ b/src/scratchpad/src/org/apache/poi/hslf/record/Document.java
@@ -36,6 +36,7 @@ public class Document extends PositionDependentRecordContainer
// Links to our more interesting children
private DocumentAtom documentAtom;
private Environment environment;
+ private PPDrawingGroup ppDrawing;
private SlideListWithText[] slwts;
/**
@@ -47,6 +48,11 @@ public class Document extends PositionDependentRecordContainer
* settings for the document in it
*/
public Environment getEnvironment() { return environment; }
+ /**
+ * Returns the PPDrawingGroup, which holds an Escher Structure
+ * that contains information on pictures in the slides.
+ */
+ public PPDrawingGroup getPPDrawingGroup() { return ppDrawing; }
/**
* Returns all the SlideListWithTexts that are defined for
* this Document. They hold the text, and some of the text
@@ -82,6 +88,9 @@ public class Document extends PositionDependentRecordContainer
if(_children[i] instanceof Environment) {
environment = (Environment)_children[i];
}
+ if(_children[i] instanceof PPDrawingGroup) {
+ ppDrawing = (PPDrawingGroup)_children[i];
+ }
}
// Now grab them all
slwts = new SlideListWithText[slwtcount];
diff --git a/src/scratchpad/src/org/apache/poi/hslf/record/PPDrawingGroup.java b/src/scratchpad/src/org/apache/poi/hslf/record/PPDrawingGroup.java
new file mode 100644
index 000000000..f877d2a4a
--- /dev/null
+++ b/src/scratchpad/src/org/apache/poi/hslf/record/PPDrawingGroup.java
@@ -0,0 +1,103 @@
+package org.apache.poi.hslf.record;
+
+import org.apache.poi.ddf.*;
+import org.apache.poi.util.LittleEndian;
+
+import java.io.OutputStream;
+import java.io.IOException;
+import java.io.ByteArrayOutputStream;
+import java.util.List;
+import java.util.Iterator;
+
+/**
+ * Container records which always exists inside Document.
+ * It always acts as a holder for escher DGG container
+ * which may contain which Escher BStore container information
+ * about pictures containes in the presentation (if any).
+ *
+ * @author Yegor Kozlov
+ */
+public class PPDrawingGroup extends RecordAtom {
+
+ private byte[] _header;
+ private EscherContainerRecord dggContainer;
+
+ protected PPDrawingGroup(byte[] source, int start, int len) {
+ // Get the header
+ _header = new byte[8];
+ System.arraycopy(source,start,_header,0,8);
+
+ // Get the contents for now
+ byte[] contents = new byte[len];
+ System.arraycopy(source,start,contents,0,len);
+
+ DefaultEscherRecordFactory erf = new DefaultEscherRecordFactory();
+ EscherRecord child = erf.createRecord(contents, 0);
+ child.fillFields( contents, 0, erf );
+ dggContainer = (EscherContainerRecord)child.getChild(0);
+ }
+
+ /**
+ * We are type 1035
+ */
+ public long getRecordType() {
+ return RecordTypes.PPDrawingGroup.typeID;
+ }
+
+ /**
+ * We're pretending to be an atom, so return null
+ */
+ public Record[] getChildRecords() {
+ return null;
+ }
+
+ public void writeOut(OutputStream out) throws IOException {
+ ByteArrayOutputStream bout = new ByteArrayOutputStream();
+ List child = dggContainer.getChildRecords();
+ for (int i = 0; i < child.size(); i++) {
+ EscherRecord r = (EscherRecord)child.get(i);
+ if (r.getRecordId() == EscherContainerRecord.BSTORE_CONTAINER){
+ EscherContainerRecord bstore = (EscherContainerRecord)r;
+
+ ByteArrayOutputStream b2 = new ByteArrayOutputStream();
+ List blip = bstore.getChildRecords();
+ for (Iterator it=blip.iterator(); it.hasNext();) {
+ EscherBSERecord bse = (EscherBSERecord)it.next();
+ byte[] b = new byte[36+8];
+ bse.serialize(0, b);
+ b2.write(b);
+ }
+ byte[] bstorehead = new byte[8];
+ LittleEndian.putShort(bstorehead, 0, bstore.getOptions());
+ LittleEndian.putShort(bstorehead, 2, bstore.getRecordId());
+ LittleEndian.putInt(bstorehead, 4, b2.size());
+ bout.write(bstorehead);
+ bout.write(b2.toByteArray());
+
+ } else {
+ bout.write(r.serialize());
+ }
+ }
+ int size = bout.size();
+
+ // Update the size (header bytes 5-8)
+ LittleEndian.putInt(_header,4,size+8);
+
+ // Write out our header
+ out.write(_header);
+
+ byte[] dgghead = new byte[8];
+ LittleEndian.putShort(dgghead, 0, dggContainer.getOptions());
+ LittleEndian.putShort(dgghead, 2, dggContainer.getRecordId());
+ LittleEndian.putInt(dgghead, 4, size);
+ out.write(dgghead);
+
+ // Finally, write out the children
+ out.write(bout.toByteArray());
+
+ }
+
+ public EscherContainerRecord getDggContainer(){
+ return dggContainer;
+ }
+}
diff --git a/src/scratchpad/src/org/apache/poi/hslf/record/RecordTypes.java b/src/scratchpad/src/org/apache/poi/hslf/record/RecordTypes.java
index eaca249e9..41935120c 100644
--- a/src/scratchpad/src/org/apache/poi/hslf/record/RecordTypes.java
+++ b/src/scratchpad/src/org/apache/poi/hslf/record/RecordTypes.java
@@ -60,7 +60,7 @@ public class RecordTypes {
public static final Type SorterViewInfo = new Type(1032,null);
public static final Type ExObjList = new Type(1033,null);
public static final Type ExObjListAtom = new Type(1034,null);
- public static final Type PPDrawingGroup = new Type(1035,null);
+ public static final Type PPDrawingGroup = new Type(1035,PPDrawingGroup.class);
public static final Type PPDrawing = new Type(1036,PPDrawing.class);
public static final Type NamedShows = new Type(1040,null);
public static final Type NamedShow = new Type(1041,null);
diff --git a/src/scratchpad/src/org/apache/poi/hslf/usermodel/Picture.java b/src/scratchpad/src/org/apache/poi/hslf/usermodel/Picture.java
deleted file mode 100644
index 64caff7fa..000000000
--- a/src/scratchpad/src/org/apache/poi/hslf/usermodel/Picture.java
+++ /dev/null
@@ -1,140 +0,0 @@
-/* ====================================================================
- Copyright 2002-2004 Apache Software Foundation
-
- Licensed 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 org.apache.poi.util.LittleEndian;
-
-/**
- * Represents a picture in a PowerPoint document.
- * - * The information about an image in PowerPoint document is stored in - * two places: - *
- * Data in the "Pictures" OLE stream is organized as follows:
- * For each image there is an entry: 25 byte header + image data.
- * Image data is the exact content of the JPEG file, i.e. PowerPoint
- * puts the whole jpeg file there without any modifications.
- * Header format:
- *
Picture class.
+ */
+ public void setType(int format){
+ switch (format){
+ case Picture.JPEG: LittleEndian.putInt(header, 0, -266516832); break;
+ case Picture.PNG: LittleEndian.putInt(header, 0, -266441216); break;
+ }
+ }
+
+ /**
+ * Returns the header of the Picture
+ *
+ * @return the header of the Picture
+ */
+ public byte[] getHeader(){
+ return header;
+ }
+
+ /**
+ * Compute 16-byte checksum of this picture
+ */
+ public static byte[] getChecksum(byte[] data) {
+ MessageDigest sha;
+ try {
+ sha = MessageDigest.getInstance("MD5");
+ } catch (NoSuchAlgorithmException e){
+ throw new RuntimeException(e.getMessage());
+ }
+ sha.update(data);
+ return sha.digest();
+ }
+
+ /**
+ * Write this picture into OutputStream
+ */
+ public void write(OutputStream out) throws IOException {
+ out.write(header);
+ out.write(pictdata);
+ }
+
+}
diff --git a/src/scratchpad/src/org/apache/poi/hslf/usermodel/SlideShow.java b/src/scratchpad/src/org/apache/poi/hslf/usermodel/SlideShow.java
index a114ee91f..ba5e75562 100644
--- a/src/scratchpad/src/org/apache/poi/hslf/usermodel/SlideShow.java
+++ b/src/scratchpad/src/org/apache/poi/hslf/usermodel/SlideShow.java
@@ -23,6 +23,10 @@ import java.util.*;
import java.awt.Dimension;
import java.io.*;
+import org.apache.poi.ddf.EscherBSERecord;
+import org.apache.poi.ddf.EscherContainerRecord;
+import org.apache.poi.ddf.EscherOptRecord;
+import org.apache.poi.ddf.EscherRecord;
import org.apache.poi.hslf.*;
import org.apache.poi.hslf.model.*;
import org.apache.poi.hslf.record.Document;
@@ -50,6 +54,7 @@ import org.apache.poi.hslf.exceptions.CorruptPowerPointFileException;
* - handle Slide creation cleaner
*
* @author Nick Burch
+ * @author Yegor kozlov
*/
public class SlideShow
@@ -74,6 +79,12 @@ public class SlideShow
// MetaSheets (eg masters) not yet supported
// private MetaSheets[] _msheets;
+
+ /* ===============================================================
+ * Setup Code
+ * ===============================================================
+ */
+
/**
* Constructs a Powerpoint document from the underlying
@@ -86,7 +97,6 @@ public class SlideShow
// Get useful things from our base slideshow
_hslfSlideShow = hslfSlideShow;
_records = _hslfSlideShow.getRecords();
- byte[] _docstream = _hslfSlideShow.getUnderlyingBytes();
// Handle Parent-aware Reocrds
for(int i=0; i<_records.length; i++) {
@@ -100,6 +110,12 @@ public class SlideShow
buildSlidesAndNotes();
}
+ /**
+ * Constructs a new, empty, Powerpoint document.
+ */
+ public SlideShow() throws IOException {
+ this(new HSLFSlideShow());
+ }
/**
* Find the records that are parent-aware, and tell them
@@ -373,6 +389,77 @@ public class SlideShow
}
}
+ /**
+ * Writes out the slideshow file the is represented by an instance of
+ * this class
+ * @param out The OutputStream to write to.
+ * @throws IOException If there is an unexpected IOException from the passed
+ * in OutputStream
+ */
+ public void write(OutputStream out) throws IOException {
+ _hslfSlideShow.write(out);
+ }
+
+
+ /* ===============================================================
+ * Accessor Code
+ * ===============================================================
+ */
+
+
+ /**
+ * Returns an array of the most recent version of all the interesting
+ * records
+ */
+ public Record[] getMostRecentCoreRecords() { return _mostRecentCoreRecords; }
+
+ /**
+ * Returns an array of all the normal Slides found in the slideshow
+ */
+ public Slide[] getSlides() { return _slides; }
+
+ /**
+ * Returns an array of all the normal Notes found in the slideshow
+ */
+ public Notes[] getNotes() { return _notes; }
+
+ /**
+ * Returns an array of all the meta Sheets (master sheets etc)
+ * found in the slideshow
+ */
+ //public MetaSheet[] getMetaSheets() { return _msheets; }
+
+ /**
+ * Returns all the pictures attached to the SlideShow
+ */
+ public PictureData[] getPictures() throws IOException {
+ return _hslfSlideShow.getPictures();
+ }
+
+ /**
+ * Return the current page size
+ */
+ public Dimension getPageSize(){
+ DocumentAtom docatom = _documentRecord.getDocumentAtom();
+ return new Dimension((int)docatom.getSlideSizeX(), (int)docatom.getSlideSizeY());
+ }
+
+ /**
+ * Helper method for usermodel: Get the font collection
+ */
+ protected FontCollection getFontCollection() { return _fonts; }
+ /**
+ * Helper method for usermodel: Get the document record
+ */
+ protected Document getDocumentRecord() { return _documentRecord; }
+
+
+ /* ===============================================================
+ * Addition Code
+ * ===============================================================
+ */
+
+
/**
* Create a blank Slide
.
*
@@ -476,63 +563,87 @@ public class SlideShow
}
- /**
- * Writes out the slideshow file the is represented by an instance of
- * this class
- * @param out The OutputStream to write to.
- * @throws IOException If there is an unexpected IOException from the passed
- * in OutputStream
- */
- public void write(OutputStream out) throws IOException {
- _hslfSlideShow.write(out);
- }
+ /**
+ * Adds a picture to this presentation and returns the associated index.
+ *
+ * @param data picture data
+ * @param format the format of the picture. One of constans defined in the Picture
class.
+ * @return the index to this picture (1 based).
+ */
+ public int addPicture(byte[] data, int format) {
+ byte[] uid = PictureData.getChecksum(data);
+ EscherContainerRecord bstore;
+ int offset = 0;
- // Accesser methods follow
+ EscherContainerRecord dggContainer = _documentRecord.getPPDrawingGroup().getDggContainer();
+ bstore = (EscherContainerRecord)Shape.getEscherChild(dggContainer, EscherContainerRecord.BSTORE_CONTAINER);
+ if (bstore == null){
+ bstore = new EscherContainerRecord();
+ bstore.setRecordId( EscherContainerRecord.BSTORE_CONTAINER);
- /**
- * Returns an array of the most recent version of all the interesting
- * records
- */
- public Record[] getMostRecentCoreRecords() { return _mostRecentCoreRecords; }
+ List child = dggContainer.getChildRecords();
+ for ( int i = 0; i < child.size(); i++ ) {
+ EscherRecord rec = (EscherRecord)child.get(i);
+ if (rec.getRecordId() == EscherOptRecord.RECORD_ID){
+ child.add(i, bstore);
+ i++;
+ }
+ }
+ dggContainer.setChildRecords(child);
+ } else {
+ List lst = bstore.getChildRecords();
+ for ( int i = 0; i < lst.size(); i++ ) {
+ EscherBSERecord bse = (EscherBSERecord) lst.get(i);
+ if (Arrays.equals(bse.getUid(), uid)){
+ return i + 1;
+ }
+ offset += bse.getSize();
+ }
+ }
- /**
- * Returns an array of all the normal Slides found in the slideshow
- */
- public Slide[] getSlides() { return _slides; }
+ EscherBSERecord bse = new EscherBSERecord();
+ bse.setRecordId(EscherBSERecord.RECORD_ID);
+ bse.setOptions( (short) ( 0x0002 | ( format << 4 ) ) );
+ bse.setSize(data.length + PictureData.HEADER_SIZE);
+ bse.setUid(uid);
+ bse.setBlipTypeMacOS((byte)format);
+ bse.setBlipTypeWin32((byte)format);
- /**
- * Returns an array of all the normal Notes found in the slideshow
- */
- public Notes[] getNotes() { return _notes; }
+ bse.setRef(1);
+ bse.setOffset(offset);
- /**
- * Returns an array of all the meta Sheets (master sheets etc)
- * found in the slideshow
- */
- //public MetaSheet[] getMetaSheets() { return _msheets; }
+ bstore.addChildRecord(bse);
+ int count = bstore.getChildRecords().size();
+ bstore.setOptions((short)( (count << 4) | 0xF ));
- /**
- * Returns all the pictures attached to the SlideShow
- */
- public Picture[] getPictures() throws IOException {
- return _hslfSlideShow.getPictures();
- }
-
- /**
- * Return the current page size
- */
- public Dimension getPageSize(){
- DocumentAtom docatom = _documentRecord.getDocumentAtom();
- return new Dimension((int)docatom.getSlideSizeX(), (int)docatom.getSlideSizeY());
- }
-
- /**
- * Helper method for usermodel: Get the font collection
- */
- protected FontCollection getFontCollection() { return _fonts; }
- /**
- * Helper method for usermodel: Get the document record
- */
- protected Document getDocumentRecord() { return _documentRecord; }
+ PictureData pict = new PictureData();
+ pict.setUID(uid);
+ pict.setData(data);
+ pict.setType(format);
+
+ _hslfSlideShow.addPicture(pict);
+
+ return count;
+ }
+
+ /**
+ * Adds a picture to this presentation and returns the associated index.
+ *
+ * @param pict the file containing the image to add
+ * @param format the format of the picture. One of constans defined in the Picture
class.
+ * @return the index to this picture (1 based).
+ */
+ public int addPicture(File pict, int format) {
+ int length = (int)pict.length();
+ byte[] data = new byte[length];
+ try {
+ FileInputStream is = new FileInputStream(pict);
+ is.read(data);
+ is.close();
+ } catch (IOException e){
+ throw new RuntimeException(e);
+ }
+ return addPicture(data, format);
+ }
}