Improved picture support for HSLF, from Yegor in bug 40388

git-svn-id: https://svn.apache.org/repos/asf/jakarta/poi/trunk@448001 13f79535-47bb-0310-9956-ffa450edef68
This commit is contained in:
Nick Burch 2006-09-19 22:37:38 +00:00
parent 79606540c1
commit 6e1236c74a
19 changed files with 1274 additions and 274 deletions

View File

@ -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 &lt; 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");
out.write(data);
out.close();
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

View File

@ -23,6 +23,7 @@ import java.util.*;
import java.io.*;
import org.apache.poi.POIDocument;
import org.apache.poi.util.LittleEndian;
import org.apache.poi.poifs.filesystem.POIFSFileSystem;
import org.apache.poi.poifs.filesystem.DocumentEntry;
import org.apache.poi.poifs.filesystem.DocumentInputStream;
@ -251,13 +252,31 @@ public class HSLFSlideShow extends POIDocument
return;
}
ArrayList p = new ArrayList();
int pos = 0;
while (pos < (pictstream.length - PictureData.HEADER_SIZE)) {
PictureData pict = new PictureData(pictstream, pos);
p.add(pict);
pos += PictureData.HEADER_SIZE + pict.getSize();
}
List p = new ArrayList();
int pos = 0;
while (pos < pictstream.length) {
int offset = pos;
//image signature
int signature = LittleEndian.getUShort(pictstream, pos);
pos += LittleEndian.SHORT_SIZE;
//image type + 0xF018
int type = LittleEndian.getUShort(pictstream, pos);
pos += LittleEndian.SHORT_SIZE;
//image size
int imgsize = LittleEndian.getInt(pictstream, pos);
pos += LittleEndian.INT_SIZE;
byte[] imgdata = new byte[imgsize];
System.arraycopy(pictstream, pos, imgdata, 0, imgdata.length);
PictureData pict = PictureData.create(type - 0xF018);
pict.setRawData(imgdata);
pict.setOffset(offset);
p.add(pict);
pos += imgsize;
}
_pictures = (PictureData[])p.toArray(new PictureData[p.size()]);
}

View File

@ -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());
}
}

View File

@ -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;
}
}

View File

@ -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;
}
}

View File

@ -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();
}
}

View File

@ -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;
}
}

View File

@ -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;
}
}

View File

@ -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;
}
}
}

View File

@ -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();
}
}
}

View File

@ -4,9 +4,11 @@ import org.apache.poi.ddf.*;
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 @@ import java.util.Arrays;
* 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 class Picture extends SimpleShape {
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 class Picture extends SimpleShape {
*/
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));
}
}

View File

@ -17,6 +17,7 @@ package org.apache.poi.hslf.usermodel;
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.MessageDigest;
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];
}
/**
* 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();
/**
* 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);
}
/**
* @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;
}
/**
* 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;
}
public abstract void setData(byte[] data) throws IOException;
/**
* Set the unique identifier (UID) of this picture.
*
* @param uid checksum of the picture data
* Blip signature.
*/
public void setUID(byte[] uid){
System.arraycopy(uid, 0, header, 8, uid.length);
}
/**
* 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;
}
}
protected abstract int getSignature();
/**
* Returns type of this picture.
* Must be one of the static constans defined in the <code>Picture<code> class.
* Returns the raw binary data of this Picture excluding the first 8 bytes
* which hold image signature and size of the image data.
*
* @return type of this picture.
* @return picture data
*/
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 byte[] getRawData(){
return rawdata;
}
public void setRawData(byte[] data){
rawdata = data;
}
/**
* Returns the header of the Picture
* File offset in the 'Pictures' stream
*
* @return the header of the Picture
* @return offset in the 'Pictures' stream
*/
public byte[] getHeader(){
return header;
public int getOffset(){
return offset;
}
/**
* File offset in the 'Pictures' stream
*
* @return offset in the 'Pictures' stream
*/
public int getOffset(){
return offset;
}
/**
* 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;
}
/**
* 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
* 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;
}
/**
* Compute 16-byte checksum of this picture using MD5 algorithm.
*/
public static byte[] getChecksum(byte[] data) {
MessageDigest sha;
@ -206,8 +130,81 @@ public class PictureData {
* 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;
}
}

View File

@ -620,7 +620,7 @@ public class SlideShow
* @param format the format of the picture. One of constans defined in the <code>Picture</code> class.
* @return the index to this picture (1 based).
*/
public int addPicture(byte[] data, int format) {
public int addPicture(byte[] data, int format) throws IOException {
byte[] uid = PictureData.getChecksum(data);
EscherContainerRecord bstore;
@ -652,14 +652,23 @@ public class SlideShow
}
}
PictureData pict = PictureData.create(format);
pict.setData(data);
pict.setOffset(offset);
EscherBSERecord bse = new EscherBSERecord();
bse.setRecordId(EscherBSERecord.RECORD_ID);
bse.setOptions( (short) ( 0x0002 | ( format << 4 ) ) );
bse.setSize(data.length + PictureData.HEADER_SIZE);
bse.setSize(pict.getRawData().length + 8);
bse.setUid(uid);
bse.setBlipTypeMacOS((byte)format);
bse.setBlipTypeWin32((byte)format);
if (format == Picture.EMF) bse.setBlipTypeMacOS((byte)Picture.PICT);
else if (format == Picture.WMF) bse.setBlipTypeMacOS((byte)Picture.PICT);
else if (format == Picture.PICT) bse.setBlipTypeWin32((byte)Picture.WMF);
bse.setRef(1);
bse.setOffset(offset);
@ -667,12 +676,6 @@ public class SlideShow
int count = bstore.getChildRecords().size();
bstore.setOptions((short)( (count << 4) | 0xF ));
PictureData pict = new PictureData();
pict.setUID(uid);
pict.setData(data);
pict.setType(format);
pict.setOffset(offset);
_hslfSlideShow.addPicture(pict);
return count;
@ -685,7 +688,7 @@ public class SlideShow
* @param format the format of the picture. One of constans defined in the <code>Picture</code> class.
* @return the index to this picture (1 based).
*/
public int addPicture(File pict, int format) {
public int addPicture(File pict, int format) throws IOException {
int length = (int)pict.length();
byte[] data = new byte[length];
try {

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.0 KiB

View File

@ -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;
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 setUp() throws Exception {
cwd = new File(System.getProperty("HSLF.testdata.path"));
}
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);
ByteArrayOutputStream out = new ByteArrayOutputStream();
ppt.write(out);
out.close();
ppt = new HSLFSlideShow(new ByteArrayInputStream(out.toByteArray()));
pict = ppt.getPictures();
assertNotNull(pict);
}
public void testAddPictures() throws Exception {
int idx;
Slide slide;
Picture pict;
/**
* Test read/write Macintosh PICT
*/
public void testPICT() throws Exception {
SlideShow ppt = new SlideShow();
idx = ppt.addPicture(new File(dirname + "/clock.jpg"), Picture.JPEG);
slide = ppt.createSlide();
pict = new Picture(idx);
slide.addShape(pict);
idx = ppt.addPicture(new File(dirname + "/painting.png"), Picture.PNG);
pict = new Picture(idx);
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 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.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));
}
/**
* 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();
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);
//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())));
//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));
}
}