The escher records themselves

git-svn-id: https://svn.apache.org/repos/asf/jakarta/poi/branches/REL_2_BRANCH@353499 13f79535-47bb-0310-9956-ffa450edef68
This commit is contained in:
Glen Stampoultzis 2004-02-10 22:03:32 +00:00
parent 8e27ed87eb
commit ed9f2b0bac
31 changed files with 5989 additions and 0 deletions

View File

@ -0,0 +1,114 @@
package org.apache.poi.ddf;
import org.apache.poi.hssf.record.RecordFormatException;
import java.lang.reflect.Constructor;
import java.util.HashMap;
import java.util.Map;
/**
* Generates escher records when provided the byte array containing those records.
*
* @author Glen Stampoultzis
* @see EscherRecordFactory
*/
public class DefaultEscherRecordFactory
implements EscherRecordFactory
{
private static Class[] escherRecordClasses = {
EscherBSERecord.class, EscherOptRecord.class, EscherClientAnchorRecord.class, EscherDgRecord.class,
EscherSpgrRecord.class, EscherSpRecord.class, EscherClientDataRecord.class, EscherDggRecord.class,
EscherSplitMenuColorsRecord.class, EscherChildAnchorRecord.class, EscherTextboxRecord.class
};
private static Map recordsMap = recordsToMap( escherRecordClasses );
/**
* Creates an instance of the escher record factory
*/
public DefaultEscherRecordFactory()
{
}
/**
* Generates an escher record including the any children contained under that record.
* An exception is thrown if the record could not be generated.
*
* @param data The byte array containing the records
* @param offset The starting offset into the byte array
* @return The generated escher record
*/
public EscherRecord createRecord( byte[] data, int offset )
{
EscherRecord.EscherRecordHeader header = EscherRecord.EscherRecordHeader.readHeader( data, offset );
if ( ( header.getOptions() & (short) 0x000F ) == (short) 0x000F )
{
EscherContainerRecord r = new EscherContainerRecord();
r.setRecordId( header.getRecordId() );
r.setOptions( header.getOptions() );
return r;
}
else if ( header.getRecordId() >= EscherBlipRecord.RECORD_ID_START && header.getRecordId() <= EscherBlipRecord.RECORD_ID_END )
{
EscherBlipRecord r = new EscherBlipRecord();
r.setRecordId( header.getRecordId() );
r.setOptions( header.getOptions() );
return r;
}
else
{
Constructor recordConstructor = (Constructor) recordsMap.get( new Short( header.getRecordId() ) );
EscherRecord escherRecord = null;
if ( recordConstructor != null )
{
try
{
escherRecord = (EscherRecord) recordConstructor.newInstance( new Object[]{} );
escherRecord.setRecordId( header.getRecordId() );
escherRecord.setOptions( header.getOptions() );
}
catch ( Exception e )
{
escherRecord = null;
}
}
return escherRecord == null ? new UnknownEscherRecord() : escherRecord;
}
}
/**
* Converts from a list of classes into a map that contains the record id as the key and
* the Constructor in the value part of the map. It does this by using reflection to look up
* the RECORD_ID field then using reflection again to find a reference to the constructor.
*
* @param records The records to convert
* @return The map containing the id/constructor pairs.
*/
private static Map recordsToMap( Class[] records )
{
Map result = new HashMap();
Constructor constructor;
for ( int i = 0; i < records.length; i++ )
{
Class record = null;
short sid = 0;
record = records[i];
try
{
sid = record.getField( "RECORD_ID" ).getShort( null );
constructor = record.getConstructor( new Class[]
{
} );
}
catch ( Exception illegalArgumentException )
{
throw new RecordFormatException(
"Unable to determine record types" );
}
result.put( new Short( sid ), constructor );
}
return result;
}
}

View File

@ -0,0 +1,162 @@
package org.apache.poi.ddf;
import org.apache.poi.util.LittleEndian;
import org.apache.poi.util.HexDump;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
/**
* Escher array properties are the most wierd construction ever invented
* with all sorts of special cases. I'm hopeful I've got them all.
*
* @author Glen Stampoultzis (glens at superlinksoftware.com)
*/
public class EscherArrayProperty
extends EscherComplexProperty
{
private static final int FIXED_SIZE = 3 * 2;
public EscherArrayProperty( short id, byte[] complexData )
{
super( id, checkComplexData(complexData) );
}
public EscherArrayProperty( short propertyNumber, boolean isBlipId, byte[] complexData )
{
super( propertyNumber, isBlipId, checkComplexData(complexData) );
}
private static byte[] checkComplexData( byte[] complexData )
{
if (complexData == null || complexData.length == 0)
complexData = new byte[6];
return complexData;
}
public int getNumberOfElementsInArray()
{
return LittleEndian.getUShort( complexData, 0 );
}
public void setNumberOfElementsInArray( int numberOfElements )
{
int expectedArraySize = numberOfElements * getActualSizeOfElements(getSizeOfElements()) + FIXED_SIZE;
if ( expectedArraySize != complexData.length )
{
byte[] newArray = new byte[expectedArraySize];
System.arraycopy( complexData, 0, newArray, 0, complexData.length );
complexData = newArray;
}
LittleEndian.putShort( complexData, 0, (short) numberOfElements );
}
public int getNumberOfElementsInMemory()
{
return LittleEndian.getUShort( complexData, 2 );
}
public void setNumberOfElementsInMemory( int numberOfElements )
{
int expectedArraySize = numberOfElements * getActualSizeOfElements(getSizeOfElements()) + FIXED_SIZE;
if ( expectedArraySize != complexData.length )
{
byte[] newArray = new byte[expectedArraySize];
System.arraycopy( complexData, 0, newArray, 0, expectedArraySize );
complexData = newArray;
}
LittleEndian.putShort( complexData, 2, (short) numberOfElements );
}
public short getSizeOfElements()
{
return LittleEndian.getShort( complexData, 4 );
}
public void setSizeOfElements( int sizeOfElements )
{
LittleEndian.putShort( complexData, 4, (short) sizeOfElements );
int expectedArraySize = getNumberOfElementsInArray() * getActualSizeOfElements(getSizeOfElements()) + FIXED_SIZE;
if ( expectedArraySize != complexData.length )
{
// Keep just the first 6 bytes. The rest is no good to us anyway.
byte[] newArray = new byte[expectedArraySize];
System.arraycopy( complexData, 0, newArray, 0, 6 );
complexData = newArray;
}
}
public byte[] getElement( int index )
{
int actualSize = getActualSizeOfElements(getSizeOfElements());
byte[] result = new byte[actualSize];
System.arraycopy(complexData, FIXED_SIZE + index * actualSize, result, 0, result.length );
return result;
}
public void setElement( int index, byte[] element )
{
int actualSize = getActualSizeOfElements(getSizeOfElements());
System.arraycopy( element, 0, complexData, FIXED_SIZE + index * actualSize, actualSize);
}
public String toString()
{
String nl = System.getProperty("line.separator");
StringBuffer results = new StringBuffer();
results.append(" {EscherArrayProperty:" + nl);
results.append(" Num Elements: " + getNumberOfElementsInArray() + nl);
results.append(" Num Elements In Memory: " + getNumberOfElementsInMemory() + nl);
results.append(" Size of elements: " + getSizeOfElements() + nl);
for (int i = 0; i < getNumberOfElementsInArray(); i++)
{
results.append(" Element " + i + ": " + HexDump.toHex(getElement(i)) + nl);
}
results.append("}" + nl);
return "propNum: " + getPropertyNumber()
+ ", propName: " + EscherProperties.getPropertyName( getPropertyNumber() )
+ ", complex: " + isComplex()
+ ", blipId: " + isBlipId()
+ ", data: " + nl + results.toString();
}
/**
* We have this method because the way in which arrays in escher works
* is screwed for seemly arbitary reasons. While most properties are
* fairly consistent and have a predictable array size, escher arrays
* have special cases.
*
* @param data The data array containing the escher array information
* @param offset The offset into the array to start reading from.
* @return the number of bytes used by this complex property.
*/
public int setArrayData( byte[] data, int offset )
{
short numElements = LittleEndian.getShort(data, offset);
short numReserved = LittleEndian.getShort(data, offset + 2);
short sizeOfElements = LittleEndian.getShort(data, offset + 4);
int arraySize = getActualSizeOfElements(sizeOfElements) * numElements;
if (arraySize == complexData.length)
complexData = new byte[arraySize + 6]; // Calculation missing the header for some reason
System.arraycopy(data, offset, complexData, 0, complexData.length );
return complexData.length;
}
/**
* Sometimes the element size is stored as a negative number. We
* negate it and shift it to get the real value.
*/
public static int getActualSizeOfElements(short sizeOfElements)
{
if (sizeOfElements < 0)
return (short) ( ( -sizeOfElements ) >> 2 );
else
return sizeOfElements;
}
}

View File

@ -0,0 +1,383 @@
package org.apache.poi.ddf;
import org.apache.poi.util.LittleEndian;
import org.apache.poi.util.HexDump;
import java.io.ByteArrayOutputStream;
import java.io.PrintWriter;
/**
* The BSE record is related closely to the <code>EscherBlipRecord</code> and stores
* extra information about the blip.
*
* @author Glen Stampoultzis
* @see EscherBlipRecord
*/
public class EscherBSERecord
extends EscherRecord
{
public static final short RECORD_ID = (short) 0xF007;
public static final String RECORD_DESCRIPTION = "MsofbtBSE";
public static final byte BT_ERROR = 0;
public static final byte BT_UNKNOWN = 1;
public static final byte BT_EMF = 2;
public static final byte BT_WMF = 3;
public static final byte BT_PICT = 4;
public static final byte BT_JPEG = 5;
public static final byte BT_PNG = 6;
public static final byte BT_DIB = 7;
private byte field_1_blipTypeWin32;
private byte field_2_blipTypeMacOS;
private byte[] field_3_uid; // 16 bytes
private short field_4_tag;
private int field_5_size;
private int field_6_ref;
private int field_7_offset;
private byte field_8_usage;
private byte field_9_name;
private byte field_10_unused2;
private byte field_11_unused3;
private byte[] remainingData;
/**
* 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 <code>data</code>.
* @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 bytesRemaining = readHeader( data, offset );
int pos = offset + 8;
field_1_blipTypeWin32 = data[pos];
field_2_blipTypeMacOS = data[pos + 1];
System.arraycopy( data, pos + 2, field_3_uid = new byte[16], 0, 16 );
field_4_tag = LittleEndian.getShort( data, pos + 18 );
field_5_size = LittleEndian.getInt( data, pos + 20 );
field_6_ref = LittleEndian.getInt( data, pos + 24 );
field_7_offset = LittleEndian.getInt( data, pos + 28 );
field_8_usage = data[pos + 32];
field_9_name = data[pos + 33];
field_10_unused2 = data[pos + 34];
field_11_unused3 = data[pos + 35];
bytesRemaining -= 36;
remainingData = new byte[bytesRemaining];
System.arraycopy( data, pos + 36, remainingData, 0, bytesRemaining );
return bytesRemaining + 8 + 36;
}
/**
* This method serializes this escher record into a byte array.
*
* @param offset The offset into <code>data</code> to start writing the record data to.
* @param data The byte array to serialize to.
* @param listener A listener to retrieve start and end callbacks. Use a <code>NullEscherSerailizationListener</code> to ignore these events.
* @return The number of bytes written.
* @see NullEscherSerializationListener
*/
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() );
int remainingBytes = remainingData.length + 36;
LittleEndian.putInt( data, offset + 4, remainingBytes );
data[offset + 8] = field_1_blipTypeWin32;
data[offset + 9] = field_2_blipTypeMacOS;
for ( int i = 0; i < 16; i++ )
data[offset + 10 + i] = field_3_uid[i];
LittleEndian.putShort( data, offset + 26, field_4_tag );
LittleEndian.putInt( data, offset + 28, field_5_size );
LittleEndian.putInt( data, offset + 32, field_6_ref );
LittleEndian.putInt( data, offset + 36, field_7_offset );
data[offset + 40] = field_8_usage;
data[offset + 41] = field_9_name;
data[offset + 42] = field_10_unused2;
data[offset + 43] = field_11_unused3;
System.arraycopy( remainingData, 0, data, offset + 44, remainingData.length );
int pos = offset + 8 + 36 + remainingData.length;
listener.afterRecordSerialize(pos, getRecordId(), pos - offset, this);
return pos - offset;
}
/**
* Returns the number of bytes that are required to serialize this record.
*
* @return Number of bytes
*/
public int getRecordSize()
{
return 8 + 1 + 1 + 16 + 2 + 4 + 4 + 4 + 1 + 1 + 1 + 1 + remainingData.length;
}
/**
* The short name for this record
*/
public String getRecordName()
{
return "BSE";
}
/**
* The expected blip type under windows (failure to match this blip type will result in
* Excel converting to this format).
*/
public byte getBlipTypeWin32()
{
return field_1_blipTypeWin32;
}
/**
* Set the expected win32 blip type
*/
public void setBlipTypeWin32( byte blipTypeWin32 )
{
this.field_1_blipTypeWin32 = blipTypeWin32;
}
/**
* The expected blip type under MacOS (failure to match this blip type will result in
* Excel converting to this format).
*/
public byte getBlipTypeMacOS()
{
return field_2_blipTypeMacOS;
}
/**
* Set the expected MacOS blip type
*/
public void setBlipTypeMacOS( byte blipTypeMacOS )
{
this.field_2_blipTypeMacOS = blipTypeMacOS;
}
/**
* 16 byte MD4 checksum.
*/
public byte[] getUid()
{
return field_3_uid;
}
/**
* 16 byte MD4 checksum.
*/
public void setUid( byte[] uid )
{
this.field_3_uid = uid;
}
/**
* unused
*/
public short getTag()
{
return field_4_tag;
}
/**
* unused
*/
public void setTag( short tag )
{
this.field_4_tag = tag;
}
/**
* Blip size in stream.
*/
public int getSize()
{
return field_5_size;
}
/**
* Blip size in stream.
*/
public void setSize( int size )
{
this.field_5_size = size;
}
/**
* The reference count of this blip.
*/
public int getRef()
{
return field_6_ref;
}
/**
* The reference count of this blip.
*/
public void setRef( int ref )
{
this.field_6_ref = ref;
}
/**
* File offset in the delay stream.
*/
public int getOffset()
{
return field_7_offset;
}
/**
* File offset in the delay stream.
*/
public void setOffset( int offset )
{
this.field_7_offset = offset;
}
/**
* Defines the way this blip is used.
*/
public byte getUsage()
{
return field_8_usage;
}
/**
* Defines the way this blip is used.
*/
public void setUsage( byte usage )
{
this.field_8_usage = usage;
}
/**
* The length in characters of the blip name.
*/
public byte getName()
{
return field_9_name;
}
/**
* The length in characters of the blip name.
*/
public void setName( byte name )
{
this.field_9_name = name;
}
public byte getUnused2()
{
return field_10_unused2;
}
public void setUnused2( byte unused2 )
{
this.field_10_unused2 = unused2;
}
public byte getUnused3()
{
return field_11_unused3;
}
public void setUnused3( byte unused3 )
{
this.field_11_unused3 = unused3;
}
/**
* Any remaining data in this record.
*/
public byte[] getRemainingData()
{
return remainingData;
}
/**
* Any remaining data in this record.
*/
public void setRemainingData( byte[] remainingData )
{
this.remainingData = remainingData;
}
/**
* Calculate the string representation of this object
*/
public String toString()
{
String nl = System.getProperty( "line.separator" );
String extraData;
ByteArrayOutputStream b = new ByteArrayOutputStream();
try
{
HexDump.dump( this.remainingData, 0, b, 0 );
extraData = b.toString();
}
catch ( Exception e )
{
extraData = e.toString();
}
return getClass().getName() + ":" + nl +
" RecordId: 0x" + HexDump.toHex( RECORD_ID ) + nl +
" Options: 0x" + HexDump.toHex( getOptions() ) + nl +
" BlipTypeWin32: " + field_1_blipTypeWin32 + nl +
" BlipTypeMacOS: " + field_2_blipTypeMacOS + nl +
" SUID: " + HexDump.toHex(field_3_uid) + nl +
" Tag: " + field_4_tag + nl +
" Size: " + field_5_size + nl +
" Ref: " + field_6_ref + nl +
" Offset: " + field_7_offset + nl +
" Usage: " + field_8_usage + nl +
" Name: " + field_9_name + nl +
" Unused2: " + field_10_unused2 + nl +
" Unused3: " + field_11_unused3 + nl +
" Extra Data:" + nl + extraData;
}
/**
* Retrieve the string representation given a blip id.
*/
public String getBlipType( byte b )
{
switch ( b )
{
case BT_ERROR:
return " ERROR";
case BT_UNKNOWN:
return " UNKNOWN";
case BT_EMF:
return " EMF";
case BT_WMF:
return " WMF";
case BT_PICT:
return " PICT";
case BT_JPEG:
return " JPEG";
case BT_PNG:
return " PNG";
case BT_DIB:
return " DIB";
default:
if ( b < 32 )
return " NotKnown";
else
return " Client";
}
}
}

View File

@ -0,0 +1,417 @@
package org.apache.poi.ddf;
import org.apache.poi.hssf.record.RecordFormatException;
import org.apache.poi.util.HexDump;
import org.apache.poi.util.LittleEndian;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.InputStream;
import java.io.IOException;
import java.util.zip.InflaterInputStream;
import java.util.zip.DeflaterOutputStream;
/**
* The blip record is used to hold details about large binary objects that occur in escher such
* as JPEG, GIF, PICT and WMF files. The contents of the stream is usually compressed. Inflate
* can be used to decompress the data.
*
* @author Glen Stampoultzis
* @see java.util.zip.Inflater
*/
public class EscherBlipRecord
extends EscherRecord
{
public static final short RECORD_ID_START = (short) 0xF018;
public static final short RECORD_ID_END = (short) 0xF117;
public static final String RECORD_DESCRIPTION = "msofbtBlip";
private static final int HEADER_SIZE = 8;
private byte[] field_1_secondaryUID;
private int field_2_cacheOfSize;
private int field_3_boundaryTop;
private int field_4_boundaryLeft;
private int field_5_boundaryWidth;
private int field_6_boundaryHeight;
private int field_7_width;
private int field_8_height;
private int field_9_cacheOfSavedSize;
private byte field_10_compressionFlag;
private byte field_11_filter;
private byte[] field_12_data;
/**
* 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 <code>data</code>.
* @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;
int size = 0;
field_1_secondaryUID = new byte[16];
System.arraycopy( data, pos + size, field_1_secondaryUID, 0, 16 ); size += 16;
field_2_cacheOfSize = LittleEndian.getInt( data, pos + size );size+=4;
field_3_boundaryTop = LittleEndian.getInt( data, pos + size );size+=4;
field_4_boundaryLeft = LittleEndian.getInt( data, pos + size );size+=4;
field_5_boundaryWidth = LittleEndian.getInt( data, pos + size );size+=4;
field_6_boundaryHeight = LittleEndian.getInt( data, pos + size );size+=4;
field_7_width = LittleEndian.getInt( data, pos + size );size+=4;
field_8_height = LittleEndian.getInt( data, pos + size );size+=4;
field_9_cacheOfSavedSize = LittleEndian.getInt( data, pos + size );size+=4;
field_10_compressionFlag = data[pos + size]; size++;
field_11_filter = data[pos + size]; size++;
int bytesRemaining = bytesAfterHeader - size;
field_12_data = new byte[bytesRemaining];
System.arraycopy(data, pos + size, field_12_data, 0, bytesRemaining);
return bytesRemaining + HEADER_SIZE + bytesAfterHeader;
}
/**
* This method serializes this escher record into a byte array.
*
* @param offset The offset into <code>data</code> to start writing the record data to.
* @param data The byte array to serialize to.
* @param listener A listener to retrieve start and end callbacks. Use a <code>NullEscherSerailizationListener</code> to ignore these events.
* @return The number of bytes written.
*
* @see NullEscherSerializationListener
*/
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() );
int remainingBytes = field_12_data.length + 36;
LittleEndian.putInt( data, offset + 4, remainingBytes );
int pos = offset + HEADER_SIZE;
System.arraycopy(field_1_secondaryUID, 0, data, pos, 16 ); pos += 16;
LittleEndian.putInt( data, pos, field_2_cacheOfSize); pos += 4;
LittleEndian.putInt( data, pos, field_3_boundaryTop); pos += 4;
LittleEndian.putInt( data, pos, field_4_boundaryLeft); pos += 4;
LittleEndian.putInt( data, pos, field_5_boundaryWidth); pos += 4;
LittleEndian.putInt( data, pos, field_6_boundaryHeight); pos += 4;
LittleEndian.putInt( data, pos, field_7_width); pos += 4;
LittleEndian.putInt( data, pos, field_8_height); pos += 4;
LittleEndian.putInt( data, pos, field_9_cacheOfSavedSize); pos += 4;
data[pos++] = field_10_compressionFlag;
data[pos++] = field_11_filter;
System.arraycopy(field_12_data, 0, data, pos, field_12_data.length); pos += field_12_data.length;
listener.afterRecordSerialize(pos, getRecordId(), pos - offset, this);
return pos - offset;
}
/**
* Returns the number of bytes that are required to serialize this record.
*
* @return Number of bytes
*/
public int getRecordSize()
{
return 58 + field_12_data.length;
}
/**
* The short name for this record
*/
public String getRecordName()
{
return "Blip";
}
/**
* Retrieve the secondary UID
*/
public byte[] getSecondaryUID()
{
return field_1_secondaryUID;
}
/**
* Set the secondary UID
*/
public void setSecondaryUID( byte[] field_1_secondaryUID )
{
this.field_1_secondaryUID = field_1_secondaryUID;
}
/**
* Retrieve the cache of the metafile size
*/
public int getCacheOfSize()
{
return field_2_cacheOfSize;
}
/**
* Set the cache of the metafile size
*/
public void setCacheOfSize( int field_2_cacheOfSize )
{
this.field_2_cacheOfSize = field_2_cacheOfSize;
}
/**
* Retrieve the top boundary of the metafile drawing commands
*/
public int getBoundaryTop()
{
return field_3_boundaryTop;
}
/**
* Set the top boundary of the metafile drawing commands
*/
public void setBoundaryTop( int field_3_boundaryTop )
{
this.field_3_boundaryTop = field_3_boundaryTop;
}
/**
* Retrieve the left boundary of the metafile drawing commands
*/
public int getBoundaryLeft()
{
return field_4_boundaryLeft;
}
/**
* Set the left boundary of the metafile drawing commands
*/
public void setBoundaryLeft( int field_4_boundaryLeft )
{
this.field_4_boundaryLeft = field_4_boundaryLeft;
}
/**
* Retrieve the boundary width of the metafile drawing commands
*/
public int getBoundaryWidth()
{
return field_5_boundaryWidth;
}
/**
* Set the boundary width of the metafile drawing commands
*/
public void setBoundaryWidth( int field_5_boundaryWidth )
{
this.field_5_boundaryWidth = field_5_boundaryWidth;
}
/**
* Retrieve the boundary height of the metafile drawing commands
*/
public int getBoundaryHeight()
{
return field_6_boundaryHeight;
}
/**
* Set the boundary height of the metafile drawing commands
*/
public void setBoundaryHeight( int field_6_boundaryHeight )
{
this.field_6_boundaryHeight = field_6_boundaryHeight;
}
/**
* Retrieve the width of the metafile in EMU's (English Metric Units).
*/
public int getWidth()
{
return field_7_width;
}
/**
* Set the width of the metafile in EMU's (English Metric Units).
*/
public void setWidth( int width )
{
this.field_7_width = width;
}
/**
* Retrieve the height of the metafile in EMU's (English Metric Units).
*/
public int getHeight()
{
return field_8_height;
}
/**
* Set the height of the metafile in EMU's (English Metric Units).
*/
public void setHeight( int height )
{
this.field_8_height = height;
}
/**
* Retrieve the cache of the saved size
*/
public int getCacheOfSavedSize()
{
return field_9_cacheOfSavedSize;
}
/**
* Set the cache of the saved size
*/
public void setCacheOfSavedSize( int field_9_cacheOfSavedSize )
{
this.field_9_cacheOfSavedSize = field_9_cacheOfSavedSize;
}
/**
* Is the contents of the blip compressed?
*/
public byte getCompressionFlag()
{
return field_10_compressionFlag;
}
/**
* Set whether the contents of the blip is compressed
*/
public void setCompressionFlag( byte field_10_compressionFlag )
{
this.field_10_compressionFlag = field_10_compressionFlag;
}
/**
* Filter should always be 0
*/
public byte getFilter()
{
return field_11_filter;
}
/**
* Filter should always be 0
*/
public void setFilter( byte field_11_filter )
{
this.field_11_filter = field_11_filter;
}
/**
* The BLIP data
*/
public byte[] getData()
{
return field_12_data;
}
/**
* The BLIP data
*/
public void setData( byte[] field_12_data )
{
this.field_12_data = field_12_data;
}
/**
* The string representation of this record.
*
* @return A string
*/
public String toString()
{
String nl = System.getProperty( "line.separator" );
String extraData;
ByteArrayOutputStream b = new ByteArrayOutputStream();
try
{
HexDump.dump( this.field_12_data, 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 +
" Secondary UID: " + HexDump.toHex( field_1_secondaryUID ) + nl +
" CacheOfSize: " + field_2_cacheOfSize + nl +
" BoundaryTop: " + field_3_boundaryTop + nl +
" BoundaryLeft: " + field_4_boundaryLeft + nl +
" BoundaryWidth: " + field_5_boundaryWidth + nl +
" BoundaryHeight: " + field_6_boundaryHeight + nl +
" X: " + field_7_width + nl +
" Y: " + field_8_height + nl +
" CacheOfSavedSize: " + field_9_cacheOfSavedSize + nl +
" CompressionFlag: " + field_10_compressionFlag + nl +
" Filter: " + field_11_filter + nl +
" Data:" + nl + extraData;
}
/**
* Compress the contents of the provided array
*
* @param data An uncompressed byte array
* @see DeflaterOutputStream#write(int b)
*/
public static byte[] compress( byte[] data )
{
ByteArrayOutputStream out = new ByteArrayOutputStream();
DeflaterOutputStream deflaterOutputStream = new DeflaterOutputStream( out );
try
{
for ( int i = 0; i < data.length; i++ )
deflaterOutputStream.write( data[i] );
}
catch ( IOException e )
{
throw new RecordFormatException( e.toString() );
}
return out.toByteArray();
}
/**
* Decompresses a byte array.
*
* @param data The compressed byte array
* @param pos The starting position into the byte array
* @param length The number of compressed bytes to decompress
* @return An uncompressed byte array
* @see InflaterInputStream#read
*/
public static byte[] decompress( byte[] data, int pos, int length )
{
byte[] compressedData = new byte[length];
System.arraycopy( data, pos + 50, compressedData, 0, length );
InputStream compressedInputStream = new ByteArrayInputStream( compressedData );
InflaterInputStream inflaterInputStream = new InflaterInputStream( compressedInputStream );
ByteArrayOutputStream out = new ByteArrayOutputStream();
int c;
try
{
while ( ( c = inflaterInputStream.read() ) != -1 )
out.write( c );
}
catch ( IOException e )
{
throw new RecordFormatException( e.toString() );
}
return out.toByteArray();
}
}

View File

@ -0,0 +1,50 @@
package org.apache.poi.ddf;
/**
* Represents a boolean property. The actual utility of this property is in doubt because many
* of the properties marked as boolean seem to actually contain special values. In other words
* they're not true booleans.
*
* @author Glen Stampoultzis
* @see EscherSimpleProperty
* @see EscherProperty
*/
public class EscherBoolProperty
extends EscherSimpleProperty
{
/**
* Create an instance of an escher boolean property.
*
* @param propertyNumber The property number
* @param value The 32 bit value of this bool property
*/
public EscherBoolProperty( short propertyNumber, int value )
{
super( propertyNumber, false, false, value );
}
/**
* Whether this boolean property is true
*/
public boolean isTrue()
{
return propertyValue != 0;
}
/**
* Whether this boolean property is false
*/
public boolean isFalse()
{
return propertyValue == 0;
}
// public String toString()
// {
// return "propNum: " + getPropertyNumber()
// + ", complex: " + isComplex()
// + ", blipId: " + isBlipId()
// + ", value: " + (getValue() != 0);
// }
}

View File

@ -0,0 +1,176 @@
package org.apache.poi.ddf;
import org.apache.poi.util.HexDump;
import org.apache.poi.util.LittleEndian;
/**
* The escher child achor record is used to specify the position of a shape under an
* existing group. The first level of shape records use a EscherClientAnchor record instead.
*
* @author Glen Stampoultzis
* @see EscherChildAnchorRecord
*/
public class EscherChildAnchorRecord
extends EscherRecord
{
public static final short RECORD_ID = (short) 0xF00F;
public static final String RECORD_DESCRIPTION = "MsofbtChildAnchor";
private int field_1_dx1;
private int field_2_dy1;
private int field_3_dx2;
private int field_4_dy2;
/**
* 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 <code>data</code>.
* @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 bytesRemaining = readHeader( data, offset );
int pos = offset + 8;
int size = 0;
field_1_dx1 = LittleEndian.getInt( data, pos + size );size+=4;
field_2_dy1 = LittleEndian.getInt( data, pos + size );size+=4;
field_3_dx2 = LittleEndian.getInt( data, pos + size );size+=4;
field_4_dy2 = LittleEndian.getInt( data, pos + size );size+=4;
return 8 + size;
}
/**
* This method serializes this escher record into a byte array.
*
* @param offset The offset into <code>data</code> to start writing the record data to.
* @param data The byte array to serialize to.
* @param listener A listener to retrieve start and end callbacks. Use a <code>NullEscherSerailizationListener</code> to ignore these events.
* @return The number of bytes written.
* @see NullEscherSerializationListener
*/
public int serialize( int offset, byte[] data, EscherSerializationListener listener )
{
listener.beforeRecordSerialize( offset, getRecordId(), this );
int pos = offset;
LittleEndian.putShort( data, pos, getOptions() ); pos += 2;
LittleEndian.putShort( data, pos, getRecordId() ); pos += 2;
LittleEndian.putInt( data, pos, getRecordSize()-8 ); pos += 4;
LittleEndian.putInt( data, pos, field_1_dx1 ); pos += 4;
LittleEndian.putInt( data, pos, field_2_dy1 ); pos += 4;
LittleEndian.putInt( data, pos, field_3_dx2 ); pos += 4;
LittleEndian.putInt( data, pos, field_4_dy2 ); pos += 4;
listener.afterRecordSerialize( pos, getRecordId(), pos - offset, this );
return pos - offset;
}
/**
* Returns the number of bytes that are required to serialize this record.
*
* @return Number of bytes
*/
public int getRecordSize()
{
return 8 + 4 * 4;
}
/**
* The record id for the EscherChildAnchorRecord.
*/
public short getRecordId()
{
return RECORD_ID;
}
/**
* The short name for this record
*/
public String getRecordName()
{
return "ChildAnchor";
}
/**
* The string representation of this record
*/
public String toString()
{
String nl = System.getProperty("line.separator");
return getClass().getName() + ":" + nl +
" RecordId: 0x" + HexDump.toHex(RECORD_ID) + nl +
" Options: 0x" + HexDump.toHex(getOptions()) + nl +
" X1: " + field_1_dx1 + nl +
" Y1: " + field_2_dy1 + nl +
" X2: " + field_3_dx2 + nl +
" Y2: " + field_4_dy2 + nl ;
}
/**
* Retrieves offset within the parent coordinate space for the top left point.
*/
public int getDx1()
{
return field_1_dx1;
}
/**
* Sets offset within the parent coordinate space for the top left point.
*/
public void setDx1( int field_1_dx1 )
{
this.field_1_dx1 = field_1_dx1;
}
/**
* Gets offset within the parent coordinate space for the top left point.
*/
public int getDy1()
{
return field_2_dy1;
}
/**
* Sets offset within the parent coordinate space for the top left point.
*/
public void setDy1( int field_2_dy1 )
{
this.field_2_dy1 = field_2_dy1;
}
/**
* Retrieves offset within the parent coordinate space for the bottom right point.
*/
public int getDx2()
{
return field_3_dx2;
}
/**
* Sets offset within the parent coordinate space for the bottom right point.
*/
public void setDx2( int field_3_dx2 )
{
this.field_3_dx2 = field_3_dx2;
}
/**
* Gets the offset within the parent coordinate space for the bottom right point.
*/
public int getDy2()
{
return field_4_dy2;
}
/**
* Sets the offset within the parent coordinate space for the bottom right point.
*/
public void setDy2( int field_4_dy2 )
{
this.field_4_dy2 = field_4_dy2;
}
}

View File

@ -0,0 +1,317 @@
package org.apache.poi.ddf;
import org.apache.poi.util.HexDump;
import org.apache.poi.util.LittleEndian;
import java.io.ByteArrayOutputStream;
/**
* The escher client anchor specifies which rows and cells the shape is bound to as well as
* the offsets within those cells. Each cell is 1024 units wide by 256 units long regardless
* of the actual size of the cell. The EscherClientAnchorRecord only applies to the top-most
* shapes. Shapes contained in groups are bound using the EscherChildAnchorRecords.
*
* @author Glen Stampoultzis
* @see EscherChildAnchorRecord
*/
public class EscherClientAnchorRecord
extends EscherRecord
{
public static final short RECORD_ID = (short) 0xF010;
public static final String RECORD_DESCRIPTION = "MsofbtClientAnchor";
private short field_1_flag;
private short field_2_col1;
private short field_3_dx1;
private short field_4_row1;
private short field_5_dy1;
private short field_6_col2;
private short field_7_dx2;
private short field_8_row2;
private short field_9_dy2;
private byte[] remainingData;
/**
* 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 <code>data</code>.
* @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 bytesRemaining = readHeader( data, offset );
int pos = offset + 8;
int size = 0;
field_1_flag = LittleEndian.getShort( data, pos + size ); size += 2;
field_2_col1 = LittleEndian.getShort( data, pos + size ); size += 2;
field_3_dx1 = LittleEndian.getShort( data, pos + size ); size += 2;
field_4_row1 = LittleEndian.getShort( data, pos + size ); size += 2;
field_5_dy1 = LittleEndian.getShort( data, pos + size ); size += 2;
field_6_col2 = LittleEndian.getShort( data, pos + size ); size += 2;
field_7_dx2 = LittleEndian.getShort( data, pos + size ); size += 2;
field_8_row2 = LittleEndian.getShort( data, pos + size ); size += 2;
field_9_dy2 = LittleEndian.getShort( data, pos + size ); size += 2;
bytesRemaining -= size;
remainingData = new byte[bytesRemaining];
System.arraycopy( data, pos + size, remainingData, 0, bytesRemaining );
return 8 + size + bytesRemaining;
}
/**
* This method serializes this escher record into a byte array.
*
* @param offset The offset into <code>data</code> to start writing the record data to.
* @param data The byte array to serialize to.
* @param listener A listener to retrieve start and end callbacks. Use a <code>NullEscherSerailizationListener</code> to ignore these events.
* @return The number of bytes written.
* @see NullEscherSerializationListener
*/
public int serialize( int offset, byte[] data, EscherSerializationListener listener )
{
listener.beforeRecordSerialize( offset, getRecordId(), this );
if (remainingData == null) remainingData = new byte[0];
LittleEndian.putShort( data, offset, getOptions() );
LittleEndian.putShort( data, offset + 2, getRecordId() );
int remainingBytes = remainingData.length + 18;
LittleEndian.putInt( data, offset + 4, remainingBytes );
LittleEndian.putShort( data, offset + 8, field_1_flag );
LittleEndian.putShort( data, offset + 10, field_2_col1 );
LittleEndian.putShort( data, offset + 12, field_3_dx1 );
LittleEndian.putShort( data, offset + 14, field_4_row1 );
LittleEndian.putShort( data, offset + 16, field_5_dy1 );
LittleEndian.putShort( data, offset + 18, field_6_col2 );
LittleEndian.putShort( data, offset + 20, field_7_dx2 );
LittleEndian.putShort( data, offset + 22, field_8_row2 );
LittleEndian.putShort( data, offset + 24, field_9_dy2 );
System.arraycopy( remainingData, 0, data, offset + 26, remainingData.length );
int pos = offset + 8 + 18 + remainingData.length;
listener.afterRecordSerialize( pos, getRecordId(), pos - offset, this );
return pos - offset;
}
/**
* Returns the number of bytes that are required to serialize this record.
*
* @return Number of bytes
*/
public int getRecordSize()
{
return 8 + 18 + (remainingData == null ? 0 : remainingData.length);
}
/**
* The record id for this record.
*/
public short getRecordId()
{
return RECORD_ID;
}
/**
* The short name for this record
*/
public String getRecordName()
{
return "ClientAnchor";
}
/**
* Returns the string representation for this record.
*
* @return A string
*/
public String toString()
{
String nl = System.getProperty("line.separator");
String extraData;
ByteArrayOutputStream b = new ByteArrayOutputStream();
try
{
HexDump.dump(this.remainingData, 0, b, 0);
extraData = b.toString();
}
catch ( Exception e )
{
extraData = "error";
}
return getClass().getName() + ":" + nl +
" RecordId: 0x" + HexDump.toHex(RECORD_ID) + nl +
" Options: 0x" + HexDump.toHex(getOptions()) + nl +
" Flag: " + field_1_flag + nl +
" Col1: " + field_2_col1 + nl +
" DX1: " + field_3_dx1 + nl +
" Row1: " + field_4_row1 + nl +
" DY1: " + field_5_dy1 + nl +
" Col2: " + field_6_col2 + nl +
" DX2: " + field_7_dx2 + nl +
" Row2: " + field_8_row2 + nl +
" DY2: " + field_9_dy2 + nl +
" Extra Data:" + nl + extraData;
}
/**
* 0 = Move and size with Cells, 2 = Move but don't size with cells, 3 = Don't move or size with cells.
*/
public short getFlag()
{
return field_1_flag;
}
/**
* 0 = Move and size with Cells, 2 = Move but don't size with cells, 3 = Don't move or size with cells.
*/
public void setFlag( short field_1_flag )
{
this.field_1_flag = field_1_flag;
}
/**
* The column number for the top-left position. 0 based.
*/
public short getCol1()
{
return field_2_col1;
}
/**
* The column number for the top-left position. 0 based.
*/
public void setCol1( short field_2_col1 )
{
this.field_2_col1 = field_2_col1;
}
/**
* The x offset within the top-left cell. Range is from 0 to 1023.
*/
public short getDx1()
{
return field_3_dx1;
}
/**
* The x offset within the top-left cell. Range is from 0 to 1023.
*/
public void setDx1( short field_3_dx1 )
{
this.field_3_dx1 = field_3_dx1;
}
/**
* The row number for the top-left corner of the shape.
*/
public short getRow1()
{
return field_4_row1;
}
/**
* The row number for the top-left corner of the shape.
*/
public void setRow1( short field_4_row1 )
{
this.field_4_row1 = field_4_row1;
}
/**
* The y offset within the top-left corner of the current shape.
*/
public short getDy1()
{
return field_5_dy1;
}
/**
* The y offset within the top-left corner of the current shape.
*/
public void setDy1( short field_5_dy1 )
{
this.field_5_dy1 = field_5_dy1;
}
/**
* The column of the bottom right corner of this shape.
*/
public short getCol2()
{
return field_6_col2;
}
/**
* The column of the bottom right corner of this shape.
*/
public void setCol2( short field_6_col2 )
{
this.field_6_col2 = field_6_col2;
}
/**
* The x offset withing the cell for the bottom-right corner of this shape.
*/
public short getDx2()
{
return field_7_dx2;
}
/**
* The x offset withing the cell for the bottom-right corner of this shape.
*/
public void setDx2( short field_7_dx2 )
{
this.field_7_dx2 = field_7_dx2;
}
/**
* The row number for the bottom-right corner of the current shape.
*/
public short getRow2()
{
return field_8_row2;
}
/**
* The row number for the bottom-right corner of the current shape.
*/
public void setRow2( short field_8_row2 )
{
this.field_8_row2 = field_8_row2;
}
/**
* The y offset withing the cell for the bottom-right corner of this shape.
*/
public short getDy2()
{
return field_9_dy2;
}
/**
* The y offset withing the cell for the bottom-right corner of this shape.
*/
public void setDy2( short field_9_dy2 )
{
this.field_9_dy2 = field_9_dy2;
}
/**
* Any remaining data in the record
*/
public byte[] getRemainingData()
{
return remainingData;
}
/**
* Any remaining data in the record
*/
public void setRemainingData( byte[] remainingData )
{
this.remainingData = remainingData;
}
}

View File

@ -0,0 +1,130 @@
package org.apache.poi.ddf;
import org.apache.poi.util.HexDump;
import org.apache.poi.util.LittleEndian;
import java.io.ByteArrayOutputStream;
/**
* The EscherClientDataRecord is used to store client specific data about the position of a
* shape within a container.
*
* @author Glen Stampoultzis
*/
public class EscherClientDataRecord
extends EscherRecord
{
public static final short RECORD_ID = (short) 0xF011;
public static final String RECORD_DESCRIPTION = "MsofbtClientData";
private byte[] remainingData;
/**
* 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 <code>data</code>.
* @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 bytesRemaining = readHeader( data, offset );
int pos = offset + 8;
remainingData = new byte[bytesRemaining];
System.arraycopy( data, pos, remainingData, 0, bytesRemaining );
return 8 + bytesRemaining;
}
/**
* This method serializes this escher record into a byte array.
*
* @param offset The offset into <code>data</code> to start writing the record data to.
* @param data The byte array to serialize to.
* @param listener A listener to retrieve start and end callbacks. Use a <code>NullEscherSerailizationListener</code> to ignore these events.
* @return The number of bytes written.
* @see NullEscherSerializationListener
*/
public int serialize( int offset, byte[] data, EscherSerializationListener listener )
{
listener.beforeRecordSerialize( offset, getRecordId(), this );
if (remainingData == null) remainingData = new byte[0];
LittleEndian.putShort( data, offset, getOptions() );
LittleEndian.putShort( data, offset + 2, getRecordId() );
LittleEndian.putInt( data, offset + 4, remainingData.length );
System.arraycopy( remainingData, 0, data, offset + 8, remainingData.length );
int pos = offset + 8 + remainingData.length;
listener.afterRecordSerialize( pos, getRecordId(), pos - offset, this );
return pos - offset;
}
/**
* Returns the number of bytes that are required to serialize this record.
*
* @return Number of bytes
*/
public int getRecordSize()
{
return 8 + (remainingData == null ? 0 : remainingData.length);
}
/**
* Returns the identifier of this record.
*/
public short getRecordId()
{
return RECORD_ID;
}
/**
* The short name for this record
*/
public String getRecordName()
{
return "ClientData";
}
/**
* Returns the string representation of this record.
*/
public String toString()
{
String nl = System.getProperty("line.separator");
String extraData;
ByteArrayOutputStream b = new ByteArrayOutputStream();
try
{
HexDump.dump(this.remainingData, 0, b, 0);
extraData = b.toString();
}
catch ( Exception e )
{
extraData = "error";
}
return getClass().getName() + ":" + nl +
" RecordId: 0x" + HexDump.toHex(RECORD_ID) + nl +
" Options: 0x" + HexDump.toHex(getOptions()) + nl +
" Extra Data:" + nl +
extraData;
}
/**
* Any data recording this record.
*/
public byte[] getRemainingData()
{
return remainingData;
}
/**
* Any data recording this record.
*/
public void setRemainingData( byte[] remainingData )
{
this.remainingData = remainingData;
}
}

View File

@ -0,0 +1,152 @@
package org.apache.poi.ddf;
import org.apache.poi.util.LittleEndian;
import org.apache.poi.util.HexDump;
import java.util.Arrays;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
/**
* A complex property differs from a simple property in that the data can not fit inside a 32 bit
* integer. See the specification for more detailed information regarding exactly what is
* stored here.
*
* @author Glen Stampoultzis
*/
public class EscherComplexProperty
extends EscherProperty
{
byte[] complexData = new byte[0];
/**
* Create a complex property using the property id and a byte array containing the complex
* data value.
*
* @param id The id consists of the property number, a flag indicating whether this is a blip id and a flag
* indicating that this is a complex property.
* @param complexData The value of this property.
*/
public EscherComplexProperty( short id, byte[] complexData )
{
super( id );
this.complexData = complexData;
}
/**
* Create a complex property using the property number, a flag to indicate whether this is a
* blip reference and the complex property data.
*
* @param propertyNumber The property number
* @param isBlipId Whether this is a blip id. Should be false.
* @param complexData The value of this complex property.
*/
public EscherComplexProperty( short propertyNumber, boolean isBlipId, byte[] complexData )
{
super( propertyNumber, true, isBlipId );
this.complexData = complexData;
}
/**
* Serializes the simple part of this property. ie the first 6 bytes.
*/
public int serializeSimplePart( byte[] data, int pos )
{
LittleEndian.putShort(data, pos, getId());
LittleEndian.putInt(data, pos + 2, complexData.length);
return 6;
}
/**
* Serializes the complex part of this property
*
* @param data The data array to serialize to
* @param pos The offset within data to start serializing to.
* @return The number of bytes serialized.
*/
public int serializeComplexPart( byte[] data, int pos )
{
System.arraycopy(complexData, 0, data, pos, complexData.length);
return complexData.length;
}
/**
* Get the complex data value.
*/
public byte[] getComplexData()
{
return complexData;
}
/**
* Determine whether this property is equal to another property.
*
* @param o The object to compare to.
* @return True if the objects are equal.
*/
public boolean equals( Object o )
{
if ( this == o ) return true;
if ( !( o instanceof EscherComplexProperty ) ) return false;
final EscherComplexProperty escherComplexProperty = (EscherComplexProperty) o;
if ( !Arrays.equals( complexData, escherComplexProperty.complexData ) ) return false;
return true;
}
/**
* Caclulates the number of bytes required to serialize this property.
*
* @return Number of bytes
*/
public int getPropertySize()
{
return 6 + complexData.length;
}
/**
* Calculates a hashcode for this property.
*/
public int hashCode()
{
return getId() * 11;
}
/**
* Retrieves the string representation for this property.
*/
public String toString()
{
String dataStr;
ByteArrayOutputStream b = new ByteArrayOutputStream();
try
{
HexDump.dump( this.complexData, 0, b, 0 );
dataStr = b.toString();
}
catch ( Exception e )
{
dataStr = e.toString();
}
finally
{
try
{
b.close();
}
catch ( IOException e )
{
e.printStackTrace();
}
}
return "propNum: " + getPropertyNumber()
+ ", propName: " + EscherProperties.getPropertyName( getPropertyNumber() )
+ ", complex: " + isComplex()
+ ", blipId: " + isBlipId()
+ ", data: " + System.getProperty("line.separator") + dataStr;
}
}

View File

@ -0,0 +1,168 @@
package org.apache.poi.ddf;
import org.apache.poi.util.HexDump;
import org.apache.poi.util.LittleEndian;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.io.PrintWriter;
/**
* Escher container records store other escher records as children.
* The container records themselves never store any information beyond
* the standard header used by all escher records. This one record is
* used to represent many different types of records.
*
* @author Glen Stampoultzis
*/
public class EscherContainerRecord extends EscherRecord
{
public static final short DGG_CONTAINER = (short)0xF000;
public static final short BSTORE_CONTAINER = (short)0xF001;
public static final short DG_CONTAINER = (short)0xF002;
public static final short SPGR_CONTAINER = (short)0xF003;
public static final short SP_CONTAINER = (short)0xF004;
public static final short SOLVER_CONTAINER = (short)0xF005;
private List childRecords = new ArrayList();
public int fillFields( byte[] data, int offset, EscherRecordFactory recordFactory )
{
int bytesRemaining = readHeader( data, offset );
int bytesWritten = 8;
offset += 8;
while ( bytesRemaining > 0 && offset < data.length )
{
EscherRecord child = recordFactory.createRecord(data, offset);
int childBytesWritten = child.fillFields( data, offset, recordFactory );
bytesWritten += childBytesWritten;
offset += childBytesWritten;
bytesRemaining -= childBytesWritten;
getChildRecords().add( child );
if (offset >= data.length && bytesRemaining > 0)
{
System.out.println("WARNING: " + bytesRemaining + " bytes remaining but no space left");
}
}
return bytesWritten;
}
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());
int remainingBytes = 0;
for ( Iterator iterator = getChildRecords().iterator(); iterator.hasNext(); )
{
EscherRecord r = (EscherRecord) iterator.next();
remainingBytes += r.getRecordSize();
}
LittleEndian.putInt(data, offset+4, remainingBytes);
int pos = offset+8;
for ( Iterator iterator = getChildRecords().iterator(); iterator.hasNext(); )
{
EscherRecord r = (EscherRecord) iterator.next();
pos += r.serialize(pos, data, listener );
}
listener.afterRecordSerialize( pos, getRecordId(), pos - offset, this );
return pos - offset;
}
public int getRecordSize()
{
int childRecordsSize = 0;
for ( Iterator iterator = getChildRecords().iterator(); iterator.hasNext(); )
{
EscherRecord r = (EscherRecord) iterator.next();
childRecordsSize += r.getRecordSize();
}
return 8 + childRecordsSize;
}
public List getChildRecords()
{
return childRecords;
}
public void setChildRecords( List childRecords )
{
this.childRecords = childRecords;
}
public String getRecordName()
{
switch ((short)getRecordId())
{
case DGG_CONTAINER:
return "DggContainer";
case BSTORE_CONTAINER:
return "BStoreContainer";
case DG_CONTAINER:
return "DgContainer";
case SPGR_CONTAINER:
return "SpgrContainer";
case SP_CONTAINER:
return "SpContainer";
case SOLVER_CONTAINER:
return "SolverContainer";
default:
return "Container 0x" + HexDump.toHex(getRecordId());
}
}
public void display( PrintWriter w, int indent )
{
super.display( w, indent );
for ( Iterator iterator = childRecords.iterator(); iterator.hasNext(); )
{
EscherRecord escherRecord = (EscherRecord) iterator.next();
escherRecord.display( w, indent + 1 );
}
}
public void addChildRecord( EscherRecord record )
{
this.childRecords.add( record );
}
public String toString()
{
String nl = System.getProperty( "line.separator" );
StringBuffer children = new StringBuffer();
if ( getChildRecords().size() > 0 )
{
children.append( " children: " + nl );
for ( Iterator iterator = getChildRecords().iterator(); iterator.hasNext(); )
{
EscherRecord record = (EscherRecord) iterator.next();
children.append( record.toString() );
// children.append( nl );
}
}
return getClass().getName() + " (" + getRecordName() + "):" + nl +
" isContainer: " + isContainerRecord() + nl +
" options: 0x" + HexDump.toHex( getOptions() ) + nl +
" recordId: 0x" + HexDump.toHex( getRecordId() ) + nl +
" numchildren: " + getChildRecords().size() + nl +
children.toString();
}
public EscherSpRecord getChildById( short recordId )
{
for ( Iterator iterator = childRecords.iterator(); iterator.hasNext(); )
{
EscherRecord escherRecord = (EscherRecord) iterator.next();
if (escherRecord.getRecordId() == recordId)
return (EscherSpRecord) escherRecord;
}
return null;
}
}

View File

@ -0,0 +1,163 @@
package org.apache.poi.ddf;
import org.apache.poi.util.HexDump;
import org.apache.poi.util.LittleEndian;
/**
* This record simply holds the number of shapes in the drawing group and the
* last shape id used for this drawing group.
*
* @author Glen Stampoultzis
*/
public class EscherDgRecord
extends EscherRecord
{
public static final short RECORD_ID = (short) 0xF008;
public static final String RECORD_DESCRIPTION = "MsofbtDg";
private int field_1_numShapes;
private int field_2_lastMSOSPID;
/**
* 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 <code>data</code>.
* @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 bytesRemaining = readHeader( data, offset );
int pos = offset + 8;
int size = 0;
field_1_numShapes = LittleEndian.getInt( data, pos + size ); size += 4;
field_2_lastMSOSPID = LittleEndian.getInt( data, pos + size ); size += 4;
// bytesRemaining -= size;
// remainingData = new byte[bytesRemaining];
// System.arraycopy( data, pos + size, remainingData, 0, bytesRemaining );
return getRecordSize();
}
/**
* This method serializes this escher record into a byte array.
*
* @param offset The offset into <code>data</code> to start writing the record data to.
* @param data The byte array to serialize to.
* @param listener A listener to retrieve start and end callbacks. Use a <code>NullEscherSerailizationListener</code> to ignore these events.
* @return The number of bytes written.
* @see NullEscherSerializationListener
*/
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, 8 );
LittleEndian.putInt( data, offset + 8, field_1_numShapes );
LittleEndian.putInt( data, offset + 12, field_2_lastMSOSPID );
// System.arraycopy( remainingData, 0, data, offset + 26, remainingData.length );
// int pos = offset + 8 + 18 + remainingData.length;
listener.afterRecordSerialize( offset + 16, getRecordId(), getRecordSize(), this );
return getRecordSize();
}
/**
* Returns the number of bytes that are required to serialize this record.
*
* @return Number of bytes
*/
public int getRecordSize()
{
return 8 + 8;
}
public short getRecordId()
{
return RECORD_ID;
}
/**
* The short name for this record
*/
public String getRecordName()
{
return "Dg";
}
/**
* Returns the string representation of this record.
*/
public String toString()
{
String nl = System.getProperty("line.separator");
// String extraData;
// ByteArrayOutputStream b = new ByteArrayOutputStream();
// try
// {
// HexDump.dump(this.remainingData, 0, b, 0);
// extraData = b.toString();
// }
// catch ( Exception e )
// {
// extraData = "error";
// }
return getClass().getName() + ":" + nl +
" RecordId: 0x" + HexDump.toHex(RECORD_ID) + nl +
" Options: 0x" + HexDump.toHex(getOptions()) + nl +
" NumShapes: " + field_1_numShapes + nl +
" LastMSOSPID: " + field_2_lastMSOSPID + nl;
}
/**
* The number of shapes in this drawing group.
*/
public int getNumShapes()
{
return field_1_numShapes;
}
/**
* The number of shapes in this drawing group.
*/
public void setNumShapes( int field_1_numShapes )
{
this.field_1_numShapes = field_1_numShapes;
}
/**
* The last shape id used in this drawing group.
*/
public int getLastMSOSPID()
{
return field_2_lastMSOSPID;
}
/**
* The last shape id used in this drawing group.
*/
public void setLastMSOSPID( int field_2_lastMSOSPID )
{
this.field_2_lastMSOSPID = field_2_lastMSOSPID;
}
/**
* Gets the drawing group id for this record. This is encoded in the
* instance part of the option record.
*
* @return a drawing group id.
*/
public short getDrawingGroupId()
{
return (short) ( getOptions() >> 4 );
}
public void incrementShapeCount()
{
this.field_1_numShapes++;
}
}

View File

@ -0,0 +1,241 @@
package org.apache.poi.ddf;
import org.apache.poi.util.HexDump;
import org.apache.poi.util.LittleEndian;
import org.apache.poi.hssf.record.RecordFormatException;
import java.lang.reflect.Array;
import java.util.*;
/**
* This record defines the drawing groups used for a particular sheet.
*/
public class EscherDggRecord
extends EscherRecord
{
public static final short RECORD_ID = (short) 0xF006;
public static final String RECORD_DESCRIPTION = "MsofbtDgg";
private int field_1_shapeIdMax;
// private int field_2_numIdClusters; // for some reason the number of clusters is actually the real number + 1
private int field_3_numShapesSaved;
private int field_4_drawingsSaved;
private FileIdCluster[] field_5_fileIdClusters;
public static class FileIdCluster
{
public FileIdCluster( int drawingGroupId, int numShapeIdsUsed )
{
this.field_1_drawingGroupId = drawingGroupId;
this.field_2_numShapeIdsUsed = numShapeIdsUsed;
}
private int field_1_drawingGroupId;
private int field_2_numShapeIdsUsed;
public int getDrawingGroupId()
{
return field_1_drawingGroupId;
}
public int getNumShapeIdsUsed()
{
return field_2_numShapeIdsUsed;
}
public void incrementShapeId( )
{
this.field_2_numShapeIdsUsed++;
}
}
/**
* 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 <code>data</code>.
* @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 bytesRemaining = readHeader( data, offset );
int pos = offset + 8;
int size = 0;
field_1_shapeIdMax = LittleEndian.getInt( data, pos + size );size+=4;
int field_2_numIdClusters = LittleEndian.getInt( data, pos + size );size+=4;
field_3_numShapesSaved = LittleEndian.getInt( data, pos + size );size+=4;
field_4_drawingsSaved = LittleEndian.getInt( data, pos + size );size+=4;
field_5_fileIdClusters = new FileIdCluster[field_2_numIdClusters-1];
for (int i = 0; i < field_2_numIdClusters-1; i++)
{
field_5_fileIdClusters[i] = new FileIdCluster(LittleEndian.getInt( data, pos + size ), LittleEndian.getInt( data, pos + size + 4 ));
size += 8;
}
bytesRemaining -= size;
if (bytesRemaining != 0)
throw new RecordFormatException("Expecting no remaining data but got " + bytesRemaining + " byte(s).");
return 8 + size + bytesRemaining;
}
/**
* This method serializes this escher record into a byte array.
*
* @param offset The offset into <code>data</code> to start writing the record data to.
* @param data The byte array to serialize to.
* @param listener A listener to retrieve start and end callbacks. Use a <code>NullEscherSerailizationListener</code> to ignore these events.
* @return The number of bytes written.
*
* @see NullEscherSerializationListener
*/
public int serialize( int offset, byte[] data, EscherSerializationListener listener )
{
listener.beforeRecordSerialize( offset, getRecordId(), this );
int pos = offset;
LittleEndian.putShort( data, pos, getOptions() ); pos += 2;
LittleEndian.putShort( data, pos, getRecordId() ); pos += 2;
int remainingBytes = getRecordSize() - 8;
LittleEndian.putInt( data, pos, remainingBytes ); pos += 4;
LittleEndian.putInt( data, pos, field_1_shapeIdMax ); pos += 4;
LittleEndian.putInt( data, pos, getNumIdClusters() ); pos += 4;
LittleEndian.putInt( data, pos, field_3_numShapesSaved ); pos += 4;
LittleEndian.putInt( data, pos, field_4_drawingsSaved ); pos += 4;
for ( int i = 0; i < field_5_fileIdClusters.length; i++ )
{
LittleEndian.putInt( data, pos, field_5_fileIdClusters[i].field_1_drawingGroupId ); pos += 4;
LittleEndian.putInt( data, pos, field_5_fileIdClusters[i].field_2_numShapeIdsUsed ); pos += 4;
}
listener.afterRecordSerialize( pos, getRecordId(), getRecordSize(), this );
return getRecordSize();
}
/**
* Returns the number of bytes that are required to serialize this record.
*
* @return Number of bytes
*/
public int getRecordSize()
{
return 8 + 16 + (8 * field_5_fileIdClusters.length);
}
public short getRecordId()
{
return RECORD_ID;
}
/**
* The short name for this record
*/
public String getRecordName()
{
return "Dgg";
}
public String toString()
{
String nl = System.getProperty("line.separator");
// String extraData;
// ByteArrayOutputStream b = new ByteArrayOutputStream();
// try
// {
// HexDump.dump(this.remainingData, 0, b, 0);
// extraData = b.toString();
// }
// catch ( Exception e )
// {
// extraData = "error";
// }
StringBuffer field_5_string = new StringBuffer();
for ( int i = 0; i < field_5_fileIdClusters.length; i++ )
{
field_5_string.append(" DrawingGroupId").append(i+1).append(": ");
field_5_string.append(field_5_fileIdClusters[i].field_1_drawingGroupId);
field_5_string.append(nl);
field_5_string.append(" NumShapeIdsUsed").append(i+1).append(": ");
field_5_string.append(field_5_fileIdClusters[i].field_2_numShapeIdsUsed);
field_5_string.append(nl);
}
return getClass().getName() + ":" + nl +
" RecordId: 0x" + HexDump.toHex(RECORD_ID) + nl +
" Options: 0x" + HexDump.toHex(getOptions()) + nl +
" ShapeIdMax: " + field_1_shapeIdMax + nl +
" NumIdClusters: " + getNumIdClusters() + nl +
" NumShapesSaved: " + field_3_numShapesSaved + nl +
" DrawingsSaved: " + field_4_drawingsSaved + nl +
"" + field_5_string.toString();
}
public int getShapeIdMax()
{
return field_1_shapeIdMax;
}
/**
* The maximum is actually the next available. shape id.
*/
public void setShapeIdMax( int field_1_shapeIdMax )
{
this.field_1_shapeIdMax = field_1_shapeIdMax;
}
public int getNumIdClusters()
{
return field_5_fileIdClusters.length + 1;
}
public int getNumShapesSaved()
{
return field_3_numShapesSaved;
}
public void setNumShapesSaved( int field_3_numShapesSaved )
{
this.field_3_numShapesSaved = field_3_numShapesSaved;
}
public int getDrawingsSaved()
{
return field_4_drawingsSaved;
}
public void setDrawingsSaved( int field_4_drawingsSaved )
{
this.field_4_drawingsSaved = field_4_drawingsSaved;
}
public FileIdCluster[] getFileIdClusters()
{
return field_5_fileIdClusters;
}
public void setFileIdClusters( FileIdCluster[] field_5_fileIdClusters )
{
this.field_5_fileIdClusters = field_5_fileIdClusters;
}
public void addCluster( int dgId, int numShapedUsed )
{
List clusters = new ArrayList(Arrays.asList(field_5_fileIdClusters));
clusters.add(new FileIdCluster(dgId, numShapedUsed));
Collections.sort(clusters, new Comparator()
{
public int compare( Object o1, Object o2 )
{
FileIdCluster f1 = (FileIdCluster) o1;
FileIdCluster f2 = (FileIdCluster) o2;
if (f1.getDrawingGroupId() == f2.getDrawingGroupId())
return 0;
if (f1.getDrawingGroupId() < f2.getDrawingGroupId())
return -1;
else
return +1;
}
} );
field_5_fileIdClusters = (FileIdCluster[]) clusters.toArray( new FileIdCluster[clusters.size()] );
}
}

View File

@ -0,0 +1,990 @@
/* ====================================================================
* The Apache Software License, Version 1.1
*
* Copyright (c) 2003 The Apache Software Foundation. All rights
* reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in
* the documentation and/or other materials provided with the
* distribution.
*
* 3. The end-user documentation included with the redistribution,
* if any, must include the following acknowledgment:
* "This product includes software developed by the
* Apache Software Foundation (http://www.apache.org/)."
* Alternately, this acknowledgment may appear in the software itself,
* if and wherever such third-party acknowledgments normally appear.
*
* 4. The names "Apache" and "Apache Software Foundation" and
* "Apache POI" must not be used to endorse or promote products
* derived from this software without prior written permission. For
* written permission, please contact apache@apache.org.
*
* 5. Products derived from this software may not be called "Apache",
* "Apache POI", nor may "Apache" appear in their name, without
* prior written permission of the Apache Software Foundation.
*
* THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR
* ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
* USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
* OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
* ====================================================================
*
* This software consists of voluntary contributions made by many
* individuals on behalf of the Apache Software Foundation. For more
* information on the Apache Software Foundation, please see
* <http://www.apache.org/>.
*/
package org.apache.poi.ddf;
import org.apache.poi.util.HexDump;
import org.apache.poi.util.HexRead;
import org.apache.poi.util.LittleEndian;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.PrintStream;
import java.util.zip.InflaterInputStream;
/**
* Used to dump the contents of escher records to a PrintStream.
*
* @author Glen Stampoultzis (glens at apache.org)
*/
public class EscherDump
{
public EscherDump()
{
}
/**
* Decodes the escher stream from a byte array and dumps the results to
* a print stream.
*
* @param data The data array containing the escher records.
* @param offset The starting offset within the data array.
* @param size The number of bytes to read.
* @param out The output stream to write the results to.
*
*/
public void dump( byte[] data, int offset, int size, PrintStream out ) throws IOException, LittleEndian.BufferUnderrunException
{
EscherRecordFactory recordFactory = new DefaultEscherRecordFactory();
int pos = offset;
while ( pos < offset + size )
{
EscherRecord r = recordFactory.createRecord(data, pos);
int bytesRead = r.fillFields(data, pos, recordFactory );
System.out.println( r.toString() );
pos += bytesRead;
}
}
/**
* This version of dump is a translation from the open office escher dump routine.
*
* @param maxLength The number of bytes to read
* @param in An input stream to read from.
* @param out An output stream to write to.
*/
public void dumpOld( long maxLength, InputStream in, PrintStream out ) throws IOException, LittleEndian.BufferUnderrunException
{
long remainingBytes = maxLength;
short options; // 4 bits for the version and 12 bits for the instance
short recordId;
int recordBytesRemaining; // including enclosing records
StringBuffer stringBuf = new StringBuffer();
short nDumpSize;
String recordName;
boolean atEOF = false;
while ( !atEOF && ( remainingBytes > 0 ) )
{
stringBuf = new StringBuffer();
options = LittleEndian.readShort( in );
recordId = LittleEndian.readShort( in );
recordBytesRemaining = LittleEndian.readInt( in );
remainingBytes -= 2 + 2 + 4;
switch ( recordId )
{
case (short) 0xF000:
recordName = "MsofbtDggContainer";
break;
case (short) 0xF006:
recordName = "MsofbtDgg";
break;
case (short) 0xF016:
recordName = "MsofbtCLSID";
break;
case (short) 0xF00B:
recordName = "MsofbtOPT";
break;
case (short) 0xF11A:
recordName = "MsofbtColorMRU";
break;
case (short) 0xF11E:
recordName = "MsofbtSplitMenuColors";
break;
case (short) 0xF001:
recordName = "MsofbtBstoreContainer";
break;
case (short) 0xF007:
recordName = "MsofbtBSE";
break;
case (short) 0xF002:
recordName = "MsofbtDgContainer";
break;
case (short) 0xF008:
recordName = "MsofbtDg";
break;
case (short) 0xF118:
recordName = "MsofbtRegroupItem";
break;
case (short) 0xF120:
recordName = "MsofbtColorScheme";
break;
case (short) 0xF003:
recordName = "MsofbtSpgrContainer";
break;
case (short) 0xF004:
recordName = "MsofbtSpContainer";
break;
case (short) 0xF009:
recordName = "MsofbtSpgr";
break;
case (short) 0xF00A:
recordName = "MsofbtSp";
break;
case (short) 0xF00C:
recordName = "MsofbtTextbox";
break;
case (short) 0xF00D:
recordName = "MsofbtClientTextbox";
break;
case (short) 0xF00E:
recordName = "MsofbtAnchor";
break;
case (short) 0xF00F:
recordName = "MsofbtChildAnchor";
break;
case (short) 0xF010:
recordName = "MsofbtClientAnchor";
break;
case (short) 0xF011:
recordName = "MsofbtClientData";
break;
case (short) 0xF11F:
recordName = "MsofbtOleObject";
break;
case (short) 0xF11D:
recordName = "MsofbtDeletedPspl";
break;
case (short) 0xF005:
recordName = "MsofbtSolverContainer";
break;
case (short) 0xF012:
recordName = "MsofbtConnectorRule";
break;
case (short) 0xF013:
recordName = "MsofbtAlignRule";
break;
case (short) 0xF014:
recordName = "MsofbtArcRule";
break;
case (short) 0xF015:
recordName = "MsofbtClientRule";
break;
case (short) 0xF017:
recordName = "MsofbtCalloutRule";
break;
case (short) 0xF119:
recordName = "MsofbtSelection";
break;
case (short) 0xF122:
recordName = "MsofbtUDefProp";
break;
default:
if ( recordId >= (short) 0xF018 && recordId <= (short) 0xF117 )
recordName = "MsofbtBLIP";
else if ( ( options & (short) 0x000F ) == (short) 0x000F )
recordName = "UNKNOWN container";
else
recordName = "UNKNOWN ID";
}
stringBuf.append( " " );
stringBuf.append( HexDump.toHex( recordId ) );
stringBuf.append( " " ).append( recordName ).append( " [" );
stringBuf.append( HexDump.toHex( options ) );
stringBuf.append( ',' );
stringBuf.append( HexDump.toHex( recordBytesRemaining ) );
stringBuf.append( "] instance: " );
stringBuf.append( HexDump.toHex( ( (short) ( options >> 4 ) ) ) );
out.println( stringBuf.toString() );
if ( recordId == (short) 0xF007 && 36 <= remainingBytes && 36 <= recordBytesRemaining )
{ // BSE, FBSE
// ULONG nP = pIn->GetRecPos();
byte n8;
// short n16;
// int n32;
stringBuf = new StringBuffer( " btWin32: " );
n8 = (byte) in.read();
stringBuf.append( HexDump.toHex( n8 ) );
stringBuf.append( getBlipType( n8 ) );
stringBuf.append( " btMacOS: " );
n8 = (byte) in.read();
stringBuf.append( HexDump.toHex( n8 ) );
stringBuf.append( getBlipType( n8 ) );
out.println( stringBuf.toString() );
out.println( " rgbUid:" );
HexDump.dump( in, out, 0, 16 );
out.print( " tag: " );
outHex( 2, in, out );
out.println();
out.print( " size: " );
outHex( 4, in, out );
out.println();
out.print( " cRef: " );
outHex( 4, in, out );
out.println();
out.print( " offs: " );
outHex( 4, in, out );
out.println();
out.print( " usage: " );
outHex( 1, in, out );
out.println();
out.print( " cbName: " );
outHex( 1, in, out );
out.println();
out.print( " unused2: " );
outHex( 1, in, out );
out.println();
out.print( " unused3: " );
outHex( 1, in, out );
out.println();
// subtract the number of bytes we've read
remainingBytes -= 36;
//n -= pIn->GetRecPos() - nP;
recordBytesRemaining = 0; // loop to MsofbtBLIP
}
else if ( recordId == (short) 0xF010 && 0x12 <= remainingBytes && 0x12 <= recordBytesRemaining )
{ // ClientAnchor
//ULONG nP = pIn->GetRecPos();
// short n16;
out.print( " Flag: " );
outHex( 2, in, out );
out.println();
out.print( " Col1: " );
outHex( 2, in, out );
out.print( " dX1: " );
outHex( 2, in, out );
out.print( " Row1: " );
outHex( 2, in, out );
out.print( " dY1: " );
outHex( 2, in, out );
out.println();
out.print( " Col2: " );
outHex( 2, in, out );
out.print( " dX2: " );
outHex( 2, in, out );
out.print( " Row2: " );
outHex( 2, in, out );
out.print( " dY2: " );
outHex( 2, in, out );
out.println();
remainingBytes -= 18;
recordBytesRemaining -= 18;
}
else if ( recordId == (short) 0xF00B || recordId == (short) 0xF122 )
{ // OPT
int nComplex = 0;
out.println( " PROPID VALUE" );
while ( recordBytesRemaining >= 6 + nComplex && remainingBytes >= 6 + nComplex )
{
short n16;
int n32;
n16 = LittleEndian.readShort( in );
n32 = LittleEndian.readInt( in );
recordBytesRemaining -= 6;
remainingBytes -= 6;
out.print( " " );
out.print( HexDump.toHex( n16 ) );
out.print( " (" );
int propertyId = n16 & (short) 0x3FFF;
out.print( " " + propertyId );
if ( ( n16 & (short) 0x8000 ) == 0 )
{
if ( ( n16 & (short) 0x4000 ) != 0 )
out.print( ", fBlipID" );
out.print( ") " );
out.print( HexDump.toHex( n32 ) );
if ( ( n16 & (short) 0x4000 ) == 0 )
{
out.print( " (" );
out.print( dec1616( n32 ) );
out.print( ')' );
out.print( " {" + propName( (short)propertyId ) + "}" );
}
out.println();
}
else
{
out.print( ", fComplex) " );
out.print( HexDump.toHex( n32 ) );
out.print( " - Complex prop len" );
out.println( " {" + propName( (short)propertyId ) + "}" );
nComplex += n32;
}
}
// complex property data
while ( ( nComplex & remainingBytes ) > 0 )
{
nDumpSize = ( nComplex > (int) remainingBytes ) ? (short) remainingBytes : (short) nComplex;
HexDump.dump( in, out, 0, nDumpSize );
nComplex -= nDumpSize;
recordBytesRemaining -= nDumpSize;
remainingBytes -= nDumpSize;
}
}
else if ( recordId == (short) 0xF012 )
{
out.print( " Connector rule: " );
out.print( LittleEndian.readInt( in ) );
out.print( " ShapeID A: " );
out.print( LittleEndian.readInt( in ) );
out.print( " ShapeID B: " );
out.print( LittleEndian.readInt( in ) );
out.print( " ShapeID connector: " );
out.print( LittleEndian.readInt( in ) );
out.print( " Connect pt A: " );
out.print( LittleEndian.readInt( in ) );
out.print( " Connect pt B: " );
out.println( LittleEndian.readInt( in ) );
recordBytesRemaining -= 24;
remainingBytes -= 24;
}
else if ( recordId >= (short) 0xF018 && recordId < (short) 0xF117 )
{
out.println( " Secondary UID: " );
HexDump.dump( in, out, 0, 16 );
out.println( " Cache of size: " + HexDump.toHex( LittleEndian.readInt( in ) ) );
out.println( " Boundary top: " + HexDump.toHex( LittleEndian.readInt( in ) ) );
out.println( " Boundary left: " + HexDump.toHex( LittleEndian.readInt( in ) ) );
out.println( " Boundary width: " + HexDump.toHex( LittleEndian.readInt( in ) ) );
out.println( " Boundary height: " + HexDump.toHex( LittleEndian.readInt( in ) ) );
out.println( " X: " + HexDump.toHex( LittleEndian.readInt( in ) ) );
out.println( " Y: " + HexDump.toHex( LittleEndian.readInt( in ) ) );
out.println( " Cache of saved size: " + HexDump.toHex( LittleEndian.readInt( in ) ) );
out.println( " Compression Flag: " + HexDump.toHex( (byte) in.read() ) );
out.println( " Filter: " + HexDump.toHex( (byte) in.read() ) );
out.println( " Data (after decompression): " );
recordBytesRemaining -= 34 + 16;
remainingBytes -= 34 + 16;
nDumpSize = ( recordBytesRemaining > (int) remainingBytes ) ? (short) remainingBytes : (short) recordBytesRemaining;
byte[] buf = new byte[nDumpSize];
int read = in.read( buf );
while ( read != -1 && read < nDumpSize )
read += in.read( buf, read, buf.length );
ByteArrayInputStream bin = new ByteArrayInputStream( buf );
InputStream in1 = new InflaterInputStream( bin );
int bytesToDump = -1;
HexDump.dump( in1, out, 0, bytesToDump );
recordBytesRemaining -= nDumpSize;
remainingBytes -= nDumpSize;
}
boolean isContainer = ( options & (short) 0x000F ) == (short) 0x000F;
if ( isContainer && remainingBytes >= 0 )
{ // Container
if ( recordBytesRemaining <= (int) remainingBytes )
out.println( " completed within" );
else
out.println( " continued elsewhere" );
}
else if ( remainingBytes >= 0 )
// -> 0x0000 ... 0x0FFF
{
nDumpSize = ( recordBytesRemaining > (int) remainingBytes ) ? (short) remainingBytes : (short) recordBytesRemaining;
if ( nDumpSize != 0 )
{
HexDump.dump( in, out, 0, nDumpSize );
remainingBytes -= nDumpSize;
}
}
else
out.println( " >> OVERRUN <<" );
}
}
/**
* Returns a property name given a property id. This is used only by the
* old escher dump routine.
*
* @param propertyId The property number for the name
* @return A descriptive name.
*/
private String propName( short propertyId )
{
class PropName {
public PropName( int id, String name )
{
this.id = id;
this.name = name;
}
int id;
String name;
}
final PropName[] props = new PropName[] {
new PropName(4, "transform.rotation"),
new PropName(119, "protection.lockrotation"),
new PropName(120, "protection.lockaspectratio"),
new PropName(121, "protection.lockposition"),
new PropName(122, "protection.lockagainstselect"),
new PropName(123, "protection.lockcropping"),
new PropName(124, "protection.lockvertices"),
new PropName(125, "protection.locktext"),
new PropName(126, "protection.lockadjusthandles"),
new PropName(127, "protection.lockagainstgrouping"),
new PropName(128, "text.textid"),
new PropName(129, "text.textleft"),
new PropName(130, "text.texttop"),
new PropName(131, "text.textright"),
new PropName(132, "text.textbottom"),
new PropName(133, "text.wraptext"),
new PropName(134, "text.scaletext"),
new PropName(135, "text.anchortext"),
new PropName(136, "text.textflow"),
new PropName(137, "text.fontrotation"),
new PropName(138, "text.idofnextshape"),
new PropName(139, "text.bidir"),
new PropName(187, "text.singleclickselects"),
new PropName(188, "text.usehostmargins"),
new PropName(189, "text.rotatetextwithshape"),
new PropName(190, "text.sizeshapetofittext"),
new PropName(191, "text.sizetexttofitshape"),
new PropName(192, "geotext.unicode"),
new PropName(193, "geotext.rtftext"),
new PropName(194, "geotext.alignmentoncurve"),
new PropName(195, "geotext.defaultpointsize"),
new PropName(196, "geotext.textspacing"),
new PropName(197, "geotext.fontfamilyname"),
new PropName(240, "geotext.reverseroworder"),
new PropName(241, "geotext.hastexteffect"),
new PropName(242, "geotext.rotatecharacters"),
new PropName(243, "geotext.kerncharacters"),
new PropName(244, "geotext.tightortrack"),
new PropName(245, "geotext.stretchtofitshape"),
new PropName(246, "geotext.charboundingbox"),
new PropName(247, "geotext.scaletextonpath"),
new PropName(248, "geotext.stretchcharheight"),
new PropName(249, "geotext.nomeasurealongpath"),
new PropName(250, "geotext.boldfont"),
new PropName(251, "geotext.italicfont"),
new PropName(252, "geotext.underlinefont"),
new PropName(253, "geotext.shadowfont"),
new PropName(254, "geotext.smallcapsfont"),
new PropName(255, "geotext.strikethroughfont"),
new PropName(256, "blip.cropfromtop"),
new PropName(257, "blip.cropfrombottom"),
new PropName(258, "blip.cropfromleft"),
new PropName(259, "blip.cropfromright"),
new PropName(260, "blip.bliptodisplay"),
new PropName(261, "blip.blipfilename"),
new PropName(262, "blip.blipflags"),
new PropName(263, "blip.transparentcolor"),
new PropName(264, "blip.contrastsetting"),
new PropName(265, "blip.brightnesssetting"),
new PropName(266, "blip.gamma"),
new PropName(267, "blip.pictureid"),
new PropName(268, "blip.doublemod"),
new PropName(269, "blip.picturefillmod"),
new PropName(270, "blip.pictureline"),
new PropName(271, "blip.printblip"),
new PropName(272, "blip.printblipfilename"),
new PropName(273, "blip.printflags"),
new PropName(316, "blip.nohittestpicture"),
new PropName(317, "blip.picturegray"),
new PropName(318, "blip.picturebilevel"),
new PropName(319, "blip.pictureactive"),
new PropName(320, "geometry.left"),
new PropName(321, "geometry.top"),
new PropName(322, "geometry.right"),
new PropName(323, "geometry.bottom"),
new PropName(324, "geometry.shapepath"),
new PropName(325, "geometry.vertices"),
new PropName(326, "geometry.segmentinfo"),
new PropName(327, "geometry.adjustvalue"),
new PropName(328, "geometry.adjust2value"),
new PropName(329, "geometry.adjust3value"),
new PropName(330, "geometry.adjust4value"),
new PropName(331, "geometry.adjust5value"),
new PropName(332, "geometry.adjust6value"),
new PropName(333, "geometry.adjust7value"),
new PropName(334, "geometry.adjust8value"),
new PropName(335, "geometry.adjust9value"),
new PropName(336, "geometry.adjust10value"),
new PropName(378, "geometry.shadowOK"),
new PropName(379, "geometry.3dok"),
new PropName(380, "geometry.lineok"),
new PropName(381, "geometry.geotextok"),
new PropName(382, "geometry.fillshadeshapeok"),
new PropName(383, "geometry.fillok"),
new PropName(384, "fill.filltype"),
new PropName(385, "fill.fillcolor"),
new PropName(386, "fill.fillopacity"),
new PropName(387, "fill.fillbackcolor"),
new PropName(388, "fill.backopacity"),
new PropName(389, "fill.crmod"),
new PropName(390, "fill.patterntexture"),
new PropName(391, "fill.blipfilename"),
new PropName(392, "fill.blipflags"),
new PropName(393, "fill.width"),
new PropName(394, "fill.height"),
new PropName(395, "fill.angle"),
new PropName(396, "fill.focus"),
new PropName(397, "fill.toleft"),
new PropName(398, "fill.totop"),
new PropName(399, "fill.toright"),
new PropName(400, "fill.tobottom"),
new PropName(401, "fill.rectleft"),
new PropName(402, "fill.recttop"),
new PropName(403, "fill.rectright"),
new PropName(404, "fill.rectbottom"),
new PropName(405, "fill.dztype"),
new PropName(406, "fill.shadepreset"),
new PropName(407, "fill.shadecolors"),
new PropName(408, "fill.originx"),
new PropName(409, "fill.originy"),
new PropName(410, "fill.shapeoriginx"),
new PropName(411, "fill.shapeoriginy"),
new PropName(412, "fill.shadetype"),
new PropName(443, "fill.filled"),
new PropName(444, "fill.hittestfill"),
new PropName(445, "fill.shape"),
new PropName(446, "fill.userect"),
new PropName(447, "fill.nofillhittest"),
new PropName(448, "linestyle.color"),
new PropName(449, "linestyle.opacity"),
new PropName(450, "linestyle.backcolor"),
new PropName(451, "linestyle.crmod"),
new PropName(452, "linestyle.linetype"),
new PropName(453, "linestyle.fillblip"),
new PropName(454, "linestyle.fillblipname"),
new PropName(455, "linestyle.fillblipflags"),
new PropName(456, "linestyle.fillwidth"),
new PropName(457, "linestyle.fillheight"),
new PropName(458, "linestyle.filldztype"),
new PropName(459, "linestyle.linewidth"),
new PropName(460, "linestyle.linemiterlimit"),
new PropName(461, "linestyle.linestyle"),
new PropName(462, "linestyle.linedashing"),
new PropName(463, "linestyle.linedashstyle"),
new PropName(464, "linestyle.linestartarrowhead"),
new PropName(465, "linestyle.lineendarrowhead"),
new PropName(466, "linestyle.linestartarrowwidth"),
new PropName(467, "linestyle.lineestartarrowlength"),
new PropName(468, "linestyle.lineendarrowwidth"),
new PropName(469, "linestyle.lineendarrowlength"),
new PropName(470, "linestyle.linejoinstyle"),
new PropName(471, "linestyle.lineendcapstyle"),
new PropName(507, "linestyle.arrowheadsok"),
new PropName(508, "linestyle.anyline"),
new PropName(509, "linestyle.hitlinetest"),
new PropName(510, "linestyle.linefillshape"),
new PropName(511, "linestyle.nolinedrawdash"),
new PropName(512, "shadowstyle.type"),
new PropName(513, "shadowstyle.color"),
new PropName(514, "shadowstyle.highlight"),
new PropName(515, "shadowstyle.crmod"),
new PropName(516, "shadowstyle.opacity"),
new PropName(517, "shadowstyle.offsetx"),
new PropName(518, "shadowstyle.offsety"),
new PropName(519, "shadowstyle.secondoffsetx"),
new PropName(520, "shadowstyle.secondoffsety"),
new PropName(521, "shadowstyle.scalextox"),
new PropName(522, "shadowstyle.scaleytox"),
new PropName(523, "shadowstyle.scalextoy"),
new PropName(524, "shadowstyle.scaleytoy"),
new PropName(525, "shadowstyle.perspectivex"),
new PropName(526, "shadowstyle.perspectivey"),
new PropName(527, "shadowstyle.weight"),
new PropName(528, "shadowstyle.originx"),
new PropName(529, "shadowstyle.originy"),
new PropName(574, "shadowstyle.shadow"),
new PropName(575, "shadowstyle.shadowobsured"),
new PropName(576, "perspective.type"),
new PropName(577, "perspective.offsetx"),
new PropName(578, "perspective.offsety"),
new PropName(579, "perspective.scalextox"),
new PropName(580, "perspective.scaleytox"),
new PropName(581, "perspective.scalextoy"),
new PropName(582, "perspective.scaleytox"),
new PropName(583, "perspective.perspectivex"),
new PropName(584, "perspective.perspectivey"),
new PropName(585, "perspective.weight"),
new PropName(586, "perspective.originx"),
new PropName(587, "perspective.originy"),
new PropName(639, "perspective.perspectiveon"),
new PropName(640, "3d.specularamount"),
new PropName(661, "3d.diffuseamount"),
new PropName(662, "3d.shininess"),
new PropName(663, "3d.edgethickness"),
new PropName(664, "3d.extrudeforward"),
new PropName(665, "3d.extrudebackward"),
new PropName(666, "3d.extrudeplane"),
new PropName(667, "3d.extrusioncolor"),
new PropName(648, "3d.crmod"),
new PropName(700, "3d.3deffect"),
new PropName(701, "3d.metallic"),
new PropName(702, "3d.useextrusioncolor"),
new PropName(703, "3d.lightface"),
new PropName(704, "3dstyle.yrotationangle"),
new PropName(705, "3dstyle.xrotationangle"),
new PropName(706, "3dstyle.rotationaxisx"),
new PropName(707, "3dstyle.rotationaxisy"),
new PropName(708, "3dstyle.rotationaxisz"),
new PropName(709, "3dstyle.rotationangle"),
new PropName(710, "3dstyle.rotationcenterx"),
new PropName(711, "3dstyle.rotationcentery"),
new PropName(712, "3dstyle.rotationcenterz"),
new PropName(713, "3dstyle.rendermode"),
new PropName(714, "3dstyle.tolerance"),
new PropName(715, "3dstyle.xviewpoint"),
new PropName(716, "3dstyle.yviewpoint"),
new PropName(717, "3dstyle.zviewpoint"),
new PropName(718, "3dstyle.originx"),
new PropName(719, "3dstyle.originy"),
new PropName(720, "3dstyle.skewangle"),
new PropName(721, "3dstyle.skewamount"),
new PropName(722, "3dstyle.ambientintensity"),
new PropName(723, "3dstyle.keyx"),
new PropName(724, "3dstyle.keyy"),
new PropName(725, "3dstyle.keyz"),
new PropName(726, "3dstyle.keyintensity"),
new PropName(727, "3dstyle.fillx"),
new PropName(728, "3dstyle.filly"),
new PropName(729, "3dstyle.fillz"),
new PropName(730, "3dstyle.fillintensity"),
new PropName(763, "3dstyle.constrainrotation"),
new PropName(764, "3dstyle.rotationcenterauto"),
new PropName(765, "3dstyle.parallel"),
new PropName(766, "3dstyle.keyharsh"),
new PropName(767, "3dstyle.fillharsh"),
new PropName(769, "shape.master"),
new PropName(771, "shape.connectorstyle"),
new PropName(772, "shape.blackandwhitesettings"),
new PropName(773, "shape.wmodepurebw"),
new PropName(774, "shape.wmodebw"),
new PropName(826, "shape.oleicon"),
new PropName(827, "shape.preferrelativeresize"),
new PropName(828, "shape.lockshapetype"),
new PropName(830, "shape.deleteattachedobject"),
new PropName(831, "shape.backgroundshape"),
new PropName(832, "callout.callouttype"),
new PropName(833, "callout.xycalloutgap"),
new PropName(834, "callout.calloutangle"),
new PropName(835, "callout.calloutdroptype"),
new PropName(836, "callout.calloutdropspecified"),
new PropName(837, "callout.calloutlengthspecified"),
new PropName(889, "callout.iscallout"),
new PropName(890, "callout.calloutaccentbar"),
new PropName(891, "callout.callouttextborder"),
new PropName(892, "callout.calloutminusx"),
new PropName(893, "callout.calloutminusy"),
new PropName(894, "callout.dropauto"),
new PropName(895, "callout.lengthspecified"),
new PropName(896, "groupshape.shapename"),
new PropName(897, "groupshape.description"),
new PropName(898, "groupshape.hyperlink"),
new PropName(899, "groupshape.wrappolygonvertices"),
new PropName(900, "groupshape.wrapdistleft"),
new PropName(901, "groupshape.wrapdisttop"),
new PropName(902, "groupshape.wrapdistright"),
new PropName(903, "groupshape.wrapdistbottom"),
new PropName(904, "groupshape.regroupid"),
new PropName(953, "groupshape.editedwrap"),
new PropName(954, "groupshape.behinddocument"),
new PropName(955, "groupshape.ondblclicknotify"),
new PropName(956, "groupshape.isbutton"),
new PropName(957, "groupshape.1dadjustment"),
new PropName(958, "groupshape.hidden"),
new PropName(959, "groupshape.print"),
};
for ( int i = 0; i < props.length; i++ )
{
if (props[i].id == propertyId)
{
return props[i].name;
}
}
return "unknown property";
}
/**
* Returns the blip description given a blip id.
*
* @param b blip id
* @return A description.
*/
private String getBlipType( byte b )
{
switch ( b )
{
case 0:
return " ERROR";
case 1:
return " UNKNOWN";
case 2:
return " EMF";
case 3:
return " WMF";
case 4:
return " PICT";
case 5:
return " JPEG";
case 6:
return " PNG";
case 7:
return " DIB";
default:
if ( b < 32 )
return " NotKnown";
else
return " Client";
}
}
/**
* Straight conversion from OO. Converts a type of float.
*/
private String dec1616( int n32 )
{
String result = "";
result += (short) ( n32 >> 16 );
result += '.';
result += (short) ( n32 & (short) 0xFFFF );
return result;
}
/**
* Dumps out a hex value by reading from a input stream.
*
* @param bytes How many bytes this hex value consists of.
* @param in The stream to read the hex value from.
* @param out The stream to write the nicely formatted hex value to.
*/
private void outHex( int bytes, InputStream in, PrintStream out ) throws IOException, LittleEndian.BufferUnderrunException
{
switch ( bytes )
{
case 1:
out.print( HexDump.toHex( (byte) in.read() ) );
break;
case 2:
out.print( HexDump.toHex( LittleEndian.readShort( in ) ) );
break;
case 4:
out.print( HexDump.toHex( LittleEndian.readInt( in ) ) );
break;
default:
throw new IOException( "Unable to output variable of that width" );
}
}
/**
* A simple test stub.
*/
public static void main( String[] args ) throws IOException
{
String dump =
"0F 00 00 F0 89 07 00 00 00 00 06 F0 18 00 00 00 " +
"05 04 00 00 02 00 00 00 05 00 00 00 01 00 00 00 " +
"01 00 00 00 05 00 00 00 4F 00 01 F0 2F 07 00 00 " +
"42 00 07 F0 B7 01 00 00 03 04 3F 14 AE 6B 0F 65 " +
"B0 48 BF 5E 94 63 80 E8 91 73 FF 00 93 01 00 00 " +
"01 00 00 00 00 00 00 00 00 00 FF FF 20 54 1C F0 " +
"8B 01 00 00 3F 14 AE 6B 0F 65 B0 48 BF 5E 94 63 " +
"80 E8 91 73 92 0E 00 00 00 00 00 00 00 00 00 00 " +
"D1 07 00 00 DD 05 00 00 4A AD 6F 00 8A C5 53 00 " +
"59 01 00 00 00 FE 78 9C E3 9B C4 00 04 AC 77 D9 " +
"2F 32 08 32 FD E7 61 F8 FF 0F C8 FD 05 C5 30 19 " +
"10 90 63 90 FA 0F 06 0C 8C 0C 5C 70 19 43 30 EB " +
"0E FB 05 86 85 0C DB 18 58 80 72 8C 70 16 0B 83 " +
"05 56 51 29 88 C9 60 D9 69 0C 6C 20 26 23 03 C8 " +
"74 B0 A8 0E 03 07 FB 45 56 C7 A2 CC C4 1C 06 66 " +
"A0 0D 2C 40 39 5E 86 4C 06 3D A0 4E 10 D0 60 D9 " +
"C8 58 CC E8 CF B0 80 61 3A 8A 7E 0D C6 23 AC 4F " +
"E0 E2 98 B6 12 2B 06 73 9D 12 E3 52 56 59 F6 08 " +
"8A CC 52 66 A3 50 FF 96 2B 94 E9 DF 4C A1 FE 2D " +
"3A 03 AB 9F 81 C2 F0 A3 54 BF 0F 85 EE A7 54 FF " +
"40 FB 7F A0 E3 9F D2 F4 4F 71 FE 19 58 FF 2B 31 " +
"7F 67 36 3B 25 4F 99 1B 4E 53 A6 5F 89 25 95 E9 " +
"C4 00 C7 83 12 F3 1F 26 35 4A D3 D2 47 0E 0A C3 " +
"41 8E C9 8A 52 37 DC 15 A1 D0 0D BC 4C 06 0C 2B " +
"28 2C 13 28 D4 EF 43 61 5A A0 58 3F 85 71 E0 4B " +
"69 9E 64 65 FE 39 C0 E5 22 30 1D 30 27 0E 74 3A " +
"18 60 FD 4A CC B1 2C 13 7D 07 36 2D 2A 31 85 B2 " +
"6A 0D 74 1D 1D 22 4D 99 FE 60 0A F5 9B EC 1C 58 " +
"FD 67 06 56 3F 38 0D 84 3C A5 30 0E 28 D3 AF C4 " +
"A4 CA FA 44 7A 0D 65 6E 60 7F 4D A1 1B 24 58 F7 " +
"49 AF A5 CC 0D CC DF 19 FE 03 00 F0 B1 25 4D 42 " +
"00 07 F0 E1 01 00 00 03 04 39 50 BE 98 B0 6F 57 " +
"24 31 70 5D 23 2F 9F 10 66 FF 00 BD 01 00 00 01 " +
"00 00 00 00 00 00 00 00 00 FF FF 20 54 1C F0 B5 " +
"01 00 00 39 50 BE 98 B0 6F 57 24 31 70 5D 23 2F " +
"9F 10 66 DA 03 00 00 00 00 00 00 00 00 00 00 D1 " +
"07 00 00 DD 05 00 00 4A AD 6F 00 8A C5 53 00 83 " +
"01 00 00 00 FE 78 9C A5 52 BF 4B 42 51 14 3E F7 " +
"DC 77 7A 16 45 48 8B 3C 48 A8 16 15 0D 6C 88 D0 " +
"04 C3 40 A3 32 1C 84 96 08 21 04 A1 C5 5C A2 35 " +
"82 C0 35 6A AB 1C 6A 6B A8 24 5A 83 68 08 84 84 " +
"96 A2 86 A0 7F C2 86 5E E7 5E F5 41 E4 10 BC 03 " +
"1F E7 FB F1 CE B9 F7 F1 9E 7C 05 2E 7A 37 9B E0 " +
"45 7B 10 EC 6F 96 5F 1D 74 13 55 7E B0 6C 5D 20 " +
"60 C0 49 A2 9A BD 99 4F 50 83 1B 30 38 13 0E 33 " +
"60 A6 A7 6B B5 37 EB F4 10 FA 14 15 A0 B6 6B 37 " +
"0C 1E B3 49 73 5B A5 C2 26 48 3E C1 E0 6C 08 4A " +
"30 C9 93 AA 02 B8 20 13 62 05 4E E1 E8 D7 7C C0 " +
"B8 14 95 5E BE B8 A7 CF 1E BE 55 2C 56 B9 78 DF " +
"08 7E 88 4C 27 FF 7B DB FF 7A DD B7 1A 17 67 34 " +
"6A AE BA DA 35 D1 E7 72 BE FE EC 6E FE DA E5 7C " +
"3D EC 7A DE 03 FD 50 06 0B 23 F2 0E F3 B2 A5 11 " +
"91 0D 4C B5 B5 F3 BF 94 C1 8F 24 F7 D9 6F 60 94 " +
"3B C9 9A F3 1C 6B E7 BB F0 2E 49 B2 25 2B C6 B1 " +
"EE 69 EE 15 63 4F 71 7D CE 85 CC C8 35 B9 C3 28 " +
"28 CE D0 5C 67 79 F2 4A A2 14 23 A4 38 43 73 9D " +
"2D 69 2F C1 08 31 9F C5 5C 9B EB 7B C5 69 19 B3 " +
"B4 81 F3 DC E3 B4 8E 8B CC B3 94 53 5A E7 41 2A " +
"63 9A AA 38 C5 3D 48 BB EC 57 59 6F 2B AD 73 1F " +
"1D 60 92 AE 70 8C BB 8F CE 31 C1 3C 49 27 4A EB " +
"DC A4 5B 8C D1 0B 0E 73 37 E9 11 A7 99 C7 E8 41 " +
"69 B0 7F 00 96 F2 A7 E8 42 00 07 F0 B4 01 00 00 " +
"03 04 1A BA F9 D6 A9 B9 3A 03 08 61 E9 90 FF 7B " +
"9E E6 FF 00 90 01 00 00 01 00 00 00 00 00 00 00 " +
"00 00 FF FF 20 54 1C F0 88 01 00 00 1A BA F9 D6 " +
"A9 B9 3A 03 08 61 E9 90 FF 7B 9E E6 12 0E 00 00 " +
"00 00 00 00 00 00 00 00 D1 07 00 00 DD 05 00 00 " +
"4A AD 6F 00 8A C5 53 00 56 01 00 00 00 FE 78 9C " +
"E3 13 62 00 02 D6 BB EC 17 19 04 99 FE F3 30 FC " +
"FF 07 E4 FE 82 62 98 0C 08 C8 31 48 FD 07 03 06 " +
"46 06 2E B8 8C 21 98 75 87 FD 02 C3 42 86 6D 0C " +
"2C 40 39 46 38 8B 85 C1 02 AB A8 14 C4 64 B0 EC " +
"34 06 36 10 93 91 01 64 3A 58 54 87 81 83 FD 22 " +
"AB 63 51 66 62 0E 03 33 D0 06 16 A0 1C 2F 43 26 " +
"83 1E 50 27 08 68 B0 6C 64 2C 66 F4 67 58 C0 30 " +
"1D 45 BF 06 E3 11 D6 27 70 71 4C 5B 89 15 83 B9 " +
"4E 89 71 29 AB 2C 7B 04 45 66 29 B3 51 A8 7F CB " +
"15 CA F4 6F A6 50 FF 16 9D 81 D5 CF 40 61 F8 51 " +
"AA DF 87 42 F7 53 AA 7F A0 FD 3F D0 F1 4F 69 FA " +
"A7 38 FF 0C AC FF 95 98 BF 33 9B 9D 92 A7 CC 0D " +
"A7 29 D3 AF C4 92 CA 74 62 80 E3 41 89 F9 0F 93 " +
"1A A5 69 E9 23 07 85 E1 20 C7 64 45 A9 1B EE 8A " +
"50 E8 06 5E 26 03 86 15 14 96 09 14 EA F7 A1 30 " +
"2D 50 AC 9F C2 38 F0 A5 34 4F B2 32 FF 1C E0 72 " +
"11 98 0E 98 13 07 38 1D 28 31 C7 B2 4C F4 1D D8 " +
"B4 A0 C4 14 CA AA 35 D0 75 64 88 34 65 FA 83 29 " +
"D4 6F B2 73 60 F5 9F A1 54 FF 0E CA D3 40 C8 53 " +
"0A E3 E0 09 85 6E 50 65 7D 22 BD 86 32 37 B0 BF " +
"A6 D0 0D 12 AC FB A4 D7 52 E6 06 E6 EF 0C FF 01 " +
"97 1D 12 C7 42 00 07 F0 C3 01 00 00 03 04 BA 4C " +
"B6 23 BA 8B 27 BE C8 55 59 86 24 9F 89 D4 FF 00 " +
"9F 01 00 00 01 00 00 00 00 00 00 00 00 00 FF FF " +
"20 54 1C F0 97 01 00 00 BA 4C B6 23 BA 8B 27 BE " +
"C8 55 59 86 24 9F 89 D4 AE 0E 00 00 00 00 00 00 " +
"00 00 00 00 D1 07 00 00 DD 05 00 00 4A AD 6F 00 " +
"8A C5 53 00 65 01 00 00 00 FE 78 9C E3 5B C7 00 " +
"04 AC 77 D9 2F 32 08 32 FD E7 61 F8 FF 0F C8 FD " +
"05 C5 30 19 10 90 63 90 FA 0F 06 0C 8C 0C 5C 70 " +
"19 43 30 EB 0E FB 05 86 85 0C DB 18 58 80 72 8C " +
"70 16 0B 83 05 56 51 29 88 C9 60 D9 69 0C 6C 20 " +
"26 23 03 C8 74 B0 A8 0E 03 07 FB 45 56 C7 A2 CC " +
"C4 1C 06 66 A0 0D 2C 40 39 5E 86 4C 06 3D A0 4E " +
"10 D0 60 99 C6 B8 98 D1 9F 61 01 C3 74 14 FD 1A " +
"8C 2B D8 84 B1 88 4B A5 A5 75 03 01 50 DF 59 46 " +
"77 46 0F A8 3C A6 AB 88 15 83 B9 5E 89 B1 8B D5 " +
"97 2D 82 22 B3 94 29 D5 BF E5 CA C0 EA DF AC 43 " +
"A1 FD 14 EA 67 A0 30 FC 28 D5 EF 43 A1 FB 7D 87 " +
"B8 FF 07 3A FE 07 3A FD 53 EA 7E 0A C3 4F 89 F9 " +
"0E 73 EA 69 79 CA DC 70 8A 32 FD 4A 2C 5E 4C DF " +
"87 7A 3C BC E0 A5 30 1E 3E 31 C5 33 AC A0 30 2F " +
"52 A8 DF 87 C2 30 A4 54 3F A5 65 19 85 65 A9 12 " +
"D3 2B 16 0D 8A CB 13 4A F3 E3 27 E6 09 03 9D 0E " +
"06 58 BF 12 B3 13 CB C1 01 4E 8B 4A 4C 56 AC 91 " +
"03 5D 37 86 48 53 A6 3F 98 42 FD 26 3B 07 56 FF " +
"99 1D 14 EA A7 CC 7E 70 1A 08 79 42 61 1C 3C A5 " +
"D0 0D 9C 6C C2 32 6B 29 73 03 DB 6B CA DC C0 F8 " +
"97 F5 AD CC 1A CA DC C0 F4 83 32 37 B0 A4 30 CE " +
"FC C7 48 99 1B FE 33 32 FC 07 00 6C CC 2E 23 33 " +
"00 0B F0 12 00 00 00 BF 00 08 00 08 00 81 01 09 " +
"00 00 08 C0 01 40 00 00 08 40 00 1E F1 10 00 00 " +
"00 0D 00 00 08 0C 00 00 08 17 00 00 08 F7 00 00 " +
"10 ";
// Decode the stream to bytes
byte[] bytes = HexRead.readData( new ByteArrayInputStream( dump.getBytes() ), -1 );
// Create a new instance of the escher dumper
EscherDump dumper = new EscherDump();
// Dump the contents of scher to screen.
// dumper.dumpOld( bytes.length, new ByteArrayInputStream( bytes ), System.out );
dumper.dump(bytes, 0, bytes.length, System.out);
}
public void dump( int recordSize, byte[] data, PrintStream out ) throws IOException, LittleEndian.BufferUnderrunException
{
// ByteArrayInputStream is = new ByteArrayInputStream( data );
// dump( recordSize, is, out );
dump( data, 0, recordSize, System.out );
}
}

View File

@ -0,0 +1,174 @@
package org.apache.poi.ddf;
import org.apache.poi.util.LittleEndian;
import org.apache.poi.util.HexDump;
import java.util.*;
import java.io.IOException;
/**
* The opt record is used to store property values for a shape. It is the key to determining
* the attributes of a shape. Properties can be of two types: simple or complex. Simple types
* are fixed length. Complex properties are variable length.
*
* @author Glen Stampoultzis
*/
public class EscherOptRecord
extends EscherRecord
{
public static final short RECORD_ID = (short) 0xF00B;
public static final String RECORD_DESCRIPTION = "msofbtOPT";
private List properties = new ArrayList();
/**
* 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 <code>data</code>.
* @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 bytesRemaining = readHeader( data, offset );
int pos = offset + 8;
EscherPropertyFactory f = new EscherPropertyFactory();
properties = f.createProperties( data, pos, getInstance() );
return bytesRemaining + 8;
}
/**
* This method serializes this escher record into a byte array.
*
* @param offset The offset into <code>data</code> to start writing the record data to.
* @param data The byte array to serialize to.
* @param listener A listener to retrieve start and end callbacks. Use a <code>NullEscherSerailizationListener</code> to ignore these events.
*
* @return The number of bytes written.
* @see NullEscherSerializationListener
*/
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, getPropertiesSize() );
int pos = offset + 8;
for ( Iterator iterator = properties.iterator(); iterator.hasNext(); )
{
EscherProperty escherProperty = (EscherProperty) iterator.next();
pos += escherProperty.serializeSimplePart( data, pos );
}
for ( Iterator iterator = properties.iterator(); iterator.hasNext(); )
{
EscherProperty escherProperty = (EscherProperty) iterator.next();
pos += escherProperty.serializeComplexPart( data, pos );
}
listener.afterRecordSerialize( pos, getRecordId(), pos - offset, this );
return pos - offset;
}
/**
* Returns the number of bytes that are required to serialize this record.
*
* @return Number of bytes
*/
public int getRecordSize()
{
return 8 + getPropertiesSize();
}
/**
* Automatically recalculate the correct option
*/
public short getOptions()
{
setOptions( (short) ( ( properties.size() << 4 ) | 0x3 ) );
return super.getOptions();
}
/**
* The short name for this record
*/
public String getRecordName()
{
return "Opt";
}
private int getPropertiesSize()
{
int totalSize = 0;
for ( Iterator iterator = properties.iterator(); iterator.hasNext(); )
{
EscherProperty escherProperty = (EscherProperty) iterator.next();
totalSize += escherProperty.getPropertySize();
}
return totalSize;
}
/**
* Retrieve the string representation of this record.
*/
public String toString()
{
String nl = System.getProperty( "line.separator" );
StringBuffer propertiesBuf = new StringBuffer();
for ( Iterator iterator = properties.iterator(); iterator.hasNext(); )
propertiesBuf.append( " "
+ iterator.next().toString()
+ nl );
return "org.apache.poi.ddf.EscherOptRecord:" + nl +
" isContainer: " + isContainerRecord() + nl +
" options: 0x" + HexDump.toHex( getOptions() ) + nl +
" recordId: 0x" + HexDump.toHex( getRecordId() ) + nl +
" numchildren: " + getChildRecords().size() + nl +
" properties:" + nl +
propertiesBuf.toString();
}
/**
* The list of properties stored by this record.
*/
public List getEscherProperties()
{
return properties;
}
/**
* The list of properties stored by this record.
*/
public EscherProperty getEscherProperty( int index )
{
return (EscherProperty) properties.get( index );
}
/**
* Add a property to this record.
*/
public void addEscherProperty( EscherProperty prop )
{
properties.add( prop );
}
/**
* Records should be sorted by property number before being stored.
*/
public void sortProperties()
{
Collections.sort( properties, new Comparator()
{
public int compare( Object o1, Object o2 )
{
EscherProperty p1 = (EscherProperty) o1;
EscherProperty p2 = (EscherProperty) o2;
return new Short( p1.getPropertyNumber() ).compareTo( new Short( p2.getPropertyNumber() ) );
}
} );
}
}

View File

@ -0,0 +1,607 @@
package org.apache.poi.ddf;
import java.util.HashMap;
import java.util.Map;
/**
* Provides a list of all known escher properties including the description and
* type.
*
* @author Glen Stampoultzis (glens at apache.org)
*/
public class EscherProperties
{
// Property constants
public static final short TRANSFORM__ROTATION = 4;
public static final short PROTECTION__LOCKROTATION = 119;
public static final short PROTECTION__LOCKASPECTRATIO = 120;
public static final short PROTECTION__LOCKPOSITION = 121;
public static final short PROTECTION__LOCKAGAINSTSELECT = 122;
public static final short PROTECTION__LOCKCROPPING = 123;
public static final short PROTECTION__LOCKVERTICES = 124;
public static final short PROTECTION__LOCKTEXT = 125;
public static final short PROTECTION__LOCKADJUSTHANDLES = 126;
public static final short PROTECTION__LOCKAGAINSTGROUPING = 127;
public static final short TEXT__TEXTID = 128;
public static final short TEXT__TEXTLEFT = 129;
public static final short TEXT__TEXTTOP = 130;
public static final short TEXT__TEXTRIGHT = 131;
public static final short TEXT__TEXTBOTTOM = 132;
public static final short TEXT__WRAPTEXT = 133;
public static final short TEXT__SCALETEXT = 134;
public static final short TEXT__ANCHORTEXT = 135;
public static final short TEXT__TEXTFLOW = 136;
public static final short TEXT__FONTROTATION = 137;
public static final short TEXT__IDOFNEXTSHAPE = 138;
public static final short TEXT__BIDIR = 139;
public static final short TEXT__SINGLECLICKSELECTS = 187;
public static final short TEXT__USEHOSTMARGINS = 188;
public static final short TEXT__ROTATETEXTWITHSHAPE = 189;
public static final short TEXT__SIZESHAPETOFITTEXT = 190;
public static final short TEXT__SIZE_TEXT_TO_FIT_SHAPE = 191 ;
public static final short GEOTEXT__UNICODE = 192;
public static final short GEOTEXT__RTFTEXT = 193;
public static final short GEOTEXT__ALIGNMENTONCURVE = 194;
public static final short GEOTEXT__DEFAULTPOINTSIZE = 195;
public static final short GEOTEXT__TEXTSPACING = 196;
public static final short GEOTEXT__FONTFAMILYNAME = 197;
public static final short GEOTEXT__REVERSEROWORDER = 240;
public static final short GEOTEXT__HASTEXTEFFECT = 241;
public static final short GEOTEXT__ROTATECHARACTERS = 242;
public static final short GEOTEXT__KERNCHARACTERS = 243;
public static final short GEOTEXT__TIGHTORTRACK = 244;
public static final short GEOTEXT__STRETCHTOFITSHAPE = 245;
public static final short GEOTEXT__CHARBOUNDINGBOX = 246;
public static final short GEOTEXT__SCALETEXTONPATH = 247;
public static final short GEOTEXT__STRETCHCHARHEIGHT = 248;
public static final short GEOTEXT__NOMEASUREALONGPATH = 249;
public static final short GEOTEXT__BOLDFONT = 250;
public static final short GEOTEXT__ITALICFONT = 251;
public static final short GEOTEXT__UNDERLINEFONT = 252;
public static final short GEOTEXT__SHADOWFONT = 253;
public static final short GEOTEXT__SMALLCAPSFONT = 254;
public static final short GEOTEXT__STRIKETHROUGHFONT = 255;
public static final short BLIP__CROPFROMTOP = 256;
public static final short BLIP__CROPFROMBOTTOM = 257;
public static final short BLIP__CROPFROMLEFT = 258;
public static final short BLIP__CROPFROMRIGHT = 259;
public static final short BLIP__BLIPTODISPLAY = 260;
public static final short BLIP__BLIPFILENAME = 261;
public static final short BLIP__BLIPFLAGS = 262;
public static final short BLIP__TRANSPARENTCOLOR = 263;
public static final short BLIP__CONTRASTSETTING = 264;
public static final short BLIP__BRIGHTNESSSETTING = 265;
public static final short BLIP__GAMMA = 266;
public static final short BLIP__PICTUREID = 267;
public static final short BLIP__DOUBLEMOD = 268;
public static final short BLIP__PICTUREFILLMOD = 269;
public static final short BLIP__PICTURELINE = 270;
public static final short BLIP__PRINTBLIP = 271;
public static final short BLIP__PRINTBLIPFILENAME = 272;
public static final short BLIP__PRINTFLAGS = 273;
public static final short BLIP__NOHITTESTPICTURE = 316;
public static final short BLIP__PICTUREGRAY = 317;
public static final short BLIP__PICTUREBILEVEL = 318;
public static final short BLIP__PICTUREACTIVE = 319;
public static final short GEOMETRY__LEFT = 320;
public static final short GEOMETRY__TOP = 321;
public static final short GEOMETRY__RIGHT = 322;
public static final short GEOMETRY__BOTTOM = 323;
public static final short GEOMETRY__SHAPEPATH = 324;
public static final short GEOMETRY__VERTICES = 325;
public static final short GEOMETRY__SEGMENTINFO = 326;
public static final short GEOMETRY__ADJUSTVALUE = 327;
public static final short GEOMETRY__ADJUST2VALUE = 328;
public static final short GEOMETRY__ADJUST3VALUE = 329;
public static final short GEOMETRY__ADJUST4VALUE = 330;
public static final short GEOMETRY__ADJUST5VALUE = 331;
public static final short GEOMETRY__ADJUST6VALUE = 332;
public static final short GEOMETRY__ADJUST7VALUE = 333;
public static final short GEOMETRY__ADJUST8VALUE = 334;
public static final short GEOMETRY__ADJUST9VALUE = 335;
public static final short GEOMETRY__ADJUST10VALUE = 336;
public static final short GEOMETRY__SHADOWok = 378;
public static final short GEOMETRY__3DOK = 379;
public static final short GEOMETRY__LINEOK = 380;
public static final short GEOMETRY__GEOTEXTOK = 381;
public static final short GEOMETRY__FILLSHADESHAPEOK = 382;
public static final short GEOMETRY__FILLOK = 383;
public static final short FILL__FILLTYPE = 384;
public static final short FILL__FILLCOLOR = 385 ;
public static final short FILL__FILLOPACITY = 386;
public static final short FILL__FILLBACKCOLOR = 387;
public static final short FILL__BACKOPACITY = 388;
public static final short FILL__CRMOD = 389;
public static final short FILL__PATTERNTEXTURE = 390;
public static final short FILL__BLIPFILENAME = 391;
public static final short FILL__BLIPFLAGS = 392;
public static final short FILL__WIDTH = 393;
public static final short FILL__HEIGHT = 394;
public static final short FILL__ANGLE = 395;
public static final short FILL__FOCUS = 396;
public static final short FILL__TOLEFT = 397;
public static final short FILL__TOTOP = 398;
public static final short FILL__TORIGHT = 399;
public static final short FILL__TOBOTTOM = 400;
public static final short FILL__RECTLEFT = 401;
public static final short FILL__RECTTOP = 402;
public static final short FILL__RECTRIGHT = 403;
public static final short FILL__RECTBOTTOM = 404;
public static final short FILL__DZTYPE = 405;
public static final short FILL__SHADEPRESET = 406;
public static final short FILL__SHADECOLORS = 407;
public static final short FILL__ORIGINX = 408;
public static final short FILL__ORIGINY = 409;
public static final short FILL__SHAPEORIGINX = 410;
public static final short FILL__SHAPEORIGINY = 411;
public static final short FILL__SHADETYPE = 412;
public static final short FILL__FILLED = 443;
public static final short FILL__HITTESTFILL = 444;
public static final short FILL__SHAPE = 445;
public static final short FILL__USERECT = 446;
public static final short FILL__NOFILLHITTEST = 447;
public static final short LINESTYLE__COLOR = 448 ;
public static final short LINESTYLE__OPACITY = 449;
public static final short LINESTYLE__BACKCOLOR = 450;
public static final short LINESTYLE__CRMOD = 451;
public static final short LINESTYLE__LINETYPE = 452;
public static final short LINESTYLE__FILLBLIP = 453;
public static final short LINESTYLE__FILLBLIPNAME = 454;
public static final short LINESTYLE__FILLBLIPFLAGS = 455;
public static final short LINESTYLE__FILLWIDTH = 456;
public static final short LINESTYLE__FILLHEIGHT = 457;
public static final short LINESTYLE__FILLDZTYPE = 458;
public static final short LINESTYLE__LINEWIDTH = 459;
public static final short LINESTYLE__LINEMITERLIMIT = 460;
public static final short LINESTYLE__LINESTYLE = 461;
public static final short LINESTYLE__LINEDASHING = 462;
public static final short LINESTYLE__LINEDASHSTYLE = 463;
public static final short LINESTYLE__LINESTARTARROWHEAD = 464;
public static final short LINESTYLE__LINEENDARROWHEAD = 465;
public static final short LINESTYLE__LINESTARTARROWWIDTH = 466;
public static final short LINESTYLE__LINEESTARTARROWLENGTH = 467;
public static final short LINESTYLE__LINEENDARROWWIDTH = 468;
public static final short LINESTYLE__LINEENDARROWLENGTH = 469;
public static final short LINESTYLE__LINEJOINSTYLE = 470;
public static final short LINESTYLE__LINEENDCAPSTYLE = 471;
public static final short LINESTYLE__ARROWHEADSOK = 507;
public static final short LINESTYLE__ANYLINE = 508;
public static final short LINESTYLE__HITLINETEST = 509;
public static final short LINESTYLE__LINEFILLSHAPE = 510;
public static final short LINESTYLE__NOLINEDRAWDASH = 511;
public static final short SHADOWSTYLE__TYPE = 512;
public static final short SHADOWSTYLE__COLOR = 513;
public static final short SHADOWSTYLE__HIGHLIGHT = 514;
public static final short SHADOWSTYLE__CRMOD = 515;
public static final short SHADOWSTYLE__OPACITY = 516;
public static final short SHADOWSTYLE__OFFSETX = 517;
public static final short SHADOWSTYLE__OFFSETY = 518;
public static final short SHADOWSTYLE__SECONDOFFSETX = 519;
public static final short SHADOWSTYLE__SECONDOFFSETY = 520;
public static final short SHADOWSTYLE__SCALEXTOX = 521;
public static final short SHADOWSTYLE__SCALEYTOX = 522;
public static final short SHADOWSTYLE__SCALEXTOY = 523;
public static final short SHADOWSTYLE__SCALEYTOY = 524;
public static final short SHADOWSTYLE__PERSPECTIVEX = 525;
public static final short SHADOWSTYLE__PERSPECTIVEY = 526;
public static final short SHADOWSTYLE__WEIGHT = 527;
public static final short SHADOWSTYLE__ORIGINX = 528;
public static final short SHADOWSTYLE__ORIGINY = 529;
public static final short SHADOWSTYLE__SHADOW = 574;
public static final short SHADOWSTYLE__SHADOWOBSURED = 575;
public static final short PERSPECTIVE__TYPE = 576;
public static final short PERSPECTIVE__OFFSETX = 577;
public static final short PERSPECTIVE__OFFSETY = 578;
public static final short PERSPECTIVE__SCALEXTOX = 579;
public static final short PERSPECTIVE__SCALEYTOX = 580;
public static final short PERSPECTIVE__SCALEXTOY = 581;
public static final short PERSPECTIVE__SCALEYTOY = 582;
public static final short PERSPECTIVE__PERSPECTIVEX = 583;
public static final short PERSPECTIVE__PERSPECTIVEY = 584;
public static final short PERSPECTIVE__WEIGHT = 585;
public static final short PERSPECTIVE__ORIGINX = 586;
public static final short PERSPECTIVE__ORIGINY = 587;
public static final short PERSPECTIVE__PERSPECTIVEON = 639;
public static final short THREED__SPECULARAMOUNT = 640;
public static final short THREED__DIFFUSEAMOUNT = 661;
public static final short THREED__SHININESS = 662;
public static final short THREED__EDGETHICKNESS = 663;
public static final short THREED__EXTRUDEFORWARD = 664;
public static final short THREED__EXTRUDEBACKWARD = 665;
public static final short THREED__EXTRUDEPLANE = 666;
public static final short THREED__EXTRUSIONCOLOR = 667;
public static final short THREED__CRMOD = 648;
public static final short THREED__3DEFFECT = 700;
public static final short THREED__METALLIC = 701;
public static final short THREED__USEEXTRUSIONCOLOR = 702;
public static final short THREED__LIGHTFACE = 703;
public static final short THREEDSTYLE__YROTATIONANGLE = 704;
public static final short THREEDSTYLE__XROTATIONANGLE = 705;
public static final short THREEDSTYLE__ROTATIONAXISX = 706;
public static final short THREEDSTYLE__ROTATIONAXISY = 707;
public static final short THREEDSTYLE__ROTATIONAXISZ = 708;
public static final short THREEDSTYLE__ROTATIONANGLE = 709;
public static final short THREEDSTYLE__ROTATIONCENTERX = 710;
public static final short THREEDSTYLE__ROTATIONCENTERY = 711;
public static final short THREEDSTYLE__ROTATIONCENTERZ = 712;
public static final short THREEDSTYLE__RENDERMODE = 713;
public static final short THREEDSTYLE__TOLERANCE = 714;
public static final short THREEDSTYLE__XVIEWPOINT = 715;
public static final short THREEDSTYLE__YVIEWPOINT = 716;
public static final short THREEDSTYLE__ZVIEWPOINT = 717;
public static final short THREEDSTYLE__ORIGINX = 718;
public static final short THREEDSTYLE__ORIGINY = 719;
public static final short THREEDSTYLE__SKEWANGLE = 720;
public static final short THREEDSTYLE__SKEWAMOUNT = 721;
public static final short THREEDSTYLE__AMBIENTINTENSITY = 722;
public static final short THREEDSTYLE__KEYX = 723;
public static final short THREEDSTYLE__KEYY = 724;
public static final short THREEDSTYLE__KEYZ = 725;
public static final short THREEDSTYLE__KEYINTENSITY = 726;
public static final short THREEDSTYLE__FILLX = 727;
public static final short THREEDSTYLE__FILLY = 728;
public static final short THREEDSTYLE__FILLZ = 729;
public static final short THREEDSTYLE__FILLINTENSITY = 730;
public static final short THREEDSTYLE__CONSTRAINROTATION = 763;
public static final short THREEDSTYLE__ROTATIONCENTERAUTO = 764;
public static final short THREEDSTYLE__PARALLEL = 765;
public static final short THREEDSTYLE__KEYHARSH = 766;
public static final short THREEDSTYLE__FILLHARSH = 767;
public static final short SHAPE__MASTER = 769;
public static final short SHAPE__CONNECTORSTYLE = 771;
public static final short SHAPE__BLACKANDWHITESETTINGS = 772;
public static final short SHAPE__WMODEPUREBW = 773;
public static final short SHAPE__WMODEBW = 774;
public static final short SHAPE__OLEICON = 826;
public static final short SHAPE__PREFERRELATIVERESIZE = 827;
public static final short SHAPE__LOCKSHAPETYPE = 828;
public static final short SHAPE__DELETEATTACHEDOBJECT = 830;
public static final short SHAPE__BACKGROUNDSHAPE = 831;
public static final short CALLOUT__CALLOUTTYPE = 832;
public static final short CALLOUT__XYCALLOUTGAP = 833;
public static final short CALLOUT__CALLOUTANGLE = 834;
public static final short CALLOUT__CALLOUTDROPTYPE = 835;
public static final short CALLOUT__CALLOUTDROPSPECIFIED = 836;
public static final short CALLOUT__CALLOUTLENGTHSPECIFIED = 837;
public static final short CALLOUT__ISCALLOUT = 889;
public static final short CALLOUT__CALLOUTACCENTBAR = 890;
public static final short CALLOUT__CALLOUTTEXTBORDER = 891;
public static final short CALLOUT__CALLOUTMINUSX = 892;
public static final short CALLOUT__CALLOUTMINUSY = 893;
public static final short CALLOUT__DROPAUTO = 894;
public static final short CALLOUT__LENGTHSPECIFIED = 895;
public static final short GROUPSHAPE__SHAPENAME = 896;
public static final short GROUPSHAPE__DESCRIPTION = 897;
public static final short GROUPSHAPE__HYPERLINK = 898;
public static final short GROUPSHAPE__WRAPPOLYGONVERTICES = 899;
public static final short GROUPSHAPE__WRAPDISTLEFT = 900;
public static final short GROUPSHAPE__WRAPDISTTOP = 901;
public static final short GROUPSHAPE__WRAPDISTRIGHT = 902;
public static final short GROUPSHAPE__WRAPDISTBOTTOM = 903;
public static final short GROUPSHAPE__REGROUPID = 904;
public static final short GROUPSHAPE__EDITEDWRAP = 953;
public static final short GROUPSHAPE__BEHINDDOCUMENT = 954;
public static final short GROUPSHAPE__ONDBLCLICKNOTIFY = 955;
public static final short GROUPSHAPE__ISBUTTON = 956;
public static final short GROUPSHAPE__1DADJUSTMENT = 957;
public static final short GROUPSHAPE__HIDDEN = 958;
public static final short GROUPSHAPE__PRINT = 959;
private static Map properties;
private static void initProps()
{
if ( properties == null )
{
properties = new HashMap();
addProp( TRANSFORM__ROTATION, data( "transform.rotation" ) );
addProp( PROTECTION__LOCKROTATION , data( "protection.lockrotation" ) );
addProp( PROTECTION__LOCKASPECTRATIO , data( "protection.lockaspectratio" ) );
addProp( PROTECTION__LOCKPOSITION , data( "protection.lockposition" ) );
addProp( PROTECTION__LOCKAGAINSTSELECT , data( "protection.lockagainstselect" ) );
addProp( PROTECTION__LOCKCROPPING , data( "protection.lockcropping" ) );
addProp( PROTECTION__LOCKVERTICES , data( "protection.lockvertices" ) );
addProp( PROTECTION__LOCKTEXT , data( "protection.locktext" ) );
addProp( PROTECTION__LOCKADJUSTHANDLES , data( "protection.lockadjusthandles" ) );
addProp( PROTECTION__LOCKAGAINSTGROUPING , data( "protection.lockagainstgrouping", EscherPropertyMetaData.TYPE_BOOLEAN ) );
addProp( TEXT__TEXTID , data( "text.textid" ) );
addProp( TEXT__TEXTLEFT , data( "text.textleft" ) );
addProp( TEXT__TEXTTOP , data( "text.texttop" ) );
addProp( TEXT__TEXTRIGHT , data( "text.textright" ) );
addProp( TEXT__TEXTBOTTOM , data( "text.textbottom" ) );
addProp( TEXT__WRAPTEXT , data( "text.wraptext" ) );
addProp( TEXT__SCALETEXT , data( "text.scaletext" ) );
addProp( TEXT__ANCHORTEXT , data( "text.anchortext" ) );
addProp( TEXT__TEXTFLOW , data( "text.textflow" ) );
addProp( TEXT__FONTROTATION , data( "text.fontrotation" ) );
addProp( TEXT__IDOFNEXTSHAPE , data( "text.idofnextshape" ) );
addProp( TEXT__BIDIR , data( "text.bidir" ) );
addProp( TEXT__SINGLECLICKSELECTS , data( "text.singleclickselects" ) );
addProp( TEXT__USEHOSTMARGINS , data( "text.usehostmargins" ) );
addProp( TEXT__ROTATETEXTWITHSHAPE , data( "text.rotatetextwithshape" ) );
addProp( TEXT__SIZESHAPETOFITTEXT , data( "text.sizeshapetofittext" ) );
addProp( TEXT__SIZE_TEXT_TO_FIT_SHAPE, data( "text.sizetexttofitshape", EscherPropertyMetaData.TYPE_BOOLEAN ) );
addProp( GEOTEXT__UNICODE , data( "geotext.unicode" ) );
addProp( GEOTEXT__RTFTEXT , data( "geotext.rtftext" ) );
addProp( GEOTEXT__ALIGNMENTONCURVE , data( "geotext.alignmentoncurve" ) );
addProp( GEOTEXT__DEFAULTPOINTSIZE , data( "geotext.defaultpointsize" ) );
addProp( GEOTEXT__TEXTSPACING , data( "geotext.textspacing" ) );
addProp( GEOTEXT__FONTFAMILYNAME , data( "geotext.fontfamilyname" ) );
addProp( GEOTEXT__REVERSEROWORDER , data( "geotext.reverseroworder" ) );
addProp( GEOTEXT__HASTEXTEFFECT , data( "geotext.hastexteffect" ) );
addProp( GEOTEXT__ROTATECHARACTERS , data( "geotext.rotatecharacters" ) );
addProp( GEOTEXT__KERNCHARACTERS , data( "geotext.kerncharacters" ) );
addProp( GEOTEXT__TIGHTORTRACK , data( "geotext.tightortrack" ) );
addProp( GEOTEXT__STRETCHTOFITSHAPE , data( "geotext.stretchtofitshape" ) );
addProp( GEOTEXT__CHARBOUNDINGBOX , data( "geotext.charboundingbox" ) );
addProp( GEOTEXT__SCALETEXTONPATH , data( "geotext.scaletextonpath" ) );
addProp( GEOTEXT__STRETCHCHARHEIGHT , data( "geotext.stretchcharheight" ) );
addProp( GEOTEXT__NOMEASUREALONGPATH , data( "geotext.nomeasurealongpath" ) );
addProp( GEOTEXT__BOLDFONT , data( "geotext.boldfont" ) );
addProp( GEOTEXT__ITALICFONT , data( "geotext.italicfont" ) );
addProp( GEOTEXT__UNDERLINEFONT , data( "geotext.underlinefont" ) );
addProp( GEOTEXT__SHADOWFONT , data( "geotext.shadowfont" ) );
addProp( GEOTEXT__SMALLCAPSFONT , data( "geotext.smallcapsfont" ) );
addProp( GEOTEXT__STRIKETHROUGHFONT , data( "geotext.strikethroughfont" ) );
addProp( BLIP__CROPFROMTOP , data( "blip.cropfromtop" ) );
addProp( BLIP__CROPFROMBOTTOM , data( "blip.cropfrombottom" ) );
addProp( BLIP__CROPFROMLEFT , data( "blip.cropfromleft" ) );
addProp( BLIP__CROPFROMRIGHT , data( "blip.cropfromright" ) );
addProp( BLIP__BLIPTODISPLAY , data( "blip.bliptodisplay" ) );
addProp( BLIP__BLIPFILENAME , data( "blip.blipfilename" ) );
addProp( BLIP__BLIPFLAGS , data( "blip.blipflags" ) );
addProp( BLIP__TRANSPARENTCOLOR , data( "blip.transparentcolor" ) );
addProp( BLIP__CONTRASTSETTING , data( "blip.contrastsetting" ) );
addProp( BLIP__BRIGHTNESSSETTING , data( "blip.brightnesssetting" ) );
addProp( BLIP__GAMMA , data( "blip.gamma" ) );
addProp( BLIP__PICTUREID , data( "blip.pictureid" ) );
addProp( BLIP__DOUBLEMOD , data( "blip.doublemod" ) );
addProp( BLIP__PICTUREFILLMOD , data( "blip.picturefillmod" ) );
addProp( BLIP__PICTURELINE , data( "blip.pictureline" ) );
addProp( BLIP__PRINTBLIP , data( "blip.printblip" ) );
addProp( BLIP__PRINTBLIPFILENAME , data( "blip.printblipfilename" ) );
addProp( BLIP__PRINTFLAGS , data( "blip.printflags" ) );
addProp( BLIP__NOHITTESTPICTURE , data( "blip.nohittestpicture" ) );
addProp( BLIP__PICTUREGRAY , data( "blip.picturegray" ) );
addProp( BLIP__PICTUREBILEVEL , data( "blip.picturebilevel" ) );
addProp( BLIP__PICTUREACTIVE , data( "blip.pictureactive" ) );
addProp( GEOMETRY__LEFT , data( "geometry.left" ) );
addProp( GEOMETRY__TOP , data( "geometry.top" ) );
addProp( GEOMETRY__RIGHT , data( "geometry.right" ) );
addProp( GEOMETRY__BOTTOM , data( "geometry.bottom" ) );
addProp( GEOMETRY__SHAPEPATH , data( "geometry.shapepath", EscherPropertyMetaData.TYPE_SHAPEPATH ) );
addProp( GEOMETRY__VERTICES , data( "geometry.vertices" , EscherPropertyMetaData.TYPE_ARRAY ) );
addProp( GEOMETRY__SEGMENTINFO , data( "geometry.segmentinfo", EscherPropertyMetaData.TYPE_ARRAY ) );
addProp( GEOMETRY__ADJUSTVALUE , data( "geometry.adjustvalue" ) );
addProp( GEOMETRY__ADJUST2VALUE , data( "geometry.adjust2value" ) );
addProp( GEOMETRY__ADJUST3VALUE , data( "geometry.adjust3value" ) );
addProp( GEOMETRY__ADJUST4VALUE , data( "geometry.adjust4value" ) );
addProp( GEOMETRY__ADJUST5VALUE , data( "geometry.adjust5value" ) );
addProp( GEOMETRY__ADJUST6VALUE , data( "geometry.adjust6value" ) );
addProp( GEOMETRY__ADJUST7VALUE , data( "geometry.adjust7value" ) );
addProp( GEOMETRY__ADJUST8VALUE , data( "geometry.adjust8value" ) );
addProp( GEOMETRY__ADJUST9VALUE , data( "geometry.adjust9value" ) );
addProp( GEOMETRY__ADJUST10VALUE , data( "geometry.adjust10value" ) );
addProp( GEOMETRY__SHADOWok , data( "geometry.shadowOK" ) );
addProp( GEOMETRY__3DOK , data( "geometry.3dok" ) );
addProp( GEOMETRY__LINEOK , data( "geometry.lineok" ) );
addProp( GEOMETRY__GEOTEXTOK , data( "geometry.geotextok" ) );
addProp( GEOMETRY__FILLSHADESHAPEOK , data( "geometry.fillshadeshapeok" ) );
addProp( GEOMETRY__FILLOK , data( "geometry.fillok", EscherPropertyMetaData.TYPE_BOOLEAN ) );
addProp( FILL__FILLTYPE , data( "fill.filltype" ) );
addProp( FILL__FILLCOLOR, data( "fill.fillcolor", EscherPropertyMetaData.TYPE_RGB ) );
addProp( FILL__FILLOPACITY , data( "fill.fillopacity" ) );
addProp( FILL__FILLBACKCOLOR , data( "fill.fillbackcolor", EscherPropertyMetaData.TYPE_RGB ) );
addProp( FILL__BACKOPACITY , data( "fill.backopacity" ) );
addProp( FILL__CRMOD , data( "fill.crmod" ) );
addProp( FILL__PATTERNTEXTURE , data( "fill.patterntexture" ) );
addProp( FILL__BLIPFILENAME , data( "fill.blipfilename" ) );
addProp( FILL__BLIPFLAGS, data( "fill.blipflags" ) );
addProp( FILL__WIDTH , data( "fill.width" ) );
addProp( FILL__HEIGHT , data( "fill.height" ) );
addProp( FILL__ANGLE , data( "fill.angle" ) );
addProp( FILL__FOCUS , data( "fill.focus" ) );
addProp( FILL__TOLEFT , data( "fill.toleft" ) );
addProp( FILL__TOTOP , data( "fill.totop" ) );
addProp( FILL__TORIGHT , data( "fill.toright" ) );
addProp( FILL__TOBOTTOM , data( "fill.tobottom" ) );
addProp( FILL__RECTLEFT , data( "fill.rectleft" ) );
addProp( FILL__RECTTOP , data( "fill.recttop" ) );
addProp( FILL__RECTRIGHT , data( "fill.rectright" ) );
addProp( FILL__RECTBOTTOM , data( "fill.rectbottom" ) );
addProp( FILL__DZTYPE , data( "fill.dztype" ) );
addProp( FILL__SHADEPRESET , data( "fill.shadepreset" ) );
addProp( FILL__SHADECOLORS , data( "fill.shadecolors", EscherPropertyMetaData.TYPE_ARRAY ) );
addProp( FILL__ORIGINX , data( "fill.originx" ) );
addProp( FILL__ORIGINY , data( "fill.originy" ) );
addProp( FILL__SHAPEORIGINX , data( "fill.shapeoriginx" ) );
addProp( FILL__SHAPEORIGINY , data( "fill.shapeoriginy" ) );
addProp( FILL__SHADETYPE , data( "fill.shadetype" ) );
addProp( FILL__FILLED , data( "fill.filled" ) );
addProp( FILL__HITTESTFILL , data( "fill.hittestfill" ) );
addProp( FILL__SHAPE , data( "fill.shape" ) );
addProp( FILL__USERECT , data( "fill.userect" ) );
addProp( FILL__NOFILLHITTEST , data( "fill.nofillhittest", EscherPropertyMetaData.TYPE_BOOLEAN ) );
addProp( LINESTYLE__COLOR, data( "linestyle.color", EscherPropertyMetaData.TYPE_RGB ) );
addProp( LINESTYLE__OPACITY , data( "linestyle.opacity" ) );
addProp( LINESTYLE__BACKCOLOR , data( "linestyle.backcolor", EscherPropertyMetaData.TYPE_RGB ) );
addProp( LINESTYLE__CRMOD , data( "linestyle.crmod" ) );
addProp( LINESTYLE__LINETYPE , data( "linestyle.linetype" ) );
addProp( LINESTYLE__FILLBLIP , data( "linestyle.fillblip" ) );
addProp( LINESTYLE__FILLBLIPNAME , data( "linestyle.fillblipname" ) );
addProp( LINESTYLE__FILLBLIPFLAGS , data( "linestyle.fillblipflags" ) );
addProp( LINESTYLE__FILLWIDTH , data( "linestyle.fillwidth" ) );
addProp( LINESTYLE__FILLHEIGHT , data( "linestyle.fillheight" ) );
addProp( LINESTYLE__FILLDZTYPE , data( "linestyle.filldztype" ) );
addProp( LINESTYLE__LINEWIDTH , data( "linestyle.linewidth" ) );
addProp( LINESTYLE__LINEMITERLIMIT , data( "linestyle.linemiterlimit" ) );
addProp( LINESTYLE__LINESTYLE , data( "linestyle.linestyle" ) );
addProp( LINESTYLE__LINEDASHING , data( "linestyle.linedashing" ) );
addProp( LINESTYLE__LINEDASHSTYLE , data( "linestyle.linedashstyle", EscherPropertyMetaData.TYPE_ARRAY ) );
addProp( LINESTYLE__LINESTARTARROWHEAD , data( "linestyle.linestartarrowhead" ) );
addProp( LINESTYLE__LINEENDARROWHEAD , data( "linestyle.lineendarrowhead" ) );
addProp( LINESTYLE__LINESTARTARROWWIDTH , data( "linestyle.linestartarrowwidth" ) );
addProp( LINESTYLE__LINEESTARTARROWLENGTH , data( "linestyle.lineestartarrowlength" ) );
addProp( LINESTYLE__LINEENDARROWWIDTH , data( "linestyle.lineendarrowwidth" ) );
addProp( LINESTYLE__LINEENDARROWLENGTH , data( "linestyle.lineendarrowlength" ) );
addProp( LINESTYLE__LINEJOINSTYLE , data( "linestyle.linejoinstyle" ) );
addProp( LINESTYLE__LINEENDCAPSTYLE , data( "linestyle.lineendcapstyle" ) );
addProp( LINESTYLE__ARROWHEADSOK , data( "linestyle.arrowheadsok" ) );
addProp( LINESTYLE__ANYLINE , data( "linestyle.anyline" ) );
addProp( LINESTYLE__HITLINETEST , data( "linestyle.hitlinetest" ) );
addProp( LINESTYLE__LINEFILLSHAPE , data( "linestyle.linefillshape" ) );
addProp( LINESTYLE__NOLINEDRAWDASH , data( "linestyle.nolinedrawdash", EscherPropertyMetaData.TYPE_BOOLEAN ) );
addProp( SHADOWSTYLE__TYPE , data( "shadowstyle.type" ) );
addProp( SHADOWSTYLE__COLOR , data( "shadowstyle.color", EscherPropertyMetaData.TYPE_RGB ) );
addProp( SHADOWSTYLE__HIGHLIGHT , data( "shadowstyle.highlight" ) );
addProp( SHADOWSTYLE__CRMOD , data( "shadowstyle.crmod" ) );
addProp( SHADOWSTYLE__OPACITY , data( "shadowstyle.opacity" ) );
addProp( SHADOWSTYLE__OFFSETX , data( "shadowstyle.offsetx" ) );
addProp( SHADOWSTYLE__OFFSETY , data( "shadowstyle.offsety" ) );
addProp( SHADOWSTYLE__SECONDOFFSETX , data( "shadowstyle.secondoffsetx" ) );
addProp( SHADOWSTYLE__SECONDOFFSETY , data( "shadowstyle.secondoffsety" ) );
addProp( SHADOWSTYLE__SCALEXTOX , data( "shadowstyle.scalextox" ) );
addProp( SHADOWSTYLE__SCALEYTOX , data( "shadowstyle.scaleytox" ) );
addProp( SHADOWSTYLE__SCALEXTOY , data( "shadowstyle.scalextoy" ) );
addProp( SHADOWSTYLE__SCALEYTOY , data( "shadowstyle.scaleytoy" ) );
addProp( SHADOWSTYLE__PERSPECTIVEX , data( "shadowstyle.perspectivex" ) );
addProp( SHADOWSTYLE__PERSPECTIVEY , data( "shadowstyle.perspectivey" ) );
addProp( SHADOWSTYLE__WEIGHT , data( "shadowstyle.weight" ) );
addProp( SHADOWSTYLE__ORIGINX , data( "shadowstyle.originx" ) );
addProp( SHADOWSTYLE__ORIGINY , data( "shadowstyle.originy" ) );
addProp( SHADOWSTYLE__SHADOW , data( "shadowstyle.shadow" ) );
addProp( SHADOWSTYLE__SHADOWOBSURED , data( "shadowstyle.shadowobsured" ) );
addProp( PERSPECTIVE__TYPE , data( "perspective.type" ) );
addProp( PERSPECTIVE__OFFSETX , data( "perspective.offsetx" ) );
addProp( PERSPECTIVE__OFFSETY , data( "perspective.offsety" ) );
addProp( PERSPECTIVE__SCALEXTOX , data( "perspective.scalextox" ) );
addProp( PERSPECTIVE__SCALEYTOX , data( "perspective.scaleytox" ) );
addProp( PERSPECTIVE__SCALEXTOY , data( "perspective.scalextoy" ) );
addProp( PERSPECTIVE__SCALEYTOY , data( "perspective.scaleytoy" ) );
addProp( PERSPECTIVE__PERSPECTIVEX , data( "perspective.perspectivex" ) );
addProp( PERSPECTIVE__PERSPECTIVEY , data( "perspective.perspectivey" ) );
addProp( PERSPECTIVE__WEIGHT , data( "perspective.weight" ) );
addProp( PERSPECTIVE__ORIGINX , data( "perspective.originx" ) );
addProp( PERSPECTIVE__ORIGINY , data( "perspective.originy" ) );
addProp( PERSPECTIVE__PERSPECTIVEON , data( "perspective.perspectiveon" ) );
addProp( THREED__SPECULARAMOUNT , data( "3d.specularamount" ) );
addProp( THREED__DIFFUSEAMOUNT , data( "3d.diffuseamount" ) );
addProp( THREED__SHININESS , data( "3d.shininess" ) );
addProp( THREED__EDGETHICKNESS , data( "3d.edgethickness" ) );
addProp( THREED__EXTRUDEFORWARD , data( "3d.extrudeforward" ) );
addProp( THREED__EXTRUDEBACKWARD , data( "3d.extrudebackward" ) );
addProp( THREED__EXTRUDEPLANE , data( "3d.extrudeplane" ) );
addProp( THREED__EXTRUSIONCOLOR , data( "3d.extrusioncolor", EscherPropertyMetaData.TYPE_RGB ) );
addProp( THREED__CRMOD , data( "3d.crmod" ) );
addProp( THREED__3DEFFECT , data( "3d.3deffect" ) );
addProp( THREED__METALLIC , data( "3d.metallic" ) );
addProp( THREED__USEEXTRUSIONCOLOR , data( "3d.useextrusioncolor", EscherPropertyMetaData.TYPE_RGB ) );
addProp( THREED__LIGHTFACE , data( "3d.lightface" ) );
addProp( THREEDSTYLE__YROTATIONANGLE , data( "3dstyle.yrotationangle" ) );
addProp( THREEDSTYLE__XROTATIONANGLE , data( "3dstyle.xrotationangle" ) );
addProp( THREEDSTYLE__ROTATIONAXISX , data( "3dstyle.rotationaxisx" ) );
addProp( THREEDSTYLE__ROTATIONAXISY , data( "3dstyle.rotationaxisy" ) );
addProp( THREEDSTYLE__ROTATIONAXISZ , data( "3dstyle.rotationaxisz" ) );
addProp( THREEDSTYLE__ROTATIONANGLE , data( "3dstyle.rotationangle" ) );
addProp( THREEDSTYLE__ROTATIONCENTERX , data( "3dstyle.rotationcenterx" ) );
addProp( THREEDSTYLE__ROTATIONCENTERY , data( "3dstyle.rotationcentery" ) );
addProp( THREEDSTYLE__ROTATIONCENTERZ , data( "3dstyle.rotationcenterz" ) );
addProp( THREEDSTYLE__RENDERMODE , data( "3dstyle.rendermode" ) );
addProp( THREEDSTYLE__TOLERANCE , data( "3dstyle.tolerance" ) );
addProp( THREEDSTYLE__XVIEWPOINT , data( "3dstyle.xviewpoint" ) );
addProp( THREEDSTYLE__YVIEWPOINT , data( "3dstyle.yviewpoint" ) );
addProp( THREEDSTYLE__ZVIEWPOINT , data( "3dstyle.zviewpoint" ) );
addProp( THREEDSTYLE__ORIGINX , data( "3dstyle.originx" ) );
addProp( THREEDSTYLE__ORIGINY , data( "3dstyle.originy" ) );
addProp( THREEDSTYLE__SKEWANGLE , data( "3dstyle.skewangle" ) );
addProp( THREEDSTYLE__SKEWAMOUNT , data( "3dstyle.skewamount" ) );
addProp( THREEDSTYLE__AMBIENTINTENSITY , data( "3dstyle.ambientintensity" ) );
addProp( THREEDSTYLE__KEYX , data( "3dstyle.keyx" ) );
addProp( THREEDSTYLE__KEYY , data( "3dstyle.keyy" ) );
addProp( THREEDSTYLE__KEYZ , data( "3dstyle.keyz" ) );
addProp( THREEDSTYLE__KEYINTENSITY , data( "3dstyle.keyintensity" ) );
addProp( THREEDSTYLE__FILLX , data( "3dstyle.fillx" ) );
addProp( THREEDSTYLE__FILLY , data( "3dstyle.filly" ) );
addProp( THREEDSTYLE__FILLZ , data( "3dstyle.fillz" ) );
addProp( THREEDSTYLE__FILLINTENSITY , data( "3dstyle.fillintensity" ) );
addProp( THREEDSTYLE__CONSTRAINROTATION , data( "3dstyle.constrainrotation" ) );
addProp( THREEDSTYLE__ROTATIONCENTERAUTO , data( "3dstyle.rotationcenterauto" ) );
addProp( THREEDSTYLE__PARALLEL , data( "3dstyle.parallel" ) );
addProp( THREEDSTYLE__KEYHARSH , data( "3dstyle.keyharsh" ) );
addProp( THREEDSTYLE__FILLHARSH , data( "3dstyle.fillharsh" ) );
addProp( SHAPE__MASTER , data( "shape.master" ) );
addProp( SHAPE__CONNECTORSTYLE , data( "shape.connectorstyle" ) );
addProp( SHAPE__BLACKANDWHITESETTINGS , data( "shape.blackandwhitesettings" ) );
addProp( SHAPE__WMODEPUREBW , data( "shape.wmodepurebw" ) );
addProp( SHAPE__WMODEBW , data( "shape.wmodebw" ) );
addProp( SHAPE__OLEICON , data( "shape.oleicon" ) );
addProp( SHAPE__PREFERRELATIVERESIZE , data( "shape.preferrelativeresize" ) );
addProp( SHAPE__LOCKSHAPETYPE , data( "shape.lockshapetype" ) );
addProp( SHAPE__DELETEATTACHEDOBJECT , data( "shape.deleteattachedobject" ) );
addProp( SHAPE__BACKGROUNDSHAPE , data( "shape.backgroundshape" ) );
addProp( CALLOUT__CALLOUTTYPE , data( "callout.callouttype" ) );
addProp( CALLOUT__XYCALLOUTGAP , data( "callout.xycalloutgap" ) );
addProp( CALLOUT__CALLOUTANGLE , data( "callout.calloutangle" ) );
addProp( CALLOUT__CALLOUTDROPTYPE , data( "callout.calloutdroptype" ) );
addProp( CALLOUT__CALLOUTDROPSPECIFIED , data( "callout.calloutdropspecified" ) );
addProp( CALLOUT__CALLOUTLENGTHSPECIFIED , data( "callout.calloutlengthspecified" ) );
addProp( CALLOUT__ISCALLOUT , data( "callout.iscallout" ) );
addProp( CALLOUT__CALLOUTACCENTBAR , data( "callout.calloutaccentbar" ) );
addProp( CALLOUT__CALLOUTTEXTBORDER , data( "callout.callouttextborder" ) );
addProp( CALLOUT__CALLOUTMINUSX , data( "callout.calloutminusx" ) );
addProp( CALLOUT__CALLOUTMINUSY , data( "callout.calloutminusy" ) );
addProp( CALLOUT__DROPAUTO , data( "callout.dropauto" ) );
addProp( CALLOUT__LENGTHSPECIFIED , data( "callout.lengthspecified" ) );
addProp( GROUPSHAPE__SHAPENAME , data( "groupshape.shapename" ) );
addProp( GROUPSHAPE__DESCRIPTION , data( "groupshape.description" ) );
addProp( GROUPSHAPE__HYPERLINK , data( "groupshape.hyperlink" ) );
addProp( GROUPSHAPE__WRAPPOLYGONVERTICES , data( "groupshape.wrappolygonvertices", EscherPropertyMetaData.TYPE_ARRAY ) );
addProp( GROUPSHAPE__WRAPDISTLEFT , data( "groupshape.wrapdistleft" ) );
addProp( GROUPSHAPE__WRAPDISTTOP , data( "groupshape.wrapdisttop" ) );
addProp( GROUPSHAPE__WRAPDISTRIGHT , data( "groupshape.wrapdistright" ) );
addProp( GROUPSHAPE__WRAPDISTBOTTOM , data( "groupshape.wrapdistbottom" ) );
addProp( GROUPSHAPE__REGROUPID , data( "groupshape.regroupid" ) );
addProp( GROUPSHAPE__EDITEDWRAP , data( "groupshape.editedwrap" ) );
addProp( GROUPSHAPE__BEHINDDOCUMENT , data( "groupshape.behinddocument" ) );
addProp( GROUPSHAPE__ONDBLCLICKNOTIFY , data( "groupshape.ondblclicknotify" ) );
addProp( GROUPSHAPE__ISBUTTON , data( "groupshape.isbutton" ) );
addProp( GROUPSHAPE__1DADJUSTMENT , data( "groupshape.1dadjustment" ) );
addProp( GROUPSHAPE__HIDDEN , data( "groupshape.hidden" ) );
addProp( GROUPSHAPE__PRINT , data( "groupshape.print", EscherPropertyMetaData.TYPE_BOOLEAN ) );
}
}
private static void addProp( int s, EscherPropertyMetaData data )
{
properties.put( new Short( (short) s ), data );
}
private static EscherPropertyMetaData data( String propName, byte type )
{
return new EscherPropertyMetaData( propName, type );
}
private static EscherPropertyMetaData data( String propName )
{
return new EscherPropertyMetaData( propName );
}
public static String getPropertyName( short propertyId )
{
initProps();
EscherPropertyMetaData o = (EscherPropertyMetaData) properties.get( new Short( propertyId ) );
return o == null ? "unknown" : o.getDescription();
}
public static byte getPropertyType( short propertyId )
{
initProps();
EscherPropertyMetaData escherPropertyMetaData = (EscherPropertyMetaData) properties.get( new Short( propertyId ) );
return escherPropertyMetaData == null ? 0 : escherPropertyMetaData.getType();
}
}

View File

@ -0,0 +1,78 @@
package org.apache.poi.ddf;
/**
* This is the abstract base class for all escher properties.
*
* @see EscherOptRecord
*
* @author Glen Stampoultzis (glens at apache.org)
*/
abstract public class EscherProperty
{
private short id;
/**
* The id is distinct from the actual property number. The id includes the property number the blip id
* flag and an indicator whether the property is complex or not.
*/
public EscherProperty( short id )
{
this.id = id;
}
/**
* Constructs a new escher property. The three parameters are combined to form a property
* id.
*/
public EscherProperty( short propertyNumber, boolean isComplex, boolean isBlipId )
{
this.id = (short)(propertyNumber +
(isComplex ? 0x8000 : 0x0) +
(isBlipId ? 0x4000 : 0x0));
}
public short getId()
{
return id;
}
public short getPropertyNumber()
{
return (short) ( id & (short) 0x3FFF );
}
public boolean isComplex()
{
return ( id & (short) 0x8000 ) != 0;
}
public boolean isBlipId()
{
return ( id & (short) 0x4000 ) != 0;
}
public String getName()
{
return EscherProperties.getPropertyName(id);
}
/**
* Most properties are just 6 bytes in length. Override this if we're
* dealing with complex properties.
*/
public int getPropertySize()
{
return 6;
}
/**
* Escher properties consist of a simple fixed length part and a complex variable length part.
* The fixed length part is serialized first.
*/
abstract public int serializeSimplePart( byte[] data, int pos );
/**
* Escher properties consist of a simple fixed length part and a complex variable length part.
* The fixed length part is serialized first.
*/
abstract public int serializeComplexPart( byte[] data, int pos );
}

View File

@ -0,0 +1,91 @@
package org.apache.poi.ddf;
import org.apache.poi.util.LittleEndian;
import org.apache.poi.hssf.record.RecordFormatException;
import java.util.List;
import java.util.ArrayList;
import java.util.Iterator;
/**
* Generates a property given a reference into the byte array storing that property.
*
* @author Glen Stampoultzis
*/
public class EscherPropertyFactory
{
/**
* Create new properties from a byte array.
*
* @param data The byte array containing the property
* @param offset The starting offset into the byte array
* @return The new properties
*/
public List createProperties( byte[] data, int offset, short numProperties )
{
List results = new ArrayList();
int pos = offset;
int complexBytes = 0;
// while ( bytesRemaining >= 6 )
for (int i = 0; i < numProperties; i++)
{
short propId;
int propData;
propId = LittleEndian.getShort( data, pos );
propData = LittleEndian.getInt( data, pos + 2 );
short propNumber = (short) ( propId & (short) 0x3FFF );
boolean isComplex = ( propId & (short) 0x8000 ) != 0;
boolean isBlipId = ( propId & (short) 0x4000 ) != 0;
if ( isComplex )
complexBytes = propData;
else
complexBytes = 0;
byte propertyType = EscherProperties.getPropertyType( (short) propNumber );
if ( propertyType == EscherPropertyMetaData.TYPE_BOOLEAN )
results.add( new EscherBoolProperty( propNumber, propData ) );
else if ( propertyType == EscherPropertyMetaData.TYPE_RGB )
results.add( new EscherRGBProperty( propNumber, propData ) );
else if ( propertyType == EscherPropertyMetaData.TYPE_SHAPEPATH )
results.add( new EscherShapePathProperty( propNumber, propData ) );
else
{
if ( !isComplex )
results.add( new EscherSimpleProperty( propNumber, propData ) );
else
{
if ( propertyType == EscherPropertyMetaData.TYPE_ARRAY)
results.add( new EscherArrayProperty( propId, new byte[propData]) );
else
results.add( new EscherComplexProperty( propId, new byte[propData]) );
}
}
pos += 6;
// bytesRemaining -= 6 + complexBytes;
}
// Get complex data
for ( Iterator iterator = results.iterator(); iterator.hasNext(); )
{
EscherProperty p = (EscherProperty) iterator.next();
if (p instanceof EscherComplexProperty)
{
if (p instanceof EscherArrayProperty)
{
pos += ((EscherArrayProperty)p).setArrayData(data, pos);
}
else
{
byte[] complexData = ((EscherComplexProperty)p).getComplexData();
System.arraycopy(data, pos, complexData, 0, complexData.length);
pos += complexData.length;
}
}
}
return results;
}
}

View File

@ -0,0 +1,51 @@
package org.apache.poi.ddf;
/**
* This class stores the type and description of an escher property.
*
* @author Glen Stampoultzis (glens at apache.org)
*/
public class EscherPropertyMetaData
{
// Escher property types.
public final static byte TYPE_UNKNOWN = (byte) 0;
public final static byte TYPE_BOOLEAN = (byte) 1;
public final static byte TYPE_RGB = (byte) 2;
public final static byte TYPE_SHAPEPATH = (byte) 3;
public final static byte TYPE_SIMPLE = (byte)4;
public final static byte TYPE_ARRAY = (byte)5;;
private String description;
private byte type;
/**
* @param description The description of the escher property.
*/
public EscherPropertyMetaData( String description )
{
this.description = description;
}
/**
*
* @param description The description of the escher property.
* @param type The type of the property.
*/
public EscherPropertyMetaData( String description, byte type )
{
this.description = description;
this.type = type;
}
public String getDescription()
{
return description;
}
public byte getType()
{
return type;
}
}

View File

@ -0,0 +1,37 @@
package org.apache.poi.ddf;
/**
* A color property.
*
* @author Glen Stampoultzis (glens at apache.org)
*/
public class EscherRGBProperty
extends EscherSimpleProperty
{
public EscherRGBProperty( short propertyNumber, int rgbColor )
{
super( propertyNumber, false, false, rgbColor );
}
public int getRgbColor()
{
return propertyValue;
}
public byte getRed()
{
return (byte) ( propertyValue & 0xFF );
}
public byte getGreen()
{
return (byte) ( (propertyValue >> 8) & 0xFF );
}
public byte getBlue()
{
return (byte) ( (propertyValue >> 16) & 0xFF );
}
}

View File

@ -0,0 +1,273 @@
package org.apache.poi.ddf;
import org.apache.poi.util.LittleEndian;
import java.io.PrintWriter;
import java.util.Collections;
import java.util.List;
/**
* The base abstract record from which all escher records are defined. Subclasses will need
* to define methods for serialization/deserialization and for determining the record size.
*
* @author Glen Stampoultzis
*/
abstract public class EscherRecord
{
private short options;
private short recordId;
/**
* Create a new instance
*/
public EscherRecord()
{
}
/**
* Delegates to fillFields(byte[], int, EscherRecordFactory)
*
* @see #fillFields(byte[], int, org.apache.poi.ddf.EscherRecordFactory)
*/
protected int fillFields( byte[] data, EscherRecordFactory f )
{
return fillFields( data, 0, f );
}
/**
* The contract of this method is to deserialize an escher record including
* it's children.
*
* @param data The byte array containing the serialized escher
* records.
* @param offset The offset into the byte array.
* @param recordFactory A factory for creating new escher records.
* @return The number of bytes written.
*/
public abstract int fillFields( byte[] data, int offset, EscherRecordFactory recordFactory );
/**
* Reads the 8 byte header information and populates the <code>options</code>
* and <code>recordId</code> records.
*
* @param data the byte array to read from
* @param offset the offset to start reading from
* @return the number of bytes remaining in this record. This
* may include the children if this is a container.
*/
protected int readHeader( byte[] data, int offset )
{
EscherRecordHeader header = EscherRecordHeader.readHeader(data, offset);
options = header.getOptions();
recordId = header.getRecordId();
return header.getRemainingBytes();
}
/**
* Determine whether this is a container record by inspecting the option
* field.
* @return true is this is a container field.
*/
public boolean isContainerRecord()
{
return (options & (short)0x000f) == (short)0x000f;
}
/**
* @return The options field for this record. All records have one.
*/
public short getOptions()
{
return options;
}
/**
* Set the options this this record. Container records should have the
* last nibble set to 0xF.
*/
public void setOptions( short options )
{
this.options = options;
}
/**
* Serializes to a new byte array. This is done by delegating to
* serialize(int, byte[]);
*
* @return the serialized record.
* @see #serialize(int, byte[])
*/
public byte[] serialize()
{
byte[] retval = new byte[getRecordSize()];
serialize( 0, retval );
return retval;
}
/**
* Serializes to an existing byte array without serialization listener.
* This is done by delegating to serialize(int, byte[], EscherSerializationListener).
*
* @param offset the offset within the data byte array.
* @param data the data array to serialize to.
* @return The number of bytes written.
*
* @see #serialize(int, byte[], org.apache.poi.ddf.EscherSerializationListener)
*/
public int serialize( int offset, byte[] data)
{
return serialize( offset, data, new NullEscherSerializationListener() );
}
/**
* 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 abstract int serialize( int offset, byte[] data, EscherSerializationListener listener );
/**
* Subclasses should effeciently return the number of bytes required to
* serialize the record.
*
* @return number of bytes
*/
abstract public int getRecordSize();
/**
* Return the current record id.
*
* @return The 16 bit record id.
*/
public short getRecordId()
{
return recordId;
}
/**
* Sets the record id for this record.
*/
public void setRecordId( short recordId )
{
this.recordId = recordId;
}
/**
* @return Returns the children of this record. By default this will
* be an empty list. EscherCotainerRecord is the only record
* that may contain children.
*
* @see EscherContainerRecord
*/
public List getChildRecords() { return Collections.EMPTY_LIST; }
/**
* Sets the child records for this record. By default this will throw
* an exception as only EscherContainerRecords may have children.
*
* @param childRecords Not used in base implementation.
*/
public void setChildRecords( List childRecords ) { throw new IllegalArgumentException("This record does not support child records."); }
/**
* Escher records may need to be clonable in the future.
*/
public Object clone()
{
throw new RuntimeException( "The class " + getClass().getName() + " needs to define a clone method" );
}
/**
* Returns the indexed child record.
*/
public EscherRecord getChild( int index )
{
return (EscherRecord) getChildRecords().get(index);
}
/**
* The display methods allows escher variables to print the record names
* according to their hierarchy.
*
* @param w The print writer to output to.
* @param indent The current indent level.
*/
public void display(PrintWriter w, int indent)
{
for (int i = 0; i < indent * 4; i++) w.print(' ');
w.println(getRecordName());
}
/**
* Subclasses should return the short name for this escher record.
*/
public abstract String getRecordName();
/**
* Returns the instance part of the option record.
*
* @return The instance part of the record
*/
public short getInstance()
{
return (short) ( options >> 4 );
}
/**
* This class reads the standard escher header.
*/
static class EscherRecordHeader
{
private short options;
private short recordId;
private int remainingBytes;
private EscherRecordHeader()
{
}
public static EscherRecordHeader readHeader( byte[] data, int offset )
{
EscherRecordHeader header = new EscherRecordHeader();
header.options = LittleEndian.getShort(data, offset);
header.recordId = LittleEndian.getShort(data, offset + 2);
header.remainingBytes = LittleEndian.getInt( data, offset + 4 );
return header;
}
public short getOptions()
{
return options;
}
public short getRecordId()
{
return recordId;
}
public int getRemainingBytes()
{
return remainingBytes;
}
public String toString()
{
return "EscherRecordHeader{" +
"options=" + options +
", recordId=" + recordId +
", remainingBytes=" + remainingBytes +
"}";
}
}
}

View File

@ -0,0 +1,16 @@
package org.apache.poi.ddf;
/**
* The escher record factory interface allows for the creation of escher
* records from a pointer into a data array.
*
* @author Glen Stampoultzis (glens at apache.org)
*/
public interface EscherRecordFactory
{
/**
* Create a new escher record from the data provided. Does not attempt
* to fill the contents of the record however.
*/
EscherRecord createRecord( byte[] data, int offset );
}

View File

@ -0,0 +1,27 @@
package org.apache.poi.ddf;
/**
* Interface for listening to escher serialization events.
*
* @author Glen Stampoultzis (glens at apache.org)
*/
public interface EscherSerializationListener
{
/**
* Fired before a given escher record is serialized.
*
* @param offset The position in the data array at which the record will be serialized.
* @param recordId The id of the record about to be serialized.
*/
void beforeRecordSerialize(int offset, short recordId, EscherRecord record);
/**
* Fired after a record has been serialized.
*
* @param offset The position of the end of the serialized record + 1
* @param recordId The id of the record about to be serialized
* @param size The number of bytes written for this record. If it is a container
* record then this will include the size of any included records.
*/
void afterRecordSerialize(int offset, short recordId, int size, EscherRecord record);
}

View File

@ -0,0 +1,25 @@
package org.apache.poi.ddf;
/**
* Defines the constants for the various possible shape paths.
*
* @author Glen Stampoultzis (glens at apache.org)
*/
public class EscherShapePathProperty
extends EscherSimpleProperty
{
public static final int LINE_OF_STRAIGHT_SEGMENTS = 0;
public static final int CLOSED_POLYGON = 1;
public static final int CURVES = 2;
public static final int CLOSED_CURVES = 3;
public static final int COMPLEX = 4;
public EscherShapePathProperty( short propertyNumber, int shapePath )
{
super( propertyNumber, false, false, shapePath );
}
}

View File

@ -0,0 +1,103 @@
package org.apache.poi.ddf;
import org.apache.poi.util.LittleEndian;
import org.apache.poi.util.HexDump;
/**
* A simple property is of fixed length and as a property number in addition
* to a 32-bit value. Properties that can't be stored in only 32-bits are
* stored as EscherComplexProperty objects.
*
* @author Glen Stampoultzis (glens at apache.org)
*/
public class EscherSimpleProperty extends EscherProperty
{
protected int propertyValue;
/**
* The id is distinct from the actual property number. The id includes the property number the blip id
* flag and an indicator whether the property is complex or not.
*/
public EscherSimpleProperty( short id, int propertyValue )
{
super( id );
this.propertyValue = propertyValue;
}
/**
* Constructs a new escher property. The three parameters are combined to form a property
* id.
*/
public EscherSimpleProperty( short propertyNumber, boolean isComplex, boolean isBlipId, int propertyValue )
{
super( propertyNumber, isComplex, isBlipId );
this.propertyValue = propertyValue;
}
/**
* Serialize the simple part of the escher record.
*
* @return the number of bytes serialized.
*/
public int serializeSimplePart( byte[] data, int offset )
{
LittleEndian.putShort(data, offset, getId());
LittleEndian.putInt(data, offset + 2, propertyValue);
return 6;
}
/**
* Escher properties consist of a simple fixed length part and a complex variable length part.
* The fixed length part is serialized first.
*/
public int serializeComplexPart( byte[] data, int pos )
{
return 0;
}
/**
* @return Return the 32 bit value of this property.
*/
public int getPropertyValue()
{
return propertyValue;
}
/**
* Returns true if one escher property is equal to another.
*/
public boolean equals( Object o )
{
if ( this == o ) return true;
if ( !( o instanceof EscherSimpleProperty ) ) return false;
final EscherSimpleProperty escherSimpleProperty = (EscherSimpleProperty) o;
if ( propertyValue != escherSimpleProperty.propertyValue ) return false;
if ( getId() != escherSimpleProperty.getId() ) return false;
return true;
}
/**
* Returns a hashcode so that this object can be stored in collections that
* require the use of such things.
*/
public int hashCode()
{
return propertyValue;
}
/**
* @return the string representation of this property.
*/
public String toString()
{
return "propNum: " + getPropertyNumber()
+ ", propName: " + EscherProperties.getPropertyName( getPropertyNumber() )
+ ", complex: " + isComplex()
+ ", blipId: " + isBlipId()
+ ", value: " + propertyValue + " (0x" + HexDump.toHex(propertyValue) + ")";
}
}

View File

@ -0,0 +1,201 @@
package org.apache.poi.ddf;
import org.apache.poi.util.HexDump;
import org.apache.poi.util.LittleEndian;
/**
* Together the the EscherOptRecord this record defines some of the basic
* properties of a shape.
*
* @author Glen Stampoultzis (glens at apache.org)
*/
public class EscherSpRecord
extends EscherRecord
{
public static final short RECORD_ID = (short) 0xF00A;
public static final String RECORD_DESCRIPTION = "MsofbtSp";
public static final int FLAG_GROUP = 0x0001;
public static final int FLAG_CHILD = 0x0002;
public static final int FLAG_PATRIARCH = 0x0004;
public static final int FLAG_DELETED = 0x0008;
public static final int FLAG_OLESHAPE = 0x0010;
public static final int FLAG_HAVEMASTER = 0x0020;
public static final int FLAG_FLIPHORIZ = 0x0040;
public static final int FLAG_FLIPVERT = 0x0080;
public static final int FLAG_CONNECTOR = 0x0100;
public static final int FLAG_HAVEANCHOR = 0x0200;
public static final int FLAG_BACKGROUND = 0x0400;
public static final int FLAG_HASSHAPETYPE = 0x0800;
private int field_1_shapeId;
private int field_2_flags;
/**
* 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 <code>data</code>.
* @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 bytesRemaining = readHeader( data, offset );
int pos = offset + 8;
int size = 0;
field_1_shapeId = LittleEndian.getInt( data, pos + size ); size += 4;
field_2_flags = LittleEndian.getInt( data, pos + size ); size += 4;
// bytesRemaining -= size;
// remainingData = new byte[bytesRemaining];
// System.arraycopy( data, pos + size, remainingData, 0, bytesRemaining );
return getRecordSize();
}
/**
* This method serializes this escher record into a byte array.
*
* @param offset The offset into <code>data</code> to start writing the record data to.
* @param data The byte array to serialize to.
* @param listener A listener to retrieve start and end callbacks. Use a <code>NullEscherSerailizationListener</code> to ignore these events.
* @return The number of bytes written.
*
* @see NullEscherSerializationListener
*/
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() );
int remainingBytes = 8;
LittleEndian.putInt( data, offset + 4, remainingBytes );
LittleEndian.putInt( data, offset + 8, field_1_shapeId );
LittleEndian.putInt( data, offset + 12, field_2_flags );
// System.arraycopy( remainingData, 0, data, offset + 26, remainingData.length );
// int pos = offset + 8 + 18 + remainingData.length;
listener.afterRecordSerialize( offset + getRecordSize(), getRecordId(), getRecordSize(), this );
return 8 + 8;
}
/**
* Returns the number of bytes that are required to serialize this record.
*
* @return Number of bytes
*/
public int getRecordSize()
{
return 8 + 8;
}
/**
* @return the 16 bit identifier for this record.
*/
public short getRecordId()
{
return RECORD_ID;
}
/**
* The short name for this record
*/
public String getRecordName()
{
return "Sp";
}
/**
* @return the string representing this shape.
*/
public String toString()
{
String nl = System.getProperty("line.separator");
return getClass().getName() + ":" + nl +
" RecordId: 0x" + HexDump.toHex(RECORD_ID) + nl +
" Options: 0x" + HexDump.toHex(getOptions()) + nl +
" ShapeId: " + field_1_shapeId + nl +
" Flags: " + decodeFlags(field_2_flags) + " (0x" + HexDump.toHex(field_2_flags) + ")" + nl;
}
/**
* Converts the shape flags into a more descriptive name.
*/
private String decodeFlags( int flags )
{
StringBuffer result = new StringBuffer();
result.append( ( flags & FLAG_GROUP ) != 0 ? "|GROUP" : "" );
result.append( ( flags & FLAG_CHILD ) != 0 ? "|CHILD" : "" );
result.append( ( flags & FLAG_PATRIARCH ) != 0 ? "|PATRIARCH" : "" );
result.append( ( flags & FLAG_DELETED ) != 0 ? "|DELETED" : "" );
result.append( ( flags & FLAG_OLESHAPE ) != 0 ? "|OLESHAPE" : "" );
result.append( ( flags & FLAG_HAVEMASTER ) != 0 ? "|HAVEMASTER" : "" );
result.append( ( flags & FLAG_FLIPHORIZ ) != 0 ? "|FLIPHORIZ" : "" );
result.append( ( flags & FLAG_FLIPVERT ) != 0 ? "|FLIPVERT" : "" );
result.append( ( flags & FLAG_CONNECTOR ) != 0 ? "|CONNECTOR" : "" );
result.append( ( flags & FLAG_HAVEANCHOR ) != 0 ? "|HAVEANCHOR" : "" );
result.append( ( flags & FLAG_BACKGROUND ) != 0 ? "|BACKGROUND" : "" );
result.append( ( flags & FLAG_HASSHAPETYPE ) != 0 ? "|HASSHAPETYPE" : "" );
result.deleteCharAt(0);
return result.toString();
}
/**
* @return A number that identifies this shape
*/
public int getShapeId()
{
return field_1_shapeId;
}
/**
* Sets a number that identifies this shape.
*/
public void setShapeId( int field_1_shapeId )
{
this.field_1_shapeId = field_1_shapeId;
}
/**
* The flags that apply to this shape.
*
* @see #FLAG_GROUP
* @see #FLAG_CHILD
* @see #FLAG_PATRIARCH
* @see #FLAG_DELETED
* @see #FLAG_OLESHAPE
* @see #FLAG_HAVEMASTER
* @see #FLAG_FLIPHORIZ
* @see #FLAG_FLIPVERT
* @see #FLAG_CONNECTOR
* @see #FLAG_HAVEANCHOR
* @see #FLAG_BACKGROUND
* @see #FLAG_HASSHAPETYPE
*/
public int getFlags()
{
return field_2_flags;
}
/**
* The flags that apply to this shape.
*
* @see #FLAG_GROUP
* @see #FLAG_CHILD
* @see #FLAG_PATRIARCH
* @see #FLAG_DELETED
* @see #FLAG_OLESHAPE
* @see #FLAG_HAVEMASTER
* @see #FLAG_FLIPHORIZ
* @see #FLAG_FLIPVERT
* @see #FLAG_CONNECTOR
* @see #FLAG_HAVEANCHOR
* @see #FLAG_BACKGROUND
* @see #FLAG_HASSHAPETYPE
*/
public void setFlags( int field_2_flags )
{
this.field_2_flags = field_2_flags;
}
}

View File

@ -0,0 +1,192 @@
package org.apache.poi.ddf;
import org.apache.poi.util.HexDump;
import org.apache.poi.util.LittleEndian;
import org.apache.poi.hssf.record.RecordFormatException;
/**
* The spgr record defines information about a shape group. Groups in escher
* are simply another form of shape that you can't physically see.
*
* @author Glen Stampoultzis (glens at apache.org)
*/
public class EscherSpgrRecord
extends EscherRecord
{
public static final short RECORD_ID = (short) 0xF009;
public static final String RECORD_DESCRIPTION = "MsofbtSpgr";
private int field_1_rectX1;
private int field_2_rectY1;
private int field_3_rectX2;
private int field_4_rectY2;
/**
* 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 <code>data</code>.
* @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 bytesRemaining = readHeader( data, offset );
int pos = offset + 8;
int size = 0;
field_1_rectX1 = LittleEndian.getInt( data, pos + size );size+=4;
field_2_rectY1 = LittleEndian.getInt( data, pos + size );size+=4;
field_3_rectX2 = LittleEndian.getInt( data, pos + size );size+=4;
field_4_rectY2 = LittleEndian.getInt( data, pos + size );size+=4;
bytesRemaining -= size;
if (bytesRemaining != 0) throw new RecordFormatException("Expected no remaining bytes but got " + bytesRemaining);
// remainingData = new byte[bytesRemaining];
// System.arraycopy( data, pos + size, remainingData, 0, bytesRemaining );
return 8 + size + bytesRemaining;
}
/**
* This method serializes this escher record into a byte array.
*
* @param offset The offset into <code>data</code> to start writing the record data to.
* @param data The byte array to serialize to.
* @param listener A listener to retrieve start and end callbacks. Use a <code>NullEscherSerailizationListener</code> to ignore these events.
* @return The number of bytes written.
*
* @see NullEscherSerializationListener
*/
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() );
int remainingBytes = 16;
LittleEndian.putInt( data, offset + 4, remainingBytes );
LittleEndian.putInt( data, offset + 8, field_1_rectX1 );
LittleEndian.putInt( data, offset + 12, field_2_rectY1 );
LittleEndian.putInt( data, offset + 16, field_3_rectX2 );
LittleEndian.putInt( data, offset + 20, field_4_rectY2 );
// System.arraycopy( remainingData, 0, data, offset + 26, remainingData.length );
// int pos = offset + 8 + 18 + remainingData.length;
listener.afterRecordSerialize( offset + getRecordSize(), getRecordId(), offset + getRecordSize(), this );
return 8 + 16;
}
/**
* Returns the number of bytes that are required to serialize this record.
*
* @return Number of bytes
*/
public int getRecordSize()
{
return 8 + 16;
}
/**
* The 16 bit identifier of this shape group record.
*/
public short getRecordId()
{
return RECORD_ID;
}
/**
* The short name for this record
*/
public String getRecordName()
{
return "Spgr";
}
/**
* @return the string representation of this record.
*/
public String toString()
{
String nl = System.getProperty("line.separator");
// String extraData;
// ByteArrayOutputStream b = new ByteArrayOutputStream();
// try
// {
// HexDump.dump(this.remainingData, 0, b, 0);
// extraData = b.toString();
// }
// catch ( Exception e )
// {
// extraData = "error";
// }
return getClass().getName() + ":" + nl +
" RecordId: 0x" + HexDump.toHex(RECORD_ID) + nl +
" Options: 0x" + HexDump.toHex(getOptions()) + nl +
" RectX: " + field_1_rectX1 + nl +
" RectY: " + field_2_rectY1 + nl +
" RectWidth: " + field_3_rectX2 + nl +
" RectHeight: " + field_4_rectY2 + nl;
}
/**
* The starting top-left coordinate of child records.
*/
public int getRectX1()
{
return field_1_rectX1;
}
/**
* The starting top-left coordinate of child records.
*/
public void setRectX1( int x1 )
{
this.field_1_rectX1 = x1;
}
/**
* The starting top-left coordinate of child records.
*/
public int getRectY1()
{
return field_2_rectY1;
}
/**
* The starting top-left coordinate of child records.
*/
public void setRectY1( int y1 )
{
this.field_2_rectY1 = y1;
}
/**
* The starting bottom-right coordinate of child records.
*/
public int getRectX2()
{
return field_3_rectX2;
}
/**
* The starting bottom-right coordinate of child records.
*/
public void setRectX2( int x2 )
{
this.field_3_rectX2 = x2;
}
/**
* The starting bottom-right coordinate of child records.
*/
public int getRectY2()
{
return field_4_rectY2;
}
/**
* The starting bottom-right coordinate of child records.
*/
public void setRectY2( int field_4_rectY2 )
{
this.field_4_rectY2 = field_4_rectY2;
}
}

View File

@ -0,0 +1,171 @@
package org.apache.poi.ddf;
import org.apache.poi.util.HexDump;
import org.apache.poi.util.LittleEndian;
import org.apache.poi.hssf.record.RecordFormatException;
/**
* A list of the most recently used colours for the drawings contained in
* this document.
*
* @author Glen Stampoultzis (glens at apache.org)
*/
public class EscherSplitMenuColorsRecord
extends EscherRecord
{
public static final short RECORD_ID = (short) 0xF11E;
public static final String RECORD_DESCRIPTION = "MsofbtSplitMenuColors";
private int field_1_color1;
private int field_2_color2;
private int field_3_color3;
private int field_4_color4;
/**
* 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 <code>data</code>.
* @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 bytesRemaining = readHeader( data, offset );
int pos = offset + 8;
int size = 0;
field_1_color1 = LittleEndian.getInt( data, pos + size );size+=4;
field_2_color2 = LittleEndian.getInt( data, pos + size );size+=4;
field_3_color3 = LittleEndian.getInt( data, pos + size );size+=4;
field_4_color4 = LittleEndian.getInt( data, pos + size );size+=4;
bytesRemaining -= size;
if (bytesRemaining != 0)
throw new RecordFormatException("Expecting no remaining data but got " + bytesRemaining + " byte(s).");
return 8 + size + bytesRemaining;
}
/**
* This method serializes this escher record into a byte array.
*
* @param offset The offset into <code>data</code> to start writing the record data to.
* @param data The byte array to serialize to.
* @param listener A listener to retrieve start and end callbacks. Use a <code>NullEscherSerailizationListener</code> to ignore these events.
* @return The number of bytes written.
*
* @see NullEscherSerializationListener
*/
public int serialize( int offset, byte[] data, EscherSerializationListener listener )
{
// int field_2_numIdClusters = field_5_fileIdClusters.length + 1;
listener.beforeRecordSerialize( offset, getRecordId(), this );
int pos = offset;
LittleEndian.putShort( data, pos, getOptions() ); pos += 2;
LittleEndian.putShort( data, pos, getRecordId() ); pos += 2;
int remainingBytes = getRecordSize() - 8;
LittleEndian.putInt( data, pos, remainingBytes ); pos += 4;
LittleEndian.putInt( data, pos, field_1_color1 ); pos += 4;
LittleEndian.putInt( data, pos, field_2_color2 ); pos += 4;
LittleEndian.putInt( data, pos, field_3_color3 ); pos += 4;
LittleEndian.putInt( data, pos, field_4_color4 ); pos += 4;
listener.afterRecordSerialize( pos, getRecordId(), pos - offset, this );
return getRecordSize();
}
/**
* Returns the number of bytes that are required to serialize this record.
*
* @return Number of bytes
*/
public int getRecordSize()
{
return 8 + 4 * 4;
}
/**
* @return the 16 bit identifer for this record.
*/
public short getRecordId()
{
return RECORD_ID;
}
/**
* The short name for this record
*/
public String getRecordName()
{
return "SplitMenuColors";
}
/**
* @return a string representation of this record.
*/
public String toString()
{
String nl = System.getProperty("line.separator");
// String extraData;
// ByteArrayOutputStream b = new ByteArrayOutputStream();
// try
// {
// HexDump.dump(this.remainingData, 0, b, 0);
// extraData = b.toString();
// }
// catch ( Exception e )
// {
// extraData = "error";
// }
return getClass().getName() + ":" + nl +
" RecordId: 0x" + HexDump.toHex(RECORD_ID) + nl +
" Options: 0x" + HexDump.toHex(getOptions()) + nl +
" Color1: 0x" + HexDump.toHex(field_1_color1) + nl +
" Color2: 0x" + HexDump.toHex(field_2_color2) + nl +
" Color3: 0x" + HexDump.toHex(field_3_color3) + nl +
" Color4: 0x" + HexDump.toHex(field_4_color4) + nl +
"";
}
public int getColor1()
{
return field_1_color1;
}
public void setColor1( int field_1_color1 )
{
this.field_1_color1 = field_1_color1;
}
public int getColor2()
{
return field_2_color2;
}
public void setColor2( int field_2_color2 )
{
this.field_2_color2 = field_2_color2;
}
public int getColor3()
{
return field_3_color3;
}
public void setColor3( int field_3_color3 )
{
this.field_3_color3 = field_3_color3;
}
public int getColor4()
{
return field_4_color4;
}
public void setColor4( int field_4_color4 )
{
this.field_4_color4 = field_4_color4;
}
}

View File

@ -0,0 +1,215 @@
/* ====================================================================
* The Apache Software License, Version 1.1
*
* Copyright (c) 2002 The Apache Software Foundation. All rights
* reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in
* the documentation and/or other materials provided with the
* distribution.
*
* 3. The end-user documentation included with the redistribution,
* if any, must include the following acknowledgment:
* "This product includes software developed by the
* Apache Software Foundation (http://www.apache.org/)."
* Alternately, this acknowledgment may appear in the software itself,
* if and wherever such third-party acknowledgments normally appear.
*
* 4. The names "Apache" and "Apache Software Foundation" and
* "Apache POI" must not be used to endorse or promote products
* derived from this software without prior written permission. For
* written permission, please contact apache@apache.org.
*
* 5. Products derived from this software may not be called "Apache",
* "Apache POI", nor may "Apache" appear in their name, without
* prior written permission of the Apache Software Foundation.
*
* THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR
* ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
* USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
* OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
* ====================================================================
*
* This software consists of voluntary contributions made by many
* individuals on behalf of the Apache Software Foundation. For more
* information on the Apache Software Foundation, please see
* <http://www.apache.org/>.
*/
package org.apache.poi.ddf;
import org.apache.poi.util.HexDump;
import org.apache.poi.util.LittleEndian;
import org.apache.poi.hssf.record.RecordFormatException;
import java.util.Iterator;
import java.util.List;
import java.util.ArrayList;
/**
* Supports text boxes
*
* @author Glen Stampoultzis (glens at apache.org)
*/
public class EscherTextboxRecord extends EscherRecord
{
public static final short RECORD_ID = (short)0xF00D;
public static final String RECORD_DESCRIPTION = "msofbtClientTextbox";
private static final byte[] NO_BYTES = new byte[0];
/** The data for this record not including the the 8 byte header */
private byte[] thedata = NO_BYTES;
public EscherTextboxRecord()
{
}
/**
* 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 <code>data</code>.
* @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 bytesRemaining = readHeader( data, offset );
if ( isContainerRecord() )
{
int bytesWritten = 0;
thedata = new byte[0];
offset += 8;
bytesWritten += 8;
while ( bytesRemaining > 0 )
{
EscherRecord child = recordFactory.createRecord( data, offset );
int childBytesWritten = child.fillFields( data, offset, recordFactory );
bytesWritten += childBytesWritten;
offset += childBytesWritten;
bytesRemaining -= childBytesWritten;
getChildRecords().add( child );
}
return bytesWritten;
}
else
{
thedata = new byte[bytesRemaining];
System.arraycopy( data, offset + 8, thedata, 0, bytesRemaining );
return bytesRemaining + 8;
}
}
/**
* Writes this record and any contained records to the supplied byte
* array.
*
* @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());
int remainingBytes = thedata.length;
for ( Iterator iterator = getChildRecords().iterator(); iterator.hasNext(); )
{
EscherRecord r = (EscherRecord) iterator.next();
remainingBytes += r.getRecordSize();
}
LittleEndian.putInt(data, offset+4, remainingBytes);
System.arraycopy(thedata, 0, data, offset+8, thedata.length);
int pos = offset+8+thedata.length;
for ( Iterator iterator = getChildRecords().iterator(); iterator.hasNext(); )
{
EscherRecord r = (EscherRecord) iterator.next();
pos += r.serialize(pos, data, listener );
}
listener.afterRecordSerialize( pos, getRecordId(), pos - offset, this );
int size = pos - offset;
if (size != getRecordSize())
throw new RecordFormatException(size + " bytes written but getRecordSize() reports " + getRecordSize());
return size;
}
/**
* Returns any extra data associated with this record. In practice excel
* does not seem to put anything here.
*/
public byte[] getData()
{
return thedata;
}
/**
* Returns the number of bytes that are required to serialize this record.
*
* @return Number of bytes
*/
public int getRecordSize()
{
return 8 + thedata.length;
}
public Object clone()
{
// shallow clone
return super.clone();
}
/**
* The short name for this record
*/
public String getRecordName()
{
return "ClientTextbox";
}
public String toString()
{
String nl = System.getProperty( "line.separator" );
String theDumpHex = "";
try
{
if (thedata.length != 0)
{
theDumpHex = " Extra Data:" + nl;
theDumpHex += HexDump.dump(thedata, 0, 0);
}
}
catch ( Exception e )
{
theDumpHex = "Error!!";
}
return getClass().getName() + ":" + nl +
" isContainer: " + isContainerRecord() + nl +
" options: 0x" + HexDump.toHex( getOptions() ) + nl +
" recordId: 0x" + HexDump.toHex( getRecordId() ) + nl +
" numchildren: " + getChildRecords().size() + nl +
theDumpHex;
}
}

View File

@ -0,0 +1,20 @@
package org.apache.poi.ddf;
/**
* Ignores all serialization events.
*
* @author Glen Stampoultzis (glens at apache.org)
*/
public class NullEscherSerializationListener implements EscherSerializationListener
{
public void beforeRecordSerialize( int offset, short recordId, EscherRecord record )
{
// do nothing
}
public void afterRecordSerialize( int offset, short recordId, int size, EscherRecord record )
{
// do nothing
}
}

View File

@ -0,0 +1,234 @@
/* ====================================================================
* The Apache Software License, Version 1.1
*
* Copyright (c) 2002 The Apache Software Foundation. All rights
* reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in
* the documentation and/or other materials provided with the
* distribution.
*
* 3. The end-user documentation included with the redistribution,
* if any, must include the following acknowledgment:
* "This product includes software developed by the
* Apache Software Foundation (http://www.apache.org/)."
* Alternately, this acknowledgment may appear in the software itself,
* if and wherever such third-party acknowledgments normally appear.
*
* 4. The names "Apache" and "Apache Software Foundation" and
* "Apache POI" must not be used to endorse or promote products
* derived from this software without prior written permission. For
* written permission, please contact apache@apache.org.
*
* 5. Products derived from this software may not be called "Apache",
* "Apache POI", nor may "Apache" appear in their name, without
* prior written permission of the Apache Software Foundation.
*
* THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR
* ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
* USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
* OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
* ====================================================================
*
* This software consists of voluntary contributions made by many
* individuals on behalf of the Apache Software Foundation. For more
* information on the Apache Software Foundation, please see
* <http://www.apache.org/>.
*/
package org.apache.poi.ddf;
import org.apache.poi.util.HexDump;
import org.apache.poi.util.LittleEndian;
import java.util.Iterator;
import java.util.List;
import java.util.ArrayList;
/**
* This record is used whenever a escher record is encountered that
* we do not explicitly support.
*
* @author Glen Stampoultzis (glens at apache.org)
*/
public class UnknownEscherRecord extends EscherRecord
{
private static final byte[] NO_BYTES = new byte[0];
/** The data for this record not including the the 8 byte header */
private byte[] thedata = NO_BYTES;
private List childRecords = new ArrayList();
public UnknownEscherRecord()
{
}
/**
* 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 <code>data</code>.
* @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 bytesRemaining = readHeader( data, offset );
if ( isContainerRecord() )
{
int bytesWritten = 0;
thedata = new byte[0];
offset += 8;
bytesWritten += 8;
while ( bytesRemaining > 0 )
{
EscherRecord child = recordFactory.createRecord( data, offset );
int childBytesWritten = child.fillFields( data, offset, recordFactory );
bytesWritten += childBytesWritten;
offset += childBytesWritten;
bytesRemaining -= childBytesWritten;
getChildRecords().add( child );
}
return bytesWritten;
}
else
{
thedata = new byte[bytesRemaining];
System.arraycopy( data, offset + 8, thedata, 0, bytesRemaining );
return bytesRemaining + 8;
}
}
/**
* Writes this record and any contained records to the supplied byte
* array.
*
* @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());
int remainingBytes = thedata.length;
for ( Iterator iterator = getChildRecords().iterator(); iterator.hasNext(); )
{
EscherRecord r = (EscherRecord) iterator.next();
remainingBytes += r.getRecordSize();
}
LittleEndian.putInt(data, offset+4, remainingBytes);
System.arraycopy(thedata, 0, data, offset+8, thedata.length);
int pos = offset+8+thedata.length;
for ( Iterator iterator = getChildRecords().iterator(); iterator.hasNext(); )
{
EscherRecord r = (EscherRecord) iterator.next();
pos += r.serialize(pos, data, listener );
}
listener.afterRecordSerialize( pos, getRecordId(), pos - offset, this );
return pos - offset;
}
public byte[] getData()
{
return thedata;
}
/**
* Returns the number of bytes that are required to serialize this record.
*
* @return Number of bytes
*/
public int getRecordSize()
{
return 8 + thedata.length;
}
public List getChildRecords()
{
return childRecords;
}
public void setChildRecords( List childRecords )
{
this.childRecords = childRecords;
}
public Object clone()
{
// shallow clone
return super.clone();
}
/**
* The short name for this record
*/
public String getRecordName()
{
return "Unknown 0x" + HexDump.toHex(getRecordId());
}
public String toString()
{
String nl = System.getProperty( "line.separator" );
StringBuffer children = new StringBuffer();
if ( getChildRecords().size() > 0 )
{
children.append( " children: " + nl );
for ( Iterator iterator = getChildRecords().iterator(); iterator.hasNext(); )
{
EscherRecord record = (EscherRecord) iterator.next();
children.append( record.toString() );
children.append( nl );
}
}
String theDumpHex = "";
try
{
if (thedata.length != 0)
{
theDumpHex = " Extra Data:" + nl;
theDumpHex += HexDump.dump(thedata, 0, 0);
}
}
catch ( Exception e )
{
theDumpHex = "Error!!";
}
return getClass().getName() + ":" + nl +
" isContainer: " + isContainerRecord() + nl +
" options: 0x" + HexDump.toHex( getOptions() ) + nl +
" recordId: 0x" + HexDump.toHex( getRecordId() ) + nl +
" numchildren: " + getChildRecords().size() + nl +
theDumpHex +
children.toString();
}
public void addChildRecord( EscherRecord childRecord )
{
getChildRecords().add( childRecord );
}
}

View File

@ -0,0 +1,11 @@
<html>
<body>
<p>This package contains classes for decoding the Microsoft Office
Drawing format otherwise known as escher henceforth known in POI
as the Dreadful Drawing Format.
</p>
</body>
</html>