git-svn-id: https://svn.apache.org/repos/asf/jakarta/poi/trunk@353660 13f79535-47bb-0310-9956-ffa450edef68tags/BEFORE_RICHTEXT
@@ -256,10 +256,10 @@ | |||
<copy todir="${main.output.dir}"> | |||
<fileset dir="${main.resource1.dir}"/> | |||
</copy> | |||
<javac srcdir="${main.src}" destdir="${main.output.dir}" debug="on"> | |||
<javac srcdir="${main.src}" destdir="${main.output.dir}" debug="on" fork="yes" includeAntRuntime="no" failonerror="true"> | |||
<classpath refid="main.classpath"/> | |||
</javac> | |||
<javac srcdir="${main.src.test}" destdir="${main.output.test.dir}" debug="on"> | |||
<javac srcdir="${main.src.test}" destdir="${main.output.test.dir}" debug="on" fork="yes" includeAntRuntime="no" failonerror="true"> | |||
<classpath> | |||
<path refid="main.classpath"/> | |||
<pathelement location="${main.output.dir}"/> | |||
@@ -269,10 +269,10 @@ | |||
</target> | |||
<target name="compile-scratchpad" depends="init"> | |||
<javac srcdir="${scratchpad.src}" destdir="${scratchpad.output.dir}" debug="on"> | |||
<javac srcdir="${scratchpad.src}" destdir="${scratchpad.output.dir}" debug="on" fork="yes" includeAntRuntime="no" failonerror="true"> | |||
<classpath refid="scratchpad.classpath"/> | |||
</javac> | |||
<javac srcdir="${scratchpad.src.test}" destdir="${scratchpad.output.test.dir}" debug="on"> | |||
<javac srcdir="${scratchpad.src.test}" destdir="${scratchpad.output.test.dir}" debug="on" fork="yes" includeAntRuntime="no" failonerror="true"> | |||
<classpath> | |||
<path refid="scratchpad.classpath"/> | |||
<pathelement location="${scratchpad.output.dir}"/> | |||
@@ -282,10 +282,10 @@ | |||
</target> | |||
<target name="compile-contrib" depends="init"> | |||
<javac srcdir="${contrib.src}" destdir="${contrib.output.dir}" debug="on"> | |||
<javac srcdir="${contrib.src}" destdir="${contrib.output.dir}" debug="on" fork="yes" includeAntRuntime="no" failonerror="true"> | |||
<classpath refid="contrib.classpath"/> | |||
</javac> | |||
<javac srcdir="${contrib.src.test}" destdir="${contrib.output.test.dir}" debug="on"> | |||
<javac srcdir="${contrib.src.test}" destdir="${contrib.output.test.dir}" debug="on" fork="yes" includeAntRuntime="no" failonerror="true"> | |||
<classpath> | |||
<path refid="contrib.classpath"/> | |||
<pathelement location="${contrib.output.dir}"/> |
@@ -31,20 +31,21 @@ | |||
<li><link href="#CustomColors">Custom colors</link></li> | |||
<li><link href="#ReadWriteWorkbook">Reading and writing</link></li> | |||
<li><link href="#NewLinesInCells">Use newlines in cells.</link></li> | |||
<li><link href="#DataFormats">Create user defined data formats.</link></li> | |||
<li><link href="#DataFormats">Create user defined data formats</link></li> | |||
<li><link href="#FitTo">Fit Sheet to One Page</link></li> | |||
<li><link href="#PrintArea2">Set print area for a sheet.</link></li> | |||
<li><link href="#FooterPageNumbers">Set page numbers on the footer of a sheet.</link></li> | |||
<li><link href="#ShiftRows">Shift rows.</link></li> | |||
<li><link href="#SelectSheet">Set a sheet as selected.</link></li> | |||
<li><link href="#Zoom">Set the zoom magnification for a sheet.</link></li> | |||
<li><link href="#Splits">Create split and freeze panes.</link></li> | |||
<li><link href="#Repeating">Repeating rows and columns.</link></li> | |||
<li><link href="#HeaderFooter">Headers and Footers.</link></li> | |||
<li><link href="#DrawingShapes">Drawing Shapes.</link></li> | |||
<li><link href="#StylingShapes">Styling Shapes.</link></li> | |||
<li><link href="#Graphics2d">Shapes and Graphics2d.</link></li> | |||
<li><link href="#Outlining">Outlining.</link></li> | |||
<li><link href="#PrintArea2">Set print area for a sheet</link></li> | |||
<li><link href="#FooterPageNumbers">Set page numbers on the footer of a sheet</link></li> | |||
<li><link href="#ShiftRows">Shift rows</link></li> | |||
<li><link href="#SelectSheet">Set a sheet as selected</link></li> | |||
<li><link href="#Zoom">Set the zoom magnification for a sheet</link></li> | |||
<li><link href="#Splits">Create split and freeze panes</link></li> | |||
<li><link href="#Repeating">Repeating rows and columns</link></li> | |||
<li><link href="#HeaderFooter">Headers and Footers</link></li> | |||
<li><link href="#DrawingShapes">Drawing Shapes</link></li> | |||
<li><link href="#StylingShapes">Styling Shapes</link></li> | |||
<li><link href="#Graphics2d">Shapes and Graphics2d</link></li> | |||
<li><link href="#Outlining">Outlining</link></li> | |||
<li><link href="#Images">Images</link></li> | |||
</ul> | |||
</section> | |||
<section><title>Features</title> | |||
@@ -940,5 +941,34 @@ | |||
</section> | |||
</section> | |||
</section> | |||
<anchor id="Images"/> | |||
<section> | |||
<title>Images</title> | |||
<p> | |||
Images are part of the drawing support. To add an image just | |||
call <code>createPicture()</code> on the drawing patriarch. | |||
At the time of writing the following types are supported: | |||
</p> | |||
<ul> | |||
<li>PNG</li> | |||
<li>JPG</li> | |||
<li>DIB</li> | |||
</ul> | |||
<p> | |||
It is not currently possible to read existing images and it | |||
should be noted that any existing drawings may be erased | |||
once you add a image to a sheet. | |||
</p> | |||
<source> | |||
// Create the drawing patriarch. This is the top level container for | |||
// all shapes. This will clear out any existing shapes for that sheet. | |||
HSSFPatriarch patriarch = sheet5.createDrawingPatriarch(); | |||
HSSFClientAnchor anchor; | |||
anchor = new HSSFClientAnchor(0,0,0,255,(short)2,2,(short)4,7); | |||
anchor.setAnchorType( 2 ); | |||
patriarch.createPicture(anchor, loadPicture( "src/resources/logos/logoKarmokar4.png", wb )); | |||
</source> | |||
</section> | |||
</body> | |||
</document> |
@@ -14,6 +14,7 @@ | |||
<release version="unreleased" date="???"> | |||
<action dev="POI-DEVELOPERS" type="fix" context="All">Bugzilla Bug 29976 [PATCH] HSSF hyperlink formula size problem</action> | |||
<action dev="POI-DEVELOPERS" type="add" context="All">Image writing support</action> | |||
</release> | |||
<release version="2.5.1-FINAL" date="29 Feburary 2004"> |
@@ -19,9 +19,7 @@ package org.apache.poi.hssf.usermodel.examples; | |||
import org.apache.poi.hssf.usermodel.*; | |||
import java.io.IOException; | |||
import java.io.FileOutputStream; | |||
import java.io.FileNotFoundException; | |||
import java.io.*; | |||
/** | |||
* Demonstrates how to use the office drawing capabilities of POI. | |||
@@ -39,12 +37,14 @@ public class OfficeDrawing | |||
HSSFSheet sheet2 = wb.createSheet("second sheet"); | |||
HSSFSheet sheet3 = wb.createSheet("third sheet"); | |||
HSSFSheet sheet4 = wb.createSheet("fourth sheet"); | |||
HSSFSheet sheet5 = wb.createSheet("fifth sheet"); | |||
// Draw stuff in them | |||
drawSheet1( sheet1 ); | |||
drawSheet2( sheet2 ); | |||
drawSheet3( sheet3 ); | |||
drawSheet4( sheet4, wb ); | |||
drawSheet5( sheet5, wb ); | |||
// Write the file out. | |||
FileOutputStream fileOut = new FileOutputStream("workbook.xls"); | |||
@@ -143,6 +143,53 @@ public class OfficeDrawing | |||
textbox3.setNoFill(true); // make it transparent | |||
} | |||
private static void drawSheet5( HSSFSheet sheet5, HSSFWorkbook wb ) throws IOException | |||
{ | |||
// Create the drawing patriarch. This is the top level container for | |||
// all shapes. This will clear out any existing shapes for that sheet. | |||
HSSFPatriarch patriarch = sheet5.createDrawingPatriarch(); | |||
HSSFClientAnchor anchor; | |||
anchor = new HSSFClientAnchor(0,0,0,255,(short)2,2,(short)4,7); | |||
anchor.setAnchorType( 2 ); | |||
patriarch.createPicture(anchor, loadPicture( "src/resources/logos/logoKarmokar4.png", wb )); | |||
anchor = new HSSFClientAnchor(0,0,0,255,(short)4,2,(short)5,7); | |||
anchor.setAnchorType( 2 ); | |||
patriarch.createPicture(anchor, loadPicture( "src/resources/logos/logoKarmokar4edited.png", wb )); | |||
anchor = new HSSFClientAnchor(0,0,1023,255,(short)6,2,(short)8,7); | |||
anchor.setAnchorType( 2 ); | |||
HSSFPicture picture = patriarch.createPicture(anchor, loadPicture( "src/resources/logos/logoKarmokar4s.png", wb )); | |||
picture.setLineStyle( picture.LINESTYLE_DASHDOTGEL ); | |||
} | |||
private static int loadPicture( String path, HSSFWorkbook wb ) throws IOException | |||
{ | |||
int pictureIndex; | |||
FileInputStream fis = null; | |||
ByteArrayOutputStream bos = null; | |||
try | |||
{ | |||
fis = new FileInputStream( path); | |||
bos = new ByteArrayOutputStream( ); | |||
int c; | |||
while ( (c = fis.read()) != -1) | |||
bos.write( c ); | |||
pictureIndex = wb.addPicture( bos.toByteArray(), HSSFWorkbook.PICTURE_TYPE_PNG ); | |||
} | |||
finally | |||
{ | |||
if (fis != null) | |||
fis.close(); | |||
if (bos != null) | |||
bos.close(); | |||
} | |||
return pictureIndex; | |||
} | |||
private static void drawOval( HSSFPatriarch patriarch ) | |||
{ | |||
// Create an oval and style to taste. |
@@ -66,7 +66,17 @@ public class DefaultEscherRecordFactory | |||
} | |||
else if ( header.getRecordId() >= EscherBlipRecord.RECORD_ID_START && header.getRecordId() <= EscherBlipRecord.RECORD_ID_END ) | |||
{ | |||
EscherBlipRecord r = new EscherBlipRecord(); | |||
EscherBlipRecord r; | |||
if (header.getRecordId() == EscherBitmapBlip.RECORD_ID_DIB || | |||
header.getRecordId() == EscherBitmapBlip.RECORD_ID_JPEG || | |||
header.getRecordId() == EscherBitmapBlip.RECORD_ID_PNG) | |||
{ | |||
r = new EscherBitmapBlip(); | |||
} | |||
else | |||
{ | |||
r = new EscherBlipRecord(); | |||
} | |||
r.setRecordId( header.getRecordId() ); | |||
r.setOptions( header.getOptions() ); | |||
return r; |
@@ -17,15 +17,15 @@ | |||
package org.apache.poi.ddf; | |||
import org.apache.poi.util.LittleEndian; | |||
import org.apache.poi.util.HexDump; | |||
import org.apache.poi.util.LittleEndian; | |||
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. | |||
* extra information about the blip. A blip record is actually stored inside | |||
* the BSE record even though the BSE record isn't actually a container record. | |||
* | |||
* @author Glen Stampoultzis | |||
* @see EscherBlipRecord | |||
@@ -56,6 +56,7 @@ public class EscherBSERecord | |||
private byte field_9_name; | |||
private byte field_10_unused2; | |||
private byte field_11_unused3; | |||
private EscherBlipRecord field_12_blipRecord; | |||
private byte[] remainingData; | |||
@@ -85,9 +86,28 @@ public class EscherBSERecord | |||
field_10_unused2 = data[pos + 34]; | |||
field_11_unused3 = data[pos + 35]; | |||
bytesRemaining -= 36; | |||
int bytesRead = 0; | |||
if (bytesRemaining > 0) | |||
{ | |||
field_12_blipRecord = (EscherBlipRecord) recordFactory.createRecord( data, pos + 36 ); | |||
bytesRead = field_12_blipRecord.fillFields( data, pos + 36, recordFactory ); | |||
} | |||
pos += 36 + bytesRead; | |||
bytesRemaining -= bytesRead; | |||
// if (field_1_blipTypeWin32 == BT_PNG) | |||
// { | |||
// byte[] uid = new byte[16]; | |||
// System.arraycopy( data, pos + 36, uid, 0, 16 ); | |||
// byte[] puid = new byte[16]; | |||
// System.arraycopy( data, pos + 52, puid, 0, 16 ); | |||
// byte tag = data[pos+68]; | |||
// System.out.println( HexDump.dump( data, 0, 0 ) ); | |||
// byte[] pngBytes = EscherBlipRecord.decompress( data, pos+69, bytesRemaining); | |||
// } | |||
remainingData = new byte[bytesRemaining]; | |||
System.arraycopy( data, pos + 36, remainingData, 0, bytesRemaining ); | |||
return bytesRemaining + 8 + 36; | |||
System.arraycopy( data, pos, remainingData, 0, bytesRemaining ); | |||
return bytesRemaining + 8 + 36 + (field_12_blipRecord == null ? 0 : field_12_blipRecord.getRecordSize()) ; | |||
} | |||
@@ -104,9 +124,14 @@ public class EscherBSERecord | |||
{ | |||
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 + 36; | |||
if (remainingData == null) remainingData = new byte[0]; | |||
int blipSize = field_12_blipRecord == null ? 0 : field_12_blipRecord.getRecordSize(); | |||
int remainingBytes = remainingData.length + 36 + blipSize; | |||
LittleEndian.putInt( data, offset + 4, remainingBytes ); | |||
data[offset + 8] = field_1_blipTypeWin32; | |||
@@ -121,8 +146,15 @@ public class EscherBSERecord | |||
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; | |||
int bytesWritten = 0; | |||
if (field_12_blipRecord != null) | |||
{ | |||
bytesWritten = field_12_blipRecord.serialize( offset + 44, data, new NullEscherSerializationListener() ); | |||
} | |||
if (remainingData == null) | |||
remainingData = new byte[0]; | |||
System.arraycopy( remainingData, 0, data, offset + 44 + bytesWritten, remainingData.length ); | |||
int pos = offset + 8 + 36 + remainingData.length + bytesWritten; | |||
listener.afterRecordSerialize(pos, getRecordId(), pos - offset, this); | |||
return pos - offset; | |||
@@ -135,7 +167,7 @@ public class EscherBSERecord | |||
*/ | |||
public int getRecordSize() | |||
{ | |||
return 8 + 1 + 1 + 16 + 2 + 4 + 4 + 4 + 1 + 1 + 1 + 1 + remainingData.length; | |||
return 8 + 1 + 1 + 16 + 2 + 4 + 4 + 4 + 1 + 1 + 1 + 1 + field_12_blipRecord.getRecordSize() + (remainingData == null ? 0 : remainingData.length); | |||
} | |||
/** | |||
@@ -312,6 +344,16 @@ public class EscherBSERecord | |||
this.field_11_unused3 = unused3; | |||
} | |||
public EscherBlipRecord getBlipRecord() | |||
{ | |||
return field_12_blipRecord; | |||
} | |||
public void setBlipRecord( EscherBlipRecord field_12_blipRecord ) | |||
{ | |||
this.field_12_blipRecord = field_12_blipRecord; | |||
} | |||
/** | |||
* Any remaining data in this record. | |||
*/ | |||
@@ -360,9 +402,8 @@ public class EscherBSERecord | |||
" Name: " + field_9_name + nl + | |||
" Unused2: " + field_10_unused2 + nl + | |||
" Unused3: " + field_11_unused3 + nl + | |||
" blipRecord: " + field_12_blipRecord + nl + | |||
" Extra Data:" + nl + extraData; | |||
} | |||
/** |
@@ -17,24 +17,14 @@ | |||
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 org.apache.poi.util.HexDump; | |||
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 | |||
* @version $Id$ | |||
*/ | |||
public class EscherBlipRecord | |||
extends EscherRecord | |||
@@ -42,21 +32,14 @@ public class EscherBlipRecord | |||
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; | |||
protected byte[] field_pictureData; | |||
public EscherBlipRecord() | |||
{ | |||
} | |||
/** | |||
* This method deserializes the record from a byte array. | |||
@@ -66,44 +49,27 @@ public class EscherBlipRecord | |||
* @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 | |||
) | |||
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++; | |||
field_pictureData = new byte[bytesAfterHeader]; | |||
System.arraycopy(data, pos, field_pictureData, 0, bytesAfterHeader); | |||
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; | |||
return bytesAfterHeader + 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. | |||
* Serializes the record to an existing byte array. | |||
* | |||
* @see NullEscherSerializationListener | |||
* @param offset the offset within the byte array | |||
* @param data the data array to serialize to | |||
* @param listener a listener for begin and end serialization events. This | |||
* is useful because the serialization is | |||
* hierarchical/recursive and sometimes you need to be able | |||
* break into that. | |||
* @return the number of bytes written. | |||
*/ | |||
public int serialize( int offset, byte[] data, EscherSerializationListener listener ) | |||
{ | |||
@@ -111,25 +77,11 @@ public class EscherBlipRecord | |||
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; | |||
System.arraycopy( field_pictureData, 0, data, offset + 4, field_pictureData.length ); | |||
listener.afterRecordSerialize(pos, getRecordId(), pos - offset, this); | |||
return pos - offset; | |||
listener.afterRecordSerialize(offset + 4 + field_pictureData.length, getRecordId(), field_pictureData.length + 4, this); | |||
return field_pictureData.length + 4; | |||
} | |||
/** | |||
@@ -139,7 +91,7 @@ public class EscherBlipRecord | |||
*/ | |||
public int getRecordSize() | |||
{ | |||
return 58 + field_12_data.length; | |||
return field_pictureData.length + 4; | |||
} | |||
/** | |||
@@ -150,203 +102,6 @@ public class EscherBlipRecord | |||
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" ); | |||
@@ -355,7 +110,7 @@ public class EscherBlipRecord | |||
ByteArrayOutputStream b = new ByteArrayOutputStream(); | |||
try | |||
{ | |||
HexDump.dump( this.field_12_data, 0, b, 0 ); | |||
HexDump.dump( this.field_pictureData, 0, b, 0 ); | |||
extraData = b.toString(); | |||
} | |||
catch ( Exception e ) | |||
@@ -365,70 +120,7 @@ public class EscherBlipRecord | |||
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() ); | |||
} | |||
" Extra Data:" + nl + extraData; | |||
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(); | |||
} | |||
} |
@@ -0,0 +1,435 @@ | |||
/* ==================================================================== | |||
Copyright 2002-2004 Apache Software Foundation | |||
Licensed under the Apache License, Version 2.0 (the "License"); | |||
you may not use this file except in compliance with the License. | |||
You may obtain a copy of the License at | |||
http://www.apache.org/licenses/LICENSE-2.0 | |||
Unless required by applicable law or agreed to in writing, software | |||
distributed under the License is distributed on an "AS IS" BASIS, | |||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |||
See the License for the specific language governing permissions and | |||
limitations under the License. | |||
==================================================================== */ | |||
package org.apache.poi.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 EscherBlipWMFRecord | |||
extends EscherBlipRecord | |||
{ | |||
// 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); | |||
size += bytesRemaining; | |||
return HEADER_SIZE + 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); | |||
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(); | |||
} | |||
} |
@@ -32,12 +32,12 @@ public class EscherBoolProperty | |||
/** | |||
* Create an instance of an escher boolean property. | |||
* | |||
* @param propertyNumber The property number | |||
* @param propertyNumber The property number (or id) | |||
* @param value The 32 bit value of this bool property | |||
*/ | |||
public EscherBoolProperty( short propertyNumber, int value ) | |||
{ | |||
super( propertyNumber, false, false, value ); | |||
super(propertyNumber, value); | |||
} | |||
/** |
@@ -154,7 +154,7 @@ public class EscherClientAnchorRecord | |||
} | |||
catch ( Exception e ) | |||
{ | |||
extraData = "error"; | |||
extraData = "error\n"; | |||
} | |||
return getClass().getName() + ":" + nl + | |||
" RecordId: 0x" + HexDump.toHex(RECORD_ID) + nl + |
@@ -119,7 +119,7 @@ public class EscherClientDataRecord | |||
} | |||
catch ( Exception e ) | |||
{ | |||
extraData = "error"; | |||
extraData = "error\n"; | |||
} | |||
return getClass().getName() + ":" + nl + | |||
" RecordId: 0x" + HexDump.toHex(RECORD_ID) + nl + |
@@ -17,11 +17,10 @@ | |||
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 org.apache.poi.hssf.record.RecordFormatException; | |||
import java.lang.reflect.Array; | |||
import java.util.*; | |||
/** | |||
@@ -83,8 +82,8 @@ public class EscherDggRecord | |||
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 = new FileIdCluster[(bytesRemaining-size) / 8]; // Can't rely on field_2_numIdClusters | |||
for (int i = 0; i < field_5_fileIdClusters.length; i++) | |||
{ | |||
field_5_fileIdClusters[i] = new FileIdCluster(LittleEndian.getInt( data, pos + size ), LittleEndian.getInt( data, pos + size + 4 )); | |||
size += 8; | |||
@@ -114,6 +113,7 @@ public class EscherDggRecord | |||
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; | |||
@@ -200,6 +200,9 @@ public class EscherDggRecord | |||
this.field_1_shapeIdMax = field_1_shapeIdMax; | |||
} | |||
/** | |||
* Number of id clusters + 1 | |||
*/ | |||
public int getNumIdClusters() | |||
{ | |||
return field_5_fileIdClusters.length + 1; |
@@ -26,7 +26,7 @@ package org.apache.poi.ddf; | |||
*/ | |||
abstract public class EscherProperty | |||
{ | |||
private short id; | |||
protected short id; | |||
/** | |||
* The id is distinct from the actual property number. The id includes the property number the blip id |
@@ -18,11 +18,10 @@ | |||
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; | |||
import java.util.List; | |||
/** | |||
* Generates a property given a reference into the byte array storing that property. | |||
@@ -43,7 +42,7 @@ public class EscherPropertyFactory | |||
List results = new ArrayList(); | |||
int pos = offset; | |||
int complexBytes = 0; | |||
// while ( bytesRemaining >= 6 ) | |||
for (int i = 0; i < numProperties; i++) | |||
{ | |||
@@ -54,21 +53,18 @@ public class EscherPropertyFactory | |||
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 ) ); | |||
results.add( new EscherBoolProperty( propId, propData ) ); | |||
else if ( propertyType == EscherPropertyMetaData.TYPE_RGB ) | |||
results.add( new EscherRGBProperty( propNumber, propData ) ); | |||
results.add( new EscherRGBProperty( propId, propData ) ); | |||
else if ( propertyType == EscherPropertyMetaData.TYPE_SHAPEPATH ) | |||
results.add( new EscherShapePathProperty( propNumber, propData ) ); | |||
results.add( new EscherShapePathProperty( propId, propData ) ); | |||
else | |||
{ | |||
if ( !isComplex ) | |||
results.add( new EscherSimpleProperty( propNumber, propData ) ); | |||
results.add( new EscherSimpleProperty( propId, propData ) ); | |||
else | |||
{ | |||
if ( propertyType == EscherPropertyMetaData.TYPE_ARRAY) |
@@ -28,7 +28,7 @@ public class EscherRGBProperty | |||
public EscherRGBProperty( short propertyNumber, int rgbColor ) | |||
{ | |||
super( propertyNumber, false, false, rgbColor ); | |||
super( propertyNumber, rgbColor ); | |||
} | |||
public int getRgbColor() |
@@ -111,6 +111,7 @@ public class EscherSimpleProperty extends EscherProperty | |||
public String toString() | |||
{ | |||
return "propNum: " + getPropertyNumber() | |||
+ ", RAW: 0x" + HexDump.toHex( getId() ) | |||
+ ", propName: " + EscherProperties.getPropertyName( getPropertyNumber() ) | |||
+ ", complex: " + isComplex() | |||
+ ", blipId: " + isBlipId() |
@@ -112,6 +112,23 @@ public class BiffViewer { | |||
in.read(data); | |||
loc += recsize; | |||
Record record = createRecord(rectype, recsize, data ); | |||
// if (record.getSid() == DrawingGroupRecord.sid) | |||
// { | |||
// if (activeRecord.getRecord().getSid() == DrawingGroupRecord.sid) | |||
// { | |||
// DrawingGroupRecord dg = (DrawingGroupRecord) activeRecord.getRecord(); | |||
// System.out.println( "Joined" ); | |||
// dg.join( (AbstractEscherHolderRecord) record ); | |||
// } | |||
// else | |||
// { | |||
// records.add(record); | |||
// if (activeRecord != null) | |||
// activeRecord.dump(); | |||
// activeRecord = new RecordDetails(rectype, recsize, startloc, data, record); | |||
// } | |||
// } | |||
// else | |||
if (record.getSid() != ContinueRecord.sid) | |||
{ | |||
records.add(record); | |||
@@ -178,7 +195,6 @@ public class BiffViewer { | |||
} | |||
} | |||
private static void dumpUnknownRecord(byte[] data) throws IOException { | |||
// record hex dump it! | |||
System.out.println( | |||
@@ -630,29 +646,40 @@ public class BiffViewer { | |||
*/ | |||
public static void main(String[] args) { | |||
try { | |||
BiffViewer viewer = new BiffViewer(args); | |||
System.setProperty("poi.deserialize.escher", "true"); | |||
if ((args.length > 1) && args[1].equals("on")) { | |||
viewer.setDump(true); | |||
if (args.length == 0) | |||
{ | |||
System.out.println( "Biff viewer needs a filename" ); | |||
} | |||
if ((args.length > 1) && args[1].equals("bfd")) { | |||
POIFSFileSystem fs = | |||
new POIFSFileSystem(new FileInputStream(args[0])); | |||
InputStream stream = | |||
fs.createDocumentInputStream("Workbook"); | |||
int size = stream.available(); | |||
byte[] data = new byte[size]; | |||
stream.read(data); | |||
HexDump.dump(data, 0, System.out, 0); | |||
} else { | |||
viewer.run(); | |||
else | |||
{ | |||
BiffViewer viewer = new BiffViewer(args); | |||
if ((args.length > 1) && args[1].equals("on")) { | |||
viewer.setDump(true); | |||
} | |||
if ((args.length > 1) && args[1].equals("bfd")) { | |||
POIFSFileSystem fs = | |||
new POIFSFileSystem(new FileInputStream(args[0])); | |||
InputStream stream = | |||
fs.createDocumentInputStream("Workbook"); | |||
int size = stream.available(); | |||
byte[] data = new byte[size]; | |||
stream.read(data); | |||
HexDump.dump(data, 0, System.out, 0); | |||
} else { | |||
viewer.run(); | |||
} | |||
} | |||
} catch (Exception e) { | |||
e.printStackTrace(); | |||
} | |||
} | |||
/** | |||
* This record supports dumping of completed continue records. | |||
*/ | |||
static class RecordDetails | |||
{ | |||
short rectype, recsize; |
@@ -34,7 +34,7 @@ public abstract class AbstractShape | |||
*/ | |||
public static AbstractShape createShape( HSSFShape hssfShape, int shapeId ) | |||
{ | |||
AbstractShape shape = null; | |||
AbstractShape shape; | |||
if (hssfShape instanceof HSSFTextbox) | |||
{ | |||
shape = new TextboxShape( (HSSFTextbox)hssfShape, shapeId ); | |||
@@ -48,6 +48,9 @@ public abstract class AbstractShape | |||
HSSFSimpleShape simpleShape = (HSSFSimpleShape) hssfShape; | |||
switch ( simpleShape.getShapeType() ) | |||
{ | |||
case HSSFSimpleShape.OBJECT_TYPE_PICTURE: | |||
shape = new PictureShape( simpleShape, shapeId ); | |||
break; | |||
case HSSFSimpleShape.OBJECT_TYPE_LINE: | |||
shape = new LineShape( simpleShape, shapeId ); | |||
break; |
@@ -37,7 +37,7 @@ public class ConvertAnchor | |||
EscherClientAnchorRecord anchor = new EscherClientAnchorRecord(); | |||
anchor.setRecordId( EscherClientAnchorRecord.RECORD_ID ); | |||
anchor.setOptions( (short) 0x0000 ); | |||
anchor.setFlag( (short) 0 ); | |||
anchor.setFlag( (short) a.getAnchorType() ); | |||
anchor.setCol1( (short) Math.min(a.getCol1(), a.getCol2()) ); | |||
anchor.setDx1( (short) Math.min(a.getDx1(), a.getDx2()) ); | |||
anchor.setRow1( (short) Math.min(a.getRow1(), a.getRow2()) ); |
@@ -19,17 +19,17 @@ | |||
package org.apache.poi.hssf.model; | |||
import org.apache.poi.hssf.record.*; | |||
import org.apache.poi.hssf.record.aggregates.ColumnInfoRecordsAggregate; | |||
import org.apache.poi.hssf.record.aggregates.FormulaRecordAggregate; | |||
import org.apache.poi.hssf.record.aggregates.RowRecordsAggregate; | |||
import org.apache.poi.hssf.record.aggregates.ValueRecordsAggregate; | |||
import org.apache.poi.hssf.record.aggregates.ColumnInfoRecordsAggregate; | |||
import org.apache.poi.hssf.record.formula.Ptg; | |||
import org.apache.poi.util.POILogFactory; | |||
import org.apache.poi.util.POILogger; | |||
import java.util.ArrayList; | |||
import java.util.Iterator; | |||
import java.util.List; | |||
import java.util.List; // normally I don't do this, buy we literally mean ALL | |||
/** | |||
* Low level model implementation of a Sheet (one workbook contains many sheets) | |||
@@ -768,6 +768,26 @@ public class Sheet implements Model | |||
} | |||
} | |||
//// uncomment to test record sizes //// | |||
// System.out.println( record.getClass().getName() ); | |||
// byte[] data2 = new byte[record.getRecordSize()]; | |||
// record.serialize(0, data2 ); // rec.length; | |||
// if (LittleEndian.getUShort(data2, 2) != record.getRecordSize() - 4 | |||
// && record instanceof RowRecordsAggregate == false | |||
// && record instanceof ValueRecordsAggregate == false | |||
// && record instanceof EscherAggregate == false) | |||
// { | |||
// throw new RuntimeException("Blah!!! Size off by " + ( LittleEndian.getUShort(data2, 2) - record.getRecordSize() - 4) + " records."); | |||
// } | |||
//asd: int len = record.serialize(pos + offset, data ); | |||
///// DEBUG BEGIN ///// | |||
//asd: if (len != record.getRecordSize()) | |||
//asd: throw new IllegalStateException("Record size does not match serialized bytes. Serialized size = " + len + " but getRecordSize() returns " + record.getRecordSize() + ". Record object is " + record.getClass()); | |||
///// DEBUG END ///// | |||
//asd: pos += len; // rec.length; | |||
} | |||
if (log.check( POILogger.DEBUG )) | |||
@@ -2023,9 +2043,9 @@ public class Sheet implements Model | |||
{ | |||
int retval = 0; | |||
for (int k = 0; k < records.size(); k++) | |||
for ( int k = 0; k < records.size(); k++ ) | |||
{ | |||
retval += (( Record ) records.get(k)).getRecordSize(); | |||
retval += ( (Record) records.get( k ) ).getRecordSize(); | |||
} | |||
//Add space for the IndexRecord | |||
if (rows != null) { | |||
@@ -2430,7 +2450,7 @@ public class Sheet implements Model | |||
return margins; | |||
} | |||
public int aggregateDrawingRecords(DrawingManager drawingManager) | |||
public int aggregateDrawingRecords(DrawingManager2 drawingManager) | |||
{ | |||
int loc = findFirstRecordLocBySid(DrawingRecord.sid); | |||
boolean noDrawingRecordsFound = loc == -1; |
@@ -29,7 +29,6 @@ import java.util.ArrayList; | |||
import java.util.Iterator; | |||
import java.util.List; | |||
import java.util.Locale; | |||
import java.io.UnsupportedEncodingException; | |||
/** | |||
* Low level model implementation of a Workbook. Provides creational methods | |||
@@ -99,7 +98,8 @@ public class Workbook implements Model | |||
protected int numfonts = 0; // hold the number of font records | |||
private short maxformatid = -1; // holds the max format id | |||
private boolean uses1904datewindowing = false; // whether 1904 date windowing is being used | |||
private DrawingManager drawingManager; | |||
private DrawingManager2 drawingManager; | |||
private List escherBSERecords = new ArrayList(); // EscherBSERecord | |||
private static POILogger log = POILogFactory.getLogger(Workbook.class); | |||
@@ -748,7 +748,12 @@ public class Workbook implements Model | |||
{ | |||
record = sst.createExtSSTRecord(sstPos + offset); | |||
} | |||
pos += record.serialize( pos + offset, data ); // rec.length; | |||
int len = record.serialize( pos + offset, data ); | |||
///// DEBUG BEGIN ///// | |||
// if (len != record.getRecordSize()) | |||
// throw new IllegalStateException("Record size does not match serialized bytes. Serialized size = " + len + " but getRecordSize() returns " + record.getRecordSize()); | |||
///// DEBUG END ///// | |||
pos += len; // rec.length; | |||
} | |||
} | |||
if (log.check( POILogger.DEBUG )) | |||
@@ -2104,13 +2109,12 @@ public class Workbook implements Model | |||
} | |||
/** | |||
* Creates a drawing group record. If it already exists then it's left | |||
* alone. | |||
* Creates a drawing group record. If it already exists then it's modified. | |||
*/ | |||
public void createDrawingGroup() | |||
{ | |||
int dggLoc = findFirstRecordLocBySid(EscherContainerRecord.DGG_CONTAINER); | |||
if (dggLoc == -1) | |||
if (drawingManager == null) | |||
{ | |||
EscherContainerRecord dggContainer = new EscherContainerRecord(); | |||
EscherDggRecord dgg = new EscherDggRecord(); | |||
@@ -2125,11 +2129,23 @@ public class Workbook implements Model | |||
dgg.setNumShapesSaved(0); | |||
dgg.setDrawingsSaved(0); | |||
dgg.setFileIdClusters(new EscherDggRecord.FileIdCluster[] {} ); | |||
drawingManager = new DrawingManager(dgg); | |||
drawingManager = new DrawingManager2(dgg); | |||
EscherContainerRecord bstoreContainer = null; | |||
if (escherBSERecords.size() > 0) | |||
{ | |||
bstoreContainer = new EscherContainerRecord(); | |||
bstoreContainer.setRecordId( EscherContainerRecord.BSTORE_CONTAINER ); | |||
bstoreContainer.setOptions( (short) ( (escherBSERecords.size() << 4) | 0xF ) ); | |||
for ( Iterator iterator = escherBSERecords.iterator(); iterator.hasNext(); ) | |||
{ | |||
EscherRecord escherRecord = (EscherRecord) iterator.next(); | |||
bstoreContainer.addChildRecord( escherRecord ); | |||
} | |||
} | |||
opt.setRecordId((short) 0xF00B); | |||
opt.setOptions((short) 0x0033); | |||
opt.addEscherProperty( new EscherBoolProperty(EscherProperties.TEXT__SIZE_TEXT_TO_FIT_SHAPE, 524296) ); | |||
opt.addEscherProperty( new EscherRGBProperty(EscherProperties.FILL__FILLCOLOR, 134217737) ); | |||
opt.addEscherProperty( new EscherRGBProperty(EscherProperties.FILL__FILLCOLOR, 0x08000041) ); | |||
opt.addEscherProperty( new EscherRGBProperty(EscherProperties.LINESTYLE__COLOR, 134217792) ); | |||
splitMenuColors.setRecordId((short) 0xF11E); | |||
splitMenuColors.setOptions((short) 0x0040); | |||
@@ -2139,17 +2155,61 @@ public class Workbook implements Model | |||
splitMenuColors.setColor4(0x100000F7); | |||
dggContainer.addChildRecord(dgg); | |||
if (bstoreContainer != null) | |||
dggContainer.addChildRecord( bstoreContainer ); | |||
dggContainer.addChildRecord(opt); | |||
dggContainer.addChildRecord(splitMenuColors); | |||
DrawingGroupRecord drawingGroup = new DrawingGroupRecord(); | |||
drawingGroup.addEscherRecord(dggContainer); | |||
int loc = findFirstRecordLocBySid(CountryRecord.sid); | |||
getRecords().add(loc+1, drawingGroup); | |||
int dgLoc = findFirstRecordLocBySid(DrawingGroupRecord.sid); | |||
if (dgLoc == -1) | |||
{ | |||
DrawingGroupRecord drawingGroup = new DrawingGroupRecord(); | |||
drawingGroup.addEscherRecord(dggContainer); | |||
int loc = findFirstRecordLocBySid(CountryRecord.sid); | |||
getRecords().add(loc+1, drawingGroup); | |||
} | |||
else | |||
{ | |||
DrawingGroupRecord drawingGroup = new DrawingGroupRecord(); | |||
drawingGroup.addEscherRecord(dggContainer); | |||
getRecords().set(dgLoc, drawingGroup); | |||
} | |||
} | |||
} | |||
public int addBSERecord(EscherBSERecord e) | |||
{ | |||
createDrawingGroup(); | |||
// maybe we don't need that as an instance variable anymore | |||
escherBSERecords.add( e ); | |||
int dgLoc = findFirstRecordLocBySid(DrawingGroupRecord.sid); | |||
DrawingGroupRecord drawingGroup = (DrawingGroupRecord) getRecords().get( dgLoc ); | |||
EscherContainerRecord dggContainer = (EscherContainerRecord) drawingGroup.getEscherRecord( 0 ); | |||
EscherContainerRecord bstoreContainer; | |||
if (dggContainer.getChild( 1 ).getRecordId() == EscherContainerRecord.BSTORE_CONTAINER ) | |||
{ | |||
bstoreContainer = (EscherContainerRecord) dggContainer.getChild( 1 ); | |||
} | |||
else | |||
{ | |||
bstoreContainer = new EscherContainerRecord(); | |||
bstoreContainer.setRecordId( EscherContainerRecord.BSTORE_CONTAINER ); | |||
dggContainer.getChildRecords().add( 1, bstoreContainer ); | |||
} | |||
bstoreContainer.setOptions( (short) ( (escherBSERecords.size() << 4) | 0xF ) ); | |||
bstoreContainer.addChildRecord( e ); | |||
return escherBSERecords.size(); | |||
} | |||
public DrawingManager getDrawingManager() | |||
public DrawingManager2 getDrawingManager() | |||
{ | |||
return drawingManager; | |||
} |
@@ -88,7 +88,7 @@ public abstract class AbstractEscherHolderRecord | |||
{ | |||
if (id != getSid()) | |||
{ | |||
throw new RecordFormatException("Not a Bar record"); | |||
throw new RecordFormatException("Not an escher record"); | |||
} | |||
} | |||
@@ -102,15 +102,20 @@ public abstract class AbstractEscherHolderRecord | |||
} | |||
else | |||
{ | |||
EscherRecordFactory recordFactory = new DefaultEscherRecordFactory(); | |||
int pos = offset; | |||
while ( pos < offset + size ) | |||
{ | |||
EscherRecord r = recordFactory.createRecord(data, pos); | |||
int bytesRead = r.fillFields(data, pos, recordFactory ); | |||
escherRecords.add(r); | |||
pos += bytesRead; | |||
} | |||
convertToEscherRecords( offset, size, data ); | |||
} | |||
} | |||
private void convertToEscherRecords( int offset, int size, byte[] data ) | |||
{ | |||
EscherRecordFactory recordFactory = new DefaultEscherRecordFactory(); | |||
int pos = offset; | |||
while ( pos < offset + size ) | |||
{ | |||
EscherRecord r = recordFactory.createRecord(data, pos); | |||
int bytesRead = r.fillFields(data, pos, recordFactory ); | |||
escherRecords.add(r); | |||
pos += bytesRead; | |||
} | |||
} | |||
@@ -120,6 +125,8 @@ public abstract class AbstractEscherHolderRecord | |||
final String nl = System.getProperty("line.separator"); | |||
buffer.append('[' + getRecordName() + ']' + nl); | |||
if (escherRecords.size() == 0) | |||
buffer.append("No Escher Records Decoded" + nl); | |||
for ( Iterator iterator = escherRecords.iterator(); iterator.hasNext(); ) | |||
{ | |||
EscherRecord r = (EscherRecord) iterator.next(); | |||
@@ -138,10 +145,16 @@ public abstract class AbstractEscherHolderRecord | |||
LittleEndian.putShort( data, 2 + offset, (short) ( getRecordSize() - 4 ) ); | |||
if ( escherRecords.size() == 0 && rawData != null ) | |||
{ | |||
System.arraycopy( rawData, 0, data, offset + 4, rawData.length ); | |||
LittleEndian.putShort(data, 0 + offset, getSid()); | |||
LittleEndian.putShort(data, 2 + offset, (short)(getRecordSize() - 4)); | |||
System.arraycopy( rawData, 0, data, 4 + offset, rawData.length); | |||
return rawData.length + 4; | |||
} | |||
else | |||
{ | |||
LittleEndian.putShort(data, 0 + offset, getSid()); | |||
LittleEndian.putShort(data, 2 + offset, (short)(getRecordSize() - 4)); | |||
int pos = offset + 4; | |||
for ( Iterator iterator = escherRecords.iterator(); iterator.hasNext(); ) | |||
{ | |||
@@ -255,6 +268,45 @@ public abstract class AbstractEscherHolderRecord | |||
return (EscherRecord) escherRecords.get(index); | |||
} | |||
/** | |||
* Big drawing group records are split but it's easier to deal with them | |||
* as a whole group so we need to join them together. | |||
*/ | |||
public void join( AbstractEscherHolderRecord record ) | |||
{ | |||
int length = this.rawData.length + record.getRawData().length; | |||
byte[] data = new byte[length]; | |||
System.arraycopy( rawData, 0, data, 0, rawData.length ); | |||
System.arraycopy( record.getRawData(), 0, data, rawData.length, record.getRawData().length ); | |||
rawData = data; | |||
} | |||
public void processContinueRecord( byte[] record ) | |||
{ | |||
int length = this.rawData.length + record.length; | |||
byte[] data = new byte[length]; | |||
System.arraycopy( rawData, 0, data, 0, rawData.length ); | |||
System.arraycopy( record, 0, data, rawData.length, record.length ); | |||
rawData = data; | |||
} | |||
public byte[] getRawData() | |||
{ | |||
return rawData; | |||
} | |||
public void setRawData( byte[] rawData ) | |||
{ | |||
this.rawData = rawData; | |||
} | |||
/** | |||
* Convert raw data to escher records. | |||
*/ | |||
public void decode() | |||
{ | |||
convertToEscherRecords(0, rawData.length, rawData ); | |||
} | |||
} // END OF CLASS | |||
@@ -16,10 +16,22 @@ | |||
package org.apache.poi.hssf.record; | |||
import org.apache.poi.ddf.EscherRecord; | |||
import org.apache.poi.ddf.NullEscherSerializationListener; | |||
import org.apache.poi.util.ArrayUtil; | |||
import org.apache.poi.util.LittleEndian; | |||
import java.util.Iterator; | |||
import java.util.List; | |||
public class DrawingGroupRecord extends AbstractEscherHolderRecord | |||
{ | |||
public static final short sid = 0xEB; | |||
static final int MAX_RECORD_SIZE = 8228; | |||
private static final int MAX_DATA_SIZE = MAX_RECORD_SIZE - 4; | |||
public DrawingGroupRecord() | |||
{ | |||
} | |||
@@ -43,4 +55,92 @@ public class DrawingGroupRecord extends AbstractEscherHolderRecord | |||
{ | |||
return sid; | |||
} | |||
public int serialize(int offset, byte[] data) | |||
{ | |||
byte[] rawData = getRawData(); | |||
if (getEscherRecords().size() == 0 && rawData != null) | |||
{ | |||
return writeData( offset, data, rawData ); | |||
} | |||
else | |||
{ | |||
byte[] buffer = new byte[getRawDataSize()]; | |||
int pos = 0; | |||
for ( Iterator iterator = getEscherRecords().iterator(); iterator.hasNext(); ) | |||
{ | |||
EscherRecord r = (EscherRecord) iterator.next(); | |||
pos += r.serialize(pos, buffer, new NullEscherSerializationListener() ); | |||
} | |||
return writeData( offset, data, buffer ); | |||
} | |||
} | |||
/** | |||
* Size of record (including 4 byte headers for all sections) | |||
*/ | |||
public int getRecordSize() | |||
{ | |||
return grossSizeFromDataSize( getRawDataSize() ); | |||
} | |||
public int getRawDataSize() | |||
{ | |||
List escherRecords = getEscherRecords(); | |||
byte[] rawData = getRawData(); | |||
if (escherRecords.size() == 0 && rawData != null) | |||
{ | |||
return rawData.length; | |||
} | |||
else | |||
{ | |||
int size = 0; | |||
for ( Iterator iterator = escherRecords.iterator(); iterator.hasNext(); ) | |||
{ | |||
EscherRecord r = (EscherRecord) iterator.next(); | |||
size += r.getRecordSize(); | |||
} | |||
return size; | |||
} | |||
} | |||
static int grossSizeFromDataSize(int dataSize) | |||
{ | |||
return dataSize + ( (dataSize - 1) / MAX_DATA_SIZE + 1 ) * 4; | |||
} | |||
private int writeData( int offset, byte[] data, byte[] rawData ) | |||
{ | |||
int writtenActualData = 0; | |||
int writtenRawData = 0; | |||
while (writtenRawData < rawData.length) | |||
{ | |||
int segmentLength = Math.min( rawData.length - writtenRawData, MAX_DATA_SIZE); | |||
if (writtenRawData / MAX_DATA_SIZE >= 2) | |||
writeContinueHeader( data, offset, segmentLength ); | |||
else | |||
writeHeader( data, offset, segmentLength ); | |||
writtenActualData += 4; | |||
offset += 4; | |||
ArrayUtil.arraycopy( rawData, writtenRawData, data, offset, segmentLength ); | |||
offset += segmentLength; | |||
writtenRawData += segmentLength; | |||
writtenActualData += segmentLength; | |||
} | |||
return writtenActualData; | |||
} | |||
private void writeHeader( byte[] data, int offset, int sizeExcludingHeader ) | |||
{ | |||
LittleEndian.putShort(data, 0 + offset, getSid()); | |||
LittleEndian.putShort(data, 2 + offset, (short) sizeExcludingHeader); | |||
} | |||
private void writeContinueHeader( byte[] data, int offset, int sizeExcludingHeader ) | |||
{ | |||
LittleEndian.putShort(data, 0 + offset, ContinueRecord.sid); | |||
LittleEndian.putShort(data, 2 + offset, (short) sizeExcludingHeader); | |||
} | |||
} |
@@ -68,6 +68,14 @@ public class DrawingRecord extends Record | |||
recordData = data; | |||
} | |||
public void processContinueRecord( byte[] record ) | |||
{ | |||
byte[] newBuffer = new byte[ recordData.length + record.length ]; | |||
System.arraycopy( recordData, 0, newBuffer, 0, recordData.length ); | |||
System.arraycopy( record, 0, newBuffer, recordData.length, record.length); | |||
recordData = newBuffer; | |||
} | |||
public int serialize( int offset, byte[] data ) | |||
{ | |||
if (recordData == null) |
@@ -20,7 +20,7 @@ import org.apache.poi.ddf.*; | |||
import org.apache.poi.hssf.usermodel.*; | |||
import org.apache.poi.hssf.model.AbstractShape; | |||
import org.apache.poi.hssf.model.TextboxShape; | |||
import org.apache.poi.hssf.model.DrawingManager; | |||
import org.apache.poi.hssf.model.DrawingManager2; | |||
import org.apache.poi.hssf.model.ConvertAnchor; | |||
import java.util.*; | |||
@@ -256,10 +256,10 @@ public class EscherAggregate extends AbstractEscherHolderRecord | |||
/** Maps shape container objects to their OBJ records */ | |||
private Map shapeToObj = new HashMap(); | |||
private DrawingManager drawingManager; | |||
private DrawingManager2 drawingManager; | |||
private short drawingGroupId; | |||
public EscherAggregate( DrawingManager drawingManager ) | |||
public EscherAggregate( DrawingManager2 drawingManager ) | |||
{ | |||
this.drawingManager = drawingManager; | |||
} | |||
@@ -305,7 +305,7 @@ public class EscherAggregate extends AbstractEscherHolderRecord | |||
/** | |||
* Collapses the drawing records into an aggregate. | |||
*/ | |||
public static EscherAggregate createAggregate( List records, int locFirstDrawingRecord, DrawingManager drawingManager ) | |||
public static EscherAggregate createAggregate( List records, int locFirstDrawingRecord, DrawingManager2 drawingManager ) | |||
{ | |||
// Keep track of any shape records created so we can match them back to the object id's. | |||
// Textbox objects are also treated as shape objects. | |||
@@ -571,6 +571,9 @@ public class EscherAggregate extends AbstractEscherHolderRecord | |||
escherParent.addChildRecord( shapeModel.getSpContainer() ); | |||
} | |||
} | |||
// drawingManager.newCluster( (short)1 ); | |||
// drawingManager.newCluster( (short)2 ); | |||
} | |||
private void convertGroup( HSSFShapeGroup shape, EscherContainerRecord escherParent, Map shapeToObj ) | |||
@@ -678,7 +681,7 @@ public class EscherAggregate extends AbstractEscherHolderRecord | |||
spContainer1.setRecordId( EscherContainerRecord.SP_CONTAINER ); | |||
spContainer1.setOptions( (short) 0x000F ); | |||
spgr.setRecordId( EscherSpgrRecord.RECORD_ID ); | |||
spgr.setOptions( (short) 0x0001 ); // don't know what the 1 is for. | |||
spgr.setOptions( (short) 0x0001 ); // version | |||
spgr.setRectX1( patriarch.getX1() ); | |||
spgr.setRectY1( patriarch.getY1() ); | |||
spgr.setRectX2( patriarch.getX2() ); |
@@ -173,6 +173,14 @@ public class ObjRecord | |||
return subrecords.add( o ); | |||
} | |||
// made public to satisfy biffviewer | |||
/* protected */ | |||
public void processContinueRecord( byte[] record ) | |||
{ | |||
super.processContinueRecord( record ); | |||
} | |||
public Object clone() | |||
{ | |||
ObjRecord rec = new ObjRecord(); |
@@ -148,6 +148,7 @@ public class RecordFactory | |||
{ | |||
short rectype = 0; | |||
DrawingRecord lastDrawingRecord = new DrawingRecord( ); | |||
do | |||
{ | |||
rectype = LittleEndian.readShort(in); | |||
@@ -176,7 +177,13 @@ public class RecordFactory | |||
if (record != null) | |||
{ | |||
if (rectype == ContinueRecord.sid && | |||
if (rectype == DrawingGroupRecord.sid | |||
&& last_record instanceof DrawingGroupRecord) | |||
{ | |||
DrawingGroupRecord lastDGRecord = (DrawingGroupRecord) last_record; | |||
lastDGRecord.join((AbstractEscherHolderRecord) record); | |||
} | |||
else if (rectype == ContinueRecord.sid && | |||
! (last_record instanceof ContinueRecord) && // include continuation records after | |||
! (last_record instanceof UnknownRecord) ) // unknown records or previous continuation records | |||
{ | |||
@@ -185,11 +192,18 @@ public class RecordFactory | |||
throw new RecordFormatException( | |||
"First record is a ContinueRecord??"); | |||
} | |||
last_record.processContinueRecord(data); | |||
// Drawing records have a very strange continue behaviour. There can actually be OBJ records mixed between the continues. | |||
if (last_record instanceof ObjRecord) | |||
lastDrawingRecord.processContinueRecord( data ); | |||
else | |||
last_record.processContinueRecord(data); | |||
} | |||
else | |||
{ | |||
last_record = record; | |||
if (record instanceof DrawingRecord) | |||
lastDrawingRecord = (DrawingRecord) record; | |||
records.add(record); | |||
} | |||
} |
@@ -14,7 +14,7 @@ import java.util.List; | |||
public class ColumnInfoRecordsAggregate | |||
extends Record | |||
{ | |||
int size = 0; | |||
// int size = 0; | |||
List records = null; | |||
public ColumnInfoRecordsAggregate() | |||
@@ -40,6 +40,9 @@ public class ColumnInfoRecordsAggregate | |||
public int getRecordSize() | |||
{ | |||
int size = 0; | |||
for ( Iterator iterator = records.iterator(); iterator.hasNext(); ) | |||
size += ( (ColumnInfoRecord) iterator.next() ).getRecordSize(); | |||
return size; | |||
} | |||
@@ -68,7 +71,6 @@ public class ColumnInfoRecordsAggregate | |||
*/ | |||
public void insertColumn( ColumnInfoRecord col ) | |||
{ | |||
size += col.getRecordSize(); | |||
records.add( col ); | |||
} | |||
@@ -78,7 +80,6 @@ public class ColumnInfoRecordsAggregate | |||
*/ | |||
public void insertColumn( int idx, ColumnInfoRecord col ) | |||
{ | |||
size += col.getRecordSize(); | |||
records.add( idx, col ); | |||
} | |||
@@ -228,7 +228,7 @@ public class RowRecordsAggregate | |||
/** | |||
* called by the constructor, should set class level fields. Should throw | |||
* runtime exception for bad/icomplete data. | |||
* runtime exception for bad/incomplete data. | |||
* | |||
* @param data raw data | |||
* @param size size of data |
@@ -16,8 +16,7 @@ | |||
package org.apache.poi.hssf.usermodel; | |||
import org.apache.poi.ddf.EscherClientAnchorRecord; | |||
import org.apache.poi.ddf.EscherRecord; | |||
/** | |||
@@ -33,6 +32,7 @@ public class HSSFClientAnchor | |||
int row1; | |||
short col2; | |||
int row2; | |||
int anchorType; | |||
/** | |||
* Creates a new client anchor and defaults all the anchor positions to 0. | |||
@@ -213,6 +213,26 @@ public class HSSFClientAnchor | |||
return row1 > row2; | |||
} | |||
/** | |||
* Gets the anchor type | |||
* <p> | |||
* 0 = Move and size with Cells, 2 = Move but don't size with cells, 3 = Don't move or size with cells. | |||
*/ | |||
public int getAnchorType() | |||
{ | |||
return anchorType; | |||
} | |||
/** | |||
* Sets the anchor type | |||
* <p> | |||
* 0 = Move and size with Cells, 2 = Move but don't size with cells, 3 = Don't move or size with cells. | |||
*/ | |||
public void setAnchorType( int anchorType ) | |||
{ | |||
this.anchorType = anchorType; | |||
} | |||
private void checkRange( int value, int minRange, int maxRange, String varName ) | |||
{ | |||
if (value < minRange || value > maxRange) |
@@ -77,6 +77,23 @@ public class HSSFPatriarch | |||
return shape; | |||
} | |||
/** | |||
* Creates a picture. | |||
* | |||
* @param anchor the client anchor describes how this group is attached | |||
* to the sheet. | |||
* @return the newly created shape. | |||
*/ | |||
public HSSFPicture createPicture(HSSFClientAnchor anchor, int pictureIndex) | |||
{ | |||
HSSFPicture shape = new HSSFPicture(null, anchor); | |||
shape.setPictureIndex( pictureIndex ); | |||
shape.anchor = anchor; | |||
shapes.add(shape); | |||
return shape; | |||
} | |||
/** | |||
* Creates a polygon | |||
* |
@@ -1220,19 +1220,22 @@ public class HSSFSheet | |||
* Aggregates the drawing records and dumps the escher record hierarchy | |||
* to the standard output. | |||
*/ | |||
public void dumpDrawingRecords() | |||
public void dumpDrawingRecords(boolean fat) | |||
{ | |||
sheet.aggregateDrawingRecords(book.getDrawingManager()); | |||
EscherAggregate r = (EscherAggregate) getSheet().findFirstRecordBySid(EscherAggregate.sid); | |||
List escherRecords = r.getEscherRecords(); | |||
PrintWriter w = new PrintWriter(System.out); | |||
for ( Iterator iterator = escherRecords.iterator(); iterator.hasNext(); ) | |||
{ | |||
EscherRecord escherRecord = (EscherRecord) iterator.next(); | |||
PrintWriter w = new PrintWriter(System.out); | |||
escherRecord.display(w, 0); | |||
w.close(); | |||
if (fat) | |||
System.out.println(escherRecord.toString()); | |||
else | |||
escherRecord.display(w, 0); | |||
} | |||
w.flush(); | |||
} | |||
/** |
@@ -34,7 +34,7 @@ public class HSSFSimpleShape | |||
// public final static short OBJECT_TYPE_CHART = 5; | |||
// public final static short OBJECT_TYPE_TEXT = 6; | |||
// public final static short OBJECT_TYPE_BUTTON = 7; | |||
// public final static short OBJECT_TYPE_PICTURE = 8; | |||
public final static short OBJECT_TYPE_PICTURE = 8; | |||
// public final static short OBJECT_TYPE_POLYGON = 9; | |||
// public final static short OBJECT_TYPE_CHECKBOX = 11; | |||
// public final static short OBJECT_TYPE_OPTION_BUTTON = 12; | |||
@@ -63,6 +63,7 @@ public class HSSFSimpleShape | |||
* @see #OBJECT_TYPE_LINE | |||
* @see #OBJECT_TYPE_OVAL | |||
* @see #OBJECT_TYPE_RECTANGLE | |||
* @see #OBJECT_TYPE_PICTURE | |||
*/ | |||
public int getShapeType() { return shapeType; } | |||
@@ -74,6 +75,7 @@ public class HSSFSimpleShape | |||
* @see #OBJECT_TYPE_LINE | |||
* @see #OBJECT_TYPE_OVAL | |||
* @see #OBJECT_TYPE_RECTANGLE | |||
* @see #OBJECT_TYPE_PICTURE | |||
*/ | |||
public void setShapeType( int shapeType ){ this.shapeType = shapeType; } | |||
@@ -22,6 +22,9 @@ | |||
*/ | |||
package org.apache.poi.hssf.usermodel; | |||
import org.apache.poi.ddf.EscherBSERecord; | |||
import org.apache.poi.ddf.EscherBitmapBlip; | |||
import org.apache.poi.ddf.EscherRecord; | |||
import org.apache.poi.hssf.eventmodel.EventRecordFactory; | |||
import org.apache.poi.hssf.model.Sheet; | |||
import org.apache.poi.hssf.model.Workbook; | |||
@@ -38,6 +41,7 @@ import java.io.ByteArrayInputStream; | |||
import java.io.IOException; | |||
import java.io.InputStream; | |||
import java.io.OutputStream; | |||
import java.io.PrintWriter; | |||
import java.util.ArrayList; | |||
import java.util.Iterator; | |||
import java.util.List; | |||
@@ -57,7 +61,6 @@ import java.util.Stack; | |||
*/ | |||
public class HSSFWorkbook | |||
extends java.lang.Object | |||
{ | |||
private static final int DEBUG = POILogger.DEBUG; | |||
@@ -108,13 +111,31 @@ public class HSSFWorkbook | |||
*/ | |||
private HSSFDataFormat formatter; | |||
/// NOT YET SUPPORTED: | |||
/** Extended windows meta file */ | |||
//public static final int PICTURE_TYPE_EMF = 2; | |||
/** Windows Meta File */ | |||
//public static final int PICTURE_TYPE_WMF = 3; | |||
/** Mac PICT format */ | |||
//public static final int PICTURE_TYPE_PICT = 4; | |||
/** JPEG format */ | |||
public static final int PICTURE_TYPE_JPEG = 5; | |||
/** PNG format */ | |||
public static final int PICTURE_TYPE_PNG = 6; | |||
/** Device independant bitmap */ | |||
public static final int PICTURE_TYPE_DIB = 7; | |||
private static POILogger log = POILogFactory.getLogger(HSSFWorkbook.class); | |||
/** | |||
* Creates new HSSFWorkbook from scratch (start here!) | |||
* | |||
*/ | |||
public HSSFWorkbook() | |||
{ | |||
this(Workbook.createWorkbook()); | |||
@@ -159,8 +180,6 @@ public class HSSFWorkbook | |||
EventRecordFactory factory = new EventRecordFactory(); | |||
List records = RecordFactory.createRecords(stream); | |||
workbook = Workbook.createWorkbook(records); | |||
@@ -796,8 +815,9 @@ public class HSSFWorkbook | |||
// byte[] sb = (byte[])sheetbytes.get(k); | |||
// System.arraycopy(sb, 0, retval, pos, sb.length); | |||
pos += ((HSSFSheet) sheets.get(k)).getSheet().serialize(pos, | |||
retval); // sb.length; | |||
int len = ((HSSFSheet) sheets.get(k)).getSheet().serialize(pos, | |||
retval); | |||
pos += len; // sb.length; | |||
} | |||
/* for (int k = pos; k < totalsize; k++) | |||
{ | |||
@@ -1067,6 +1087,68 @@ public class HSSFWorkbook | |||
workbook.getRecords().add(loc, r); | |||
} | |||
/** | |||
* Spits out a list of all the drawing records in the workbook. | |||
*/ | |||
public void dumpDrawingGroupRecords(boolean fat) | |||
{ | |||
DrawingGroupRecord r = (DrawingGroupRecord) workbook.findFirstRecordBySid( DrawingGroupRecord.sid ); | |||
r.decode(); | |||
List escherRecords = r.getEscherRecords(); | |||
PrintWriter w = new PrintWriter(System.out); | |||
for ( Iterator iterator = escherRecords.iterator(); iterator.hasNext(); ) | |||
{ | |||
EscherRecord escherRecord = (EscherRecord) iterator.next(); | |||
if (fat) | |||
System.out.println(escherRecord.toString()); | |||
else | |||
escherRecord.display(w, 0); | |||
} | |||
w.flush(); | |||
} | |||
/** | |||
* Adds a picture to the workbook. | |||
* | |||
* @param pictureData The bytes of the picture | |||
* @param format The format of the picture. One of <code>PICTURE_TYPE_*</code> | |||
* | |||
* @return the index to this picture (1 based). | |||
*/ | |||
public int addPicture(byte[] pictureData, int format) | |||
{ | |||
byte[] uid = newUID(); | |||
EscherBitmapBlip blipRecord = new EscherBitmapBlip(); | |||
blipRecord.setRecordId( (short) ( EscherBitmapBlip.RECORD_ID_START + format ) ); | |||
if (format == HSSFWorkbook.PICTURE_TYPE_PNG) | |||
blipRecord.setOptions( (short) 0x6E00 ); // MSOBI | |||
else if (format == HSSFWorkbook.PICTURE_TYPE_JPEG) | |||
blipRecord.setOptions( (short) 0x46A0 ); // MSOBI | |||
else if (format == HSSFWorkbook.PICTURE_TYPE_DIB) | |||
blipRecord.setOptions( (short) 0x7A8 ); // MSOBI | |||
blipRecord.setUID( uid ); | |||
blipRecord.setMarker( (byte) 0xFF ); | |||
blipRecord.setPictureData( pictureData ); | |||
EscherBSERecord r = new EscherBSERecord(); | |||
r.setRecordId( EscherBSERecord.RECORD_ID ); | |||
r.setOptions( (short) ( 0x0002 | ( format << 4 ) ) ); | |||
r.setBlipTypeMacOS( (byte) format ); | |||
r.setBlipTypeWin32( (byte) format ); | |||
r.setUid( uid ); | |||
r.setTag( (short) 0xFF ); | |||
r.setSize( pictureData.length + 25 ); | |||
r.setRef( 1 ); | |||
r.setOffset( 0 ); | |||
r.setBlipRecord( blipRecord ); | |||
return workbook.addBSERecord( r ); | |||
} | |||
private byte[] newUID() | |||
{ | |||
byte[] bytes = new byte[16]; | |||
return bytes; | |||
} | |||
} |
@@ -17,12 +17,11 @@ | |||
package org.apache.poi.util; | |||
import org.apache.poi.hssf.usermodel.HSSFWorkbook; | |||
import org.apache.poi.hssf.usermodel.HSSFSheet; | |||
import org.apache.poi.hssf.usermodel.HSSFWorkbook; | |||
import org.apache.poi.poifs.filesystem.POIFSFileSystem; | |||
import java.io.FileInputStream; | |||
import java.io.FileNotFoundException; | |||
import java.io.IOException; | |||
/** | |||
@@ -35,7 +34,15 @@ public class DrawingDump | |||
POIFSFileSystem fs = | |||
new POIFSFileSystem(new FileInputStream(args[0])); | |||
HSSFWorkbook wb = new HSSFWorkbook(fs); | |||
HSSFSheet sheet = wb.getSheetAt(0); | |||
sheet.dumpDrawingRecords(); | |||
System.out.println( "Drawing group:" ); | |||
wb.dumpDrawingGroupRecords(true); | |||
for (int sheetNum = 1; sheetNum <= wb.getNumberOfSheets(); sheetNum++) | |||
{ | |||
System.out.println( "Sheet " + sheetNum + ":" ); | |||
HSSFSheet sheet = wb.getSheetAt(sheetNum - 1); | |||
sheet.dumpDrawingRecords(true); | |||
} | |||
} | |||
} |
@@ -75,7 +75,7 @@ public class HexDump | |||
{ | |||
if (data.length == 0) | |||
{ | |||
stream.write( "No Data".getBytes() ); | |||
stream.write( ("No Data" + System.getProperty( "line.separator")).getBytes() ); | |||
stream.flush(); | |||
return; | |||
} |
@@ -98,8 +98,9 @@ public class TestEscherBSERecord extends TestCase | |||
" Name: 5" + nl + | |||
" Unused2: 6" + nl + | |||
" Unused3: 7" + nl + | |||
" blipRecord: null" + nl + | |||
" Extra Data:" + nl + | |||
"No Data", record.toString() ); | |||
"No Data" + nl, record.toString() ); | |||
} | |||
} |
@@ -21,7 +21,7 @@ import junit.framework.TestCase; | |||
import org.apache.poi.util.HexDump; | |||
import org.apache.poi.util.HexRead; | |||
public class TestEscherBlipRecord extends TestCase | |||
public class TestEscherBlipWMFRecord extends TestCase | |||
{ | |||
private String dataStr; | |||
private byte[] data; | |||
@@ -37,7 +37,7 @@ public class TestEscherBlipRecord extends TestCase | |||
public void testSerialize() throws Exception | |||
{ | |||
EscherBlipRecord r = new EscherBlipRecord(); | |||
EscherBlipWMFRecord r = new EscherBlipWMFRecord(); | |||
r.setBoundaryLeft(1); | |||
r.setBoundaryHeight(2); | |||
r.setBoundaryTop(3); | |||
@@ -52,7 +52,7 @@ public class TestEscherBlipRecord extends TestCase | |||
(byte)0x01, (byte)0x01, (byte)0x01, (byte)0x01, }); | |||
r.setWidth(10); | |||
r.setHeight(11); | |||
r.setRecordId(EscherBlipRecord.RECORD_ID_START); | |||
r.setRecordId(EscherBlipWMFRecord.RECORD_ID_START); | |||
r.setOptions((short)5420); | |||
r.setData(new byte[] { (byte)0x01, (byte)0x02 } ); | |||
@@ -79,10 +79,10 @@ public class TestEscherBlipRecord extends TestCase | |||
public void testFillFields() throws Exception | |||
{ | |||
EscherBlipRecord r = new EscherBlipRecord(); | |||
EscherBlipWMFRecord r = new EscherBlipWMFRecord(); | |||
r.fillFields( data, 0, new DefaultEscherRecordFactory()); | |||
assertEquals( EscherBlipRecord.RECORD_ID_START, r.getRecordId() ); | |||
assertEquals( EscherBlipWMFRecord.RECORD_ID_START, r.getRecordId() ); | |||
assertEquals( 1, r.getBoundaryLeft() ); | |||
assertEquals( 2, r.getBoundaryHeight() ); | |||
assertEquals( 3, r.getBoundaryTop() ); | |||
@@ -100,12 +100,12 @@ public class TestEscherBlipRecord extends TestCase | |||
public void testToString() throws Exception | |||
{ | |||
EscherBlipRecord r = new EscherBlipRecord(); | |||
EscherBlipWMFRecord r = new EscherBlipWMFRecord(); | |||
r.fillFields( data, 0, new DefaultEscherRecordFactory() ); | |||
String nl = System.getProperty("line.separator"); | |||
assertEquals( "org.apache.poi.ddf.EscherBlipRecord:" + nl + | |||
assertEquals( "org.apache.poi.ddf.EscherBlipWMFRecord:" + nl + | |||
" RecordId: 0xF018" + nl + | |||
" Options: 0x152C" + nl + | |||
" Secondary UID: [01, 01, 01, 01, 01, 01, 01, 01, 01, 01, 01, 01, 01, 01, 01, 01, ]" + nl + |
@@ -24,7 +24,7 @@ public class TestEscherBoolProperty extends TestCase | |||
public void testToString() throws Exception | |||
{ | |||
EscherBoolProperty p = new EscherBoolProperty((short)1, 1); | |||
assertEquals("propNum: 1, propName: unknown, complex: false, blipId: false, value: 1 (0x00000001)", p.toString()); | |||
assertEquals("propNum: 1, RAW: 0x0001, propName: unknown, complex: false, blipId: false, value: 1 (0x00000001)", p.toString()); | |||
} | |||
} |
@@ -58,7 +58,7 @@ public class TestEscherClientDataRecord extends TestCase | |||
" RecordId: 0xF011" + nl + | |||
" Options: 0x0002" + nl + | |||
" Extra Data:" + nl + | |||
"No Data" ; | |||
"No Data" + nl ; | |||
assertEquals( expected, createRecord().toString() ); | |||
} | |||
@@ -150,7 +150,7 @@ public class TestEscherOptRecord extends TestCase | |||
" recordId: 0x" + HexDump.toHex(EscherOptRecord.RECORD_ID) + nl + | |||
" numchildren: 0" + nl + | |||
" properties:" + nl + | |||
" propNum: 1, propName: unknown, complex: false, blipId: false, value: 1 (0x00000001)" + nl; | |||
" propNum: 1, RAW: 0x0001, propName: unknown, complex: false, blipId: false, value: 1 (0x00000001)" + nl; | |||
assertEquals( expected, r.toString()); | |||
} | |||
@@ -19,16 +19,14 @@ | |||
package org.apache.poi.hssf.model; | |||
import junit.framework.TestCase; | |||
import org.apache.poi.hssf.usermodel.HSSFSheet; | |||
import org.apache.poi.hssf.record.BOFRecord; | |||
import org.apache.poi.hssf.record.EOFRecord; | |||
import org.apache.poi.hssf.record.DimensionsRecord; | |||
import org.apache.poi.hssf.record.*; | |||
import org.apache.poi.hssf.record.aggregates.ColumnInfoRecordsAggregate; | |||
import org.apache.poi.hssf.record.aggregates.RowRecordsAggregate; | |||
import org.apache.poi.hssf.record.aggregates.ValueRecordsAggregate; | |||
import org.apache.poi.hssf.record.aggregates.ColumnInfoRecordsAggregate; | |||
import java.util.List; | |||
import java.util.ArrayList; | |||
import java.util.Iterator; | |||
import java.util.List; | |||
/** | |||
* Unit test for the Sheet class. | |||
@@ -55,4 +53,232 @@ public class TestSheet extends TestCase | |||
assertTrue( sheet.records.get(pos++) instanceof EOFRecord ); | |||
} | |||
public void testAddMergedRegion() | |||
{ | |||
Sheet sheet = Sheet.createSheet(); | |||
int regionsToAdd = 4096; | |||
int startRecords = sheet.getRecords().size(); | |||
//simple test that adds a load of regions | |||
for (int n = 0; n < regionsToAdd; n++) | |||
{ | |||
int index = sheet.addMergedRegion(0, (short) 0, 1, (short) 1); | |||
assertTrue("Merged region index expected to be " + n + " got " + index, index == n); | |||
} | |||
//test all the regions were indeed added | |||
assertTrue(sheet.getNumMergedRegions() == regionsToAdd); | |||
//test that the regions were spread out over the appropriate number of records | |||
int recordsAdded = sheet.getRecords().size() - startRecords; | |||
int recordsExpected = regionsToAdd/1027; | |||
if ((regionsToAdd % 1027) != 0) | |||
recordsExpected++; | |||
assertTrue("The " + regionsToAdd + " merged regions should have been spread out over " + recordsExpected + " records, not " + recordsAdded, recordsAdded == recordsExpected); | |||
} | |||
public void testRemoveMergedRegion() | |||
{ | |||
Sheet sheet = Sheet.createSheet(); | |||
int regionsToAdd = 4096; | |||
for (int n = 0; n < regionsToAdd; n++) | |||
sheet.addMergedRegion(0, (short) 0, 1, (short) 1); | |||
int records = sheet.getRecords().size(); | |||
//remove a third from the beginning | |||
for (int n = 0; n < regionsToAdd/3; n++) | |||
{ | |||
sheet.removeMergedRegion(0); | |||
//assert they have been deleted | |||
assertTrue("Num of regions should be " + (regionsToAdd - n - 1) + " not " + sheet.getNumMergedRegions(), sheet.getNumMergedRegions() == regionsToAdd - n - 1); | |||
} | |||
//assert any record removing was done | |||
int recordsRemoved = (regionsToAdd/3)/1027; //doesn't work for particular values of regionsToAdd | |||
assertTrue("Expected " + recordsRemoved + " record to be removed from the starting " + records + ". Currently there are " + sheet.getRecords().size() + " records", records - sheet.getRecords().size() == recordsRemoved); | |||
} | |||
/** | |||
* Bug: 22922 (Reported by Xuemin Guan) | |||
* <p> | |||
* Remove mergedregion fails when a sheet loses records after an initial CreateSheet | |||
* fills up the records. | |||
* | |||
*/ | |||
public void testMovingMergedRegion() { | |||
List records = new ArrayList(); | |||
MergeCellsRecord merged = new MergeCellsRecord(); | |||
merged.addArea(0, (short)0, 1, (short)2); | |||
records.add(new RowRecord()); | |||
records.add(new RowRecord()); | |||
records.add(new RowRecord()); | |||
records.add(merged); | |||
Sheet sheet = Sheet.createSheet(records, 0); | |||
sheet.records.remove(0); | |||
//stub object to throw off list INDEX operations | |||
sheet.removeMergedRegion(0); | |||
assertEquals("Should be no more merged regions", 0, sheet.getNumMergedRegions()); | |||
} | |||
public void testGetMergedRegionAt() | |||
{ | |||
//TODO | |||
} | |||
public void testGetNumMergedRegions() | |||
{ | |||
//TODO | |||
} | |||
/** | |||
* Makes sure all rows registered for this sheet are aggregated, they were being skipped | |||
* | |||
*/ | |||
public void testRowAggregation() { | |||
List records = new ArrayList(); | |||
RowRecord row = new RowRecord(); | |||
row.setRowNumber(0); | |||
records.add(row); | |||
row = new RowRecord(); | |||
row.setRowNumber(1); | |||
records.add(row); | |||
records.add(new StringRecord()); | |||
row = new RowRecord(); | |||
row.setRowNumber(2); | |||
records.add(row); | |||
Sheet sheet = Sheet.createSheet(records, 0); | |||
assertNotNull("Row [2] was skipped", sheet.getRow(2)); | |||
} | |||
/** | |||
* Make sure page break functionality works (in memory) | |||
* | |||
*/ | |||
public void testRowPageBreaks(){ | |||
short colFrom = 0; | |||
short colTo = 255; | |||
Sheet sheet = Sheet.createSheet(); | |||
sheet.setRowBreak(0, colFrom, colTo); | |||
assertTrue("no row break at 0", sheet.isRowBroken(0)); | |||
assertEquals("1 row break available", 1, sheet.getNumRowBreaks()); | |||
sheet.setRowBreak(0, colFrom, colTo); | |||
sheet.setRowBreak(0, colFrom, colTo); | |||
assertTrue("no row break at 0", sheet.isRowBroken(0)); | |||
assertEquals("1 row break available", 1, sheet.getNumRowBreaks()); | |||
sheet.setRowBreak(10, colFrom, colTo); | |||
sheet.setRowBreak(11, colFrom, colTo); | |||
assertTrue("no row break at 10", sheet.isRowBroken(10)); | |||
assertTrue("no row break at 11", sheet.isRowBroken(11)); | |||
assertEquals("3 row break available", 3, sheet.getNumRowBreaks()); | |||
boolean is10 = false; | |||
boolean is0 = false; | |||
boolean is11 = false; | |||
Iterator iterator = sheet.getRowBreaks(); | |||
while (iterator.hasNext()) { | |||
PageBreakRecord.Break breakItem = (PageBreakRecord.Break)iterator.next(); | |||
int main = (int)breakItem.main; | |||
if (main != 0 && main != 10 && main != 11) fail("Invalid page break"); | |||
if (main == 0) is0 = true; | |||
if (main == 10) is10= true; | |||
if (main == 11) is11 = true; | |||
} | |||
assertTrue("one of the breaks didnt make it", is0 && is10 && is11); | |||
sheet.removeRowBreak(11); | |||
assertFalse("row should be removed", sheet.isRowBroken(11)); | |||
sheet.removeRowBreak(0); | |||
assertFalse("row should be removed", sheet.isRowBroken(0)); | |||
sheet.removeRowBreak(10); | |||
assertFalse("row should be removed", sheet.isRowBroken(10)); | |||
assertEquals("no more breaks", 0, sheet.getNumRowBreaks()); | |||
} | |||
/** | |||
* Make sure column pag breaks works properly (in-memory) | |||
* | |||
*/ | |||
public void testColPageBreaks(){ | |||
short rowFrom = 0; | |||
short rowTo = (short)65535; | |||
Sheet sheet = Sheet.createSheet(); | |||
sheet.setColumnBreak((short)0, rowFrom, rowTo); | |||
assertTrue("no col break at 0", sheet.isColumnBroken((short)0)); | |||
assertEquals("1 col break available", 1, sheet.getNumColumnBreaks()); | |||
sheet.setColumnBreak((short)0, rowFrom, rowTo); | |||
assertTrue("no col break at 0", sheet.isColumnBroken((short)0)); | |||
assertEquals("1 col break available", 1, sheet.getNumColumnBreaks()); | |||
sheet.setColumnBreak((short)1, rowFrom, rowTo); | |||
sheet.setColumnBreak((short)10, rowFrom, rowTo); | |||
sheet.setColumnBreak((short)15, rowFrom, rowTo); | |||
assertTrue("no col break at 1", sheet.isColumnBroken((short)1)); | |||
assertTrue("no col break at 10", sheet.isColumnBroken((short)10)); | |||
assertTrue("no col break at 15", sheet.isColumnBroken((short)15)); | |||
assertEquals("4 col break available", 4, sheet.getNumColumnBreaks()); | |||
boolean is10 = false; | |||
boolean is0 = false; | |||
boolean is1 = false; | |||
boolean is15 = false; | |||
Iterator iterator = sheet.getColumnBreaks(); | |||
while (iterator.hasNext()) { | |||
PageBreakRecord.Break breakItem = (PageBreakRecord.Break)iterator.next(); | |||
int main = (int)breakItem.main; | |||
if (main != 0 && main != 1 && main != 10 && main != 15) fail("Invalid page break"); | |||
if (main == 0) is0 = true; | |||
if (main == 1) is1 = true; | |||
if (main == 10) is10= true; | |||
if (main == 15) is15 = true; | |||
} | |||
assertTrue("one of the breaks didnt make it", is0 && is1 && is10 && is15); | |||
sheet.removeColumnBreak((short)15); | |||
assertFalse("column break should not be there", sheet.isColumnBroken((short)15)); | |||
sheet.removeColumnBreak((short)0); | |||
assertFalse("column break should not be there", sheet.isColumnBroken((short)0)); | |||
sheet.removeColumnBreak((short)1); | |||
assertFalse("column break should not be there", sheet.isColumnBroken((short)1)); | |||
sheet.removeColumnBreak((short)10); | |||
assertFalse("column break should not be there", sheet.isColumnBroken((short)10)); | |||
assertEquals("no more breaks", 0, sheet.getNumColumnBreaks()); | |||
} | |||
} |
@@ -23,6 +23,9 @@ import org.apache.poi.util.HexDump; | |||
public class TestDrawingGroupRecord extends TestCase | |||
{ | |||
static final int MAX_RECORD_SIZE = 8228; | |||
private static final int MAX_DATA_SIZE = MAX_RECORD_SIZE - 4; | |||
public void testGetRecordSize() | |||
throws Exception | |||
{ | |||
@@ -48,5 +51,93 @@ public class TestDrawingGroupRecord extends TestCase | |||
assertEquals(28, size); | |||
assertEquals(24, dggContainer.getRecordSize()); | |||
r = new DrawingGroupRecord( ); | |||
r.setRawData( new byte[MAX_DATA_SIZE] ); | |||
assertEquals( MAX_RECORD_SIZE, r.getRecordSize() ); | |||
r.setRawData( new byte[MAX_DATA_SIZE+1] ); | |||
assertEquals( MAX_RECORD_SIZE + 5, r.getRecordSize() ); | |||
r.setRawData( new byte[MAX_DATA_SIZE*2] ); | |||
assertEquals( MAX_RECORD_SIZE * 2, r.getRecordSize() ); | |||
r.setRawData( new byte[MAX_DATA_SIZE*2 + 1] ); | |||
assertEquals( MAX_RECORD_SIZE * 2 + 5, r.getRecordSize() ); | |||
} | |||
public void testSerialize() throws Exception | |||
{ | |||
// Check under max record size | |||
DrawingGroupRecord r = new DrawingGroupRecord(); | |||
byte[] rawData = new byte[100]; | |||
rawData[0] = 100; | |||
rawData[99] = (byte) 200; | |||
r.setRawData( rawData ); | |||
byte[] buffer = new byte[r.getRecordSize()]; | |||
int size = r.serialize( 0, buffer ); | |||
assertEquals( 104, size ); | |||
assertEquals("[EB, 00, 64, 00, 64, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, C8, ]", HexDump.toHex(buffer)); | |||
// check at max record size | |||
rawData = new byte[MAX_DATA_SIZE]; | |||
r.setRawData( rawData ); | |||
buffer = new byte[r.getRecordSize()]; | |||
size = r.serialize( 0, buffer ); | |||
assertEquals( MAX_RECORD_SIZE, size ); | |||
// check over max record size | |||
rawData = new byte[MAX_DATA_SIZE+1]; | |||
rawData[rawData.length-1] = (byte) 255; | |||
r.setRawData( rawData ); | |||
buffer = new byte[r.getRecordSize()]; | |||
size = r.serialize( 0, buffer ); | |||
assertEquals( MAX_RECORD_SIZE + 5, size ); | |||
assertEquals( "[EB, 00, 20, 20, ]", HexDump.toHex(cut(buffer, 0, 4) )); | |||
assertEquals( "[00, EB, 00, 01, 00, FF, ]", HexDump.toHex(cut(buffer, MAX_RECORD_SIZE - 1, MAX_RECORD_SIZE + 5) )); | |||
// check continue record | |||
rawData = new byte[MAX_DATA_SIZE * 2 + 1]; | |||
rawData[rawData.length-1] = (byte) 255; | |||
r.setRawData( rawData ); | |||
buffer = new byte[r.getRecordSize()]; | |||
size = r.serialize( 0, buffer ); | |||
assertEquals( MAX_RECORD_SIZE * 2 + 5, size ); | |||
assertEquals( MAX_RECORD_SIZE * 2 + 5, r.getRecordSize() ); | |||
assertEquals( "[EB, 00, 20, 20, ]", HexDump.toHex(cut(buffer, 0, 4) )); | |||
assertEquals( "[EB, 00, 20, 20, ]", HexDump.toHex(cut(buffer, MAX_RECORD_SIZE, MAX_RECORD_SIZE + 4) )); | |||
assertEquals( "[3C, 00, 01, 00, FF, ]", HexDump.toHex(cut(buffer, MAX_RECORD_SIZE * 2, MAX_RECORD_SIZE * 2 + 5) )); | |||
// check continue record | |||
rawData = new byte[664532]; | |||
r.setRawData( rawData ); | |||
buffer = new byte[r.getRecordSize()]; | |||
size = r.serialize( 0, buffer ); | |||
assertEquals( 664856, size ); | |||
assertEquals( 664856, r.getRecordSize() ); | |||
} | |||
private byte[] cut( byte[] data, int fromInclusive, int toExclusive ) | |||
{ | |||
int length = toExclusive - fromInclusive; | |||
byte[] result = new byte[length]; | |||
System.arraycopy( data, fromInclusive, result, 0, length); | |||
return result; | |||
} | |||
public void testGrossSizeFromDataSize() throws Exception | |||
{ | |||
for (int i = 0; i < MAX_RECORD_SIZE * 4; i += 11) | |||
{ | |||
System.out.print( "data size = " + i + ", gross size = " + DrawingGroupRecord.grossSizeFromDataSize( i ) ); | |||
System.out.println( " Diff: " + (DrawingGroupRecord.grossSizeFromDataSize( i ) - i) ); | |||
} | |||
assertEquals( 4, DrawingGroupRecord.grossSizeFromDataSize( 0 ) ); | |||
assertEquals( 5, DrawingGroupRecord.grossSizeFromDataSize( 1 ) ); | |||
assertEquals( MAX_RECORD_SIZE, DrawingGroupRecord.grossSizeFromDataSize( MAX_DATA_SIZE ) ); | |||
assertEquals( MAX_RECORD_SIZE + 5, DrawingGroupRecord.grossSizeFromDataSize( MAX_DATA_SIZE + 1 ) ); | |||
assertEquals( MAX_RECORD_SIZE * 2, DrawingGroupRecord.grossSizeFromDataSize( MAX_DATA_SIZE * 2 ) ); | |||
assertEquals( MAX_RECORD_SIZE * 2 + 5, DrawingGroupRecord.grossSizeFromDataSize( MAX_DATA_SIZE * 2 + 1 ) ); | |||
} | |||
} |
@@ -17,14 +17,13 @@ | |||
package org.apache.poi.hssf.record; | |||
import junit.framework.TestCase; | |||
import org.apache.poi.util.HexRead; | |||
import org.apache.poi.util.HexDump; | |||
import org.apache.poi.ddf.EscherContainerRecord; | |||
import org.apache.poi.ddf.EscherSpRecord; | |||
import org.apache.poi.ddf.EscherClientDataRecord; | |||
import org.apache.poi.ddf.EscherContainerRecord; | |||
import org.apache.poi.ddf.EscherDggRecord; | |||
import org.apache.poi.hssf.model.DrawingManager; | |||
import org.apache.poi.hssf.model.Sheet; | |||
import org.apache.poi.ddf.EscherSpRecord; | |||
import org.apache.poi.hssf.model.DrawingManager2; | |||
import org.apache.poi.util.HexDump; | |||
import org.apache.poi.util.HexRead; | |||
import java.util.ArrayList; | |||
import java.util.List; | |||
@@ -82,7 +81,7 @@ public class TestEscherAggregate extends TestCase | |||
records.add( d2 ); | |||
records.add( r2 ); | |||
DrawingManager drawingManager = new DrawingManager(new EscherDggRecord() ); | |||
DrawingManager2 drawingManager = new DrawingManager2(new EscherDggRecord() ); | |||
EscherAggregate aggregate = EscherAggregate.createAggregate( records, 0, drawingManager ); | |||
assertEquals( 1, aggregate.getEscherRecords().size() ); |
@@ -23,6 +23,7 @@ import junit.framework.*; | |||
import java.io.*; | |||
/** | |||
* @author Glen Stampoultzis (glens at apache.org) | |||
* @author Marc Johnson (mjohnson at apache dot org) | |||
*/ | |||
@@ -281,7 +282,8 @@ public class TestHexDump | |||
// verify proper behaviour with empty byte array | |||
ByteArrayOutputStream os = new ByteArrayOutputStream( ); | |||
HexDump.dump( new byte[0], 0, os, 0 ); | |||
assertEquals( "No Data", os.toString() ); | |||
assertEquals( "No Data" + System.getProperty( "line.separator"), os.toString() ); | |||
} | |||
public void testToHex() | |||
@@ -289,7 +291,7 @@ public class TestHexDump | |||
{ | |||
assertEquals( "000A", HexDump.toHex((short)0xA)); | |||
assertEquals( "0A", HexDump.toHex((byte)0xA)); | |||
assertEquals( "0000000A", HexDump.toHex((int)0xA)); | |||
assertEquals( "0000000A", HexDump.toHex(0xA)); | |||
assertEquals( "FFFF", HexDump.toHex((short)0xFFFF)); | |||
@@ -18,11 +18,12 @@ | |||
package org.apache.poi.util; | |||
import junit.framework.TestCase; | |||
import org.apache.poi.util.LittleEndian.BufferUnderrunException; | |||
import java.io.*; | |||
import junit.framework.*; | |||
import java.io.ByteArrayInputStream; | |||
import java.io.IOException; | |||
import java.io.InputStream; | |||
/** | |||
* Class to test LittleEndian functionality | |||
@@ -39,7 +40,6 @@ public class TestLittleEndian | |||
* | |||
* @param name | |||
*/ | |||
public TestLittleEndian(String name) | |||
{ | |||
super(name); |