<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 < 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
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;
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()]);
}
--- /dev/null
+/* ====================================================================\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
--- /dev/null
+/* ====================================================================\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
--- /dev/null
+/* ====================================================================\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
--- /dev/null
+/* ====================================================================\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
--- /dev/null
+/* ====================================================================\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
--- /dev/null
+/* ====================================================================\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
--- /dev/null
+/* ====================================================================\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
--- /dev/null
+\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
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
* 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
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
*/\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
\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
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
* 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
* @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;
}
}
+ 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);
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;
* @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 {
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