]> source.dussan.org Git - poi.git/commitdiff
Patches from Yegor (Bug #39097), along with some sorting out of indenting, method...
authorNick Burch <nick@apache.org>
Sun, 26 Mar 2006 16:20:08 +0000 (16:20 +0000)
committerNick Burch <nick@apache.org>
Sun, 26 Mar 2006 16:20:08 +0000 (16:20 +0000)
git-svn-id: https://svn.apache.org/repos/asf/jakarta/poi/trunk@388920 13f79535-47bb-0310-9956-ffa450edef68

17 files changed:
src/scratchpad/src/org/apache/poi/hslf/HSLFSlideShow.java
src/scratchpad/src/org/apache/poi/hslf/data/empty.ppt [new file with mode: 0644]
src/scratchpad/src/org/apache/poi/hslf/model/Ellipse.java
src/scratchpad/src/org/apache/poi/hslf/model/Line.java
src/scratchpad/src/org/apache/poi/hslf/model/Picture.java [new file with mode: 0644]
src/scratchpad/src/org/apache/poi/hslf/model/Rectangle.java
src/scratchpad/src/org/apache/poi/hslf/model/Shape.java
src/scratchpad/src/org/apache/poi/hslf/model/ShapeFactory.java
src/scratchpad/src/org/apache/poi/hslf/model/ShapeGroup.java
src/scratchpad/src/org/apache/poi/hslf/model/Sheet.java
src/scratchpad/src/org/apache/poi/hslf/model/SimpleShape.java
src/scratchpad/src/org/apache/poi/hslf/record/Document.java
src/scratchpad/src/org/apache/poi/hslf/record/PPDrawingGroup.java [new file with mode: 0644]
src/scratchpad/src/org/apache/poi/hslf/record/RecordTypes.java
src/scratchpad/src/org/apache/poi/hslf/usermodel/Picture.java [deleted file]
src/scratchpad/src/org/apache/poi/hslf/usermodel/PictureData.java [new file with mode: 0644]
src/scratchpad/src/org/apache/poi/hslf/usermodel/SlideShow.java

index ed2de1af24244c422bafe134bb51fed2deac2b1b..f6bd2cf9889c070bd2d368323210497c8b325855 100644 (file)
@@ -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();
-  }
 
+               // Look for Picture Streams:
+               readPictures();
+       }
 
-  /**
-   * Shuts things down. Closes underlying streams etc
-   *
-   * @throws IOException
-   */
-  public void close() throws IOException
-  {
-       if(istream != null) {
-               istream.close();
+       /**
+        * 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 ********************* */
+       /* ******************* adding methods follow ********************* */
 
+       /**
+        * 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;
+       }
 
-  /**
-   * 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;
-  }
+       /* ******************* fetching methods follow ********************* */
 
-  /**
-   * 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; }
+       /**
+        * Returns an array of all the records found in the slideshow
+        */
+       public Record[] getRecords() { return _records; }
 
-  /** 
-   * Fetch the Summary Information of the document
-   */
-  public SummaryInformation getSummaryInformation() { return sInf; }
+       /**
+        * 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 Current User Atom of the document
-  */
- public CurrentUserAtom getCurrentUserAtom() { return currentUser; }
+       /** 
+        * 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; }
 
        /**
-        *  Read pictures contained in this presentation
+        * 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 <code>null</code> if the
+        *  @return array with the read pictures or <code>null</code> 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 (file)
index 0000000..23e1e94
Binary files /dev/null and b/src/scratchpad/src/org/apache/poi/hslf/data/empty.ppt differ
index 9db95012c58a4b587314838afad49f503bad6706..0c58bdc128e4dda68225db7f548fbbe515b0a338 100644 (file)
@@ -33,16 +33,15 @@ public class Ellipse extends SimpleShape {
 \r
     public Ellipse(Shape parent){\r
         super(null, parent);\r
-        _escherContainer = create(parent instanceof ShapeGroup);\r
+        _escherContainer = createSpContainer(parent instanceof ShapeGroup);\r
     }\r
 \r
     public Ellipse(){\r
         this(null);\r
     }\r
 \r
-    protected EscherContainerRecord create(boolean isChild){\r
-        EscherContainerRecord spcont = super.create(isChild);\r
-        spcont.setOptions((short)15);\r
+    protected EscherContainerRecord createSpContainer(boolean isChild){\r
+        EscherContainerRecord spcont = super.createSpContainer(isChild);\r
 \r
         EscherSpRecord spRecord = spcont.getChildById(EscherSpRecord.RECORD_ID);\r
         short type = (ShapeTypes.Ellipse << 4) + 2;\r
index ea8e32fa6fbebfc3d521a970bbbc13cc1fff829e..d6ea230cd93e36a7dae190a94bd05581ba34750a 100644 (file)
@@ -96,16 +96,15 @@ public class Line extends SimpleShape {
 \r
     public Line(Shape parent){\r
         super(null, parent);\r
-        _escherContainer = create(parent instanceof ShapeGroup);\r
+        _escherContainer = createSpContainer(parent instanceof ShapeGroup);\r
     }\r
 \r
     public Line(){\r
         this(null);\r
     }\r
 \r
-    protected EscherContainerRecord create(boolean isChild){\r
-        EscherContainerRecord spcont = super.create(isChild);\r
-        spcont.setOptions((short)15);\r
+    protected EscherContainerRecord createSpContainer(boolean isChild){\r
+        EscherContainerRecord spcont = super.createSpContainer(isChild);\r
 \r
         EscherSpRecord spRecord = spcont.getChildById(EscherSpRecord.RECORD_ID);\r
         short type = (ShapeTypes.Line << 4) + 2;\r
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 (file)
index 0000000..5e74988
--- /dev/null
@@ -0,0 +1,137 @@
+package org.apache.poi.hslf.model;\r
+\r
+import org.apache.poi.ddf.*;\r
+import org.apache.poi.hslf.usermodel.PictureData;\r
+import org.apache.poi.hslf.usermodel.SlideShow;\r
+\r
+import javax.imageio.ImageIO;\r
+import java.awt.image.BufferedImage;\r
+import java.io.ByteArrayInputStream;\r
+import java.io.IOException;\r
+\r
+\r
+/**\r
+ * Represents a picture in a PowerPoint document.\r
+ * <p>\r
+ * The information about an image in PowerPoint document is stored in\r
+ * two places:\r
+ *  <li> EscherBSE container in the Document keeps information about image\r
+ *    type, image index to refer by slides etc.\r
+ *  <li> "Pictures" OLE stream holds the actual data of the image.\r
+ * </p>\r
+ * <p>\r
+ *  Data in the "Pictures" OLE stream is organized as follows:<br>\r
+ *  For each image there is an entry: 25 byte header + image data.\r
+ *  Image data is the exact content of the JPEG file, i.e. PowerPoint\r
+ *  puts the whole jpeg file there without any modifications.<br>\r
+ *   Header format:\r
+ *    <li> 2 byte: image type. For JPEGs it is 0x46A0, for PNG it is 0x6E00.\r
+ *    <li> 2 byte: unknown.\r
+ *    <li> 4 byte : image size + 17. Looks like shift from the end of\r
+ *          header but why to add it to the image  size?\r
+ *    <li> next 16 bytes. Unique identifier of this image which is used by\r
+ *          EscherBSE record.\r
+ *  </p>\r
+ *\r
+ * @author Yegor Kozlov\r
+ */\r
+public class Picture extends SimpleShape {\r
+\r
+    /**\r
+    *  Windows Metafile\r
+    *  ( NOT YET SUPPORTED )\r
+    */\r
+    public static final int WMF = 3;\r
+\r
+    /**\r
+    * Macintosh PICT\r
+     *  ( NOT YET SUPPORTED )\r
+    */\r
+    public static final int PICT = 4;\r
+\r
+    /**\r
+    *  JPEG\r
+    */\r
+    public static final int JPEG = 5;\r
+\r
+    /**\r
+    *  PNG\r
+    */\r
+    public static final int PNG = 6;\r
+\r
+    /**\r
+    * Windows DIB (BMP)\r
+    */\r
+    public static final int DIB = 7;\r
+\r
+    /**\r
+     * Create a new <code>Picture</code>\r
+     *\r
+    * @param idx the index of the picture\r
+     */\r
+    public Picture(int idx){\r
+        super(null, null);\r
+        _escherContainer = createSpContainer(idx);\r
+    }\r
+\r
+    /**\r
+      * Create a <code>Picture</code> object\r
+      *\r
+      * @param escherRecord the <code>EscherSpContainer</code> record which holds information about\r
+      *        this picture in the <code>Slide</code>\r
+      * @param parent the parent shape of this picture\r
+      */\r
+     protected Picture(EscherContainerRecord escherRecord, Shape parent){\r
+        super(escherRecord, parent);\r
+    }\r
+\r
+    /**\r
+     * Returns index associated with this picture.\r
+     * Index starts with 1 and points to a EscherBSE record which\r
+     * holds information about this picture.\r
+     *\r
+     * @return the index to this picture (1 based).\r
+     */\r
+    public int getPictureIndex(){\r
+        EscherOptRecord opt = (EscherOptRecord)getEscherChild(_escherContainer, EscherOptRecord.RECORD_ID);\r
+        EscherSimpleProperty prop = (EscherSimpleProperty)getEscherProperty(opt, EscherProperties.BLIP__BLIPTODISPLAY + 0x4000);\r
+        return prop.getPropertyValue();\r
+    }\r
+\r
+    /**\r
+     * Create a new Picture and populate the inital structure of the <code>EscherSp</code> record which holds information about this picture.\r
+\r
+     * @param idx the index of the picture which referes to <code>EscherBSE</code> container.\r
+     * @return the create Picture object\r
+     */\r
+    protected EscherContainerRecord createSpContainer(int idx) {\r
+        EscherContainerRecord spContainer = super.createSpContainer(false);\r
+        spContainer.setOptions((short)15);\r
+\r
+        EscherSpRecord spRecord = spContainer.getChildById(EscherSpRecord.RECORD_ID);\r
+        spRecord.setOptions((short)((ShapeTypes.PictureFrame << 4) | 0x2));\r
+\r
+        //set default properties for a picture\r
+        EscherOptRecord opt = (EscherOptRecord)getEscherChild(spContainer, EscherOptRecord.RECORD_ID);\r
+        setEscherProperty(opt, EscherProperties.PROTECTION__LOCKAGAINSTGROUPING, 8388736);\r
+\r
+        //another weird feature of powerpoint: for picture id we must add 0x4000.\r
+        setEscherProperty(opt, (short)(EscherProperties.BLIP__BLIPTODISPLAY + 0x4000), idx);\r
+\r
+        return spContainer;\r
+    }\r
+\r
+    /**\r
+     * Set default size of the picture\r
+     *\r
+     * @param ppt presentation which holds the picture\r
+     */\r
+    public void setDefaultSize(SlideShow ppt) throws IOException {\r
+        int idx = getPictureIndex();\r
+\r
+        PictureData pict = ppt.getPictures()[idx-1];\r
+        BufferedImage img = ImageIO.read(new ByteArrayInputStream(pict.getData()));\r
+\r
+        setAnchor(new java.awt.Rectangle(0, 0, img.getWidth()*6, img.getHeight()*6));\r
+    }\r
+}\r
index a4be8e2711f70dd1a041d670ec4abb18734f5c79..2c5d69d22fc8c3d12b83faec70f0164ad9972051 100644 (file)
@@ -33,15 +33,15 @@ public class Rectangle extends SimpleShape {
 \r
     public Rectangle(Shape parent){\r
         super(null, parent);\r
-        _escherContainer = create(parent instanceof ShapeGroup);\r
+        _escherContainer = createSpContainer(parent instanceof ShapeGroup);\r
     }\r
 \r
     public Rectangle(){\r
         this(null);\r
     }\r
 \r
-    protected EscherContainerRecord create(boolean isChild){\r
-        EscherContainerRecord spcont = super.create(isChild);\r
+    protected EscherContainerRecord createSpContainer(boolean isChild){\r
+        EscherContainerRecord spcont = super.createSpContainer(isChild);\r
         spcont.setOptions((short)15);\r
 \r
         EscherSpRecord spRecord = spcont.getChildById(EscherSpRecord.RECORD_ID);\r
index a01b1169175168b292f8e8e6ed3bd09ffd7fc74a..aa1b200e64d54b749d750f930098ba1d7e5cac3a 100644 (file)
@@ -26,25 +26,37 @@ import java.util.Iterator;
   *\r
   * @author Yegor Kozlov\r
  */\r
-public class Shape {\r
+public abstract class Shape {\r
 \r
     public static final int EMU_PER_POINT = 12700;\r
 \r
-    /**\r
-     *  The parent of the shape\r
-     */\r
-    protected Shape _parent;\r
-\r
     /**\r
      * Either EscherSpContainer or EscheSpgrContainer record\r
      * which holds information about this shape.\r
      */\r
     protected EscherContainerRecord _escherContainer;\r
 \r
-    protected Shape(EscherContainerRecord escherRecord, Shape parent){\r
+    /**\r
+     * Parent of this shape.\r
+     * <code>null</code> for the topmost shapes.\r
+     */\r
+    protected Shape _parent;\r
+\r
+    /**\r
+     * Create a Shape object. This constructor is used when an existing Shape is read from from a PowerPoint document.\r
+     *\r
+     * @param escherRecord       <code>EscherSpContainer</code> container which holds information about this shape\r
+     * @param parent             the parent of this Shape\r
+     */\r
+      protected Shape(EscherContainerRecord escherRecord, Shape parent){\r
         _escherContainer = escherRecord;\r
         _parent = parent;\r
-    }\r
+     }\r
+\r
+    /**\r
+     * Creates the lowerlevel escher records for this shape.\r
+     */\r
+    protected abstract EscherContainerRecord createSpContainer(boolean isChild);\r
 \r
     /**\r
      *  @return the parent of this shape\r
@@ -128,17 +140,27 @@ public class Shape {
         setAnchor(anchor);\r
     }\r
 \r
-    protected static EscherRecord getEscherChild(EscherContainerRecord owner, int recordId){\r
+    /**\r
+     * Helper method to return escher child by record ID\r
+     *\r
+     * @return escher record or <code>null</code> if not found.\r
+     */\r
+    public static EscherRecord getEscherChild(EscherContainerRecord owner, int recordId){\r
         for ( Iterator iterator = owner.getChildRecords().iterator(); iterator.hasNext(); )\r
         {\r
             EscherRecord escherRecord = (EscherRecord) iterator.next();\r
             if (escherRecord.getRecordId() == recordId)\r
-                return (EscherRecord) escherRecord;\r
+                return escherRecord;\r
         }\r
         return null;\r
     }\r
 \r
-    protected static EscherProperty getEscherProperty(EscherOptRecord opt, int propId){\r
+    /**\r
+     * Returns  escher property by id.\r
+     *\r
+     * @return escher property or <code>null</code> if not found.\r
+     */\r
+     public static EscherProperty getEscherProperty(EscherOptRecord opt, int propId){\r
         for ( Iterator iterator = opt.getEscherProperties().iterator(); iterator.hasNext(); )\r
         {\r
             EscherProperty prop = (EscherProperty) iterator.next();\r
@@ -148,7 +170,14 @@ public class Shape {
         return null;\r
     }\r
 \r
-    protected static void setEscherProperty(EscherOptRecord opt, short propId, int value){\r
+    /**\r
+     * Set an escher property in the opt record.\r
+     *\r
+     * @param opt       The opt record to set the properties to.\r
+     * @param propId    The id of the property. One of the constants defined in EscherOptRecord.\r
+     * @param value     value of the property\r
+     */\r
+     public static void setEscherProperty(EscherOptRecord opt, short propId, int value){\r
         java.util.List props = opt.getEscherProperties();\r
         for ( Iterator iterator = props.iterator(); iterator.hasNext(); ) {\r
             EscherProperty prop = (EscherProperty) iterator.next();\r
@@ -163,10 +192,10 @@ public class Shape {
     }\r
 \r
     /**\r
-     *\r
-     * @return escher container which holds information about this shape\r
+     * @return  The shape container and it's children that can represent this\r
+     *          shape.\r
      */\r
-    public EscherContainerRecord getShapeRecord(){\r
+    public EscherContainerRecord getSpContainer(){\r
         return _escherContainer;\r
     }\r
 }\r
index 67f2d708bda7be0e23891b39fff33d37cfa89764..c886638c3ca92b145b40bb46370ae34d12554b1e 100644 (file)
@@ -37,10 +37,10 @@ public class ShapeFactory {
         switch (type){\r
             case ShapeTypes.TextBox:\r
             case ShapeTypes.Rectangle:\r
-                shape = new Shape(spContainer, parent);\r
+                shape = new Rectangle(spContainer, parent);\r
                 break;\r
             case ShapeTypes.PictureFrame:\r
-                shape = new Shape(spContainer, parent);\r
+                shape = new Picture(spContainer, parent);\r
                 break;\r
             case ShapeTypes.Line:\r
                 shape = new Line(spContainer, parent);\r
@@ -52,7 +52,7 @@ public class ShapeFactory {
                 shape = new ShapeGroup(spContainer, parent);\r
                 break;\r
             default:\r
-                shape = new Shape(spContainer, parent);\r
+                shape = new SimpleShape(spContainer, parent);\r
                 break;\r
         }\r
         return shape;\r
index c11e29471699a4113235236a1556609e70a99c1c..0d2d61e95a90482ad654fb0cecb4ec6f9530ad27 100644 (file)
@@ -27,13 +27,9 @@ import java.util.List;
  */\r
 public class ShapeGroup extends Shape{\r
 \r
-    public ShapeGroup(Shape parent){\r
-        super(null, parent);\r
-        _escherContainer = create();\r
-    }\r
-\r
     public ShapeGroup(){\r
-        this(null);\r
+        this(null, null);\r
+        _escherContainer = createSpContainer(false);\r
     }\r
 \r
     protected ShapeGroup(EscherContainerRecord escherRecord, Shape parent){\r
@@ -91,7 +87,7 @@ public class ShapeGroup extends Shape{
     /**\r
      * Create a new ShapeGroup and create an instance of <code>EscherSpgrContainer</code> which represents a group of shapes\r
      */\r
-    protected EscherContainerRecord create() {\r
+    protected EscherContainerRecord createSpContainer(boolean isChild) {\r
         EscherContainerRecord spgr = new EscherContainerRecord();\r
         spgr.setRecordId(EscherContainerRecord.SPGR_CONTAINER);\r
         spgr.setOptions((short)15);\r
@@ -124,7 +120,7 @@ public class ShapeGroup extends Shape{
      * @param shape - the Shape to add\r
      */\r
     public void addShape(Shape shape){\r
-        _escherContainer.addChildRecord(shape.getShapeRecord());\r
+        _escherContainer.addChildRecord(shape.getSpContainer());\r
     }\r
 \r
     /**\r
index f0834578e77225c312c1068700435e470b92e0c9..e4a688628e899cc609db4de96a887480e90c9b24 100644 (file)
@@ -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);
index 7f08b83a64994e9413e984ae0446c7cf6335eaf5..787780dcf53100ea7eeb6f271beeb07a2c10731e 100644 (file)
@@ -39,10 +39,10 @@ public class SimpleShape extends Shape {
      * @param isChild   <code>true</code> if the Line is inside a group, <code>false</code> otherwise\r
      * @return the record container which holds this shape\r
      */\r
-    protected EscherContainerRecord create(boolean isChild) {\r
+    protected EscherContainerRecord createSpContainer(boolean isChild) {\r
         EscherContainerRecord spContainer = new EscherContainerRecord();\r
         spContainer.setRecordId( EscherContainerRecord.SP_CONTAINER );\r
-        //spContainer.setOptions((short)15);\r
+        spContainer.setOptions((short)15);\r
 \r
         EscherSpRecord sp = new EscherSpRecord();\r
         int flags = EscherSpRecord.FLAG_HAVEANCHOR | EscherSpRecord.FLAG_HASSHAPETYPE;\r
index c3c0012dd95f0af96293f41803bfe9708173b6f3..2593eecd30d682889ee27a373406d1d2a5655e07 100644 (file)
@@ -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 (file)
index 0000000..f877d2a
--- /dev/null
@@ -0,0 +1,103 @@
+package org.apache.poi.hslf.record;\r
+\r
+import org.apache.poi.ddf.*;\r
+import org.apache.poi.util.LittleEndian;\r
+\r
+import java.io.OutputStream;\r
+import java.io.IOException;\r
+import java.io.ByteArrayOutputStream;\r
+import java.util.List;\r
+import java.util.Iterator;\r
+\r
+/**\r
+ * Container records which always exists inside Document.\r
+ * It always acts as a holder for escher DGG container\r
+ *  which may contain which Escher BStore container information \r
+ *  about pictures containes in the presentation (if any).\r
+ * \r
+ * @author Yegor Kozlov\r
+ */\r
+public class PPDrawingGroup extends RecordAtom {\r
+\r
+    private byte[] _header;\r
+    private EscherContainerRecord dggContainer;\r
+\r
+    protected PPDrawingGroup(byte[] source, int start, int len) {\r
+        // Get the header\r
+        _header = new byte[8];\r
+        System.arraycopy(source,start,_header,0,8);\r
+\r
+        // Get the contents for now\r
+        byte[] contents = new byte[len];\r
+        System.arraycopy(source,start,contents,0,len);\r
+\r
+        DefaultEscherRecordFactory erf = new DefaultEscherRecordFactory();\r
+        EscherRecord child = erf.createRecord(contents, 0);\r
+        child.fillFields( contents, 0, erf );\r
+        dggContainer = (EscherContainerRecord)child.getChild(0);\r
+    }\r
+\r
+    /**\r
+     * We are type 1035\r
+     */\r
+    public long getRecordType() {\r
+        return RecordTypes.PPDrawingGroup.typeID;\r
+    }\r
+\r
+    /**\r
+     * We're pretending to be an atom, so return null\r
+     */\r
+    public Record[] getChildRecords() {\r
+        return null;\r
+    }\r
+\r
+    public void writeOut(OutputStream out) throws IOException {\r
+        ByteArrayOutputStream bout = new ByteArrayOutputStream();\r
+        List child = dggContainer.getChildRecords();\r
+        for (int i = 0; i < child.size(); i++) {\r
+            EscherRecord r = (EscherRecord)child.get(i);\r
+            if (r.getRecordId() == EscherContainerRecord.BSTORE_CONTAINER){\r
+                EscherContainerRecord bstore = (EscherContainerRecord)r;\r
+\r
+                ByteArrayOutputStream b2 = new ByteArrayOutputStream();\r
+                List blip = bstore.getChildRecords();\r
+                for (Iterator it=blip.iterator(); it.hasNext();) {\r
+                    EscherBSERecord bse = (EscherBSERecord)it.next();\r
+                    byte[] b = new byte[36+8];\r
+                    bse.serialize(0, b);\r
+                    b2.write(b);\r
+                }\r
+                byte[] bstorehead = new byte[8];\r
+                LittleEndian.putShort(bstorehead, 0, bstore.getOptions());\r
+                LittleEndian.putShort(bstorehead, 2, bstore.getRecordId());\r
+                LittleEndian.putInt(bstorehead, 4, b2.size());\r
+                bout.write(bstorehead);\r
+                bout.write(b2.toByteArray());\r
+\r
+            } else {\r
+                bout.write(r.serialize());\r
+            }\r
+        }\r
+        int size = bout.size();\r
+\r
+        // Update the size (header bytes 5-8)\r
+        LittleEndian.putInt(_header,4,size+8);\r
+\r
+        // Write out our header\r
+        out.write(_header);\r
+\r
+        byte[] dgghead = new byte[8];\r
+        LittleEndian.putShort(dgghead, 0, dggContainer.getOptions());\r
+        LittleEndian.putShort(dgghead, 2, dggContainer.getRecordId());\r
+        LittleEndian.putInt(dgghead, 4, size);\r
+        out.write(dgghead);\r
+\r
+        // Finally, write out the children\r
+        out.write(bout.toByteArray());\r
+\r
+    }\r
+\r
+    public EscherContainerRecord getDggContainer(){\r
+        return dggContainer;\r
+    }\r
+}\r
index eaca249e962b0fb533a83ce886324265d6bf9b2d..41935120c1c8175b189539801d6e5c9a84bd16cf 100644 (file)
@@ -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 (file)
index 64caff7..0000000
+++ /dev/null
@@ -1,140 +0,0 @@
-/* ====================================================================\r
-   Copyright 2002-2004   Apache Software Foundation\r
-\r
-   Licensed under the Apache License, Version 2.0 (the "License");\r
-   you may not use this file except in compliance with the License.\r
-   You may obtain a copy of the License at\r
-\r
-       http://www.apache.org/licenses/LICENSE-2.0\r
-\r
-   Unless required by applicable law or agreed to in writing, software\r
-   distributed under the License is distributed on an "AS IS" BASIS,\r
-   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r
-   See the License for the specific language governing permissions and\r
-   limitations under the License.\r
-==================================================================== */\r
-package org.apache.poi.hslf.usermodel;\r
-\r
-import org.apache.poi.util.LittleEndian;\r
-\r
-/**\r
- * Represents a picture in a PowerPoint document.\r
- * <p>\r
- * The information about an image in PowerPoint document is stored in \r
- * two places:\r
- *  <li> EscherBSE container in the Document keeps information about image \r
- *    type, image index to refer by slides etc.\r
- *  <li> "Pictures" OLE stream holds the actual data of the image.\r
- * </p>\r
- * <p>\r
- *  Data in the "Pictures" OLE stream is organized as follows:<br>\r
- *  For each image there is an entry: 25 byte header + image data.\r
- *  Image data is the exact content of the JPEG file, i.e. PowerPoint\r
- *  puts the whole jpeg file there without any modifications.<br>\r
- *   Header format:\r
- *    <li> 2 byte: image type. For JPEGs it is 0x46A0, for PNG it is 0x6E00.\r
- *    <li> 2 byte: unknown.\r
- *    <li> 4 byte : image size + 17. Looks like shift from the end of \r
- *          header but why to add it to the image  size?\r
- *    <li> next 16 bytes. Unique identifier of this image which is used by \r
- *          EscherBSE record.\r
- *  </p>\r
- *\r
- * @author Yegor Kozlov\r
- */\r
-public class Picture {\r
-\r
-       /**\r
-       *  Windows Metafile\r
-       */\r
-       public static final int WMF = 0x2160;\r
-\r
-       /**\r
-       * Macintosh PICT\r
-       */\r
-       public static final int PICT = 0x5420;\r
-\r
-       /**\r
-       *  JPEG\r
-       */\r
-       public static final int JPEG = 0x46A0;\r
-\r
-       /**\r
-       *  PNG\r
-       */\r
-       public static final int PNG = 0x6E00;\r
-\r
-       /**\r
-       * Windows DIB (BMP)\r
-       */\r
-       public static final int DIB = 0x7A80;\r
-\r
-       /**\r
-       * The size of the header\r
-       */\r
-       public static final int HEADER_SIZE = 25;\r
-\r
-       /**\r
-       * Binary data of the picture\r
-       */\r
-       protected byte[] pictdata;\r
-\r
-       /**\r
-       * Header which holds information about this picture\r
-       */\r
-       protected byte[] header;\r
-\r
-       /**\r
-       * Read a picture from "Pictures" OLE stream\r
-       *\r
-       * @param pictstream    the bytes to read\r
-       * @param offset        the index of the first byte to read\r
-       */\r
-       public Picture(byte[] pictstream, int offset){\r
-               header = new byte[Picture.HEADER_SIZE];\r
-               System.arraycopy(pictstream, offset, header, 0, header.length);\r
-\r
-               int size = LittleEndian.getInt(header, 4) - 17;\r
-               pictdata = new byte[size];\r
-               System.arraycopy(pictstream, offset + Picture.HEADER_SIZE, pictdata, 0, pictdata.length);\r
-       }\r
-\r
-       /**\r
-       * @return  the binary data of this picture\r
-       */\r
-       public byte[] getData(){\r
-               return pictdata;\r
-       }\r
-\r
-       /**\r
-       * Return image size in bytes\r
-       *\r
-       * @return the size of the picture in bytes\r
-       */\r
-       public int getSize(){\r
-               return pictdata.length;\r
-       }\r
-\r
-       /**\r
-       * Returns the unique identifier (UID) of this picture.\r
-       * The UID is a checksum of the picture data. Its length is 16 bytes\r
-       * and it must be unique across the presentation.\r
-       *\r
-       * @return the unique identifier of this picture\r
-       */\r
-       public byte[] getUID(){\r
-               byte[] uid = new byte[16];\r
-               System.arraycopy(header, 8, uid, 0, uid.length);\r
-               return uid;\r
-       }\r
-\r
-       /**\r
-       * Returns the type of this picture. Must be one of the static constans defined in this class.\r
-       *\r
-       * @return type of this picture.\r
-       */\r
-       public int getType(){\r
-               int type = LittleEndian.getShort(header, 0);\r
-               return type;\r
-       }\r
-}\r
diff --git a/src/scratchpad/src/org/apache/poi/hslf/usermodel/PictureData.java b/src/scratchpad/src/org/apache/poi/hslf/usermodel/PictureData.java
new file mode 100644 (file)
index 0000000..0d2eba1
--- /dev/null
@@ -0,0 +1,157 @@
+/* ====================================================================\r
+   Copyright 2002-2004   Apache Software Foundation\r
+\r
+   Licensed under the Apache License, Version 2.0 (the "License");\r
+   you may not use this file except in compliance with the License.\r
+   You may obtain a copy of the License at\r
+\r
+       http://www.apache.org/licenses/LICENSE-2.0\r
+\r
+   Unless required by applicable law or agreed to in writing, software\r
+   distributed under the License is distributed on an "AS IS" BASIS,\r
+   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r
+   See the License for the specific language governing permissions and\r
+   limitations under the License.\r
+==================================================================== */\r
+package org.apache.poi.hslf.usermodel;\r
+\r
+import org.apache.poi.util.LittleEndian;\r
+import org.apache.poi.hslf.model.Picture;\r
+\r
+import java.io.OutputStream;\r
+import java.io.IOException;\r
+import java.security.MessageDigest;\r
+import java.security.NoSuchAlgorithmException;\r
+\r
+/**\r
+ * A class that represents the image data contained in the Presentation.\r
+ *\r
+ *  @author Yegor Kozlov\r
+ */\r
+public class PictureData {\r
+\r
+       /**\r
+       * The size of the header\r
+       */\r
+       public static final int HEADER_SIZE = 25;\r
+\r
+       /**\r
+       * Binary data of the picture\r
+       */\r
+       protected byte[] pictdata;\r
+\r
+       /**\r
+       * Header which holds information about this picture\r
+       */\r
+       protected byte[] header;\r
+\r
+    public PictureData(){\r
+        header = new byte[PictureData.HEADER_SIZE];\r
+    }\r
+\r
+       /**\r
+       * Read a picture from "Pictures" OLE stream\r
+       *\r
+       * @param pictstream    the bytes to read\r
+       * @param offset        the index of the first byte to read\r
+       */\r
+       public PictureData(byte[] pictstream, int offset){\r
+               header = new byte[PictureData.HEADER_SIZE];\r
+               System.arraycopy(pictstream, offset, header, 0, header.length);\r
+\r
+               int size = LittleEndian.getInt(header, 4) - 17;\r
+               pictdata = new byte[size];\r
+               System.arraycopy(pictstream, offset + PictureData.HEADER_SIZE, pictdata, 0, pictdata.length);\r
+       }\r
+\r
+       /**\r
+       * @return  the binary data of this picture\r
+       */\r
+       public byte[] getData(){\r
+               return pictdata;\r
+       }\r
+\r
+    /**\r
+     *  Set picture data\r
+     */\r
+    public void setData(byte[] data) {\r
+        pictdata = data;\r
+        LittleEndian.putInt(header, 4, data.length + 17);\r
+    }\r
+\r
+       /**\r
+       * Return image size in bytes\r
+       *\r
+       * @return the size of the picture in bytes\r
+       */\r
+       public int getSize(){\r
+               return pictdata.length;\r
+       }\r
+\r
+       /**\r
+       * Returns the unique identifier (UID) of this picture.\r
+       * The UID is a checksum of the picture data. Its length is 16 bytes\r
+       * and it must be unique across the presentation.\r
+       *\r
+       * @return the unique identifier of this picture\r
+       */\r
+       public byte[] getUID(){\r
+               byte[] uid = new byte[16];\r
+               System.arraycopy(header, 8, uid, 0, uid.length);\r
+               return uid;\r
+       }\r
+\r
+    /**\r
+     * Set the unique identifier (UID) of this picture.\r
+     *\r
+     * @param uid checksum of the picture data\r
+     */\r
+    public void setUID(byte[] uid){\r
+        System.arraycopy(uid, 0, header, 8, uid.length);\r
+    }\r
+\r
+       /**\r
+       * Set the type of this picture.\r
+       *\r
+       * @return type of this picture.\r
+    * Must be one of the static constans defined in the <code>Picture<code> class.\r
+       */\r
+       public void setType(int format){\r
+        switch (format){\r
+            case Picture.JPEG: LittleEndian.putInt(header, 0, -266516832); break;\r
+            case Picture.PNG: LittleEndian.putInt(header, 0, -266441216); break;\r
+        }\r
+       }\r
+\r
+    /**\r
+     * Returns the header of the Picture\r
+     *\r
+     * @return the header of the Picture\r
+     */\r
+    public byte[] getHeader(){\r
+        return header;\r
+    }\r
+\r
+    /**\r
+     * Compute 16-byte checksum of this picture\r
+     */\r
+    public static byte[] getChecksum(byte[] data) {\r
+        MessageDigest sha;\r
+        try {\r
+            sha = MessageDigest.getInstance("MD5");\r
+        } catch (NoSuchAlgorithmException e){\r
+            throw new RuntimeException(e.getMessage());\r
+        }\r
+        sha.update(data);\r
+        return sha.digest();\r
+    }\r
+\r
+    /**\r
+     * Write this picture into <code>OutputStream</code>\r
+     */\r
+    public void write(OutputStream out) throws IOException {\r
+        out.write(header);\r
+        out.write(pictdata);\r
+    }\r
+\r
+}\r
index a114ee91f95efa254f6f4782fd60dc207e60f879..ba5e75562f64c7d694c0c5a447b4d89026e07fa8 100644 (file)
@@ -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 <code>Slide</code>.
         *
@@ -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);
-   }
-
-
-       // Accesser methods follow
-
-       /**
-        * 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 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; }
+    /**
+     * 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 <code>Picture</code> 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;
+
+        EscherContainerRecord dggContainer = _documentRecord.getPPDrawingGroup().getDggContainer();
+        bstore = (EscherContainerRecord)Shape.getEscherChild(dggContainer, EscherContainerRecord.BSTORE_CONTAINER);
+        if (bstore == null){
+            bstore = new EscherContainerRecord();
+            bstore.setRecordId( EscherContainerRecord.BSTORE_CONTAINER);
+
+            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();
+             }
+        }
+
+        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);
+
+        bse.setRef(1);
+        bse.setOffset(offset);
+
+        bstore.addChildRecord(bse);
+        int count = bstore.getChildRecords().size();
+        bstore.setOptions((short)( (count << 4) | 0xF ));
+
+        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 <code>Picture</code> 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);
+    }
 }