]> source.dussan.org Git - poi.git/commitdiff
Improved picture support for HSLF, from Yegor in bug 40388
authorNick Burch <nick@apache.org>
Tue, 19 Sep 2006 22:37:38 +0000 (22:37 +0000)
committerNick Burch <nick@apache.org>
Tue, 19 Sep 2006 22:37:38 +0000 (22:37 +0000)
git-svn-id: https://svn.apache.org/repos/asf/jakarta/poi/trunk@448001 13f79535-47bb-0310-9956-ffa450edef68

19 files changed:
src/documentation/content/xdocs/hslf/how-to-shapes.xml
src/scratchpad/src/org/apache/poi/hslf/HSLFSlideShow.java
src/scratchpad/src/org/apache/poi/hslf/blip/Bitmap.java [new file with mode: 0644]
src/scratchpad/src/org/apache/poi/hslf/blip/EMF.java [new file with mode: 0644]
src/scratchpad/src/org/apache/poi/hslf/blip/JPEG.java [new file with mode: 0644]
src/scratchpad/src/org/apache/poi/hslf/blip/Metafile.java [new file with mode: 0644]
src/scratchpad/src/org/apache/poi/hslf/blip/PICT.java [new file with mode: 0644]
src/scratchpad/src/org/apache/poi/hslf/blip/PNG.java [new file with mode: 0644]
src/scratchpad/src/org/apache/poi/hslf/blip/WMF.java [new file with mode: 0644]
src/scratchpad/src/org/apache/poi/hslf/extractor/ImageExtractor.java [new file with mode: 0644]
src/scratchpad/src/org/apache/poi/hslf/model/Picture.java
src/scratchpad/src/org/apache/poi/hslf/usermodel/PictureData.java
src/scratchpad/src/org/apache/poi/hslf/usermodel/SlideShow.java
src/scratchpad/testcases/org/apache/poi/hslf/data/cow.pict [new file with mode: 0644]
src/scratchpad/testcases/org/apache/poi/hslf/data/pictures.ppt [new file with mode: 0644]
src/scratchpad/testcases/org/apache/poi/hslf/data/santa.wmf [new file with mode: 0644]
src/scratchpad/testcases/org/apache/poi/hslf/data/tomcat.png [new file with mode: 0644]
src/scratchpad/testcases/org/apache/poi/hslf/data/wrench.emf [new file with mode: 0644]
src/scratchpad/testcases/org/apache/poi/hslf/usermodel/TestPictures.java

index f20bcef03bfc941bedfe9f4f721fef37c3ab5704..fb8229304b2a499dfdc1e0cdf0695f5735457483 100644 (file)
@@ -17,7 +17,7 @@
                     <li><link href="#PageSize">How to retrieve or change slide size</link></li>\r
                     <li><link href="#GetShapes">How to get shapes contained in a particular slide</link></li>\r
                     <li><link href="#Shapes">Drawing a shape on a slide</link></li>\r
-                    <li><link href="#Pictures">How to add/retrieve pictures</link></li>\r
+                    <li><link href="#Pictures">How to work with pictures</link></li>\r
                     <li><link href="#SlideTitle">How to set slide title</link></li>\r
                 </ul>\r
             </section>\r
                      corner of the slide. Distances in the drawing layer are measured in points (72 points = 1 inch).\r
                    </p>\r
                     <source>\r
-  SlideShow ppt = new SlideShow);\r
+        SlideShow ppt = new SlideShow();\r
 \r
   Slide slide = ppt.createSlide();\r
 \r
   //TextBox\r
   TextBox txt = new TextBox();\r
   txt.setText("Hello, World!");\r
-  txt.setAnchor(new java.awt.Rectangle(100, 100, 200, 50));\r
-  txt.setFontSize(32);\r
-  txt.setFontName("Arial");\r
-  txt.setBold(true);\r
+        txt.setAnchor(new java.awt.Rectangle(300, 100, 300, 50));\r
+\r
+        //use RichTextRun to work with the text format\r
+        RichTextRun rt = txt.getRichTextRuns()[0];\r
+        rt.setFontSize(32);\r
+        rt.setFontName("Arial");\r
+        rt.setBold(true);\r
+        rt.setItalic(true);\r
+        rt.setUnderlined(true);\r
+        rt.setFontColor(Color.red);\r
+        rt.setAlignment(TextBox.AlignRight);\r
+\r
   slide.addShape(txt);\r
 \r
   //Autoshape\r
   slide.addShape(sh2);\r
 \r
   FileOutputStream out = new FileOutputStream("slideshow.ppt");\r
-  wb.write(out);\r
+        ppt.write(out);\r
   out.close();\r
                   </source>\r
                 </section>\r
                 <anchor id="Pictures"/>\r
-                <section><title>How to add/retrieve pictures</title>\r
-                  <p>\r
-                    Note, for now only PNG and JPEG formats are supported.\r
-                  </p>\r
+                <section><title>How to work with pictures</title>\r
+                 \r
+  <p>\r
+  Currently, HSLF API supports the following types of pictures:\r
+  <ul>\r
+    <li>Windows Metafiles (WMF)</li>\r
+    <li>Enhanced Metafiles (EMF)</li>\r
+    <li>JPEG Interchange Format</li>\r
+    <li>Portable Network Graphics (PNG)</li>\r
+    <li>Macintosh PICT</li>\r
+  </ul>\r
+  </p>\r
+\r
                     <source>\r
   SlideShow ppt = new SlideShow(new HSLFSlideShow("slideshow.ppt"));\r
 \r
   for (int i = 0; i &lt; pdata.length; i++){\r
     PictureData pict = pdata[i];\r
 \r
-    //raw picture data\r
+    // picture data\r
     byte[] data = pict.getData();\r
 \r
     int type = pict.getType();\r
-    if (type == Picture.JPEG){\r
-      FileOutputStream out = new FileOutputStream("pict"+i+".jpg");\r
-      out.write(data);\r
-      out.close();\r
-    } else if (type == Picture.PNG){\r
-      FileOutputStream out = new FileOutputStream("pict"+i+".png");\r
+    String ext;\r
+    switch (type){\r
+      case Picture.JPEG: ext=".jpg"; break;\r
+      case Picture.PNG: ext=".png"; break;\r
+      case Picture.WMF: ext=".wmf"; break;\r
+      case Picture.EMF: ext=".emf"; break;\r
+      case Picture.PICT: ext=".pict"; break;\r
+      default: continue;\r
+    }\r
+    FileOutputStream out = new FileOutputStream("pict_"+i + ext);\r
       out.write(data);\r
       out.close();\r
-    }\r
+\r
   }\r
 \r
   // add a new picture to this slideshow and insert it in a  new slide\r
index 9aa5bd9d2ac95d4b8b9090a44837bcabfb5bde9a..48121e0c1450df62c169267280b54a0604e0da9b 100644 (file)
@@ -23,6 +23,7 @@ import java.util.*;
 import java.io.*;
 
 import org.apache.poi.POIDocument;
+import org.apache.poi.util.LittleEndian;
 import org.apache.poi.poifs.filesystem.POIFSFileSystem;
 import org.apache.poi.poifs.filesystem.DocumentEntry;
 import org.apache.poi.poifs.filesystem.DocumentInputStream;
@@ -251,13 +252,31 @@ public class HSLFSlideShow extends POIDocument
                        return;
                }
 
-               ArrayList p = new ArrayList();
-               int pos = 0; 
-               while (pos < (pictstream.length - PictureData.HEADER_SIZE)) {
-                       PictureData pict = new PictureData(pictstream, pos);
-                       p.add(pict);
-                       pos += PictureData.HEADER_SIZE + pict.getSize();
-               }
+        List p = new ArrayList();
+        int pos = 0;
+
+        while (pos < pictstream.length) {
+            int offset = pos;
+
+            //image signature
+            int signature = LittleEndian.getUShort(pictstream, pos);
+            pos += LittleEndian.SHORT_SIZE;
+            //image type + 0xF018
+            int type = LittleEndian.getUShort(pictstream, pos);
+            pos += LittleEndian.SHORT_SIZE;
+            //image size
+            int imgsize = LittleEndian.getInt(pictstream, pos);
+            pos += LittleEndian.INT_SIZE;
+
+            byte[] imgdata = new byte[imgsize];
+            System.arraycopy(pictstream, pos, imgdata, 0, imgdata.length);
+
+            PictureData pict = PictureData.create(type - 0xF018);
+            pict.setRawData(imgdata);
+            pict.setOffset(offset);
+            p.add(pict);
+            pos += imgsize;
+        }
 
                _pictures = (PictureData[])p.toArray(new PictureData[p.size()]);
        }
diff --git a/src/scratchpad/src/org/apache/poi/hslf/blip/Bitmap.java b/src/scratchpad/src/org/apache/poi/hslf/blip/Bitmap.java
new file mode 100644 (file)
index 0000000..c37674d
--- /dev/null
@@ -0,0 +1,47 @@
+/* ====================================================================\r
+   Copyright 2002-2004   Apache Software Foundation\r
+\r
+   Licensed under the Apache License, Version 2.0 (the "License");\r
+   you may not use this file except in compliance with the License.\r
+   You may obtain a copy of the License at\r
+\r
+       http://www.apache.org/licenses/LICENSE-2.0\r
+\r
+   Unless required by applicable law or agreed to in writing, software\r
+   distributed under the License is distributed on an "AS IS" BASIS,\r
+   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r
+   See the License for the specific language governing permissions and\r
+   limitations under the License.\r
+==================================================================== */\r
+package org.apache.poi.hslf.blip;\r
+\r
+import org.apache.poi.hslf.usermodel.PictureData;\r
+\r
+import java.io.IOException;\r
+import java.io.ByteArrayOutputStream;\r
+\r
+/**\r
+ * Represents a bitmap picture data:  JPEG or PNG.\r
+ * The data is not compressed and the exact file content is written in the stream.\r
+ * \r
+ * @author Yegor Kozlov\r
+ */\r
+public abstract  class Bitmap extends PictureData {\r
+\r
+    public byte[] getData(){\r
+        byte[] rawdata = getRawData();\r
+        byte[] imgdata = new byte[rawdata.length-17];\r
+        System.arraycopy(rawdata, 17, imgdata, 0, imgdata.length);\r
+        return imgdata;\r
+    }\r
+\r
+    public void setData(byte[] data) throws IOException {\r
+        ByteArrayOutputStream out = new ByteArrayOutputStream();\r
+        byte[] checksum = getChecksum(data);\r
+        out.write(checksum);\r
+        out.write(0);\r
+        out.write(data);\r
+\r
+        setRawData(out.toByteArray());\r
+    }\r
+}\r
diff --git a/src/scratchpad/src/org/apache/poi/hslf/blip/EMF.java b/src/scratchpad/src/org/apache/poi/hslf/blip/EMF.java
new file mode 100644 (file)
index 0000000..a502403
--- /dev/null
@@ -0,0 +1,92 @@
+/* ====================================================================\r
+   Copyright 2002-2004   Apache Software Foundation\r
+\r
+   Licensed under the Apache License, Version 2.0 (the "License");\r
+   you may not use this file except in compliance with the License.\r
+   You may obtain a copy of the License at\r
+\r
+       http://www.apache.org/licenses/LICENSE-2.0\r
+\r
+   Unless required by applicable law or agreed to in writing, software\r
+   distributed under the License is distributed on an "AS IS" BASIS,\r
+   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r
+   See the License for the specific language governing permissions and\r
+   limitations under the License.\r
+==================================================================== */\r
+package org.apache.poi.hslf.blip;\r
+\r
+import org.apache.poi.hslf.model.Picture;\r
+import org.apache.poi.hslf.model.Shape;\r
+\r
+import java.io.ByteArrayOutputStream;\r
+import java.io.InputStream;\r
+import java.io.ByteArrayInputStream;\r
+import java.io.IOException;\r
+import java.util.zip.InflaterInputStream;\r
+import java.util.zip.DeflaterOutputStream;\r
+\r
+/**\r
+ * Represents EMF (Windows Enhanced Metafile) picture data.\r
+ * \r
+ * @author Yegor Kozlov\r
+ */\r
+public class EMF extends Metafile {\r
+\r
+    /**\r
+     * Extract compressed EMF data from a ppt\r
+     */\r
+    public byte[] getData(){\r
+        try {\r
+            byte[] rawdata = getRawData();\r
+\r
+            ByteArrayOutputStream out = new ByteArrayOutputStream();\r
+            InputStream is = new ByteArrayInputStream( rawdata );\r
+            Header header = new Header();\r
+            header.read(rawdata, CHECKSUM_SIZE);\r
+            is.skip(header.getSize() + CHECKSUM_SIZE);\r
+\r
+            InflaterInputStream inflater = new InflaterInputStream( is );\r
+            byte[] chunk = new byte[4096];\r
+            int count;\r
+            while ((count = inflater.read(chunk)) >=0 ) {\r
+                out.write(chunk,0,count);\r
+            }\r
+            inflater.close();\r
+            return out.toByteArray();\r
+        } catch (IOException e){\r
+            throw new RuntimeException(e);\r
+        }\r
+    }\r
+\r
+    public void setData(byte[] data) throws IOException {\r
+        byte[] compressed = compress(data, 0, data.length);\r
+\r
+        Header header = new Header();\r
+        header.wmfsize = data.length;\r
+        //we don't have a EMF reader in java, have to set default image size  200x200\r
+        header.bounds = new java.awt.Rectangle(0, 0, 200, 200);\r
+        header.size = new java.awt.Dimension(header.bounds.width*Shape.EMU_PER_POINT, header.bounds.height*Shape.EMU_PER_POINT);\r
+        header.zipsize = compressed.length;\r
+\r
+        byte[] checksum = getChecksum(data);\r
+        ByteArrayOutputStream out = new ByteArrayOutputStream();\r
+        out.write(checksum);\r
+        header.write(out);\r
+        out.write(compressed);\r
+\r
+        setRawData(out.toByteArray());\r
+    }\r
+\r
+    public int getType(){\r
+        return Picture.EMF;\r
+    }\r
+\r
+    /**\r
+     * EMF signature is <code>0x3D40</code>\r
+     *\r
+     * @return EMF signature (<code>0x3D40</code>)\r
+     */\r
+    public int getSignature(){\r
+        return 0x3D40;\r
+    }\r
+}\r
diff --git a/src/scratchpad/src/org/apache/poi/hslf/blip/JPEG.java b/src/scratchpad/src/org/apache/poi/hslf/blip/JPEG.java
new file mode 100644 (file)
index 0000000..852e4c6
--- /dev/null
@@ -0,0 +1,43 @@
+/* ====================================================================\r
+   Copyright 2002-2004   Apache Software Foundation\r
+\r
+   Licensed under the Apache License, Version 2.0 (the "License");\r
+   you may not use this file except in compliance with the License.\r
+   You may obtain a copy of the License at\r
+\r
+       http://www.apache.org/licenses/LICENSE-2.0\r
+\r
+   Unless required by applicable law or agreed to in writing, software\r
+   distributed under the License is distributed on an "AS IS" BASIS,\r
+   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r
+   See the License for the specific language governing permissions and\r
+   limitations under the License.\r
+==================================================================== */\r
+package org.apache.poi.hslf.blip;\r
+\r
+import org.apache.poi.hslf.model.Picture;\r
+\r
+/**\r
+ * Represents a JPEG picture data in a PPT file\r
+ * \r
+ * @author Yegor Kozlov\r
+ */\r
+public class JPEG extends Bitmap {\r
+\r
+    /**\r
+     * @return type of  this picture\r
+     * @see  org.apache.poi.hslf.model.Picture#JPEG\r
+     */\r
+    public int getType(){\r
+        return Picture.JPEG;\r
+    }\r
+\r
+    /**\r
+     * JPEG signature is <code>0x46A0</code>\r
+     *\r
+     * @return JPEG signature (<code>0x46A0</code>)\r
+     */\r
+    public int getSignature(){\r
+        return 0x46A0;\r
+    }\r
+}\r
diff --git a/src/scratchpad/src/org/apache/poi/hslf/blip/Metafile.java b/src/scratchpad/src/org/apache/poi/hslf/blip/Metafile.java
new file mode 100644 (file)
index 0000000..1c00f21
--- /dev/null
@@ -0,0 +1,123 @@
+/* ====================================================================\r
+   Copyright 2002-2004   Apache Software Foundation\r
+\r
+   Licensed under the Apache License, Version 2.0 (the "License");\r
+   you may not use this file except in compliance with the License.\r
+   You may obtain a copy of the License at\r
+\r
+       http://www.apache.org/licenses/LICENSE-2.0\r
+\r
+   Unless required by applicable law or agreed to in writing, software\r
+   distributed under the License is distributed on an "AS IS" BASIS,\r
+   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r
+   See the License for the specific language governing permissions and\r
+   limitations under the License.\r
+==================================================================== */\r
+package org.apache.poi.hslf.blip;\r
+\r
+import org.apache.poi.util.LittleEndian;\r
+import org.apache.poi.hslf.usermodel.PictureData;\r
+\r
+import java.awt.*;\r
+import java.io.*;\r
+import java.util.zip.DeflaterOutputStream;\r
+import java.util.zip.InflaterInputStream;\r
+\r
+/**\r
+ * Represents a metafile picture which can be one of the following types: EMF, WMF, or PICT.\r
+ * A metafile is stored compressed using the ZIP deflate/inflate algorithm.\r
+ * \r
+ * @author Yegor Kozlov\r
+ */\r
+public abstract class Metafile extends PictureData {\r
+\r
+    /**\r
+     *  A structure which represents a 34-byte header preceeding the compressed metafile data\r
+     *\r
+     * @author Yegor Kozlov\r
+     */\r
+    public static class Header{\r
+\r
+        /**\r
+         * size of the original file\r
+         */\r
+        public int wmfsize;\r
+\r
+        /**\r
+         * Boundary of the metafile drawing commands\r
+         */\r
+        public Rectangle bounds;\r
+\r
+        /**\r
+         *  Size of the metafile in EMUs\r
+         */\r
+        public Dimension size;\r
+\r
+        /**\r
+         * size of the compressed metafile data\r
+         */\r
+        public int zipsize;\r
+\r
+        /**\r
+         * Reserved. Always 0.\r
+         */\r
+        public int compression;\r
+\r
+        /**\r
+         * Reserved. Always 254.\r
+         */\r
+        public int filter = 254;\r
+\r
+        public void read(byte[] data, int offset){\r
+            int pos = offset;\r
+            wmfsize = LittleEndian.getInt(data, pos);   pos += LittleEndian.INT_SIZE;\r
+\r
+            int left = LittleEndian.getInt(data, pos); pos += LittleEndian.INT_SIZE;\r
+            int top = LittleEndian.getInt(data, pos); pos += LittleEndian.INT_SIZE;\r
+            int right = LittleEndian.getInt(data, pos); pos += LittleEndian.INT_SIZE;\r
+            int bottom = LittleEndian.getInt(data, pos); pos += LittleEndian.INT_SIZE;\r
+\r
+            bounds = new Rectangle(left, top, right-left, bottom-top);\r
+            int width = LittleEndian.getInt(data, pos); pos += LittleEndian.INT_SIZE;\r
+            int height = LittleEndian.getInt(data, pos); pos += LittleEndian.INT_SIZE;\r
+\r
+            size = new Dimension(width, height);\r
+\r
+            zipsize = LittleEndian.getInt(data, pos); pos += LittleEndian.INT_SIZE;\r
+\r
+            compression = LittleEndian.getUnsignedByte(data, pos); pos++;\r
+            filter = LittleEndian.getUnsignedByte(data, pos); pos++;\r
+        }\r
+\r
+        public void write(OutputStream out) throws IOException {\r
+            byte[] header = new byte[34];\r
+            int pos = 0;\r
+            LittleEndian.putInt(header, pos, wmfsize); pos += LittleEndian.INT_SIZE; //hmf\r
+\r
+            LittleEndian.putInt(header, pos, bounds.x); pos += LittleEndian.INT_SIZE; //left\r
+            LittleEndian.putInt(header, pos, bounds.y); pos += LittleEndian.INT_SIZE; //top\r
+            LittleEndian.putInt(header, pos, bounds.x + bounds.width); pos += LittleEndian.INT_SIZE; //right\r
+            LittleEndian.putInt(header, pos, bounds.y + bounds.height); pos += LittleEndian.INT_SIZE; //bottom\r
+            LittleEndian.putInt(header, pos, size.width); pos += LittleEndian.INT_SIZE; //inch\r
+            LittleEndian.putInt(header, pos, size.height); pos += LittleEndian.INT_SIZE; //inch\r
+            LittleEndian.putInt(header, pos, zipsize); pos += LittleEndian.INT_SIZE; //inch\r
+\r
+            header[pos] = 0; pos ++;\r
+            header[pos] = (byte)filter; pos ++;\r
+\r
+            out.write(header);\r
+        }\r
+\r
+        public int getSize(){\r
+            return 34;\r
+        }\r
+    }\r
+\r
+    protected byte[] compress(byte[] bytes, int offset, int length) throws IOException {\r
+        ByteArrayOutputStream out = new ByteArrayOutputStream();\r
+        DeflaterOutputStream  deflater = new DeflaterOutputStream( out );\r
+        deflater.write(bytes, offset, length);\r
+        deflater.close();\r
+        return out.toByteArray();\r
+    }\r
+}\r
diff --git a/src/scratchpad/src/org/apache/poi/hslf/blip/PICT.java b/src/scratchpad/src/org/apache/poi/hslf/blip/PICT.java
new file mode 100644 (file)
index 0000000..f79eb19
--- /dev/null
@@ -0,0 +1,117 @@
+/* ====================================================================\r
+   Copyright 2002-2004   Apache Software Foundation\r
+\r
+   Licensed under the Apache License, Version 2.0 (the "License");\r
+   you may not use this file except in compliance with the License.\r
+   You may obtain a copy of the License at\r
+\r
+       http://www.apache.org/licenses/LICENSE-2.0\r
+\r
+   Unless required by applicable law or agreed to in writing, software\r
+   distributed under the License is distributed on an "AS IS" BASIS,\r
+   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r
+   See the License for the specific language governing permissions and\r
+   limitations under the License.\r
+==================================================================== */\r
+package org.apache.poi.hslf.blip;\r
+\r
+import org.apache.poi.hslf.model.Picture;\r
+import org.apache.poi.hslf.model.Shape;\r
+import org.apache.poi.util.LittleEndian;\r
+\r
+import java.io.*;\r
+import java.util.zip.InflaterInputStream;\r
+import java.util.zip.DeflaterOutputStream;\r
+\r
+/**\r
+ * Represents Macintosh PICT picture data.\r
+ * \r
+ * @author Yegor Kozlov\r
+ */\r
+public class PICT extends Metafile {\r
+\r
+    public PICT(){\r
+        super();\r
+    }\r
+\r
+    /**\r
+     * Extract compressed PICT data from a ppt\r
+     */\r
+    public byte[] getData(){\r
+        byte[] rawdata = getRawData();\r
+        try {\r
+            byte[] macheader = new byte[512];\r
+            ByteArrayOutputStream out = new ByteArrayOutputStream();\r
+            out.write(macheader);\r
+            int pos = CHECKSUM_SIZE;\r
+            byte[] pict;\r
+            try {\r
+                pict = read(rawdata, pos);\r
+            } catch (IOException e){\r
+                //weird MAC behaviour.\r
+                //if failed to read right after the checksum - skip 16 bytes and try again\r
+                pict = read(rawdata, pos + 16);\r
+            }\r
+            out.write(pict);\r
+            return out.toByteArray();\r
+        } catch (IOException e){\r
+            throw new RuntimeException(e);\r
+        }\r
+    }\r
+\r
+    private byte[] read(byte[] data, int pos) throws IOException {\r
+        ByteArrayOutputStream out = new ByteArrayOutputStream();\r
+        ByteArrayInputStream bis = new ByteArrayInputStream(data);\r
+        Header header = new Header();\r
+        header.read(data, pos);\r
+        bis.skip(pos + header.getSize());\r
+        InflaterInputStream inflater = new InflaterInputStream( bis );\r
+        byte[] chunk = new byte[4096];\r
+        int count;\r
+        while ((count = inflater.read(chunk)) >=0 ) {\r
+            out.write(chunk,0,count);\r
+        }\r
+        inflater.close();\r
+        return out.toByteArray();\r
+    }\r
+\r
+    public void setData(byte[] data) throws IOException {\r
+        int pos = 512; //skip the first 512 bytes - they are MAC specific crap\r
+        byte[] compressed = compress(data, pos, data.length-pos);\r
+\r
+        Header header = new Header();\r
+        header.wmfsize = data.length - 512;\r
+        //we don't have a PICT reader in java, have to set default image size  200x200\r
+        header.bounds = new java.awt.Rectangle(0, 0, 200, 200);\r
+        header.size = new java.awt.Dimension(header.bounds.width*Shape.EMU_PER_POINT,\r
+                header.bounds.height*Shape.EMU_PER_POINT);\r
+        header.zipsize = compressed.length;\r
+\r
+        byte[] checksum = getChecksum(data);\r
+        ByteArrayOutputStream out = new ByteArrayOutputStream();\r
+        out.write(checksum);\r
+\r
+        out.write(new byte[16]); //16-byte prefix which is safe to ignore\r
+        header.write(out);\r
+        out.write(compressed);\r
+\r
+        setRawData(out.toByteArray());\r
+    }\r
+\r
+    /**\r
+     * @see org.apache.poi.hslf.model.Picture#PICT\r
+     */\r
+    public int getType(){\r
+        return Picture.PICT;\r
+    }\r
+\r
+    /**\r
+     * PICT signature is <code>0x5430</code>\r
+     *\r
+     * @return PICT signature (<code>0x5430</code>)\r
+     */\r
+    public int getSignature(){\r
+        return 0x5430;\r
+    }\r
+\r
+}\r
diff --git a/src/scratchpad/src/org/apache/poi/hslf/blip/PNG.java b/src/scratchpad/src/org/apache/poi/hslf/blip/PNG.java
new file mode 100644 (file)
index 0000000..db9c782
--- /dev/null
@@ -0,0 +1,68 @@
+/* ====================================================================\r
+   Copyright 2002-2004   Apache Software Foundation\r
+\r
+   Licensed under the Apache License, Version 2.0 (the "License");\r
+   you may not use this file except in compliance with the License.\r
+   You may obtain a copy of the License at\r
+\r
+       http://www.apache.org/licenses/LICENSE-2.0\r
+\r
+   Unless required by applicable law or agreed to in writing, software\r
+   distributed under the License is distributed on an "AS IS" BASIS,\r
+   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r
+   See the License for the specific language governing permissions and\r
+   limitations under the License.\r
+==================================================================== */\r
+package org.apache.poi.hslf.blip;\r
+\r
+import org.apache.poi.hslf.model.Picture;\r
+\r
+import javax.imageio.ImageIO;\r
+import java.awt.image.BufferedImage;\r
+import java.io.ByteArrayInputStream;\r
+import java.io.IOException;\r
+\r
+/**\r
+ * Represents a PNG picture data in a PPT file\r
+ * \r
+ * @author Yegor Kozlov\r
+ */\r
+public class PNG extends Bitmap {\r
+\r
+    /**\r
+     * @return PNG data\r
+     */\r
+    public byte[] getData(){\r
+         byte[] data = super.getData();\r
+          try {\r
+              //PNG created on MAC may have a 16-byte prefix which prevents successful reading.\r
+              //Just cut it off!.\r
+              BufferedImage bi = ImageIO.read(new ByteArrayInputStream(data));\r
+              if (bi == null){\r
+                  byte[] png = new byte[data.length-16];\r
+                  System.arraycopy(data, 16, png, 0, png.length);\r
+                  data = png;\r
+              }\r
+          } catch (IOException e){\r
+              throw new RuntimeException(e);\r
+          }\r
+         return data;\r
+     }\r
+\r
+    /**\r
+     * @return type of  this picture\r
+     * @see  org.apache.poi.hslf.model.Picture#PNG\r
+     */\r
+    public int getType(){\r
+        return Picture.PNG;\r
+    }\r
+\r
+    /**\r
+     * PNG signature is <code>0x6E00</code>\r
+     *\r
+     * @return PNG signature (<code>0x6E00</code>)\r
+     */\r
+    public int getSignature(){\r
+        return 0x6E00;\r
+    }\r
+}\r
diff --git a/src/scratchpad/src/org/apache/poi/hslf/blip/WMF.java b/src/scratchpad/src/org/apache/poi/hslf/blip/WMF.java
new file mode 100644 (file)
index 0000000..36ca266
--- /dev/null
@@ -0,0 +1,187 @@
+/* ====================================================================\r
+   Copyright 2002-2004   Apache Software Foundation\r
+\r
+   Licensed under the Apache License, Version 2.0 (the "License");\r
+   you may not use this file except in compliance with the License.\r
+   You may obtain a copy of the License at\r
+\r
+       http://www.apache.org/licenses/LICENSE-2.0\r
+\r
+   Unless required by applicable law or agreed to in writing, software\r
+   distributed under the License is distributed on an "AS IS" BASIS,\r
+   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r
+   See the License for the specific language governing permissions and\r
+   limitations under the License.\r
+==================================================================== */\r
+package org.apache.poi.hslf.blip;\r
+\r
+import org.apache.poi.util.LittleEndian;\r
+import org.apache.poi.hslf.model.Picture;\r
+import org.apache.poi.hslf.model.Shape;\r
+\r
+import java.io.*;\r
+import java.util.zip.InflaterInputStream;\r
+import java.util.zip.DeflaterOutputStream;\r
+\r
+/**\r
+ * Represents a WMF (Windows Metafile) picture data.\r
+ * \r
+ * @author Yegor Kozlov\r
+ */\r
+public class WMF extends Metafile {\r
+\r
+    /**\r
+     * Extract compressed WMF data from a ppt\r
+     */\r
+    public byte[] getData(){\r
+        try {\r
+            byte[] rawdata = getRawData();\r
+\r
+            ByteArrayOutputStream out = new ByteArrayOutputStream();\r
+            InputStream is = new ByteArrayInputStream( rawdata );\r
+            Header header = new Header();\r
+            header.read(rawdata, CHECKSUM_SIZE);\r
+            is.skip(header.getSize() + CHECKSUM_SIZE);\r
+\r
+            AldusHeader aldus = new AldusHeader();\r
+            aldus.left = header.bounds.x;\r
+            aldus.top = header.bounds.y;\r
+            aldus.right = header.bounds.x + header.bounds.width;\r
+            aldus.bottom = header.bounds.y + header.bounds.height;\r
+            aldus.write(out);\r
+\r
+            InflaterInputStream inflater = new InflaterInputStream( is );\r
+            byte[] chunk = new byte[4096];\r
+            int count;\r
+            while ((count = inflater.read(chunk)) >=0 ) {\r
+                out.write(chunk,0,count);\r
+            }\r
+            inflater.close();\r
+            return out.toByteArray();\r
+        } catch (IOException e){\r
+            throw new RuntimeException(e);\r
+        }\r
+    }\r
+\r
+    public void setData(byte[] data) throws IOException {\r
+        int pos = 0;\r
+        AldusHeader aldus = new AldusHeader();\r
+        aldus.read(data, pos);\r
+        pos += aldus.getSize();\r
+\r
+        byte[] compressed = compress(data, pos, data.length-pos);\r
+\r
+        Header header = new Header();\r
+        header.wmfsize = data.length - aldus.getSize();\r
+        header.bounds = new java.awt.Rectangle((short)aldus.left, (short)aldus.top, (short)aldus.right-(short)aldus.left, (short)aldus.bottom-(short)aldus.top);\r
+        //coefficiaent to translate from WMF dpi to 96pdi\r
+        int coeff = 96*Shape.EMU_PER_POINT/aldus.inch;\r
+        header.size = new java.awt.Dimension(header.bounds.width*coeff, header.bounds.height*coeff);\r
+        header.zipsize = compressed.length;\r
+\r
+        byte[] checksum = getChecksum(data);\r
+        ByteArrayOutputStream out = new ByteArrayOutputStream();\r
+        out.write(checksum);\r
+        header.write(out);\r
+        out.write(compressed);\r
+\r
+        setRawData(out.toByteArray());\r
+    }\r
+\r
+    /**\r
+     * We are of type <code>Picture.WMF</code>\r
+     */\r
+    public int getType(){\r
+        return Picture.WMF;\r
+    }\r
+\r
+    /**\r
+     * WMF signature is <code>0x2160</code>\r
+     */\r
+    public int getSignature(){\r
+        return 0x2160;\r
+    }\r
+\r
+\r
+    /**\r
+     * Aldus Placeable Metafile header - 22 byte structure before WMF data.\r
+     * <ul>\r
+     *  <li>int Key;               Magic number (always 9AC6CDD7h)\r
+     *  <li>short  Handle;         Metafile HANDLE number (always 0)\r
+     *  <li>short Left;            Left coordinate in metafile units\r
+     *  <li>short Top;             Top coordinate in metafile units\r
+     *  <li>short Right;           Right coordinate in metafile units\r
+     *  <li>short Bottom;          Bottom coordinate in metafile units\r
+     *  <li>short  Inch;           Number of metafile units per inch\r
+     *  <li>int Reserved;          Reserved (always 0)\r
+     *  <li>short  Checksum;       Checksum value for previous 10 shorts\r
+     * </ul>\r
+     */\r
+    public static class AldusHeader{\r
+        public static final int APMHEADER_KEY = 0x9AC6CDD7;\r
+\r
+        public int handle;\r
+        public int left, top, right, bottom;\r
+        public int inch = 72; //default resolution is 72 dpi\r
+        public int reserved;\r
+        public int checksum;\r
+\r
+        public void read(byte[] data, int offset){\r
+            int pos = offset;\r
+            int key = LittleEndian.getInt(data, pos); pos += LittleEndian.INT_SIZE; //header key\r
+            if (key != APMHEADER_KEY) throw new RuntimeException("Not a valid WMF file");\r
+\r
+            handle = LittleEndian.getUShort(data, pos); pos += LittleEndian.SHORT_SIZE;\r
+            left = LittleEndian.getUShort(data, pos); pos += LittleEndian.SHORT_SIZE;\r
+            top = LittleEndian.getUShort(data, pos); pos += LittleEndian.SHORT_SIZE;\r
+            right = LittleEndian.getUShort(data, pos); pos += LittleEndian.SHORT_SIZE;\r
+            bottom = LittleEndian.getUShort(data, pos); pos += LittleEndian.SHORT_SIZE;\r
+\r
+            inch = LittleEndian.getUShort(data, pos); pos += LittleEndian.SHORT_SIZE;\r
+            reserved = LittleEndian.getInt(data, pos); pos += LittleEndian.INT_SIZE;\r
+\r
+            checksum = LittleEndian.getShort(data, pos); pos += LittleEndian.SHORT_SIZE;\r
+            if (checksum != getChecksum())\r
+                throw new RuntimeException("WMF checksum does not match the header data");\r
+        }\r
+\r
+        /**\r
+         * Returns a checksum value for the previous 10 shorts in the header.\r
+         * The checksum is calculated by XORing each short value to an initial value of 0:\r
+         */\r
+        public int getChecksum(){\r
+            int checksum = 0;\r
+            checksum ^=  (APMHEADER_KEY & 0x0000FFFF);\r
+            checksum ^= ((APMHEADER_KEY & 0xFFFF0000) >> 16);\r
+            checksum ^= left;\r
+            checksum ^= top;\r
+            checksum ^= right;\r
+            checksum ^= bottom;\r
+            checksum ^= inch;\r
+            return checksum;\r
+        }\r
+\r
+        public void write(OutputStream out) throws IOException {\r
+            byte[] header = new byte[22];\r
+            int pos = 0;\r
+            LittleEndian.putInt(header, pos, APMHEADER_KEY); pos += LittleEndian.INT_SIZE; //header key\r
+            LittleEndian.putUShort(header, pos, 0); pos += LittleEndian.SHORT_SIZE; //hmf\r
+            LittleEndian.putUShort(header, pos, left); pos += LittleEndian.SHORT_SIZE; //left\r
+            LittleEndian.putUShort(header, pos, top); pos += LittleEndian.SHORT_SIZE; //top\r
+            LittleEndian.putUShort(header, pos, right); pos += LittleEndian.SHORT_SIZE; //right\r
+            LittleEndian.putUShort(header, pos, bottom); pos += LittleEndian.SHORT_SIZE; //bottom\r
+            LittleEndian.putUShort(header, pos, inch); pos += LittleEndian.SHORT_SIZE; //inch\r
+            LittleEndian.putInt(header, pos, 0); pos += LittleEndian.INT_SIZE;  //reserved\r
+\r
+            checksum = getChecksum();\r
+            LittleEndian.putUShort(header, pos, checksum);\r
+\r
+            out.write(header);\r
+        }\r
+\r
+        public int getSize(){\r
+            return 22;\r
+        }\r
+    }\r
+\r
+}\r
diff --git a/src/scratchpad/src/org/apache/poi/hslf/extractor/ImageExtractor.java b/src/scratchpad/src/org/apache/poi/hslf/extractor/ImageExtractor.java
new file mode 100644 (file)
index 0000000..85fd16f
--- /dev/null
@@ -0,0 +1,75 @@
+\r
+/* ====================================================================\r
+   Copyright 2002-2004   Apache Software Foundation\r
+\r
+   Licensed under the Apache License, Version 2.0 (the "License");\r
+   you may not use this file except in compliance with the License.\r
+   You may obtain a copy of the License at\r
+\r
+       http://www.apache.org/licenses/LICENSE-2.0\r
+\r
+   Unless required by applicable law or agreed to in writing, software\r
+   distributed under the License is distributed on an "AS IS" BASIS,\r
+   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r
+   See the License for the specific language governing permissions and\r
+   limitations under the License.\r
+==================================================================== */\r
+package org.apache.poi.hslf.extractor;\r
+\r
+import org.apache.poi.hslf.usermodel.SlideShow;\r
+import org.apache.poi.hslf.usermodel.PictureData;\r
+import org.apache.poi.hslf.HSLFSlideShow;\r
+import org.apache.poi.hslf.model.Picture;\r
+\r
+import java.io.IOException;\r
+import java.io.FileOutputStream;\r
+\r
+/**\r
+ * Utility to extract pictures from a PowerPoint file.\r
+ *\r
+ * @author Yegor Kozlov\r
+ */\r
+public class ImageExtractor {\r
+    public static void main(String args[]) throws IOException {\r
+        if (args.length < 1) {\r
+            System.err.println("Usage:");\r
+            System.err.println("\tImageExtractor <file>");\r
+            return;\r
+        }\r
+        SlideShow ppt = new SlideShow(new HSLFSlideShow(args[0]));\r
+\r
+        //extract all pictures contained in the presentation\r
+        PictureData[] pdata = ppt.getPictureData();\r
+        for (int i = 0; i < pdata.length; i++) {\r
+            PictureData pict = pdata[i];\r
+\r
+            // picture data\r
+            byte[] data = pict.getData();\r
+\r
+            int type = pict.getType();\r
+            String ext;\r
+            switch (type) {\r
+                case Picture.JPEG:\r
+                    ext = ".jpg";\r
+                    break;\r
+                case Picture.PNG:\r
+                    ext = ".png";\r
+                    break;\r
+                case Picture.WMF:\r
+                    ext = ".wmf";\r
+                    break;\r
+                case Picture.EMF:\r
+                    ext = ".emf";\r
+                    break;\r
+                case Picture.PICT:\r
+                    ext = ".pict";\r
+                    break;\r
+                default:\r
+                    continue;\r
+            }\r
+            FileOutputStream out = new FileOutputStream("pict_" + i + ext);\r
+            out.write(data);\r
+            out.close();\r
+        }\r
+    }\r
+}\r
index 34f560c1115899548aa40d4be07b9adbe481d980..f1b9b3a4df11e217278695b33db54c8ab94e96ae 100644 (file)
@@ -4,9 +4,11 @@ import org.apache.poi.ddf.*;
 import org.apache.poi.hslf.usermodel.PictureData;\r
 import org.apache.poi.hslf.usermodel.SlideShow;\r
 import org.apache.poi.hslf.record.Document;\r
+import org.apache.poi.hslf.blip.Bitmap;\r
 \r
 import javax.imageio.ImageIO;\r
 import java.awt.image.BufferedImage;\r
+import java.awt.*;\r
 import java.io.ByteArrayInputStream;\r
 import java.io.IOException;\r
 import java.util.List;\r
@@ -22,33 +24,23 @@ import java.util.Arrays;
  *    type, image index to refer by slides etc.\r
  *  <li> "Pictures" OLE stream holds the actual data of the image.\r
  * </p>\r
- * <p>\r
- *  Data in the "Pictures" OLE stream is organized as follows:<br>\r
- *  For each image there is an entry: 25 byte header + image data.\r
- *  Image data is the exact content of the JPEG file, i.e. PowerPoint\r
- *  puts the whole jpeg file there without any modifications.<br>\r
- *   Header format:\r
- *    <li> 2 byte: image type. For JPEGs it is 0x46A0, for PNG it is 0x6E00.\r
- *    <li> 2 byte: unknown.\r
- *    <li> 4 byte : image size + 17. Looks like shift from the end of\r
- *          header but why to add it to the image  size?\r
- *    <li> next 16 bytes. Unique identifier of this image which is used by\r
- *          EscherBSE record.\r
- *  </p>\r
  *\r
  * @author Yegor Kozlov\r
  */\r
 public class Picture extends SimpleShape {\r
 \r
     /**\r
-    *  Windows Metafile\r
-    *  ( NOT YET SUPPORTED )\r
+    *  Windows Enhanced Metafile (EMF)\r
+    */\r
+    public static final int EMF = 2;\r
+\r
+    /**\r
+    *  Windows Metafile (WMF)\r
     */\r
     public static final int WMF = 3;\r
 \r
     /**\r
     * Macintosh PICT\r
-     *  ( NOT YET SUPPORTED )\r
     */\r
     public static final int PICT = 4;\r
 \r
@@ -63,10 +55,10 @@ public class Picture extends SimpleShape {
     public static final int PNG = 6;\r
 \r
     /**\r
-    * Windows DIB (BMP)\r
-    */\r
-    public static final int DIB = 7;\r
-\r
+     * Windows DIB (BMP)\r
+     */\r
+    public static final byte DIB = 7;\r
+    \r
     /**\r
      * Create a new <code>Picture</code>\r
      *\r
@@ -129,11 +121,17 @@ public class Picture extends SimpleShape {
      */\r
     public void setDefaultSize(){\r
         PictureData pict = getPictureData();\r
-        try {\r
-            BufferedImage img = ImageIO.read(new ByteArrayInputStream(pict.getData()));\r
-            setAnchor(new java.awt.Rectangle(0, 0, img.getWidth(), img.getHeight()));\r
-        } catch (IOException e){\r
-            throw new RuntimeException(e);\r
+        if (pict  instanceof Bitmap){\r
+            try {\r
+                BufferedImage img = ImageIO.read(new ByteArrayInputStream(pict.getData()));\r
+                if(img != null) setAnchor(new java.awt.Rectangle(0, 0, img.getWidth(), img.getHeight()));\r
+                else setAnchor(new java.awt.Rectangle(0, 0, 200, 200));\r
+            } catch (IOException e){\r
+                ;\r
+            }\r
+        } else {\r
+            //default size of a metafile picture is 200x200 \r
+            setAnchor(new java.awt.Rectangle(50, 50, 200, 200));\r
         }\r
     }\r
 \r
index 7eacac5938b6fddaec846bb231836a0ef71dbfc6..c8d6f0266056148e5621a73426c4e4872545e5c9 100644 (file)
@@ -17,6 +17,7 @@ package org.apache.poi.hslf.usermodel;
 \r
 import org.apache.poi.util.LittleEndian;\r
 import org.apache.poi.hslf.model.Picture;\r
+import org.apache.poi.hslf.blip.*;\r
 \r
 import java.io.OutputStream;\r
 import java.io.IOException;\r
@@ -24,172 +25,95 @@ import java.security.MessageDigest;
 import java.security.NoSuchAlgorithmException;\r
 \r
 /**\r
- * A class that represents the image data contained in the Presentation.\r
- * \r
+ * A class that represents image data contained in a slide show.\r
  *\r
  *  @author Yegor Kozlov\r
  */\r
-public class PictureData {\r
+public abstract class PictureData {\r
 \r
-       /**\r
-       * The size of the header\r
-       */\r
-       public static final int HEADER_SIZE = 25;\r
-\r
-    protected static final int JPEG_HEADER = -266516832;\r
-    protected static final int PNG_HEADER = -266441216;\r
+    /**\r
+     * Size of the image checksum calculated using MD5 algorithm.\r
+     */\r
+    protected static final int CHECKSUM_SIZE = 16;\r
 \r
        /**\r
        * Binary data of the picture\r
        */\r
-       protected byte[] pictdata;\r
-\r
-       /**\r
-       * Header which holds information about this picture\r
-       */\r
-       protected byte[] header;\r
-\r
+    private byte[] rawdata;\r
        /**\r
         * The offset to the picture in the stream\r
         */\r
        protected int offset;\r
 \r
-    public PictureData(){\r
-        header = new byte[PictureData.HEADER_SIZE];\r
-    }\r
-\r
-       /**\r
-       * Read a picture from "Pictures" OLE stream\r
-       *\r
-       * @param pictstream    the bytes to read\r
-       * @param offset        the index of the first byte to read\r
-       */\r
-       public PictureData(byte[] pictstream, int offset){\r
-               header = new byte[PictureData.HEADER_SIZE];\r
-               System.arraycopy(pictstream, offset, header, 0, header.length);\r
-\r
-               // Get the size of the picture, and make sure it's sane\r
-               // Size is stored unsigned, since it must always be positive\r
-               int size = (int)LittleEndian.getUInt(header, 4) - 17;\r
-               int startPos = offset + PictureData.HEADER_SIZE;\r
-               if(size < 0) { size = 0; }\r
-               if(size > (pictstream.length - startPos)) { \r
-                       int remaining = pictstream.length - startPos;\r
-                       System.err.println("Warning: PictureData claimed picture was of length " + size + ", but only " + remaining + " remained!");\r
-                       size = remaining;\r
-               }\r
-\r
-               // Save the picture data\r
-               pictdata = new byte[size];\r
-               this.offset = offset;\r
-               System.arraycopy(pictstream, startPos, pictdata, 0, pictdata.length);\r
-       }\r
+    /**\r
+     * Returns type of this picture.\r
+     * Must be one of the static constants defined in the <code>Picture<code> class.\r
+     *\r
+     * @return type of this picture.\r
+     */\r
+    public abstract int getType();\r
 \r
-       /**\r
-       * @return  the binary data of this picture\r
-       */\r
-       public byte[] getData(){\r
-               return pictdata;\r
-       }\r
+    /**\r
+     * Returns the binary data of this Picture\r
+     * @return picture data\r
+     */\r
+    public abstract byte[] getData();\r
 \r
     /**\r
      *  Set picture data\r
      */\r
-    public void setData(byte[] data) {\r
-        pictdata = data;\r
-        LittleEndian.putInt(header, 4, data.length + 17);\r
-    }\r
-\r
-       /**\r
-       * Return image size in bytes\r
-       *\r
-       * @return the size of the picture in bytes\r
-       */\r
-       public int getSize(){\r
-               return pictdata.length;\r
-       }\r
+    public abstract void setData(byte[] data) throws IOException;\r
 \r
-       /**\r
-       * Returns the unique identifier (UID) of this picture.\r
-       * The UID is a checksum of the picture data. Its length is 16 bytes\r
-       * and it must be unique across the presentation.\r
-       *\r
-       * @return the unique identifier of this picture\r
-       */\r
-       public byte[] getUID(){\r
-               byte[] uid = new byte[16];\r
-               System.arraycopy(header, 8, uid, 0, uid.length);\r
-               return uid;\r
-       }\r
+    /**\r
+     * Blip signature.\r
+     */\r
+    protected abstract int getSignature();\r
 \r
     /**\r
-     * Set the unique identifier (UID) of this picture.\r
+     * Returns the raw binary data of this Picture excluding the first 8 bytes\r
+     * which hold image signature and size of the image data.\r
      *\r
-     * @param uid checksum of the picture data\r
+     * @return picture data\r
      */\r
-    public void setUID(byte[] uid){\r
-        System.arraycopy(uid, 0, header, 8, uid.length);\r
+    public byte[] getRawData(){\r
+        return rawdata;\r
     }\r
 \r
-       /**\r
-       * Set the type of this picture.\r
-       *\r
-       * @return type of this picture.\r
-    * Must be one of the static constans defined in the <code>Picture<code> class.\r
-       */\r
-       public void setType(int format){\r
-        switch (format){\r
-            case Picture.JPEG: LittleEndian.putInt(header, 0, PictureData.JPEG_HEADER); break;\r
-            case Picture.PNG: LittleEndian.putInt(header, 0, PictureData.PNG_HEADER); break;\r
-        }\r
-       }\r
+    public void setRawData(byte[] data){\r
+        rawdata = data;\r
+    }\r
 \r
     /**\r
-     * Returns type of this picture.\r
-     * Must be one of the static constans defined in the <code>Picture<code> class.\r
+     * File offset in the 'Pictures' stream\r
      *\r
-     * @return type of this picture.\r
+     * @return offset in the 'Pictures' stream\r
      */\r
-    public int getType(){\r
-        int format = 0;\r
-        int val = LittleEndian.getInt(header, 0);\r
-        switch (val){\r
-            case PictureData.JPEG_HEADER: format = Picture.JPEG; break;\r
-            case PictureData.PNG_HEADER: format = Picture.PNG; break;\r
-        }\r
-        return format;\r
+    public int getOffset(){\r
+        return offset;\r
     }\r
 \r
     /**\r
-     * Returns the header of the Picture\r
+     * Set offset of this picture in the 'Pictures' stream.\r
+     * We need to set it when a new picture is created.\r
      *\r
-     * @return the header of the Picture\r
+     * @param offset in the 'Pictures' stream\r
      */\r
-    public byte[] getHeader(){\r
-        return header;\r
+    public void setOffset(int offset){\r
+        this.offset = offset;\r
     }\r
 \r
-       /**\r
-        * File offset in the 'Pictures' stream\r
-        *\r
-        * @return offset in the 'Pictures' stream\r
-        */\r
-       public int getOffset(){\r
-               return offset;\r
-       }\r
+    /**\r
+     * Returns 16-byte checksum of this picture\r
+     */\r
+    public byte[] getUID(){\r
+        byte[] uid = new byte[16];\r
+        System.arraycopy(rawdata, 0, uid, 0, uid.length);\r
+        return uid;\r
+    }\r
 \r
-       /**\r
-        * Set offset of this picture in the 'Pictures' stream.\r
-        * We need to set it when a new picture is created.\r
-        *\r
-        * @param offset in the 'Pictures' stream\r
-        */\r
-       public void setOffset(int offset){\r
-               this.offset = offset;\r
-       }\r
 \r
     /**\r
-     * Compute 16-byte checksum of this picture\r
+     * Compute 16-byte checksum of this picture using MD5 algorithm.\r
      */\r
     public static byte[] getChecksum(byte[] data) {\r
         MessageDigest sha;\r
@@ -206,8 +130,81 @@ public class PictureData {
      * Write this picture into <code>OutputStream</code>\r
      */\r
     public void write(OutputStream out) throws IOException {\r
-        out.write(header);\r
-        out.write(pictdata);\r
+        byte[] data;\r
+\r
+        data = new byte[LittleEndian.SHORT_SIZE];\r
+        LittleEndian.putUShort(data, 0, getSignature());\r
+        out.write(data);\r
+\r
+        data = new byte[LittleEndian.SHORT_SIZE];\r
+        LittleEndian.putUShort(data, 0, getType() + 0xF018);\r
+        out.write(data);\r
+\r
+        byte[] rawdata = getRawData();\r
+\r
+        data = new byte[LittleEndian.INT_SIZE];\r
+        LittleEndian.putInt(data, 0, rawdata.length);\r
+        out.write(data);\r
+\r
+        out.write(rawdata);\r
+    }\r
+\r
+    /**\r
+     * Create an instance of <code>PictureData</code> by type.\r
+     *\r
+     * @param type type of the picture data.\r
+     * Must be one of the static constants defined in the <code>Picture<code> class.\r
+     * @return concrete instance of <code>PictureData</code>\r
+     */\r
+     public static PictureData create(int type){\r
+        PictureData pict;\r
+        switch (type){\r
+            case Picture.EMF:\r
+                pict = new EMF();\r
+                break;\r
+            case Picture.WMF:\r
+                pict = new WMF();\r
+                break;\r
+            case Picture.PICT:\r
+                pict = new PICT();\r
+                break;\r
+            case Picture.JPEG:\r
+                pict = new JPEG();\r
+                break;\r
+            case Picture.PNG:\r
+                pict = new PNG();\r
+                break;\r
+            default:\r
+                throw new RuntimeException("Unsupported picture type: " + type);\r
+        }\r
+        return pict;\r
+    }\r
+\r
+    /**\r
+     * Return 24 byte header which preceeds the actual picture data.\r
+     * <p>\r
+     * The header consists of 2-byte signature, 2-byte type,\r
+     * 4-byte image size and 16-byte checksum of the image data.\r
+     * </p>\r
+     *\r
+     * @return the 24 byte header which preceeds the actual picture data.\r
+     */\r
+    public byte[] getHeader() {\r
+        byte[] header = new byte[16 + 8];\r
+        LittleEndian.putInt(header, 0, getSignature());\r
+        LittleEndian.putInt(header, 4, getRawData().length);\r
+        System.arraycopy(rawdata, 0, header, 8, 16);\r
+        return header;\r
+    }\r
+\r
+    /**\r
+    * Return image size in bytes\r
+    *\r
+    *  @return the size of the picture in bytes\r
+     * @deprecated Use <code>getData().length</code> instead.\r
+    */\r
+    public int getSize(){\r
+        return getData().length;\r
     }\r
 \r
 }\r
index 090ebb0566551c0d22242938d5ad1944373b8f33..f12b0cfdb3e376148ff3d7f1addb22e0b37f6a5c 100644 (file)
@@ -620,7 +620,7 @@ public class SlideShow
      * @param format    the format of the picture.  One of constans defined in the <code>Picture</code> class.
      * @return          the index to this picture (1 based).
      */
-    public int addPicture(byte[] data, int format) {
+    public int addPicture(byte[] data, int format) throws IOException {
         byte[] uid = PictureData.getChecksum(data);
 
         EscherContainerRecord bstore;
@@ -652,14 +652,23 @@ public class SlideShow
              }
         }
 
+        PictureData pict = PictureData.create(format);
+        pict.setData(data);
+        pict.setOffset(offset);
+
         EscherBSERecord bse = new EscherBSERecord();
         bse.setRecordId(EscherBSERecord.RECORD_ID);
         bse.setOptions( (short) ( 0x0002 | ( format << 4 ) ) );
-        bse.setSize(data.length + PictureData.HEADER_SIZE);
+        bse.setSize(pict.getRawData().length + 8);
         bse.setUid(uid);
+
         bse.setBlipTypeMacOS((byte)format);
         bse.setBlipTypeWin32((byte)format);
 
+        if (format == Picture.EMF) bse.setBlipTypeMacOS((byte)Picture.PICT);
+        else if (format == Picture.WMF) bse.setBlipTypeMacOS((byte)Picture.PICT);
+        else if (format == Picture.PICT) bse.setBlipTypeWin32((byte)Picture.WMF);
+
         bse.setRef(1);
         bse.setOffset(offset);
 
@@ -667,12 +676,6 @@ public class SlideShow
         int count = bstore.getChildRecords().size();
         bstore.setOptions((short)( (count << 4) | 0xF ));
 
-        PictureData pict = new PictureData();
-        pict.setUID(uid);
-        pict.setData(data);
-        pict.setType(format);
-               pict.setOffset(offset);
-
         _hslfSlideShow.addPicture(pict);
 
         return count;
@@ -685,7 +688,7 @@ public class SlideShow
      * @param format    the format of the picture.  One of constans defined in the <code>Picture</code> class.
      * @return          the index to this picture (1 based).
      */
-    public int addPicture(File pict, int format) {
+    public int addPicture(File pict, int format) throws IOException {
         int length = (int)pict.length();
         byte[] data = new byte[length];
         try {
diff --git a/src/scratchpad/testcases/org/apache/poi/hslf/data/cow.pict b/src/scratchpad/testcases/org/apache/poi/hslf/data/cow.pict
new file mode 100644 (file)
index 0000000..71b81d2
Binary files /dev/null and b/src/scratchpad/testcases/org/apache/poi/hslf/data/cow.pict differ
diff --git a/src/scratchpad/testcases/org/apache/poi/hslf/data/pictures.ppt b/src/scratchpad/testcases/org/apache/poi/hslf/data/pictures.ppt
new file mode 100644 (file)
index 0000000..9f6ce6d
Binary files /dev/null and b/src/scratchpad/testcases/org/apache/poi/hslf/data/pictures.ppt differ
diff --git a/src/scratchpad/testcases/org/apache/poi/hslf/data/santa.wmf b/src/scratchpad/testcases/org/apache/poi/hslf/data/santa.wmf
new file mode 100644 (file)
index 0000000..20993ba
Binary files /dev/null and b/src/scratchpad/testcases/org/apache/poi/hslf/data/santa.wmf differ
diff --git a/src/scratchpad/testcases/org/apache/poi/hslf/data/tomcat.png b/src/scratchpad/testcases/org/apache/poi/hslf/data/tomcat.png
new file mode 100644 (file)
index 0000000..b10c6b7
Binary files /dev/null and b/src/scratchpad/testcases/org/apache/poi/hslf/data/tomcat.png differ
diff --git a/src/scratchpad/testcases/org/apache/poi/hslf/data/wrench.emf b/src/scratchpad/testcases/org/apache/poi/hslf/data/wrench.emf
new file mode 100644 (file)
index 0000000..8001607
Binary files /dev/null and b/src/scratchpad/testcases/org/apache/poi/hslf/data/wrench.emf differ
index 3a3133a952b90e03002cb4d89f2a8c6f11efd5ef..861be0c2c6e884fed12ce8779877a9e91e966660 100644 (file)
 package org.apache.poi.hslf.usermodel;\r
 \r
 import org.apache.poi.hslf.*;\r
-import org.apache.poi.hslf.usermodel.PictureData;\r
-import org.apache.poi.hslf.usermodel.SlideShow;\r
-import org.apache.poi.hslf.model.Slide;\r
-import org.apache.poi.hslf.model.Shape;\r
-import org.apache.poi.hslf.model.Picture;\r
-import org.apache.poi.util.LittleEndian;\r
+import org.apache.poi.hslf.blip.*;\r
+import org.apache.poi.hslf.model.*;\r
 import junit.framework.TestCase;\r
 \r
-import javax.imageio.ImageIO;\r
-import java.awt.image.BufferedImage;\r
-import java.io.ByteArrayInputStream;\r
-import java.io.ByteArrayOutputStream;\r
-import java.io.File;\r
+import java.io.*;\r
+import java.util.Arrays;\r
 \r
 /**\r
- * Test extracting images from a ppt file\r
+ * Test adding/reading pictures\r
  *\r
  * @author Yegor Kozlov\r
  */\r
 public class TestPictures extends TestCase{\r
-    public static String dirname = System.getProperty("HSLF.testdata.path");\r
-    public static String filename = dirname + "/ppt_with_png.ppt";\r
 \r
-    public void testReadPictures() throws Exception {\r
+    protected File cwd;\r
 \r
-        HSLFSlideShow ppt = new HSLFSlideShow(filename);\r
-        PictureData[] pict = ppt.getPictures();\r
-        assertNotNull(pict);\r
-        for (int i = 0; i < pict.length; i++) {\r
-            byte[] data = pict[i].getData();\r
-\r
-            BufferedImage img = ImageIO.read(new ByteArrayInputStream(data));\r
-            assertNotNull(img);\r
-            assertEquals(Picture.PNG, pict[i].getType());\r
-        }\r
-        ppt.close();\r
+    public void setUp() throws Exception {\r
+        cwd = new File(System.getProperty("HSLF.testdata.path"));\r
     }\r
 \r
-    public void testReadPicturesForSlide() throws Exception {\r
+    /**\r
+     * Test read/write Macintosh PICT\r
+     */\r
+    public void testPICT() throws Exception {\r
+        SlideShow ppt = new SlideShow();\r
+\r
+        Slide slide = ppt.createSlide();\r
+        File img = new File(cwd, "cow.pict");\r
+        int idx = ppt.addPicture(img, Picture.PICT);\r
+        Picture pict = new Picture(idx);\r
+        assertEquals(idx, pict.getPictureIndex());\r
+        slide.addShape(pict);\r
 \r
-        SlideShow ppt = new SlideShow(new HSLFSlideShow(filename));\r
+        //serialize and read again\r
+        ByteArrayOutputStream out = new ByteArrayOutputStream();\r
+        ppt.write(out);\r
+        out.close();\r
 \r
-        Slide[] slide = ppt.getSlides();\r
-        for (int i = 0; i < slide.length; i++) {\r
-            Slide sl = slide[i];\r
-            Shape[] sh = sl.getShapes();\r
-            for (int j = 0; j < sh.length; j++) {\r
-                Shape shape = sh[j];\r
-                if (shape instanceof Picture){\r
-                    Picture picture = (Picture)shape;\r
+        ppt = new SlideShow(new HSLFSlideShow(new ByteArrayInputStream(out.toByteArray())));\r
 \r
-                    PictureData pictdata = picture.getPictureData();\r
-                    assertEquals(Picture.PNG, pictdata.getType());\r
+        //make sure we can read this picture shape and it refers to the correct picture data\r
+        Shape[] sh = ppt.getSlides()[0].getShapes();\r
+        assertEquals(1, sh.length);\r
+        pict = (Picture)sh[0];\r
+        assertEquals(idx, pict.getPictureIndex());\r
 \r
-                    //raw data.\r
-                    byte[] data = pictdata.getData();\r
-                    BufferedImage img = ImageIO.read(new ByteArrayInputStream(data));\r
-                    assertNotNull(img);\r
-                }\r
-            }\r
+        //check picture data\r
+        PictureData[] pictures = ppt.getPictureData();\r
+        //the Picture shape refers to the PictureData object in the Presentation\r
+        assertEquals(pict.getPictureData(), pictures[0]);\r
 \r
-        }\r
+        assertEquals(1, pictures.length);\r
+        assertEquals(Picture.PICT, pictures[0].getType());\r
+        assertTrue(pictures[0] instanceof PICT);\r
+        //compare the content of the initial file with what is stored in the PictureData\r
+        byte[] src_bytes = read(img);\r
+        byte[] ppt_bytes = pictures[0].getData();\r
+        assertEquals(src_bytes.length, ppt_bytes.length);\r
+        //in PICT the first 512 bytes are MAC specific and may not be preserved, ignore them\r
+        byte[] b1 = new byte[src_bytes.length-512];\r
+        System.arraycopy(src_bytes, 512, b1, 0, b1.length);\r
+        byte[] b2 = new byte[ppt_bytes.length-512];\r
+        System.arraycopy(ppt_bytes, 512, b2, 0, b2.length);\r
+        assertTrue(Arrays.equals(b1, b2));\r
     }\r
 \r
-    public void testSerializePictures() throws Exception {\r
-        HSLFSlideShow ppt = new HSLFSlideShow(filename);\r
-        PictureData[] pict = ppt.getPictures();\r
-        assertNotNull(pict);\r
+    /**\r
+     * Test read/write WMF\r
+     */\r
+    public void testWMF() throws Exception {\r
+        SlideShow ppt = new SlideShow();\r
+\r
+        Slide slide = ppt.createSlide();\r
+        File img = new File(cwd, "santa.wmf");\r
+        int idx = ppt.addPicture(img, Picture.WMF);\r
+        Picture pict = new Picture(idx);\r
+        assertEquals(idx, pict.getPictureIndex());\r
+        slide.addShape(pict);\r
 \r
+        //serialize and read again\r
         ByteArrayOutputStream out = new ByteArrayOutputStream();\r
         ppt.write(out);\r
         out.close();\r
 \r
-        ppt = new HSLFSlideShow(new ByteArrayInputStream(out.toByteArray()));\r
-        pict = ppt.getPictures();\r
-        assertNotNull(pict);\r
+        ppt = new SlideShow(new HSLFSlideShow(new ByteArrayInputStream(out.toByteArray())));\r
+\r
+        //make sure we can read this picture shape and it refers to the correct picture data\r
+        Shape[] sh = ppt.getSlides()[0].getShapes();\r
+        assertEquals(1, sh.length);\r
+        pict = (Picture)sh[0];\r
+        assertEquals(idx, pict.getPictureIndex());\r
+\r
+        //check picture data\r
+        PictureData[] pictures = ppt.getPictureData();\r
+        //the Picture shape refers to the PictureData object in the Presentation\r
+        assertEquals(pict.getPictureData(), pictures[0]);\r
+\r
+        assertEquals(1, pictures.length);\r
+        assertEquals(Picture.WMF, pictures[0].getType());\r
+        assertTrue(pictures[0] instanceof WMF);\r
+        //compare the content of the initial file with what is stored in the PictureData\r
+        byte[] src_bytes = read(img);\r
+        byte[] ppt_bytes = pictures[0].getData();\r
+        assertEquals(src_bytes.length, ppt_bytes.length);\r
+        //in WMF the first 22 bytes - is a metafile header\r
+        byte[] b1 = new byte[src_bytes.length-22];\r
+        System.arraycopy(src_bytes, 22, b1, 0, b1.length);\r
+        byte[] b2 = new byte[ppt_bytes.length-22];\r
+        System.arraycopy(ppt_bytes, 22, b2, 0, b2.length);\r
+        assertTrue(Arrays.equals(b1, b2));\r
     }\r
 \r
-    public void testAddPictures() throws Exception {\r
-        int idx;\r
-        Slide slide;\r
-        Picture pict;\r
+    /**\r
+     * Test read/write EMF\r
+     */\r
+    public void testEMF() throws Exception {\r
+        SlideShow ppt = new SlideShow();\r
+\r
+        Slide slide = ppt.createSlide();\r
+        File img = new File(cwd, "wrench.emf");\r
+        int idx = ppt.addPicture(img, Picture.EMF);\r
+        Picture pict = new Picture(idx);\r
+        assertEquals(idx, pict.getPictureIndex());\r
+        slide.addShape(pict);\r
+\r
+        //serialize and read again\r
+        ByteArrayOutputStream out = new ByteArrayOutputStream();\r
+        ppt.write(out);\r
+        out.close();\r
 \r
+        ppt = new SlideShow(new HSLFSlideShow(new ByteArrayInputStream(out.toByteArray())));\r
+\r
+        //make sure we can get this picture shape and it refers to the correct picture data\r
+        Shape[] sh = ppt.getSlides()[0].getShapes();\r
+        assertEquals(1, sh.length);\r
+        pict = (Picture)sh[0];\r
+        assertEquals(idx, pict.getPictureIndex());\r
+\r
+        //check picture data\r
+        PictureData[] pictures = ppt.getPictureData();\r
+        //the Picture shape refers to the PictureData object in the Presentation\r
+        assertEquals(pict.getPictureData(), pictures[0]);\r
+\r
+        assertEquals(1, pictures.length);\r
+        assertEquals(Picture.EMF, pictures[0].getType());\r
+        assertTrue(pictures[0] instanceof EMF);\r
+        //compare the content of the initial file with what is stored in the PictureData\r
+        byte[] src_bytes = read(img);\r
+        byte[] ppt_bytes = pictures[0].getData();\r
+        assertTrue(Arrays.equals(src_bytes, ppt_bytes));\r
+    }\r
+\r
+    /**\r
+     * Test read/write PNG\r
+     */\r
+    public void testPNG() throws Exception {\r
         SlideShow ppt = new SlideShow();\r
 \r
-        idx = ppt.addPicture(new File(dirname + "/clock.jpg"), Picture.JPEG);\r
-        slide = ppt.createSlide();\r
-        pict = new Picture(idx);\r
+        Slide slide = ppt.createSlide();\r
+        File img = new File(cwd, "tomcat.png");\r
+        int idx = ppt.addPicture(img, Picture.PNG);\r
+        Picture pict = new Picture(idx);\r
+        assertEquals(idx, pict.getPictureIndex());\r
         slide.addShape(pict);\r
 \r
-        idx = ppt.addPicture(new File(dirname + "/painting.png"), Picture.PNG);\r
-        pict = new Picture(idx);\r
+        //serialize and read again\r
+        ByteArrayOutputStream out = new ByteArrayOutputStream();\r
+        ppt.write(out);\r
+        out.close();\r
+\r
+        ppt = new SlideShow(new HSLFSlideShow(new ByteArrayInputStream(out.toByteArray())));\r
+\r
+        //make sure we can read this picture shape and it refers to the correct picture data\r
+        Shape[] sh = ppt.getSlides()[0].getShapes();\r
+        assertEquals(1, sh.length);\r
+        pict = (Picture)sh[0];\r
+        assertEquals(idx, pict.getPictureIndex());\r
+\r
+        //check picture data\r
+        PictureData[] pictures = ppt.getPictureData();\r
+        //the Picture shape refers to the PictureData object in the Presentation\r
+        assertEquals(pict.getPictureData(), pictures[0]);\r
+\r
+        assertEquals(1, pictures.length);\r
+        assertEquals(Picture.PNG, pictures[0].getType());\r
+        assertTrue(pictures[0] instanceof PNG);\r
+        //compare the content of the initial file with what is stored in the PictureData\r
+        byte[] src_bytes = read(img);\r
+        byte[] ppt_bytes = pictures[0].getData();\r
+        assertTrue(Arrays.equals(src_bytes, ppt_bytes));\r
+    }\r
+\r
+    /**\r
+     * Test read/write JPEG\r
+     */\r
+    public void testJPEG() throws Exception {\r
+        SlideShow ppt = new SlideShow();\r
+\r
+        Slide slide = ppt.createSlide();\r
+        File img = new File(cwd, "clock.jpg");\r
+        int idx = ppt.addPicture(img, Picture.JPEG);\r
+        Picture pict = new Picture(idx);\r
+        assertEquals(idx, pict.getPictureIndex());\r
         slide.addShape(pict);\r
 \r
+        //serialize and read again\r
         ByteArrayOutputStream out = new ByteArrayOutputStream();\r
         ppt.write(out);\r
         out.close();\r
 \r
         ppt = new SlideShow(new HSLFSlideShow(new ByteArrayInputStream(out.toByteArray())));\r
-        assertTrue(ppt.getPictureData().length == 2 );\r
+\r
+        //make sure we can read this picture shape and it refers to the correct picture data\r
+        Shape[] sh = ppt.getSlides()[0].getShapes();\r
+        assertEquals(1, sh.length);\r
+        pict = (Picture)sh[0];\r
+        assertEquals(idx, pict.getPictureIndex());\r
+\r
+        //check picture data\r
+        PictureData[] pictures = ppt.getPictureData();\r
+        //the Picture shape refers to the PictureData object in the Presentation\r
+        assertEquals(pict.getPictureData(), pictures[0]);\r
+\r
+        assertEquals(1, pictures.length);\r
+        assertEquals(Picture.JPEG, pictures[0].getType());\r
+        assertTrue(pictures[0] instanceof JPEG);\r
+        //compare the content of the initial file with what is stored in the PictureData\r
+        byte[] src_bytes = read(img);\r
+        byte[] ppt_bytes = pictures[0].getData();\r
+        assertTrue(Arrays.equals(src_bytes, ppt_bytes));\r
     }\r
 \r
+    /**\r
+     * Read file into a byte array\r
+     */\r
+    protected byte[] read(File f) throws IOException {\r
+        byte[] bytes = new byte[(int)f.length()];\r
+        FileInputStream is = new FileInputStream(f);\r
+        is.read(bytes);\r
+        is.close();\r
+        return bytes;\r
+    }\r
+\r
+    /**\r
+     * Read pictures in different formats from a reference slide show\r
+     */\r
+    public void testReadPictures() throws Exception {\r
+\r
+        byte[] src_bytes, ppt_bytes, b1, b2;\r
+        Picture pict;\r
+        PictureData pdata;\r
+\r
+        SlideShow ppt = new SlideShow(new HSLFSlideShow(new File(cwd, "pictures.ppt").getPath()));\r
+        Slide[] slides = ppt.getSlides();\r
+        PictureData[] pictures = ppt.getPictureData();\r
+        assertEquals(5, pictures.length);\r
+\r
+        pict = (Picture)slides[0].getShapes()[0]; //the first slide contains JPEG\r
+        pdata = pict.getPictureData();\r
+        assertTrue(pdata instanceof JPEG);\r
+        assertEquals(Picture.JPEG, pdata.getType());\r
+        src_bytes = pdata.getData();\r
+        ppt_bytes = read(new File(cwd, "clock.jpg"));\r
+        assertTrue(Arrays.equals(src_bytes, ppt_bytes));\r
+\r
+        pict = (Picture)slides[1].getShapes()[0]; //the second slide contains PNG\r
+        pdata = pict.getPictureData();\r
+        assertTrue(pdata instanceof PNG);\r
+        assertEquals(Picture.PNG, pdata.getType());\r
+        src_bytes = pdata.getData();\r
+        ppt_bytes = read(new File(cwd, "tomcat.png"));\r
+        assertTrue(Arrays.equals(src_bytes, ppt_bytes));\r
+\r
+        pict = (Picture)slides[2].getShapes()[0]; //the third slide contains WMF\r
+        pdata = pict.getPictureData();\r
+        assertTrue(pdata instanceof WMF);\r
+        assertEquals(Picture.WMF, pdata.getType());\r
+        src_bytes = pdata.getData();\r
+        ppt_bytes = read(new File(cwd, "santa.wmf"));\r
+        assertEquals(src_bytes.length, ppt_bytes.length);\r
+        //ignore the first 22 bytes - it is a WMF metafile header\r
+        b1 = new byte[src_bytes.length-22];\r
+        System.arraycopy(src_bytes, 22, b1, 0, b1.length);\r
+        b2 = new byte[ppt_bytes.length-22];\r
+        System.arraycopy(ppt_bytes, 22, b2, 0, b2.length);\r
+        assertTrue(Arrays.equals(b1, b2));\r
+\r
+        pict = (Picture)slides[3].getShapes()[0]; //the forth slide contains PICT\r
+        pdata = pict.getPictureData();\r
+        assertTrue(pdata instanceof PICT);\r
+        assertEquals(Picture.PICT, pdata.getType());\r
+        src_bytes = pdata.getData();\r
+        ppt_bytes = read(new File(cwd, "cow.pict"));\r
+        assertEquals(src_bytes.length, ppt_bytes.length);\r
+        //ignore the first 512 bytes - it is a MAC specific crap\r
+        b1 = new byte[src_bytes.length-512];\r
+        System.arraycopy(src_bytes, 512, b1, 0, b1.length);\r
+        b2 = new byte[ppt_bytes.length-512];\r
+        System.arraycopy(ppt_bytes, 512, b2, 0, b2.length);\r
+        assertTrue(Arrays.equals(b1, b2));\r
+\r
+        pict = (Picture)slides[4].getShapes()[0]; //the fifth slide contains EMF\r
+        pdata = pict.getPictureData();\r
+        assertTrue(pdata instanceof EMF);\r
+        assertEquals(Picture.EMF, pdata.getType());\r
+        src_bytes = pdata.getData();\r
+        ppt_bytes = read(new File(cwd, "wrench.emf"));\r
+        assertTrue(Arrays.equals(src_bytes, ppt_bytes));\r
+\r
+    }\r
+\r
+\r
 }\r