diff --git a/src/java/org/apache/poi/ddf/EscherBitmapBlip.java b/src/java/org/apache/poi/ddf/EscherBitmapBlip.java
new file mode 100644
index 0000000000..6222aeceb2
--- /dev/null
+++ b/src/java/org/apache/poi/ddf/EscherBitmapBlip.java
@@ -0,0 +1,139 @@
+package org.apache.poi.ddf;
+
+import org.apache.poi.util.HexDump;
+import org.apache.poi.util.LittleEndian;
+
+import java.io.ByteArrayOutputStream;
+
+/**
+ * @author Glen Stampoultzis
+ * @version $Id$
+ */
+public class EscherBitmapBlip
+ extends EscherBlipRecord
+{
+ public static final short RECORD_ID_JPEG = (short) 0xF018 + 5;
+ public static final short RECORD_ID_PNG = (short) 0xF018 + 6;
+ public static final short RECORD_ID_DIB = (short) 0xF018 + 7;
+
+ private static final int HEADER_SIZE = 8;
+
+ private byte[] field_1_UID;
+ private byte field_2_marker = (byte) 0xFF;
+
+
+ /**
+ * This method deserializes the record from a byte array.
+ *
+ * @param data The byte array containing the escher record information
+ * @param offset The starting offset into data
.
+ * @param recordFactory May be null since this is not a container record.
+ * @return The number of bytes read from the byte array.
+ */
+ public int fillFields( byte[] data, int offset, EscherRecordFactory recordFactory )
+ {
+ int bytesAfterHeader = readHeader( data, offset );
+ int pos = offset + HEADER_SIZE;
+
+ field_1_UID = new byte[16];
+ System.arraycopy( data, pos, field_1_UID, 0, 16 ); pos += 16;
+ field_2_marker = data[pos]; pos++;
+
+ field_pictureData = new byte[bytesAfterHeader - 17];
+ System.arraycopy( data, pos, field_pictureData, 0, field_pictureData.length );
+
+ return bytesAfterHeader + HEADER_SIZE;
+ }
+
+ /**
+ * Serializes the record to an existing byte array.
+ *
+ * @param offset the offset within the byte array
+ * @param data the data array to serialize to
+ * @param listener a listener for begin and end serialization events. This
+ * is useful because the serialization is
+ * hierarchical/recursive and sometimes you need to be able
+ * break into that.
+ * @return the number of bytes written.
+ */
+ public int serialize( int offset, byte[] data, EscherSerializationListener listener )
+ {
+ listener.beforeRecordSerialize(offset, getRecordId(), this);
+
+ LittleEndian.putShort( data, offset, getOptions() );
+ LittleEndian.putShort( data, offset + 2, getRecordId() );
+ LittleEndian.putInt( data, offset + 4, getRecordSize() - HEADER_SIZE );
+ int pos = offset + HEADER_SIZE;
+
+ System.arraycopy( field_1_UID, 0, data, pos, 16 );
+ data[pos + 16] = field_2_marker;
+ System.arraycopy( field_pictureData, 0, data, pos + 17, field_pictureData.length );
+
+ listener.afterRecordSerialize(offset + getRecordSize(), getRecordId(), getRecordSize(), this);
+ return HEADER_SIZE + 16 + 1 + field_pictureData.length;
+ }
+
+ /**
+ * Returns the number of bytes that are required to serialize this record.
+ *
+ * @return Number of bytes
+ */
+ public int getRecordSize()
+ {
+ return 8 + 16 + 1 + field_pictureData.length;
+ }
+
+ public byte[] getUID()
+ {
+ return field_1_UID;
+ }
+
+ public void setUID( byte[] field_1_UID )
+ {
+ this.field_1_UID = field_1_UID;
+ }
+
+ public byte getMarker()
+ {
+ return field_2_marker;
+ }
+
+ public void setMarker( byte field_2_marker )
+ {
+ this.field_2_marker = field_2_marker;
+ }
+
+ public byte[] getPicturedata()
+ {
+ return field_pictureData;
+ }
+
+ public void setPictureData(byte[] pictureData)
+ {
+ field_pictureData = pictureData;
+ }
+
+ public String toString()
+ {
+ String nl = System.getProperty( "line.separator" );
+
+ String extraData;
+ ByteArrayOutputStream b = new ByteArrayOutputStream();
+ try
+ {
+ HexDump.dump( this.field_pictureData, 0, b, 0 );
+ extraData = b.toString();
+ }
+ catch ( Exception e )
+ {
+ extraData = e.toString();
+ }
+ return getClass().getName() + ":" + nl +
+ " RecordId: 0x" + HexDump.toHex( getRecordId() ) + nl +
+ " Options: 0x" + HexDump.toHex( getOptions() ) + nl +
+ " UID: 0x" + HexDump.toHex( field_1_UID ) + nl +
+ " Marker: 0x" + HexDump.toHex( field_2_marker ) + nl +
+ " Extra Data:" + nl + extraData;
+ }
+
+}
diff --git a/src/java/org/apache/poi/hssf/model/DrawingManager2.java b/src/java/org/apache/poi/hssf/model/DrawingManager2.java
new file mode 100644
index 0000000000..5efd53bd0f
--- /dev/null
+++ b/src/java/org/apache/poi/hssf/model/DrawingManager2.java
@@ -0,0 +1,130 @@
+/* ====================================================================
+ Copyright 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.hssf.model;
+
+import org.apache.poi.ddf.EscherDgRecord;
+import org.apache.poi.ddf.EscherDggRecord;
+
+import java.util.List;
+import java.util.ArrayList;
+
+
+/**
+ * Provides utilities to manage drawing groups.
+ *
+ * @author Glen Stampoultzis (glens at apache.org)
+ */
+public class DrawingManager2
+{
+ EscherDggRecord dgg;
+ List drawingGroups = new ArrayList( );
+
+
+ public DrawingManager2( EscherDggRecord dgg )
+ {
+ this.dgg = dgg;
+ }
+
+ public EscherDgRecord createDgRecord()
+ {
+ EscherDgRecord dg = new EscherDgRecord();
+ dg.setRecordId( EscherDgRecord.RECORD_ID );
+ short dgId = findNewDrawingGroupId();
+ dg.setOptions( (short) ( dgId << 4 ) );
+ dg.setNumShapes( 0 );
+ dg.setLastMSOSPID( -1 );
+ drawingGroups.add(dg);
+ dgg.addCluster( dgId, 0 );
+ dgg.setDrawingsSaved( dgg.getDrawingsSaved() + 1 );
+ return dg;
+ }
+
+ /**
+ * Allocates new shape id for the new drawing group id.
+ *
+ * @return a new shape id.
+ */
+ public int allocateShapeId(short drawingGroupId)
+ {
+ dgg.setNumShapesSaved( dgg.getNumShapesSaved() + 1 );
+
+ // Add to existing cluster if space available
+ for (int i = 0; i < dgg.getFileIdClusters().length; i++)
+ {
+ EscherDggRecord.FileIdCluster c = dgg.getFileIdClusters()[i];
+ if (c.getDrawingGroupId() == drawingGroupId && c.getNumShapeIdsUsed() != 1024)
+ {
+ int result = c.getNumShapeIdsUsed() + (1024 * (i+1));
+ c.incrementShapeId();
+ EscherDgRecord dg = getDrawingGroup(drawingGroupId);
+ dg.setNumShapes( dg.getNumShapes() + 1 );
+ dg.setLastMSOSPID( result );
+ if (result >= dgg.getShapeIdMax())
+ dgg.setShapeIdMax( result + 1 );
+ return result;
+ }
+ }
+
+ // Create new cluster
+ dgg.addCluster( drawingGroupId, 0 );
+ dgg.getFileIdClusters()[dgg.getFileIdClusters().length-1].incrementShapeId();
+ EscherDgRecord dg = getDrawingGroup(drawingGroupId);
+ dg.setNumShapes( dg.getNumShapes() + 1 );
+ int result = (1024 * dgg.getFileIdClusters().length);
+ dg.setLastMSOSPID( result );
+ if (result >= dgg.getShapeIdMax())
+ dgg.setShapeIdMax( result + 1 );
+ return result;
+ }
+
+ //////////// Non-public methods /////////////
+ short findNewDrawingGroupId()
+ {
+ short dgId = 1;
+ while ( drawingGroupExists( dgId ) )
+ dgId++;
+ return dgId;
+ }
+
+ EscherDgRecord getDrawingGroup(int drawingGroupId)
+ {
+ return (EscherDgRecord) drawingGroups.get(drawingGroupId-1);
+ }
+
+ boolean drawingGroupExists( short dgId )
+ {
+ for ( int i = 0; i < dgg.getFileIdClusters().length; i++ )
+ {
+ if ( dgg.getFileIdClusters()[i].getDrawingGroupId() == dgId )
+ return true;
+ }
+ return false;
+ }
+
+ int findFreeSPIDBlock()
+ {
+ int max = dgg.getShapeIdMax();
+ int next = ( ( max / 1024 ) + 1 ) * 1024;
+ return next;
+ }
+
+ public EscherDggRecord getDgg()
+ {
+ return dgg;
+ }
+
+}
diff --git a/src/java/org/apache/poi/hssf/usermodel/HSSFPicture.java b/src/java/org/apache/poi/hssf/usermodel/HSSFPicture.java
new file mode 100644
index 0000000000..7baf6d2fce
--- /dev/null
+++ b/src/java/org/apache/poi/hssf/usermodel/HSSFPicture.java
@@ -0,0 +1,39 @@
+package org.apache.poi.hssf.usermodel;
+
+/**
+ * Represents a escher picture. Eg. A GIF, JPEG etc...
+ *
+ * @author Glen Stampoultzis
+ * @version $Id$
+ */
+public class HSSFPicture
+ extends HSSFSimpleShape
+{
+ public static final int PICTURE_TYPE_EMF = 0; // Windows Enhanced Metafile
+ public static final int PICTURE_TYPE_WMF = 1; // Windows Metafile
+ public static final int PICTURE_TYPE_PICT = 2; // Macintosh PICT
+ public static final int PICTURE_TYPE_JPEG = 3; // JFIF
+ public static final int PICTURE_TYPE_PNG = 4; // PNG
+ public static final int PICTURE_TYPE_DIB = 5; // Windows DIB
+
+ int pictureIndex;
+
+ /**
+ * Constructs a picture object.
+ */
+ HSSFPicture( HSSFShape parent, HSSFAnchor anchor )
+ {
+ super( parent, anchor );
+ setShapeType(OBJECT_TYPE_PICTURE);
+ }
+
+ public int getPictureIndex()
+ {
+ return pictureIndex;
+ }
+
+ public void setPictureIndex( int pictureIndex )
+ {
+ this.pictureIndex = pictureIndex;
+ }
+}
diff --git a/src/java/org/apache/poi/util/ArrayUtil.java b/src/java/org/apache/poi/util/ArrayUtil.java
new file mode 100644
index 0000000000..1d05a5ff36
--- /dev/null
+++ b/src/java/org/apache/poi/util/ArrayUtil.java
@@ -0,0 +1,51 @@
+/* ====================================================================
+ Copyright 2003-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.util;
+
+/**
+ * Utility classes for dealing with arrays.
+ *
+ * @author Glen Stampoultzis
+ * @version $Id$
+ */
+public class ArrayUtil
+{
+ /**
+ * This is really a debugging version of System.arraycopy()
.
+ * Use it to provide better exception messages when copying arrays around.
+ * For production use it's better to use the original for speed.
+ */
+ public static void arraycopy(byte[] src, int src_position, byte[] dst, int dst_position, int length)
+ {
+ if (src_position < 0)
+ throw new IllegalArgumentException("src_position was less than 0. Actual value " + src_position);
+ if (src_position >= src.length)
+ throw new IllegalArgumentException( "src_position was greater than src array size. Tried to write starting at position " + src_position + " but the array length is " + src.length );
+ if (src_position + length > src.length)
+ throw new IllegalArgumentException("src_position + length would overrun the src array. Expected end at " + (src_position + length) + " actual end at " + src.length);
+ if (dst_position < 0)
+ throw new IllegalArgumentException("dst_position was less than 0. Actual value " + dst_position);
+ if (dst_position >= dst.length)
+ throw new IllegalArgumentException( "dst_position was greater than dst array size. Tried to write starting at position " + dst_position + " but the array length is " + dst.length );
+ if (dst_position + length > dst.length)
+ throw new IllegalArgumentException("dst_position + length would overrun the dst array. Expected end at " + (dst_position + length) + " actual end at " + dst.length);
+
+ System.arraycopy( src, src_position, dst, dst_position, length);
+ }
+
+
+}