Return-Path: Delivered-To: apmail-jakarta-poi-dev-archive@www.apache.org Received: (qmail 10559 invoked from network); 19 Sep 2006 22:37:49 -0000 Received: from hermes.apache.org (HELO mail.apache.org) (209.237.227.199) by minotaur.apache.org with SMTP; 19 Sep 2006 22:37:49 -0000 Received: (qmail 33990 invoked by uid 500); 19 Sep 2006 22:37:48 -0000 Delivered-To: apmail-jakarta-poi-dev-archive@jakarta.apache.org Received: (qmail 33967 invoked by uid 500); 19 Sep 2006 22:37:48 -0000 Mailing-List: contact poi-dev-help@jakarta.apache.org; run by ezmlm Precedence: bulk List-Unsubscribe: List-Help: List-Post: List-Id: "POI Developers List" Reply-To: "POI Developers List" Delivered-To: mailing list poi-dev@jakarta.apache.org Received: (qmail 33956 invoked by uid 500); 19 Sep 2006 22:37:48 -0000 Received: (qmail 33950 invoked by uid 99); 19 Sep 2006 22:37:48 -0000 Received: from idunn.apache.osuosl.org (HELO idunn.apache.osuosl.org) (140.211.166.84) by apache.org (qpsmtpd/0.29) with ESMTP; Tue, 19 Sep 2006 15:37:48 -0700 X-ASF-Spam-Status: No, hits=-9.8 required=5.0 tests=ALL_TRUSTED,NO_REAL_NAME Received: from ([140.211.166.113:50910] helo=eris.apache.org) by idunn.apache.osuosl.org (ecelerity 2.1 r(10620)) with ESMTP id 7C/AD-28275-63170154 for ; Tue, 19 Sep 2006 15:37:42 -0700 Received: by eris.apache.org (Postfix, from userid 65534) id 486A41A981A; Tue, 19 Sep 2006 15:37:40 -0700 (PDT) Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit Subject: 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... Date: Tue, 19 Sep 2006 22:37:39 -0000 To: poi-cvs@jakarta.apache.org From: nick@apache.org X-Mailer: svnmailer-1.1.0 Message-Id: <20060919223740.486A41A981A@eris.apache.org> X-Spam-Rating: minotaur.apache.org 1.6.2 0/1000/N 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 @@
  • How to retrieve or change slide size
  • How to get shapes contained in a particular slide
  • Drawing a shape on a slide
  • -
  • How to add/retrieve pictures
  • +
  • How to work with pictures
  • How to set slide title
  • @@ -106,7 +106,7 @@ corner of the slide. Distances in the drawing layer are measured in points (72 points = 1 inch).

    - 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(); -
    How to add/retrieve pictures -

    - Note, for now only PNG and JPEG formats are supported. -

    +
    How to work with pictures + +

    + Currently, HSLF API supports the following types of pictures: +

      +
    • Windows Metafiles (WMF)
    • +
    • Enhanced Metafiles (EMF)
    • +
    • JPEG Interchange Format
    • +
    • Portable Network Graphics (PNG)
    • +
    • Macintosh PICT
    • +
    +

    + 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 0x3D40 + * + * @return EMF signature (0x3D40) + */ + 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 0x46A0 + * + * @return JPEG signature (0x46A0) + */ + 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 0x5430 + * + * @return PICT signature (0x5430) + */ + 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 0x6E00 + * + * @return PNG signature (0x6E00) + */ + 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 Picture.WMF + */ + public int getType(){ + return Picture.WMF; + } + + /** + * WMF signature is 0x2160 + */ + public int getSignature(){ + return 0x2160; + } + + + /** + * Aldus Placeable Metafile header - 22 byte structure before WMF data. + *
      + *
    • int Key; Magic number (always 9AC6CDD7h) + *
    • short Handle; Metafile HANDLE number (always 0) + *
    • short Left; Left coordinate in metafile units + *
    • short Top; Top coordinate in metafile units + *
    • short Right; Right coordinate in metafile units + *
    • short Bottom; Bottom coordinate in metafile units + *
    • short Inch; Number of metafile units per inch + *
    • int Reserved; Reserved (always 0) + *
    • short Checksum; Checksum value for previous 10 shorts + *
    + */ + 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 "); + 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. *
  • "Pictures" OLE stream holds the actual data of the image. *

    - *

    - * Data in the "Pictures" OLE stream is organized as follows:
    - * 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.
    - * Header format: - *

  • 2 byte: image type. For JPEGs it is 0x46A0, for PNG it is 0x6E00. - *
  • 2 byte: unknown. - *
  • 4 byte : image size + 17. Looks like shift from the end of - * header but why to add it to the image size? - *
  • next 16 bytes. Unique identifier of this image which is used by - * EscherBSE record. - *

    * * @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 Picture * @@ -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 Picture 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 Picture 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 Picture 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 OutputStream */ 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 PictureData by type. + * + * @param type type of the picture data. + * Must be one of the static constants defined in the Picture class. + * @return concrete instance of PictureData + */ + 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. + *

    + * The header consists of 2-byte signature, 2-byte type, + * 4-byte image size and 16-byte checksum of the image data. + *

    + * + * @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 getData().length 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 Picture 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 Picture 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/