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