]> source.dussan.org Git - poi.git/commitdiff
initial support for embedded movies and controls.
authorYegor Kozlov <yegor@apache.org>
Fri, 29 Aug 2008 14:01:04 +0000 (14:01 +0000)
committerYegor Kozlov <yegor@apache.org>
Fri, 29 Aug 2008 14:01:04 +0000 (14:01 +0000)
git-svn-id: https://svn.apache.org/repos/asf/poi/trunk@690262 13f79535-47bb-0310-9956-ffa450edef68

38 files changed:
src/documentation/content/xdocs/hslf/how-to-shapes.xml
src/scratchpad/src/org/apache/poi/hslf/blip/WMF.java
src/scratchpad/src/org/apache/poi/hslf/dev/PPTXMLDump.java
src/scratchpad/src/org/apache/poi/hslf/model/ActiveXShape.java [new file with mode: 0755]
src/scratchpad/src/org/apache/poi/hslf/model/MovieShape.java [new file with mode: 0755]
src/scratchpad/src/org/apache/poi/hslf/model/ShapeFactory.java
src/scratchpad/src/org/apache/poi/hslf/model/SimpleShape.java
src/scratchpad/src/org/apache/poi/hslf/model/Table.java
src/scratchpad/src/org/apache/poi/hslf/model/TextPainter.java
src/scratchpad/src/org/apache/poi/hslf/model/TextRun.java
src/scratchpad/src/org/apache/poi/hslf/model/TextShape.java
src/scratchpad/src/org/apache/poi/hslf/record/AnimationInfo.java [new file with mode: 0755]
src/scratchpad/src/org/apache/poi/hslf/record/AnimationInfoAtom.java [new file with mode: 0755]
src/scratchpad/src/org/apache/poi/hslf/record/ExAviMovie.java [new file with mode: 0755]
src/scratchpad/src/org/apache/poi/hslf/record/ExControl.java
src/scratchpad/src/org/apache/poi/hslf/record/ExControlAtom.java
src/scratchpad/src/org/apache/poi/hslf/record/ExEmbed.java
src/scratchpad/src/org/apache/poi/hslf/record/ExMCIMovie.java [new file with mode: 0755]
src/scratchpad/src/org/apache/poi/hslf/record/ExMediaAtom.java [new file with mode: 0755]
src/scratchpad/src/org/apache/poi/hslf/record/ExOleObjAtom.java
src/scratchpad/src/org/apache/poi/hslf/record/ExOleObjStg.java
src/scratchpad/src/org/apache/poi/hslf/record/ExVideoContainer.java [new file with mode: 0755]
src/scratchpad/src/org/apache/poi/hslf/record/InteractiveInfoAtom.java
src/scratchpad/src/org/apache/poi/hslf/record/OEShapeAtom.java [new file with mode: 0755]
src/scratchpad/src/org/apache/poi/hslf/record/RecordTypes.java
src/scratchpad/src/org/apache/poi/hslf/record/StyleTextPropAtom.java
src/scratchpad/src/org/apache/poi/hslf/record/TextRulerAtom.java
src/scratchpad/src/org/apache/poi/hslf/usermodel/RichTextRun.java
src/scratchpad/src/org/apache/poi/hslf/usermodel/SlideShow.java
src/scratchpad/testcases/org/apache/poi/hslf/model/TestMovieShape.java [new file with mode: 0755]
src/scratchpad/testcases/org/apache/poi/hslf/model/TestTable.java [new file with mode: 0755]
src/scratchpad/testcases/org/apache/poi/hslf/record/TestAnimationInfoAtom.java [new file with mode: 0755]
src/scratchpad/testcases/org/apache/poi/hslf/record/TestExControl.java [new file with mode: 0755]
src/scratchpad/testcases/org/apache/poi/hslf/record/TestExMediaAtom.java [new file with mode: 0755]
src/scratchpad/testcases/org/apache/poi/hslf/record/TestExOleObjAtom.java [new file with mode: 0755]
src/scratchpad/testcases/org/apache/poi/hslf/record/TestExOleObjStg.java [new file with mode: 0755]
src/scratchpad/testcases/org/apache/poi/hslf/record/TestExVideoContainer.java [new file with mode: 0755]
src/scratchpad/testcases/org/apache/poi/hslf/record/TestTextRulerAtom.java

index 37bf1837387d5e3bcde577935ad4dbbc58830209..db91bb72cee7e335585c373332c3ec0dd1672136 100644 (file)
@@ -47,6 +47,7 @@
                     <li><link href="#Graphics2D">Shapes and Graphics2D</link></li>
                     <li><link href="#Render">How to convert slides into images</link></li>
                     <li><link href="#HeadersFooters">Headers / Footers</link></li>
+                    <li><link href="#Movies">How to embed movies</link></li>
                 </ul>
             </section>
             <section><title>Features</title>
           hdd.setFootersText("Created by POI-HSLF");
                 </source>
               </section>
+            <anchor id="Movies"/>
+            <section><title>How to embed movies</title>
+              <source>
+
+        SlideShow ppt = new SlideShow();
+        Slide slide = ppt.createSlide();
+
+        //UNC or local Returns UNC or local path to a video file
+        String moviePath = "card.mpg";
+        int movieIdx = ppt.addMovie(moviePath, MovieShape.MOVIE_MPEG);
+
+        String thumbnailPath = "card.png";
+        int thumbnailIdx = ppt.addPicture(new File(thumbnailPath), Picture.PNG);
+        MovieShape shape = new MovieShape(movieIdx, thumbnailIdx);
+        shape.setAnchor(new Rectangle2D.Float(300,225,120,90));
+        slide.addShape(shape);
+
+        FileOutputStream out = new FileOutputStream("hslf-movie.ppt");
+        ppt.write(out);
+        out.close();
+                </source>
+              </section>
                 </section>
         </section>
     </body>
index fe18664d075aa7a997e19e8e747352724b09a368..b7f22c137b85abade91294ef3aec3ef330b365a1 100644 (file)
@@ -17,6 +17,7 @@
 package org.apache.poi.hslf.blip;
 
 import org.apache.poi.util.LittleEndian;
+import org.apache.poi.util.POILogger;
 import org.apache.poi.hslf.model.Picture;
 import org.apache.poi.hslf.model.Shape;
 import org.apache.poi.hslf.exceptions.HSLFException;
@@ -76,7 +77,7 @@ public class WMF extends Metafile {
         Header header = new Header();
         header.wmfsize = data.length - aldus.getSize();
         header.bounds = new java.awt.Rectangle((short)aldus.left, (short)aldus.top, (short)aldus.right-(short)aldus.left, (short)aldus.bottom-(short)aldus.top);
-        //coefficiaent to translate from WMF dpi to 96pdi
+        //coefficient to translate from WMF dpi to 96pdi
         int coeff = 96*Shape.EMU_PER_POINT/aldus.inch;
         header.size = new java.awt.Dimension(header.bounds.width*coeff, header.bounds.height*coeff);
         header.zipsize = compressed.length;
@@ -119,7 +120,7 @@ public class WMF extends Metafile {
      *  <li>short  Checksum;       Checksum value for previous 10 shorts
      * </ul>
      */
-    public static class AldusHeader{
+    public class AldusHeader{
         public static final int APMHEADER_KEY = 0x9AC6CDD7;
 
         public int handle;
@@ -143,8 +144,9 @@ public class WMF extends Metafile {
             reserved = LittleEndian.getInt(data, pos); pos += LittleEndian.INT_SIZE;
 
             checksum = LittleEndian.getShort(data, pos); pos += LittleEndian.SHORT_SIZE;
-            if (checksum != getChecksum())
-                throw new HSLFException("WMF checksum does not match the header data");
+            if (checksum != getChecksum()){
+                logger.log(POILogger.WARN, "WMF checksum does not match the header data");
+            }
         }
 
         /**
index 603361e4d82f27234353cd056bc226c4d5206b88..d02c4928ebad0b496d5c42d904b8911746ae2205 100644 (file)
@@ -125,7 +125,7 @@ public class PPTXMLDump {
                 dump(data, pos, size, padding);
             } else {
                 //dump first 100 bytes of the atom data
-                dump(out, data, pos, Math.min(size, 100), padding, true);
+                dump(out, data, pos, size, padding, true);
             }
                        padding--;
             write(out, "</"+recname + ">" + CR, padding);
diff --git a/src/scratchpad/src/org/apache/poi/hslf/model/ActiveXShape.java b/src/scratchpad/src/org/apache/poi/hslf/model/ActiveXShape.java
new file mode 100755 (executable)
index 0000000..4b1889a
--- /dev/null
@@ -0,0 +1,152 @@
+package org.apache.poi.hslf.model;\r
+\r
+import org.apache.poi.ddf.*;\r
+import org.apache.poi.hslf.record.*;\r
+import org.apache.poi.hslf.exceptions.HSLFException;\r
+import org.apache.poi.util.LittleEndian;\r
+\r
+import java.io.ByteArrayOutputStream;\r
+import java.io.UnsupportedEncodingException;\r
+import java.util.Iterator;\r
+\r
+/**\r
+ * Represents an ActiveX control in a PowerPoint document.\r
+ *\r
+ * TODO: finish\r
+ * @author Yegor Kozlov\r
+ */\r
+public class ActiveXShape extends Picture {\r
+    public static final int DEFAULT_ACTIVEX_THUMBNAIL = -1;\r
+\r
+    /**\r
+     * Create a new <code>Picture</code>\r
+     *\r
+    * @param pictureIdx the index of the picture\r
+     */\r
+    public ActiveXShape(int movieIdx, int pictureIdx){\r
+        super(pictureIdx, null);\r
+        setActiveXIndex(movieIdx);\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 ActiveXShape(EscherContainerRecord escherRecord, Shape parent){\r
+        super(escherRecord, parent);\r
+    }\r
+\r
+    /**\r
+     * Create a new Placeholder and initialize internal structures\r
+     *\r
+     * @return the created <code>EscherContainerRecord</code> which holds shape data\r
+     */\r
+    protected EscherContainerRecord createSpContainer(int idx, boolean isChild) {\r
+        _escherContainer = super.createSpContainer(idx, isChild);\r
+\r
+        EscherSpRecord spRecord = _escherContainer.getChildById(EscherSpRecord.RECORD_ID);\r
+        spRecord.setFlags(EscherSpRecord.FLAG_HAVEANCHOR | EscherSpRecord.FLAG_HASSHAPETYPE | EscherSpRecord.FLAG_OLESHAPE);\r
+\r
+        setShapeType(ShapeTypes.HostControl);\r
+        setEscherProperty(EscherProperties.BLIP__PICTUREID, idx);\r
+        setEscherProperty(EscherProperties.LINESTYLE__COLOR, 0x8000001);\r
+        setEscherProperty(EscherProperties.LINESTYLE__NOLINEDRAWDASH, 0x80008);\r
+        setEscherProperty(EscherProperties.SHADOWSTYLE__COLOR, 0x8000002);\r
+        setEscherProperty(EscherProperties.PROTECTION__LOCKAGAINSTGROUPING, -1);\r
+\r
+        EscherClientDataRecord cldata = new EscherClientDataRecord();\r
+        cldata.setOptions((short)0xF);\r
+        _escherContainer.getChildRecords().add(cldata);\r
+\r
+        OEShapeAtom oe = new OEShapeAtom();\r
+\r
+        //convert hslf into ddf\r
+        ByteArrayOutputStream out = new ByteArrayOutputStream();\r
+        try {\r
+            oe.writeOut(out);\r
+        } catch(Exception e){\r
+            throw new HSLFException(e);\r
+        }\r
+        cldata.setRemainingData(out.toByteArray());\r
+\r
+        return _escherContainer;\r
+    }\r
+\r
+    /**\r
+     * Assign a control to this shape\r
+     *\r
+     * @see {@link org.apache.poi.hslf.usermodel.SlideShow#addMovie(String, int)}\r
+     * @param idx  the index of the movie\r
+     */\r
+    public void setActiveXIndex(int idx){\r
+        EscherContainerRecord spContainer = getSpContainer();\r
+        for (Iterator it = spContainer.getChildRecords().iterator(); it.hasNext();) {\r
+            EscherRecord obj = (EscherRecord) it.next();\r
+            if (obj.getRecordId() == EscherClientDataRecord.RECORD_ID) {\r
+                EscherClientDataRecord clientRecord = (EscherClientDataRecord)obj;\r
+                byte[] recdata = clientRecord.getRemainingData();\r
+                LittleEndian.putInt(recdata, 8, idx);\r
+            }\r
+        }\r
+    }\r
+\r
+    public int getControlIndex(){\r
+        int idx = -1;\r
+        OEShapeAtom oe = (OEShapeAtom)getClientDataRecord(RecordTypes.OEShapeAtom.typeID);\r
+        if(oe != null) idx = oe.getOptions();\r
+        return idx;\r
+    }\r
+\r
+    /**\r
+     * Set a property of this ActiveX control\r
+     * @param key\r
+     * @param value\r
+     */\r
+    public void setProperty(String key, String value){\r
+\r
+    }\r
+\r
+    /**\r
+     * Document-level container that specifies information about an ActiveX control\r
+     *\r
+     * @return container that specifies information about an ActiveX control\r
+     */\r
+    public ExControl getExControl(){\r
+        int idx = getControlIndex();\r
+        ExControl ctrl = null;\r
+        Document doc = getSheet().getSlideShow().getDocumentRecord();\r
+        ExObjList lst = (ExObjList)doc.findFirstOfType(RecordTypes.ExObjList.typeID);\r
+        if(lst != null){\r
+            Record[] ch = lst.getChildRecords();\r
+            for (int i = 0; i < ch.length; i++) {\r
+                if(ch[i] instanceof ExControl){\r
+                    ExControl c = (ExControl)ch[i];\r
+                    if(c.getExOleObjAtom().getObjID() == idx){\r
+                        ctrl = c;\r
+                        break;\r
+                    }\r
+                }\r
+            }\r
+        }\r
+        return ctrl;\r
+    }\r
+\r
+    protected void afterInsert(Sheet sheet){\r
+        ExControl ctrl = getExControl();\r
+        ctrl.getExControlAtom().setSlideId(sheet._getSheetNumber());\r
+\r
+        try {\r
+            String name = ctrl.getProgId() + "-" + getControlIndex();\r
+            byte[] data = (name + '\u0000').getBytes("UTF-16LE");\r
+            EscherComplexProperty prop = new EscherComplexProperty(EscherProperties.GROUPSHAPE__SHAPENAME, false, data);\r
+            EscherOptRecord opt = (EscherOptRecord)getEscherChild(_escherContainer, EscherOptRecord.RECORD_ID);\r
+            opt.addEscherProperty(prop);\r
+        } catch (UnsupportedEncodingException e){\r
+            throw new HSLFException(e);\r
+        }\r
+\r
+    }\r
+}\r
diff --git a/src/scratchpad/src/org/apache/poi/hslf/model/MovieShape.java b/src/scratchpad/src/org/apache/poi/hslf/model/MovieShape.java
new file mode 100755 (executable)
index 0000000..4cf7037
--- /dev/null
@@ -0,0 +1,159 @@
+package org.apache.poi.hslf.model;\r
+\r
+import org.apache.poi.ddf.*;\r
+import org.apache.poi.hslf.record.*;\r
+import org.apache.poi.hslf.exceptions.HSLFException;\r
+import org.apache.poi.hslf.usermodel.SlideShow;\r
+import org.apache.poi.util.LittleEndian;\r
+\r
+import java.io.ByteArrayOutputStream;\r
+import java.util.Iterator;\r
+\r
+/**\r
+ * Represents a movie in a PowerPoint document.\r
+ *\r
+ * @author Yegor Kozlov\r
+ */\r
+public class MovieShape extends Picture {\r
+    public static final int DEFAULT_MOVIE_THUMBNAIL = -1;\r
+\r
+    public static final int MOVIE_MPEG = 1;\r
+    public static final int MOVIE_AVI  = 2;\r
+\r
+    /**\r
+     * Create a new <code>Picture</code>\r
+     *\r
+    * @param pictureIdx the index of the picture\r
+     */\r
+    public MovieShape(int movieIdx, int pictureIdx){\r
+        super(pictureIdx, null);\r
+        setMovieIndex(movieIdx);\r
+        setAutoPlay(true);\r
+    }\r
+\r
+    /**\r
+     * Create a new <code>Picture</code>\r
+     *\r
+     * @param idx the index of the picture\r
+     * @param parent the parent shape\r
+     */\r
+    public MovieShape(int movieIdx, int idx, Shape parent) {\r
+        super(idx, parent);\r
+        setMovieIndex(movieIdx);\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 MovieShape(EscherContainerRecord escherRecord, Shape parent){\r
+        super(escherRecord, parent);\r
+    }\r
+\r
+    /**\r
+     * Create a new Placeholder and initialize internal structures\r
+     *\r
+     * @return the created <code>EscherContainerRecord</code> which holds shape data\r
+     */\r
+    protected EscherContainerRecord createSpContainer(int idx, boolean isChild) {\r
+        _escherContainer = super.createSpContainer(idx, isChild);\r
+\r
+        setEscherProperty(EscherProperties.PROTECTION__LOCKAGAINSTGROUPING, 0x1000100);\r
+        setEscherProperty(EscherProperties.FILL__NOFILLHITTEST, 0x10001);\r
+\r
+        EscherClientDataRecord cldata = new EscherClientDataRecord();\r
+        cldata.setOptions((short)0xF);\r
+        _escherContainer.getChildRecords().add(cldata);\r
+\r
+        OEShapeAtom oe = new OEShapeAtom();\r
+        InteractiveInfo info = new InteractiveInfo();\r
+        InteractiveInfoAtom infoAtom = info.getInteractiveInfoAtom();\r
+        infoAtom.setAction(InteractiveInfoAtom.ACTION_MEDIA);\r
+        infoAtom.setHyperlinkType(InteractiveInfoAtom.LINK_NULL);\r
+\r
+        AnimationInfo an = new AnimationInfo();\r
+        AnimationInfoAtom anAtom = an.getAnimationInfoAtom();\r
+        anAtom.setFlag(AnimationInfoAtom.Automatic, true);\r
+\r
+        //convert hslf into ddf\r
+        ByteArrayOutputStream out = new ByteArrayOutputStream();\r
+        try {\r
+            oe.writeOut(out);\r
+            an.writeOut(out);\r
+            info.writeOut(out);\r
+        } catch(Exception e){\r
+            throw new HSLFException(e);\r
+        }\r
+        cldata.setRemainingData(out.toByteArray());\r
+\r
+        return _escherContainer;\r
+    }\r
+\r
+    /**\r
+     * Assign a movie to this shape\r
+     *\r
+     * @see {@link org.apache.poi.hslf.usermodel.SlideShow#addMovie(String, int)}\r
+     * @param idx  the index of the movie\r
+     */\r
+    public void setMovieIndex(int idx){\r
+        OEShapeAtom oe = (OEShapeAtom)getClientDataRecord(RecordTypes.OEShapeAtom.typeID);\r
+        oe.setOptions(idx);\r
+\r
+        AnimationInfo an = (AnimationInfo)getClientDataRecord(RecordTypes.AnimationInfo.typeID);\r
+        if(an != null) {\r
+            AnimationInfoAtom ai = an.getAnimationInfoAtom();\r
+            ai.setDimColor(0x07000000);\r
+            ai.setFlag(AnimationInfoAtom.Automatic, true);\r
+            ai.setFlag(AnimationInfoAtom.Play, true);\r
+            ai.setFlag(AnimationInfoAtom.Synchronous, true);\r
+            ai.setOrderID(idx + 1);\r
+        }\r
+    }\r
+\r
+    public void setAutoPlay(boolean flag){\r
+        AnimationInfo an = (AnimationInfo)getClientDataRecord(RecordTypes.AnimationInfo.typeID);\r
+        if(an != null){\r
+            an.getAnimationInfoAtom().setFlag(AnimationInfoAtom.Automatic, flag);\r
+            updateClientData();\r
+        }\r
+    }\r
+\r
+    public boolean  isAutoPlay(){\r
+        AnimationInfo an = (AnimationInfo)getClientDataRecord(RecordTypes.AnimationInfo.typeID);\r
+        if(an != null){\r
+            return an.getAnimationInfoAtom().getFlag(AnimationInfoAtom.Automatic);\r
+        }\r
+        return false;\r
+    }\r
+\r
+    /**\r
+     *  Returns UNC or local path to a video file\r
+     *\r
+     * @return UNC or local path to a video file\r
+     */\r
+    public String getPath(){\r
+        OEShapeAtom oe = (OEShapeAtom)getClientDataRecord(RecordTypes.OEShapeAtom.typeID);\r
+        int idx = oe.getOptions();\r
+\r
+        SlideShow ppt = getSheet().getSlideShow();\r
+        ExObjList lst = (ExObjList)ppt.getDocumentRecord().findFirstOfType(RecordTypes.ExObjList.typeID);\r
+        if(lst == null) return null;\r
+\r
+        Record[]  r = lst.getChildRecords();\r
+        for (int i = 0; i < r.length; i++) {\r
+            if(r[i] instanceof ExMCIMovie){\r
+                ExMCIMovie mci = (ExMCIMovie)r[i];\r
+                ExVideoContainer exVideo = mci.getExVideo();\r
+                int objectId = exVideo.getExMediaAtom().getObjectId();\r
+                if(objectId == idx){\r
+                    return exVideo.getPathAtom().getText();\r
+                }\r
+            }\r
+\r
+        }\r
+        return null;\r
+    }\r
+}\r
index 4bef9033dbe2f255a47360e7643d08385954d61e..8b4abd5e590e5680058bf0e288192bd4a9ee1f88 100644 (file)
@@ -19,8 +19,10 @@ package org.apache.poi.hslf.model;
 import org.apache.poi.ddf.*;
 import org.apache.poi.util.POILogger;
 import org.apache.poi.util.POILogFactory;
+import org.apache.poi.hslf.record.*;
 
 import java.util.List;
+import java.util.Iterator;
 
 /**
  * Create a <code>Shape</code> object depending on its type
@@ -45,14 +47,14 @@ public class ShapeFactory {
 
     public static ShapeGroup createShapeGroup(EscherContainerRecord spContainer, Shape parent){
         ShapeGroup group = null;
-        UnknownEscherRecord opt = (UnknownEscherRecord)Shape.getEscherChild((EscherContainerRecord)spContainer.getChild(0), (short)0xF122);
+        EscherRecord opt = Shape.getEscherChild((EscherContainerRecord)spContainer.getChild(0), (short)0xF122);
         if(opt != null){
             try {
                 EscherPropertyFactory f = new EscherPropertyFactory();
-                List props = f.createProperties( opt.getData(), 0, opt.getInstance() );
+                List props = f.createProperties( opt.serialize(), 8, opt.getInstance() );
                 EscherSimpleProperty p = (EscherSimpleProperty)props.get(0);
                 if(p.getPropertyNumber() == 0x39F && p.getPropertyValue() == 1){
-                    group = new ShapeGroup(spContainer, parent);
+                    group = new Table(spContainer, parent);
                 } else {
                     group = new ShapeGroup(spContainer, parent);
                 }
@@ -68,7 +70,7 @@ public class ShapeFactory {
      }
 
     public static Shape createSimpeShape(EscherContainerRecord spContainer, Shape parent){
-        Shape shape;
+        Shape shape = null;
         EscherSpRecord spRecord = spContainer.getChildById(EscherSpRecord.RECORD_ID);
 
         int type = spRecord.getOptions() >> 4;
@@ -76,14 +78,26 @@ public class ShapeFactory {
             case ShapeTypes.TextBox:
                 shape = new TextBox(spContainer, parent);
                 break;
-            case ShapeTypes.HostControl: 
+            case ShapeTypes.HostControl:
             case ShapeTypes.PictureFrame: {
-                EscherOptRecord opt = (EscherOptRecord)Shape.getEscherChild(spContainer, EscherOptRecord.RECORD_ID);
-                EscherProperty prop = Shape.getEscherProperty(opt, EscherProperties.BLIP__PICTUREID);
-                if(prop != null)
-                    shape = new OLEShape(spContainer, parent); //presence of BLIP__PICTUREID indicates it is an embedded object 
-                else
-                    shape = new Picture(spContainer, parent);
+                InteractiveInfo info = (InteractiveInfo)getClientDataRecord(spContainer, RecordTypes.InteractiveInfo.typeID);
+                OEShapeAtom oes = (OEShapeAtom)getClientDataRecord(spContainer, RecordTypes.OEShapeAtom.typeID);
+                if(info != null && info.getInteractiveInfoAtom() != null){
+                    switch(info.getInteractiveInfoAtom().getAction()){
+                        case InteractiveInfoAtom.ACTION_OLE:
+                            shape = new OLEShape(spContainer, parent);
+                            break;
+                        case InteractiveInfoAtom.ACTION_MEDIA:
+                            shape = new MovieShape(spContainer, parent);
+                            break;
+                        default:
+                            break;
+                    }
+                } else if (oes != null){
+                    shape = new OLEShape(spContainer, parent);
+                }
+                
+                if(shape == null) shape = new Picture(spContainer, parent);
                 break;
             }
             case ShapeTypes.Line:
@@ -108,4 +122,22 @@ public class ShapeFactory {
         return shape;
 
     }
+
+    protected static Record getClientDataRecord(EscherContainerRecord spContainer, int recordType) {
+        Record oep = null;
+        for (Iterator it = spContainer.getChildRecords().iterator(); it.hasNext();) {
+            EscherRecord obj = (EscherRecord) it.next();
+            if (obj.getRecordId() == EscherClientDataRecord.RECORD_ID) {
+                byte[] data = obj.serialize();
+                Record[] records = Record.findChildRecords(data, 8, data.length - 8);
+                for (int j = 0; j < records.length; j++) {
+                    if (records[j].getRecordType() == recordType) {
+                        return records[j];
+                    }
+                }
+            }
+        }
+        return oep;
+    }
+
 }
index d336cb3f3ffbef268dd210a2f75419dc395d5d58..71d10948c5bc52f236758cc5b6b3d73b30a5071a 100644 (file)
@@ -21,11 +21,13 @@ import org.apache.poi.ddf.*;
 import org.apache.poi.util.LittleEndian;
 import org.apache.poi.hslf.record.ColorSchemeAtom;
 import org.apache.poi.hslf.record.Record;
+import org.apache.poi.hslf.exceptions.HSLFException;
 
 import java.awt.*;
 import java.awt.geom.AffineTransform;
 import java.awt.geom.Rectangle2D;
 import java.util.Iterator;
+import java.io.ByteArrayOutputStream;
 
 /**
  *  An abstract simple (non-group) shape.
@@ -35,6 +37,12 @@ import java.util.Iterator;
  */
 public class SimpleShape extends Shape {
 
+    /**
+     * Records stored in EscherClientDataRecord
+     */
+    protected Record[] _clientRecords;
+    protected EscherClientDataRecord _clientData;
+
     /**
      * Create a SimpleShape object and initialize it from the supplied Record container.
      *
@@ -293,21 +301,46 @@ public class SimpleShape extends Shape {
      * @param recordType type of the record to search
      */
     protected Record getClientDataRecord(int recordType) {
-        Record oep = null;
-        EscherContainerRecord spContainer = getSpContainer();
-        for (Iterator it = spContainer.getChildRecords().iterator(); it.hasNext();) {
-            EscherRecord obj = (EscherRecord) it.next();
-            if (obj.getRecordId() == EscherClientDataRecord.RECORD_ID) {
-                byte[] data = obj.serialize();
-                Record[] records = Record.findChildRecords(data, 8, data.length - 8);
-                for (int j = 0; j < records.length; j++) {
-                    if (records[j].getRecordType() == recordType) {
-                        return records[j];
-                    }
-                }
+
+        Record[] records = getClientRecords();
+        if(records != null) for (int i = 0; i < records.length; i++) {
+            if(records[i].getRecordType() == recordType){
+                return records[i];
+            }
+        }
+        return null;
+    }
+
+    protected Record[] getClientRecords() {
+        if(_clientData == null){
+            EscherRecord r = Shape.getEscherChild(getSpContainer(), EscherClientDataRecord.RECORD_ID);
+            //ddf can return EscherContainerRecord with recordId=EscherClientDataRecord.RECORD_ID
+            //convert in to EscherClientDataRecord on the fly
+            if(!(r instanceof EscherClientDataRecord)){
+                byte[] data = r.serialize();
+                r = new EscherClientDataRecord();
+                r.fillFields(data, 0, new DefaultEscherRecordFactory());
             }
+            _clientData = (EscherClientDataRecord)r;
         }
-        return oep;
+        if(_clientData != null && _clientRecords == null){
+            byte[] data = _clientData.getRemainingData();
+            _clientRecords = Record.findChildRecords(data, 0, data.length);
+        }
+        return _clientRecords;
     }
 
+    protected void updateClientData() {
+        if(_clientData != null && _clientRecords != null){
+            ByteArrayOutputStream out = new ByteArrayOutputStream();
+            try {
+                for (int i = 0; i < _clientRecords.length; i++) {
+                    _clientRecords[i].writeOut(out);
+                }
+            } catch(Exception e){
+                throw new HSLFException(e);
+            }
+            _clientData.setRemainingData(out.toByteArray());
+        }
+    }
 }
index 43dab58af129ef7f5bafe6354778ede79df27796..113a2d8f2b05b460bbfb2e0eaf8c149bb066fd46 100755 (executable)
@@ -89,7 +89,7 @@ public class Table extends ShapeGroup {
      * @param escherRecord <code>EscherSpContainer</code> container which holds information about this shape\r
      * @param parent       the parent of the shape\r
      */\r
-    protected Table(EscherContainerRecord escherRecord, Shape parent) {\r
+    public Table(EscherContainerRecord escherRecord, Shape parent) {\r
         super(escherRecord, parent);\r
     }\r
 \r
index 6eff54329078d622c775cca914a1131eafe1c873..9a86120e7c7484b1b622280ad5538b955fa6bc47 100755 (executable)
@@ -23,9 +23,11 @@ import org.apache.poi.util.POILogFactory;
 \r
 import java.text.AttributedString;\r
 import java.text.AttributedCharacterIterator;\r
+import java.text.BreakIterator;\r
 import java.awt.font.TextAttribute;\r
 import java.awt.font.LineBreakMeasurer;\r
 import java.awt.font.TextLayout;\r
+import java.awt.font.FontRenderContext;\r
 import java.awt.*;\r
 import java.awt.geom.Rectangle2D;\r
 import java.awt.geom.Point2D;\r
@@ -89,11 +91,69 @@ public class TextPainter {
     }\r
 \r
     public void paint(Graphics2D graphics){\r
+        Rectangle2D anchor = _shape.getLogicalAnchor2D();\r
+        TextElement[] elem = getTextElements((float)anchor.getWidth(), graphics.getFontRenderContext());\r
+        if(elem == null) return;\r
+\r
+        float textHeight = 0;\r
+        for (int i = 0; i < elem.length; i++) {\r
+            textHeight += elem[i].ascent + elem[i].descent;\r
+        }\r
+\r
+        int valign = _shape.getVerticalAlignment();\r
+        double y0 = anchor.getY();\r
+        switch (valign){\r
+            case TextShape.AnchorTopBaseline:\r
+            case TextShape.AnchorTop:\r
+                y0 += _shape.getMarginTop();\r
+                break;\r
+            case TextShape.AnchorBottom:\r
+                y0 += anchor.getHeight() - textHeight - _shape.getMarginBottom();\r
+                break;\r
+            default:\r
+            case TextShape.AnchorMiddle:\r
+                float delta =  (float)anchor.getHeight() - textHeight - _shape.getMarginTop() - _shape.getMarginBottom();\r
+                y0 += _shape.getMarginTop()  + delta/2;\r
+                break;\r
+        }\r
+\r
+        //finally draw the text fragments\r
+        for (int i = 0; i < elem.length; i++) {\r
+            y0 += elem[i].ascent;\r
+\r
+            Point2D.Double pen = new Point2D.Double();\r
+            pen.y = y0;\r
+            switch (elem[i]._align) {\r
+                default:\r
+                case TextShape.AlignLeft:\r
+                    pen.x = anchor.getX() + _shape.getMarginLeft();\r
+                    break;\r
+                case TextShape.AlignCenter:\r
+                    pen.x = anchor.getX() + _shape.getMarginLeft() +\r
+                            (anchor.getWidth() - elem[i].advance - _shape.getMarginLeft() - _shape.getMarginRight()) / 2;\r
+                    break;\r
+                case TextShape.AlignRight:\r
+                    pen.x = anchor.getX() + _shape.getMarginLeft() +\r
+                            (anchor.getWidth() - elem[i].advance - _shape.getMarginLeft() - _shape.getMarginRight());\r
+                    break;\r
+            }\r
+            if(elem[i]._bullet != null){\r
+                graphics.drawString(elem[i]._bullet.getIterator(), (float)(pen.x + elem[i]._bulletOffset), (float)pen.y);\r
+            }\r
+            AttributedCharacterIterator chIt = elem[i]._text.getIterator();\r
+            if(chIt.getEndIndex() > chIt.getBeginIndex()) {\r
+                graphics.drawString(chIt, (float)(pen.x + elem[i]._textOffset), (float)pen.y);\r
+            }\r
+            y0 += elem[i].descent;\r
+        }\r
+    }\r
+\r
+    public TextElement[] getTextElements(float textWidth, FontRenderContext frc){\r
         TextRun run = _shape.getTextRun();\r
-        if (run == null) return;\r
+        if (run == null) return null;\r
 \r
         String text = run.getText();\r
-        if (text == null || text.equals("")) return;\r
+        if (text == null || text.equals("")) return null;\r
 \r
         AttributedString at = getAttributedString(run);\r
 \r
@@ -101,11 +161,8 @@ public class TextPainter {
         int paragraphStart = it.getBeginIndex();\r
         int paragraphEnd = it.getEndIndex();\r
 \r
-        Rectangle2D anchor = _shape.getLogicalAnchor2D();\r
-\r
-        float textHeight = 0;\r
         ArrayList lines = new ArrayList();\r
-        LineBreakMeasurer measurer = new LineBreakMeasurer(it, graphics.getFontRenderContext());\r
+        LineBreakMeasurer measurer = new LineBreakMeasurer(it, frc);\r
         measurer.setPosition(paragraphStart);\r
         while (measurer.getPosition() < paragraphEnd) {\r
             int startIndex = measurer.getPosition();\r
@@ -120,7 +177,7 @@ public class TextPainter {
                 break;\r
             }\r
 \r
-            float wrappingWidth = (float)anchor.getWidth() - _shape.getMarginLeft() - _shape.getMarginRight();\r
+            float wrappingWidth = textWidth - _shape.getMarginLeft() - _shape.getMarginRight();\r
             int bulletOffset = rt.getBulletOffset();\r
             int textOffset = rt.getTextOffset();\r
             int indent = rt.getIndentLevel();\r
@@ -138,7 +195,7 @@ public class TextPainter {
                 if(text_val != 0) textOffset = text_val;\r
             }\r
 \r
-            wrappingWidth -= textOffset;\r
+            if(bulletOffset > 0 || prStart || startIndex == 0) wrappingWidth -= textOffset;\r
 \r
             if (_shape.getWordWrap() == TextShape.WrapNone) {\r
                 wrappingWidth = _shape.getSheet().getSlideShow().getPageSize().width;\r
@@ -147,7 +204,7 @@ public class TextPainter {
             TextLayout textLayout = measurer.nextLayout(wrappingWidth + 1,\r
                     nextBreak == -1 ? paragraphEnd : nextBreak, true);\r
             if (textLayout == null) {\r
-                textLayout = measurer.nextLayout((float)anchor.getWidth(),\r
+                textLayout = measurer.nextLayout(textWidth,\r
                     nextBreak == -1 ? paragraphEnd : nextBreak, false);\r
             }\r
             if(textLayout == null){\r
@@ -173,6 +230,8 @@ public class TextPainter {
             el.advance = textLayout.getAdvance();\r
             el._textOffset = textOffset;\r
             el._text = new AttributedString(it, startIndex, endIndex);\r
+            el.textStartIndex = startIndex;\r
+            el.textEndIndex = endIndex;\r
 \r
             if (prStart){\r
                 int sp = rt.getSpaceBefore();\r
@@ -203,8 +262,6 @@ public class TextPainter {
             }\r
             el.descent = descent;\r
 \r
-            textHeight += el.ascent + el.descent;\r
-\r
             if(rt.isBullet() && (prStart || startIndex == 0)){\r
                 it.setIndex(startIndex);\r
 \r
@@ -236,56 +293,11 @@ public class TextPainter {
             lines.add(el);\r
         }\r
 \r
-        int valign = _shape.getVerticalAlignment();\r
-        double y0 = anchor.getY();\r
-        switch (valign){\r
-            case TextShape.AnchorTopBaseline:\r
-            case TextShape.AnchorTop:\r
-                y0 += _shape.getMarginTop();\r
-                break;\r
-            case TextShape.AnchorBottom:\r
-                y0 += anchor.getHeight() - textHeight - _shape.getMarginBottom();\r
-                break;\r
-            default:\r
-            case TextShape.AnchorMiddle:\r
-                float delta =  (float)anchor.getHeight() - textHeight - _shape.getMarginTop() - _shape.getMarginBottom();\r
-                y0 += _shape.getMarginTop()  + delta/2;\r
-                break;\r
-        }\r
-\r
         //finally draw the text fragments\r
-        for (int i = 0; i < lines.size(); i++) {\r
-            TextElement elem = (TextElement)lines.get(i);\r
-            y0 += elem.ascent;\r
-\r
-            Point2D.Double pen = new Point2D.Double();\r
-            pen.y = y0;\r
-            switch (elem._align) {\r
-                default:\r
-                case TextShape.AlignLeft:\r
-                    pen.x = anchor.getX() + _shape.getMarginLeft();\r
-                    break;\r
-                case TextShape.AlignCenter:\r
-                    pen.x = anchor.getX() + _shape.getMarginLeft() +\r
-                            (anchor.getWidth() - elem.advance - _shape.getMarginLeft() - _shape.getMarginRight()) / 2;\r
-                    break;\r
-                case TextShape.AlignRight:\r
-                    pen.x = anchor.getX() + _shape.getMarginLeft() +\r
-                            (anchor.getWidth() - elem.advance - _shape.getMarginLeft() - _shape.getMarginRight());\r
-                    break;\r
-            }\r
-            if(elem._bullet != null){\r
-                graphics.drawString(elem._bullet.getIterator(), (float)(pen.x + elem._bulletOffset), (float)pen.y);\r
-            }\r
-            AttributedCharacterIterator chIt = elem._text.getIterator();\r
-            if(chIt.getEndIndex() > chIt.getBeginIndex()) {\r
-                graphics.drawString(chIt, (float)(pen.x + elem._textOffset), (float)pen.y);\r
-            }\r
-            y0 += elem.descent;\r
-        }\r
+        TextElement[] elems = new TextElement[lines.size()];\r
+        return (TextElement[])lines.toArray(elems);\r
     }\r
 \r
-\r
     public static class TextElement {\r
         public AttributedString _text;\r
         public int _textOffset;\r
@@ -294,5 +306,6 @@ public class TextPainter {
         public int _align;\r
         public float ascent, descent;\r
         public float advance;\r
+        public int textStartIndex, textEndIndex;\r
     }\r
 }\r
index 27dbe1b2d573f0c50bf5fb7bd3f5bf2d8deeb1be..cf2db50e86b2a6e0846f759693640845b85398a7 100644 (file)
@@ -22,6 +22,7 @@ package org.apache.poi.hslf.model;
 
 import java.util.LinkedList;
 import java.util.Vector;
+import java.util.List;
 
 import org.apache.poi.hslf.model.textproperties.TextPropCollection;
 import org.apache.poi.hslf.record.*;
@@ -45,7 +46,8 @@ public class TextRun
        protected TextBytesAtom  _byteAtom;
        protected TextCharsAtom  _charAtom;
        protected StyleTextPropAtom _styleAtom;
-       protected boolean _isUnicode;
+    protected TextRulerAtom _ruler;
+    protected boolean _isUnicode;
        protected RichTextRun[] _rtRuns;
        private SlideShow slideShow;
     private Sheet sheet;
@@ -103,155 +105,159 @@ public class TextRun
                        pStyles = _styleAtom.getParagraphStyles();
                        cStyles = _styleAtom.getCharacterStyles();
                }
-
-               // Handle case of no current style, with a default
-               if(pStyles.size() == 0 || cStyles.size() == 0) { 
-                       _rtRuns = new RichTextRun[1];
-                       _rtRuns[0] = new RichTextRun(this, 0, runRawText.length());
-               } else {
-                       // Build up Rich Text Runs, one for each 
-                       //  character/paragraph style pair
-                       Vector rtrs = new Vector();
-
-                       int pos = 0;
-                       
-                       int curP = 0;
-                       int curC = 0;
-                       int pLenRemain = -1;
-                       int cLenRemain = -1;
-                       
-                       // Build one for each run with the same style
-                       while(pos <= runRawText.length() && curP < pStyles.size() && curC < cStyles.size()) {
-                               // Get the Props to use
-                               TextPropCollection pProps = (TextPropCollection)pStyles.get(curP);
-                               TextPropCollection cProps = (TextPropCollection)cStyles.get(curC);
-                               
-                               int pLen = pProps.getCharactersCovered();
-                               int cLen = cProps.getCharactersCovered();
-                               
-                               // Handle new pass
-                               boolean freshSet = false;
-                               if(pLenRemain == -1 && cLenRemain == -1) { freshSet = true; }
-                               if(pLenRemain == -1) { pLenRemain = pLen; }
-                               if(cLenRemain == -1) { cLenRemain = cLen; }
-                               
-                               // So we know how to build the eventual run
-                               int runLen = -1;
-                               boolean pShared = false;
-                               boolean cShared = false;
-                               
-                               // Same size, new styles - neither shared
-                               if(pLen == cLen && freshSet) {
-                                       runLen = cLen;
-                                       pShared = false;
-                                       cShared = false;
-                                       curP++;
-                                       curC++;
-                                       pLenRemain = -1;
-                                       cLenRemain = -1;
-                               } else {
-                                       // Some sharing
-                                       
-                                       // See if we are already in a shared block
-                                       if(pLenRemain < pLen) {
-                                               // Existing shared p block
-                                               pShared = true;
-                                               
-                                               // Do we end with the c block, or either side of it?
-                                               if(pLenRemain == cLenRemain) {
-                                                       // We end at the same time
-                                                       cShared = false;
-                                                       runLen = pLenRemain;
-                                                       curP++;
-                                                       curC++;
-                                                       pLenRemain = -1;
-                                                       cLenRemain = -1;
-                                               } else if(pLenRemain < cLenRemain) {
-                                                       // We end before the c block
-                                                       cShared = true;
-                                                       runLen = pLenRemain;
-                                                       curP++;
-                                                       cLenRemain -= pLenRemain;
-                                                       pLenRemain = -1;
-                                               } else {
-                                                       // We end after the c block
-                                                       cShared = false;
-                                                       runLen = cLenRemain;
-                                                       curC++;
-                                                       pLenRemain -= cLenRemain;
-                                                       cLenRemain = -1;
-                                               }
-                                       } else if(cLenRemain < cLen) {
-                                               // Existing shared c block
-                                               cShared = true;
-                                               
-                                               // Do we end with the p block, or either side of it?
-                                               if(pLenRemain == cLenRemain) {
-                                                       // We end at the same time
-                                                       pShared = false;
-                                                       runLen = cLenRemain;
-                                                       curP++;
-                                                       curC++;
-                                                       pLenRemain = -1;
-                                                       cLenRemain = -1;
-                                               } else if(cLenRemain < pLenRemain) {
-                                                       // We end before the p block
-                                                       pShared = true;
-                                                       runLen = cLenRemain;
-                                                       curC++;
-                                                       pLenRemain -= cLenRemain;
-                                                       cLenRemain = -1;
-                                               } else {
-                                                       // We end after the p block
-                                                       pShared = false;
-                                                       runLen = pLenRemain;
-                                                       curP++;
-                                                       cLenRemain -= pLenRemain;
-                                                       pLenRemain = -1;
-                                               }
-                                       } else {
-                                               // Start of a shared block
-                                               if(pLenRemain < cLenRemain) {
-                                                       // Shared c block
-                                                       pShared = false;
-                                                       cShared = true;
-                                                       runLen = pLenRemain;
-                                                       curP++;
-                                                       cLenRemain -= pLenRemain;
-                                                       pLenRemain = -1;
-                                               } else {
-                                                       // Shared p block
-                                                       pShared = true;
-                                                       cShared = false;
-                                                       runLen = cLenRemain;
-                                                       curC++;
-                                                       pLenRemain -= cLenRemain;
-                                                       cLenRemain = -1;
-                                               }
-                                       }
-                               }
-                               
-                               // Wind on
-                               int prevPos = pos;
-                               pos += runLen;
-                               // Adjust for end-of-run extra 1 length
-                               if(pos > runRawText.length()) {
-                                       runLen--; 
-                               }
-                               
-                               // Save
-                               RichTextRun rtr = new RichTextRun(this, prevPos, runLen, pProps, cProps, pShared, cShared);
-                               rtrs.add(rtr);
-                       }
-                       
-                       // Build the array
-                       _rtRuns = new RichTextRun[rtrs.size()];
-                       rtrs.copyInto(_rtRuns);
-               }
+        buildRichTextRuns(pStyles, cStyles, runRawText);
        }
        
-       
-       // Update methods follow
+       public void buildRichTextRuns(LinkedList pStyles, LinkedList cStyles, String runRawText){
+
+        // Handle case of no current style, with a default
+        if(pStyles.size() == 0 || cStyles.size() == 0) {
+            _rtRuns = new RichTextRun[1];
+            _rtRuns[0] = new RichTextRun(this, 0, runRawText.length());
+        } else {
+            // Build up Rich Text Runs, one for each
+            //  character/paragraph style pair
+            Vector rtrs = new Vector();
+
+            int pos = 0;
+
+            int curP = 0;
+            int curC = 0;
+            int pLenRemain = -1;
+            int cLenRemain = -1;
+
+            // Build one for each run with the same style
+            while(pos <= runRawText.length() && curP < pStyles.size() && curC < cStyles.size()) {
+                // Get the Props to use
+                TextPropCollection pProps = (TextPropCollection)pStyles.get(curP);
+                TextPropCollection cProps = (TextPropCollection)cStyles.get(curC);
+
+                int pLen = pProps.getCharactersCovered();
+                int cLen = cProps.getCharactersCovered();
+
+                // Handle new pass
+                boolean freshSet = false;
+                if(pLenRemain == -1 && cLenRemain == -1) { freshSet = true; }
+                if(pLenRemain == -1) { pLenRemain = pLen; }
+                if(cLenRemain == -1) { cLenRemain = cLen; }
+
+                // So we know how to build the eventual run
+                int runLen = -1;
+                boolean pShared = false;
+                boolean cShared = false;
+
+                // Same size, new styles - neither shared
+                if(pLen == cLen && freshSet) {
+                    runLen = cLen;
+                    pShared = false;
+                    cShared = false;
+                    curP++;
+                    curC++;
+                    pLenRemain = -1;
+                    cLenRemain = -1;
+                } else {
+                    // Some sharing
+
+                    // See if we are already in a shared block
+                    if(pLenRemain < pLen) {
+                        // Existing shared p block
+                        pShared = true;
+
+                        // Do we end with the c block, or either side of it?
+                        if(pLenRemain == cLenRemain) {
+                            // We end at the same time
+                            cShared = false;
+                            runLen = pLenRemain;
+                            curP++;
+                            curC++;
+                            pLenRemain = -1;
+                            cLenRemain = -1;
+                        } else if(pLenRemain < cLenRemain) {
+                            // We end before the c block
+                            cShared = true;
+                            runLen = pLenRemain;
+                            curP++;
+                            cLenRemain -= pLenRemain;
+                            pLenRemain = -1;
+                        } else {
+                            // We end after the c block
+                            cShared = false;
+                            runLen = cLenRemain;
+                            curC++;
+                            pLenRemain -= cLenRemain;
+                            cLenRemain = -1;
+                        }
+                    } else if(cLenRemain < cLen) {
+                        // Existing shared c block
+                        cShared = true;
+
+                        // Do we end with the p block, or either side of it?
+                        if(pLenRemain == cLenRemain) {
+                            // We end at the same time
+                            pShared = false;
+                            runLen = cLenRemain;
+                            curP++;
+                            curC++;
+                            pLenRemain = -1;
+                            cLenRemain = -1;
+                        } else if(cLenRemain < pLenRemain) {
+                            // We end before the p block
+                            pShared = true;
+                            runLen = cLenRemain;
+                            curC++;
+                            pLenRemain -= cLenRemain;
+                            cLenRemain = -1;
+                        } else {
+                            // We end after the p block
+                            pShared = false;
+                            runLen = pLenRemain;
+                            curP++;
+                            cLenRemain -= pLenRemain;
+                            pLenRemain = -1;
+                        }
+                    } else {
+                        // Start of a shared block
+                        if(pLenRemain < cLenRemain) {
+                            // Shared c block
+                            pShared = false;
+                            cShared = true;
+                            runLen = pLenRemain;
+                            curP++;
+                            cLenRemain -= pLenRemain;
+                            pLenRemain = -1;
+                        } else {
+                            // Shared p block
+                            pShared = true;
+                            cShared = false;
+                            runLen = cLenRemain;
+                            curC++;
+                            pLenRemain -= cLenRemain;
+                            cLenRemain = -1;
+                        }
+                    }
+                }
+
+                // Wind on
+                int prevPos = pos;
+                pos += runLen;
+                // Adjust for end-of-run extra 1 length
+                if(pos > runRawText.length()) {
+                    runLen--;
+                }
+
+                // Save
+                RichTextRun rtr = new RichTextRun(this, prevPos, runLen, pProps, cProps, pShared, cShared);
+                rtrs.add(rtr);
+            }
+
+            // Build the array
+            _rtRuns = new RichTextRun[rtrs.size()];
+            rtrs.copyInto(_rtRuns);
+        }
+
+    }
+
+    // Update methods follow
        
        /**
         * Adds the supplied text onto the end of the TextRun, 
@@ -379,7 +385,7 @@ public class TextRun
         * @param run
         * @param s
         */
-       public synchronized void changeTextInRichTextRun(RichTextRun run, String s) {
+       public void changeTextInRichTextRun(RichTextRun run, String s) {
                // Figure out which run it is
                int runID = -1;
                for(int i=0; i<_rtRuns.length; i++) {
@@ -457,7 +463,7 @@ public class TextRun
         *  as the the first character has. 
         * If you care about styling, do setText on a RichTextRun instead 
         */
-       public synchronized void setRawText(String s) {
+       public void setRawText(String s) {
                // Save the new text to the atoms
                storeText(s);
                RichTextRun fst = _rtRuns[0];
@@ -491,7 +497,7 @@ public class TextRun
      * Changes the text.
      * Converts '\r' into '\n'
      */
-    public synchronized void setText(String s) {
+    public void setText(String s) {
         String text = normalize(s);
         setRawText(text);
     }
@@ -500,7 +506,7 @@ public class TextRun
         * Ensure a StyleTextPropAtom is present for this run, 
         *  by adding if required. Normally for internal TextRun use.
         */
-       public synchronized void ensureStyleAtomPresent() {
+       public void ensureStyleAtomPresent() {
                if(_styleAtom != null) {
                        // All there
                        return;
@@ -669,13 +675,28 @@ public class TextRun
     }
 
     public TextRulerAtom getTextRuler(){
-        for (int i = 0; i < _records.length; i++) {
-            if(_records[i] instanceof TextRulerAtom) return (TextRulerAtom)_records[i];
+        if(_ruler == null){
+            if(_records != null) for (int i = 0; i < _records.length; i++) {
+                if(_records[i] instanceof TextRulerAtom) {
+                    _ruler = (TextRulerAtom)_records[i];
+                    break;
+                }
+            }
+
         }
-        return null;
+        return _ruler;
 
     }
 
+    public TextRulerAtom createTextRuler(){
+        _ruler = getTextRuler();
+        if(_ruler == null){
+            _ruler = TextRulerAtom.getParagraphInstance();
+            _headerAtom.getParentRecord().appendChildRecord(_ruler);
+        }
+        return _ruler;
+    }
+
     /**
      * Returns a new string with line breaks converted into internal ppt representation
      */
@@ -692,4 +713,5 @@ public class TextRun
     public Record[] getRecords(){
         return _records;
     }
+
 }
index de78b62656289d4a76a94008b06b08abfabe542c..751753b22e538e426dd25d9c42fc43459dd2c480 100755 (executable)
@@ -133,6 +133,7 @@ public abstract class TextShape extends SimpleShape {
             _txtbox.appendChildRecord(sta);
 
             _txtrun = new TextRun(tha,tca,sta);
+            _txtrun._records = new Record[]{tha, tca, sta};
             _txtrun.setText("");
 
             _escherContainer.addChildRecord(_txtbox.getEscherRecord());
diff --git a/src/scratchpad/src/org/apache/poi/hslf/record/AnimationInfo.java b/src/scratchpad/src/org/apache/poi/hslf/record/AnimationInfo.java
new file mode 100755 (executable)
index 0000000..37dae8f
--- /dev/null
@@ -0,0 +1,97 @@
+/*\r
+* Licensed to the Apache Software Foundation (ASF) under one or more\r
+* contributor license agreements.  See the NOTICE file distributed with\r
+* this work for additional information regarding copyright ownership.\r
+* The ASF licenses this file to You under the Apache License, Version 2.0\r
+* (the "License"); you may not use this file except in compliance with\r
+* the License.  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.record;\r
+\r
+import java.io.IOException;\r
+import java.io.OutputStream;\r
+\r
+import org.apache.poi.util.LittleEndian;\r
+import org.apache.poi.util.POILogger;\r
+\r
+/**\r
+ * A container record that specifies information about animation information for a shape.\r
+ *\r
+ * @author Yegor Kozlov\r
+ */\r
+public class AnimationInfo extends RecordContainer {\r
+       private byte[] _header;\r
+\r
+       // Links to our more interesting children\r
+       private AnimationInfoAtom animationAtom;\r
+\r
+       /**\r
+        * Set things up, and find our more interesting children\r
+        */\r
+       protected AnimationInfo(byte[] source, int start, int len) {\r
+               // Grab the header\r
+               _header = new byte[8];\r
+               System.arraycopy(source,start,_header,0,8);\r
+\r
+               // Find our children\r
+               _children = Record.findChildRecords(source,start+8,len-8);\r
+               findInterestingChildren();\r
+       }\r
+\r
+       /**\r
+        * Go through our child records, picking out the ones that are\r
+        *  interesting, and saving those for use by the easy helper\r
+        *  methods.\r
+        */     \r
+       private void findInterestingChildren() {\r
+\r
+               // First child should be the ExMediaAtom\r
+               if(_children[0] instanceof AnimationInfoAtom) {\r
+                       animationAtom = (AnimationInfoAtom)_children[0];\r
+               } else {\r
+                       logger.log(POILogger.ERROR, "First child record wasn't a AnimationInfoAtom, was of type " + _children[0].getRecordType());\r
+               }\r
+       }\r
+\r
+       /**\r
+        * Create a new AnimationInfo, with blank fields\r
+        */\r
+       public AnimationInfo() {\r
+        // Setup our header block\r
+               _header = new byte[8];\r
+               _header[0] = 0x0f; // We are a container record\r
+               LittleEndian.putShort(_header, 2, (short)getRecordType());\r
+               \r
+        _children = new Record[1];\r
+               _children[0] = animationAtom = new AnimationInfoAtom();\r
+       }\r
+\r
+       /**\r
+        * We are of type 4103\r
+        */\r
+       public long getRecordType() { return RecordTypes.AnimationInfo.typeID; }\r
+\r
+       /**\r
+        * Write the contents of the record back, so it can be written\r
+        *  to disk\r
+        */\r
+       public void writeOut(OutputStream out) throws IOException {\r
+               writeOut(_header[0],_header[1],getRecordType(),_children,out);\r
+       }\r
+\r
+    /**\r
+     * Returns the AnimationInfo\r
+     */\r
+    public AnimationInfoAtom getAnimationInfoAtom() {\r
+        return animationAtom;\r
+    }\r
+\r
+}\r
diff --git a/src/scratchpad/src/org/apache/poi/hslf/record/AnimationInfoAtom.java b/src/scratchpad/src/org/apache/poi/hslf/record/AnimationInfoAtom.java
new file mode 100755 (executable)
index 0000000..66982ea
--- /dev/null
@@ -0,0 +1,278 @@
+/* ====================================================================\r
+   Licensed to the Apache Software Foundation (ASF) under one or more\r
+   contributor license agreements.  See the NOTICE file distributed with\r
+   this work for additional information regarding copyright ownership.\r
+   The ASF licenses this file to You under the Apache License, Version 2.0\r
+   (the "License"); you may not use this file except in compliance with\r
+   the License.  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
+        \r
+\r
+package org.apache.poi.hslf.record;\r
+\r
+import java.io.IOException;\r
+import java.io.OutputStream;\r
+import java.util.Date;\r
+\r
+import org.apache.poi.hslf.util.SystemTimeUtils;\r
+import org.apache.poi.util.LittleEndian;\r
+\r
+/**\r
+ * An atom record that specifies the animation information for a shape.\r
+ *\r
+ * @author Yegor Kozlov\r
+ */\r
+public class AnimationInfoAtom extends RecordAtom\r
+{\r
+\r
+    /**\r
+     * whether the animation plays in the reverse direction\r
+     */\r
+    public static final int Reverse = 1;\r
+    /**\r
+     * whether the animation starts automatically\r
+     */\r
+    public static final int Automatic = 4;\r
+    /**\r
+     * whether the animation has an associated sound\r
+     */\r
+    public static final int Sound = 16;\r
+    /**\r
+     * whether all playing sounds are stopped when this animation begins\r
+     */\r
+    public static final int StopSound = 64;\r
+    /**\r
+     * whether an associated sound, media or action verb is activated when the shape is clicked.\r
+     */\r
+    public static final int Play = 256;\r
+    /**\r
+     * specifies that the animation, while playing, stops other slide show actions.\r
+     */\r
+    public static final int Synchronous = 1024;\r
+    /**\r
+     * whether the shape is hidden while the animation is not playing\r
+     */\r
+    public static final int Hide = 4096;\r
+    /**\r
+     * whether the background of the shape is animated\r
+     */\r
+    public static final int AnimateBg = 16384;\r
+\r
+    /**\r
+     * Record header.\r
+     */\r
+    private byte[] _header;\r
+\r
+    /**\r
+     * record data\r
+     */\r
+    private byte[] _recdata;\r
+\r
+    /**\r
+     * Constructs a brand new link related atom record.\r
+     */\r
+    protected AnimationInfoAtom() {\r
+        _recdata = new byte[28];\r
+\r
+        _header = new byte[8];\r
+        LittleEndian.putShort(_header, 0, (short)0x01);\r
+        LittleEndian.putShort(_header, 2, (short)getRecordType());\r
+        LittleEndian.putInt(_header, 4, _recdata.length);\r
+    }\r
+\r
+    /**\r
+     * Constructs the link related atom record from its\r
+     *  source data.\r
+     *\r
+     * @param source the source data as a byte array.\r
+     * @param start the start offset into the byte array.\r
+     * @param len the length of the slice in the byte array.\r
+     */\r
+    protected AnimationInfoAtom(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
+        // Grab the record data\r
+        _recdata = new byte[len-8];\r
+        System.arraycopy(source,start+8,_recdata,0,len-8);\r
+    }\r
+\r
+    /**\r
+     * Gets the record type.\r
+     * @return the record type.\r
+     */\r
+    public long getRecordType() {\r
+        return RecordTypes.AnimationInfoAtom.typeID;\r
+    }\r
+\r
+    /**\r
+     * Write the contents of the record back, so it can be written\r
+     * to disk\r
+     *\r
+     * @param out the output stream to write to.\r
+     * @throws java.io.IOException if an error occurs.\r
+     */\r
+    public void writeOut(OutputStream out) throws IOException {\r
+        out.write(_header);\r
+        out.write(_recdata);\r
+    }\r
+\r
+    /**\r
+     * A rgb structure that specifies a color for the dim effect after the animation is complete.\r
+     *\r
+     * @return  color for the dim effect after the animation is complete\r
+     */\r
+    public int getDimColor(){\r
+        return LittleEndian.getInt(_recdata, 0);\r
+    }\r
+\r
+    /**\r
+     * A rgb structure that specifies a color for the dim effect after the animation is complete.\r
+     *\r
+     * @param rgb  color for the dim effect after the animation is complete\r
+     */\r
+    public void setDimColor(int rgb){\r
+         LittleEndian.putInt(_recdata, 0, rgb);\r
+    }\r
+\r
+    /**\r
+     *  A bit mask specifying options for displaying headers and footers\r
+     *\r
+     * @return A bit mask specifying options for displaying headers and footers\r
+     */\r
+    public int getMask(){\r
+        return LittleEndian.getInt(_recdata, 4);\r
+    }\r
+\r
+    /**\r
+     *  A bit mask specifying options for displaying video\r
+     *\r
+     * @param mask A bit mask specifying options for displaying video\r
+     */\r
+    public void setMask(int mask){\r
+        LittleEndian.putInt(_recdata, 4, mask);\r
+    }\r
+\r
+    /**\r
+     * @param bit the bit to check\r
+     * @return whether the specified flag is set\r
+     */\r
+    public boolean getFlag(int bit){\r
+        return (getMask() & bit) != 0;\r
+    }\r
+\r
+    /**\r
+     * @param  bit the bit to set\r
+     * @param  value whether the specified bit is set\r
+     */\r
+    public void setFlag(int bit, boolean value){\r
+        int mask = getMask();\r
+        if(value) mask |= bit;\r
+        else mask &= ~bit;\r
+        setMask(mask);\r
+    }\r
+\r
+    /**\r
+     * A 4-byte unsigned integer that specifies a reference to a sound\r
+     * in the SoundCollectionContainer record to locate the embedded audio\r
+     *\r
+     * @return  reference to a sound\r
+     */\r
+    public int getSoundIdRef(){\r
+        return LittleEndian.getInt(_recdata, 8);\r
+    }\r
+\r
+    /**\r
+     * A 4-byte unsigned integer that specifies a reference to a sound\r
+     * in the SoundCollectionContainer record to locate the embedded audio\r
+     *\r
+     * @param id reference to a sound\r
+     */\r
+    public void setSoundIdRef(int id){\r
+         LittleEndian.putInt(_recdata, 8, id);\r
+    }\r
+\r
+    /**\r
+     * A signed integer that specifies the delay time, in milliseconds, before the animation starts to play.\r
+     * If {@link Automatic} is 0x1, this value MUST be greater than or equal to 0; otherwise, this field MUST be ignored.\r
+     */\r
+    public int getDelayTime(){\r
+        return LittleEndian.getInt(_recdata, 12);\r
+    }\r
+    /**\r
+     * A signed integer that specifies the delay time, in milliseconds, before the animation starts to play.\r
+     * If {@link Automatic} is 0x1, this value MUST be greater than or equal to 0; otherwise, this field MUST be ignored.\r
+     */\r
+    public void setDelayTime(int id){\r
+         LittleEndian.putInt(_recdata, 12, id);\r
+    }\r
+\r
+    /**\r
+     * A signed integer that specifies the order of the animation in the slide.\r
+     * It MUST be greater than or equal to -2. The value -2 specifies that this animation follows the order of\r
+     * the corresponding placeholder shape on the main master slide or title master slide.\r
+     * The value -1 SHOULD NOT <105> be used.\r
+     */\r
+    public int getOrderID(){\r
+        return LittleEndian.getInt(_recdata, 16);\r
+    }\r
+\r
+    /**\r
+     * A signed integer that specifies the order of the animation in the slide.\r
+     * It MUST be greater than or equal to -2. The value -2 specifies that this animation follows the order of\r
+     * the corresponding placeholder shape on the main master slide or title master slide.\r
+     * The value -1 SHOULD NOT <105> be used.\r
+     */\r
+    public void setOrderID(int id){\r
+         LittleEndian.putInt(_recdata, 16, id);\r
+    }\r
+\r
+    /**\r
+     * An unsigned integer that specifies the number of slides that this animation continues playing.\r
+     * This field is utilized only in conjunction with media.\r
+     * The value 0xFFFFFFFF specifies that the animation plays for one slide.\r
+     */\r
+    public int getSlideCount(){\r
+        return LittleEndian.getInt(_recdata, 18);\r
+    }\r
+\r
+    /**\r
+     * An unsigned integer that specifies the number of slides that this animation continues playing.\r
+     * This field is utilized only in conjunction with media.\r
+     * The value 0xFFFFFFFF specifies that the animation plays for one slide.\r
+     */\r
+    public void setSlideCount(int id){\r
+         LittleEndian.putInt(_recdata, 18, id);\r
+    }\r
+\r
+    public String toString(){\r
+        StringBuffer buf = new StringBuffer();\r
+        buf.append("AnimationInfoAtom\n");\r
+        buf.append("\tDimColor: " + getDimColor() + "\n");\r
+        int mask = getMask();\r
+        buf.append("\tMask: " + mask + ", 0x"+Integer.toHexString(mask)+"\n");\r
+        buf.append("\t  Reverse: " + getFlag(Reverse)+"\n");\r
+        buf.append("\t  Automatic: " + getFlag(Automatic)+"\n");\r
+        buf.append("\t  Sound: " + getFlag(Sound)+"\n");\r
+        buf.append("\t  StopSound: " + getFlag(StopSound)+"\n");\r
+        buf.append("\t  Play: " + getFlag(Play)+"\n");\r
+        buf.append("\t  Synchronous: " + getFlag(Synchronous)+"\n");\r
+        buf.append("\t  Hide: " + getFlag(Hide)+"\n");\r
+        buf.append("\t  AnimateBg: " + getFlag(AnimateBg)+"\n");\r
+        buf.append("\tSoundIdRef: " + getSoundIdRef() + "\n");\r
+        buf.append("\tDelayTime: " + getDelayTime() + "\n");\r
+        buf.append("\tOrderID: " + getOrderID() + "\n");\r
+        buf.append("\tSlideCount: " + getSlideCount() + "\n");\r
+        return buf.toString();\r
+    }\r
+\r
+}\r
diff --git a/src/scratchpad/src/org/apache/poi/hslf/record/ExAviMovie.java b/src/scratchpad/src/org/apache/poi/hslf/record/ExAviMovie.java
new file mode 100755 (executable)
index 0000000..81ad98d
--- /dev/null
@@ -0,0 +1,53 @@
+/*\r
+* Licensed to the Apache Software Foundation (ASF) under one or more\r
+* contributor license agreements.  See the NOTICE file distributed with\r
+* this work for additional information regarding copyright ownership.\r
+* The ASF licenses this file to You under the Apache License, Version 2.0\r
+* (the "License"); you may not use this file except in compliance with\r
+* the License.  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.record;\r
+\r
+import java.io.IOException;\r
+import java.io.OutputStream;\r
+\r
+import org.apache.poi.util.LittleEndian;\r
+import org.apache.poi.util.POILogger;\r
+\r
+/**\r
+ * A container record that specifies information about a movie stored externally.\r
+ *\r
+ * @author Yegor Kozlov\r
+ */\r
+public class ExAviMovie extends ExMCIMovie {\r
+\r
+    /**\r
+     * Set things up, and find our more interesting children\r
+     */\r
+    protected ExAviMovie(byte[] source, int start, int len) {\r
+        super(source, start, len);\r
+    }\r
+\r
+    /**\r
+     * Create a new ExAviMovie, with blank fields\r
+     */\r
+    public ExAviMovie() {\r
+        super();\r
+\r
+    }\r
+    /**\r
+     * We are of type 4102\r
+     */\r
+    public long getRecordType() {\r
+        return RecordTypes.ExAviMovie.typeID;\r
+    }\r
+\r
+}\r
index e6e7da893c71ed0591578ae52918bca70f0741da..603cbad764800e6de61c6520850d7bf8def93c57 100755 (executable)
@@ -24,7 +24,7 @@ import org.apache.poi.util.LittleEndian;
 import org.apache.poi.util.POILogger;\r
 \r
 /**\r
- * Container for OLE Control object. It contains:\r
+ * A container record that specifies information about an ActiveX control. It contains:\r
  * <p>\r
  * 1. ExControlAtom (4091)\r
  * 2. ExOleObjAtom (4035)\r
@@ -40,9 +40,6 @@ import org.apache.poi.util.POILogger;
  */\r
 public class ExControl extends ExEmbed {\r
 \r
-    // Links to our more interesting children\r
-    private ExControlAtom ctrlAtom;\r
-\r
     /**\r
      * Set things up, and find our more interesting children\r
      *\r
@@ -60,7 +57,7 @@ public class ExControl extends ExEmbed {
     public ExControl() {\r
         super();\r
 \r
-        _children[0] = ctrlAtom = new ExControlAtom();\r
+        _children[0] = embedAtom = new ExControlAtom();\r
     }\r
 \r
     /**\r
@@ -70,7 +67,7 @@ public class ExControl extends ExEmbed {
      */\r
     public ExControlAtom getExControlAtom()\r
     {\r
-        return ctrlAtom;\r
+        return (ExControlAtom)_children[0];\r
     }\r
 \r
     /**\r
@@ -82,14 +79,4 @@ public class ExControl extends ExEmbed {
     public long getRecordType() {\r
         return RecordTypes.ExControl.typeID;\r
     }\r
-\r
-    protected RecordAtom getEmbedAtom(Record[] children){\r
-        RecordAtom atom = null;\r
-        if(_children[0] instanceof ExControlAtom) {\r
-            atom = (ExControlAtom)_children[0];\r
-        } else {\r
-            logger.log(POILogger.ERROR, "First child record wasn't a ExControlAtom, was of type " + _children[0].getRecordType());\r
-        }\r
-        return atom;\r
-    }\r
 }\r
index ae99d0a235e7b32aa426106a305fa318694d5e4c..0204803ea5ae59d2a4508181772fa4c5998d7e48 100755 (executable)
@@ -23,8 +23,7 @@ import java.io.OutputStream;
 import org.apache.poi.util.LittleEndian;\r
 \r
 /**\r
- * Contains a long integer, slideID, which stores the unique slide identifier of the slide\r
- * where this control resides.\r
+ * An atom record that specifies an ActiveX control.\r
  *\r
  * @author Yegor Kozlov\r
  */\r
@@ -67,10 +66,32 @@ public class ExControlAtom extends RecordAtom {
         _id = LittleEndian.getInt(source, start + 8);\r
     }\r
 \r
+    /**\r
+     * An integer that specifies which presentation slide is associated with the ActiveX control.\r
+     * <p>\r
+     * It MUST be 0x00000000 or equal to the value of the slideId field of a SlidePersistAtom record.\r
+     * The value 0x00000000 specifies a null reference.\r
+     * </p>\r
+     *\r
+     * @return an integer that specifies which presentation slide is associated with the ActiveX control\r
+     */\r
     public int getSlideId() {\r
         return _id;\r
     }\r
 \r
+    /**\r
+     * Sets which presentation slide is associated with the ActiveX control.\r
+     *\r
+     * @param id an integer that specifies which presentation slide is associated with the ActiveX control\r
+     * <p>\r
+     * It MUST be 0x00000000 or equal to the value of the slideId field of a SlidePersistAtom record.\r
+     * The value 0x00000000 specifies a null reference.\r
+     * </p>\r
+     */\r
+    public void setSlideId(int id) {\r
+        _id = id;\r
+    }\r
+\r
     /**\r
      * Gets the record type.\r
      * @return the record type.\r
index 6d61f2ef71097bb5f554eae75cd20898fbbb3e0f..09e95abe4c22285b8996181382ad8667ff0ba5f5 100644 (file)
@@ -36,7 +36,7 @@ public class ExEmbed extends RecordContainer {
     private byte[] _header;
 
     // Links to our more interesting children
-    private RecordAtom embedAtom;
+    protected RecordAtom embedAtom;
     private ExOleObjAtom oleObjAtom;
     private CString menuName;
     private CString progId;
@@ -72,10 +72,11 @@ public class ExEmbed extends RecordContainer {
 
         // Setup our child records
         CString cs1 = new CString();
+        cs1.setOptions(0x1 << 4);
         CString cs2 = new CString();
+        cs2.setOptions(0x2 << 4);
         CString cs3 = new CString();
-//        cs1.setOptions(0x00);
-//        cs2.setOptions(0x10);
+        cs3.setOptions(0x3 << 4);
         _children[0] = new ExEmbedAtom();
         _children[1] = new ExOleObjAtom();
         _children[2] = cs1;
@@ -91,7 +92,11 @@ public class ExEmbed extends RecordContainer {
     private void findInterestingChildren() {
 
         // First child should be the ExHyperlinkAtom
-        embedAtom = getEmbedAtom(_children);
+        if(_children[0] instanceof ExEmbedAtom) {
+            embedAtom = (ExEmbedAtom)_children[0];
+        } else {
+            logger.log(POILogger.ERROR, "First child record wasn't a ExEmbedAtom, was of type " + _children[0].getRecordType());
+        }
 
         // Second child should be the ExOleObjAtom
         if (_children[1] instanceof ExOleObjAtom) {
@@ -102,25 +107,17 @@ public class ExEmbed extends RecordContainer {
 
         for (int i = 2; i < _children.length; i++) {
             if (_children[i] instanceof CString){
-                if (menuName == null) menuName = (CString)_children[i];
-                else if (progId == null) progId = (CString)_children[i];
-                else if (clipboardName == null) clipboardName = (CString)_children[i];
-            } else {
-                logger.log(POILogger.ERROR, "Record after atoms wasn't a CString, was of type " + _children[i].getRecordType());
+                CString cs = (CString)_children[i];
+                int opts = cs.getOptions() >> 4;
+                switch(opts){
+                    case 0x1: menuName = cs; break;
+                    case 0x2: progId = cs; break;
+                    case 0x3: clipboardName = cs; break;
+                }
             }
         }
     }
 
-    protected RecordAtom getEmbedAtom(Record[] children){
-        RecordAtom atom = null;
-        if(_children[0] instanceof ExEmbedAtom) {
-            atom = (ExEmbedAtom)_children[0];
-        } else {
-            logger.log(POILogger.ERROR, "First child record wasn't a ExEmbedAtom, was of type " + _children[0].getRecordType());
-        }
-        return atom;
-    }
-
     /**
      * Gets the {@link ExEmbedAtom}.
      *
@@ -151,6 +148,11 @@ public class ExEmbed extends RecordContainer {
         return menuName == null ? null : menuName.getText();
     }
 
+    public void setMenuName(String s)
+    {
+        if(menuName != null) menuName.setText(s);
+    }
+
     /**
      * Gets the OLE Programmatic Identifier.
      * 
@@ -161,6 +163,10 @@ public class ExEmbed extends RecordContainer {
         return progId == null ? null : progId.getText();
     }
 
+    public void setProgId(String s)
+    {
+        if(progId != null) progId.setText(s);
+    }
     /**
      * Gets the name that appears in the paste special dialog.
      *
@@ -171,6 +177,10 @@ public class ExEmbed extends RecordContainer {
         return clipboardName == null ? null : clipboardName.getText();
     }
 
+    public void setClipboardName(String s)
+    {
+        if(clipboardName != null) clipboardName.setText(s);
+    }
     /**
      * Returns the type (held as a little endian in bytes 3 and 4)
      * that this class handles.
diff --git a/src/scratchpad/src/org/apache/poi/hslf/record/ExMCIMovie.java b/src/scratchpad/src/org/apache/poi/hslf/record/ExMCIMovie.java
new file mode 100755 (executable)
index 0000000..c226092
--- /dev/null
@@ -0,0 +1,100 @@
+/*\r
+* Licensed to the Apache Software Foundation (ASF) under one or more\r
+* contributor license agreements.  See the NOTICE file distributed with\r
+* this work for additional information regarding copyright ownership.\r
+* The ASF licenses this file to You under the Apache License, Version 2.0\r
+* (the "License"); you may not use this file except in compliance with\r
+* the License.  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.record;\r
+\r
+import java.io.IOException;\r
+import java.io.OutputStream;\r
+\r
+import org.apache.poi.util.LittleEndian;\r
+import org.apache.poi.util.POILogger;\r
+\r
+/**\r
+ * A container record that specifies information about a movie stored externally.\r
+ *\r
+ * @author Yegor Kozlov\r
+ */\r
+public class ExMCIMovie extends RecordContainer {\r
+    private byte[] _header;\r
+\r
+    //An ExVideoContainer record that specifies information about the MCI movie\r
+    private ExVideoContainer exVideo;\r
+\r
+    /**\r
+     * Set things up, and find our more interesting children\r
+     */\r
+    protected ExMCIMovie(byte[] source, int start, int len) {\r
+        // Grab the header\r
+        _header = new byte[8];\r
+        System.arraycopy(source, start, _header, 0, 8);\r
+\r
+        // Find our children\r
+        _children = Record.findChildRecords(source, start + 8, len - 8);\r
+        findInterestingChildren();\r
+    }\r
+\r
+    /**\r
+     * Create a new ExMCIMovie, with blank fields\r
+     */\r
+    public ExMCIMovie() {\r
+        _header = new byte[8];\r
+        // Setup our header block\r
+        _header[0] = 0x0f; // We are a container record\r
+        LittleEndian.putShort(_header, 2, (short) getRecordType());\r
+\r
+        exVideo = new ExVideoContainer();\r
+        _children = new Record[]{exVideo};\r
+\r
+    }\r
+\r
+    /**\r
+     * Go through our child records, picking out the ones that are\r
+     * interesting, and saving those for use by the easy helper\r
+     * methods.\r
+     */\r
+    private void findInterestingChildren() {\r
+\r
+        // First child should be the ExVideoContainer\r
+        if (_children[0] instanceof ExVideoContainer) {\r
+            exVideo = (ExVideoContainer) _children[0];\r
+        } else {\r
+            logger.log(POILogger.ERROR, "First child record wasn't a ExVideoContainer, was of type " + _children[0].getRecordType());\r
+        }\r
+    }\r
+\r
+    /**\r
+     * We are of type 4103\r
+     */\r
+    public long getRecordType() {\r
+        return RecordTypes.ExMCIMovie.typeID;\r
+    }\r
+\r
+    /**\r
+     * Write the contents of the record back, so it can be written\r
+     * to disk\r
+     */\r
+    public void writeOut(OutputStream out) throws IOException {\r
+        writeOut(_header[0], _header[1], getRecordType(), _children, out);\r
+    }\r
+\r
+    /**\r
+     * Returns the ExVideoContainer that specifies information about the MCI movie\r
+     */\r
+    public ExVideoContainer getExVideo() {\r
+        return exVideo; }\r
+\r
+\r
+}\r
diff --git a/src/scratchpad/src/org/apache/poi/hslf/record/ExMediaAtom.java b/src/scratchpad/src/org/apache/poi/hslf/record/ExMediaAtom.java
new file mode 100755 (executable)
index 0000000..e80768f
--- /dev/null
@@ -0,0 +1,172 @@
+/* ====================================================================\r
+   Licensed to the Apache Software Foundation (ASF) under one or more\r
+   contributor license agreements.  See the NOTICE file distributed with\r
+   this work for additional information regarding copyright ownership.\r
+   The ASF licenses this file to You under the Apache License, Version 2.0\r
+   (the "License"); you may not use this file except in compliance with\r
+   the License.  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
+        \r
+\r
+package org.apache.poi.hslf.record;\r
+\r
+import java.io.IOException;\r
+import java.io.OutputStream;\r
+import java.util.Date;\r
+\r
+import org.apache.poi.hslf.util.SystemTimeUtils;\r
+import org.apache.poi.util.LittleEndian;\r
+\r
+/**\r
+ * An atom record that specifies information about external audio or video data.\r
+ *\r
+ * @author Yegor Kozlov\r
+ */\r
+public class ExMediaAtom extends RecordAtom\r
+{\r
+\r
+    /**\r
+     * A bit that specifies whether the audio or video data is repeated continuously during playback.\r
+     */\r
+    public static final int fLoop = 1;\r
+    /**\r
+     * A bit that specifies whether the audio or video data is rewound after playing.\r
+     */\r
+    public static final int fRewind = 2;\r
+    /**\r
+     * A bit that specifies whether the audio data is recorded narration for the slide show. It MUST be FALSE if this ExMediaAtom record is contained by an ExVideoContainer record.\r
+     */\r
+    public static final int fNarration = 4;\r
+\r
+    /**\r
+     * Record header.\r
+     */\r
+    private byte[] _header;\r
+\r
+    /**\r
+     * record data\r
+     */\r
+    private byte[] _recdata;\r
+\r
+    /**\r
+     * Constructs a brand new link related atom record.\r
+     */\r
+    protected ExMediaAtom() {\r
+        _recdata = new byte[8];\r
+\r
+        _header = new byte[8];\r
+        LittleEndian.putShort(_header, 2, (short)getRecordType());\r
+        LittleEndian.putInt(_header, 4, _recdata.length);\r
+    }\r
+\r
+    /**\r
+     * Constructs the link related atom record from its\r
+     *  source data.\r
+     *\r
+     * @param source the source data as a byte array.\r
+     * @param start the start offset into the byte array.\r
+     * @param len the length of the slice in the byte array.\r
+     */\r
+    protected ExMediaAtom(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
+        // Grab the record data\r
+        _recdata = new byte[len-8];\r
+        System.arraycopy(source,start+8,_recdata,0,len-8);\r
+    }\r
+\r
+    /**\r
+     * Gets the record type.\r
+     * @return the record type.\r
+     */\r
+    public long getRecordType() { return RecordTypes.ExMediaAtom.typeID; }\r
+\r
+    /**\r
+     * Write the contents of the record back, so it can be written\r
+     * to disk\r
+     *\r
+     * @param out the output stream to write to.\r
+     * @throws java.io.IOException if an error occurs.\r
+     */\r
+    public void writeOut(OutputStream out) throws IOException {\r
+        out.write(_header);\r
+        out.write(_recdata);\r
+    }\r
+\r
+    /**\r
+     * A 4-byte unsigned integer that specifies an ID for an external object.\r
+     *\r
+     * @return  A 4-byte unsigned integer that specifies an ID for an external object.\r
+     */\r
+    public int getObjectId(){\r
+        return LittleEndian.getInt(_recdata, 0);\r
+    }\r
+\r
+    /**\r
+     * A 4-byte unsigned integer that specifies an ID for an external object.\r
+     *\r
+     * @param id  A 4-byte unsigned integer that specifies an ID for an external object.\r
+     */\r
+    public void setObjectId(int id){\r
+         LittleEndian.putInt(_recdata, 0, id);\r
+    }\r
+\r
+    /**\r
+     *  A bit mask specifying options for displaying headers and footers\r
+     *\r
+     * @return A bit mask specifying options for displaying headers and footers\r
+     */\r
+    public int getMask(){\r
+        return LittleEndian.getInt(_recdata, 4);\r
+    }\r
+\r
+    /**\r
+     *  A bit mask specifying options for displaying video\r
+     *\r
+     * @param mask A bit mask specifying options for displaying video\r
+     */\r
+    public void setMask(int mask){\r
+        LittleEndian.putInt(_recdata, 4, mask);\r
+    }\r
+\r
+    /**\r
+     * @param bit the bit to check\r
+     * @return whether the specified flag is set\r
+     */\r
+    public boolean getFlag(int bit){\r
+        return (getMask() & bit) != 0;\r
+    }\r
+\r
+    /**\r
+     * @param  bit the bit to set\r
+     * @param  value whether the specified bit is set\r
+     */\r
+    public void setFlag(int bit, boolean value){\r
+        int mask = getMask();\r
+        if(value) mask |= bit;\r
+        else mask &= ~bit;\r
+        setMask(mask);\r
+    }\r
+\r
+    public String toString(){\r
+        StringBuffer buf = new StringBuffer();\r
+        buf.append("ExMediaAtom\n");\r
+        buf.append("\tObjectId: " + getObjectId() + "\n");\r
+        buf.append("\tMask    : " + getMask() + "\n");\r
+        buf.append("\t  fLoop        : " + getFlag(fLoop) + "\n");\r
+        buf.append("\t  fRewind   : " + getFlag(fRewind) + "\n");\r
+        buf.append("\t  fNarration    : " + getFlag(fNarration) + "\n");\r
+        return buf.toString();\r
+    }\r
+\r
+}\r
index ae96275cd538c8528247d9b735f59e0d8e44e15a..9f412d249e89370abb18d53a5778c904342b3685 100644 (file)
@@ -65,11 +65,35 @@ import org.apache.poi.util.LittleEndian;
  */
 public class ExOleObjAtom extends RecordAtom {
 
+    /**
+     * The object) is displayed as an embedded object inside of a container,
+     */
     public static final int DRAW_ASPECT_VISIBLE = 1;
+    /**
+     *   The object is displayed as a thumbnail image.
+     */
+    public static final int DRAW_ASPECT_THUMBNAIL = 2;
+    /**
+     *   The object is displayed as an icon.
+     */
     public static final int DRAW_ASPECT_ICON = 4;
+    /**
+     *   The object is displayed on the screen as though it were printed to a printer.
+     */
+    public static final int DRAW_ASPECT_DOCPRINT = 8;
 
+    /**
+     * An embedded OLE object; the object is serialized and saved within the file.
+     */
     public static final int TYPE_EMBEDDED = 0;
+    /**
+     * A linked OLE object; the object is saved outside of the file.
+     */
     public static final int TYPE_LINKED = 1;
+    /**
+     * The OLE object is an ActiveX control.
+     */
+    public static final int TYPE_CONTROL = 2;
 
     public static final int SUBTYPE_DEFAULT = 0;
     public static final int SUBTYPE_CLIPART_GALLERY = 1;
@@ -101,14 +125,13 @@ public class ExOleObjAtom extends RecordAtom {
     /**
      * Constructs a brand new link related atom record.
      */
-    protected ExOleObjAtom() {
+    public ExOleObjAtom() {
         _header = new byte[8];
-        _data = new byte[18];
+        _data = new byte[24];
 
+        LittleEndian.putShort(_header, 0, (short)1); //MUST be 0x1
         LittleEndian.putShort(_header, 2, (short)getRecordType());
         LittleEndian.putInt(_header, 4, _data.length);
-
-        // I hope it is fine for the other values to be zero.
     }
 
     /**
@@ -144,6 +167,16 @@ public class ExOleObjAtom extends RecordAtom {
         return LittleEndian.getInt(_data, 0);
     }
 
+    /**
+     * Sets whether the object can be completely seen, or if only the
+     * icon is visible.
+     *
+     * @param aspect the draw aspect, one of the {@code DRAW_ASPECT_*} constants.
+     */
+     public void setDrawAspect(int aspect) {
+        LittleEndian.putInt(_data, 0, aspect);
+    }
+
     /**
      * Gets whether the object is embedded or linked.
      *
@@ -153,6 +186,15 @@ public class ExOleObjAtom extends RecordAtom {
         return LittleEndian.getInt(_data, 4);
     }
 
+    /**
+     * Sets whether the object is embedded or linked.
+     *
+     * @param type the type, one of the {@code TYPE_EMBEDDED_*} constants.
+     */
+    public void setType(int type) {
+        LittleEndian.putInt(_data, 4, type);
+    }
+
     /**
      * Gets the unique identifier for the OLE object.
      *
@@ -162,6 +204,15 @@ public class ExOleObjAtom extends RecordAtom {
         return LittleEndian.getInt(_data, 8);
     }
 
+    /**
+     * Sets the unique identifier for the OLE object.
+     *
+     * @param id the object ID.
+     */
+    public void setObjID(int id) {
+        LittleEndian.putInt(_data, 8, id);
+    }
+
     /**
      * Gets the type of OLE object.
      * 
@@ -171,6 +222,15 @@ public class ExOleObjAtom extends RecordAtom {
         return LittleEndian.getInt(_data, 12);
     }
 
+    /**
+     * Sets the type of OLE object.
+     *
+     * @param type the sub-type, one of the {@code SUBTYPE_*} constants.
+     */
+    public void setSubType(int type) {
+        LittleEndian.putInt(_data, 12, type);
+    }
+
     /**
      * Gets the reference to the persistent object
      *
@@ -181,6 +241,16 @@ public class ExOleObjAtom extends RecordAtom {
         return LittleEndian.getInt(_data, 16);
     }
 
+    /**
+     * Sets the reference to the persistent object
+     *
+     * @param ref the reference to the persistent object, corresponds with an
+     *         {@code ExOleObjStg} storage container.
+     */
+    public void setObjStgDataRef(int ref) {
+        LittleEndian.putInt(_data, 16, ref);
+    }
+
     /**
      * Gets whether the object's image is blank.
      *
@@ -191,6 +261,24 @@ public class ExOleObjAtom extends RecordAtom {
         return LittleEndian.getInt(_data, 20) != 0;
     }
     
+    /**
+     * Gets misc options (the last four bytes in the atom).
+     *
+     * @return {@code true} if the object's image is blank.
+     */
+    public int getOptions() {
+        // Even though this is a mere boolean, KOffice's code says it's an int.
+        return LittleEndian.getInt(_data, 20);
+    }
+
+    /**
+     * Sets misc options (the last four bytes in the atom).
+     */
+    public void setOptions(int opts) {
+        // Even though this is a mere boolean, KOffice's code says it's an int.
+        LittleEndian.putInt(_data, 20, opts);
+    }
+
     /**
      * Returns the type (held as a little endian in bytes 3 and 4)
      * that this class handles.
@@ -210,4 +298,16 @@ public class ExOleObjAtom extends RecordAtom {
         out.write(_header);
         out.write(_data);
     }
+
+    public String toString(){
+        StringBuffer buf = new StringBuffer();
+        buf.append("ExOleObjAtom\n");
+        buf.append("  drawAspect: " + getDrawAspect() + "\n");
+        buf.append("  type: " + getType() + "\n");
+        buf.append("  objID: " + getObjID() + "\n");
+        buf.append("  subType: " + getSubType() + "\n");
+        buf.append("  objStgDataRef: " + getObjStgDataRef() + "\n");
+        buf.append("  options: " + getOptions() + "\n");
+        return buf.toString();
+    }
 }
index c7408b0fe4e5ffbe903c27659d9abedd37341c7d..36fbebf4c48b320303e012b46d5af20f40ab9b37 100644 (file)
@@ -20,6 +20,7 @@ package org.apache.poi.hslf.record;
 import java.io.*;
 import java.util.zip.InflaterInputStream;
 import java.util.zip.DeflaterOutputStream;
+import java.util.Hashtable;
 
 import org.apache.poi.util.LittleEndian;
 
@@ -28,7 +29,7 @@ import org.apache.poi.util.LittleEndian;
  *
  * @author Daniel Noll
  */
-public class ExOleObjStg extends RecordAtom implements PersistRecord {
+public class ExOleObjStg extends RecordAtom implements PositionDependentRecord, PersistRecord {
 
     private int _persistId; // Found from PersistPtrHolder
 
@@ -45,10 +46,11 @@ public class ExOleObjStg extends RecordAtom implements PersistRecord {
     /**
      * Constructs a new empty storage container.
      */
-    protected ExOleObjStg() {
+    public ExOleObjStg() {
         _header = new byte[8];
         _data = new byte[0];
 
+        LittleEndian.putShort(_header, 0, (short)0x10);
         LittleEndian.putShort(_header, 2, (short)getRecordType());
         LittleEndian.putInt(_header, 4, _data.length);
     }
@@ -90,6 +92,10 @@ public class ExOleObjStg extends RecordAtom implements PersistRecord {
         return new InflaterInputStream(compressedStream);
     }
 
+    public byte[] getRawData() {
+        return _data;
+    }
+
     /**
      * Sets the embedded data.
      *
@@ -144,4 +150,23 @@ public class ExOleObjStg extends RecordAtom implements PersistRecord {
     public void setPersistId(int id) {
         _persistId = id;
     }
+
+    /** Our location on the disk, as of the last write out */
+    protected int myLastOnDiskOffset;
+
+    /** Fetch our location on the disk, as of the last write out */
+    public int getLastOnDiskOffset() { return myLastOnDiskOffset; }
+
+    /**
+     * Update the Record's idea of where on disk it lives, after a write out.
+     * Use with care...
+     */
+    public void setLastOnDiskOffset(int offset) {
+        myLastOnDiskOffset = offset;
+    }
+
+    public void updateOtherRecordReferences(Hashtable oldToNewReferencesLookup) {
+        return;
+    }
+
 }
diff --git a/src/scratchpad/src/org/apache/poi/hslf/record/ExVideoContainer.java b/src/scratchpad/src/org/apache/poi/hslf/record/ExVideoContainer.java
new file mode 100755 (executable)
index 0000000..36f9fda
--- /dev/null
@@ -0,0 +1,108 @@
+/*\r
+* Licensed to the Apache Software Foundation (ASF) under one or more\r
+* contributor license agreements.  See the NOTICE file distributed with\r
+* this work for additional information regarding copyright ownership.\r
+* The ASF licenses this file to You under the Apache License, Version 2.0\r
+* (the "License"); you may not use this file except in compliance with\r
+* the License.  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.record;\r
+\r
+import java.io.IOException;\r
+import java.io.OutputStream;\r
+\r
+import org.apache.poi.util.LittleEndian;\r
+import org.apache.poi.util.POILogger;\r
+\r
+/**\r
+ * A container record that specifies information about external video data.\r
+ *\r
+ * @author Yegor Kozlov\r
+ */\r
+public class ExVideoContainer extends RecordContainer {\r
+       private byte[] _header;\r
+\r
+       // Links to our more interesting children\r
+       private ExMediaAtom mediaAtom;\r
+    //the UNC or local path to a video file.\r
+    private CString pathAtom;\r
+\r
+       /**\r
+        * Set things up, and find our more interesting children\r
+        */\r
+       protected ExVideoContainer(byte[] source, int start, int len) {\r
+               // Grab the header\r
+               _header = new byte[8];\r
+               System.arraycopy(source,start,_header,0,8);\r
+\r
+               // Find our children\r
+               _children = Record.findChildRecords(source,start+8,len-8);\r
+               findInterestingChildren();\r
+       }\r
+\r
+       /**\r
+        * Go through our child records, picking out the ones that are\r
+        *  interesting, and saving those for use by the easy helper\r
+        *  methods.\r
+        */     \r
+       private void findInterestingChildren() {\r
+\r
+               // First child should be the ExMediaAtom\r
+               if(_children[0] instanceof ExMediaAtom) {\r
+                       mediaAtom = (ExMediaAtom)_children[0];\r
+               } else {\r
+                       logger.log(POILogger.ERROR, "First child record wasn't a ExMediaAtom, was of type " + _children[0].getRecordType());\r
+               }\r
+        if(_children[1] instanceof CString) {\r
+            pathAtom = (CString)_children[1];\r
+        } else {\r
+            logger.log(POILogger.ERROR, "Second child record wasn't a CString, was of type " + _children[1].getRecordType());\r
+        }\r
+       }\r
+\r
+       /**\r
+        * Create a new ExVideoContainer, with blank fields\r
+        */\r
+       public ExVideoContainer() {\r
+        // Setup our header block\r
+               _header = new byte[8];\r
+               _header[0] = 0x0f; // We are a container record\r
+               LittleEndian.putShort(_header, 2, (short)getRecordType());\r
+               \r
+        _children = new Record[2];\r
+               _children[0] = mediaAtom = new ExMediaAtom();\r
+               _children[1] = pathAtom = new CString();\r
+       }\r
+\r
+       /**\r
+        * We are of type 4103\r
+        */\r
+       public long getRecordType() { return RecordTypes.ExVideoContainer.typeID; }\r
+\r
+       /**\r
+        * Write the contents of the record back, so it can be written\r
+        *  to disk\r
+        */\r
+       public void writeOut(OutputStream out) throws IOException {\r
+               writeOut(_header[0],_header[1],getRecordType(),_children,out);\r
+       }\r
+\r
+    /**\r
+     * Returns the ExMediaAtom of this link\r
+     */\r
+    public ExMediaAtom getExMediaAtom() { return mediaAtom; }\r
+\r
+    /**\r
+     * Returns the Path Atom (CString) of this link\r
+     */\r
+    public CString getPathAtom() { return pathAtom; }\r
+\r
+}\r
index 8943f882d8b448c3eec0b04e84d091ed67e612fe..0ee8cd883f5f607b937912f0ecc58a1cfe45a9b4 100644 (file)
@@ -39,27 +39,39 @@ public class InteractiveInfoAtom extends RecordAtom
     /**
      * Action Table
      */
-    public static final int ACTION_NONE = 0;
-    public static final int ACTION_MACRO = 1;
-    public static final int ACTION_RUNPROGRAM = 2;
-    public static final int ACTION_JUMP = 3;
-    public static final int ACTION_HYPERLINK = 4;
-    public static final int ACTION_OLE = 5;
-    public static final int ACTION_MEDIA = 6;
-    public static final int ACTION_CUSTOMSHOW = 7;
+    public static final byte ACTION_NONE = 0;
+    public static final byte ACTION_MACRO = 1;
+    public static final byte ACTION_RUNPROGRAM = 2;
+    public static final byte ACTION_JUMP = 3;
+    public static final byte ACTION_HYPERLINK = 4;
+    public static final byte ACTION_OLE = 5;
+    public static final byte ACTION_MEDIA = 6;
+    public static final byte ACTION_CUSTOMSHOW = 7;
 
     /**
      *  Jump Table
      */
-    public static final int JUMP_NONE = 0;
-    public static final int JUMP_NEXTSLIDE = 1;
-    public static final int JUMP_PREVIOUSSLIDE = 2;
-    public static final int JUMP_FIRSTSLIDE = 3;
-    public static final int JUMP_LASTSLIDE = 4;
-    public static final int JUMP_LASTSLIDEVIEWED = 5;
-    public static final int JUMP_ENDSHOW = 6;
-
+    public static final byte JUMP_NONE = 0;
+    public static final byte JUMP_NEXTSLIDE = 1;
+    public static final byte JUMP_PREVIOUSSLIDE = 2;
+    public static final byte JUMP_FIRSTSLIDE = 3;
+    public static final byte JUMP_LASTSLIDE = 4;
+    public static final byte JUMP_LASTSLIDEVIEWED = 5;
+    public static final byte JUMP_ENDSHOW = 6;
 
+    /**
+     * Types of hyperlinks
+     */
+    public static final byte LINK_NextSlide = 0x00;
+    public static final byte LINK_PreviousSlide = 0x01;
+    public static final byte LINK_FirstSlide = 0x02;
+    public static final byte LINK_LastSlide = 0x03;
+    public static final byte LINK_CustomShow = 0x06;
+    public static final byte LINK_SlideNumber = 0x07;
+    public static final byte LINK_Url = 0x08;
+    public static final byte LINK_OtherPresentation = 0x09;
+    public static final byte LINK_OtherFile = 0x0A;
+    public static final byte LINK_NULL = (byte)0xFF;
 
     /**
      * Record header.
diff --git a/src/scratchpad/src/org/apache/poi/hslf/record/OEShapeAtom.java b/src/scratchpad/src/org/apache/poi/hslf/record/OEShapeAtom.java
new file mode 100755 (executable)
index 0000000..4a319c2
--- /dev/null
@@ -0,0 +1,110 @@
+/* ====================================================================\r
+   Licensed to the Apache Software Foundation (ASF) under one or more\r
+   contributor license agreements.  See the NOTICE file distributed with\r
+   this work for additional information regarding copyright ownership.\r
+   The ASF licenses this file to You under the Apache License, Version 2.0\r
+   (the "License"); you may not use this file except in compliance with\r
+   the License.  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
+        \r
+\r
+package org.apache.poi.hslf.record;\r
+\r
+import java.io.IOException;\r
+import java.io.OutputStream;\r
+import java.util.Date;\r
+\r
+import org.apache.poi.hslf.util.SystemTimeUtils;\r
+import org.apache.poi.util.LittleEndian;\r
+\r
+/**\r
+ * Atom that contains information that describes shape client data.\r
+ *\r
+ * @author Yegor Kozlov\r
+ */\r
+public class OEShapeAtom extends RecordAtom\r
+{\r
+\r
+    /**\r
+     * Record header.\r
+     */\r
+    private byte[] _header;\r
+\r
+    /**\r
+     * record data\r
+     */\r
+    private byte[] _recdata;\r
+\r
+    /**\r
+     * Constructs a brand new link related atom record.\r
+     */\r
+    public OEShapeAtom() {\r
+        _recdata = new byte[4];\r
+\r
+        _header = new byte[8];\r
+        LittleEndian.putShort(_header, 2, (short)getRecordType());\r
+        LittleEndian.putInt(_header, 4, _recdata.length);\r
+    }\r
+\r
+    /**\r
+     * Constructs the link related atom record from its\r
+     *  source data.\r
+     *\r
+     * @param source the source data as a byte array.\r
+     * @param start the start offset into the byte array.\r
+     * @param len the length of the slice in the byte array.\r
+     */\r
+    protected OEShapeAtom(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
+        // Grab the record data\r
+        _recdata = new byte[len-8];\r
+        System.arraycopy(source,start+8,_recdata,0,len-8);\r
+    }\r
+\r
+    /**\r
+     * Gets the record type.\r
+     * @return the record type.\r
+     */\r
+    public long getRecordType() { return RecordTypes.OEShapeAtom.typeID; }\r
+\r
+    /**\r
+     * Write the contents of the record back, so it can be written\r
+     * to disk\r
+     *\r
+     * @param out the output stream to write to.\r
+     * @throws java.io.IOException if an error occurs.\r
+     */\r
+    public void writeOut(OutputStream out) throws IOException {\r
+        out.write(_header);\r
+        out.write(_recdata);\r
+    }\r
+\r
+    /**\r
+     * shape flags.\r
+     *\r
+     * @return  shape flags.\r
+     */\r
+    public int getOptions(){\r
+        return LittleEndian.getInt(_recdata, 0);\r
+    }\r
+\r
+    /**\r
+     * shape flags.\r
+     *\r
+     * @param id  shape flags.\r
+     */\r
+    public void setOptions(int id){\r
+         LittleEndian.putInt(_recdata, 0, id);\r
+    }\r
+}\r
index 9c867d5ab0fb07bf24297dd25d4c64c029bf5e0c..758d21bdac661127e6faa9ca54e681bc53951cc0 100644 (file)
@@ -77,7 +77,7 @@ public class RecordTypes {
     public static final Type BookmarkSeedAtom = new Type(2025,null);
     public static final Type ColorSchemeAtom = new Type(2032,ColorSchemeAtom.class);
     public static final Type ExObjRefAtom = new Type(3009,null);
-    public static final Type OEShapeAtom = new Type(3009,null);
+    public static final Type OEShapeAtom = new Type(3009,OEShapeAtom.class);
     public static final Type OEPlaceholderAtom = new Type(3011,OEPlaceholderAtom.class);
     public static final Type GPopublicintAtom = new Type(3024,null);
     public static final Type GRatioAtom = new Type(3031,null);
@@ -128,11 +128,11 @@ public class RecordTypes {
     public static final Type DateTimeMCAtom = new Type(4087,null);
     public static final Type GenericDateMCAtom = new Type(4088,null);
     public static final Type FooterMCAtom = new Type(4090,null);
-    public static final Type ExControlAtom = new Type(4091,null);
-    public static final Type ExMediaAtom = new Type(4100,null);
-    public static final Type ExVideo = new Type(4101,null);
-    public static final Type ExAviMovie = new Type(4102,null);
-    public static final Type ExMCIMovie = new Type(4103,null);
+    public static final Type ExControlAtom = new Type(4091,ExControlAtom.class);
+    public static final Type ExMediaAtom = new Type(4100,ExMediaAtom.class);
+    public static final Type ExVideoContainer = new Type(4101,ExVideoContainer.class);
+    public static final Type ExAviMovie = new Type(4102,ExAviMovie.class);
+    public static final Type ExMCIMovie = new Type(4103,ExMCIMovie.class);
     public static final Type ExMIDIAudio = new Type(4109,null);
     public static final Type ExCDAudio = new Type(4110,null);
     public static final Type ExWAVAudioEmbedded = new Type(4111,null);
@@ -140,7 +140,8 @@ public class RecordTypes {
     public static final Type ExOleObjStg = new Type(4113,ExOleObjStg.class);
     public static final Type ExCDAudioAtom = new Type(4114,null);
     public static final Type ExWAVAudioEmbeddedAtom = new Type(4115,null);
-    public static final Type AnimationInfoAtom = new Type(4116,null);
+    public static final Type AnimationInfo = new Type(4116,AnimationInfo.class);
+    public static final Type AnimationInfoAtom = new Type(4081,AnimationInfoAtom.class);
     public static final Type RTFDateTimeMCAtom = new Type(4117,null);
     public static final Type ProgTags = new Type(5000,DummyPositionSensitiveRecordWithChildren.class);
     public static final Type ProgStringTag = new Type(5001,null);
index 969e9036afffda7aa03f11be0c8b77617e9b9bd8..7b2304074f02dab8a323dc98ff8f6289876432ec 100644 (file)
@@ -367,8 +367,14 @@ public class StyleTextPropAtom extends RecordAtom
 
                rawContents     = baos.toByteArray();
        }
-       
-       /**
+
+    public void setRawContents(byte[] bytes) {
+        rawContents = bytes;
+        reserved = new byte[0];
+        initialised = false;
+    }
+
+    /**
         * Create a new Paragraph TextPropCollection, and add it to the list
         * @param charactersCovered The number of characters this TextPropCollection will cover
         * @return the new TextPropCollection, which will then be in the list
index 71ded85406139db760a6a49b0a310e324bb33b74..0be988d51f5e395c55fe8c684525e7f2e93d05a8 100755 (executable)
@@ -53,7 +53,7 @@ public class TextRulerAtom extends RecordAtom {
     /**\r
      * Constructs a new empty ruler atom.\r
      */\r
-    protected TextRulerAtom() {\r
+    public TextRulerAtom() {\r
         _header = new byte[8];\r
         _data = new byte[0];\r
 \r
@@ -191,4 +191,19 @@ public class TextRulerAtom extends RecordAtom {
     public int[] getBulletOffsets(){\r
         return bulletOffsets;\r
     }\r
+\r
+    public static TextRulerAtom getParagraphInstance(){\r
+        byte[] data = new byte[] {\r
+            0x00, 0x00, (byte)0xA6, 0x0F, 0x0A, 0x00, 0x00, 0x00,\r
+            0x10, 0x03, 0x00, 0x00, (byte)0xF9, 0x00, 0x41, 0x01, 0x41, 0x01\r
+        };\r
+        TextRulerAtom ruler = new TextRulerAtom(data, 0, data.length);\r
+        return ruler;\r
+    }\r
+\r
+    public void setParagraphIndent(short tetxOffset, short bulletOffset){\r
+        LittleEndian.putShort(_data, 4, tetxOffset);\r
+        LittleEndian.putShort(_data, 6, bulletOffset);\r
+        LittleEndian.putShort(_data, 8, bulletOffset);\r
+    }\r
 }\r
index 6bc203b658f97a512c9726554544320386cef698..a4238b473806b1c4dc6ca6f6d9f33664c6073f22 100644 (file)
@@ -687,6 +687,7 @@ public class RichTextRun {
      */
     public void setBulletFont(int idx) {
         setParaTextPropVal("bullet.font", idx);
+        setFlag(false, ParagraphFlagsTextProp.BULLET_HARDFONT_IDX, true);
     }
 
     /**
index a546d0444c1d26adb3027647a1dc4ca13a6099c6..f480a30aa5632a053abb9e8e7a2fb0ce2e6a2917 100644 (file)
@@ -24,11 +24,7 @@ import java.io.FileInputStream;
 import java.io.IOException;
 import java.io.InputStream;
 import java.io.OutputStream;
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.Enumeration;
-import java.util.Hashtable;
-import java.util.List;
+import java.util.*;
 
 import org.apache.poi.ddf.EscherBSERecord;
 import org.apache.poi.ddf.EscherContainerRecord;
@@ -38,21 +34,9 @@ import org.apache.poi.hslf.HSLFSlideShow;
 import org.apache.poi.hslf.exceptions.CorruptPowerPointFileException;
 import org.apache.poi.hslf.exceptions.HSLFException;
 import org.apache.poi.hslf.model.*;
-import org.apache.poi.hslf.record.Document;
-import org.apache.poi.hslf.record.DocumentAtom;
-import org.apache.poi.hslf.record.FontCollection;
-import org.apache.poi.hslf.record.FontEntityAtom;
-import org.apache.poi.hslf.record.HeadersFootersContainer;
-import org.apache.poi.hslf.record.ParentAwareRecord;
-import org.apache.poi.hslf.record.PersistPtrHolder;
-import org.apache.poi.hslf.record.PositionDependentRecord;
-import org.apache.poi.hslf.record.PositionDependentRecordContainer;
-import org.apache.poi.hslf.record.Record;
-import org.apache.poi.hslf.record.RecordContainer;
-import org.apache.poi.hslf.record.RecordTypes;
-import org.apache.poi.hslf.record.SlideListWithText;
-import org.apache.poi.hslf.record.SlidePersistAtom;
-import org.apache.poi.hslf.record.UserEditAtom;
+import org.apache.poi.hslf.model.Notes;
+import org.apache.poi.hslf.model.Slide;
+import org.apache.poi.hslf.record.*;
 import org.apache.poi.hslf.record.SlideListWithText.SlideAtomsSet;
 import org.apache.poi.util.ArrayUtil;
 import org.apache.poi.util.POILogFactory;
@@ -590,9 +574,8 @@ public final class SlideShow {
         * Create a blank <code>Slide</code>.
         *
         * @return  the created <code>Slide</code>
-        * @throws IOException
         */
-       public Slide createSlide() throws IOException {
+       public Slide createSlide() {
                SlideListWithText slist = null;
 
                // We need to add the records to the SLWT that deals
@@ -660,9 +643,13 @@ public final class SlideShow {
                for (int i = 0; i < _records.length; i++) {
                        Record record = _records[i];
                        ByteArrayOutputStream out = new ByteArrayOutputStream();
-                       record.writeOut(out);
+                       try {
+                record.writeOut(out);
+            } catch (IOException e){
+                throw new HSLFException(e);
+            }
 
-                       // Grab interesting records as they come past
+              // Grab interesting records as they come past
                        if(_records[i].getRecordType() == RecordTypes.PersistPtrIncrementalBlock.typeID){
                                ptr = (PersistPtrHolder)_records[i];
                        }
@@ -888,4 +875,72 @@ public final class SlideShow {
         }
     }
 
+    /**
+     * Add a movie in this presentation
+     *
+     * @param path the path or url to the movie
+     * @return 0-based index of the movie
+     */
+    public int addMovie(String path, int type) {
+        ExObjList lst = (ExObjList)_documentRecord.findFirstOfType(RecordTypes.ExObjList.typeID);
+        if(lst == null){
+            lst = new ExObjList();
+            _documentRecord.addChildAfter(lst, _documentRecord.getDocumentAtom());
+        }
+
+        ExObjListAtom objAtom = lst.getExObjListAtom();
+        //increment the object ID seed
+        int objectId = (int)objAtom.getObjectIDSeed() + 1;
+        objAtom.setObjectIDSeed(objectId);
+        ExMCIMovie mci;
+        switch (type){
+            case MovieShape.MOVIE_MPEG:
+                mci = new ExMCIMovie();
+                break;
+            case MovieShape.MOVIE_AVI:
+                mci = new ExAviMovie();
+                break;
+            default:
+                throw new IllegalArgumentException("Unsupported Movie: " + type);
+        }
+
+        lst.appendChildRecord(mci);
+        ExVideoContainer exVideo = mci.getExVideo();
+        exVideo.getExMediaAtom().setObjectId(objectId);
+        exVideo.getExMediaAtom().setMask(0xE80000);
+        exVideo.getPathAtom().setText(path);
+        return objectId;
+    }
+
+    /**
+     * Add a control in this presentation
+     *
+     * @param name   name of the control, e.g. "Shockwave Flash Object"
+     * @param progId OLE Programmatic Identifier, e.g. "ShockwaveFlash.ShockwaveFlash.9"
+     * @return 0-based index of the control
+     */
+    public int addControl(String name, String progId) {
+        ExObjList lst = _documentRecord.getExObjList();
+        if (lst == null) {
+            lst = new ExObjList();
+            _documentRecord.addChildAfter(lst, _documentRecord.getDocumentAtom());
+        }
+        ExObjListAtom objAtom = lst.getExObjListAtom();
+        //increment the object ID seed
+        int objectId = (int) objAtom.getObjectIDSeed() + 1;
+        objAtom.setObjectIDSeed(objectId);
+        ExControl ctrl = new ExControl();
+        ExOleObjAtom oleObj = ctrl.getExOleObjAtom();
+        oleObj.setObjID(objectId);
+        oleObj.setDrawAspect(ExOleObjAtom.DRAW_ASPECT_VISIBLE);
+        oleObj.setType(ExOleObjAtom.TYPE_CONTROL);
+        oleObj.setSubType(ExOleObjAtom.SUBTYPE_DEFAULT);
+
+        ctrl.setProgId(progId);
+        ctrl.setMenuName(name);
+        ctrl.setClipboardName(name);
+        lst.addChildAfter(ctrl, objAtom);
+
+        return objectId;
+    }
 }
diff --git a/src/scratchpad/testcases/org/apache/poi/hslf/model/TestMovieShape.java b/src/scratchpad/testcases/org/apache/poi/hslf/model/TestMovieShape.java
new file mode 100755 (executable)
index 0000000..9e0bfaf
--- /dev/null
@@ -0,0 +1,66 @@
+/* ====================================================================\r
+   Licensed to the Apache Software Foundation (ASF) under one or more\r
+   contributor license agreements.  See the NOTICE file distributed with\r
+   this work for additional information regarding copyright ownership.\r
+   The ASF licenses this file to You under the Apache License, Version 2.0\r
+   (the "License"); you may not use this file except in compliance with\r
+   the License.  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.model;\r
+\r
+import junit.framework.TestCase;\r
+\r
+import java.io.*;\r
+import java.awt.*;\r
+import java.awt.geom.Rectangle2D;\r
+\r
+import org.apache.poi.hslf.usermodel.SlideShow;\r
+import org.apache.poi.hslf.HSLFSlideShow;\r
+\r
+/**\r
+ * Test <code>MovieShape</code> object.\r
+ * \r
+ * @author Yegor Kozlov\r
+ */\r
+public class TestMovieShape extends TestCase {\r
+\r
+    protected String cwd = System.getProperty("HSLF.testdata.path");\r
+\r
+    public void testCreate() throws Exception {\r
+        SlideShow ppt = new SlideShow();\r
+\r
+        Slide slide = ppt.createSlide();\r
+\r
+        String path = cwd + "/test-movie.mpg";\r
+        int movieIdx = ppt.addMovie(path, MovieShape.MOVIE_MPEG);\r
+        int thumbnailIdx = ppt.addPicture(new File(cwd, "tomcat.png"), Picture.PNG);\r
+\r
+        MovieShape shape = new MovieShape(movieIdx, thumbnailIdx);\r
+        shape.setAnchor(new Rectangle2D.Float(300,225,120,90));\r
+        slide.addShape(shape);\r
+\r
+        assertEquals(path, shape.getPath());\r
+        assertTrue(shape.isAutoPlay());\r
+        shape.setAutoPlay(false);\r
+        assertFalse(shape.isAutoPlay());\r
+\r
+        ByteArrayOutputStream out = new ByteArrayOutputStream();\r
+        ppt.write(out);\r
+\r
+        ppt = new SlideShow(new ByteArrayInputStream(out.toByteArray()));\r
+        slide = ppt.getSlides()[0];\r
+        shape = (MovieShape)slide.getShapes()[0];\r
+        assertEquals(path, shape.getPath());\r
+        assertFalse(shape.isAutoPlay());\r
+\r
+    }\r
+\r
+}\r
diff --git a/src/scratchpad/testcases/org/apache/poi/hslf/model/TestTable.java b/src/scratchpad/testcases/org/apache/poi/hslf/model/TestTable.java
new file mode 100755 (executable)
index 0000000..fff6482
--- /dev/null
@@ -0,0 +1,63 @@
+/* ====================================================================\r
+   Licensed to the Apache Software Foundation (ASF) under one or more\r
+   contributor license agreements.  See the NOTICE file distributed with\r
+   this work for additional information regarding copyright ownership.\r
+   The ASF licenses this file to You under the Apache License, Version 2.0\r
+   (the "License"); you may not use this file except in compliance with\r
+   the License.  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.model;\r
+\r
+import junit.framework.TestCase;\r
+\r
+import java.io.*;\r
+import java.awt.*;\r
+import java.awt.geom.Rectangle2D;\r
+\r
+import org.apache.poi.hslf.usermodel.SlideShow;\r
+import org.apache.poi.hslf.HSLFSlideShow;\r
+\r
+/**\r
+ * Test <code>Table</code> object.\r
+ * \r
+ * @author Yegor Kozlov\r
+ */\r
+public class TestTable extends TestCase {\r
+\r
+    /**\r
+     * Test that ShapeFactory works properly and returns <code>Table</code>\r
+     */\r
+    public void testShapeFactory() throws Exception {\r
+        SlideShow ppt = new SlideShow();\r
+\r
+        Slide slide = ppt.createSlide();\r
+\r
+        Table tbl = new Table(2, 5);\r
+        slide.addShape(tbl);\r
+\r
+        assertTrue(slide.getShapes()[0] instanceof Table);\r
+        Table tbl2 = (Table)slide.getShapes()[0];\r
+        assertEquals(tbl.getNumberOfColumns(), tbl2.getNumberOfColumns());\r
+        assertEquals(tbl.getNumberOfRows(), tbl2.getNumberOfRows());\r
+\r
+        ByteArrayOutputStream out = new ByteArrayOutputStream();\r
+        ppt.write(out);\r
+        out.close();\r
+\r
+        ppt = new SlideShow(new ByteArrayInputStream(out.toByteArray()));\r
+        slide = ppt.getSlides()[0];\r
+        assertTrue(slide.getShapes()[0] instanceof Table);\r
+        Table tbl3 = (Table)slide.getShapes()[0];\r
+        assertEquals(tbl.getNumberOfColumns(), tbl3.getNumberOfColumns());\r
+        assertEquals(tbl.getNumberOfRows(), tbl3.getNumberOfRows());\r
+    }\r
+\r
+}\r
diff --git a/src/scratchpad/testcases/org/apache/poi/hslf/record/TestAnimationInfoAtom.java b/src/scratchpad/testcases/org/apache/poi/hslf/record/TestAnimationInfoAtom.java
new file mode 100755 (executable)
index 0000000..3b8cf75
--- /dev/null
@@ -0,0 +1,91 @@
+\r
+/* ====================================================================\r
+   Licensed to the Apache Software Foundation (ASF) under one or more\r
+   contributor license agreements.  See the NOTICE file distributed with\r
+   this work for additional information regarding copyright ownership.\r
+   The ASF licenses this file to You under the Apache License, Version 2.0\r
+   (the "License"); you may not use this file except in compliance with\r
+   the License.  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
+        \r
+\r
+\r
+package org.apache.poi.hslf.record;\r
+\r
+\r
+import junit.framework.TestCase;\r
+import java.io.ByteArrayOutputStream;\r
+import java.util.Arrays;\r
+\r
+import org.apache.poi.util.HexDump;\r
+\r
+/**\r
+ * Tests that {@link HeadersFootersAtom} works properly\r
+ *\r
+ * @author Yegor Kozlov\r
+ */\r
+public class TestAnimationInfoAtom extends TestCase {\r
+    // From a real file\r
+    /*\r
+     <AnimationInfoAtom info="1" type="4081" size="28" offset="4015" header="01 00 F1 0F 1C 00 00 00 ">\r
+       00 00 00 07 04 05 00 00 00 00 00 00 00 00 00 00 02 00 00 00 00 00 00 00 00\r
+       00 00 00\r
+     </AnimationInfoAtom>\r
+     */\r
+    private byte[] data = new byte[] {\r
+         0x01, 0x00, (byte)0xF1, 0x0F, 0x1C, 0x00, 0x00, 0x00,\r
+            0x00, 0x00, 0x00, 0x07, 0x04, 0x05, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\r
+            0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00\r
+    };\r
+\r
+    public void testRead() throws Exception {\r
+        AnimationInfoAtom record = new AnimationInfoAtom(data, 0, data.length);\r
+        assertEquals(RecordTypes.AnimationInfoAtom.typeID, record.getRecordType());\r
+        assertTrue(record.getFlag(AnimationInfoAtom.Automatic));\r
+        assertTrue(record.getFlag(AnimationInfoAtom.Play));\r
+        assertTrue(record.getFlag(AnimationInfoAtom.Synchronous));\r
+        assertFalse(record.getFlag(AnimationInfoAtom.Reverse));\r
+        assertFalse(record.getFlag(AnimationInfoAtom.Sound));\r
+        assertFalse(record.getFlag(AnimationInfoAtom.StopSound));\r
+        assertFalse(record.getFlag(AnimationInfoAtom.Hide));\r
+        assertFalse(record.getFlag(AnimationInfoAtom.AnimateBg));\r
+        assertEquals(0x07000000, record.getDimColor());\r
+        assertEquals(0, record.getSoundIdRef());\r
+        assertEquals(0, record.getDelayTime());\r
+        assertEquals(2, record.getOrderID());\r
+        assertEquals(0, record.getSlideCount());\r
+    }\r
+\r
+    public void testWrite() throws Exception {\r
+        AnimationInfoAtom record = new AnimationInfoAtom(data, 0, data.length);\r
+        ByteArrayOutputStream baos = new ByteArrayOutputStream();\r
+        record.writeOut(baos);\r
+        byte[] b = baos.toByteArray();\r
+\r
+        assertTrue(Arrays.equals(data, b));\r
+    }\r
+\r
+    public void testNewRecord() throws Exception {\r
+        AnimationInfoAtom record = new AnimationInfoAtom();\r
+        record.setDimColor(0x07000000);\r
+        record.setOrderID(2);\r
+        record.setFlag(AnimationInfoAtom.Automatic, true);\r
+        record.setFlag(AnimationInfoAtom.Play, true);\r
+        record.setFlag(AnimationInfoAtom.Synchronous, true);\r
+\r
+        ByteArrayOutputStream baos = new ByteArrayOutputStream();\r
+        record.writeOut(baos);\r
+        byte[] b = baos.toByteArray();\r
+\r
+        assertTrue(Arrays.equals(data, b));\r
+    }\r
+\r
+}
\ No newline at end of file
diff --git a/src/scratchpad/testcases/org/apache/poi/hslf/record/TestExControl.java b/src/scratchpad/testcases/org/apache/poi/hslf/record/TestExControl.java
new file mode 100755 (executable)
index 0000000..82255d9
--- /dev/null
@@ -0,0 +1,127 @@
+\r
+/* ====================================================================\r
+   Licensed to the Apache Software Foundation (ASF) under one or more\r
+   contributor license agreements.  See the NOTICE file distributed with\r
+   this work for additional information regarding copyright ownership.\r
+   The ASF licenses this file to You under the Apache License, Version 2.0\r
+   (the "License"); you may not use this file except in compliance with\r
+   the License.  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
+        \r
+\r
+\r
+package org.apache.poi.hslf.record;\r
+\r
+\r
+import junit.framework.TestCase;\r
+import java.io.ByteArrayOutputStream;\r
+import java.util.Arrays;\r
+\r
+/**\r
+ * Tests that {@link org.apache.poi.hslf.record.ExControl} works properly\r
+ *\r
+ * @author Yegor Kozlov\r
+ */\r
+public class TestExControl extends TestCase {\r
+\r
+       // From a real file (embedded SWF control)\r
+    /*\r
+     <ExControl info="15" type="4078" size="218" offset="76" header="0F 00 EE 0F DA 00 00 00 ">\r
+       <ExControlAtom info="0" type="4091" size="4" offset="84" header="00 00 FB 0F 04 00 00 00 ">\r
+         00 01 00 00\r
+       </ExControlAtom>\r
+       <ExOleObjAtom info="1" type="4035" size="24" offset="96" header="01 00 C3 0F 18 00 00 00 ">\r
+         01 00 00 00 02 00 00 00 01 00 00 00 00 00 00 00 02 00 00 00 00 96 13 00\r
+       </ExOleObjAtom>\r
+       <CString info="16" type="4026" size="44" offset="128" header="10 00 BA 0F 2C 00 00 00 ">\r
+         53 00 68 00 6F 00 63 00 6B 00 77 00 61 00 76 00 65 00 20 00 46 00 6C 00 61\r
+         00 73 00 68 00 20 00 4F 00 62 00 6A 00 65 00 63 00 74 00\r
+       </CString>\r
+       <CString info="32" type="4026" size="62" offset="180" header="20 00 BA 0F 3E 00 00 00 ">\r
+         53 00 68 00 6F 00 63 00 6B 00 77 00 61 00 76 00 65 00 46 00 6C 00 61 00 73\r
+         00 68 00 2E 00 53 00 68 00 6F 00 63 00 6B 00 77 00 61 00 76 00 65 00 46 00\r
+         6C 00 61 00 73 00 68 00 2E 00 39 00\r
+       </CString>\r
+       <CString info="48" type="4026" size="44" offset="250" header="30 00 BA 0F 2C 00 00 00 ">\r
+         53 00 68 00 6F 00 63 00 6B 00 77 00 61 00 76 00 65 00 20 00 46 00 6C 00 61\r
+         00 73 00 68 00 20 00 4F 00 62 00 6A 00 65 00 63 00 74 00\r
+       </CString>\r
+     </ExControl>\r
+     */\r
+    private byte[] data = new byte[] {\r
+            0x0F, 0x00, (byte)0xEE, 0x0F, (byte)0xDA, 0x00, 0x00, 0x00, 0x00, 0x00, (byte)0xFB, 0x0F, 0x04, 0x00, 0x00, 0x00,\r
+            0x00, 0x01, 0x00, 0x00, 0x01, 0x00, (byte)0xC3, 0x0F, 0x18, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x02, 0x00,\r
+            0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, (byte)0x96, 0x13, 0x00,\r
+            0x10, 0x00, (byte)0xBA, 0x0F, 0x2C, 0x00, 0x00, 0x00, 0x53, 0x00, 0x68, 0x00, 0x6F, 0x00, 0x63, 0x00, 0x6B, 0x00,\r
+            0x77, 0x00, 0x61, 0x00, 0x76, 0x00, 0x65, 0x00, 0x20, 0x00, 0x46, 0x00, 0x6C, 0x00, 0x61, 0x00, 0x73, 0x00, 0x68,\r
+            0x00, 0x20, 0x00, 0x4F, 0x00, 0x62, 0x00, 0x6A, 0x00, 0x65, 0x00, 0x63, 0x00, 0x74, 0x00, 0x20, 0x00, (byte)0xBA,\r
+            0x0F, 0x3E, 0x00, 0x00, 0x00, 0x53, 0x00, 0x68, 0x00, 0x6F, 0x00, 0x63, 0x00, 0x6B, 0x00, 0x77, 0x00, 0x61, 0x00,\r
+            0x76, 0x00, 0x65, 0x00, 0x46, 0x00, 0x6C, 0x00, 0x61, 0x00, 0x73, 0x00, 0x68, 0x00, 0x2E, 0x00, 0x53, 0x00, 0x68,\r
+            0x00, 0x6F, 0x00, 0x63, 0x00, 0x6B, 0x00, 0x77, 0x00, 0x61, 0x00, 0x76, 0x00, 0x65, 0x00, 0x46, 0x00, 0x6C, 0x00,\r
+            0x61, 0x00, 0x73, 0x00, 0x68, 0x00, 0x2E, 0x00, 0x39, 0x00, 0x30, 0x00, (byte)0xBA, 0x0F, 0x2C, 0x00, 0x00, 0x00,\r
+            0x53, 0x00, 0x68, 0x00, 0x6F, 0x00, 0x63, 0x00, 0x6B, 0x00, 0x77, 0x00, 0x61, 0x00, 0x76, 0x00, 0x65, 0x00, 0x20,\r
+            0x00, 0x46, 0x00, 0x6C, 0x00, 0x61, 0x00, 0x73, 0x00, 0x68, 0x00, 0x20, 0x00, 0x4F, 0x00, 0x62, 0x00, 0x6A, 0x00,\r
+            0x65, 0x00, 0x63, 0x00, 0x74, 0x00\r
+    };\r
+\r
+    public void testRead() throws Exception {\r
+               ExControl record = new ExControl(data, 0, data.length);\r
+               assertEquals(RecordTypes.ExControl.typeID, record.getRecordType());\r
+\r
+        assertNotNull(record.getExControlAtom());\r
+        assertEquals(256, record.getExControlAtom().getSlideId());\r
+\r
+        ExOleObjAtom oleObj = record.getExOleObjAtom();\r
+        assertNotNull(oleObj);\r
+        assertEquals(oleObj.getDrawAspect(), ExOleObjAtom.DRAW_ASPECT_VISIBLE);\r
+        assertEquals(oleObj.getType(), ExOleObjAtom.TYPE_CONTROL);\r
+        assertEquals(oleObj.getSubType(), ExOleObjAtom.SUBTYPE_DEFAULT);\r
+\r
+        assertEquals("Shockwave Flash Object", record.getMenuName());\r
+        assertEquals("ShockwaveFlash.ShockwaveFlash.9", record.getProgId());\r
+        assertEquals("Shockwave Flash Object", record.getClipboardName());\r
+    }\r
+\r
+       public void testWrite() throws Exception {\r
+               ExControl record = new ExControl(data, 0, data.length);\r
+               ByteArrayOutputStream baos = new ByteArrayOutputStream();\r
+               record.writeOut(baos);\r
+               byte[] b = baos.toByteArray();\r
+\r
+        assertTrue(Arrays.equals(data, b));\r
+       }\r
+\r
+    public void testNewRecord() throws Exception {\r
+        ExControl record = new ExControl();\r
+        ExControlAtom ctrl = record.getExControlAtom();\r
+        ctrl.setSlideId(256);\r
+\r
+        ExOleObjAtom oleObj = record.getExOleObjAtom();\r
+        oleObj.setDrawAspect(ExOleObjAtom.DRAW_ASPECT_VISIBLE);\r
+        oleObj.setType(ExOleObjAtom.TYPE_CONTROL);\r
+        oleObj.setObjID(1);\r
+        oleObj.setSubType(ExOleObjAtom.SUBTYPE_DEFAULT);\r
+        oleObj.setObjStgDataRef(2);\r
+        oleObj.setOptions(1283584);\r
+\r
+        record.setMenuName("Shockwave Flash Object");\r
+        record.setProgId("ShockwaveFlash.ShockwaveFlash.9");\r
+        record.setClipboardName("Shockwave Flash Object");\r
+\r
+        ByteArrayOutputStream baos = new ByteArrayOutputStream();\r
+        record.writeOut(baos);\r
+        byte[] b = baos.toByteArray();\r
+\r
+        assertEquals(data.length, b.length);\r
+        assertTrue(Arrays.equals(data, b));\r
+    }\r
+\r
+}
\ No newline at end of file
diff --git a/src/scratchpad/testcases/org/apache/poi/hslf/record/TestExMediaAtom.java b/src/scratchpad/testcases/org/apache/poi/hslf/record/TestExMediaAtom.java
new file mode 100755 (executable)
index 0000000..6cda6a1
--- /dev/null
@@ -0,0 +1,94 @@
+\r
+/* ====================================================================\r
+   Licensed to the Apache Software Foundation (ASF) under one or more\r
+   contributor license agreements.  See the NOTICE file distributed with\r
+   this work for additional information regarding copyright ownership.\r
+   The ASF licenses this file to You under the Apache License, Version 2.0\r
+   (the "License"); you may not use this file except in compliance with\r
+   the License.  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
+        \r
+\r
+\r
+package org.apache.poi.hslf.record;\r
+\r
+\r
+import junit.framework.TestCase;\r
+import java.io.ByteArrayOutputStream;\r
+import java.util.Arrays;\r
+\r
+/**\r
+ * Tests that {@link org.apache.poi.hslf.record.HeadersFootersAtom} works properly\r
+ *\r
+ * @author Yegor Kozlov\r
+ */\r
+public class TestExMediaAtom extends TestCase {\r
+       // From a real file\r
+    private byte[] data = new byte[] {\r
+            0x00, 0x00, (byte)0x04, 0x10, 0x08, 0x00, 0x00, 00,\r
+            0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 };\r
+\r
+    public void testRead() throws Exception {\r
+               ExMediaAtom record = new ExMediaAtom(data, 0, data.length);\r
+               assertEquals(RecordTypes.ExMediaAtom.typeID, record.getRecordType());\r
+\r
+        assertEquals(1, record.getObjectId());\r
+        assertFalse(record.getFlag(ExMediaAtom.fLoop));\r
+        assertFalse(record.getFlag(ExMediaAtom.fNarration));\r
+        assertFalse(record.getFlag(ExMediaAtom.fRewind));\r
+    }\r
+\r
+       public void testWrite() throws Exception {\r
+               ExMediaAtom record = new ExMediaAtom(data, 0, data.length);\r
+               ByteArrayOutputStream baos = new ByteArrayOutputStream();\r
+               record.writeOut(baos);\r
+               byte[] b = baos.toByteArray();\r
+\r
+               assertTrue(Arrays.equals(data, b));\r
+       }\r
+\r
+    public void testNewRecord() throws Exception {\r
+        ExMediaAtom ref = new ExMediaAtom(data, 0, data.length);\r
+        System.out.println(ref.getMask());\r
+\r
+        ExMediaAtom record = new ExMediaAtom();\r
+        record.setObjectId(1);\r
+        record.setFlag(HeadersFootersAtom.fHasDate, false);\r
+        record.setFlag(HeadersFootersAtom.fHasTodayDate, false);\r
+        record.setFlag(HeadersFootersAtom.fHasFooter, false);\r
+\r
+        ByteArrayOutputStream baos = new ByteArrayOutputStream();\r
+        record.writeOut(baos);\r
+        byte[] b = baos.toByteArray();\r
+\r
+        assertTrue(Arrays.equals(data, b));\r
+    }\r
+\r
+    public void testFlags() throws Exception {\r
+        ExMediaAtom record = new ExMediaAtom();\r
+\r
+        //in a new record all the bits are 0\r
+        for(int i = 0; i < 3; i++) assertFalse(record.getFlag(1 << i));\r
+\r
+        record.setFlag(ExMediaAtom.fLoop, true);\r
+        assertTrue(record.getFlag(ExMediaAtom.fLoop));\r
+\r
+        record.setFlag(ExMediaAtom.fNarration, true);\r
+        assertTrue(record.getFlag(ExMediaAtom.fNarration));\r
+\r
+        record.setFlag(ExMediaAtom.fNarration, false);\r
+        assertFalse(record.getFlag(ExMediaAtom.fNarration));\r
+\r
+        record.setFlag(ExMediaAtom.fNarration, false);\r
+        assertFalse(record.getFlag(ExMediaAtom.fNarration));\r
+\r
+    }\r
+}
\ No newline at end of file
diff --git a/src/scratchpad/testcases/org/apache/poi/hslf/record/TestExOleObjAtom.java b/src/scratchpad/testcases/org/apache/poi/hslf/record/TestExOleObjAtom.java
new file mode 100755 (executable)
index 0000000..ba539c2
--- /dev/null
@@ -0,0 +1,78 @@
+\r
+/* ====================================================================\r
+   Licensed to the Apache Software Foundation (ASF) under one or more\r
+   contributor license agreements.  See the NOTICE file distributed with\r
+   this work for additional information regarding copyright ownership.\r
+   The ASF licenses this file to You under the Apache License, Version 2.0\r
+   (the "License"); you may not use this file except in compliance with\r
+   the License.  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
+        \r
+\r
+\r
+package org.apache.poi.hslf.record;\r
+\r
+\r
+import junit.framework.TestCase;\r
+import java.io.ByteArrayOutputStream;\r
+import java.util.Arrays;\r
+\r
+/**\r
+ * Tests that {@link ExOleObjAtom} works properly\r
+ *\r
+ * @author Yegor Kozlov\r
+ */\r
+public class TestExOleObjAtom extends TestCase {\r
+       // From a real file (embedded SWF control)\r
+    private byte[] data = new byte[] {\r
+            0x01, 0x00, (byte)0xC3, 0x0F, 0x18, 0x00, 0x00, 0x00,\r
+            0x01, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,\r
+            0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, (byte)0x96, 0x13, 0x00  };\r
+\r
+    public void testRead() throws Exception {\r
+               ExOleObjAtom record = new ExOleObjAtom(data, 0, data.length);\r
+               assertEquals(RecordTypes.ExOleObjAtom.typeID, record.getRecordType());\r
+        System.out.println(record);\r
+\r
+        assertEquals(record.getDrawAspect(), ExOleObjAtom.DRAW_ASPECT_VISIBLE);\r
+        assertEquals(record.getType(), ExOleObjAtom.TYPE_CONTROL);\r
+        assertEquals(record.getObjID(), 1);\r
+        assertEquals(record.getSubType(), ExOleObjAtom.SUBTYPE_DEFAULT);\r
+        assertEquals(record.getObjStgDataRef(), 2);\r
+        assertEquals(record.getOptions(), 1283584); //ther meaning is unknown\r
+    }\r
+\r
+       public void testWrite() throws Exception {\r
+               ExOleObjAtom record = new ExOleObjAtom(data, 0, data.length);\r
+               ByteArrayOutputStream baos = new ByteArrayOutputStream();\r
+               record.writeOut(baos);\r
+               byte[] b = baos.toByteArray();\r
+\r
+               assertTrue(Arrays.equals(data, b));\r
+       }\r
+\r
+    public void testNewRecord() throws Exception {\r
+        ExOleObjAtom record = new ExOleObjAtom();\r
+        record.setDrawAspect(ExOleObjAtom.DRAW_ASPECT_VISIBLE);\r
+        record.setType(ExOleObjAtom.TYPE_CONTROL);\r
+        record.setObjID(1);\r
+        record.setSubType(ExOleObjAtom.SUBTYPE_DEFAULT);\r
+        record.setObjStgDataRef(2);\r
+        record.setOptions(1283584);\r
+\r
+        ByteArrayOutputStream baos = new ByteArrayOutputStream();\r
+        record.writeOut(baos);\r
+        byte[] b = baos.toByteArray();\r
+\r
+        assertTrue(Arrays.equals(data, b));\r
+    }\r
+\r
+}
\ No newline at end of file
diff --git a/src/scratchpad/testcases/org/apache/poi/hslf/record/TestExOleObjStg.java b/src/scratchpad/testcases/org/apache/poi/hslf/record/TestExOleObjStg.java
new file mode 100755 (executable)
index 0000000..4ce08c7
--- /dev/null
@@ -0,0 +1,144 @@
+\r
+/* ====================================================================\r
+   Licensed to the Apache Software Foundation (ASF) under one or more\r
+   contributor license agreements.  See the NOTICE file distributed with\r
+   this work for additional information regarding copyright ownership.\r
+   The ASF licenses this file to You under the Apache License, Version 2.0\r
+   (the "License"); you may not use this file except in compliance with\r
+   the License.  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
+        \r
+\r
+\r
+package org.apache.poi.hslf.record;\r
+\r
+\r
+import junit.framework.TestCase;\r
+import java.io.ByteArrayOutputStream;\r
+import java.io.FileOutputStream;\r
+import java.io.InputStream;\r
+import java.io.IOException;\r
+import java.util.Arrays;\r
+\r
+import org.apache.poi.poifs.filesystem.POIFSFileSystem;\r
+import org.apache.poi.poifs.filesystem.DirectoryNode;\r
+import org.apache.poi.poifs.filesystem.DocumentEntry;\r
+\r
+/**\r
+ * Tests that {@link ExOleObjStg} works properly\r
+ *\r
+ * @author Yegor Kozlov\r
+ */\r
+public class TestExOleObjStg extends TestCase {\r
+\r
+    // From a real file (embedded SWF control)\r
+    /*\r
+    <ExOleObjStg info="16" type="4113" size="347" offset="4322" header="10 00 11 10 5B 01 00 00 ">\r
+      00 0E 00 00 78 9C BB 70 5E F0 C1 C2 8D 52 0F 19 D0 80 1D 03 33 C3 BF FF 9C\r
+      0C 6C 48 62 8C 40 CC 04 E3 08 30 30 B0 40 C5 FE FD FF FF 1F 24 C4 0C C4 FF\r
+      47 C1 90 02 41 0C F9 40 58 C2 A0 C0 E0 CA 90 07 A4 8B 18 2A D1 93 02 5E 20\r
+      C6 C0 0A 8F 73 50 5A C8 BB 5D 73 29 77 DD 79 C1 69 3B 5C 5C 83 43 50 D5 06\r
+      BC 48 2F 2B 66 38 C9 C8 0E 64 3B 30 42 C4 9C 81 B6 83 EC 4D 05 93 C5 24 D9\r
+      0D 02 42 0C 4C 8C C8 FE 21 56 9F 02 23 C9 56 E1 04 E4 D8 4F 4D 40 89 FD A0\r
+      BC FB 17 4B BA F8 07 C5 A3 60 78 03 7A E6 FF 09 67 59 1B 41 F9 9F 95 61 34\r
+      FF 53 13 50 62 3F 4C 1F AC 1C 18 CD F7 23 0B C0 DA 74 A0 B6 1B A8 3D 37 1A\r
+      F7 23 0B A4 87 A6 85 0A 00 1B 64 6F 38 21 98 03 DA C2 E7 60 90 01 92 69 0C\r
+      39 0C 65 0C 05 40 32 11 58 2F A4 02 6B 07 3D 60 19 5D 0E 14 27 4E 05 1F 90\r
+      0C 67 C8 04 96 ED 29 C0 72 BE 1C C8 E3 06 E3 FF FF 39 18 B8 80 2C 0F A0 5C\r
+      3A 43 06 58 2D A8 A7 E1 C3 10 02 97 87 B8 02 E6 1A 60 77 83 21 18 A8 12 64\r
+      8A 23 D0 B6 1C B8 59 C8 AA 90 F5 F0 62 94 75 DC C0 DE 0A 37 5C 1D 33 54 35\r
+      88 97 08 35 91 83 81 07 EC 27 10 BF 18 E8 9B E1 0F 00 BD 65 3D D4\r
+    </ExOleObjStg>\r
+     */\r
+    private byte[] data = new byte[] {\r
+            0x10, 0x00, 0x11, 0x10, 0x5B, 0x01, 0x00, 0x00, 0x00, 0x0E, 0x00, 0x00, 0x78, (byte)0x9C, (byte)0xBB, 0x70,\r
+            0x5E, (byte)0xF0, (byte)0xC1, (byte)0xC2, (byte)0x8D, 0x52, 0x0F, 0x19, (byte)0xD0, (byte)0x80, 0x1D, 0x03,\r
+            0x33, (byte)0xC3, (byte)0xBF, (byte)0xFF, (byte)0x9C, 0x0C, 0x6C, 0x48, 0x62, (byte)0x8C, 0x40, (byte)0xCC,\r
+            0x04, (byte)0xE3, 0x08, 0x30, 0x30, (byte)0xB0, 0x40, (byte)0xC5, (byte)0xFE, (byte)0xFD, (byte)0xFF, (byte)0xFF,\r
+            0x1F, 0x24, (byte)0xC4, (byte)0x0C, (byte)0xC4, (byte)0xFF, 0x47, (byte)0xC1, (byte)0x90, 0x02, 0x41, 0x0C,\r
+            (byte)0xF9, 0x40, 0x58, (byte)0xC2, (byte)0xA0, (byte)0xC0, (byte)0xE0, (byte)0xCA, (byte)0x90, 0x07, (byte)0xA4,\r
+            (byte)0x8B, 0x18, 0x2A, (byte)0xD1, (byte)0x93, 0x02, 0x5E, 0x20, (byte)0xC6, (byte)0xC0, 0x0A, (byte)0x8F,\r
+            0x73, 0x50, 0x5A, (byte)0xC8, (byte)0xBB, 0x5D, 0x73, 0x29, 0x77, (byte)0xDD, 0x79, (byte)0xC1, 0x69, 0x3B,\r
+            0x5C, 0x5C, (byte)0x83, 0x43, 0x50, (byte)0xD5, 0x06, (byte)0xBC, 0x48, 0x2F, 0x2B, 0x66, 0x38, (byte)0xC9,\r
+            (byte)0xC8, 0x0E, 0x64, 0x3B, 0x30, 0x42, (byte)0xC4, (byte)0x9C, (byte)0x81, (byte)0xB6, (byte)0x83, (byte)0xEC,\r
+            0x4D, 0x05, (byte)0x93, (byte)0xC5, 0x24, (byte)0xD9, 0x0D, 0x02, 0x42, 0x0C, 0x4C, (byte)0x8C, (byte)0xC8,\r
+            (byte)0xFE, 0x21, 0x56, (byte)0x9F, 0x02, 0x23, (byte)0xC9, 0x56, (byte)0xE1, 0x04, (byte)0xE4, (byte)0xD8,\r
+            0x4F, 0x4D, 0x40, (byte)0x89, (byte)0xFD, (byte)0xA0, (byte)0xBC, (byte)0xFB, 0x17, 0x4B, (byte)0xBA, (byte)0xF8,\r
+            0x07, (byte)0xC5, (byte)0xA3, 0x60, 0x78, 0x03, 0x7A, (byte)0xE6, (byte)0xFF, 0x09, 0x67, 0x59, 0x1B, 0x41,\r
+            (byte)0xF9, (byte)0x9F, (byte)0x95, 0x61, 0x34, (byte)0xFF, 0x53, 0x13, 0x50, 0x62, 0x3F, 0x4C, 0x1F, (byte)0xAC,\r
+            0x1C, 0x18, (byte)0xCD, (byte)0xF7, 0x23, 0x0B, (byte)0xC0, (byte)0xDA, 0x74, (byte)0xA0, (byte)0xB6, 0x1B,\r
+            (byte)0xA8, 0x3D, 0x37, 0x1A, (byte)0xF7, 0x23, 0x0B, (byte)0xA4, (byte)0x87, (byte)0xA6, (byte)0x85, 0x0A,\r
+            0x00, 0x1B, 0x64, 0x6F, 0x38, 0x21, (byte)0x98, 0x03, (byte)0xDA, (byte)0xC2, (byte)0xE7, 0x60, (byte)0x90,\r
+            0x01, (byte)0x92, 0x69, 0x0C, 0x39, 0x0C, 0x65, 0x0C, 0x05, 0x40, 0x32, 0x11, 0x58, 0x2F, (byte)0xA4, 0x02,\r
+            0x6B, 0x07, 0x3D, 0x60, 0x19, 0x5D, 0x0E, 0x14, 0x27, 0x4E, 0x05, 0x1F, (byte)0x90, 0x0C, 0x67, (byte)0xC8,\r
+            0x04, (byte)0x96, (byte)0xED, 0x29, (byte)0xC0, 0x72, (byte)0xBE, 0x1C, (byte)0xC8, (byte)0xE3, 0x06, (byte)0xE3,\r
+            (byte)0xFF, (byte)0xFF, 0x39, 0x18, (byte)0xB8, (byte)0x80, 0x2C, 0x0F, (byte)0xA0, 0x5C, 0x3A, 0x43, 0x06, 0x58,\r
+            0x2D, (byte)0xA8, (byte)0xA7, (byte)0xE1, (byte)0xC3, 0x10, 0x02, (byte)0x97, (byte)0x87, (byte)0xB8, 0x02,\r
+            (byte)0xE6, 0x1A, 0x60, 0x77, (byte)0x83, 0x21, 0x18, (byte)0xA8, 0x12, 0x64, (byte)0x8A, 0x23, (byte)0xD0,\r
+            (byte)0xB6, 0x1C, (byte)0xB8, 0x59, (byte)0xC8, (byte)0xAA, (byte)0x90, (byte)0xF5, (byte)0xF0, 0x62, (byte)0x94,\r
+            0x75, (byte)0xDC, (byte)0xC0, (byte)0xDE, 0x0A, 0x37, 0x5C, 0x1D, 0x33, 0x54, 0x35, (byte)0x88, (byte)0x97, 0x08,\r
+            0x35, (byte)0x91, (byte)0x83, (byte)0x81, 0x07, (byte)0xEC, 0x27, 0x10, (byte)0xBF, 0x18, (byte)0xE8, (byte)0x9B,\r
+            (byte)0xE1, 0x0F, 0x00, (byte)0xBD, 0x65, 0x3D, (byte)0xD4\r
+                };\r
+\r
+    public void testRead() throws Exception {\r
+        ExOleObjStg record = new ExOleObjStg(data, 0, data.length);\r
+        assertEquals(RecordTypes.ExOleObjStg.typeID, record.getRecordType());\r
+\r
+        int len = record.getDataLength();\r
+        byte[] oledata = readAll(record.getData());\r
+        assertEquals(len, oledata.length);\r
+\r
+        POIFSFileSystem fs = new POIFSFileSystem(record.getData());\r
+        assertTrue("Constructed POIFS from ExOleObjStg data", true);\r
+        DocumentEntry doc = (DocumentEntry)fs.getRoot().getEntry("Contents");\r
+        assertNotNull(doc);\r
+        assertTrue("Fetched the Contents stream containing OLE properties", true);\r
+    }\r
+\r
+    public void testWrite() throws Exception {\r
+        ExOleObjStg record = new ExOleObjStg(data, 0, data.length);\r
+        ByteArrayOutputStream baos = new ByteArrayOutputStream();\r
+        record.writeOut(baos);\r
+        byte[] b = baos.toByteArray();\r
+\r
+        assertTrue(Arrays.equals(data, b));\r
+    }\r
+\r
+    public void testNewRecord() throws Exception {\r
+        ExOleObjStg src = new ExOleObjStg(data, 0, data.length);\r
+        byte[] oledata = readAll(src.getData());\r
+\r
+        ExOleObjStg tgt = new ExOleObjStg();\r
+        tgt.setData(oledata);\r
+\r
+\r
+        assertEquals(src.getDataLength(), tgt.getDataLength());\r
+\r
+        ByteArrayOutputStream out = new ByteArrayOutputStream();\r
+        tgt.writeOut(out);\r
+        byte[] b = out.toByteArray();\r
+\r
+        assertEquals(data.length, b.length);\r
+        assertTrue(Arrays.equals(data, b));\r
+    }\r
+\r
+    private byte[] readAll(InputStream is) throws IOException {\r
+        int pos;\r
+        byte[] chunk = new byte[1024];\r
+        ByteArrayOutputStream out = new  ByteArrayOutputStream();\r
+        while((pos = is.read(chunk)) > 0){\r
+            out.write(chunk, 0, pos);\r
+        }\r
+        return out.toByteArray();\r
+\r
+    }\r
+}
\ No newline at end of file
diff --git a/src/scratchpad/testcases/org/apache/poi/hslf/record/TestExVideoContainer.java b/src/scratchpad/testcases/org/apache/poi/hslf/record/TestExVideoContainer.java
new file mode 100755 (executable)
index 0000000..224aedf
--- /dev/null
@@ -0,0 +1,90 @@
+\r
+/* ====================================================================\r
+   Licensed to the Apache Software Foundation (ASF) under one or more\r
+   contributor license agreements.  See the NOTICE file distributed with\r
+   this work for additional information regarding copyright ownership.\r
+   The ASF licenses this file to You under the Apache License, Version 2.0\r
+   (the "License"); you may not use this file except in compliance with\r
+   the License.  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
+        \r
+\r
+\r
+package org.apache.poi.hslf.record;\r
+\r
+\r
+import junit.framework.TestCase;\r
+import java.io.ByteArrayOutputStream;\r
+import java.util.Arrays;\r
+\r
+/**\r
+ * Tests that {@link HeadersFootersAtom} works properly\r
+ *\r
+ * @author Yegor Kozlov\r
+ */\r
+public class TestExVideoContainer extends TestCase {\r
+\r
+    // From a real file\r
+    private byte[] data = new byte[]{\r
+            0x0F, 0x00, 0x05, 0x10, (byte) 0x9E, 0x00, 0x00, 0x00,\r
+            0x00, 0x00, 0x04, 0x10, 0x08, 0x00, 0x00, 0x00,\r
+            0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\r
+            0x00, 0x00, (byte)0xBA, 0x0F, (byte)0x86, 0x00, 0x00, 0x00,\r
+            0x44, 0x00, 0x3A, 0x00, 0x5C, 0x00, 0x70, 0x00, 0x72, 0x00, 0x6F, 0x00, 0x6A, 0x00, 0x65, 0x00,\r
+            0x63, 0x00, 0x74, 0x00, 0x73, 0x00, 0x5C, 0x00, 0x53, 0x00, 0x63, 0x00, 0x68, 0x00, 0x75, 0x00,\r
+            0x6C, 0x00, 0x65, 0x00, 0x72, 0x00, 0x41, 0x00, 0x47, 0x00, 0x5C, 0x00, 0x6D, 0x00, 0x63, 0x00,\r
+            0x6F, 0x00, 0x6D, 0x00, 0x5F, 0x00, 0x76, 0x00, 0x5F, 0x00, 0x31, 0x00, 0x5F, 0x00, 0x30, 0x00,\r
+            0x5F, 0x00, 0x34, 0x00, 0x5C, 0x00, 0x76, 0x00, 0x69, 0x00, 0x65, 0x00, 0x77, 0x00, 0x5C, 0x00,\r
+            0x64, 0x00, 0x61, 0x00, 0x74, 0x00, 0x61, 0x00, 0x5C, 0x00, 0x74, 0x00, 0x65, 0x00, 0x73, 0x00,\r
+            0x74, 0x00, 0x73, 0x00, 0x5C, 0x00, 0x69, 0x00, 0x6D, 0x00, 0x61, 0x00, 0x67, 0x00, 0x65, 0x00,\r
+            0x73, 0x00, 0x5C, 0x00, 0x63, 0x00, 0x61, 0x00, 0x72, 0x00, 0x64, 0x00, 0x73, 0x00, 0x2E, 0x00,\r
+            0x6D, 0x00, 0x70, 0x00, 0x67, 0x00};\r
+\r
+\r
+\r
+\r
+    public void testRead() throws Exception {\r
+               ExVideoContainer record = new ExVideoContainer(data, 0, data.length);\r
+               assertEquals(RecordTypes.ExVideoContainer.typeID, record.getRecordType());\r
+\r
+        ExMediaAtom exMedia = record.getExMediaAtom();\r
+        assertEquals(1, exMedia.getObjectId());\r
+        assertNotNull(exMedia);\r
+        assertFalse(exMedia.getFlag(ExMediaAtom.fLoop));\r
+        assertFalse(exMedia.getFlag(ExMediaAtom.fNarration));\r
+        assertFalse(exMedia.getFlag(ExMediaAtom.fRewind));\r
+\r
+        CString path = record.getPathAtom();\r
+        assertNotNull(exMedia);\r
+        assertEquals("D:\\projects\\SchulerAG\\mcom_v_1_0_4\\view\\data\\tests\\images\\cards.mpg", path.getText());\r
+    }\r
+\r
+       public void testWrite() throws Exception {\r
+               ExVideoContainer record = new ExVideoContainer(data, 0, data.length);\r
+               ByteArrayOutputStream baos = new ByteArrayOutputStream();\r
+               record.writeOut(baos);\r
+               byte[] b = baos.toByteArray();\r
+\r
+               assertTrue(Arrays.equals(data, b));\r
+       }\r
+\r
+    public void testNewRecord() throws Exception {\r
+        ExVideoContainer record = new ExVideoContainer();\r
+        record.getExMediaAtom().setObjectId(1);\r
+        record.getPathAtom().setText("D:\\projects\\SchulerAG\\mcom_v_1_0_4\\view\\data\\tests\\images\\cards.mpg");\r
+\r
+        ByteArrayOutputStream baos = new ByteArrayOutputStream();\r
+        record.writeOut(baos);\r
+        byte[] b = baos.toByteArray();\r
+\r
+        assertTrue(Arrays.equals(data, b));\r
+    }\r
+}
\ No newline at end of file
index 0b610cb6452cbeca0026f4a1b2321275dbc9bdc6..3579ace5905d7dcf372d242150dce1b70f561580 100755 (executable)
@@ -48,6 +48,10 @@ public class TestTextRulerAtom extends TestCase {
         0x03, 0x69, 0x04, (byte)0xF6, 0x05, (byte)0xF6, 0x05\r
        };\r
 \r
+    private byte[] data_2 = new byte[] {\r
+        0x00, 0x00, (byte)0xA6, 0x0F, 0x0A, 0x00, 0x00, 0x00,\r
+        0x10, 0x03, 0x00, 0x00, (byte)0xF9, 0x00, 0x41, 0x01, 0x41, 0x01\r
+    };\r
 \r
     public void testReadRuler() throws Exception {\r
                TextRulerAtom ruler = new TextRulerAtom(data_1, 0, data_1.length);\r
@@ -73,4 +77,16 @@ public class TestTextRulerAtom extends TestCase {
         byte[] result = out.toByteArray();\r
         assertTrue(Arrays.equals(result, data_1));\r
        }\r
+\r
+    public void testRead2() throws Exception {\r
+               TextRulerAtom ruler = TextRulerAtom.getParagraphInstance();\r
+        ruler.setParagraphIndent((short)249, (short)321);\r
+        ByteArrayOutputStream out = new ByteArrayOutputStream();\r
+        ruler.writeOut(out);\r
+\r
+        byte[] result = out.toByteArray();\r
+        assertTrue(Arrays.equals(result, data_2));\r
+\r
+       }\r
+\r
 }\r